test: cover renameColumnUpdatesAnIndexAndThePlannerStillUsesIt

This commit is contained in:
dualfroz committed 2026-09-08 16:20:30 +02:00
1 parent d53bea481c
commit f8d57f5621
1 file changed
+157
+157
View File
@@ -313,3 +313,160 @@ class AlterTableTest {
assertEquals(listOf(listOf(int(30))), select("SELECT years FROM t").rows)
}
@Test
fun renameColumnUpdatesAnIndexAndThePlannerStillUsesIt() {
run("CREATE TABLE t (id INTEGER PRIMARY KEY, age INTEGER)")
run("INSERT INTO t VALUES (1, 30), (2, 40)")
run("CREATE INDEX idx_age ON t (age)")
run("ALTER TABLE t RENAME COLUMN age TO years")
val plan = explain("EXPLAIN SELECT id FROM t WHERE years = 30").joinToString("\n")
assertTrue(plan.contains("Index Scan using idx_age"), plan)
assertEquals(listOf(listOf(int(1))), select("SELECT id FROM t WHERE years = 30").rows)
}
// ---- RENAME TABLE ----
@Test
fun renameTableRenamesAndPreservesData() {
run("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT)")
run("INSERT INTO t VALUES (1, 'ada')")
val result = exec("ALTER TABLE t RENAME TO people")
assertEquals(AlterTableResult("people"), result)
assertEquals(listOf(listOf(int(1), text("ada"))), select("SELECT * FROM people").rows)
assertFailsWith<KestrelException> { run("SELECT * FROM t") }
assertEquals(listOf("people"), catalog.tableNames)
}
@Test
fun renameTableRejectsWhenNewNameAlreadyExistsAsTable() {
run("CREATE TABLE t (id INTEGER PRIMARY KEY)")
run("CREATE TABLE other (id INTEGER PRIMARY KEY)")
val error = assertFailsWith<KestrelException> { run("ALTER TABLE t RENAME TO other") }
assertTrue(error.message!!.contains("already exists"))
}
@Test
fun renameTableRejectsWhenNewNameAlreadyExistsAsView() {
run("CREATE TABLE t (id INTEGER PRIMARY KEY)")
run("CREATE TABLE other (id INTEGER PRIMARY KEY)")
run("CREATE VIEW v AS SELECT * FROM other")
val error = assertFailsWith<KestrelException> { run("ALTER TABLE t RENAME TO v") }
assertTrue(error.message!!.contains("already exists"))
}
@Test
fun renameTableRejectsRenamingAView() {
run("CREATE TABLE t (id INTEGER PRIMARY KEY)")
run("CREATE VIEW v AS SELECT * FROM t")
val error = assertFailsWith<KestrelException> { run("ALTER TABLE v RENAME TO w") }
assertTrue(error.message!!.contains("cannot modify view"))
}
@Test
fun renameTablePropagatesToOtherTablesForeignKeyReference() {
run("CREATE TABLE departments (id INTEGER PRIMARY KEY)")
run("CREATE TABLE employees (id INTEGER PRIMARY KEY, dept_id INTEGER REFERENCES departments(id) ON DELETE CASCADE)")
run("INSERT INTO departments VALUES (1)")
run("INSERT INTO employees VALUES (1, 1)")
run("ALTER TABLE departments RENAME TO divisions")
run("INSERT INTO divisions VALUES (2)")
run("INSERT INTO employees VALUES (2, 2)")
assertFailsWith<KestrelException> { run("INSERT INTO employees VALUES (3, 99)") }
run("DELETE FROM divisions WHERE id = 1")
assertEquals(listOf(listOf(int(2))), select("SELECT id FROM employees").rows)
}
@Test
fun renameTableUpdatesItsOwnSelfReferencingForeignKey() {
run(
"CREATE TABLE staff (id INTEGER PRIMARY KEY, manager_id INTEGER REFERENCES staff(id))"
)
run("INSERT INTO staff VALUES (1, NULL)")
run("ALTER TABLE staff RENAME TO personnel")
run("INSERT INTO personnel VALUES (2, 1)")
assertFailsWith<KestrelException> { run("INSERT INTO personnel VALUES (3, 99)") }
assertEquals(2, select("SELECT * FROM personnel").rows.size)
}
@Test
fun renameTableUpdatesAnIndexAndThePlannerStillUsesIt() {
run("CREATE TABLE t (id INTEGER PRIMARY KEY, age INTEGER)")
run("INSERT INTO t VALUES (1, 30), (2, 40)")
run("CREATE INDEX idx_age ON t (age)")
run("ALTER TABLE t RENAME TO people")
val plan = explain("EXPLAIN SELECT id FROM people WHERE age = 30").joinToString("\n")
assertTrue(plan.contains("Index Scan using idx_age on people"), plan)
assertEquals(listOf(listOf(int(1))), select("SELECT id FROM people WHERE age = 30").rows)
}
@Test
fun renameTablePropagatesIntoASimpleViewDefinition() {
run("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT)")
run("INSERT INTO t VALUES (1, 'ada')")
run("CREATE VIEW v AS SELECT * FROM t")
run("ALTER TABLE t RENAME TO people")
assertEquals(listOf(listOf(int(1), text("ada"))), select("SELECT * FROM v").rows)
}
@Test
fun renameTablePropagatesIntoAViewJoinAndASubqueryExpression() {
run("CREATE TABLE departments (id INTEGER PRIMARY KEY, name TEXT)")
run("CREATE TABLE employees (id INTEGER PRIMARY KEY, dept_id INTEGER)")
run("INSERT INTO departments VALUES (1, 'eng')")
run("INSERT INTO employees VALUES (1, 1)")
run(
"CREATE VIEW v AS SELECT e.id FROM employees e JOIN departments d ON e.dept_id = d.id " +
"WHERE d.id IN (SELECT id FROM departments)"
)
run("ALTER TABLE departments RENAME TO divisions")
assertEquals(listOf(listOf(int(1))), select("SELECT * FROM v").rows)
}
@Test
fun renameTablePropagatesIntoADerivedTableAndAUnion() {
run("CREATE TABLE t (id INTEGER PRIMARY KEY)")
run("INSERT INTO t VALUES (1), (2)")
run(
"CREATE VIEW v AS SELECT * FROM (SELECT id FROM t) sub " +
"UNION SELECT id FROM t WHERE id = 2"
)
run("ALTER TABLE t RENAME TO renamed")
assertEquals(listOf(listOf(int(1)), listOf(int(2))), select("SELECT * FROM v ORDER BY id").rows)
}
@Test
fun renameTableDoesNotTouchATableSourceShadowedByACteOfTheSameName() {
run("CREATE TABLE t (id INTEGER PRIMARY KEY)")
run("INSERT INTO t VALUES (1)")
run("CREATE VIEW v AS WITH t AS (SELECT 99 AS id FROM t) SELECT * FROM t")
run("ALTER TABLE t RENAME TO renamed")
// The CTE named "t" shadows the base table for the rest of that WITH clause, so the view
// must still yield the CTE's own row (99), not read from the (now renamed) base table.
assertEquals(listOf(listOf(int(99))), select("SELECT * FROM v").rows)
assertEquals(listOf(listOf(int(1))), select("SELECT * FROM renamed").rows)
}
@Test
fun renameTableRenamingToItsOwnNameIsANoOp() {
run("CREATE TABLE t (id INTEGER PRIMARY KEY)")
run("INSERT INTO t VALUES (1)")
run("ALTER TABLE t RENAME TO t")
assertEquals(listOf(listOf(int(1))), select("SELECT * FROM t").rows)
}
// ---- DEFAULT (CREATE TABLE + INSERT) ----
@Test
fun insertUsesColumnDefaultWhenColumnOmittedFromColumnList() {
run("CREATE TABLE t (id INTEGER PRIMARY KEY, status TEXT DEFAULT 'pending')")
run("INSERT INTO t (id) VALUES (1)")
assertEquals(listOf(listOf(text("pending"))), select("SELECT status FROM t").rows)
}
@Test
fun insertDoesNotUseDefaultWhenExplicitNullIsGiven() {
run("CREATE TABLE t (id INTEGER PRIMARY KEY, status TEXT DEFAULT 'pending')")
run("INSERT INTO t (id, status) VALUES (1, NULL)")
assertEquals(listOf(listOf(Value.NullValue)), select("SELECT status FROM t").rows)
}