feat(parser): extend Parser with parseTablePrimaryKey

This commit is contained in:
dualfroz committed 2026-09-02 21:57:10 +02:00
1 parent 48fa9202a4
commit 4e101e46eb
1 file changed
+61 -9
+61 -9
View File
@@ -420,18 +420,58 @@ class Parser(private val tokens: List<Token>) {
return CreateTableStatement(tableName, columns, foreignKeys, checks, uniqueConstraints, compositePrimaryKey, temporary) return CreateTableStatement(tableName, columns, foreignKeys, checks, uniqueConstraints, compositePrimaryKey, temporary)
} }
private fun parseColumnDefinition(): Column { private fun parseTablePrimaryKey(): List<String> {
val columnName = expectIdentifier("expected column name") expect(TokenType.Primary, "expected PRIMARY")
val typeToken = advance() expect(TokenType.Key, "expected KEY after PRIMARY")
val type = when (typeToken.type) { expect(TokenType.LeftParen, "expected '(' after PRIMARY KEY")
TokenType.Integer -> ColumnType.Integer val columns = mutableListOf<String>()
TokenType.Text -> ColumnType.Text do {
TokenType.Boolean -> ColumnType.Boolean columns.add(expectIdentifier("expected column name"))
TokenType.Real -> ColumnType.Real } while (match(TokenType.Comma))
else -> throw error("expected a column type but found '${describe(typeToken)}'", typeToken) expect(TokenType.RightParen, "expected ')' after PRIMARY KEY column list")
return columns
}
/**
* `CONSTRAINT name FOREIGN KEY (...) | CHECK (...) | UNIQUE (...)` - a named table-level
* constraint, standard SQL syntax this engine accepts both inline in `CREATE TABLE` (here) and
* via `ALTER TABLE ... ADD` (see [parseAlterTable]'s identical dispatch). `PRIMARY KEY` is
* deliberately not accepted after `CONSTRAINT name`: [kestrel.catalog.TableSchema] has no name
* field for a table's PRIMARY KEY at all, so there would be nowhere to put it, and
* `ALTER TABLE ... DROP CONSTRAINT` never needs to target a PRIMARY KEY (see
* [kestrel.catalog.Catalog.dropColumn]'s own doc on why dropping a PRIMARY KEY is out of scope
* entirely - redesign it or drop the table instead).
*/
private fun parseNamedTableConstraint(
foreignKeys: MutableList<ForeignKeyConstraint>,
checks: MutableList<CheckConstraint>,
uniqueConstraints: MutableList<UniqueConstraint>
) {
advance()
val name = expectIdentifier("expected constraint name after CONSTRAINT")
when {
check(TokenType.Foreign) -> foreignKeys.add(parseTableForeignKey().copy(name = name))
check(TokenType.Check) -> checks.add(parseTableCheck().copy(name = name))
check(TokenType.Unique) -> uniqueConstraints.add(parseTableUnique().copy(name = name))
else -> throw error(
"expected FOREIGN KEY, CHECK, or UNIQUE after CONSTRAINT $name but found '${describe(peek())}'",
peek()
)
} }
}
private fun parseColumnDefinition(
foreignKeys: MutableList<ForeignKeyConstraint>,
checks: MutableList<CheckConstraint>,
uniqueConstraints: MutableList<UniqueConstraint>
): Column {
val columnName = expectIdentifier("expected column name")
val spec = parseTypeSpec()
var notNull = false var notNull = false
var primaryKey = false var primaryKey = false
var default: Expression? = null
var generated: Expression? = null
var collation = Collation.Binary
while (true) { while (true) {
when { when {
check(TokenType.Not) -> { check(TokenType.Not) -> {
@@ -445,6 +485,18 @@ class Parser(private val tokens: List<Token>) {
primaryKey = true primaryKey = true
notNull = true notNull = true
} }
check(TokenType.Unique) -> {
advance()
uniqueConstraints.add(UniqueConstraint(null, listOf(columnName)))
}
check(TokenType.References) -> foreignKeys.add(parseInlineForeignKey(columnName))
check(TokenType.Check) -> checks.add(parseColumnCheck())
check(TokenType.Default) -> {
advance()
default = parseExpression()
}
isContextualKeyword("GENERATED") -> generated = parseGeneratedClause()
check(TokenType.Collate) -> collation = parseColumnCollation()
else -> break else -> break
} }
} }