-
-
Notifications
You must be signed in to change notification settings - Fork 731
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
Add the ability to execute multiple SQL statements at once. #4
Conversation
This is done by the new MultipleStatement class, which calls sqlite3_exec under the hood.
Hello @peter-ss, Thanks for the PR, because I now understand your need. Yet, when your write:
An answer could be to simply run several statements in your migration blocks: // Database V1
migrator.registerMigration("createTables") { db in
try db.execute("CREATE TABLE table1 (...)")
try db.execute("CREATE TABLE table2 (...)")
try db.execute("CREATE TABLE table3 (...)")
} OK, now the gist of your commit. I can't accept it as is it, because the
Do you think you could fix this? |
… contains multiple SQL statements. Test that trailing semicolon is both accepted and optional.
Hold on @peter-ss. You made me come up with a better idea. I won't expose yet any "reusable" object since 1. it is not a requested feature, and 2. users who know well sqlite may feel dizzy in front of such an unexpected feature: they would raise an eyebrow if the documentation would talk about "Prepared Multiple Statements", and this is how you lose user's confidence. Instead, I'll have Database.execute() accept both single and multiple SQL statements. Arguments will just not be supported for multiple statements. I think it's a simpler option. Both simple (API does not grow), and discoverable by power users. |
Hi there, I agree with your assessment of MultipleStatement. I also agree that it I'd be happy to adjust the code however you think best. Peter On Mon, Aug 17, 2015 at 7:45 AM, Gwendal Roué [email protected]
|
…ent and no argument successfully returns the inserted Row ID.
Hi again @peter-ss. I did just add a few commits:
Could you have a look at the failing tests (1, 2 and 3), and tell me if they look sensible to you? If so, I can let you make them pass, if you like. Tell me: I don't want to short-circuit you. |
Those lines can tell you if a SQL string contains a single or several statements (precisely, the |
The tests look reasonable, and well thought-out. So should I build the code that determines if we have a multiple statement On Mon, Aug 17, 2015 at 8:06 AM, Gwendal Roué [email protected]
|
@peter-ss You're right, we don't want to add the multiple statement handling in UpdateStatement, which remains focused on single, actually reusable, statements. So:
This should give us kind of a roadmap, what do you think? |
Yes - sounds good. Let me think on it a bit. I will create a local branch On Mon, Aug 17, 2015 at 8:41 AM, Gwendal Roué [email protected]
|
Take your time, Peter 😄 |
…levant if the executed statement is not an INSERT statement).
Ah - one additional wrinkle: the execute() method returns an Also, it occurs to me that if we refactor the logic that determines if we I'm wondering if perhaps adding a second method to Database is a better way
If we decide to go this route, I would handle the sqlite3_exec() call right What do you think? Peter On Mon, Aug 17, 2015 at 8:41 AM, Gwendal Roué [email protected]
|
Hm. What about extracting UpdateStatement.Changes to a general DatabaseChanges struct, and leaving the insertedRowID nil in case of multiple statements?
Correct. Yet signatures of internal APIs are not sacred: I would not care if they would have to change.
This is indeed a problem: we would need to parse the initial statement twice: once with prepare(), and a second time with exec(). Now you have something.
I'm thinking the same way, now :-)
You have my full acknowledgment, Peter! Thanks for your dedication :-) I'm still in favor of sharing the result of this new method with the one returned by |
GRDB tries to hide the statefulness of sqlite behind an Object-Oriented API. Sure it leaks in some places, but I have tried hard to close most doors. The "last inserted Row ID" is a tough one. There is no Database.lastInsertedRowID read-only property, and a Changes.insertedRowID instead (documented along with the sentence "Relevant if and only if the statement is an INSERT statement" in order to limit the leaking...). If we would return an insertedRowID from executeMultiple(), we would bring back a stateful API, which I don't like. So I'm really in favor of returning a DatabaseChanges (global class extracted from UpdateStatement.Changes), with a nil insertedRowID (documented as such) returned from Database.executeMultiple(). |
I'm fine with returning a value consistent with the Database.execute method I will adjust the unit tests accordingly as well. One thing I did notice, Peter On Mon, Aug 17, 2015 at 10:27 AM, Gwendal Roué [email protected]
|
That's so cool :-)
Yes, I did update that test in 53671c0 :-) |
Closed in favor of #6 |
Added the ability to execute multiple SQL statements in a single sqlite call. The existing UpdateStatement did not allow this, because the underlying call to sqlite3_prepare and sqlite3_step only handle single SQL statements.
Being able to run multiple statements at once is very useful. For example, several of my migrations have many SQL statements, and I do not want to register each statement as a migration.
I would be happy to write up an example use case for the framework documentation. Just let me know.