test: cover renamingATemporaryTableKeepsItTemporarySoTheRenamedNameAl...

This commit is contained in:
dualfroz committed 2026-09-09 19:20:52 +02:00
1 parent 965cc2974d
commit 6c074cc16b
1 file changed
+155
+155
View File
@@ -891,4 +891,159 @@ class PersistenceTest {
dir,
"""
CREATE TABLE regular (id INTEGER PRIMARY KEY, note TEXT);
CREATE TEMP TABLE scratch (id INTEGER PRIMARY KEY, note TEXT);
INSERT INTO regular VALUES (1, 'keepsMe');
INSERT INTO scratch VALUES (1, 'losesMe');
.exit
""".trimIndent()
)
val reopened = runScript(dir, "SELECT note FROM regular;\nSELECT note FROM scratch;\n.exit\n")
assertContains(reopened, "keepsMe")
assertContains(reopened, "Error: no such table: scratch")
assertTrue(!reopened.contains("losesMe"), "a TEMP table's rows must not survive reopen")
}
@Test
fun renamingATemporaryTableKeepsItTemporarySoTheRenamedNameAlsoDoesNotSurviveReopen() {
val dir = tempDir()
runScript(
dir,
"""
CREATE TEMP TABLE scratch (id INTEGER PRIMARY KEY);
ALTER TABLE scratch RENAME TO renamedScratch;
INSERT INTO renamedScratch VALUES (1);
.exit
""".trimIndent()
)
val reopened = runScript(dir, "SELECT id FROM renamedScratch;\n.exit\n")
assertContains(reopened, "Error: no such table: renamedScratch")
}
@Test
fun addColumnAndDropColumnOnATemporaryTableKeepItTemporaryAcrossReopen() {
val dir = tempDir()
runScript(
dir,
"""
CREATE TEMP TABLE scratch (id INTEGER PRIMARY KEY, a INTEGER);
ALTER TABLE scratch ADD COLUMN b TEXT;
ALTER TABLE scratch DROP COLUMN a;
INSERT INTO scratch VALUES (1, 'x');
.exit
""".trimIndent()
)
val reopened = runScript(dir, "SELECT id FROM scratch;\n.exit\n")
assertContains(reopened, "Error: no such table: scratch")
}
@Test
fun renamingAColumnOnATemporaryTableKeepsItTemporaryAcrossReopen() {
val dir = tempDir()
runScript(
dir,
"""
CREATE TEMP TABLE scratch (id INTEGER PRIMARY KEY, a INTEGER);
ALTER TABLE scratch RENAME COLUMN a TO b;
INSERT INTO scratch VALUES (1, 2);
.exit
""".trimIndent()
)
val reopened = runScript(dir, "SELECT id FROM scratch;\n.exit\n")
assertContains(reopened, "Error: no such table: scratch")
}
@Test
fun aTemporaryTablesForeignKeyBeingDroppedByAnUnrelatedCascadeKeepsItTemporaryAcrossReopen() {
val dir = tempDir()
runScript(
dir,
"""
CREATE TABLE parent (id INTEGER PRIMARY KEY);
CREATE TEMP TABLE child (id INTEGER PRIMARY KEY, parentId INTEGER, FOREIGN KEY (parentId) REFERENCES parent(id));
DROP TABLE parent CASCADE;
INSERT INTO child VALUES (1, NULL);
.exit
""".trimIndent()
)
val reopened = runScript(dir, "SELECT id FROM child;\n.exit\n")
assertContains(reopened, "Error: no such table: child")
}
@Test
fun anIndexOnATemporaryTableNeverPersists() {
val dir = tempDir()
runScript(
dir,
"""
CREATE TEMP TABLE scratch (id INTEGER PRIMARY KEY, tag TEXT);
CREATE INDEX scratchTag ON scratch (tag);
INSERT INTO scratch VALUES (1, 'x');
.exit
""".trimIndent()
)
val reopened = runScript(dir, "SELECT id FROM scratch;\n.indexes\n.exit\n")
assertContains(reopened, "Error: no such table: scratch")
assertTrue(!reopened.contains("scratchTag"), "an index on a TEMP table must not survive reopen either")
}
@Test
fun droppingAnIndexOnATemporaryTableNeverReachesTheIncrementalWalEitherSideOfIt() {
val dir = tempDir()
runScriptThatCrashesBeforeItCanCheckpoint(
dir,
"""
CREATE TEMP TABLE scratch (id INTEGER PRIMARY KEY, tag TEXT);
CREATE INDEX scratchTag ON scratch (tag);
DROP INDEX scratchTag;
INSERT INTO scratch VALUES (1, 'x');
""".trimIndent() + "\n"
)
val reopened = runScript(dir, "SELECT id FROM scratch;\n.indexes\n.exit\n")
assertContains(reopened, "Error: no such table: scratch")
assertTrue(!reopened.contains("scratchTag"))
}
@Test
fun aTemporaryTableNeverReachesTheIncrementalWriteAheadLogEvenBeforeACheckpoint() {
val dir = tempDir()
runScriptThatCrashesBeforeItCanCheckpoint(
dir,
"""
CREATE TABLE regular (id INTEGER PRIMARY KEY);
CREATE TEMP TABLE scratch (id INTEGER PRIMARY KEY);
INSERT INTO regular VALUES (111);
INSERT INTO scratch VALUES (222);
""".trimIndent() + "\n"
)
// No checkpoint ever ran, so this reopen replays raw incremental WAL entries only - if the
// TEMP table's CREATE/INSERT had leaked into that log, replaying "INSERT INTO scratch..."
// would either resurrect it or (if its CREATE alone were skipped but not its INSERT) throw
// "no such table" loudly enough to show up here.
val reopened = runScript(dir, "SELECT id FROM regular;\nSELECT id FROM scratch;\n.exit\n")
assertContains(reopened, "111")
assertContains(reopened, "Error: no such table: scratch")
assertTrue(!reopened.contains("222"), "a TEMP table's row must never reach the WAL, crash or not")
}
@Test
fun aTruncateNamingBothATemporaryAndARegularTableStillPersistsTheRegularTablesTruncationBeforeACheckpoint() {
val dir = tempDir()
runScriptThatCrashesBeforeItCanCheckpoint(
dir,
"""
CREATE TABLE regular (id INTEGER);
CREATE TEMP TABLE scratch (id INTEGER);
INSERT INTO regular VALUES (111), (222);
INSERT INTO scratch VALUES (333);
TRUNCATE TABLE regular, scratch;
""".trimIndent() + "\n"
)
// If the TEMP name were not stripped out of the persisted TRUNCATE text, replaying
// "TRUNCATE TABLE regular, scratch" against a reload where scratch was never recreated would
// throw before truncating ANYTHING (Catalog.truncateTables validates every name up front) -
// silently leaving regular's rows intact instead of empty, since replay errors are swallowed
// (see Repl.printResult's `if (replaying) return`).
val reopened = runScript(dir, "SELECT id FROM regular;\n.exit\n")
assertTrue(!reopened.contains("111") && !reopened.contains("222"), "regular's TRUNCATE must survive the crash-before-checkpoint replay")
}
}