feat(parser): extend Parser with parseTablePrimaryKey
This commit is contained in:
1 parent
48fa9202a4
commit
4e101e46eb
1 file changed
+61
-9
@@ -420,18 +420,58 @@ class Parser(private val tokens: List<Token>) {
|
||||
return CreateTableStatement(tableName, columns, foreignKeys, checks, uniqueConstraints, compositePrimaryKey, temporary)
|
||||
}
|
||||
|
||||
private fun parseColumnDefinition(): Column {
|
||||
val columnName = expectIdentifier("expected column name")
|
||||
val typeToken = advance()
|
||||
val type = when (typeToken.type) {
|
||||
TokenType.Integer -> ColumnType.Integer
|
||||
TokenType.Text -> ColumnType.Text
|
||||
TokenType.Boolean -> ColumnType.Boolean
|
||||
TokenType.Real -> ColumnType.Real
|
||||
else -> throw error("expected a column type but found '${describe(typeToken)}'", typeToken)
|
||||
private fun parseTablePrimaryKey(): List<String> {
|
||||
expect(TokenType.Primary, "expected PRIMARY")
|
||||
expect(TokenType.Key, "expected KEY after PRIMARY")
|
||||
expect(TokenType.LeftParen, "expected '(' after PRIMARY KEY")
|
||||
val columns = mutableListOf<String>()
|
||||
do {
|
||||
columns.add(expectIdentifier("expected column name"))
|
||||
} while (match(TokenType.Comma))
|
||||
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 primaryKey = false
|
||||
var default: Expression? = null
|
||||
var generated: Expression? = null
|
||||
var collation = Collation.Binary
|
||||
while (true) {
|
||||
when {
|
||||
check(TokenType.Not) -> {
|
||||
@@ -445,6 +485,18 @@ class Parser(private val tokens: List<Token>) {
|
||||
primaryKey = 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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user