test: cover dumpAfterAlterTableAddColumnReflectsTheNewShape
This commit is contained in:
1 parent
074f8aabbe
commit
b5f8b44728
1 file changed
+159
@@ -643,3 +643,162 @@ class SqlWriterTest {
|
|||||||
// (not just the value 10 already sitting in the dump's INSERT statement).
|
// (not just the value 10 already sitting in the dump's INSERT statement).
|
||||||
freshExecutor.execute(Parser(Lexer("INSERT INTO t (a) VALUES (7)").tokenize()).parseProgram().single())
|
freshExecutor.execute(Parser(Lexer("INSERT INTO t (a) VALUES (7)").tokenize()).parseProgram().single())
|
||||||
|
|
||||||
|
val result = freshExecutor.execute(
|
||||||
|
Parser(Lexer("SELECT b FROM t ORDER BY a ASC").tokenize()).parseProgram().single()
|
||||||
|
) as SelectResult
|
||||||
|
assertEquals(listOf(listOf(Value.IntegerValue(10)), listOf(Value.IntegerValue(14))), result.rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dumpAfterAlterTableAddColumnReflectsTheNewShape() {
|
||||||
|
seed()
|
||||||
|
run("ALTER TABLE t ADD COLUMN status TEXT DEFAULT 'new'")
|
||||||
|
val dump = SqlWriter.dump(catalog)
|
||||||
|
assertTrue(dump.contains("status TEXT DEFAULT 'new'"))
|
||||||
|
|
||||||
|
val freshCatalog = Catalog()
|
||||||
|
val freshExecutor = Executor(freshCatalog)
|
||||||
|
Parser(Lexer(dump).tokenize()).parseProgram().forEach { freshExecutor.execute(it) }
|
||||||
|
val result = freshExecutor.execute(
|
||||||
|
Parser(Lexer("SELECT status FROM t WHERE id = 1").tokenize()).parseProgram().single()
|
||||||
|
) as SelectResult
|
||||||
|
assertEquals(listOf(listOf(text("new"))), result.rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dumpAndReplayAfterAlterTableAddColumnGeneratedPreservesTheExpressionAndBackfilledValues() {
|
||||||
|
run("CREATE TABLE t (a INTEGER)")
|
||||||
|
run("INSERT INTO t (a) VALUES (5)")
|
||||||
|
run("ALTER TABLE t ADD COLUMN b INTEGER GENERATED ALWAYS AS (a * 2) STORED")
|
||||||
|
val dump = SqlWriter.dump(catalog)
|
||||||
|
assertTrue(dump.contains("GENERATED ALWAYS AS"), dump)
|
||||||
|
|
||||||
|
val freshCatalog = Catalog()
|
||||||
|
val freshExecutor = Executor(freshCatalog)
|
||||||
|
Parser(Lexer(dump).tokenize()).parseProgram().forEach { freshExecutor.execute(it) }
|
||||||
|
// A row inserted only AFTER replay proves the GENERATED expression itself round-tripped,
|
||||||
|
// not just the value backfilled into row a=5 before the dump was taken.
|
||||||
|
freshExecutor.execute(Parser(Lexer("INSERT INTO t (a) VALUES (7)").tokenize()).parseProgram().single())
|
||||||
|
|
||||||
|
val result = freshExecutor.execute(
|
||||||
|
Parser(Lexer("SELECT b FROM t ORDER BY a ASC").tokenize()).parseProgram().single()
|
||||||
|
) as SelectResult
|
||||||
|
assertEquals(listOf(listOf(Value.IntegerValue(10)), listOf(Value.IntegerValue(14))), result.rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun createCompositeUniqueIndexRoundTripsThroughSqlWriter() {
|
||||||
|
run("CREATE TABLE t (id INTEGER PRIMARY KEY, a INTEGER, b INTEGER)")
|
||||||
|
writeThenReplay("CREATE UNIQUE INDEX idx_ab ON t (a, b)")
|
||||||
|
run("INSERT INTO t VALUES (1, 1, 1)")
|
||||||
|
assertFailsWith<KestrelException> { run("INSERT INTO t VALUES (2, 1, 1)") }
|
||||||
|
run("INSERT INTO t VALUES (3, 1, 2)")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dumpAndReplayPreservesACompositeUniqueIndex() {
|
||||||
|
run("CREATE TABLE t (id INTEGER PRIMARY KEY, a INTEGER, b INTEGER)")
|
||||||
|
run("CREATE UNIQUE INDEX idx_ab ON t (a, b)")
|
||||||
|
run("INSERT INTO t VALUES (1, 1, 1)")
|
||||||
|
val dump = SqlWriter.dump(catalog)
|
||||||
|
assertTrue(dump.contains("CREATE UNIQUE INDEX idx_ab ON t (a, b);"))
|
||||||
|
|
||||||
|
val freshCatalog = Catalog()
|
||||||
|
val freshExecutor = Executor(freshCatalog)
|
||||||
|
Parser(Lexer(dump).tokenize()).parseProgram().forEach { freshExecutor.execute(it) }
|
||||||
|
|
||||||
|
assertFailsWith<KestrelException> {
|
||||||
|
Parser(Lexer("INSERT INTO t VALUES (2, 1, 1)").tokenize()).parseProgram()
|
||||||
|
.forEach { freshExecutor.execute(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun createViewOverAWindowFunctionRoundTripsThroughSqlWriter() {
|
||||||
|
run("CREATE TABLE employees (id INTEGER PRIMARY KEY, dept TEXT, salary INTEGER)")
|
||||||
|
run(
|
||||||
|
"INSERT INTO employees VALUES (1, 'eng', 300), (2, 'eng', 400), (3, 'sales', 150)"
|
||||||
|
)
|
||||||
|
val statement = parseOne(
|
||||||
|
"CREATE VIEW ranked AS SELECT id, dept, " +
|
||||||
|
"RANK() OVER (PARTITION BY dept ORDER BY salary DESC) AS r FROM employees"
|
||||||
|
)
|
||||||
|
val rendered = SqlWriter.write(statement)
|
||||||
|
assertTrue(rendered.contains("OVER (PARTITION BY dept ORDER BY salary DESC)"), rendered)
|
||||||
|
run(rendered)
|
||||||
|
val result = select("SELECT id, r FROM ranked ORDER BY id ASC")
|
||||||
|
assertEquals(
|
||||||
|
listOf(
|
||||||
|
listOf(Value.IntegerValue(1), Value.IntegerValue(2)),
|
||||||
|
listOf(Value.IntegerValue(2), Value.IntegerValue(1)),
|
||||||
|
listOf(Value.IntegerValue(3), Value.IntegerValue(1))
|
||||||
|
),
|
||||||
|
result.rows
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dumpAndReplayOfAWindowFunctionViewPreservesTheOverClause() {
|
||||||
|
run("CREATE TABLE employees (id INTEGER PRIMARY KEY, dept TEXT, salary INTEGER)")
|
||||||
|
run("INSERT INTO employees VALUES (1, 'eng', 300), (2, 'eng', 400)")
|
||||||
|
run(
|
||||||
|
"CREATE VIEW totals AS SELECT id, SUM(salary) OVER (PARTITION BY dept) AS deptTotal FROM employees"
|
||||||
|
)
|
||||||
|
val dump = SqlWriter.dump(catalog)
|
||||||
|
assertTrue(dump.contains("OVER (PARTITION BY dept)"), dump)
|
||||||
|
|
||||||
|
val freshCatalog = Catalog()
|
||||||
|
val freshExecutor = Executor(freshCatalog)
|
||||||
|
Parser(Lexer(dump).tokenize()).parseProgram().forEach { freshExecutor.execute(it) }
|
||||||
|
val result = Parser(Lexer("SELECT deptTotal FROM totals ORDER BY id ASC").tokenize()).parseProgram().single()
|
||||||
|
.let { freshExecutor.execute(it) } as SelectResult
|
||||||
|
assertEquals(listOf(listOf(Value.IntegerValue(700)), listOf(Value.IntegerValue(700))), result.rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun createViewOverLagWithOffsetAndDefaultRoundTripsThroughSqlWriter() {
|
||||||
|
run("CREATE TABLE employees (id INTEGER PRIMARY KEY, dept TEXT, salary INTEGER)")
|
||||||
|
run("INSERT INTO employees VALUES (1, 'eng', 400), (2, 'eng', 300)")
|
||||||
|
val statement = parseOne(
|
||||||
|
"CREATE VIEW withPrev AS SELECT id, dept, " +
|
||||||
|
"LAG(salary, 1, 0) OVER (PARTITION BY dept ORDER BY salary DESC) AS prevSalary FROM employees"
|
||||||
|
)
|
||||||
|
val rendered = SqlWriter.write(statement)
|
||||||
|
assertTrue(rendered.contains("LAG(salary, 1, 0) OVER (PARTITION BY dept ORDER BY salary DESC)"), rendered)
|
||||||
|
run(rendered)
|
||||||
|
val result = select("SELECT id, prevSalary FROM withPrev ORDER BY id ASC")
|
||||||
|
assertEquals(
|
||||||
|
listOf(
|
||||||
|
listOf(Value.IntegerValue(1), Value.IntegerValue(0)),
|
||||||
|
listOf(Value.IntegerValue(2), Value.IntegerValue(400))
|
||||||
|
),
|
||||||
|
result.rows
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun createViewOverAnExplicitRowsFrameRoundTripsThroughSqlWriter() {
|
||||||
|
run("CREATE TABLE employees (id INTEGER PRIMARY KEY, dept TEXT, salary INTEGER)")
|
||||||
|
run("INSERT INTO employees VALUES (1, 'eng', 300), (2, 'eng', 400), (3, 'eng', 100)")
|
||||||
|
val statement = parseOne(
|
||||||
|
"CREATE VIEW moving AS SELECT id, dept, " +
|
||||||
|
"SUM(salary) OVER (PARTITION BY dept ORDER BY id ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS s " +
|
||||||
|
"FROM employees"
|
||||||
|
)
|
||||||
|
val rendered = SqlWriter.write(statement)
|
||||||
|
assertTrue(
|
||||||
|
rendered.contains("OVER (PARTITION BY dept ORDER BY id ROWS BETWEEN 1 PRECEDING AND CURRENT ROW)"),
|
||||||
|
rendered
|
||||||
|
)
|
||||||
|
run(rendered)
|
||||||
|
val result = select("SELECT id, s FROM moving ORDER BY id ASC")
|
||||||
|
assertEquals(
|
||||||
|
listOf(
|
||||||
|
listOf(Value.IntegerValue(1), Value.IntegerValue(300)),
|
||||||
|
listOf(Value.IntegerValue(2), Value.IntegerValue(700)),
|
||||||
|
listOf(Value.IntegerValue(3), Value.IntegerValue(500))
|
||||||
|
),
|
||||||
|
result.rows
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Reference in new issue
Block a user