From e748bac1ad10471d2642089b33104f6c5709f9be Mon Sep 17 00:00:00 2001 From: DualFroz Date: Tue, 8 Sep 2026 21:04:57 +0200 Subject: [PATCH] test: cover DerivedTableTest and run --- src/test/kotlin/kestrel/DerivedTableTest.kt | 114 ++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 src/test/kotlin/kestrel/DerivedTableTest.kt diff --git a/src/test/kotlin/kestrel/DerivedTableTest.kt b/src/test/kotlin/kestrel/DerivedTableTest.kt new file mode 100644 index 0000000..c1164a9 --- /dev/null +++ b/src/test/kotlin/kestrel/DerivedTableTest.kt @@ -0,0 +1,114 @@ +package kestrel + +import kestrel.catalog.Catalog +import kestrel.engine.Executor +import kestrel.engine.SelectResult +import kestrel.engine.StatementResult +import kestrel.sql.lexer.Lexer +import kestrel.sql.parser.Parser +import kestrel.storage.Value +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith + +class DerivedTableTest { + private val catalog = Catalog() + private val executor = Executor(catalog) + + private fun run(sql: String): List = + Parser(Lexer(sql).tokenize()).parseProgram().map { executor.execute(it) } + + private fun select(sql: String): SelectResult = run(sql).single() as SelectResult + + private fun int(value: Long) = Value.IntegerValue(value) + private fun text(value: String) = Value.TextValue(value) + + private fun seedUsers() { + run("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL, age INTEGER, dept TEXT)") + run( + "INSERT INTO users VALUES " + + "(1, 'ada', 36, 'eng'), (2, 'linus', 54, 'eng'), (3, 'grace', 85, 'navy'), (4, 'alan', 41, NULL)" + ) + } + + @Test + fun selectsFromASimpleDerivedTable() { + seedUsers() + val result = select("SELECT s.name FROM (SELECT id, name FROM users WHERE age >= 40) AS s ORDER BY s.id ASC") + assertEquals(listOf(text("linus"), text("grace"), text("alan")), result.rows.map { it.single() }) + } + + @Test + fun derivedTableOverAnAggregateQueryWorks() { + seedUsers() + val result = select( + "SELECT d.dept, d.n FROM (SELECT dept, COUNT(*) AS n FROM users GROUP BY dept) AS d " + + "WHERE d.n > 1 ORDER BY d.dept ASC" + ) + assertEquals(listOf(listOf(text("eng"), int(2))), result.rows) + } + + @Test + fun derivedTableCanBeJoinedWithARealTable() { + seedUsers() + run("CREATE TABLE bonuses (dept TEXT, amount INTEGER)") + run("INSERT INTO bonuses VALUES ('eng', 1000), ('navy', 500)") + val result = select( + "SELECT d.dept, b.amount FROM (SELECT dept, COUNT(*) AS n FROM users GROUP BY dept) AS d " + + "JOIN bonuses AS b ON d.dept = b.dept ORDER BY d.dept ASC" + ) + assertEquals(listOf(listOf(text("eng"), int(1000)), listOf(text("navy"), int(500))), result.rows) + } + + @Test + fun twoDerivedTablesCanBeJoinedTogether() { + seedUsers() + val result = select( + "SELECT a.dept, a.n FROM (SELECT dept, COUNT(*) AS n FROM users GROUP BY dept) AS a " + + "JOIN (SELECT dept, COUNT(*) AS n FROM users GROUP BY dept) AS b ON a.dept = b.dept " + + "WHERE a.n = b.n ORDER BY a.dept ASC" + ) + assertEquals(listOf(listOf(text("eng"), int(2)), listOf(text("navy"), int(1))), result.rows) + } + + @Test + fun derivedTableColumnsAreScopedToItsAliasNotTheUnderlyingTableName() { + seedUsers() + assertFailsWith { run("SELECT users.name FROM (SELECT id, name FROM users) AS s") } + } + + @Test + fun derivedTableIsReEvaluatedEachQueryReflectingLiveData() { + seedUsers() + val before = select("SELECT COUNT(*) AS n FROM (SELECT id FROM users WHERE dept = 'eng') AS e") + assertEquals(listOf(listOf(int(2))), before.rows) + run("INSERT INTO users VALUES (5, 'margaret', 61, 'eng')") + val after = select("SELECT COUNT(*) AS n FROM (SELECT id FROM users WHERE dept = 'eng') AS e") + assertEquals(listOf(listOf(int(3))), after.rows) + } + + @Test + fun derivedTableCanContainACorrelatedSubqueryOfItsOwn() { + seedUsers() + val result = select( + "SELECT s.name FROM (SELECT id, name, dept FROM users u WHERE age > " + + "(SELECT AVG(age) FROM users u2 WHERE u2.dept = u.dept)) AS s ORDER BY s.id ASC" + ) + assertEquals(listOf(text("linus")), result.rows.map { it.single() }) + } + + @Test + fun explainDescribesADerivedTableScan() { + seedUsers() + val explain = run("EXPLAIN SELECT s.name FROM (SELECT id, name FROM users) AS s").single() as kestrel.engine.ExplainResult + assertEquals(true, explain.plan.any { it.contains("Seq Scan on s") }) + } + + @Test + fun creatingAViewFromADerivedTableWorks() { + seedUsers() + run("CREATE VIEW dept_counts AS SELECT d.dept, d.n FROM (SELECT dept, COUNT(*) AS n FROM users GROUP BY dept) AS d") + val result = select("SELECT dept, n FROM dept_counts WHERE n > 1") + assertEquals(listOf(listOf(text("eng"), int(2))), result.rows) + } +}