test: cover GeneratedColumnTest and run

This commit is contained in:
dualfroz committed 2026-09-09 11:43:05 +02:00
1 parent 9af294b2b7
commit 00f08c1bca
1 file changed
+158
@@ -0,0 +1,158 @@
package kestrel
import kestrel.catalog.Catalog
import kestrel.engine.Executor
import kestrel.engine.MergeResult
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
import kotlin.test.assertTrue
/**
* `GENERATED ALWAYS AS (expr) STORED` on the in-memory engine - a column whose value is always
* derived from the rest of its own row, never assigned directly, and recomputed on every
* INSERT/UPDATE. See [kestrel.store.sql.DiskExecutorGeneratedColumnTest] for the disk engine.
*/
class GeneratedColumnTest {
private val catalog = Catalog()
private val executor = Executor(catalog)
private fun run(sql: String): List<StatementResult> =
Parser(Lexer(sql).tokenize()).parseProgram().map { executor.execute(it) }
private fun exec(sql: String): StatementResult = run(sql).single()
private fun select(sql: String): SelectResult = exec(sql) as SelectResult
private fun int(value: Long) = Value.IntegerValue(value)
// ---- computed on INSERT ----
@Test
fun generatedColumnIsComputedWhenOmittedFromInsert() {
run("CREATE TABLE t (a INTEGER, b INTEGER GENERATED ALWAYS AS (a * 2) STORED)")
run("INSERT INTO t (a) VALUES (5)")
assertEquals(listOf(listOf(int(5), int(10))), select("SELECT a, b FROM t").rows)
}
@Test
fun generatedColumnIsComputedWhenOmittedFromPositionalInsertColumnList() {
run("CREATE TABLE t (a INTEGER, b INTEGER GENERATED ALWAYS AS (a * 2) STORED)")
run("INSERT INTO t (a, b) VALUES (5, DEFAULT)")
assertEquals(listOf(listOf(int(5), int(10))), select("SELECT a, b FROM t").rows)
}
@Test
fun insertRejectsAnExplicitValueForAGeneratedColumn() {
run("CREATE TABLE t (a INTEGER, b INTEGER GENERATED ALWAYS AS (a * 2) STORED)")
val error = assertFailsWith<KestrelException> { run("INSERT INTO t (a, b) VALUES (5, 99)") }
assertTrue(error.message!!.contains("GENERATED"))
}
@Test
fun generatedColumnRecomputesFreshForEveryRowOfAMultiRowInsert() {
run("CREATE TABLE t (a INTEGER, b INTEGER GENERATED ALWAYS AS (a * 2) STORED)")
run("INSERT INTO t (a) VALUES (1), (2), (3)")
assertEquals(
listOf(listOf(int(1), int(2)), listOf(int(2), int(4)), listOf(int(3), int(6))),
select("SELECT a, b FROM t ORDER BY a").rows
)
}
@Test
fun generatedColumnCanReferenceMultipleOtherColumns() {
run("CREATE TABLE t (price INTEGER, qty INTEGER, total INTEGER GENERATED ALWAYS AS (price * qty) STORED)")
run("INSERT INTO t (price, qty) VALUES (7, 3)")
assertEquals(listOf(listOf(int(21))), select("SELECT total FROM t").rows)
}
// ---- computed on UPDATE ----
@Test
fun updatingADependencyColumnRecomputesTheGeneratedColumn() {
run("CREATE TABLE t (a INTEGER, b INTEGER GENERATED ALWAYS AS (a * 2) STORED)")
run("INSERT INTO t (a) VALUES (5)")
run("UPDATE t SET a = 9")
assertEquals(listOf(listOf(int(18))), select("SELECT b FROM t").rows)
}
@Test
fun updateRejectsAnExplicitValueForAGeneratedColumn() {
run("CREATE TABLE t (a INTEGER, b INTEGER GENERATED ALWAYS AS (a * 2) STORED)")
run("INSERT INTO t (a) VALUES (5)")
val error = assertFailsWith<KestrelException> { run("UPDATE t SET b = 1") }
assertTrue(error.message!!.contains("GENERATED"))
}
@Test
fun updateSetToDefaultOnAGeneratedColumnRecomputesIt() {
run("CREATE TABLE t (a INTEGER, b INTEGER GENERATED ALWAYS AS (a * 2) STORED)")
run("INSERT INTO t (a) VALUES (5)")
run("UPDATE t SET a = 9, b = DEFAULT")
assertEquals(listOf(listOf(int(18))), select("SELECT b FROM t").rows)
}
// ---- ON CONFLICT DO UPDATE ----
@Test
fun onConflictDoUpdateRecomputesTheGeneratedColumn() {
run("CREATE TABLE t (id INTEGER PRIMARY KEY, a INTEGER, b INTEGER GENERATED ALWAYS AS (a * 2) STORED)")
run("INSERT INTO t (id, a) VALUES (1, 5)")
run("INSERT INTO t (id, a) VALUES (1, 20) ON CONFLICT (id) DO UPDATE SET a = EXCLUDED.a")
assertEquals(listOf(listOf(int(40))), select("SELECT b FROM t").rows)
}
@Test
fun onConflictDoUpdateRejectsAnExplicitValueForAGeneratedColumn() {
run("CREATE TABLE t (id INTEGER PRIMARY KEY, a INTEGER, b INTEGER GENERATED ALWAYS AS (a * 2) STORED)")
run("INSERT INTO t (id, a) VALUES (1, 5)")
assertFailsWith<KestrelException> {
run("INSERT INTO t (id, a) VALUES (1, 20) ON CONFLICT (id) DO UPDATE SET b = 1")
}
}
// ---- MERGE ----
@Test
fun mergeInsertComputesTheGeneratedColumn() {
run("CREATE TABLE t (id INTEGER PRIMARY KEY, a INTEGER, b INTEGER GENERATED ALWAYS AS (a * 2) STORED)")
run("CREATE TABLE s (id INTEGER, a INTEGER)")
run("INSERT INTO s VALUES (1, 5)")
val result = exec(
"MERGE INTO t USING s ON t.id = s.id WHEN NOT MATCHED THEN INSERT (id, a) VALUES (s.id, s.a)"
) as MergeResult
assertEquals(MergeResult(1, 0, 0), result)
assertEquals(listOf(listOf(int(10))), select("SELECT b FROM t").rows)
}
@Test
fun mergeUpdateRecomputesTheGeneratedColumn() {
run("CREATE TABLE t (id INTEGER PRIMARY KEY, a INTEGER, b INTEGER GENERATED ALWAYS AS (a * 2) STORED)")
run("CREATE TABLE s (id INTEGER, a INTEGER)")
run("INSERT INTO t (id, a) VALUES (1, 5)")
run("INSERT INTO s VALUES (1, 9)")
exec("MERGE INTO t USING s ON t.id = s.id WHEN MATCHED THEN UPDATE SET a = s.a")
assertEquals(listOf(listOf(int(18))), select("SELECT b FROM t").rows)
}
// ---- ordering with CHECK / NOT NULL ----
@Test
fun checkConstraintSeesTheComputedGeneratedValueNotItsOldOne() {
run("CREATE TABLE t (a INTEGER, b INTEGER GENERATED ALWAYS AS (a * 2) STORED CHECK (b > 0))")
assertFailsWith<KestrelException> { run("INSERT INTO t (a) VALUES (-1)") }
run("INSERT INTO t (a) VALUES (3)")
assertEquals(listOf(listOf(int(6))), select("SELECT b FROM t").rows)
}
@Test
fun notNullOnAGeneratedColumnRejectsARowWhoseComputedValueIsNull() {
run("CREATE TABLE t (a INTEGER, b INTEGER GENERATED ALWAYS AS (a) STORED NOT NULL)")
assertFailsWith<KestrelException> { run("INSERT INTO t (a) VALUES (NULL)") }
}