From 18deaa3ca0977f9d613c4e461211649c61aa06d6 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Thu, 10 Sep 2026 20:01:04 +0200 Subject: [PATCH] test: cover insertUpdateDeleteAgainstAViewAreRejected --- src/test/kotlin/kestrel/ViewTest.kt | 131 ++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) diff --git a/src/test/kotlin/kestrel/ViewTest.kt b/src/test/kotlin/kestrel/ViewTest.kt index e8a0313..a819437 100644 --- a/src/test/kotlin/kestrel/ViewTest.kt +++ b/src/test/kotlin/kestrel/ViewTest.kt @@ -155,3 +155,134 @@ class ViewTest { assertFailsWith { run("DROP VIEW users") } } + @Test + fun insertUpdateDeleteAgainstAViewAreRejected() { + seedUsers() + run("CREATE VIEW adults AS SELECT id, name FROM users WHERE age >= 18") + assertFailsWith { run("INSERT INTO adults VALUES (9, 'x')") } + assertFailsWith { run("UPDATE adults SET name = 'x'") } + assertFailsWith { run("DELETE FROM adults") } + } + + @Test + fun creatingAnIndexOnAViewIsRejected() { + seedUsers() + run("CREATE VIEW adults AS SELECT id, name FROM users WHERE age >= 18") + assertFailsWith { run("CREATE INDEX idx ON adults (id)") } + } + + @Test + fun mutuallyRecursiveViewsFailInsteadOfOverflowingTheStack() { + seedUsers() + run("CREATE VIEW a AS SELECT id FROM users") + run("CREATE VIEW b AS SELECT id FROM a") + run("CREATE OR REPLACE VIEW a AS SELECT id FROM b") + assertFailsWith { run("SELECT * FROM a") } + } + + @Test + fun selfReferencingViewFailsCleanly() { + seedUsers() + run("CREATE VIEW a AS SELECT id FROM users") + run("CREATE OR REPLACE VIEW a AS SELECT id FROM a") + assertFailsWith { run("SELECT * FROM a") } + } + + @Test + fun rollbackUndoesCreateViewAndDropView() { + seedUsers() + run("CREATE VIEW adults AS SELECT id FROM users WHERE age >= 18") + run("BEGIN") + run("CREATE VIEW seniors AS SELECT id FROM users WHERE age >= 60") + run("DROP VIEW adults") + run("ROLLBACK") + assertEquals(listOf("adults"), catalog.viewNames) + } + + @Test + fun commitKeepsViewChanges() { + seedUsers() + run("BEGIN") + run("CREATE VIEW adults AS SELECT id FROM users WHERE age >= 18") + run("COMMIT") + assertEquals(listOf("adults"), catalog.viewNames) + } + + @Test + fun dropTableWithADependentViewFailsByDefaultAndAlsoWithExplicitRestrict() { + seedUsers() + run("CREATE VIEW adults AS SELECT id FROM users WHERE age >= 18") + assertFailsWith { run("DROP TABLE users") } + assertFailsWith { run("DROP TABLE users RESTRICT") } + assertEquals(listOf("users"), catalog.tableNames) + assertEquals(listOf("adults"), catalog.viewNames) + } + + @Test + fun dropTableCascadeDropsTheTableAndItsDependentView() { + seedUsers() + run("CREATE VIEW adults AS SELECT id FROM users WHERE age >= 18") + run("DROP TABLE users CASCADE") + assertTrue(catalog.tableNames.isEmpty()) + assertTrue(catalog.viewNames.isEmpty()) + } + + @Test + fun dropViewWithADependentViewFailsByDefault() { + seedUsers() + run("CREATE VIEW adults AS SELECT id FROM users WHERE age >= 18") + run("CREATE VIEW seniorAdults AS SELECT id FROM adults WHERE id > 0") + assertFailsWith { run("DROP VIEW adults") } + assertEquals(listOf("adults", "seniorAdults"), catalog.viewNames) + } + + @Test + fun dropViewCascadeDropsATransitiveChainOfDependentViews() { + seedUsers() + run("CREATE VIEW a AS SELECT id FROM users WHERE age >= 18") + run("CREATE VIEW b AS SELECT id FROM a WHERE id > 0") + run("CREATE VIEW c AS SELECT id FROM b WHERE id > 0") + run("DROP VIEW a CASCADE") + assertTrue(catalog.viewNames.isEmpty()) + } + + @Test + fun dropTableCascadeHandlesADiamondOfDependentViewsWithoutFailing() { + seedUsers() + run("CREATE VIEW b AS SELECT id FROM users WHERE age >= 18") + run("CREATE VIEW c AS SELECT id FROM users WHERE age >= 21") + run("CREATE VIEW d AS SELECT b.id FROM b JOIN c ON b.id = c.id") + run("DROP TABLE users CASCADE") + assertTrue(catalog.tableNames.isEmpty()) + assertTrue(catalog.viewNames.isEmpty()) + } + + @Test + fun rollbackUndoesACascadeDrop() { + seedUsers() + run("CREATE VIEW adults AS SELECT id FROM users WHERE age >= 18") + run("BEGIN") + run("DROP TABLE users CASCADE") + run("ROLLBACK") + assertEquals(listOf("users"), catalog.tableNames) + assertEquals(listOf("adults"), catalog.viewNames) + val result = select("SELECT id FROM adults ORDER BY id ASC") + assertTrue(result.rows.isNotEmpty()) + } + + @Test + fun explainOverAViewDescribesTheOuterQuery() { + seedUsers() + run("CREATE VIEW adults AS SELECT id, name FROM users WHERE age >= 18") + val explain = exec("EXPLAIN SELECT * FROM adults") as kestrel.engine.ExplainResult + assertTrue(explain.plan.any { it.contains("adults") }) + } + + @Test + fun columnTypeOfAViewIsInferredFromActualData() { + seedUsers() + run("CREATE VIEW names AS SELECT name FROM users") + val result = select("SELECT name FROM names WHERE name = 'ada'") + assertEquals(listOf(listOf(text("ada"))), result.rows) + } +}