diff --git a/src/test/kotlin/kestrel/store/sql/DiskExecutorTest.kt b/src/test/kotlin/kestrel/store/sql/DiskExecutorTest.kt index 48d16b7..e7e53c4 100644 --- a/src/test/kotlin/kestrel/store/sql/DiskExecutorTest.kt +++ b/src/test/kotlin/kestrel/store/sql/DiskExecutorTest.kt @@ -3962,3 +3962,78 @@ class DiskExecutorTest { assertFailsWith { db.run("EXECUTE p2(1)") } } + @Test + fun aNameCanBeReparedAfterBeingDeallocated() { + val db = newDatabase() + db.seedUsers() + db.run("PREPARE byId AS SELECT name FROM users WHERE id = \$1") + db.run("DEALLOCATE byId") + db.run("PREPARE byId AS SELECT age FROM users WHERE id = \$1") + assertEquals(listOf(listOf(int(36))), db.select("EXECUTE byId(1)").rows) + } + + @Test + fun preparedStatementsAreNotUndoneByRollback() { + val db = newDatabase() + db.seedUsers() + db.run("BEGIN") + db.run("PREPARE byId AS SELECT name FROM users WHERE id = \$1") + db.run("ROLLBACK") + // Session-scoped, deliberately outside the storage-engine transaction that ROLLBACK undoes - + // see DiskExecutor's own doc on preparedStatements for why. + assertEquals(listOf(listOf(text("ada"))), db.select("EXECUTE byId(1)").rows) + } + + @Test + fun executeArgumentsMustBeConstantNotColumnReferences() { + val db = newDatabase() + db.seedUsers() + db.run("PREPARE byId AS SELECT name FROM users WHERE id = \$1") + // The bare Evaluator() resolveExecute uses to evaluate EXECUTE arguments as constants is + // shared with the in-memory engine and throws the shared KestrelException directly, not a + // DiskExecutorException - unlike disk-executor-specific validation elsewhere in this class. + assertFailsWith { db.run("EXECUTE byId(id)") } + } + + @Test + fun preparedStatementDoesNotSurviveReopen() { + val db = newDatabase() + db.seedUsers() + db.run("PREPARE byId AS SELECT name FROM users WHERE id = \$1") + assertEquals(listOf(listOf(text("ada"))), db.select("EXECUTE byId(1)").rows) + + db.reopen() + assertFailsWith { db.run("EXECUTE byId(1)") } + } + + @Test + fun aPreparedInsertsRowSurvivesReopenEvenThoughThePrepareItselfDoesNot() { + val db = newDatabase() + db.run("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL, age INTEGER DEFAULT 0)") + db.run("PREPARE addUser AS INSERT INTO users VALUES (\$1, \$2, \$3)") + db.run("EXECUTE addUser(1, 'ada', 36)") + + db.reopen() + assertEquals(listOf(listOf(int(1), text("ada"), int(36))), db.select("SELECT * FROM users").rows) + assertFailsWith { db.run("EXECUTE addUser(2, 'linus', 54)") } + } + + /** + * `CREATE TEMP TABLE`/`CREATE TEMPORARY TABLE` used to be rejected outright here - see + * [DiskExecutorTemporaryTableTest] for the full feature (now a real, live in-memory-only table + * on this executor) this pair of tests only confirms is reachable through both spellings. + */ + @Test + fun createTempTableIsAcceptedByTheDiskBackedExecutor() { + val db = newDatabase() + assertEquals(CreateTableResult("scratch"), db.run("CREATE TEMP TABLE scratch (id INTEGER PRIMARY KEY)").single()) + db.run("INSERT INTO scratch VALUES (1)") + assertEquals(listOf(listOf(int(1))), db.select("SELECT * FROM scratch").rows) + } + + @Test + fun createTemporaryTableIsAcceptedByTheDiskBackedExecutor() { + val db = newDatabase() + assertEquals(CreateTableResult("scratch"), db.run("CREATE TEMPORARY TABLE scratch (id INTEGER PRIMARY KEY)").single()) + } +}