test: cover parsesColumnLevelAndTableLevelCheckConstraints

This commit is contained in:
dualfroz committed 2026-09-09 17:26:32 +02:00
1 parent 327e8f2431
commit 22f6ce2ed4
1 file changed
+173
+173
View File
@@ -1630,4 +1630,177 @@ class ParserTest {
val statement = parseOne( val statement = parseOne(
"CREATE TABLE t (id INTEGER PRIMARY KEY, dept_id INTEGER, FOREIGN KEY (dept_id) REFERENCES departments(id))" "CREATE TABLE t (id INTEGER PRIMARY KEY, dept_id INTEGER, FOREIGN KEY (dept_id) REFERENCES departments(id))"
) as CreateTableStatement ) as CreateTableStatement
assertEquals(2, statement.columns.size)
val fk = statement.foreignKeys.single()
assertEquals(listOf("dept_id"), fk.columns)
assertEquals("departments", fk.referencedTable)
}
@Test
fun parsesColumnLevelAndTableLevelCheckConstraints() {
val statement = parseOne(
"CREATE TABLE t (id INTEGER PRIMARY KEY, age INTEGER CHECK (age > 0), CHECK (id <> 0))"
) as CreateTableStatement
assertEquals(2, statement.checks.size)
assertTrue(statement.checks[0].expression is BinaryExpression)
assertTrue(statement.checks[1].expression is BinaryExpression)
}
@Test
fun parsesColumnLevelUniqueConstraint() {
val statement = parseOne(
"CREATE TABLE t (id INTEGER PRIMARY KEY, email TEXT UNIQUE)"
) as CreateTableStatement
val unique = statement.uniqueConstraints.single()
assertEquals(listOf("email"), unique.columns)
}
@Test
fun parsesTableLevelCompositeUniqueConstraint() {
val statement = parseOne(
"CREATE TABLE t (a INTEGER, b INTEGER, c INTEGER, UNIQUE (a, b, c))"
) as CreateTableStatement
val unique = statement.uniqueConstraints.single()
assertEquals(listOf("a", "b", "c"), unique.columns)
}
@Test
fun parsesMultipleUniqueConstraintsMixingColumnAndTableLevel() {
val statement = parseOne(
"CREATE TABLE t (id INTEGER PRIMARY KEY, email TEXT UNIQUE, a INTEGER, b INTEGER, UNIQUE (a, b))"
) as CreateTableStatement
assertEquals(2, statement.uniqueConstraints.size)
assertEquals(listOf("email"), statement.uniqueConstraints[0].columns)
assertEquals(listOf("a", "b"), statement.uniqueConstraints[1].columns)
}
@Test
fun parsesAlterTableAddColumnWithNotNullAndDefault() {
val statement = parseOne("ALTER TABLE t ADD COLUMN active BOOLEAN NOT NULL DEFAULT TRUE") as AlterTableStatement
assertEquals("t", statement.tableName)
val action = statement.action as AddColumn
assertEquals("active", action.column.name)
assertEquals(ColumnType.Boolean, action.column.type)
assertTrue(action.column.notNull)
assertEquals(LiteralExpression(Value.BooleanValue(true)), action.column.default)
}
@Test
fun parsesAlterTableAddColumnWithoutDefault() {
val statement = parseOne("ALTER TABLE t ADD COLUMN note TEXT") as AlterTableStatement
val action = statement.action as AddColumn
assertEquals("note", action.column.name)
assertFalse(action.column.notNull)
assertNull(action.column.default)
}
@Test
fun parsesAlterTableDropColumnWithAndWithoutCascade() {
val plain = (parseOne("ALTER TABLE t DROP COLUMN note") as AlterTableStatement).action as DropColumn
assertEquals("note", plain.columnName)
assertEquals(DropMode.Restrict, plain.mode)
val cascaded = (parseOne("ALTER TABLE t DROP COLUMN note CASCADE") as AlterTableStatement).action as DropColumn
assertEquals(DropMode.Cascade, cascaded.mode)
}
@Test
fun parsesAlterTableRenameColumn() {
val action = (parseOne("ALTER TABLE t RENAME COLUMN a TO b") as AlterTableStatement).action as RenameColumn
assertEquals("a", action.oldName)
assertEquals("b", action.newName)
}
@Test
fun parsesAlterTableRenameTo() {
val statement = parseOne("ALTER TABLE t RENAME TO t2") as AlterTableStatement
assertEquals("t", statement.tableName)
val action = statement.action as RenameTable
assertEquals("t2", action.newName)
}
@Test
fun parsesAlterTableAlterColumnTypeWithColumnKeywordAndUsing() {
val action = (parseOne("ALTER TABLE t ALTER COLUMN amount TYPE DECIMAL(10, 2) USING amount") as AlterTableStatement)
.action as ChangeColumnType
assertEquals("amount", action.columnName)
assertEquals(ColumnType.Decimal, action.newType)
assertEquals(10, action.precision)
assertEquals(2, action.scale)
assertEquals(ColumnExpression("amount"), action.using)
}
@Test
fun parsesAlterTableAlterColumnTypeWithoutColumnKeywordOrUsing() {
val action = (parseOne("ALTER TABLE t ALTER amount TYPE REAL") as AlterTableStatement).action as ChangeColumnType
assertEquals("amount", action.columnName)
assertEquals(ColumnType.Real, action.newType)
assertNull(action.precision)
assertNull(action.using)
}
@Test
fun alterTableAlterColumnRejectsAMissingTypeKeyword() {
assertFailsWith<ParseException> { parse("ALTER TABLE t ALTER COLUMN amount REAL") }
}
@Test
fun alterTableAddColumnRejectsPrimaryKeyReferencesUniqueAndCheck() {
assertFailsWith<ParseException> { parse("ALTER TABLE t ADD COLUMN id INTEGER PRIMARY KEY") }
assertFailsWith<ParseException> { parse("ALTER TABLE t ADD COLUMN dept_id INTEGER REFERENCES departments(id)") }
assertFailsWith<ParseException> { parse("ALTER TABLE t ADD COLUMN email TEXT UNIQUE") }
assertFailsWith<ParseException> { parse("ALTER TABLE t ADD COLUMN age INTEGER CHECK (age > 0)") }
}
@Test
fun parsesColumnLevelDefaultInCreateTable() {
val statement = parseOne("CREATE TABLE t (id INTEGER PRIMARY KEY, status TEXT DEFAULT 'new')") as CreateTableStatement
assertEquals(LiteralExpression(Value.TextValue("new")), statement.columns[1].default)
}
@Test
fun parsesGeneratedAlwaysAsStoredInCreateTable() {
val statement = parseOne("CREATE TABLE t (a INTEGER, b INTEGER GENERATED ALWAYS AS (a * 2) STORED)") as CreateTableStatement
assertEquals(null, statement.columns[0].generated)
assertEquals(
BinaryExpression(BinaryOperator.Multiply, ColumnExpression("a"), LiteralExpression(Value.IntegerValue(2))),
statement.columns[1].generated
)
}
@Test
fun generatedColumnRequiresAlwaysKeyword() {
assertFailsWith<ParseException> { parse("CREATE TABLE t (a INTEGER, b INTEGER GENERATED AS (a) STORED)") }
}
@Test
fun generatedColumnRequiresAsKeyword() {
assertFailsWith<ParseException> { parse("CREATE TABLE t (a INTEGER, b INTEGER GENERATED ALWAYS (a) STORED)") }
}
@Test
fun generatedColumnRequiresStoredKeyword() {
assertFailsWith<ParseException> { parse("CREATE TABLE t (a INTEGER, b INTEGER GENERATED ALWAYS AS (a))") }
}
@Test
fun generatedColumnRejectsCombiningWithDefault() {
assertFailsWith<ParseException> { parse("CREATE TABLE t (a INTEGER, b INTEGER DEFAULT 1 GENERATED ALWAYS AS (a) STORED)") }
}
@Test
fun generatedColumnRejectsCombiningWithPrimaryKey() {
assertFailsWith<ParseException> {
parse("CREATE TABLE t (a INTEGER, b INTEGER GENERATED ALWAYS AS (a) STORED PRIMARY KEY)")
}
}
@Test
fun generatedIsAContextualKeywordUsableAsAColumnName() {
val statement = parseOne("CREATE TABLE t (generated INTEGER, always INTEGER, stored INTEGER)") as CreateTableStatement
assertEquals(listOf("generated", "always", "stored"), statement.columns.map { it.name })
}
@Test
fun parsesGeneratedAlwaysAsStoredInAlterTableAddColumn() {
val statement = parseOne("ALTER TABLE t ADD COLUMN b INTEGER GENERATED ALWAYS AS (a * 2) STORED") as AlterTableStatement
} }