Repository navigation
Conversation
A DELETE without a WHERE clause takes the truncate optimization, which runs OP_Clear on the table and each index b-tree. A vector index keeps its entries in its shadow table rather than in its b-tree, so the shadow rows were left behind and the next insert that reused a rowid failed with "vector index(insert): failed to insert shadow row". Open each vector index with OPFLAG_FORDELETE on that path, which clears it the same way REINDEX does. Fixes tursodatabase#2289
This branch has not been deployed
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.
Fixes #2289.
DELETE FROM twith no WHERE clause takes SQLite's truncate optimization insqlite3DeleteFrom: it emitsOP_Clearfor the table and for each index's b-tree instead of deleting rows one by one. A vector index stores its entries in<index>_shadow, not in its b-tree, so that path left the shadow rows in place. The next insert that reused one of the old rowids then failed withvector index(insert): failed to insert shadow row.DELETE ... WHERE 1 = 1goes through the row-by-row path, which callsvectorIndexDelete, so it wasn't affected.On the truncate path, this opens each vector index with
OP_OpenVectorIdxandOPFLAG_FORDELETE, which clears the index throughvectorIndexClear, then closes the cursor. That's the same thingsqlite3RefillIndexdoes when REINDEX has to clear a vector index. TheOP_Clearon the (empty) b-tree is unchanged.The bundled amalgamations in
libsql-ffi/bundled(plain and SQLite3MultipleCiphers) are regenerated from the patched source, so the diff there is the same hunk.Testing
Built
libsql-sqlite3on Linux x86_64 and ran:Before: the shadow table still has 2 rows and the insert fails with the error above. After: 0 rows, the insert succeeds, and
vector_top_kreturns the new row. ADELETE FROM tinside a transaction that is rolled back leaves both the table and the shadow table with their rows, andchanges()still reports the number of deleted rows.Added
vector-delete-alltotest/libsql_vector_index.test; it fails without the change and passes with it. With the change,testfixturepasseslibsql_vector_index.test,libsql_vector.test,delete.test,delete2.test,delete3.test,delete4.testandtrigger2.test. I didn't run the Rust crates' tests or the fullall.testrun.