test: cover dropColumnRestrictsWhenReferencedByAnotherTablesForeignKey

This commit is contained in:
dualfroz committed 2026-09-08 16:14:29 +02:00
1 parent 575cb89ab4
commit d53bea481c
1 file changed
+156
+156
View File
@@ -157,3 +157,159 @@ class AlterTableTest {
assertTrue(error.message!!.contains("PRIMARY KEY")) assertTrue(error.message!!.contains("PRIMARY KEY"))
} }
@Test
fun dropColumnRestrictsWhenReferencedByAnotherTablesForeignKey() {
run("CREATE TABLE departments (id INTEGER PRIMARY KEY, code TEXT UNIQUE)")
run("CREATE TABLE employees (id INTEGER PRIMARY KEY, dept_code TEXT REFERENCES departments(code))")
val error = assertFailsWith<KestrelException> { run("ALTER TABLE departments DROP COLUMN code") }
assertTrue(error.message!!.contains("FOREIGN KEY"))
}
@Test
fun dropColumnCascadeRemovesTheReferencingForeignKey() {
run("CREATE TABLE departments (id INTEGER PRIMARY KEY, code TEXT UNIQUE)")
run("CREATE TABLE employees (id INTEGER PRIMARY KEY, dept_code TEXT REFERENCES departments(code))")
run("INSERT INTO departments VALUES (1, 'eng')")
run("ALTER TABLE departments DROP COLUMN code CASCADE")
// The FK is gone, so an employee can now reference a nonexistent department code freely.
run("INSERT INTO employees VALUES (1, 'ghost')")
assertEquals(listOf(listOf(text("ghost"))), select("SELECT dept_code FROM employees").rows)
}
@Test
fun dropColumnRestrictsWhenUsedByItsOwnForeignKey() {
run("CREATE TABLE departments (id INTEGER PRIMARY KEY)")
run("CREATE TABLE employees (id INTEGER PRIMARY KEY, dept_id INTEGER REFERENCES departments(id))")
val error = assertFailsWith<KestrelException> { run("ALTER TABLE employees DROP COLUMN dept_id") }
assertTrue(error.message!!.contains("FOREIGN KEY"))
}
@Test
fun dropColumnCascadeRemovesItsOwnForeignKey() {
run("CREATE TABLE departments (id INTEGER PRIMARY KEY)")
run("CREATE TABLE employees (id INTEGER PRIMARY KEY, dept_id INTEGER REFERENCES departments(id))")
run("ALTER TABLE employees DROP COLUMN dept_id CASCADE")
assertEquals(listOf("id"), select("SELECT * FROM employees").columns)
}
@Test
fun dropColumnRestrictsWhenUsedByAUniqueConstraint() {
run("CREATE TABLE t (id INTEGER PRIMARY KEY, email TEXT UNIQUE)")
val error = assertFailsWith<KestrelException> { run("ALTER TABLE t DROP COLUMN email") }
assertTrue(error.message!!.contains("UNIQUE"))
}
@Test
fun dropColumnCascadeRemovesTheUniqueConstraint() {
run("CREATE TABLE t (id INTEGER PRIMARY KEY, email TEXT UNIQUE)")
run("INSERT INTO t VALUES (1, 'a@x.com')")
run("ALTER TABLE t DROP COLUMN email CASCADE")
run("INSERT INTO t VALUES (2)")
assertEquals(2, select("SELECT * FROM t").rows.size)
}
@Test
fun dropColumnRestrictsWhenUsedByACheckConstraint() {
run("CREATE TABLE t (id INTEGER PRIMARY KEY, age INTEGER CHECK (age > 0))")
val error = assertFailsWith<KestrelException> { run("ALTER TABLE t DROP COLUMN age") }
assertTrue(error.message!!.contains("CHECK"))
}
@Test
fun dropColumnCascadeRemovesTheCheckConstraint() {
run("CREATE TABLE t (id INTEGER PRIMARY KEY, age INTEGER CHECK (age > 0))")
run("ALTER TABLE t DROP COLUMN age CASCADE")
run("INSERT INTO t VALUES (1)")
assertEquals(1, select("SELECT * FROM t").rows.size)
}
@Test
fun dropColumnRestrictsWhenUsedByAnIndex() {
run("CREATE TABLE t (id INTEGER PRIMARY KEY, age INTEGER)")
run("CREATE INDEX idx_age ON t (age)")
val error = assertFailsWith<KestrelException> { run("ALTER TABLE t DROP COLUMN age") }
assertTrue(error.message!!.contains("idx_age"))
}
@Test
fun dropColumnCascadeRemovesTheIndex() {
run("CREATE TABLE t (id INTEGER PRIMARY KEY, age INTEGER)")
run("CREATE INDEX idx_age ON t (age)")
run("ALTER TABLE t DROP COLUMN age CASCADE")
assertTrue(catalog.indexNames.isEmpty())
}
// ---- RENAME COLUMN ----
@Test
fun renameColumnRenamesSchemaAndPreservesData() {
run("CREATE TABLE t (id INTEGER PRIMARY KEY, bio TEXT)")
run("INSERT INTO t VALUES (1, 'mathematician')")
run("ALTER TABLE t RENAME COLUMN bio TO biography")
val result = select("SELECT * FROM t")
assertEquals(listOf("id", "biography"), result.columns)
assertEquals(listOf(listOf(int(1), text("mathematician"))), result.rows)
}
@Test
fun renameColumnRejectsUnknownColumn() {
run("CREATE TABLE t (id INTEGER PRIMARY KEY)")
assertFailsWith<KestrelException> { run("ALTER TABLE t RENAME COLUMN missing TO whatever") }
}
@Test
fun renameColumnRejectsWhenNewNameAlreadyExists() {
run("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT)")
val error = assertFailsWith<KestrelException> { run("ALTER TABLE t RENAME COLUMN name TO id") }
assertTrue(error.message!!.contains("already exists"))
}
@Test
fun renameColumnUpdatesCompositePrimaryKeyListing() {
run("CREATE TABLE t (region TEXT, seq INTEGER, PRIMARY KEY (region, seq))")
run("INSERT INTO t VALUES ('eu', 1)")
run("ALTER TABLE t RENAME COLUMN region TO area")
assertFailsWith<KestrelException> { run("INSERT INTO t VALUES ('eu', 1)") }
run("INSERT INTO t VALUES ('eu', 2)")
assertEquals(2, select("SELECT * FROM t").rows.size)
}
@Test
fun renameColumnUpdatesItsOwnForeignKeyColumns() {
run("CREATE TABLE departments (id INTEGER PRIMARY KEY)")
run("CREATE TABLE employees (id INTEGER PRIMARY KEY, dept_id INTEGER REFERENCES departments(id))")
run("INSERT INTO departments VALUES (1)")
run("ALTER TABLE employees RENAME COLUMN dept_id TO department_id")
assertFailsWith<KestrelException> { run("INSERT INTO employees VALUES (1, 99)") }
run("INSERT INTO employees VALUES (1, 1)")
assertEquals(1, select("SELECT * FROM employees").rows.size)
}
@Test
fun renameColumnPropagatesToOtherTablesForeignKeyReferencedColumns() {
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 COLUMN id TO dept_pk")
run("DELETE FROM departments WHERE dept_pk = 1")
assertTrue(select("SELECT * FROM employees").rows.isEmpty(), "cascade must still work through the renamed referenced column")
}
@Test
fun renameColumnUpdatesAUniqueConstraint() {
run("CREATE TABLE t (id INTEGER PRIMARY KEY, email TEXT UNIQUE)")
run("INSERT INTO t VALUES (1, 'a@x.com')")
run("ALTER TABLE t RENAME COLUMN email TO contact_email")
assertFailsWith<KestrelException> { run("INSERT INTO t VALUES (2, 'a@x.com')") }
}
@Test
fun renameColumnUpdatesACheckConstraintAndStillEnforcesIt() {
run("CREATE TABLE t (id INTEGER PRIMARY KEY, age INTEGER CHECK (age > 0))")
run("ALTER TABLE t RENAME COLUMN age TO years")
assertFailsWith<KestrelException> { run("INSERT INTO t VALUES (1, -5)") }
run("INSERT INTO t VALUES (1, 30)")
assertEquals(listOf(listOf(int(30))), select("SELECT years FROM t").rows)
}