test: cover dumpAndReplayOfALateralViewPreservesTheLateralKeyword

This commit is contained in:
dualfroz committed 2026-09-10 13:39:59 +02:00
1 parent b512c2c5c0
commit d7c5c965a7
1 file changed
+161
+161
View File
@@ -321,3 +321,164 @@ class SqlWriterTest {
assertEquals(listOf(listOf(text("eng"), text("ada")), listOf(text("sales"), text("sam"))), result.rows) assertEquals(listOf(listOf(text("eng"), text("ada")), listOf(text("sales"), text("sam"))), result.rows)
} }
@Test
fun dumpAndReplayOfALateralViewPreservesTheLateralKeyword() {
run("CREATE TABLE departments (id INTEGER PRIMARY KEY, name TEXT)")
run("CREATE TABLE employees (id INTEGER PRIMARY KEY, deptId INTEGER, name TEXT, salary INTEGER)")
run("INSERT INTO departments VALUES (1, 'eng')")
run("INSERT INTO employees VALUES (1, 1, 'ada', 300), (2, 1, 'linus', 250)")
run(
"CREATE VIEW top_earners AS SELECT d.name AS dept, top.name AS emp FROM departments d " +
"JOIN LATERAL (SELECT e.name FROM employees e WHERE e.deptId = d.id " +
"ORDER BY e.salary DESC LIMIT 1) AS top ON TRUE"
)
val dump = SqlWriter.dump(catalog)
assertTrue(dump.contains("LATERAL ("), dump)
val freshCatalog = Catalog()
val freshExecutor = Executor(freshCatalog)
Parser(Lexer(dump).tokenize()).parseProgram().forEach { freshExecutor.execute(it) }
val result = Parser(Lexer("SELECT emp FROM top_earners").tokenize()).parseProgram().single()
.let { freshExecutor.execute(it) } as SelectResult
assertEquals(listOf(text("ada")), result.rows.single())
}
@Test
fun createViewOverARightFullAndCrossJoinRoundTripsThroughSqlWriterWithCorrectKeywords() {
run("CREATE TABLE customers (id INTEGER PRIMARY KEY, name TEXT)")
run("CREATE TABLE orders3 (id INTEGER PRIMARY KEY, customerId INTEGER)")
run("INSERT INTO customers VALUES (1, 'ada'), (2, 'grace')")
// customerId 99 matches no customer, and customer 'grace' (id 2) has no order - so a plain
// INNER JOIN would drop both, letting RIGHT/FULL prove they actually kept what INNER cannot.
run("INSERT INTO orders3 VALUES (10, 1), (11, 99)")
val rightStatement = parseOne("CREATE VIEW r AS SELECT customers.name, orders3.id FROM customers RIGHT JOIN orders3 ON customers.id = orders3.customerId")
val rightRendered = SqlWriter.write(rightStatement)
assertTrue(rightRendered.contains("RIGHT JOIN"), rightRendered)
run(rightRendered)
assertEquals(
setOf(listOf(text("ada"), Value.IntegerValue(10)), listOf(Value.NullValue, Value.IntegerValue(11))),
select("SELECT name, id FROM r").rows.toSet()
)
val fullStatement = parseOne("CREATE VIEW f AS SELECT customers.name, orders3.id FROM customers FULL JOIN orders3 ON customers.id = orders3.customerId")
val fullRendered = SqlWriter.write(fullStatement)
assertTrue(fullRendered.contains("FULL JOIN"), fullRendered)
run(fullRendered)
assertEquals(3, select("SELECT name, id FROM f").rows.size)
val crossStatement = parseOne("CREATE VIEW x AS SELECT customers.name, orders3.id FROM customers CROSS JOIN orders3")
val crossRendered = SqlWriter.write(crossStatement)
assertTrue(crossRendered.contains("CROSS JOIN"), crossRendered)
assertTrue(!crossRendered.contains(" ON "), crossRendered)
run(crossRendered)
assertEquals(4, select("SELECT name, id FROM x").rows.size)
}
@Test
fun createViewOverAUnionQueryRoundTripsThroughSqlWriter() {
seed()
writeThenReplay(
"CREATE VIEW letters AS SELECT category FROM t WHERE id = 1 UNION SELECT category FROM t WHERE id = 2"
)
val result = select("SELECT category FROM letters ORDER BY category")
assertEquals(listOf(listOf(text("a")), listOf(text("b"))), result.rows)
}
@Test
fun createViewOverAnIntersectAllQueryRoundTripsThroughSqlWriter() {
run("CREATE TABLE bag_a (v INTEGER)")
run("CREATE TABLE bag_b (v INTEGER)")
run("INSERT INTO bag_a VALUES (1), (1), (2)")
run("INSERT INTO bag_b VALUES (1), (2), (2)")
writeThenReplay("CREATE VIEW shared AS SELECT v FROM bag_a INTERSECT ALL SELECT v FROM bag_b")
val result = select("SELECT v FROM shared ORDER BY v")
assertEquals(listOf(listOf(Value.IntegerValue(1)), listOf(Value.IntegerValue(2))), result.rows)
}
@Test
fun createViewOverARecursiveCteRoundTripsThroughSqlWriter() {
run("CREATE TABLE dual (x INTEGER)")
run("INSERT INTO dual VALUES (1)")
writeThenReplay(
"CREATE VIEW counter_view AS WITH RECURSIVE counter(n) AS (" +
"SELECT 1 FROM dual UNION ALL SELECT n + 1 FROM counter WHERE n < 3" +
") SELECT n FROM counter"
)
val result = select("SELECT n FROM counter_view ORDER BY n")
assertEquals(listOf(listOf(Value.IntegerValue(1)), listOf(Value.IntegerValue(2)), listOf(Value.IntegerValue(3))), result.rows)
}
@Test
fun dumpIncludesViewDefinitionsAfterTablesAndIndexes() {
seed()
run("CREATE VIEW letters AS SELECT id, category FROM t WHERE category IS NOT NULL")
val dump = SqlWriter.dump(catalog)
assertTrue(dump.contains("CREATE VIEW letters AS"))
assertTrue(dump.indexOf("CREATE TABLE t") < dump.indexOf("CREATE VIEW letters"))
}
@Test
fun createTableWithDecimalAndDateColumnsRoundTripsThroughSqlWriter() {
val rendered = SqlWriter.write(parseOne("CREATE TABLE priced (amount DECIMAL(10, 2), born DATE)"))
assertTrue(rendered.contains("DECIMAL(10, 2)"))
assertTrue(rendered.contains("DATE"))
writeThenReplay("CREATE TABLE priced (id INTEGER PRIMARY KEY, amount DECIMAL(10, 2), born DATE)")
run("INSERT INTO priced VALUES (1, 5, CAST('2024-01-01' AS DATE))")
val result = select("SELECT amount FROM priced")
assertEquals(listOf(listOf(Value.DecimalValue(BigDecimal("5.00")))), result.rows)
}
@Test
fun castExpressionRoundTripsThroughSqlWriter() {
seed()
writeThenReplay("UPDATE t SET category = CAST(id AS TEXT) WHERE id = 1")
assertEquals(listOf(listOf(text("1"))), select("SELECT category FROM t WHERE id = 1").rows)
}
@Test
fun dropTableAndDropViewCascadeRoundTripThroughSqlWriter() {
seed()
run("CREATE VIEW letters AS SELECT id FROM t")
writeThenReplay("DROP TABLE t CASCADE")
assertTrue(catalog.tableNames.isEmpty())
assertTrue(catalog.viewNames.isEmpty())
}
@Test
fun dumpAndReplayPreservesDecimalAndDateValuesExactly() {
run("CREATE TABLE priced (id INTEGER PRIMARY KEY, amount DECIMAL(12, 4), born DATE)")
run("INSERT INTO priced VALUES (1, CAST('123456.7890' AS DECIMAL(12, 4)), CAST('2024-01-15' AS DATE))")
val dump = SqlWriter.dump(catalog)
val freshCatalog = Catalog()
val freshExecutor = Executor(freshCatalog)
Parser(Lexer(dump).tokenize()).parseProgram().forEach { freshExecutor.execute(it) }
val result = freshExecutor.execute(
Parser(Lexer("SELECT amount, born FROM priced").tokenize()).parseProgram().single()
) as SelectResult
assertEquals(
listOf(listOf(Value.DecimalValue(BigDecimal("123456.7890")), Value.DateValue(LocalDate.of(2024, 1, 15)))),
result.rows
)
}
@Test
fun createTableWithForeignKeyAndCheckRoundTripsThroughSqlWriter() {
run(SqlWriter.write(parseOne("CREATE TABLE departments (id INTEGER PRIMARY KEY, name TEXT)")))
run(
SqlWriter.write(
parseOne(
"CREATE TABLE employees (id INTEGER PRIMARY KEY, dept_id INTEGER " +
"REFERENCES departments(id) ON DELETE CASCADE, age INTEGER CHECK (age > 0))"
)
)
)
run("INSERT INTO departments VALUES (1, 'eng')")
run("INSERT INTO employees VALUES (1, 1, 30)")
assertFailsWith<KestrelException> { run("INSERT INTO employees VALUES (2, 1, -1)") }
run("DELETE FROM departments WHERE id = 1")
assertTrue(select("SELECT * FROM employees").rows.isEmpty())
}