-
Notifications
You must be signed in to change notification settings - Fork 26
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Prepare many statements from a single string. #59
base: v1.x.x
Are you sure you want to change the base?
Changes from all commits
5b8640a
ae838a1
722c124
77e9abd
85871d8
dc1d9b6
652cf78
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,7 +20,7 @@ import d2sqlite3.internal.util; | |
|
||
import std.conv : to; | ||
import std.exception : enforce; | ||
import std.string : format, toStringz; | ||
import std.string : format, toStringz, chomp; | ||
import std.typecons : Nullable; | ||
|
||
/// Set _UnlockNotify version if compiled with SqliteEnableUnlockNotify or SqliteFakeUnlockNotify | ||
|
@@ -56,6 +56,27 @@ enum Deterministic | |
no = 0 | ||
} | ||
|
||
struct PrepareMany { | ||
Database db; | ||
Statement current; | ||
string sql_left; | ||
bool empty; | ||
this(Database db, const string sql) { | ||
this.db = db; | ||
this.sql_left = sql; | ||
popFront(); | ||
} | ||
void popFront() { | ||
const size_t old = sql_left.length; | ||
current = Statement(this.db, sql_left); | ||
empty = old == sql_left.length; | ||
sql_left = sql_left.chomp(); | ||
} | ||
Statement front() { | ||
return current; | ||
} | ||
} | ||
|
||
/++ | ||
An database connection. | ||
|
||
|
@@ -278,11 +299,62 @@ public: | |
|
||
The statement becomes invalid if the Database goes out of scope and is destroyed. | ||
+/ | ||
Statement prepare(string sql) | ||
Statement prepare(const string sql) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not needed, a |
||
{ | ||
return Statement(this, sql); | ||
} | ||
|
||
/++ | ||
Prepares (compiles) several SQL statements and return them. | ||
|
||
The statements become invalid if the Database goes out of scope and is destroyed. | ||
+/ | ||
|
||
PrepareMany prepare_many(string sql) | ||
{ | ||
return PrepareMany(this, sql); | ||
} | ||
unittest { | ||
auto db = Database(":memory:"); | ||
db.execute("CREATE TABLE test (val INTEGER)"); | ||
auto statements = | ||
db.prepare_many(q"[ | ||
Comment on lines
+317
to
+321
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn't see this was a unittest at first, please fix the alignment |
||
--sql comment with a ; in it | ||
INSERT INTO test (val) | ||
VALUES (:v); | ||
SELECT val FROM | ||
-- gosh I love SQL comments and ;s! | ||
test WHERE val > :v; | ||
SELECT 'A fun string containing ; and -- because why not?'; -- I'm so clever | ||
-- SELECT 23; -- don't do this one | ||
SELECT 42; -- do this one | ||
SELECT val FROM test WHERE val < :v; | ||
]"); | ||
assert(!statements.empty); | ||
statements.popFront(); | ||
assert(!statements.empty); | ||
statements.popFront(); | ||
assert(!statements.empty); | ||
assert(statements.front.execute().oneValue!string == | ||
"A fun string containing ; and -- because why not?"); | ||
statements.popFront(); | ||
assert(!statements.empty); | ||
assert(statements.front.execute().oneValue!int == 42); | ||
statements.popFront(); | ||
assert(!statements.empty); | ||
statements.popFront(); | ||
assert(statements.empty); | ||
int count = 4; | ||
foreach(prep;db.prepare_many(q"[ | ||
SELECT 3; | ||
SELECT 2; | ||
SELECT 1; | ||
-- contact!]")) { | ||
--count; | ||
} | ||
assert(count == 0); | ||
} | ||
|
||
/// Convenience functions equivalent to an SQL statement. | ||
void begin() { execute("BEGIN"); } | ||
/// Ditto | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -84,23 +84,51 @@ private: | |
} | ||
|
||
package(d2sqlite3): | ||
this(Database db, string sql) | ||
|
||
this(Database db, const string sql) | ||
{ | ||
string temp = sql; | ||
this(db, temp); | ||
// this constructor won't be able to give any indication that | ||
// the sql wasn't fully parsed, so make sure it is. | ||
assert(temp.length == 0); | ||
// the second constructor moves the sql parameter to the | ||
// unparsed tail, so use that if you might be preparing 2 statements | ||
} | ||
|
||
this(Database db, ref string sql) | ||
Comment on lines
+88
to
+99
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't really understand the point of this change. Why complicate the API this much ? |
||
{ | ||
sqlite3_stmt* handle; | ||
immutable(char)* head = sql.toStringz; | ||
immutable(char)* tail = null; | ||
version (_UnlockNotify) | ||
{ | ||
auto result = sqlite3_blocking_prepare_v2(db, sql.toStringz, sql.length.to!int, | ||
&handle, null); | ||
auto result = sqlite3_blocking_prepare_v2(db, head, sql.length.to!int, | ||
&handle, &tail); | ||
} | ||
else | ||
{ | ||
auto result = sqlite3_prepare_v2(db.handle(), sql.toStringz, sql.length.to!int, | ||
&handle, null); | ||
auto result = sqlite3_prepare_v2(db.handle(), head, sql.length.to!int, | ||
&handle, &tail); | ||
} | ||
enforce(result == SQLITE_OK, new SqliteException(errmsg(db.handle()), result, sql)); | ||
p = Payload(db, handle); | ||
p.paramCount = sqlite3_bind_parameter_count(p.handle); | ||
debug p.sql = sql; | ||
if(tail == null) { | ||
debug p.sql = sql; | ||
sql = ""; | ||
} else { | ||
/++ | ||
the tail will be 1 past the end of the SQL statement that was just | ||
prepared. So if you prepare a string with two SQL statements, | ||
it will prepare the first statement, then set tail at the | ||
beginning of the second. This allows to prepare many ; delineated | ||
statements, without having to parse the SQL ourselves. | ||
+/ | ||
size_t head_length = tail - head; | ||
debug p.sql = sql[0..head_length]; | ||
sql = sql[head_length..$]; | ||
} | ||
} | ||
|
||
version (_UnlockNotify) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is lacking visibility attributes (
private
/package
) and attributes (@safe
/nothrow
/ ...)