From 310a2f10dc63f8a4ccd5489b4c89382ff96fb604 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Thu, 10 Sep 2026 15:03:31 +0200 Subject: [PATCH] test: cover rowsFrameShorthandIsWrittenBackOutInCanonicalBetweenForm --- src/test/kotlin/kestrel/SqlWriterTest.kt | 159 +++++++++++++++++++++++ 1 file changed, 159 insertions(+) diff --git a/src/test/kotlin/kestrel/SqlWriterTest.kt b/src/test/kotlin/kestrel/SqlWriterTest.kt index 8cc228e..eae15f6 100644 --- a/src/test/kotlin/kestrel/SqlWriterTest.kt +++ b/src/test/kotlin/kestrel/SqlWriterTest.kt @@ -802,3 +802,162 @@ class SqlWriterTest { ) } + @Test + fun rowsFrameShorthandIsWrittenBackOutInCanonicalBetweenForm() { + run("CREATE TABLE employees (id INTEGER PRIMARY KEY, salary INTEGER)") + run("INSERT INTO employees VALUES (1, 400), (2, 300)") + val statement = parseOne( + "CREATE VIEW shorthand AS SELECT id, SUM(salary) OVER (ORDER BY id ROWS 1 PRECEDING) AS s FROM employees" + ) + val rendered = SqlWriter.write(statement) + assertTrue(rendered.contains("ROWS BETWEEN 1 PRECEDING AND CURRENT ROW"), rendered) + } + + @Test + fun unboundedFrameBoundsRoundTripThroughSqlWriter() { + run("CREATE TABLE employees (id INTEGER PRIMARY KEY, salary INTEGER)") + run("INSERT INTO employees VALUES (1, 400), (2, 300)") + val statement = parseOne( + "CREATE VIEW wholePartition AS SELECT id, " + + "SUM(salary) OVER (ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS s FROM employees" + ) + val rendered = SqlWriter.write(statement) + assertTrue(rendered.contains("ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING"), rendered) + } + + @Test + fun createViewOverAnExplicitRangeFrameRoundTripsThroughSqlWriter() { + run("CREATE TABLE sales (id INTEGER PRIMARY KEY, amount INTEGER)") + run("INSERT INTO sales VALUES (1, 10), (2, 20), (3, 20), (4, 30), (5, 50)") + val statement = parseOne( + "CREATE VIEW valueTotals AS SELECT id, " + + "SUM(amount) OVER (ORDER BY amount RANGE BETWEEN 10 PRECEDING AND 10 FOLLOWING) AS s FROM sales" + ) + val rendered = SqlWriter.write(statement) + assertTrue(rendered.contains("OVER (ORDER BY amount RANGE BETWEEN 10 PRECEDING AND 10 FOLLOWING)"), rendered) + run(rendered) + val result = select("SELECT id, s FROM valueTotals ORDER BY id ASC") + assertEquals( + listOf(50L, 80L, 80L, 70L, 50L), + result.rows.map { (it[1] as Value.IntegerValue).value } + ) + } + + @Test + fun createViewOverAnExcludeCurrentRowFrameRoundTripsThroughSqlWriter() { + run("CREATE TABLE sales (id INTEGER PRIMARY KEY, amount INTEGER)") + run("INSERT INTO sales VALUES (1, 10), (2, 20), (3, 20), (4, 30), (5, 50)") + val statement = parseOne( + "CREATE VIEW excludingSelf AS SELECT id, SUM(amount) OVER (" + + "ORDER BY amount ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW" + + ") AS s FROM sales" + ) + val rendered = SqlWriter.write(statement) + assertTrue( + rendered.contains("ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE CURRENT ROW"), + rendered + ) + run(rendered) + val result = select("SELECT id, s FROM excludingSelf ORDER BY id ASC") + assertEquals( + listOf(120L, 110L, 110L, 100L, 80L), + result.rows.map { (it[1] as Value.IntegerValue).value } + ) + } + + @Test + fun createViewOverAnExplicitGroupsFrameRoundTripsThroughSqlWriter() { + run("CREATE TABLE sales (id INTEGER PRIMARY KEY, amount INTEGER)") + run("INSERT INTO sales VALUES (1, 10), (2, 20), (3, 20), (4, 30), (5, 50)") + val statement = parseOne( + "CREATE VIEW groupTotals AS SELECT id, " + + "SUM(amount) OVER (ORDER BY amount GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING) AS s FROM sales" + ) + val rendered = SqlWriter.write(statement) + assertTrue(rendered.contains("OVER (ORDER BY amount GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING)"), rendered) + run(rendered) + val result = select("SELECT id, s FROM groupTotals ORDER BY id ASC") + assertEquals( + listOf(50L, 80L, 80L, 120L, 80L), + result.rows.map { (it[1] as Value.IntegerValue).value } + ) + } + + @Test + fun excludeNoOthersIsOmittedFromRenderedSqlSinceItIsTheDefault() { + run("CREATE TABLE sales (id INTEGER PRIMARY KEY, amount INTEGER)") + run("INSERT INTO sales VALUES (1, 10), (2, 20)") + val statement = parseOne( + "CREATE VIEW totals AS SELECT id, SUM(amount) OVER (" + + "ORDER BY amount ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING EXCLUDE NO OTHERS" + + ") AS s FROM sales" + ) + val rendered = SqlWriter.write(statement) + assertTrue(!rendered.contains("EXCLUDE"), rendered) + } + + @Test + fun insertOnConflictDoNothingRoundTripsThroughSqlWriter() { + run("CREATE TABLE accounts (id INTEGER PRIMARY KEY, balance INTEGER)") + run("INSERT INTO accounts VALUES (1, 100)") + val statement = parseOne("INSERT INTO accounts VALUES (1, 999) ON CONFLICT (id) DO NOTHING") + val rendered = SqlWriter.write(statement) + assertTrue(rendered.contains("ON CONFLICT (id) DO NOTHING"), rendered) + run(rendered) + assertEquals(listOf(listOf(Value.IntegerValue(100))), select("SELECT balance FROM accounts WHERE id = 1").rows) + } + + @Test + fun insertOnConflictDoUpdateWithExcludedAndWhereRoundTripsThroughSqlWriter() { + run("CREATE TABLE accounts (id INTEGER PRIMARY KEY, balance INTEGER)") + run("INSERT INTO accounts VALUES (1, 100)") + val statement = parseOne( + "INSERT INTO accounts VALUES (1, 50) ON CONFLICT (id) DO UPDATE SET balance = accounts.balance + excluded.balance WHERE excluded.balance > 0" + ) + val rendered = SqlWriter.write(statement) + assertTrue(rendered.contains("ON CONFLICT (id) DO UPDATE SET balance = (accounts.balance + excluded.balance) WHERE (excluded.balance > 0)"), rendered) + run(rendered) + assertEquals(listOf(listOf(Value.IntegerValue(150))), select("SELECT balance FROM accounts WHERE id = 1").rows) + } + + @Test + fun createTriggerRoundTripsThroughSqlWriter() { + run("CREATE TABLE orders (id INTEGER PRIMARY KEY, amount INTEGER)") + run("CREATE TABLE audit (order_id INTEGER)") + val statement = parseOne( + "CREATE TRIGGER log_insert AFTER INSERT ON orders FOR EACH ROW WHEN NEW.amount > 0 " + + "BEGIN INSERT INTO audit VALUES (NEW.id); END" + ) + val rendered = SqlWriter.write(statement) + assertTrue(rendered.contains("CREATE TRIGGER log_insert AFTER INSERT ON orders FOR EACH ROW WHEN"), rendered) + assertTrue(rendered.contains("INSERT INTO audit VALUES (NEW.id)"), rendered) + run(rendered) + run("INSERT INTO orders VALUES (1, 50)") + assertEquals(listOf(listOf(Value.IntegerValue(1))), select("SELECT order_id FROM audit").rows) + } + + @Test + fun dropTriggerRoundTripsThroughSqlWriter() { + run("CREATE TABLE t (id INTEGER)") + run("CREATE TRIGGER log AFTER INSERT ON t FOR EACH ROW BEGIN DELETE FROM t WHERE 1 = 0; END") + val rendered = SqlWriter.write(parseOne("DROP TRIGGER log")) + assertEquals("DROP TRIGGER log;", rendered) + run(rendered) + assertFailsWith { run("DROP TRIGGER log") } + } + + @Test + fun createSequenceRoundTripsThroughSqlWriter() { + val statement = parseOne("CREATE SEQUENCE seq START WITH 5 INCREMENT BY 2 MAXVALUE 100 CYCLE") + val rendered = SqlWriter.write(statement) + assertTrue(rendered.contains("CREATE SEQUENCE seq"), rendered) + assertTrue(rendered.contains("INCREMENT BY 2"), rendered) + assertTrue(rendered.contains("MAXVALUE 100"), rendered) + assertTrue(rendered.contains("START WITH 5"), rendered) + assertTrue(rendered.contains("CYCLE"), rendered) + run(rendered) + run("CREATE TABLE t (id INTEGER)") + run("INSERT INTO t VALUES (NEXTVAL('seq'))") + assertEquals(listOf(listOf(Value.IntegerValue(5))), select("SELECT id FROM t").rows) + } +