featurebase/sql3/parser/parser.go
pokeeffe-molecula 15d2ee8b07
add allow_missing_values option to bulk insert (fb-1823) (#2372)
* add allow_missing_values option to bulk insert

* test coverage

* review feedback
2022-12-14 18:32:27 -06:00

3473 lines
78 KiB
Go

// Copyright 2021 Molecula Corp. All rights reserved.
package parser
import (
"fmt"
"io"
"strings"
"time"
)
// Parser represents a SQL parser.
type Parser struct {
s *Scanner
pos Pos // current position
tok Token // current token
lit string // current literal value
full bool // buffer full
}
// NewParser returns a new instance of Parser that reads from r.
func NewParser(r io.Reader) *Parser {
return &Parser{
s: NewScanner(r),
}
}
// ParseExprString parses s into an expression. Returns nil if s is blank.
func ParseExprString(s string) (Expr, error) {
if s == "" {
return nil, nil
}
return NewParser(strings.NewReader(s)).ParseExpr()
}
// MustParseExprString parses s into an expression. Panic on error.
func MustParseExprString(s string) Expr {
expr, err := ParseExprString(s)
if err != nil {
panic(err)
}
return expr
}
func (p *Parser) ParseStatement() (stmt Statement, err error) {
switch tok := p.peek(); tok {
case EOF:
return nil, io.EOF
//case EXPLAIN:
// if stmt, err = p.parseExplainStatement(); err != nil {
// return stmt, err
// }
default:
if stmt, err = p.parseNonExplainStatement(); err != nil {
return stmt, err
}
}
// Read trailing semicolon or end of file.
if tok := p.peek(); tok != EOF && tok != SEMI {
return stmt, p.errorExpected(p.pos, p.tok, "semicolon or EOF")
}
p.scan()
return stmt, nil
}
/*
// parseExplain parses EXPLAIN [QUERY PLAN] STMT.
func (p *Parser) parseExplainStatement() (_ *ExplainStatement, err error) {
var tok Token
// Parse initial "EXPLAIN" token.
var stmt ExplainStatement
stmt.Explain, tok, _ = p.scan()
assert(tok == EXPLAIN)
// Parse optional "QUERY PLAN" tokens.
if p.peek() == QUERY {
stmt.Query, _, _ = p.scan()
if p.peek() != PLAN {
return &stmt, p.errorExpected(p.pos, p.tok, "PLAN")
}
stmt.QueryPlan, _, _ = p.scan()
}
// Parse statement to be explained.
if stmt.Stmt, err = p.parseNonExplainStatement(); err != nil {
return &stmt, err
}
return &stmt, nil
}*/
// parseStmt parses all statement types.
func (p *Parser) parseNonExplainStatement() (Statement, error) {
switch p.peek() {
//case ANALYZE:
// return p.parseAnalyzeStatement()
case ALTER:
return p.parseAlterTableStatement()
// case BEGIN:
// return p.parseBeginStatement()
// case COMMIT, END:
// return p.parseCommitStatement()
// case ROLLBACK:
// return p.parseRollbackStatement()
// case SAVEPOINT:
// return p.parseSavepointStatement()
// case RELEASE:
// return p.parseReleaseStatement()
case BULK:
return p.parseBulkInsertStatement()
case CREATE:
return p.parseCreateStatement()
case DROP:
return p.parseDropStatement()
case SELECT:
return p.parseSelectStatement(false, nil)
case INSERT, REPLACE:
return p.parseInsertStatement(nil)
case UPDATE:
return p.parseUpdateStatement(nil)
case DELETE:
return p.parseDeleteStatement(nil)
// case WITH:
// return p.parseWithStatement()
case SHOW:
return p.parseShowStatement()
default:
return nil, p.errorExpected(p.pos, p.tok, "statement")
}
}
// parseWithStatement is called only from parseNonExplainStatement as we don't
// know what kind of statement we'll have after the CTEs (e.g. SELECT, INSERT, etc).
/*func (p *Parser) parseWithStatement() (Statement, error) {
withClause, err := p.parseWithClause()
if err != nil {
return nil, err
}
switch p.peek() {
case SELECT, VALUES:
return p.parseSelectStatement(false, withClause)
case INSERT, REPLACE:
return p.parseInsertStatement(withClause)
case UPDATE:
return p.parseUpdateStatement(withClause)
case DELETE:
return p.parseDeleteStatement(withClause)
default:
return nil, p.errorExpected(p.pos, p.tok, "SELECT, VALUES, INSERT, REPLACE, UPDATE, or DELETE")
}
}*/
func (p *Parser) parseShowStatement() (Statement, error) {
assert(p.peek() == SHOW)
show, _, _ := p.scan()
switch p.peek() {
case TABLES:
return p.parseShowTablesStatement(show)
case COLUMNS:
return p.parseShowColumnsStatement(show)
case CREATE:
return p.parseShowCreateStatement(show)
default:
return nil, p.errorExpected(p.pos, p.tok, "TABLES, COLUMNS or CREATE")
}
}
func (p *Parser) parseShowTablesStatement(showPos Pos) (*ShowTablesStatement, error) {
switch p.peek() {
case TABLES:
var stmt ShowTablesStatement
stmt.Show = showPos
stmt.Tables, _, _ = p.scan()
return &stmt, nil
default:
return nil, p.errorExpected(p.pos, p.tok, "TABLES")
}
}
func (p *Parser) parseShowColumnsStatement(showPos Pos) (_ *ShowColumnsStatement, err error) {
assert(p.peek() == COLUMNS)
columns, _, _ := p.scan()
var stmt ShowColumnsStatement
stmt.Show = showPos
stmt.Columns = columns
switch p.peek() {
case FROM:
stmt.From, _, _ = p.scan()
if stmt.TableName, err = p.parseIdent("table name"); err != nil {
return &stmt, err
}
return &stmt, nil
default:
return nil, p.errorExpected(p.pos, p.tok, "FROM")
}
}
func (p *Parser) parseShowCreateStatement(showPos Pos) (Statement, error) {
assert(p.peek() == CREATE)
create, _, _ := p.scan()
switch p.peek() {
case TABLE:
return p.parseShowCreateTableStatement(showPos, create)
default:
return nil, p.errorExpected(p.pos, p.tok, "TABLES")
}
}
func (p *Parser) parseShowCreateTableStatement(showPos Pos, createPos Pos) (_ *ShowCreateTableStatement, err error) {
assert(p.peek() == TABLE)
table, _, _ := p.scan()
var stmt ShowCreateTableStatement
stmt.Show = showPos
stmt.Create = createPos
stmt.Table = table
if stmt.TableName, err = p.parseIdent("table name"); err != nil {
return &stmt, err
}
return &stmt, nil
}
/*func (p *Parser) parseBeginStatement() (*BeginStatement, error) {
assert(p.peek() == BEGIN)
var stmt BeginStatement
stmt.Begin, _, _ = p.scan()
// Parse transaction type.
switch p.peek() {
case DEFERRED:
stmt.Deferred, _, _ = p.scan()
case IMMEDIATE:
stmt.Immediate, _, _ = p.scan()
case EXCLUSIVE:
stmt.Exclusive, _, _ = p.scan()
}
// Parse optional TRANSCTION keyword.
if p.peek() == TRANSACTION {
stmt.Transaction, _, _ = p.scan()
}
return &stmt, nil
}*/
/*func (p *Parser) parseCommitStatement() (*CommitStatement, error) {
assert(p.peek() == COMMIT || p.peek() == END)
var stmt CommitStatement
if p.peek() == COMMIT {
stmt.Commit, _, _ = p.scan()
} else {
stmt.End, _, _ = p.scan()
}
if p.peek() == TRANSACTION {
stmt.Transaction, _, _ = p.scan()
}
return &stmt, nil
}*/
/*func (p *Parser) parseRollbackStatement() (_ *RollbackStatement, err error) {
assert(p.peek() == ROLLBACK)
var stmt RollbackStatement
stmt.Rollback, _, _ = p.scan()
// Parse optional "TRANSACTION".
if p.peek() == TRANSACTION {
stmt.Transaction, _, _ = p.scan()
}
// Parse optional "TO SAVEPOINT savepoint-name"
if p.peek() == TO {
stmt.To, _, _ = p.scan()
if p.peek() == SAVEPOINT {
stmt.Savepoint, _, _ = p.scan()
}
if stmt.SavepointName, err = p.parseIdent("savepoint name"); err != nil {
return &stmt, err
}
}
return &stmt, nil
}*/
/*func (p *Parser) parseSavepointStatement() (_ *SavepointStatement, err error) {
assert(p.peek() == SAVEPOINT)
var stmt SavepointStatement
stmt.Savepoint, _, _ = p.scan()
if stmt.Name, err = p.parseIdent("savepoint name"); err != nil {
return &stmt, err
}
return &stmt, nil
}*/
/*func (p *Parser) parseReleaseStatement() (_ *ReleaseStatement, err error) {
assert(p.peek() == RELEASE)
var stmt ReleaseStatement
stmt.Release, _, _ = p.scan()
if p.peek() == SAVEPOINT {
stmt.Savepoint, _, _ = p.scan()
}
if stmt.Name, err = p.parseIdent("savepoint name"); err != nil {
return &stmt, err
}
return &stmt, nil
}*/
func (p *Parser) parseCreateStatement() (Statement, error) {
assert(p.peek() == CREATE)
pos, tok, _ := p.scan()
switch p.peek() {
case TABLE:
return p.parseCreateTableStatement(pos)
case VIEW:
return p.parseCreateViewStatement(pos)
/*case INDEX, UNIQUE:
return p.parseCreateIndexStatement(pos)*/
case FUNCTION:
return p.parseCreateFunctionStatement(pos)
default:
return nil, p.errorExpected(pos, tok, "TABLE, VIEW or FUNCTION")
}
}
func (p *Parser) parseDropStatement() (Statement, error) {
assert(p.peek() == DROP)
pos, tok, _ := p.scan()
switch p.peek() {
case TABLE:
return p.parseDropTableStatement(pos)
case VIEW:
return p.parseDropViewStatement(pos)
/* case INDEX:
return p.parseDropIndexStatement(pos)*/
case FUNCTION:
return p.parseDropFunctionStatement(pos)
default:
return nil, p.errorExpected(pos, tok, "TABLE, VIEW or FUNCTION")
}
}
func (p *Parser) parseCreateTableStatement(createPos Pos) (_ *CreateTableStatement, err error) {
assert(p.peek() == TABLE)
var stmt CreateTableStatement
stmt.Create = createPos
stmt.Table, _, _ = p.scan()
// Parse optional "IF NOT EXISTS".
if p.peek() == IF {
stmt.If, _, _ = p.scan()
pos, tok, _ := p.scan()
if tok != NOT {
return &stmt, p.errorExpected(pos, tok, "NOT")
}
stmt.IfNot = pos
pos, tok, _ = p.scan()
if tok != EXISTS {
return &stmt, p.errorExpected(pos, tok, "EXISTS")
}
stmt.IfNotExists = pos
}
if stmt.Name, err = p.parseIdent("table name"); err != nil {
return &stmt, err
}
// Parse either a column/constraint list or build table from "AS <select>".
switch p.peek() {
case LP:
stmt.Lparen, _, _ = p.scan()
if stmt.Columns, err = p.parseColumnDefinitions(); err != nil {
return &stmt, err
} /*else if stmt.Constraints, err = p.parseTableConstraints(); err != nil {
return &stmt, err
}*/
if p.peek() != RP {
return &stmt, p.errorExpected(p.pos, p.tok, "right paren")
}
stmt.Rparen, _, _ = p.scan()
//look for table options
if stmt.Options, err = p.parseTableOptions(); err != nil {
return &stmt, err
}
return &stmt, nil
/*case AS:
stmt.As, _, _ = p.scan()
if stmt.Select, err = p.parseSelectStatement(false, nil); err != nil {
return &stmt, err
}
return &stmt, nil*/
default:
return &stmt, p.errorExpected(p.pos, p.tok, "left paren")
}
}
func (p *Parser) parseTableOptions() (_ []TableOption, err error) {
if !isTableOptionStartToken(p.peek()) {
return nil, nil
}
var a []TableOption
for {
if !isTableOptionStartToken(p.peek()) {
return a, nil
}
cons, err := p.parseTableOption()
if cons != nil {
a = append(a, cons)
}
if err != nil {
return a, err
}
}
}
func (p *Parser) parseTableOption() (_ TableOption, err error) {
assert(isTableOptionStartToken(p.peek()))
var optionPos Pos
// Parse table options.
switch p.peek() {
case KEYPARTITIONS:
return p.parseKeyPartitionsOption(optionPos)
default:
assert(p.peek() == COMMENT)
return p.parseCommentOption(optionPos)
}
}
func (p *Parser) parseCommentOption(optionPos Pos) (_ *CommentOption, err error) {
assert(p.peek() == COMMENT)
var opt CommentOption
opt.Comment, _, _ = p.scan()
if isLiteralToken(p.peek()) {
opt.Expr = p.mustParseLiteral()
} else {
return &opt, p.errorExpected(p.pos, p.tok, "literal")
}
return &opt, nil
}
func (p *Parser) parseKeyPartitionsOption(optionPos Pos) (_ *KeyPartitionsOption, err error) {
assert(p.peek() == KEYPARTITIONS)
var opt KeyPartitionsOption
opt.KeyPartitions, _, _ = p.scan()
if isLiteralToken(p.peek()) {
opt.Expr = p.mustParseLiteral()
} else {
return &opt, p.errorExpected(p.pos, p.tok, "literal")
}
return &opt, nil
}
func (p *Parser) parseColumnDefinitions() (_ []*ColumnDefinition, err error) {
var columns []*ColumnDefinition
for {
switch {
case isIdentToken(p.peek()):
col, err := p.parseColumnDefinition()
columns = append(columns, col)
if err != nil {
return columns, err
}
if p.peek() == COMMA {
p.scan()
}
case p.peek() == RP || isConstraintStartToken(p.peek(), true):
return columns, nil
default:
return columns, p.errorExpected(p.pos, p.tok, "column name, or right paren")
}
}
}
func (p *Parser) parseColumnDefinition() (_ *ColumnDefinition, err error) {
var col ColumnDefinition
if col.Name, err = p.parseIdent("column name"); err != nil {
return &col, err
} else if col.Type, err = p.parseType(); err != nil {
return &col, err
}
if col.Constraints, err = p.parseColumnConstraints(); err != nil {
return &col, err
}
return &col, nil
}
/*func (p *Parser) parseTableConstraints() (_ []Constraint, err error) {
if !isConstraintStartToken(p.peek(), true) {
return nil, nil
}
var a []Constraint
for {
cons, err := p.parseConstraint(true)
if cons != nil {
a = append(a, cons)
}
if err != nil {
return a, err
}
// Scan delimiting comma.
if p.peek() != COMMA {
return a, nil
}
p.scan()
}
}*/
func (p *Parser) parseColumnConstraints() (_ []Constraint, err error) {
var a []Constraint
for isConstraintStartToken(p.peek(), false) {
cons, err := p.parseConstraint(false)
a = append(a, cons)
if err != nil {
return a, err
}
}
return a, nil
}
func (p *Parser) parseConstraint(isTable bool) (_ Constraint, err error) {
assert(isConstraintStartToken(p.peek(), isTable))
var constraintPos Pos
var name *Ident
// Parse constraint name, if specified.
/*if p.peek() == CONSTRAINT {
constraintPos, _, _ = p.scan()
if name, err = p.parseIdent("constraint name"); err != nil {
return nil, err
}
}*/
// Table constraints only use a subset of column constraints.
/*if isTable {
switch p.peek() {
case PRIMARY:
return p.parsePrimaryKeyConstraint(constraintPos, name, isTable)
case UNIQUE:
return p.parseUniqueConstraint(constraintPos, name, isTable)
case CHECK:
return p.parseCheckConstraint(constraintPos, name)
default:
assert(p.peek() == FOREIGN)
return p.parseForeignKeyConstraint(constraintPos, name, isTable)
}
}*/
// Parse column constraints.
switch p.peek() {
//case PRIMARY:
// return p.parsePrimaryKeyConstraint(constraintPos, name, isTable)
//case NOT:
// return p.parseNotNullConstraint(constraintPos, name)
case MIN:
return p.parseMinConstraint(constraintPos, name)
case MAX:
return p.parseMaxConstraint(constraintPos, name)
case TIMEUNIT:
return p.parseTimeUnitConstraint(constraintPos, name)
case TIMEQUANTUM:
return p.parseTimeQuantumConstraint(constraintPos, name)
//case UNIQUE:
// return p.parseUniqueConstraint(constraintPos, name, isTable)
//case CHECK:
// return p.parseCheckConstraint(constraintPos, name)
//case DEFAULT:
// return p.parseDefaultConstraint(constraintPos, name)
default:
assert(p.peek() == CACHETYPE)
return p.parseCacheTypeConstraint(constraintPos, name)
}
}
/*func (p *Parser) parsePrimaryKeyConstraint(constraintPos Pos, name *Ident, isTable bool) (_ *PrimaryKeyConstraint, err error) {
assert(p.peek() == PRIMARY)
var cons PrimaryKeyConstraint
cons.Constraint = constraintPos
cons.Name = name
cons.Primary, _, _ = p.scan()
if p.peek() != KEY {
return &cons, p.errorExpected(p.pos, p.tok, "KEY")
}
cons.Key, _, _ = p.scan()
// Table constraints specify columns; column constraints specify sort direction.
if isTable {
if p.peek() != LP {
return &cons, p.errorExpected(p.pos, p.tok, "left paren")
}
cons.Lparen, _, _ = p.scan()
for {
col, err := p.parseIdent("column name")
if err != nil {
return &cons, err
}
cons.Columns = append(cons.Columns, col)
if p.peek() == RP {
break
} else if p.peek() != COMMA {
return &cons, p.errorExpected(p.pos, p.tok, "comma or right paren")
}
p.scan()
}
cons.Rparen, _, _ = p.scan()
}
if !isTable {
if p.peek() == AUTOINCREMENT {
cons.Autoincrement, _, _ = p.scan()
}
}
return &cons, nil
}*/
/*func (p *Parser) parseNotNullConstraint(constraintPos Pos, name *Ident) (_ *NotNullConstraint, err error) {
assert(p.peek() == NOT)
var cons NotNullConstraint
cons.Constraint = constraintPos
cons.Name = name
cons.Not, _, _ = p.scan()
if p.peek() != NULL {
return &cons, p.errorExpected(p.pos, p.tok, "NULL")
}
cons.Null, _, _ = p.scan()
return &cons, nil
}*/
func (p *Parser) parseMinConstraint(constraintPos Pos, name *Ident) (_ *MinConstraint, err error) {
assert(p.peek() == MIN)
var cons MinConstraint
cons.Min, _, _ = p.scan()
// This parses an expression, as opposed to just a literal, because a
// negative value is a unary expression with Op = MINUS, so we need to allow
// for that.
expr, err := p.ParseExpr()
if err != nil {
return nil, err
}
cons.Expr = expr
return &cons, nil
}
func (p *Parser) parseMaxConstraint(constraintPos Pos, name *Ident) (_ *MaxConstraint, err error) {
assert(p.peek() == MAX)
var cons MaxConstraint
cons.Max, _, _ = p.scan()
// This parses an expression, as opposed to just a literal, because a
// negative value is a unary expression with Op = MINUS, so we need to allow
// for that.
expr, err := p.ParseExpr()
if err != nil {
return nil, err
}
cons.Expr = expr
return &cons, nil
}
func (p *Parser) parseCacheTypeConstraint(constraintPos Pos, name *Ident) (_ *CacheTypeConstraint, err error) {
assert(p.peek() == CACHETYPE)
var cons CacheTypeConstraint
cons.CacheType, _, _ = p.scan()
switch p.peek() {
case RANKED, LRU:
_, _, cacheTypeValue := p.scan()
// FeatureBase expects a lowercase cache type value.
cons.CacheTypeValue = strings.ToLower(cacheTypeValue)
default:
return &cons, p.errorExpected(p.pos, p.tok, "RANKED or LRU")
}
if p.peek() == SIZE {
cons.Size, _, _ = p.scan()
if isLiteralToken(p.peek()) {
cons.SizeExpr = p.mustParseLiteral()
} else {
return &cons, p.errorExpected(p.pos, p.tok, "literal")
}
}
return &cons, nil
}
func (p *Parser) parseTimeUnitConstraint(constraintPos Pos, name *Ident) (_ *TimeUnitConstraint, err error) {
assert(p.peek() == TIMEUNIT)
var cons TimeUnitConstraint
cons.TimeUnit, _, _ = p.scan()
if isLiteralToken(p.peek()) {
cons.Expr = p.mustParseLiteral()
} else {
return &cons, p.errorExpected(p.pos, p.tok, "literal")
}
if p.peek() == EPOCH {
cons.Epoch, _, _ = p.scan()
if isLiteralToken(p.peek()) {
cons.EpochExpr = p.mustParseLiteral()
} else {
return &cons, p.errorExpected(p.pos, p.tok, "literal")
}
}
return &cons, nil
}
func (p *Parser) parseTimeQuantumConstraint(constraintPos Pos, name *Ident) (_ *TimeQuantumConstraint, err error) {
assert(p.peek() == TIMEQUANTUM)
var cons TimeQuantumConstraint
cons.TimeQuantum, _, _ = p.scan()
if isLiteralToken(p.peek()) {
cons.Expr = p.mustParseLiteral()
} else {
return &cons, p.errorExpected(p.pos, p.tok, "literal")
}
if p.peek() == TTL {
cons.Ttl, _, _ = p.scan()
if isLiteralToken(p.peek()) {
cons.TtlExpr = p.mustParseLiteral()
} else {
return &cons, p.errorExpected(p.pos, p.tok, "literal")
}
}
return &cons, nil
}
/*func (p *Parser) parseUniqueConstraint(constraintPos Pos, name *Ident, isTable bool) (_ *UniqueConstraint, err error) {
assert(p.peek() == UNIQUE)
var cons UniqueConstraint
cons.Constraint = constraintPos
cons.Name = name
cons.Unique, _, _ = p.scan()
if isTable {
if p.peek() != LP {
return &cons, p.errorExpected(p.pos, p.tok, "left paren")
}
cons.Lparen, _, _ = p.scan()
for {
col, err := p.parseIdent("column name")
if err != nil {
return &cons, err
}
cons.Columns = append(cons.Columns, col)
if p.peek() == RP {
break
} else if p.peek() != COMMA {
return &cons, p.errorExpected(p.pos, p.tok, "comma or right paren")
}
p.scan()
}
cons.Rparen, _, _ = p.scan()
}
return &cons, nil
}*/
/*func (p *Parser) parseCheckConstraint(constraintPos Pos, name *Ident) (_ *CheckConstraint, err error) {
assert(p.peek() == CHECK)
var cons CheckConstraint
cons.Constraint = constraintPos
cons.Name = name
cons.Check, _, _ = p.scan()
if p.peek() != LP {
return &cons, p.errorExpected(p.pos, p.tok, "left paren")
}
cons.Lparen, _, _ = p.scan()
if cons.Expr, err = p.ParseExpr(); err != nil {
return &cons, err
}
if p.peek() != RP {
return &cons, p.errorExpected(p.pos, p.tok, "right paren")
}
cons.Rparen, _, _ = p.scan()
return &cons, nil
}*/
/*func (p *Parser) parseDefaultConstraint(constraintPos Pos, name *Ident) (_ *DefaultConstraint, err error) {
assert(p.peek() == DEFAULT)
var cons DefaultConstraint
cons.Constraint = constraintPos
cons.Name = name
cons.Default, _, _ = p.scan()
if isLiteralToken(p.peek()) {
cons.Expr = p.mustParseLiteral()
} else if p.peek() == PLUS || p.peek() == MINUS {
if cons.Expr, err = p.parseSignedNumber("signed number"); err != nil {
return &cons, err
}
} else {
if p.peek() != LP {
return &cons, p.errorExpected(p.pos, p.tok, "literal value or left paren")
}
cons.Lparen, _, _ = p.scan()
if cons.Expr, err = p.ParseExpr(); err != nil {
return &cons, err
}
if p.peek() != RP {
return &cons, p.errorExpected(p.pos, p.tok, "right paren")
}
cons.Rparen, _, _ = p.scan()
}
return &cons, nil
}*/
/*func (p *Parser) parseForeignKeyConstraint(constraintPos Pos, name *Ident, isTable bool) (_ *ForeignKeyConstraint, err error) {
var cons ForeignKeyConstraint
cons.Constraint = constraintPos
cons.Name = name
// Table constraints start with "FOREIGN KEY (col1, col2, etc)".
if isTable {
assert(p.peek() == FOREIGN)
cons.Foreign, _, _ = p.scan()
if p.peek() != KEY {
return &cons, p.errorExpected(p.pos, p.tok, "KEY")
}
cons.ForeignKey, _, _ = p.scan()
if p.peek() != LP {
return &cons, p.errorExpected(p.pos, p.tok, "left paren")
}
cons.Lparen, _, _ = p.scan()
for {
col, err := p.parseIdent("column name")
if err != nil {
return &cons, err
}
cons.Columns = append(cons.Columns, col)
if p.peek() == RP {
break
} else if p.peek() != COMMA {
return &cons, p.errorExpected(p.pos, p.tok, "comma or right paren")
}
p.scan()
}
cons.Rparen, _, _ = p.scan()
}
if p.peek() != REFERENCES {
return &cons, p.errorExpected(p.pos, p.tok, "REFERENCES")
}
cons.References, _, _ = p.scan()
if cons.ForeignTable, err = p.parseIdent("foreign table name"); err != nil {
return &cons, err
}
// Parse column list.
if p.peek() != LP {
return &cons, p.errorExpected(p.pos, p.tok, "left paren")
}
cons.ForeignLparen, _, _ = p.scan()
for {
col, err := p.parseIdent("foreign column name")
if err != nil {
return &cons, err
}
cons.ForeignColumns = append(cons.ForeignColumns, col)
if p.peek() == RP {
break
} else if p.peek() != COMMA {
return &cons, p.errorExpected(p.pos, p.tok, "comma or right paren")
}
p.scan()
}
cons.ForeignRparen, _, _ = p.scan()
// Parse foreign key args.
for p.peek() == ON {
var arg ForeignKeyArg
arg.On, _, _ = p.scan()
// Parse foreign key type.
if p.peek() == UPDATE {
arg.OnUpdate, _, _ = p.scan()
} else if p.peek() == DELETE {
arg.OnDelete, _, _ = p.scan()
} else {
return &cons, p.errorExpected(p.pos, p.tok, "UPDATE or DELETE")
}
// Parse foreign key action.
if p.peek() == SET {
arg.Set, _, _ = p.scan()
if p.peek() == NULL {
arg.SetNull, _, _ = p.scan()
} else if p.peek() == DEFAULT {
arg.SetDefault, _, _ = p.scan()
} else {
return &cons, p.errorExpected(p.pos, p.tok, "NULL or DEFAULT")
}
} else if p.peek() == CASCADE {
arg.Cascade, _, _ = p.scan()
} else if p.peek() == RESTRICT {
arg.Restrict, _, _ = p.scan()
} else if p.peek() == NO {
arg.No, _, _ = p.scan()
if p.peek() == ACTION {
arg.NoAction, _, _ = p.scan()
} else {
return &cons, p.errorExpected(p.pos, p.tok, "ACTION")
}
} else {
return &cons, p.errorExpected(p.pos, p.tok, "SET NULL, SET DEFAULT, CASCADE, RESTRICT, or NO ACTION")
}
cons.Args = append(cons.Args, &arg)
}
// Parse deferrable subclause.
if p.peek() == NOT || p.peek() == DEFERRABLE {
if p.peek() == NOT {
cons.Not, _, _ = p.scan()
if p.peek() != DEFERRABLE {
return &cons, p.errorExpected(p.pos, p.tok, "DEFERRABLE")
}
cons.NotDeferrable, _, _ = p.scan()
} else {
cons.Deferrable, _, _ = p.scan()
}
if p.peek() == INITIALLY {
cons.Initially, _, _ = p.scan()
if p.peek() == DEFERRED {
cons.InitiallyDeferred, _, _ = p.scan()
} else if p.peek() == IMMEDIATE {
cons.InitiallyImmediate, _, _ = p.scan()
}
}
}
return &cons, nil
}*/
func (p *Parser) parseDropTableStatement(dropPos Pos) (_ *DropTableStatement, err error) {
assert(p.peek() == TABLE)
var stmt DropTableStatement
stmt.Drop = dropPos
stmt.Table, _, _ = p.scan()
// Parse optional "IF EXISTS".
if p.peek() == IF {
stmt.If, _, _ = p.scan()
if p.peek() != EXISTS {
return &stmt, p.errorExpected(p.pos, p.tok, "EXISTS")
}
stmt.IfExists, _, _ = p.scan()
}
if stmt.Name, err = p.parseIdent("table name"); err != nil {
return &stmt, err
}
return &stmt, nil
}
func (p *Parser) parseCreateViewStatement(createPos Pos) (_ *CreateViewStatement, err error) {
assert(p.peek() == VIEW)
var stmt CreateViewStatement
stmt.Create = createPos
stmt.View, _, _ = p.scan()
// Parse optional "IF NOT EXISTS".
if p.peek() == IF {
stmt.If, _, _ = p.scan()
if p.peek() != NOT {
return &stmt, p.errorExpected(p.pos, p.tok, "NOT")
}
stmt.IfNot, _, _ = p.scan()
if p.peek() != EXISTS {
return &stmt, p.errorExpected(p.pos, p.tok, "EXISTS")
}
stmt.IfNotExists, _, _ = p.scan()
}
if stmt.Name, err = p.parseIdent("view name"); err != nil {
return &stmt, err
}
// Parse optional column list.
if p.peek() == LP {
stmt.Lparen, _, _ = p.scan()
for {
col, err := p.parseIdent("column name")
if err != nil {
return &stmt, err
}
stmt.Columns = append(stmt.Columns, col)
if p.peek() == RP {
break
} else if p.peek() != COMMA {
return &stmt, p.errorExpected(p.pos, p.tok, "comma or right paren")
}
p.scan()
}
stmt.Rparen, _, _ = p.scan()
}
// Parse "AS select-stmt"
if p.peek() != AS {
return &stmt, p.errorExpected(p.pos, p.tok, "AS")
}
stmt.As, _, _ = p.scan()
if stmt.Select, err = p.parseSelectStatement(false, nil); err != nil {
return &stmt, err
}
return &stmt, nil
}
func (p *Parser) parseDropViewStatement(dropPos Pos) (_ *DropViewStatement, err error) {
assert(p.peek() == VIEW)
var stmt DropViewStatement
stmt.Drop = dropPos
stmt.View, _, _ = p.scan()
// Parse optional "IF EXISTS".
if p.peek() == IF {
stmt.If, _, _ = p.scan()
if p.peek() != EXISTS {
return &stmt, p.errorExpected(p.pos, p.tok, "EXISTS")
}
stmt.IfExists, _, _ = p.scan()
}
if stmt.Name, err = p.parseIdent("view name"); err != nil {
return &stmt, err
}
return &stmt, nil
}
/*func (p *Parser) parseCreateIndexStatement(createPos Pos) (_ *CreateIndexStatement, err error) {
assert(p.peek() == INDEX || p.peek() == UNIQUE)
var stmt CreateIndexStatement
stmt.Create = createPos
if p.peek() == UNIQUE {
stmt.Unique, _, _ = p.scan()
}
if p.peek() != INDEX {
return &stmt, p.errorExpected(p.pos, p.tok, "INDEX")
}
stmt.Index, _, _ = p.scan()
// Parse optional "IF NOT EXISTS".
if p.peek() == IF {
stmt.If, _, _ = p.scan()
if p.peek() != NOT {
return &stmt, p.errorExpected(p.pos, p.tok, "NOT")
}
stmt.IfNot, _, _ = p.scan()
if p.peek() != EXISTS {
return &stmt, p.errorExpected(p.pos, p.tok, "EXISTS")
}
stmt.IfNotExists, _, _ = p.scan()
}
if stmt.Name, err = p.parseIdent("index name"); err != nil {
return &stmt, err
}
if p.peek() != ON {
return &stmt, p.errorExpected(p.pos, p.tok, "ON")
}
stmt.On, _, _ = p.scan()
if stmt.Table, err = p.parseIdent("table name"); err != nil {
return &stmt, err
}
// Parse optional column list.
if p.peek() != LP {
return &stmt, p.errorExpected(p.pos, p.tok, "left paren")
}
stmt.Lparen, _, _ = p.scan()
for {
col, err := p.parseIndexedColumn()
if err != nil {
return &stmt, err
}
stmt.Columns = append(stmt.Columns, col)
if p.peek() == RP {
break
} else if p.peek() != COMMA {
return &stmt, p.errorExpected(p.pos, p.tok, "comma or right paren")
}
p.scan()
}
stmt.Rparen, _, _ = p.scan()
// Parse optional "WHERE expr"
if p.peek() == WHERE {
stmt.Where, _, _ = p.scan()
if stmt.WhereExpr, err = p.ParseExpr(); err != nil {
return &stmt, err
}
}
return &stmt, nil
}*/
/*func (p *Parser) parseDropIndexStatement(dropPos Pos) (_ *DropIndexStatement, err error) {
assert(p.peek() == INDEX)
var stmt DropIndexStatement
stmt.Drop = dropPos
stmt.Index, _, _ = p.scan()
// Parse optional "IF EXISTS".
if p.peek() == IF {
stmt.If, _, _ = p.scan()
if p.peek() != EXISTS {
return &stmt, p.errorExpected(p.pos, p.tok, "EXISTS")
}
stmt.IfExists, _, _ = p.scan()
}
if stmt.Name, err = p.parseIdent("index name"); err != nil {
return &stmt, err
}
return &stmt, nil
}*/
func (p *Parser) parseParameterDefinitions() (_ []*ParameterDefinition, err error) {
var params []*ParameterDefinition
for {
switch {
case p.peek() == VARIABLE:
col, err := p.parseParameterDefinition()
params = append(params, col)
if err != nil {
return params, err
}
if p.peek() == COMMA {
p.scan()
}
case p.peek() == RP:
return params, nil
default:
return params, p.errorExpected(p.pos, p.tok, "parameter name, or right paren")
}
}
}
func (p *Parser) parseParameterDefinition() (_ *ParameterDefinition, err error) {
var param ParameterDefinition
if param.Name, err = p.parseVariable("parameter name"); err != nil {
return &param, err
} else if param.Type, err = p.parseType(); err != nil {
return &param, err
}
return &param, nil
}
func (p *Parser) parseCreateFunctionStatement(createPos Pos) (_ *CreateFunctionStatement, err error) {
assert(p.peek() == FUNCTION)
var stmt CreateFunctionStatement
stmt.Create = createPos
stmt.Function, _, _ = p.scan()
// Parse optional "IF NOT EXISTS".
if p.peek() == IF {
stmt.If, _, _ = p.scan()
if p.peek() != NOT {
return &stmt, p.errorExpected(p.pos, p.tok, "NOT")
}
stmt.IfNot, _, _ = p.scan()
if p.peek() != EXISTS {
return &stmt, p.errorExpected(p.pos, p.tok, "EXISTS")
}
stmt.IfNotExists, _, _ = p.scan()
}
if stmt.Name, err = p.parseIdent("function name"); err != nil {
return &stmt, err
}
// parameters
if p.peek() == LP {
stmt.Lparen, _, _ = p.scan()
stmt.Parameters, err = p.parseParameterDefinitions()
if err != nil {
return &stmt, err
}
if p.peek() != RP {
return &stmt, p.errorExpected(p.pos, p.tok, ")")
}
stmt.Rparen, _, _ = p.scan()
}
if p.peek() != RETURNS {
return &stmt, p.errorExpected(p.pos, p.tok, "RETURNS")
}
stmt.Returns, _, _ = p.scan()
stmt.ReturnDef, err = p.parseParameterDefinition()
if err != nil {
return &stmt, err
}
if p.peek() != AS {
return &stmt, p.errorExpected(p.pos, p.tok, "AS")
}
stmt.As, _, _ = p.scan()
if p.peek() != BEGIN {
return &stmt, p.errorExpected(p.pos, p.tok, "BEGIN")
}
stmt.Begin, _, _ = p.scan()
for {
s, err := p.parseFunctionBodyStatement()
if err != nil {
return &stmt, err
}
if s != nil {
stmt.Body = append(stmt.Body, s)
}
if p.peek() == END {
break
}
}
if p.peek() != END {
return &stmt, p.errorExpected(p.pos, p.tok, "END")
}
stmt.End, _, _ = p.scan()
return &stmt, nil
}
func (p *Parser) parseFunctionBodyStatement() (stmt Statement, err error) {
switch p.peek() {
case END:
break
default:
return nil, p.errorExpected(p.pos, p.tok, "statement")
}
if err != nil {
return stmt, err
}
return stmt, nil
}
func (p *Parser) parseDropFunctionStatement(dropPos Pos) (_ *DropFunctionStatement, err error) {
assert(p.peek() == FUNCTION)
var stmt DropFunctionStatement
stmt.Drop = dropPos
stmt.Trigger, _, _ = p.scan()
// Parse optional "IF EXISTS".
if p.peek() == IF {
stmt.If, _, _ = p.scan()
if p.peek() != EXISTS {
return &stmt, p.errorExpected(p.pos, p.tok, "EXISTS")
}
stmt.IfExists, _, _ = p.scan()
}
if stmt.Name, err = p.parseIdent("function name"); err != nil {
return &stmt, err
}
return &stmt, nil
}
func (p *Parser) parseIdent(desc string) (*Ident, error) {
pos, tok, lit := p.scan()
switch tok {
case IDENT, QIDENT:
return &Ident{Name: lit, NamePos: pos, Quoted: tok == QIDENT}, nil
default:
return nil, p.errorExpected(pos, tok, desc)
}
}
func (p *Parser) parseVariable(desc string) (*Variable, error) {
pos, tok, lit := p.scan()
switch tok {
case VARIABLE:
return &Variable{
Name: lit,
NamePos: pos,
}, nil
default:
return nil, p.errorExpected(pos, tok, desc)
}
}
func (p *Parser) parseType() (_ *Type, err error) {
var typ Type
if typ.Name, err = p.parseIdent("type name"); err != nil {
return &typ, err
}
// Optionally parse scale.
if p.peek() == LP {
typ.Lparen, _, _ = p.scan()
if typ.Scale, err = p.parseIntegerLiteral("scale"); err != nil {
return &typ, err
}
if p.peek() != RP {
return nil, p.errorExpected(p.pos, p.tok, "right paren")
}
typ.Rparen, _, _ = p.scan()
}
return &typ, nil
}
func (p *Parser) parseBulkInsertStatement() (_ *BulkInsertStatement, err error) {
if p.peek() != BULK {
return nil, p.errorExpected(p.pos, p.tok, "BULK")
}
var stmt BulkInsertStatement
stmt.Bulk, _, _ = p.scan()
if pk := p.peek(); pk != INSERT && pk != REPLACE {
return nil, p.errorExpected(p.pos, p.tok, "INSERT or REPLACE")
}
if p.peek() == INSERT {
stmt.Insert, _, _ = p.scan()
} else {
stmt.Replace, _, _ = p.scan()
}
if p.peek() != INTO {
return nil, p.errorExpected(p.pos, p.tok, "INTO")
}
stmt.Into, _, _ = p.scan()
// Parse table name
if stmt.Table, err = p.parseIdent("table name"); err != nil {
return nil, err
}
if p.peek() == LP {
stmt.ColumnsLparen, _, _ = p.scan()
for {
col, err := p.parseIdent("column name")
if err != nil {
return &stmt, err
}
stmt.Columns = append(stmt.Columns, col)
if p.peek() == RP {
break
} else if p.peek() != COMMA {
return &stmt, p.errorExpected(p.pos, p.tok, "comma or right paren")
}
p.scan()
}
stmt.ColumnsRparen, _, _ = p.scan()
}
if p.peek() != MAP {
return &stmt, p.errorExpected(p.pos, p.tok, "MAP")
}
stmt.Map, _, _ = p.scan()
if p.peek() != LP {
return &stmt, p.errorExpected(p.pos, p.tok, "left paren")
}
stmt.MapLparen, _, _ = p.scan()
mapIdx := 0
for {
expr, err := p.ParseExpr()
if err != nil {
return &stmt, err
}
mapType, err := p.parseType()
if err != nil {
return &stmt, err
}
stmt.MapList = append(stmt.MapList, &BulkInsertMapDefinition{
Name: &Ident{
Name: fmt.Sprintf("%d", mapIdx),
},
MapExpr: expr,
Type: mapType,
})
mapIdx++
if p.peek() == RP {
break
} else if p.peek() != COMMA {
return &stmt, p.errorExpected(p.pos, p.tok, "comma or right paren")
}
p.scan()
}
stmt.MapRparen, _, _ = p.scan()
if p.peek() == TRANSFORM {
stmt.Transform, _, _ = p.scan()
if p.peek() != LP {
return &stmt, p.errorExpected(p.pos, p.tok, "left paren")
}
stmt.TransformLparen, _, _ = p.scan()
for {
expr, err := p.ParseExpr()
if err != nil {
return &stmt, err
}
stmt.TransformList = append(stmt.TransformList, expr)
if p.peek() == RP {
break
} else if p.peek() != COMMA {
return &stmt, p.errorExpected(p.pos, p.tok, "comma or right paren")
}
p.scan()
}
stmt.TransformRparen, _, _ = p.scan()
}
if p.peek() != FROM {
return nil, p.errorExpected(p.pos, p.tok, "FROM")
}
stmt.From, _, _ = p.scan()
if isLiteralToken(p.peek()) {
stmt.DataSource = p.mustParseLiteral()
} else {
return nil, p.errorExpected(p.pos, p.tok, "literal")
}
if p.peek() != WITH {
return nil, p.errorExpected(p.pos, p.tok, "WITH")
}
stmt.With, _, _ = p.scan()
if !isBulkInsertOptionStartToken(p.peek(), p) {
return nil, p.errorExpected(p.pos, p.tok, "BATCHSIZE, ROWSLIMIT, FORMAT, INPUT, ALLOW_MISSING_VALUES or HEADER_ROW")
}
for {
err := p.parseBulkInsertOption(&stmt)
if err != nil {
return nil, err
}
if !isBulkInsertOptionStartToken(p.peek(), p) {
break
}
}
return &stmt, nil
}
func (p *Parser) parseBulkInsertOption(stmt *BulkInsertStatement) error {
switch p.peek() {
case IDENT:
ident, err := p.parseIdent("bulk insert option")
if err != nil {
return err
}
switch strings.ToUpper(ident.Name) {
case "BATCHSIZE":
if isLiteralToken(p.peek()) {
stmt.BatchSize = p.mustParseLiteral()
return nil
} else {
return p.errorExpected(p.pos, p.tok, "literal")
}
case "ROWSLIMIT":
if isLiteralToken(p.peek()) {
stmt.RowsLimit = p.mustParseLiteral()
return nil
} else {
return p.errorExpected(p.pos, p.tok, "literal")
}
case "FORMAT":
if isLiteralToken(p.peek()) {
stmt.Format = p.mustParseLiteral()
return nil
} else {
return p.errorExpected(p.pos, p.tok, "literal")
}
case "INPUT":
if isLiteralToken(p.peek()) {
stmt.Input = p.mustParseLiteral()
return nil
} else {
return p.errorExpected(p.pos, p.tok, "literal")
}
case "ALLOW_MISSING_VALUES":
stmt.AllowMissingValues = ident
return nil
case "HEADER_ROW":
stmt.HeaderRow = ident
return nil
}
}
return p.errorExpected(p.pos, p.tok, "BATCHSIZE, ROWSLIMIT, FORMAT, INPUT or HEADER_ROW")
}
func (p *Parser) parseInsertStatement(withClause *WithClause) (_ *InsertStatement, err error) {
if pk := p.peek(); pk != INSERT && pk != REPLACE {
return nil, p.errorExpected(p.pos, p.tok, "INSERT or REPLACE")
}
var stmt InsertStatement
//stmt.WithClause = withClause
if p.peek() == INSERT {
stmt.Insert, _, _ = p.scan()
/*if p.peek() == OR {
stmt.InsertOr, _, _ = p.scan()
switch p.peek() {
//case ROLLBACK:
// stmt.InsertOrRollback, _, _ = p.scan()
case REPLACE:
stmt.InsertOrReplace, _, _ = p.scan()
//case ABORT:
// stmt.InsertOrAbort, _, _ = p.scan()
//case FAIL:
// stmt.InsertOrFail, _, _ = p.scan()
//case IGNORE:
// stmt.InsertOrIgnore, _, _ = p.scan()
default:
return &stmt, p.errorExpected(p.pos, p.tok, "REPLACE")
}
} */
} else {
stmt.Replace, _, _ = p.scan()
}
if p.peek() != INTO {
return &stmt, p.errorExpected(p.pos, p.tok, "INTO")
}
stmt.Into, _, _ = p.scan()
// Parse table name & optional alias.
if stmt.Table, err = p.parseIdent("table name"); err != nil {
return &stmt, err
}
if p.peek() == AS {
stmt.As, _, _ = p.scan()
if stmt.Alias, err = p.parseIdent("alias"); err != nil {
return &stmt, err
}
}
// Parse optional column list.
if p.peek() == LP {
stmt.ColumnsLparen, _, _ = p.scan()
for {
col, err := p.parseIdent("column name")
if err != nil {
return &stmt, err
}
stmt.Columns = append(stmt.Columns, col)
if p.peek() == RP {
break
} else if p.peek() != COMMA {
return &stmt, p.errorExpected(p.pos, p.tok, "comma or right paren")
}
p.scan()
}
stmt.ColumnsRparen, _, _ = p.scan()
}
switch p.peek() {
case VALUES:
stmt.Values, _, _ = p.scan()
// Parse out the value tuples.
stmt.TupleList = make([]*ExprList, 0)
for {
var tuple ExprList
if p.peek() != LP {
return &stmt, p.errorExpected(p.pos, p.tok, "left paren")
}
tuple.Lparen, _, _ = p.scan()
for {
expr, err := p.ParseExpr()
if err != nil {
return &stmt, err
}
tuple.Exprs = append(tuple.Exprs, expr)
if p.peek() == RP {
break
} else if p.peek() != COMMA {
return &stmt, p.errorExpected(p.pos, p.tok, "comma or right paren")
}
p.scan()
}
tuple.Rparen, _, _ = p.scan()
stmt.TupleList = append(stmt.TupleList, &tuple)
if p.peek() != COMMA {
break
}
p.scan()
}
//case SELECT:
// if stmt.Select, err = p.parseSelectStatement(false, nil); err != nil {
// return &stmt, err
// }
//case DEFAULT:
// stmt.Default, _, _ = p.scan()
// if p.peek() != VALUES {
// return &stmt, p.errorExpected(p.pos, p.tok, "VALUES")
// }
// stmt.DefaultValues, _, _ = p.scan()
default:
return &stmt, p.errorExpected(p.pos, p.tok, "VALUES")
}
// Parse optional upsert clause.
//if p.peek() == ON {
// if stmt.UpsertClause, err = p.parseUpsertClause(); err != nil {
// return &stmt, err
// }
//}
return &stmt, nil
}
/*func (p *Parser) parseUpsertClause() (_ *UpsertClause, err error) {
assert(p.peek() == ON)
var clause UpsertClause
// Parse "ON CONFLICT"
clause.On, _, _ = p.scan()
if p.peek() != CONFLICT {
return &clause, p.errorExpected(p.pos, p.tok, "CONFLICT")
}
clause.OnConflict, _, _ = p.scan()
// Parse optional indexed column list & WHERE conditional.
if p.peek() == LP {
clause.Lparen, _, _ = p.scan()
for {
col, err := p.parseIndexedColumn()
if err != nil {
return &clause, err
}
clause.Columns = append(clause.Columns, col)
if p.peek() == RP {
break
} else if p.peek() != COMMA {
return &clause, p.errorExpected(p.pos, p.tok, "comma or right paren")
}
p.scan()
}
clause.Rparen, _, _ = p.scan()
if p.peek() == WHERE {
clause.Where, _, _ = p.scan()
if clause.WhereExpr, err = p.ParseExpr(); err != nil {
return &clause, err
}
}
}
// Parse "DO NOTHING" or "DO UPDATE SET".
if p.peek() != DO {
return &clause, p.errorExpected(p.pos, p.tok, "DO")
}
clause.Do, _, _ = p.scan()
// If next token is NOTHING, then read it and exit immediately.
if p.peek() == NOTHING {
clause.DoNothing, _, _ = p.scan()
return &clause, nil
} else if p.peek() != UPDATE {
return &clause, p.errorExpected(p.pos, p.tok, "NOTHING or UPDATE SET")
}
// Otherwise parse "UPDATE SET"
clause.DoUpdate, _, _ = p.scan()
if p.peek() != SET {
return &clause, p.errorExpected(p.pos, p.tok, "SET")
}
clause.DoUpdateSet, _, _ = p.scan()
// Parse list of assignments.
for {
assignment, err := p.parseAssignment()
if err != nil {
return &clause, err
}
clause.Assignments = append(clause.Assignments, assignment)
if p.peek() != COMMA {
break
}
p.scan()
}
// Parse WHERE after DO UPDATE SET.
if p.peek() == WHERE {
clause.UpdateWhere, _, _ = p.scan()
if clause.UpdateWhereExpr, err = p.ParseExpr(); err != nil {
return &clause, err
}
}
return &clause, nil
}*/
/*func (p *Parser) parseIndexedColumn() (_ *IndexedColumn, err error) {
var col IndexedColumn
if col.X, err = p.ParseExpr(); err != nil {
return &col, err
}
if p.peek() == ASC {
col.Asc, _, _ = p.scan()
} else if p.peek() == DESC {
col.Desc, _, _ = p.scan()
}
return &col, nil
}*/
func (p *Parser) parseUpdateStatement(withClause *WithClause) (_ *UpdateStatement, err error) {
assert(p.peek() == UPDATE)
var stmt UpdateStatement
stmt.WithClause = withClause
stmt.Update, _, _ = p.scan()
if p.peek() == OR {
stmt.UpdateOr, _, _ = p.scan()
switch p.peek() {
case ROLLBACK:
stmt.UpdateOrRollback, _, _ = p.scan()
case REPLACE:
stmt.UpdateOrReplace, _, _ = p.scan()
case ABORT:
stmt.UpdateOrAbort, _, _ = p.scan()
case FAIL:
stmt.UpdateOrFail, _, _ = p.scan()
case IGNORE:
stmt.UpdateOrIgnore, _, _ = p.scan()
default:
return &stmt, p.errorExpected(p.pos, p.tok, "ROLLBACK, REPLACE, ABORT, FAIL, or IGNORE")
}
}
ident, err := p.parseIdent("table name")
if err != nil {
return &stmt, err
}
stmt.Table, err = p.parseQualifiedTableName(ident)
if err != nil {
return &stmt, err
}
// Parse SET + list of assignments.
if p.peek() != SET {
return &stmt, p.errorExpected(p.pos, p.tok, "SET")
}
stmt.Set, _, _ = p.scan()
for {
assignment, err := p.parseAssignment()
if err != nil {
return &stmt, err
}
stmt.Assignments = append(stmt.Assignments, assignment)
if p.peek() != COMMA {
break
}
p.scan()
}
// Parse WHERE clause.
if p.peek() == WHERE {
stmt.Where, _, _ = p.scan()
if stmt.WhereExpr, err = p.ParseExpr(); err != nil {
return &stmt, err
}
}
return &stmt, nil
}
func (p *Parser) parseDeleteStatement(withClause *WithClause) (_ *DeleteStatement, err error) {
assert(p.peek() == DELETE)
var stmt DeleteStatement
stmt.WithClause = withClause
// Parse "DELETE FROM tbl"
stmt.Delete, _, _ = p.scan()
if p.peek() != FROM {
return &stmt, p.errorExpected(p.pos, p.tok, "FROM")
}
stmt.From, _, _ = p.scan()
ident, err := p.parseIdent("table name")
if err != nil {
return &stmt, err
}
stmt.Table, err = p.parseQualifiedTableName(ident)
if err != nil {
return &stmt, err
}
// Parse WHERE clause.
if p.peek() == WHERE {
stmt.Where, _, _ = p.scan()
if stmt.WhereExpr, err = p.ParseExpr(); err != nil {
return &stmt, err
}
}
// Parse ORDER BY clause. This differs from the SELECT parsing in that
// if an ORDER BY is specified then the LIMIT is required.
if p.peek() == ORDER {
if p.peek() == ORDER {
stmt.Order, _, _ = p.scan()
if p.peek() != BY {
return &stmt, p.errorExpected(p.pos, p.tok, "BY")
}
stmt.OrderBy, _, _ = p.scan()
for {
term, err := p.parseOrderingTerm()
if err != nil {
return &stmt, err
}
stmt.OrderingTerms = append(stmt.OrderingTerms, term)
if p.peek() != COMMA {
break
}
p.scan()
}
}
}
return &stmt, nil
}
func (p *Parser) parseAssignment() (_ *Assignment, err error) {
var assignment Assignment
// Parse either a single column (IDENT) or a column list (LP IDENT COMMA IDENT RP)
if isIdentToken(p.peek()) {
col, _ := p.parseIdent("column name")
assignment.Columns = []*Ident{col}
} else if p.peek() == LP {
assignment.Lparen, _, _ = p.scan()
for {
col, err := p.parseIdent("column name")
if err != nil {
return &assignment, err
}
assignment.Columns = append(assignment.Columns, col)
if p.peek() == RP {
break
} else if p.peek() != COMMA {
return &assignment, p.errorExpected(p.pos, p.tok, "comma or right paren")
}
p.scan()
}
assignment.Rparen, _, _ = p.scan()
} else {
return &assignment, p.errorExpected(p.pos, p.tok, "column name or column list")
}
if p.peek() != EQ {
return &assignment, p.errorExpected(p.pos, p.tok, "=")
}
assignment.Eq, _, _ = p.scan()
if assignment.Expr, err = p.ParseExpr(); err != nil {
return &assignment, err
}
return &assignment, nil
}
// parseSelectStatement parses a SELECT statement.
// If compounded is true, some parts of the SELECT syntax are skipped.
func (p *Parser) parseSelectStatement(compounded bool, withClause *WithClause) (_ *SelectStatement, err error) {
var stmt SelectStatement
//stmt.WithClause = withClause
// Parse optional "WITH [RECURSIVE} cte, cte..."
// This is only called here if this method is called directly. Generic
// statement parsing will parse the WITH clause and pass it in instead.
//if !compounded && stmt.WithClause == nil && p.peek() == WITH {
// if stmt.WithClause, err = p.parseWithClause(); err != nil {
// return &stmt, err
// }
//}
switch p.peek() {
/*case VALUES:
stmt.Values, _, _ = p.scan()
for {
var list ExprList
if p.peek() != LP {
return &stmt, p.errorExpected(p.pos, p.tok, "left paren")
}
list.Lparen, _, _ = p.scan()
for {
expr, err := p.ParseExpr()
if err != nil {
return &stmt, err
}
list.Exprs = append(list.Exprs, expr)
if p.peek() == RP {
break
} else if p.peek() != COMMA {
return &stmt, p.errorExpected(p.pos, p.tok, "comma or right paren")
}
p.scan()
}
list.Rparen, _, _ = p.scan()
stmt.ValueLists = append(stmt.ValueLists, &list)
if p.peek() != COMMA {
break
}
p.scan()
}*/
case SELECT:
stmt.Select, _, _ = p.scan()
// Parse optional "DISTINCT".
if tok := p.peek(); tok == DISTINCT {
stmt.Distinct, _, _ = p.scan()
}
if p.peek() == TOP {
stmt.Top, _, _ = p.scan()
if p.peek() == LP {
_, _, _ = p.scan()
}
if stmt.TopExpr, err = p.ParseExpr(); err != nil {
return &stmt, err
}
if p.peek() == RP {
_, _, _ = p.scan()
}
}
if p.peek() == TOPN {
stmt.TopN, _, _ = p.scan()
if p.peek() == LP {
_, _, _ = p.scan()
}
if stmt.TopExpr, err = p.ParseExpr(); err != nil {
return &stmt, err
}
if p.peek() == RP {
_, _, _ = p.scan()
}
}
// Parse result columns.
for {
col, err := p.parseResultColumn()
if err != nil {
return &stmt, err
}
stmt.Columns = append(stmt.Columns, col)
if p.peek() != COMMA {
break
}
p.scan()
}
// Parse FROM clause.
if p.peek() == FROM {
stmt.From, _, _ = p.scan()
if stmt.Source, err = p.parseSource(); err != nil {
return &stmt, err
}
}
// Parse WHERE clause.
if p.peek() == WHERE {
stmt.Where, _, _ = p.scan()
if stmt.WhereExpr, err = p.ParseExpr(); err != nil {
return &stmt, err
}
}
// Parse GROUP BY/HAVING clause.
if p.peek() == GROUP {
stmt.Group, _, _ = p.scan()
if p.peek() != BY {
return &stmt, p.errorExpected(p.pos, p.tok, "BY")
}
stmt.GroupBy, _, _ = p.scan()
for {
expr, err := p.ParseExpr()
if err != nil {
return &stmt, err
}
stmt.GroupByExprs = append(stmt.GroupByExprs, expr)
if p.peek() != COMMA {
break
}
p.scan()
}
// Parse optional HAVING clause.
if p.peek() == HAVING {
stmt.Having, _, _ = p.scan()
if stmt.HavingExpr, err = p.ParseExpr(); err != nil {
return &stmt, err
}
}
}
// Parse WINDOW clause.
if p.peek() == WINDOW {
stmt.Window, _, _ = p.scan()
for {
var window Window
if window.Name, err = p.parseIdent("window name"); err != nil {
return &stmt, err
}
if p.peek() != AS {
return &stmt, p.errorExpected(p.pos, p.tok, "AS")
}
window.As, _, _ = p.scan()
if window.Definition, err = p.parseWindowDefinition(); err != nil {
return &stmt, err
}
stmt.Windows = append(stmt.Windows, &window)
if p.peek() != COMMA {
break
}
p.scan()
}
}
default:
return &stmt, p.errorExpected(p.pos, p.tok, "SELECT")
}
// Optionally compound additional SELECT/VALUES.
switch tok := p.peek(); tok {
case UNION, INTERSECT, EXCEPT:
if tok == UNION {
stmt.Union, _, _ = p.scan()
if p.peek() == ALL {
stmt.UnionAll, _, _ = p.scan()
}
} else if tok == INTERSECT {
stmt.Intersect, _, _ = p.scan()
} else {
stmt.Except, _, _ = p.scan()
}
if stmt.Compound, err = p.parseSelectStatement(true, nil); err != nil {
return &stmt, err
}
}
// Parse ORDER BY clause.
if !compounded && p.peek() == ORDER {
stmt.Order, _, _ = p.scan()
if p.peek() != BY {
return &stmt, p.errorExpected(p.pos, p.tok, "BY")
}
stmt.OrderBy, _, _ = p.scan()
for {
term, err := p.parseOrderingTerm()
if err != nil {
return &stmt, err
}
stmt.OrderingTerms = append(stmt.OrderingTerms, term)
if p.peek() != COMMA {
break
}
p.scan()
}
}
return &stmt, nil
}
func (p *Parser) parseResultColumn() (_ *ResultColumn, err error) {
var col ResultColumn
// An initial "*" returns all columns.
if p.peek() == STAR {
col.Star, _, _ = p.scan()
return &col, nil
}
// Next can be either "EXPR [[AS] column-alias]" or "IDENT DOT STAR".
// We need read the next element as an expression and then determine what next.
if col.Expr, err = p.ParseExpr(); err != nil {
return &col, err
}
// If we have a qualified ref w/ a star, don't allow an alias.
if ref, ok := col.Expr.(*QualifiedRef); ok && ref.Star.IsValid() {
return &col, nil
}
// If "AS" is next, the alias must follow.
// Otherwise it can optionally be an IDENT alias.
if p.peek() == AS {
col.As, _, _ = p.scan()
if !isIdentToken(p.peek()) {
return &col, p.errorExpected(p.pos, p.tok, "column alias")
}
col.Alias, _ = p.parseIdent("column alias")
} else if isIdentToken(p.peek()) {
col.Alias, _ = p.parseIdent("column alias")
}
return &col, nil
}
func (p *Parser) parseSource() (source Source, err error) {
source, err = p.parseUnarySource()
if err != nil {
return source, err
}
for {
// Exit immediately if not part of a join operator.
switch p.peek() {
case COMMA, NATURAL, LEFT, INNER, CROSS, JOIN:
default:
return source, nil
}
// Parse join operator.
operator, err := p.parseJoinOperator()
if err != nil {
return source, err
}
y, err := p.parseUnarySource()
if err != nil {
return source, err
}
constraint, err := p.parseJoinConstraint()
if err != nil {
return source, err
}
// Rewrite last source to nest next join on right side.
if lhs, ok := source.(*JoinClause); ok {
source = &JoinClause{
X: lhs.X,
Operator: lhs.Operator,
Y: &JoinClause{
X: lhs.Y,
Operator: operator,
Y: y,
Constraint: constraint,
},
Constraint: lhs.Constraint,
}
} else {
source = &JoinClause{X: source, Operator: operator, Y: y, Constraint: constraint}
}
}
}
// parseUnarySource parses a quailfied table name or subquery but not a JOIN.
func (p *Parser) parseUnarySource() (source Source, err error) {
switch p.peek() {
case LP:
return p.parseParenSource()
case IDENT, QIDENT:
ident, err := p.parseIdent("table or function")
if err != nil {
return nil, err
}
if p.peek() == LP {
return p.parseTableValuedFunction(ident)
}
return p.parseQualifiedTableName(ident)
default:
return nil, p.errorExpected(p.pos, p.tok, "table name or left paren")
}
}
func (p *Parser) parseJoinOperator() (*JoinOperator, error) {
var op JoinOperator
// Handle single comma join.
if p.peek() == COMMA {
op.Comma, _, _ = p.scan()
return &op, nil
}
if p.peek() == NATURAL {
op.Natural, _, _ = p.scan()
}
// Parse "LEFT", "LEFT OUTER", "INNER", or "CROSS"
switch p.peek() {
case LEFT:
op.Left, _, _ = p.scan()
if p.peek() == OUTER {
op.Outer, _, _ = p.scan()
}
case INNER:
op.Inner, _, _ = p.scan()
case CROSS:
op.Cross, _, _ = p.scan()
}
// Parse final JOIN.
if p.peek() != JOIN {
return &op, p.errorExpected(p.pos, p.tok, "JOIN")
}
op.Join, _, _ = p.scan()
return &op, nil
}
func (p *Parser) parseJoinConstraint() (JoinConstraint, error) {
switch p.peek() {
case ON:
return p.parseOnConstraint()
case USING:
return p.parseUsingConstraint()
default:
return nil, nil
}
}
func (p *Parser) parseOnConstraint() (_ *OnConstraint, err error) {
assert(p.peek() == ON)
var con OnConstraint
con.On, _, _ = p.scan()
if con.X, err = p.ParseExpr(); err != nil {
return &con, err
}
return &con, nil
}
func (p *Parser) parseUsingConstraint() (*UsingConstraint, error) {
assert(p.peek() == USING)
var con UsingConstraint
con.Using, _, _ = p.scan()
if p.peek() != LP {
return &con, p.errorExpected(p.pos, p.tok, "left paren")
}
con.Lparen, _, _ = p.scan()
for {
col, err := p.parseIdent("column name")
if err != nil {
return &con, err
}
con.Columns = append(con.Columns, col)
if p.peek() == RP {
break
} else if p.peek() != COMMA {
return &con, p.errorExpected(p.pos, p.tok, "comma or right paren")
}
p.scan()
}
con.Rparen, _, _ = p.scan()
return &con, nil
}
func (p *Parser) parseParenSource() (_ *ParenSource, err error) {
assert(p.peek() == LP)
var source ParenSource
source.Lparen, _, _ = p.scan()
if p.peek() == SELECT {
if source.X, err = p.parseSelectStatement(false, nil); err != nil {
return &source, err
}
} else {
if source.X, err = p.parseSource(); err != nil {
return &source, err
}
}
if p.peek() != RP {
return nil, p.errorExpected(p.pos, p.tok, "right paren")
}
source.Rparen, _, _ = p.scan()
// Only parse aliases for nested select statements.
if _, ok := source.X.(*SelectStatement); ok && (p.peek() == AS || isIdentToken(p.peek())) {
if p.peek() == AS {
source.As, _, _ = p.scan()
}
if source.Alias, err = p.parseIdent("table alias"); err != nil {
return &source, err
}
}
return &source, nil
}
func (p *Parser) parseQualifiedTableName(ident *Ident) (_ *QualifiedTableName, err error) {
var tbl QualifiedTableName
tbl.Name = ident
// Parse optional table alias ("AS alias" or just "alias").
if tok := p.peek(); tok == AS || isIdentToken(tok) {
if p.peek() == AS {
tbl.As, _, _ = p.scan()
}
if tbl.Alias, err = p.parseIdent("table alias"); err != nil {
return &tbl, err
}
}
// Parse optional "INDEXED BY index-name" or "NOT INDEXED".
/*switch p.peek() {
case INDEXED:
tbl.Indexed, _, _ = p.scan()
if p.peek() != BY {
return &tbl, p.errorExpected(p.pos, p.tok, "BY")
}
tbl.IndexedBy, _, _ = p.scan()
if tbl.Index, err = p.parseIdent("index name"); err != nil {
return &tbl, err
}
case NOT:
tbl.Not, _, _ = p.scan()
if p.peek() != INDEXED {
return &tbl, p.errorExpected(p.pos, p.tok, "INDEXED")
}
tbl.NotIndexed, _, _ = p.scan()
}*/
return &tbl, nil
}
func (p *Parser) parseTableValuedFunction(ident *Ident) (_ *TableValuedFunction, err error) {
var tbl TableValuedFunction
tbl.Name = ident
tbl.Call, err = p.parseCall(ident)
if err != nil {
return &tbl, err
}
// Parse optional table alias ("AS alias" or just "alias").
if tok := p.peek(); tok == AS || isIdentToken(tok) {
if p.peek() == AS {
tbl.As, _, _ = p.scan()
}
if tbl.Alias, err = p.parseIdent("table alias"); err != nil {
return &tbl, err
}
}
return &tbl, nil
}
/*func (p *Parser) parseWithClause() (*WithClause, error) {
assert(p.peek() == WITH)
var clause WithClause
clause.With, _, _ = p.scan()
if p.peek() == RECURSIVE {
clause.Recursive, _, _ = p.scan()
}
// Parse comma-delimited list of common table expressions (CTE).
for {
cte, err := p.parseCTE()
if err != nil {
return &clause, err
}
clause.CTEs = append(clause.CTEs, cte)
if p.peek() != COMMA {
break
}
p.scan()
}
return &clause, nil
}*/
/*func (p *Parser) parseCTE() (_ *CTE, err error) {
var cte CTE
if cte.TableName, err = p.parseIdent("table name"); err != nil {
return &cte, err
}
// Parse optional column list.
if p.peek() == LP {
cte.ColumnsLparen, _, _ = p.scan()
for {
column, err := p.parseIdent("column name")
if err != nil {
return &cte, err
}
cte.Columns = append(cte.Columns, column)
if p.peek() == RP {
break
} else if p.peek() != COMMA {
return nil, p.errorExpected(p.pos, p.tok, "comma or right paren")
}
p.scan()
}
cte.ColumnsRparen, _, _ = p.scan()
}
if p.peek() != AS {
return nil, p.errorExpected(p.pos, p.tok, "AS")
}
cte.As, _, _ = p.scan()
// Parse select statement.
if p.peek() != LP {
return nil, p.errorExpected(p.pos, p.tok, "left paren")
}
cte.SelectLparen, _, _ = p.scan()
if cte.Select, err = p.parseSelectStatement(false, nil); err != nil {
return &cte, err
}
if p.peek() != RP {
return nil, p.errorExpected(p.pos, p.tok, "right paren")
}
cte.SelectRparen, _, _ = p.scan()
return &cte, nil
}*/
func (p *Parser) mustParseLiteral() Expr {
assert(isLiteralToken(p.tok))
pos, tok, lit := p.scan()
switch tok {
case STRING:
return &StringLit{ValuePos: pos, Value: lit}
case INTEGER:
return &IntegerLit{ValuePos: pos, Value: lit}
case FLOAT:
return &FloatLit{ValuePos: pos, Value: lit}
case TRUE, FALSE:
return &BoolLit{ValuePos: pos, Value: tok == TRUE}
case BLOB:
return &StringLit{ValuePos: pos, Value: lit}
default:
assert(tok == NULL)
return &NullLit{ValuePos: pos}
}
}
func (p *Parser) ParseExpr() (expr Expr, err error) {
return p.parseBinaryExpr(LowestPrec + 1)
}
func (p *Parser) parseOperand() (expr Expr, err error) {
pos, tok, lit := p.scan()
switch tok {
case IDENT, QIDENT:
ident := &Ident{Name: lit, NamePos: pos, Quoted: tok == QIDENT}
if p.peek() == DOT {
return p.parseQualifiedRef(ident)
} else if p.peek() == LP {
return p.parseCall(ident)
}
return ident, nil
case VARIABLE:
return &Variable{Name: lit, NamePos: pos}, nil
case MIN, MAX:
ident := &Ident{Name: lit, NamePos: pos, Quoted: tok == QIDENT}
return p.parseCall(ident)
case STRING:
return &StringLit{ValuePos: pos, Value: lit}, nil
case FLOAT:
return &FloatLit{ValuePos: pos, Value: lit}, nil
case INTEGER:
return &IntegerLit{ValuePos: pos, Value: lit}, nil
case NULL:
return &NullLit{ValuePos: pos}, nil
case CURRENT_DATE:
now := time.Now().UTC()
nowDate := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
return &DateLit{ValuePos: pos, Value: nowDate}, nil
case CURRENT_TIMESTAMP:
now := time.Now().UTC()
return &DateLit{ValuePos: pos, Value: now}, nil
case TRUE, FALSE:
return &BoolLit{ValuePos: pos, Value: tok == TRUE}, nil
case PLUS, MINUS, BITNOT:
expr, err = p.parseOperand()
if err != nil {
return nil, err
}
return &UnaryExpr{OpPos: pos, Op: tok, X: expr}, nil
case LP:
p.unscan()
return p.parseParenExpr()
case LB:
p.unscan()
return p.parseSetLiteralExpr()
case LBR:
p.unscan()
return p.parseTupleLiteralExpr()
case CAST:
p.unscan()
return p.parseCastExpr()
case CASE:
p.unscan()
return p.parseCaseExpr()
case NOT, EXISTS:
p.unscan()
return p.parseExists()
case SELECT:
p.unscan()
return p.parseSelectStatement(false, nil)
default:
return nil, p.errorExpected(p.pos, p.tok, "expression")
}
}
func (p *Parser) parseBinaryExpr(prec1 int) (expr Expr, err error) {
x, err := p.parseOperand()
if err != nil {
return nil, err
}
for {
if p.peek().Precedence() < prec1 {
return x, nil
}
pos, op, err := p.scanBinaryOp()
if err != nil {
return nil, err
}
switch op {
case IN, NOTIN:
y, err := p.parseExprList()
if err != nil {
return x, err
}
x = &BinaryExpr{X: x, OpPos: pos, Op: op, Y: y}
case BETWEEN, NOTBETWEEN:
// Parsing the expression should yield a binary expression with AND op.
// However, we don't want to conflate the boolean AND and the ranged AND
// so we convert the expression to a Range.
if rng, err := p.parseBinaryExpr(LowestPrec + 1); err != nil {
return x, err
} else if rng, ok := rng.(*BinaryExpr); !ok || rng.Op != AND {
return x, p.errorExpected(p.pos, p.tok, "range expression")
} else {
x = &BinaryExpr{
X: x,
OpPos: pos,
Op: op,
Y: &Range{X: rng.X, And: rng.OpPos, Y: rng.Y},
}
}
default:
y, err := p.parseBinaryExpr(op.Precedence() + 1)
if err != nil {
return nil, err
}
x = &BinaryExpr{X: x, OpPos: pos, Op: op, Y: y}
}
}
}
func (p *Parser) parseExprList() (_ *ExprList, err error) {
var list ExprList
if p.peek() != LP {
return &list, p.errorExpected(p.pos, p.tok, "left paren")
}
list.Lparen, _, _ = p.scan()
for p.peek() != RP {
x, err := p.ParseExpr()
if err != nil {
return &list, err
}
list.Exprs = append(list.Exprs, x)
if p.peek() == RP {
break
} else if p.peek() != COMMA {
return &list, p.errorExpected(p.pos, p.tok, "comma or right paren")
}
p.scan()
}
list.Rparen, _, _ = p.scan()
return &list, nil
}
func (p *Parser) parseQualifiedRef(table *Ident) (_ *QualifiedRef, err error) {
assert(p.peek() == DOT)
var expr QualifiedRef
expr.Table = table
expr.Dot, _, _ = p.scan()
if p.peek() == STAR {
expr.Star, _, _ = p.scan()
} else if isIdentToken(p.peek()) {
pos, tok, lit := p.scan()
expr.Column = &Ident{Name: lit, NamePos: pos, Quoted: tok == QIDENT}
} else {
return &expr, p.errorExpected(p.pos, p.tok, "column name")
}
return &expr, nil
}
func (p *Parser) parseCall(name *Ident) (_ *Call, err error) {
assert(p.peek() == LP)
var expr Call
expr.Name = name
expr.Lparen, _, _ = p.scan()
// Parse argument list: either "*" or "[DISTINCT] expr, expr..."
if p.peek() == STAR {
expr.Star, _, _ = p.scan()
} else {
if p.peek() == DISTINCT {
expr.Distinct, _, _ = p.scan()
}
for p.peek() != RP {
arg, err := p.ParseExpr()
if err != nil {
return &expr, err
}
expr.Args = append(expr.Args, arg)
if tok := p.peek(); tok == COMMA {
p.scan()
} else if tok != RP {
return &expr, p.errorExpected(p.pos, p.tok, "comma or right paren")
}
}
}
if p.peek() != RP {
return &expr, p.errorExpected(p.pos, p.tok, "right paren")
}
expr.Rparen, _, _ = p.scan()
// Parse optional filter clause.
if p.peek() == FILTER {
if expr.Filter, err = p.parseFilterClause(); err != nil {
return &expr, err
}
}
// Parse optional over clause.
if p.peek() == OVER {
if expr.Over, err = p.parseOverClause(); err != nil {
return &expr, err
}
}
return &expr, nil
}
func (p *Parser) parseFilterClause() (_ *FilterClause, err error) {
assert(p.peek() == FILTER)
var clause FilterClause
clause.Filter, _, _ = p.scan()
if p.peek() != LP {
return &clause, p.errorExpected(p.pos, p.tok, "left paren")
}
clause.Lparen, _, _ = p.scan()
if p.peek() != WHERE {
return &clause, p.errorExpected(p.pos, p.tok, "WHERE")
}
clause.Where, _, _ = p.scan()
if clause.X, err = p.ParseExpr(); err != nil {
return &clause, err
}
if p.peek() != RP {
return &clause, p.errorExpected(p.pos, p.tok, "right paren")
}
clause.Rparen, _, _ = p.scan()
return &clause, nil
}
func (p *Parser) parseOverClause() (_ *OverClause, err error) {
assert(p.peek() == OVER)
var clause OverClause
clause.Over, _, _ = p.scan()
// If specifying a window name, read it and exit.
if isIdentToken(p.peek()) {
pos, tok, lit := p.scan()
clause.Name = &Ident{Name: lit, NamePos: pos, Quoted: tok == QIDENT}
return &clause, nil
}
if clause.Definition, err = p.parseWindowDefinition(); err != nil {
return &clause, err
}
return &clause, nil
}
func (p *Parser) parseWindowDefinition() (_ *WindowDefinition, err error) {
var def WindowDefinition
// Otherwise parse the window definition.
if p.peek() != LP {
return &def, p.errorExpected(p.pos, p.tok, "left paren")
}
def.Lparen, _, _ = p.scan()
// Read base window name.
if isIdentToken(p.peek()) {
pos, tok, lit := p.scan()
def.Base = &Ident{Name: lit, NamePos: pos, Quoted: tok == QIDENT}
}
// Parse "PARTITION BY expr, expr..."
if p.peek() == PARTITION {
def.Partition, _, _ = p.scan()
if p.peek() != BY {
return &def, p.errorExpected(p.pos, p.tok, "BY")
}
def.PartitionBy, _, _ = p.scan()
for {
partition, err := p.ParseExpr()
if err != nil {
return &def, err
}
def.Partitions = append(def.Partitions, partition)
if p.peek() != COMMA {
break
}
p.scan()
}
}
// Parse "ORDER BY ordering-term, ordering-term..."
if p.peek() == ORDER {
def.Order, _, _ = p.scan()
if p.peek() != BY {
return &def, p.errorExpected(p.pos, p.tok, "BY")
}
def.OrderBy, _, _ = p.scan()
for {
term, err := p.parseOrderingTerm()
if err != nil {
return &def, err
}
def.OrderingTerms = append(def.OrderingTerms, term)
if p.peek() != COMMA {
break
}
p.scan()
}
}
// Parse frame spec.
if tok := p.peek(); tok == RANGE || tok == ROWS || tok == GROUPS {
if def.Frame, err = p.parseFrameSpec(); err != nil {
return &def, err
}
}
// Parse final rparen.
if p.peek() != RP {
return &def, p.errorExpected(p.pos, p.tok, "right paren")
}
def.Rparen, _, _ = p.scan()
return &def, nil
}
func (p *Parser) parseOrderingTerm() (_ *OrderingTerm, err error) {
var term OrderingTerm
if term.X, err = p.ParseExpr(); err != nil {
return &term, err
}
// Parse optional sort direction ("ASC" or "DESC")
switch p.peek() {
case ASC:
term.Asc, _, _ = p.scan()
case DESC:
term.Desc, _, _ = p.scan()
}
// Parse optional "NULLS FIRST" or "NULLS LAST"
if p.peek() == NULLS {
term.Nulls, _, _ = p.scan()
switch p.peek() {
case FIRST:
term.NullsFirst, _, _ = p.scan()
case LAST:
term.NullsLast, _, _ = p.scan()
default:
return &term, p.errorExpected(p.pos, p.tok, "FIRST or LAST")
}
}
return &term, nil
}
func (p *Parser) parseFrameSpec() (_ *FrameSpec, err error) {
assert(p.peek() == RANGE || p.peek() == ROWS || p.peek() == GROUPS)
var spec FrameSpec
switch p.peek() {
case RANGE:
spec.Range, _, _ = p.scan()
case ROWS:
spec.Rows, _, _ = p.scan()
case GROUPS:
spec.Groups, _, _ = p.scan()
}
// Parsing BETWEEN indicates that two expressions are required.
if p.peek() == BETWEEN {
spec.Between, _, _ = p.scan()
}
// Parse X expression: "UNBOUNDED PRECEDING", "CURRENT ROW", "expr PRECEDING|FOLLOWING"
if p.peek() == UNBOUNDED {
spec.UnboundedX, _, _ = p.scan()
if p.peek() != PRECEDING {
return &spec, p.errorExpected(p.pos, p.tok, "PRECEDING")
}
spec.PrecedingX, _, _ = p.scan()
} else if p.peek() == CURRENT {
spec.CurrentX, _, _ = p.scan()
if p.peek() != ROW {
return &spec, p.errorExpected(p.pos, p.tok, "ROW")
}
spec.CurrentRowX, _, _ = p.scan()
} else {
if spec.X, err = p.ParseExpr(); err != nil {
return &spec, err
}
if p.peek() == PRECEDING {
spec.PrecedingX, _, _ = p.scan()
} else if p.peek() == FOLLOWING && spec.Between.IsValid() { // FOLLOWING only allowed with BETWEEN
spec.FollowingX, _, _ = p.scan()
} else {
if spec.Between.IsValid() {
return &spec, p.errorExpected(p.pos, p.tok, "PRECEDING or FOLLOWING")
}
return &spec, p.errorExpected(p.pos, p.tok, "PRECEDING")
}
}
// Read "AND y" if range is BETWEEN.
if spec.Between.IsValid() {
if p.peek() != AND {
return &spec, p.errorExpected(p.pos, p.tok, "AND")
}
spec.And, _, _ = p.scan()
// Parse Y expression: "UNBOUNDED FOLLOWING", "CURRENT ROW", "expr PRECEDING|FOLLOWING"
if p.peek() == UNBOUNDED {
spec.UnboundedY, _, _ = p.scan()
if p.peek() != FOLLOWING {
return &spec, p.errorExpected(p.pos, p.tok, "FOLLOWING")
}
spec.FollowingY, _, _ = p.scan()
} else if p.peek() == CURRENT {
spec.CurrentY, _, _ = p.scan()
if p.peek() != ROW {
return &spec, p.errorExpected(p.pos, p.tok, "ROW")
}
spec.CurrentRowY, _, _ = p.scan()
} else {
if spec.Y, err = p.ParseExpr(); err != nil {
return &spec, err
}
if p.peek() == PRECEDING {
spec.PrecedingY, _, _ = p.scan()
} else if p.peek() == FOLLOWING {
spec.FollowingY, _, _ = p.scan()
} else {
return &spec, p.errorExpected(p.pos, p.tok, "PRECEDING or FOLLOWING")
}
}
}
// Parse optional EXCLUDE.
if p.peek() == EXCLUDE {
spec.Exclude, _, _ = p.scan()
switch p.peek() {
case NO:
spec.ExcludeNo, _, _ = p.scan()
if p.peek() != OTHERS {
return &spec, p.errorExpected(p.pos, p.tok, "OTHERS")
}
spec.ExcludeNoOthers, _, _ = p.scan()
case CURRENT:
spec.ExcludeCurrent, _, _ = p.scan()
if p.peek() != ROW {
return &spec, p.errorExpected(p.pos, p.tok, "ROW")
}
spec.ExcludeCurrentRow, _, _ = p.scan()
case GROUP:
spec.ExcludeGroup, _, _ = p.scan()
case TIES:
spec.ExcludeTies, _, _ = p.scan()
default:
return &spec, p.errorExpected(p.pos, p.tok, "NO OTHERS, CURRENT ROW, GROUP, or TIES")
}
}
return &spec, nil
}
func (p *Parser) parseParenExpr() (_ *ParenExpr, err error) {
var expr ParenExpr
expr.Lparen, _, _ = p.scan()
if expr.X, err = p.ParseExpr(); err != nil {
return &expr, err
}
expr.Rparen, _, _ = p.scan()
return &expr, nil
}
func (p *Parser) parseSetLiteralExpr() (_ *SetLiteralExpr, err error) {
var expr SetLiteralExpr
expr.Lbracket, _, _ = p.scan()
for p.peek() != RB {
x, err := p.ParseExpr()
if err != nil {
return &expr, err
}
expr.Members = append(expr.Members, x)
if p.peek() == RB {
break
} else if p.peek() != COMMA {
return &expr, p.errorExpected(p.pos, p.tok, "comma or right bracket")
}
p.scan()
}
expr.Rbracket, _, _ = p.scan()
return &expr, nil
}
func (p *Parser) parseTupleLiteralExpr() (_ *TupleLiteralExpr, err error) {
var expr TupleLiteralExpr
expr.Lbrace, _, _ = p.scan()
for p.peek() != RBR {
x, err := p.ParseExpr()
if err != nil {
return &expr, err
}
expr.Members = append(expr.Members, x)
if p.peek() == RBR {
break
} else if p.peek() != COMMA {
return &expr, p.errorExpected(p.pos, p.tok, "comma or right brace")
}
p.scan()
}
expr.Rbrace, _, _ = p.scan()
return &expr, nil
}
func (p *Parser) parseCastExpr() (_ *CastExpr, err error) {
assert(p.peek() == CAST)
var expr CastExpr
expr.Cast, _, _ = p.scan()
if p.peek() != LP {
return &expr, p.errorExpected(p.pos, p.tok, "left paren")
}
expr.Lparen, _, _ = p.scan()
if expr.X, err = p.ParseExpr(); err != nil {
return &expr, err
}
if p.peek() != AS {
return &expr, p.errorExpected(p.pos, p.tok, "AS")
}
expr.As, _, _ = p.scan()
if expr.Type, err = p.parseType(); err != nil {
return &expr, err
}
if p.peek() != RP {
return &expr, p.errorExpected(p.pos, p.tok, "right paren")
}
expr.Rparen, _, _ = p.scan()
return &expr, nil
}
func (p *Parser) parseCaseExpr() (_ *CaseExpr, err error) {
assert(p.peek() == CASE)
var expr CaseExpr
expr.Case, _, _ = p.scan()
// Parse optional expression if WHEN is not next.
if p.peek() != WHEN {
if expr.Operand, err = p.ParseExpr(); err != nil {
return &expr, err
}
}
// Parse one or more WHEN/THEN pairs.
for {
var blk CaseBlock
if p.peek() != WHEN {
return &expr, p.errorExpected(p.pos, p.tok, "WHEN")
}
blk.When, _, _ = p.scan()
if blk.Condition, err = p.ParseExpr(); err != nil {
return &expr, err
}
if p.peek() != THEN {
return &expr, p.errorExpected(p.pos, p.tok, "THEN")
}
blk.Then, _, _ = p.scan()
if blk.Body, err = p.ParseExpr(); err != nil {
return &expr, err
}
expr.Blocks = append(expr.Blocks, &blk)
if tok := p.peek(); tok == ELSE || tok == END {
break
} else if tok != WHEN {
return &expr, p.errorExpected(p.pos, p.tok, "WHEN, ELSE or END")
}
}
// Parse optional ELSE block.
if p.peek() == ELSE {
expr.Else, _, _ = p.scan()
if expr.ElseExpr, err = p.ParseExpr(); err != nil {
return &expr, err
}
}
if p.peek() != END {
return &expr, p.errorExpected(p.pos, p.tok, "END")
}
expr.End, _, _ = p.scan()
return &expr, nil
}
func (p *Parser) parseExists() (_ *Exists, err error) {
assert(p.peek() == NOT || p.peek() == EXISTS)
var expr Exists
if p.peek() == NOT {
expr.Not, _, _ = p.scan()
}
if p.peek() != EXISTS {
return &expr, p.errorExpected(p.pos, p.tok, "EXISTS")
}
expr.Exists, _, _ = p.scan()
if p.peek() != LP {
return &expr, p.errorExpected(p.pos, p.tok, "left paren")
}
expr.Lparen, _, _ = p.scan()
if expr.Select, err = p.parseSelectStatement(false, nil); err != nil {
return &expr, err
}
if p.peek() != RP {
return &expr, p.errorExpected(p.pos, p.tok, "right paren")
}
expr.Rparen, _, _ = p.scan()
return &expr, nil
}
func (p *Parser) parseIntegerLiteral(desc string) (*IntegerLit, error) {
pos, tok, lit := p.scan()
switch tok {
case INTEGER:
return &IntegerLit{ValuePos: pos, Value: lit}, nil
default:
return nil, p.errorExpected(p.pos, p.tok, desc)
}
}
func (p *Parser) parseAlterTableStatement() (_ *AlterTableStatement, err error) {
assert(p.peek() == ALTER)
var stmt AlterTableStatement
stmt.Alter, _, _ = p.scan()
if p.peek() != TABLE {
return &stmt, p.errorExpected(p.pos, p.tok, "TABLE")
}
stmt.Table, _, _ = p.scan()
if stmt.Name, err = p.parseIdent("table name"); err != nil {
return &stmt, err
}
switch p.peek() {
case RENAME:
stmt.Rename, _, _ = p.scan()
/*// Parse "RENAME TO new-table-name".
if p.peek() == TO {
stmt.RenameTo, _, _ = p.scan()
if stmt.NewName, err = p.parseIdent("new table name"); err != nil {
return &stmt, err
}
return &stmt, nil
}*/
// Otherwise parse "RENAME [COLUMN] column-name TO new-column-name".
if p.peek() == COLUMN {
stmt.RenameColumn, _, _ = p.scan()
} else if !isIdentToken(p.peek()) {
return &stmt, p.errorExpected(p.pos, p.tok, "COLUMN keyword or column name")
}
if stmt.OldColumnName, err = p.parseIdent("column name"); err != nil {
return &stmt, err
}
if p.peek() != TO {
return &stmt, p.errorExpected(p.pos, p.tok, "TO")
}
stmt.To, _, _ = p.scan()
if stmt.NewColumnName, err = p.parseIdent("new column name"); err != nil {
return &stmt, err
}
return &stmt, nil
case ADD:
stmt.Add, _, _ = p.scan()
if p.peek() == COLUMN {
stmt.AddColumn, _, _ = p.scan()
} else if !isIdentToken(p.peek()) {
return &stmt, p.errorExpected(p.pos, p.tok, "COLUMN keyword or column name")
}
if stmt.ColumnDef, err = p.parseColumnDefinition(); err != nil {
return &stmt, err
}
return &stmt, nil
case DROP:
stmt.Drop, _, _ = p.scan()
if p.peek() == COLUMN {
stmt.DropColumn, _, _ = p.scan()
} else if !isIdentToken(p.peek()) {
return &stmt, p.errorExpected(p.pos, p.tok, "COLUMN keyword or column name")
}
if stmt.DropColumnName, err = p.parseIdent("column name"); err != nil {
return &stmt, err
}
return &stmt, nil
default:
return &stmt, p.errorExpected(p.pos, p.tok, "ADD, DROP or RENAME")
}
}
/*func (p *Parser) parseAnalyzeStatement() (_ *AnalyzeStatement, err error) {
assert(p.peek() == ANALYZE)
var stmt AnalyzeStatement
stmt.Analyze, _, _ = p.scan()
if stmt.Name, err = p.parseIdent("table or index name"); err != nil {
return &stmt, err
}
return &stmt, nil
}*/
func (p *Parser) scan() (Pos, Token, string) {
if p.full {
p.full = false
return p.pos, p.tok, p.lit
}
p.pos, p.tok, p.lit = p.s.Scan()
return p.pos, p.tok, p.lit
}
// scanBinaryOp performs a scan but combines multi-word operations into a single token.
func (p *Parser) scanBinaryOp() (Pos, Token, error) {
pos, tok, _ := p.scan()
switch tok {
case IS:
switch p.peek() {
case NOT:
p.scan()
return pos, ISNOT, nil
}
return pos, IS, nil
case NOT:
switch p.peek() {
case IN:
p.scan()
return pos, NOTIN, nil
case LIKE:
p.scan()
return pos, NOTLIKE, nil
case GLOB:
p.scan()
return pos, NOTGLOB, nil
case REGEXP:
p.scan()
return pos, NOTREGEXP, nil
case MATCH:
p.scan()
return pos, NOTMATCH, nil
case BETWEEN:
p.scan()
return pos, NOTBETWEEN, nil
default:
return pos, tok, p.errorExpected(p.pos, p.tok, "IN, LIKE, GLOB, REGEXP, MATCH, or BETWEEN")
}
default:
return pos, tok, nil
}
}
func (p *Parser) peek() Token {
if !p.full {
p.scan()
p.unscan()
}
return p.tok
}
func (p *Parser) unscan() {
assert(!p.full)
p.full = true
}
func (p *Parser) errorExpected(pos Pos, tok Token, msg string) error {
msg = "expected " + msg
if pos == p.pos {
if p.tok.IsLiteral() {
msg += ", found " + p.lit
} else {
msg += ", found '" + p.tok.String() + "'"
}
}
return &Error{Pos: pos, Msg: msg}
}
// Error represents a parse error.
type Error struct {
Pos Pos
Msg string
}
// Error implements the error interface.
func (e Error) Error() string {
if e.Pos.IsValid() {
return e.Pos.String() + ": " + e.Msg
}
return e.Msg
}
// isTableOptionStartToken returns true if tok is the initial token of a table option.
func isTableOptionStartToken(tok Token) bool {
switch tok {
case KEYPARTITIONS, COMMENT:
return true
default:
return false
}
}
// isBulkInsertOptionStartToken returns true if tok is the initial token of a bulk insert option.
func isBulkInsertOptionStartToken(tok Token, p *Parser) bool {
switch tok {
case IDENT:
ident, err := p.parseIdent("bulk insert option")
defer p.unscan()
if err != nil {
return false
}
switch strings.ToUpper(ident.Name) {
case "BATCHSIZE", "ROWSLIMIT", "FORMAT", "INPUT", "HEADER_ROW", "ALLOW_MISSING_VALUES":
return true
}
}
return false
}
// isConstraintStartToken returns true if tok is the initial token of a constraint.
func isConstraintStartToken(tok Token, isTable bool) bool {
switch tok {
//case CONSTRAINT, PRIMARY, UNIQUE, CHECK:
// return true // table & column
//case FOREIGN:
// return isTable // table only
case MIN, MAX, TIMEUNIT, TIMEQUANTUM, CACHETYPE:
return !isTable // column only
default:
return false
}
}
// isLiteralToken returns true if token represents a literal value.
func isLiteralToken(tok Token) bool {
switch tok {
case FLOAT, INTEGER, STRING, BLOB, TRUE, FALSE, NULL,
CURRENT_DATE, CURRENT_TIMESTAMP:
return true
default:
return false
}
}