featurebase/sql3/parser/parser.go
Travis Turner bcb32addaf Batch insert via SQL (multiple tuples) (#2243)
* Formatting adjustments made during code review.

While reviewing the BULK INSERT logic (in order to decide how best to
approach "ingest via sql" in the cloud), I made a few formatting and
comment changes. I'm just adding them here as a separate commit so they
don't muddy up my actual work.

* Parser modifications to support mulitple tuples in INSERT INTO

This commit doesn't include all of the changes required in the
planner. Fow now, the planner is simply modified to continue supporting
a single tuple (the first tuple in the list).

* Update the planner to handle multiple INSERT INTO tuples

This is part 1. It's still using the existing logic which builds an
ImportRequest for every record (and every field!).

The next step will involve using a client.Batch to handle the records.

* Introduce client.Importer interface (used by client.Batch)

Instead of the Batch having a pointer to a client, this puts an
interface there instead (which the client implements). It also allows us
to inject a different importer (i.e. other than a featurebase.client)
into the Batch.

* Decouple batch from client

This commit pulls batch-specific code out of the client package and into
a new batch package. It introduces the batch.Importer interface, the
methods of which replace all the calls that batch was previously making
directly to client methods.

Finally, it contains two implementations of the batch.Importer
interface: one is a wrapper around client, and the other is a wrapper
around featurebase.API.

* Use docker (instead of MustRunCluster) for internal batch tests

Because the `batch` package tests are internal, using
test.MustRunCluster() resulted in an import loop (because it eventually
imports `server`, and we can't have that). So this commit replaces the
use of `test.MustRunCluster()` with docker. The setup is basically the
same as that used in the idk docker tests.

Here we also remove all client-side references to `UseIngestAPI`, which
is an experimental (json) ingest api. It's still suppored on the server,
but here we remove the external usage of it.

* cherry-pick fix

* Use batch.Import() for sql3 INSERT INTO statements

* Thread logger into sql3

* fix batch test

* Fix some shadowing complaint by linter

* Address some test issues related to stringsets

* Exclude batch integration tests from CI

* Address PR feedback

- Added description to batch.README
- Consolidated grep commands in .gitlab-ci.yml
- Removed some debugging comments
- Replaces some inadvertantly removed license headers

* Add batch package to gitlab CI

* Updated CI for batch package

Updated CI include path

Update gitlab ci

Update CI

Update CI

Trying new include path for ci

Updated gitlab ci include path

Made idk race job optional for sonarcloud upload

add testdata directory

remove testenv from dockercompose file

use GIT_STRATEGY clone in batch CI

add testdata volume to dockercompose

Co-authored-by: Fletcher Haynes <fletcher.haynes@generalassemb.ly>
(cherry picked from commit 00ef2380e5)
2022-11-15 11:25:36 -08:00

3392 lines
76 KiB
Go

// Copyright 2021 Molecula Corp. All rights reserved.
package parser
import (
"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)
default:
return nil, p.errorExpected(p.pos, p.tok, "TABLES, COLUMNS")
}
}
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) 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 TRIGGER:
return p.parseCreateTriggerStatement(pos)*/
default:
return nil, p.errorExpected(pos, tok, "TABLE")
}
}
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 TRIGGER:
return p.parseDropTriggerStatement(pos)*/
default:
return nil, p.errorExpected(pos, tok, "TABLE")
}
}
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 column constraints.
switch p.peek() {
case KEYPARTITIONS:
return p.parseKeyPartitionsOption(optionPos)
default:
assert(p.peek() == SHARDWIDTH)
return p.parseShardWidthOption(optionPos)
}
}
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) parseShardWidthOption(optionPos Pos) (_ *ShardWidthOption, err error) {
assert(p.peek() == SHARDWIDTH)
var opt ShardWidthOption
opt.ShardWidth, _, _ = 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) parseCreateTriggerStatement(createPos Pos) (_ *CreateTriggerStatement, err error) {
assert(p.peek() == TRIGGER)
var stmt CreateTriggerStatement
stmt.Create = createPos
stmt.Trigger, _, _ = 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
}
// Parse BEFORE, AFTER, or INSTEAD OF
switch p.peek() {
case BEFORE:
stmt.Before, _, _ = p.scan()
case AFTER:
stmt.After, _, _ = p.scan()
case INSTEAD:
stmt.Instead, _, _ = p.scan()
if p.peek() != OF {
return &stmt, p.errorExpected(p.pos, p.tok, "OF")
}
stmt.InsteadOf, _, _ = p.scan()
}
// Parse DELETE, INSERT, UPDATE, or UPDATE OF [columns]
switch p.peek() {
case DELETE:
stmt.Delete, _, _ = p.scan()
case INSERT:
stmt.Insert, _, _ = p.scan()
case UPDATE:
stmt.Update, _, _ = p.scan()
if p.peek() == OF {
stmt.UpdateOf, _, _ = p.scan()
for {
col, err := p.parseIdent("column name")
if err != nil {
return &stmt, err
}
stmt.UpdateOfColumns = append(stmt.UpdateOfColumns, col)
if p.peek() != COMMA {
break
}
p.scan()
}
}
default:
return &stmt, p.errorExpected(p.pos, p.tok, "DELETE, INSERT, or UPDATE")
}
// Parse "ON table-name".
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 "FOR EACH ROW".
if p.peek() == FOR {
stmt.For, _, _ = p.scan()
if p.peek() != EACH {
return &stmt, p.errorExpected(p.pos, p.tok, "EACH")
}
stmt.ForEach, _, _ = p.scan()
if p.peek() != ROW {
return &stmt, p.errorExpected(p.pos, p.tok, "ROW")
}
stmt.ForEachRow, _, _ = p.scan()
}
// Parse optional "WHEN expr".
if p.peek() == WHEN {
stmt.When, _, _ = p.scan()
if stmt.WhenExpr, err = p.ParseExpr(); err != nil {
return &stmt, err
}
}
// Parse trigger body.
if p.peek() != BEGIN {
return &stmt, p.errorExpected(p.pos, p.tok, "BEGIN")
}
stmt.Begin, _, _ = p.scan()
for {
s, err := p.parseTriggerBodyStatement()
if err != nil {
return &stmt, err
}
stmt.Body = append(stmt.Body, s)
if p.peek() == END {
break
}
}
stmt.End, _, _ = p.scan()
return &stmt, nil
}*/
/*func (p *Parser) parseTriggerBodyStatement() (stmt Statement, err error) {
switch p.peek() {
case SELECT, VALUES:
stmt, err = p.parseSelectStatement(false, nil)
case INSERT, REPLACE:
stmt, err = p.parseInsertStatement(nil)
case UPDATE:
stmt, err = p.parseUpdateStatement(nil)
case DELETE:
stmt, err = p.parseDeleteStatement(nil)
//case WITH:
// stmt, err = p.parseWithStatement()
default:
return nil, p.errorExpected(p.pos, p.tok, "statement")
}
if err != nil {
return stmt, err
}
// Ensure trailing semicolon exists.
if p.peek() != SEMI {
return stmt, p.errorExpected(p.pos, p.tok, "semicolon")
}
p.scan()
return stmt, nil
}*/
/*func (p *Parser) parseDropTriggerStatement(dropPos Pos) (_ *DropTriggerStatement, err error) {
assert(p.peek() == TRIGGER)
var stmt DropTriggerStatement
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("trigger 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) 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 p.peek() != INSERT {
return nil, p.errorExpected(p.pos, p.tok, "INSERT")
}
stmt.Insert, _, _ = p.scan()
// Parse table name & optional alias.
if stmt.Table, err = p.parseIdent("table name"); err != nil {
return nil, err
}
if p.peek() != FROM {
return nil, p.errorExpected(p.pos, p.tok, "FROM")
}
stmt.From, _, _ = p.scan()
if isLiteralToken(p.peek()) {
stmt.DataFile = p.mustParseLiteral()
} else {
return nil, p.errorExpected(p.pos, p.tok, "literal")
}
if p.peek() == WITH {
stmt.With, _, _ = p.scan()
if !isBulkInsertOptionStartToken(p.peek(), p) {
return nil, p.errorExpected(p.pos, p.tok, "BATCHSIZE, ROWSLIMIT, FORMAT or MAP")
}
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 "MAP":
if p.peek() == IDENT {
ident, err := p.parseIdent("bulk insert option")
if err != nil {
return err
}
switch strings.ToUpper(ident.Name) {
case "_ID":
stmt.MapId = &BulkInsertIDMap{}
if p.peek() != TO {
return p.errorExpected(p.pos, p.tok, "TO")
}
_, _, _ = p.scan()
if p.peek() == AUTOINCREMENT {
stmt.MapId.Auto, _, _ = p.scan()
return nil
}
stmt.MapId.ColumnExprs, err = p.parseExprList()
if err != nil {
return err
}
return nil
case "OFFSET":
columnMapItem := &BulkInsertMappedColumn{}
if isLiteralToken(p.peek()) {
columnMapItem.SourceColumnOffset = p.mustParseLiteral()
} else {
return p.errorExpected(p.pos, p.tok, "literal")
}
if p.peek() != TO {
return p.errorExpected(p.pos, p.tok, "TO")
}
_, _, _ = p.scan()
if p.peek() != IDENT {
return p.errorExpected(p.pos, p.tok, "IDENTIFIER")
}
columnMapItem.TargetColumn, err = p.parseIdent("bulk insert map offset option")
if err != nil {
return err
}
if stmt.ColumnMap == nil {
stmt.ColumnMap = []*BulkInsertMappedColumn{}
}
stmt.ColumnMap = append(stmt.ColumnMap, columnMapItem)
return nil
}
}
return p.errorExpected(p.pos, p.tok, "_ID or OFFSET")
}
}
return p.errorExpected(p.pos, p.tok, "BATCHSIZE, ROWSLIMIT, FORMAT or MAP")
}
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}
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 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, SHARDWIDTH:
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", "MAP":
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
}
}