test(sql): cover dropColumnRestrictsWhenUsedByItsOwnForeignKey
This commit is contained in:
1 parent
22f982501c
commit
decfe2c8d7
1 file changed
+158
@@ -3488,3 +3488,161 @@ class DiskExecutorTest {
|
|||||||
assertEquals(listOf(listOf(text("ghost"))), db.select("SELECT dept_code FROM employees").rows)
|
assertEquals(listOf(listOf(text("ghost"))), db.select("SELECT dept_code FROM employees").rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dropColumnRestrictsWhenUsedByItsOwnForeignKey() {
|
||||||
|
val db = newDatabase()
|
||||||
|
db.run("CREATE TABLE departments (id INTEGER PRIMARY KEY)")
|
||||||
|
db.run("CREATE TABLE employees (id INTEGER PRIMARY KEY, dept_id INTEGER REFERENCES departments(id))")
|
||||||
|
val error = assertFailsWith<DiskCatalogException> { db.run("ALTER TABLE employees DROP COLUMN dept_id") }
|
||||||
|
assertTrue(error.message!!.contains("FOREIGN KEY"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dropColumnCascadeRemovesItsOwnForeignKey() {
|
||||||
|
val db = newDatabase()
|
||||||
|
db.run("CREATE TABLE departments (id INTEGER PRIMARY KEY)")
|
||||||
|
db.run("CREATE TABLE employees (id INTEGER PRIMARY KEY, dept_id INTEGER REFERENCES departments(id))")
|
||||||
|
db.run("ALTER TABLE employees DROP COLUMN dept_id CASCADE")
|
||||||
|
assertEquals(listOf("id"), db.select("SELECT * FROM employees").columns)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dropColumnRestrictsWhenUsedByAUniqueConstraint() {
|
||||||
|
val db = newDatabase()
|
||||||
|
db.run("CREATE TABLE t (id INTEGER PRIMARY KEY, email TEXT UNIQUE)")
|
||||||
|
val error = assertFailsWith<DiskCatalogException> { db.run("ALTER TABLE t DROP COLUMN email") }
|
||||||
|
assertTrue(error.message!!.contains("UNIQUE"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dropColumnCascadeRemovesTheUniqueConstraint() {
|
||||||
|
val db = newDatabase()
|
||||||
|
db.run("CREATE TABLE t (id INTEGER PRIMARY KEY, email TEXT UNIQUE)")
|
||||||
|
db.run("INSERT INTO t VALUES (1, 'a@x.com')")
|
||||||
|
db.run("ALTER TABLE t DROP COLUMN email CASCADE")
|
||||||
|
db.run("INSERT INTO t VALUES (2)")
|
||||||
|
assertEquals(2, db.select("SELECT * FROM t").rows.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dropColumnRestrictsWhenUsedByACheckConstraint() {
|
||||||
|
val db = newDatabase()
|
||||||
|
db.run("CREATE TABLE t (id INTEGER PRIMARY KEY, age INTEGER CHECK (age > 0))")
|
||||||
|
val error = assertFailsWith<DiskCatalogException> { db.run("ALTER TABLE t DROP COLUMN age") }
|
||||||
|
assertTrue(error.message!!.contains("CHECK"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dropColumnCascadeRemovesTheCheckConstraint() {
|
||||||
|
val db = newDatabase()
|
||||||
|
db.run("CREATE TABLE t (id INTEGER PRIMARY KEY, age INTEGER CHECK (age > 0))")
|
||||||
|
db.run("ALTER TABLE t DROP COLUMN age CASCADE")
|
||||||
|
db.run("INSERT INTO t VALUES (1)")
|
||||||
|
assertEquals(1, db.select("SELECT * FROM t").rows.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dropColumnRestrictsWhenUsedByAnIndex() {
|
||||||
|
val db = newDatabase()
|
||||||
|
db.run("CREATE TABLE t (id INTEGER PRIMARY KEY, age INTEGER)")
|
||||||
|
db.run("CREATE INDEX idx_age ON t (age)")
|
||||||
|
val error = assertFailsWith<DiskCatalogException> { db.run("ALTER TABLE t DROP COLUMN age") }
|
||||||
|
assertTrue(error.message!!.contains("idx_age"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Beyond mirroring the in-memory engine's own coverage, this also proves the position
|
||||||
|
* realignment [DiskTable.rewriteRows] performs for every surviving secondary index: `dept`
|
||||||
|
* shifts from column position 2 to 1 once `age` (position 1) is dropped, and `idx_dept` must
|
||||||
|
* keep resolving against the new position rather than the stale one it was built with.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun dropColumnCascadeRemovesTheIndexAndASurvivingLaterIndexStillResolvesAtItsShiftedPosition() {
|
||||||
|
val db = newDatabase()
|
||||||
|
db.run("CREATE TABLE t (id INTEGER PRIMARY KEY, age INTEGER, dept TEXT)")
|
||||||
|
db.run("INSERT INTO t VALUES (1, 30, 'eng'), (2, 40, 'ops')")
|
||||||
|
db.run("CREATE INDEX idx_age ON t (age)")
|
||||||
|
db.run("CREATE INDEX idx_dept ON t (dept)")
|
||||||
|
db.run("ALTER TABLE t DROP COLUMN age CASCADE")
|
||||||
|
|
||||||
|
val table = db.catalog.table("t")
|
||||||
|
assertEquals(listOf("idx_dept"), table.secondaryIndexes.map { it.name })
|
||||||
|
val result = db.select("SELECT id FROM t WHERE dept = 'ops'")
|
||||||
|
assertEquals(listOf(listOf(int(2))), result.rows)
|
||||||
|
assertEquals(1, table.secondaryLookupCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dropColumnDefinitionAndRemainingDataSurviveClosingAndReopeningTheCatalog() {
|
||||||
|
val db = newDatabase()
|
||||||
|
db.run("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT, bio TEXT)")
|
||||||
|
db.run("INSERT INTO t VALUES (1, 'ada', 'mathematician')")
|
||||||
|
db.run("ALTER TABLE t DROP COLUMN bio")
|
||||||
|
db.reopen()
|
||||||
|
assertEquals(listOf("id", "name"), db.select("SELECT * FROM t").columns)
|
||||||
|
assertEquals(listOf(listOf(int(1), text("ada"))), db.select("SELECT * FROM t").rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- ALTER TABLE RENAME COLUMN ----
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun renameColumnRenamesSchemaAndPreservesData() {
|
||||||
|
val db = newDatabase()
|
||||||
|
db.run("CREATE TABLE t (id INTEGER PRIMARY KEY, bio TEXT)")
|
||||||
|
db.run("INSERT INTO t VALUES (1, 'mathematician')")
|
||||||
|
db.run("ALTER TABLE t RENAME COLUMN bio TO biography")
|
||||||
|
val result = db.select("SELECT * FROM t")
|
||||||
|
assertEquals(listOf("id", "biography"), result.columns)
|
||||||
|
assertEquals(listOf(listOf(int(1), text("mathematician"))), result.rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun renameColumnRejectsUnknownColumn() {
|
||||||
|
val db = newDatabase()
|
||||||
|
db.run("CREATE TABLE t (id INTEGER PRIMARY KEY)")
|
||||||
|
assertFailsWith<DiskCatalogException> { db.run("ALTER TABLE t RENAME COLUMN missing TO whatever") }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun renameColumnRejectsWhenNewNameAlreadyExists() {
|
||||||
|
val db = newDatabase()
|
||||||
|
db.run("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT)")
|
||||||
|
val error = assertFailsWith<DiskCatalogException> { db.run("ALTER TABLE t RENAME COLUMN name TO id") }
|
||||||
|
assertTrue(error.message!!.contains("already exists"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun renameColumnUpdatesCompositePrimaryKeyListing() {
|
||||||
|
val db = newDatabase()
|
||||||
|
db.run("CREATE TABLE t (region TEXT, seq INTEGER, PRIMARY KEY (region, seq))")
|
||||||
|
db.run("INSERT INTO t VALUES ('eu', 1)")
|
||||||
|
db.run("ALTER TABLE t RENAME COLUMN region TO area")
|
||||||
|
assertFailsWith<DiskTableException> { db.run("INSERT INTO t VALUES ('eu', 1)") }
|
||||||
|
db.run("INSERT INTO t VALUES ('eu', 2)")
|
||||||
|
assertEquals(2, db.select("SELECT * FROM t").rows.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun renameColumnUpdatesItsOwnForeignKeyColumns() {
|
||||||
|
val db = newDatabase()
|
||||||
|
db.run("CREATE TABLE departments (id INTEGER PRIMARY KEY)")
|
||||||
|
db.run("CREATE TABLE employees (id INTEGER PRIMARY KEY, dept_id INTEGER REFERENCES departments(id))")
|
||||||
|
db.run("INSERT INTO departments VALUES (1)")
|
||||||
|
db.run("ALTER TABLE employees RENAME COLUMN dept_id TO department_id")
|
||||||
|
assertFailsWith<KestrelException> { db.run("INSERT INTO employees VALUES (1, 99)") }
|
||||||
|
db.run("INSERT INTO employees VALUES (1, 1)")
|
||||||
|
assertEquals(1, db.select("SELECT * FROM employees").rows.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun renameColumnPropagatesToOtherTablesForeignKeyReferencedColumns() {
|
||||||
|
val db = newDatabase()
|
||||||
|
db.run("CREATE TABLE departments (id INTEGER PRIMARY KEY)")
|
||||||
|
db.run("CREATE TABLE employees (id INTEGER PRIMARY KEY, dept_id INTEGER REFERENCES departments(id) ON DELETE CASCADE)")
|
||||||
|
db.run("INSERT INTO departments VALUES (1)")
|
||||||
|
db.run("INSERT INTO employees VALUES (1, 1)")
|
||||||
|
db.run("ALTER TABLE departments RENAME COLUMN id TO dept_pk")
|
||||||
|
db.run("DELETE FROM departments WHERE dept_pk = 1")
|
||||||
|
assertTrue(db.select("SELECT * FROM employees").rows.isEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
Reference in new issue
Block a user