test: cover renamingATemporaryTableKeepsItTemporarySoTheRenamedNameAl...
This commit is contained in:
1 parent
965cc2974d
commit
6c074cc16b
1 file changed
+155
@@ -891,4 +891,159 @@ class PersistenceTest {
|
|||||||
dir,
|
dir,
|
||||||
"""
|
"""
|
||||||
CREATE TABLE regular (id INTEGER PRIMARY KEY, note TEXT);
|
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")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user