220 lines
4.8 KiB
Go
220 lines
4.8 KiB
Go
package sql
|
|
|
|
import (
|
|
"fmt"
|
|
"slices"
|
|
)
|
|
|
|
func (p *Parser) parseCreateTable() (*CreateTableStatement, error) {
|
|
if !p.expectNext(TABLE) {
|
|
return nil, p.unexpectedToken()
|
|
}
|
|
|
|
tok, ok := p.expectOne(QUOTE, SINGLE_QUOTE, BACKQUOTE, IDENT, IF)
|
|
if !ok {
|
|
return nil, p.unexpectedToken(IDENT, IF)
|
|
}
|
|
|
|
switch tok {
|
|
case IF:
|
|
if !p.expectSequence(NOT, EXISTS) {
|
|
return nil, p.unexpectedToken()
|
|
}
|
|
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
|
fallthrough
|
|
case QUOTE, SINGLE_QUOTE, BACKQUOTE:
|
|
if !p.expectNext(IDENT) {
|
|
return nil, p.unexpectedToken()
|
|
}
|
|
}
|
|
_, _, lit := p.rescan()
|
|
|
|
stmt := CreateTableStatement{
|
|
TableName: lit,
|
|
Columns: make([]Column, 0),
|
|
}
|
|
|
|
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
|
if !p.expectNext(LPAREN) {
|
|
return nil, p.unexpectedToken(LPAREN)
|
|
}
|
|
|
|
for {
|
|
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
|
_, tok, _ := p.scan()
|
|
|
|
switch tok {
|
|
case RPAREN:
|
|
if !p.expectNext(SEMI) {
|
|
return nil, p.unexpectedToken(SEMI)
|
|
}
|
|
return &stmt, nil
|
|
|
|
case IDENT:
|
|
column, err := p.parseColumn()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
stmt.Columns = append(stmt.Columns, column)
|
|
|
|
// TODO: HANDLE AND SAVE CONSTRAINTS
|
|
case CONSTRAINT:
|
|
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
|
if !p.expectNext(IDENT) {
|
|
return nil, p.unexpectedToken()
|
|
}
|
|
// _, _, constraintName := p.rescan()
|
|
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
|
|
|
case FOREIGN:
|
|
if !p.expectSequence(KEY, LPAREN) {
|
|
return nil, p.unexpectedToken()
|
|
}
|
|
|
|
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
|
|
|
if !p.expectNext(IDENT) {
|
|
return nil, p.unexpectedToken()
|
|
}
|
|
_, _, columnName := p.rescan()
|
|
|
|
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
|
|
|
if !p.expectSequence(RPAREN, REFERENCES) {
|
|
return nil, p.unexpectedToken()
|
|
}
|
|
|
|
ref, err := p.references()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
column := slices.IndexFunc(stmt.Columns, func(c Column) bool {
|
|
return c.Name == columnName
|
|
})
|
|
|
|
stmt.Columns[column].Extra = append(stmt.Columns[column].Extra, ref)
|
|
|
|
case PRIMARY:
|
|
if !p.expectSequence(KEY, LPAREN) {
|
|
return nil, p.unexpectedToken()
|
|
}
|
|
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
|
|
|
if !p.expectNext(IDENT) {
|
|
return nil, p.unexpectedToken()
|
|
}
|
|
|
|
primaryKeyNames := make([]string, 0)
|
|
_, _, columnName := p.rescan()
|
|
primaryKeyNames = append(primaryKeyNames, columnName)
|
|
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
|
|
|
for {
|
|
tok, ok := p.expectOne(RPAREN, COMMA)
|
|
if !ok {
|
|
return nil, p.unexpectedToken()
|
|
}
|
|
if tok == RPAREN {
|
|
break
|
|
}
|
|
|
|
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
|
if !p.expectNext(IDENT) {
|
|
return nil, p.unexpectedToken()
|
|
}
|
|
|
|
_, _, columnName := p.rescan()
|
|
|
|
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
|
primaryKeyNames = append(primaryKeyNames, columnName)
|
|
}
|
|
|
|
for _, pkName := range primaryKeyNames {
|
|
column := slices.IndexFunc(stmt.Columns, func(c Column) bool {
|
|
return c.Name == pkName
|
|
})
|
|
stmt.Columns[column].Extra = append(stmt.Columns[column].Extra, "PRIMARY_KEY")
|
|
}
|
|
|
|
case COMMA:
|
|
continue
|
|
|
|
default:
|
|
return nil, p.unexpectedToken(IDENT, RPAREN, FOREIGN, COMMA)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *Parser) parseColumn() (Column, error) {
|
|
_, _, lit := p.rescan()
|
|
column := Column{Name: lit, Extra: make([]string, 0)}
|
|
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
|
|
|
if _, ok := p.expectOne(TEXT, INTEGER, REAL, BLOB, NUMERIC); !ok {
|
|
return Column{}, p.unexpectedToken(TEXT, INTEGER, REAL, BLOB, NUMERIC)
|
|
}
|
|
_, _, column.Type = p.rescan()
|
|
|
|
for {
|
|
_, tok, lit := p.scan()
|
|
switch tok {
|
|
case COMMA:
|
|
return column, nil
|
|
case RPAREN:
|
|
p.unscan()
|
|
return column, nil
|
|
|
|
case PRIMARY:
|
|
fallthrough
|
|
case NOT:
|
|
if _, ok := p.expectOne(NULL, KEY); !ok {
|
|
return Column{}, p.unexpectedToken(NULL, KEY)
|
|
}
|
|
_, _, rlit := p.rescan()
|
|
column.Extra = append(column.Extra, fmt.Sprintf("%v_%v", lit, rlit))
|
|
|
|
case REFERENCES:
|
|
ref, err := p.references()
|
|
if err != nil {
|
|
return Column{}, err
|
|
}
|
|
column.Extra = append(column.Extra, ref)
|
|
|
|
case AUTOINCREMENT:
|
|
column.Extra = append(column.Extra, "AUTOINCREMENT")
|
|
|
|
default:
|
|
return Column{}, p.unexpectedToken(COMMA, RPAREN, PRIMARY, NOT, REFERENCES)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *Parser) references() (string, error) {
|
|
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
|
if !p.expectNext(IDENT) {
|
|
return "", p.unexpectedToken(IDENT)
|
|
}
|
|
_, _, referenceTableName := p.rescan()
|
|
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
|
|
|
if !p.expectNext(LPAREN) {
|
|
return "", p.unexpectedToken()
|
|
}
|
|
|
|
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
|
|
|
if !p.expectNext(IDENT) {
|
|
return "", p.unexpectedToken()
|
|
}
|
|
_, _, referenceColumnName := p.rescan()
|
|
|
|
p.consumeIfOne(QUOTE, SINGLE_QUOTE, BACKQUOTE)
|
|
|
|
if !p.expectNext(RPAREN) {
|
|
return "", p.unexpectedToken(RPAREN)
|
|
}
|
|
|
|
return fmt.Sprintf("ref %v.%v", referenceTableName, referenceColumnName), nil
|
|
}
|