test: cover dumpAndReplayOfALateralViewPreservesTheLateralKeyword
This commit is contained in:
1 parent
b512c2c5c0
commit
d7c5c965a7
1 file changed
+161
@@ -321,3 +321,164 @@ class SqlWriterTest {
|
||||
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())
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user