Conversation
SQLite leaves a statement whose step failed with SQLITE_BUSY in progress, so that a caller can step it again. run() resets a statement only before it runs it and returns as soon as the run fails, so the failed statement stays in progress until garbage collection finalizes it. Until then every COMMIT on that connection fails with "cannot commit transaction - SQL statements in progress", although the lock is free. Reset the statement when run() fails, in the sync and the promise API, as get() already does when its query fails.
Contributor
Author
|
Closing in favour of #238, which builds on this commit and resets the statement whether the step succeeded or failed, as better-sqlite3 does. That also covers INSERT ... RETURNING and SELECT through run(), which this change left open. Thanks for taking it further. |
8 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When a write statement fails with
SQLITE_BUSY, SQLite leaves it in progress so that a caller can step it again.run()resets a statement only before it runs it, and returns as soon as the run fails. The failed statement then stays in progress until the garbage collector finalizes it, and until then everyCOMMITon that connection fails withSQLITE_BUSY: cannot commit transaction - SQL statements in progress, although the lock is free. Through@libsql/client, one write batch that fails busy makes the following batches on the same client fail too.This resets the statement when
run()fails, in both the sync and the promise API, the same wayget()already does when its query fails.The report, with the SQLite source lines and reproductions against this binding alone, is in tursodatabase/libsql-client-ts#352.
Tests
One new case in
sync.test.jsand one inasync.test.js. A second connection holds the write lock,BEGIN IMMEDIATEfails withSQLITE_BUSYafter a 50 ms busy timeout, the lock is released, and a write transaction on the same connection must then commit. The failed statement stays referenced, so garbage collection cannot hide the bug.COMMITwithSQLITE_BUSYon the libsql provider.npm run testinintegration-testspasses: sqlite 53, libsql 53, async 68, extensions 29, concurrency 4 and connections 2.all()anditerate()already recover after a statement fails busy, checked the same way, so they are unchanged.