Skip to content

Commit ebf5459

Browse files
committed
Patch Turso translate_drop_table sqlite_sequence db mismatch
ROOT CAUSE for testCreateTemporaryTable + testTemporaryTableHasPriority: In core/translate/schema.rs::translate_drop_table, the "// if drops table, sequence table should reset." block looks up `sqlite_sequence` via `resolver.schema()` — which always returns MAIN's schema (Resolver::schema() at translate/emitter/mod.rs:228 just returns `self.schema`). It then opens the cursor with `db: database_id` set to TEMP_DB_ID for temp tables, telling the pager to read MAIN's sqlite_sequence root_page (e.g. page 25) from the TEMP database where that page doesn't exist. The result: "I/O error: short read on page 25: expected 4096 bytes, got 0" when DROPping a TEMP AUTOINCREMENT table after the suite has created any other AUTOINCREMENT tables in MAIN (which makes MAIN's sqlite_sequence land on page 25). Reproduces in fresh PDO when the test class's setUp() creates two AUTOINCREMENT tables before the test method. Fix: use `resolver.with_schema(database_id, |s| s.get_table(...))` so the sqlite_sequence root_page comes from the same database the cursor will open in. For TEMP tables, that yields temp's sqlite_sequence (created lazily when the temp table itself is created with AUTOINCREMENT). Worth reporting upstream — the same bug would affect any non-MAIN-DB DROP TABLE path with AUTOINCREMENT.
1 parent 08a4715 commit ebf5459

1 file changed

Lines changed: 43 additions & 0 deletions

File tree

.github/workflows/phpunit-tests-turso.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,6 +595,49 @@ jobs:
595595
print('patched CreateTrigger to preserve original SQL text')
596596
PY_TRIGGER_SQL
597597
598+
# ROOT CAUSE FIX (report upstream): DROP TABLE for a TEMP table
599+
# with AUTOINCREMENT looks up `sqlite_sequence` via
600+
# `resolver.schema()` (which always returns MAIN's schema) but
601+
# opens the cursor with `db: database_id` set to TEMP_DB_ID.
602+
# The result: it tries to read MAIN's sqlite_sequence root
603+
# page (e.g. page 25) from the TEMP database where that page
604+
# doesn't exist, raising
605+
# "I/O error: short read on page N: expected 4096 bytes, got 0"
606+
# See `core/translate/schema.rs::translate_drop_table` around
607+
# the `// if drops table, sequence table should reset.` block.
608+
# Repro: setUp creates two AUTOINCREMENT permanent tables
609+
# (creating MAIN's sqlite_sequence with root_page = 25), then
610+
# the test creates+drops a TEMP AUTOINCREMENT table.
611+
#
612+
# Fix: use `resolver.with_schema(database_id, ...)` so the
613+
# sqlite_sequence root_page comes from the SAME database the
614+
# cursor will open in.
615+
python3 - <<'PY_FIX_TEMP_DROP_SEQ'
616+
p = 'core/translate/schema.rs'
617+
s = open(p).read()
618+
old = (
619+
" // if drops table, sequence table should reset.\n"
620+
" if let Some(seq_table) = resolver\n"
621+
" .schema()\n"
622+
" .get_table(SQLITE_SEQUENCE_TABLE_NAME)\n"
623+
" .and_then(|t| t.btree())\n"
624+
" {\n"
625+
)
626+
new = (
627+
" // if drops table, sequence table should reset.\n"
628+
" // Use the schema for the SAME database the cursor will open\n"
629+
" // in — for TEMP tables, the resolver's main schema doesn't\n"
630+
" // own sqlite_sequence; it lives in temp's schema.\n"
631+
" if let Some(seq_table) = resolver\n"
632+
" .with_schema(database_id, |s| s.get_table(SQLITE_SEQUENCE_TABLE_NAME))\n"
633+
" .and_then(|t| t.btree())\n"
634+
" {\n"
635+
)
636+
assert old in s, 'translate_drop_table sqlite_sequence reset block not found'
637+
open(p, 'w').write(s.replace(old, new, 1))
638+
print('patched translate_drop_table to use correct database schema for sqlite_sequence')
639+
PY_FIX_TEMP_DROP_SEQ
640+
598641
echo '--- Patched stub! macro ---'
599642
sed -n '/macro_rules! stub/,/^}$/p' sqlite3/src/lib.rs
600643

0 commit comments

Comments
 (0)