test: cover rejectsGeneratedExpressionReferencingANonexistentColumn
This commit is contained in:
1 parent
00f08c1bca
commit
0cddb29e0e
1 file changed
+158
@@ -156,3 +156,161 @@ class GeneratedColumnTest {
|
|||||||
assertFailsWith<KestrelException> { run("INSERT INTO t (a) VALUES (NULL)") }
|
assertFailsWith<KestrelException> { run("INSERT INTO t (a) VALUES (NULL)") }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- validation at CREATE TABLE ----
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun rejectsGeneratedExpressionReferencingANonexistentColumn() {
|
||||||
|
val error = assertFailsWith<KestrelException> {
|
||||||
|
run("CREATE TABLE t (a INTEGER, b INTEGER GENERATED ALWAYS AS (missing) STORED)")
|
||||||
|
}
|
||||||
|
assertTrue(error.message!!.contains("no such column"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun rejectsGeneratedExpressionContainingAnAggregate() {
|
||||||
|
assertFailsWith<KestrelException> {
|
||||||
|
run("CREATE TABLE t (a INTEGER, b INTEGER GENERATED ALWAYS AS (COUNT(a)) STORED)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun rejectsGeneratedExpressionContainingASubquery() {
|
||||||
|
run("CREATE TABLE other (x INTEGER)")
|
||||||
|
assertFailsWith<KestrelException> {
|
||||||
|
run("CREATE TABLE t (a INTEGER, b INTEGER GENERATED ALWAYS AS ((SELECT x FROM other)) STORED)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun rejectsGeneratedExpressionContainingNextval() {
|
||||||
|
run("CREATE SEQUENCE seq")
|
||||||
|
assertFailsWith<KestrelException> {
|
||||||
|
run("CREATE TABLE t (a INTEGER, b INTEGER GENERATED ALWAYS AS (NEXTVAL('seq')) STORED)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun rejectsAGeneratedColumnReferencingAnotherGeneratedColumn() {
|
||||||
|
val error = assertFailsWith<KestrelException> {
|
||||||
|
run(
|
||||||
|
"CREATE TABLE t (a INTEGER, b INTEGER GENERATED ALWAYS AS (a * 2) STORED, " +
|
||||||
|
"c INTEGER GENERATED ALWAYS AS (b + 1) STORED)"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
assertTrue(error.message!!.contains("GENERATED"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- RENAME COLUMN / DROP COLUMN interaction ----
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun renameColumnPropagatesIntoADependentGeneratedExpression() {
|
||||||
|
run("CREATE TABLE t (a INTEGER, b INTEGER GENERATED ALWAYS AS (a * 2) STORED)")
|
||||||
|
run("ALTER TABLE t RENAME COLUMN a TO a2")
|
||||||
|
run("INSERT INTO t (a2) VALUES (5)")
|
||||||
|
assertEquals(listOf(listOf(int(10))), select("SELECT b FROM t").rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dropColumnRestrictsWhenAGeneratedColumnDependsOnIt() {
|
||||||
|
run("CREATE TABLE t (a INTEGER, b INTEGER GENERATED ALWAYS AS (a * 2) STORED)")
|
||||||
|
val error = assertFailsWith<KestrelException> { run("ALTER TABLE t DROP COLUMN a") }
|
||||||
|
assertTrue(error.message!!.contains("GENERATED"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun dropColumnCascadeRemovesTheDependentGeneratedColumnToo() {
|
||||||
|
run("CREATE TABLE t (a INTEGER, b INTEGER GENERATED ALWAYS AS (a * 2) STORED, c TEXT)")
|
||||||
|
run("INSERT INTO t (a, c) VALUES (5, 'x')")
|
||||||
|
run("ALTER TABLE t DROP COLUMN a CASCADE")
|
||||||
|
val result = select("SELECT * FROM t")
|
||||||
|
assertEquals(listOf("c"), result.columns)
|
||||||
|
assertEquals(listOf(listOf(text("x"))), result.rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- ALTER TABLE ADD COLUMN GENERATED ALWAYS AS ----
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun alterTableAddColumnGeneratedBackfillsEachExistingRowFromItsOwnOtherColumns() {
|
||||||
|
run("CREATE TABLE t (a INTEGER)")
|
||||||
|
run("INSERT INTO t (a) VALUES (1), (2), (3)")
|
||||||
|
run("ALTER TABLE t ADD COLUMN b INTEGER GENERATED ALWAYS AS (a * 2) STORED")
|
||||||
|
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 ASC").rows
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun alterTableAddColumnGeneratedWorksOnAnEmptyTable() {
|
||||||
|
run("CREATE TABLE t (a INTEGER)")
|
||||||
|
run("ALTER TABLE t ADD COLUMN b INTEGER GENERATED ALWAYS AS (a * 2) STORED")
|
||||||
|
run("INSERT INTO t (a) VALUES (5)")
|
||||||
|
assertEquals(listOf(listOf(int(10))), select("SELECT b FROM t").rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun afterAlterTableAddColumnGeneratedAnInsertStillRejectsAnExplicitValue() {
|
||||||
|
run("CREATE TABLE t (a INTEGER)")
|
||||||
|
run("ALTER TABLE t ADD COLUMN 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 afterAlterTableAddColumnGeneratedUpdatingTheDependencyColumnRecomputesIt() {
|
||||||
|
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")
|
||||||
|
run("UPDATE t SET a = 9")
|
||||||
|
assertEquals(listOf(listOf(int(18))), select("SELECT b FROM t").rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun alterTableAddColumnGeneratedNotNullSkipsTheDefaultRequirementOnANonEmptyTable() {
|
||||||
|
run("CREATE TABLE t (a INTEGER)")
|
||||||
|
run("INSERT INTO t (a) VALUES (1), (2)")
|
||||||
|
// A plain NOT NULL column with no DEFAULT would be rejected here (see the DEFAULT-less
|
||||||
|
// ADD COLUMN rule tested elsewhere) - GENERATED has its own per-row value to backfill with
|
||||||
|
// instead, so it needs no DEFAULT at all even though the table already has rows.
|
||||||
|
run("ALTER TABLE t ADD COLUMN b INTEGER NOT NULL GENERATED ALWAYS AS (a * 2) STORED")
|
||||||
|
assertEquals(listOf(listOf(int(2)), listOf(int(4))), select("SELECT b FROM t ORDER BY a ASC").rows)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun alterTableAddColumnGeneratedNotNullRejectsARowWhoseBackfilledValueIsNull() {
|
||||||
|
run("CREATE TABLE t (a INTEGER)")
|
||||||
|
run("INSERT INTO t (a) VALUES (NULL)")
|
||||||
|
val error = assertFailsWith<KestrelException> {
|
||||||
|
run("ALTER TABLE t ADD COLUMN b INTEGER NOT NULL GENERATED ALWAYS AS (a) STORED")
|
||||||
|
}
|
||||||
|
assertTrue(error.message!!.contains("NOT NULL"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun alterTableAddColumnGeneratedRejectsReferencingANonexistentColumn() {
|
||||||
|
run("CREATE TABLE t (a INTEGER)")
|
||||||
|
val error = assertFailsWith<KestrelException> {
|
||||||
|
run("ALTER TABLE t ADD COLUMN b INTEGER GENERATED ALWAYS AS (missing) STORED")
|
||||||
|
}
|
||||||
|
assertTrue(error.message!!.contains("no such column"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun alterTableAddColumnGeneratedRejectsReferencingAnAlreadyExistingGeneratedColumn() {
|
||||||
|
run("CREATE TABLE t (a INTEGER, b INTEGER GENERATED ALWAYS AS (a * 2) STORED)")
|
||||||
|
val error = assertFailsWith<KestrelException> {
|
||||||
|
run("ALTER TABLE t ADD COLUMN c INTEGER GENERATED ALWAYS AS (b + 1) STORED")
|
||||||
|
}
|
||||||
|
assertTrue(error.message!!.contains("GENERATED"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun alterTableAddColumnGeneratedRejectsAnAggregate() {
|
||||||
|
run("CREATE TABLE t (a INTEGER)")
|
||||||
|
assertFailsWith<KestrelException> {
|
||||||
|
run("ALTER TABLE t ADD COLUMN b INTEGER GENERATED ALWAYS AS (COUNT(a)) STORED")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun text(value: String) = Value.TextValue(value)
|
||||||
|
}
|
||||||
Reference in new issue
Block a user