mirror of
https://github.com/hanzo-ds/sqlparser.git
synced 2026-10-30 20:53:50 +00:00
1211 lines
27 KiB
Go
1211 lines
27 KiB
Go
package parser
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
func (p *Parser) tryParseWithClause(pos Pos) (*WithClause, error) {
|
|
if !p.matchKeyword(KeywordWith) {
|
|
return nil, nil
|
|
}
|
|
return p.parseWithClause(pos)
|
|
}
|
|
|
|
func (p *Parser) parseWithClause(pos Pos) (*WithClause, error) {
|
|
if err := p.expectKeyword(KeywordWith); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
cteExpr, err := p.parseCTEStmt(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ctes := []*CTEStmt{cteExpr}
|
|
for p.tryConsumeTokenKind(TokenKindComma) != nil {
|
|
cteExpr, err := p.parseCTEStmt(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ctes = append(ctes, cteExpr)
|
|
}
|
|
|
|
return &WithClause{
|
|
WithPos: pos,
|
|
CTEs: ctes,
|
|
EndPos: ctes[len(ctes)-1].End(),
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) tryParseTopClause(pos Pos) (*TopClause, error) {
|
|
if !p.matchKeyword(KeywordTop) {
|
|
return nil, nil
|
|
}
|
|
return p.parseTopClause(pos)
|
|
}
|
|
|
|
func (p *Parser) parseTopClause(pos Pos) (*TopClause, error) {
|
|
if err := p.expectKeyword(KeywordTop); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
number, err := p.parseNumber(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
topEnd := number.End()
|
|
|
|
withTies := false
|
|
if p.tryConsumeKeywords(KeywordWith) {
|
|
topEnd = p.End()
|
|
if err := p.expectKeyword(KeywordTies); err != nil {
|
|
return nil, err
|
|
}
|
|
withTies = true
|
|
}
|
|
return &TopClause{
|
|
TopPos: pos,
|
|
TopEnd: topEnd,
|
|
Number: number,
|
|
WithTies: withTies,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) tryParseDistinctOn(pos Pos) (*DistinctOn, error) {
|
|
if !p.matchKeyword(KeywordOn) {
|
|
return nil, nil
|
|
}
|
|
return p.parseDistinctOn(pos)
|
|
}
|
|
|
|
func (p *Parser) parseDistinctOn(pos Pos) (*DistinctOn, error) {
|
|
if err := p.expectKeyword(KeywordOn); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := p.expectTokenKind(TokenKindLParen); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ident, err := p.parseIdent()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
idents := []*Ident{ident}
|
|
|
|
for p.matchTokenKind(TokenKindComma) {
|
|
_ = p.lexer.consumeToken()
|
|
|
|
ident, err = p.parseIdent()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
idents = append(idents, ident)
|
|
}
|
|
|
|
if err := p.expectTokenKind(TokenKindRParen); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &DistinctOn{
|
|
Idents: idents,
|
|
DistinctOnPos: pos,
|
|
DistinctOnEnd: p.Pos(),
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) tryParseFromClause(pos Pos) (*FromClause, error) {
|
|
if !p.matchKeyword(KeywordFrom) {
|
|
return nil, nil
|
|
}
|
|
return p.parseFromClause(pos)
|
|
}
|
|
|
|
func (p *Parser) parseFromClause(pos Pos) (*FromClause, error) {
|
|
if err := p.expectKeyword(KeywordFrom); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
expr, err := p.parseJoinExpr(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &FromClause{
|
|
FromPos: pos,
|
|
Expr: expr,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) tryParseJoinConstraints(pos Pos) (Expr, error) {
|
|
switch {
|
|
case p.tryConsumeKeywords(KeywordOn):
|
|
columnExprList, err := p.parseColumnExprList(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &OnClause{
|
|
OnPos: pos,
|
|
On: columnExprList,
|
|
}, nil
|
|
case p.tryConsumeKeywords(KeywordUsing):
|
|
hasParen := p.tryConsumeTokenKind(TokenKindLParen) != nil
|
|
columnExprList, err := p.parseColumnExprListWithLParen(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if hasParen {
|
|
if err := p.expectTokenKind(TokenKindRParen); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return &UsingClause{
|
|
UsingPos: pos,
|
|
Using: columnExprList,
|
|
}, nil
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
func (p *Parser) parseJoinOp(_ Pos) []string {
|
|
var modifiers []string
|
|
switch {
|
|
case p.tryConsumeKeywords(KeywordCross): // cross join
|
|
modifiers = append(modifiers, KeywordCross)
|
|
case p.matchKeyword(KeywordAny), p.matchKeyword(KeywordAll):
|
|
modifiers = append(modifiers, p.last().String)
|
|
_ = p.lexer.consumeToken()
|
|
if p.matchKeyword(KeywordFull) {
|
|
modifiers = append(modifiers, p.last().String)
|
|
_ = p.lexer.consumeToken()
|
|
}
|
|
if p.matchKeyword(KeywordLeft) || p.matchKeyword(KeywordRight) || p.matchKeyword(KeywordInner) || p.matchKeyword(KeywordOuter) {
|
|
modifiers = append(modifiers, p.last().String)
|
|
_ = p.lexer.consumeToken()
|
|
}
|
|
case p.matchKeyword(KeywordSemi), p.matchKeyword(KeywordAsof):
|
|
modifiers = append(modifiers, p.last().String)
|
|
_ = p.lexer.consumeToken()
|
|
if p.matchKeyword(KeywordLeft) || p.matchKeyword(KeywordRight) {
|
|
modifiers = append(modifiers, p.last().String)
|
|
_ = p.lexer.consumeToken()
|
|
}
|
|
if p.matchKeyword(KeywordOuter) {
|
|
modifiers = append(modifiers, p.last().String)
|
|
_ = p.lexer.consumeToken()
|
|
}
|
|
case p.matchKeyword(KeywordInner):
|
|
modifiers = append(modifiers, p.last().String)
|
|
_ = p.lexer.consumeToken()
|
|
if p.matchKeyword(KeywordAll) || p.matchKeyword(KeywordAny) || p.matchKeyword(KeywordAsof) {
|
|
modifiers = append(modifiers, p.last().String)
|
|
_ = p.lexer.consumeToken()
|
|
}
|
|
case p.matchKeyword(KeywordLeft), p.matchKeyword(KeywordRight):
|
|
modifiers = append(modifiers, p.last().String)
|
|
_ = p.lexer.consumeToken()
|
|
if p.matchKeyword(KeywordOuter) {
|
|
modifiers = append(modifiers, p.last().String)
|
|
_ = p.lexer.consumeToken()
|
|
}
|
|
if p.matchKeyword(KeywordSemi) || p.matchKeyword(KeywordAnti) ||
|
|
p.matchKeyword(KeywordAny) || p.matchKeyword(KeywordAll) ||
|
|
p.matchKeyword(KeywordAsof) || p.matchKeyword(KeywordArray) {
|
|
modifiers = append(modifiers, p.last().String)
|
|
_ = p.lexer.consumeToken()
|
|
}
|
|
case p.matchKeyword(KeywordFull):
|
|
modifiers = append(modifiers, p.last().String)
|
|
_ = p.lexer.consumeToken()
|
|
if p.matchKeyword(KeywordOuter) {
|
|
modifiers = append(modifiers, p.last().String)
|
|
_ = p.lexer.consumeToken()
|
|
}
|
|
if p.matchKeyword(KeywordAll) || p.matchKeyword(KeywordAny) {
|
|
modifiers = append(modifiers, p.last().String)
|
|
_ = p.lexer.consumeToken()
|
|
}
|
|
}
|
|
return modifiers
|
|
}
|
|
|
|
func (p *Parser) parseJoinTableExpr(_ Pos) (Expr, error) {
|
|
switch {
|
|
case p.matchTokenKind(TokenKindIdent), p.matchTokenKind(TokenKindString), p.matchTokenKind(TokenKindLParen):
|
|
tableExpr, err := p.parseTableExpr(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
statementEnd := tableExpr.End()
|
|
|
|
hasFinal := p.matchKeyword(KeywordFinal)
|
|
if hasFinal {
|
|
statementEnd = p.End()
|
|
_ = p.lexer.consumeToken()
|
|
}
|
|
|
|
sampleRatio, err := p.tryParseSampleClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if sampleRatio != nil {
|
|
statementEnd = sampleRatio.End()
|
|
}
|
|
return &JoinTableExpr{
|
|
Table: tableExpr,
|
|
SampleRatio: sampleRatio,
|
|
HasFinal: hasFinal,
|
|
StatementEnd: statementEnd,
|
|
}, nil
|
|
default:
|
|
return nil, fmt.Errorf("expected table name or subquery, got %s", fmt.Sprintf("%v", p.lastTokenKind()))
|
|
}
|
|
}
|
|
|
|
func (p *Parser) parseJoinRightExpr(pos Pos) (expr Expr, err error) {
|
|
var rightExpr Expr
|
|
var modifiers []string
|
|
switch {
|
|
case p.tryConsumeKeywords(KeywordGlobal):
|
|
case p.tryConsumeKeywords(KeywordLocal):
|
|
case p.tryConsumeTokenKind(TokenKindComma) != nil:
|
|
return p.parseJoinExpr(p.Pos())
|
|
default:
|
|
modifiers = p.parseJoinOp(p.Pos())
|
|
}
|
|
|
|
if len(modifiers) != 0 && !p.matchKeyword(KeywordJoin) {
|
|
return nil, fmt.Errorf("expected JOIN, got %s", p.lastTokenKind())
|
|
}
|
|
if !p.tryConsumeKeywords(KeywordJoin) {
|
|
return nil, nil
|
|
}
|
|
|
|
modifiers = append(modifiers, KeywordJoin)
|
|
expr, err = p.parseJoinTableExpr(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
constrains, err := p.tryParseJoinConstraints(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// try parse next join
|
|
rightExpr, err = p.parseJoinRightExpr(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &JoinExpr{
|
|
JoinPos: pos,
|
|
Left: expr,
|
|
Right: rightExpr,
|
|
Modifiers: modifiers,
|
|
Constraints: constrains,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) parseJoinExpr(pos Pos) (expr Expr, err error) {
|
|
if expr, err = p.parseJoinTableExpr(p.Pos()); err != nil {
|
|
return nil, err
|
|
}
|
|
rightExpr, err := p.parseJoinRightExpr(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if rightExpr == nil {
|
|
return expr, nil
|
|
}
|
|
return &JoinExpr{
|
|
JoinPos: pos,
|
|
Left: expr,
|
|
Right: rightExpr,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) parseTableExpr(pos Pos) (*TableExpr, error) {
|
|
var expr Expr
|
|
var err error
|
|
switch {
|
|
case p.matchTokenKind(TokenKindString), p.matchTokenKind(TokenKindIdent):
|
|
// table name
|
|
tableIdentifier, err := p.parseTableIdentifier(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// it's a table name
|
|
if tableIdentifier.Database != nil || !p.matchTokenKind(TokenKindLParen) { // database.table
|
|
expr = tableIdentifier
|
|
} else {
|
|
// table function expr
|
|
tableArgs, err := p.parseTableArgList(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
expr = &TableFunctionExpr{
|
|
Name: tableIdentifier.Table,
|
|
Args: tableArgs,
|
|
}
|
|
}
|
|
case p.matchTokenKind(TokenKindLParen):
|
|
expr, err = p.parseSubQuery(p.Pos())
|
|
default:
|
|
return nil, errors.New("expect table name or subquery")
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
tableEnd := expr.End()
|
|
if p.tryConsumeKeywords(KeywordAs) {
|
|
alias, err := p.parseIdent()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
expr = &AliasExpr{
|
|
Expr: expr,
|
|
AliasPos: alias.Pos(),
|
|
Alias: alias,
|
|
}
|
|
tableEnd = expr.End()
|
|
} else if p.matchTokenKind(TokenKindIdent) && p.lastTokenKind() != TokenKindKeyword {
|
|
alias, err := p.parseIdent()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
expr = &AliasExpr{
|
|
Expr: expr,
|
|
AliasPos: alias.Pos(),
|
|
Alias: alias,
|
|
}
|
|
tableEnd = expr.End()
|
|
}
|
|
|
|
isFinalExist := false
|
|
if p.tryConsumeKeywords(KeywordFinal) {
|
|
switch expr.(type) {
|
|
case *TableFunctionExpr:
|
|
return nil, errors.New("table function doesn't support FINAL")
|
|
case *SelectQuery:
|
|
return nil, errors.New("subquery doesn't support FINAL")
|
|
}
|
|
isFinalExist = true
|
|
tableEnd = expr.End()
|
|
}
|
|
|
|
return &TableExpr{
|
|
TablePos: pos,
|
|
TableEnd: tableEnd,
|
|
Expr: expr,
|
|
HasFinal: isFinalExist,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) tryParsePrewhereClause(pos Pos) (*PrewhereClause, error) {
|
|
if !p.matchKeyword(KeywordPrewhere) {
|
|
return nil, nil
|
|
}
|
|
return p.parsePrewhereClause(pos)
|
|
}
|
|
func (p *Parser) parsePrewhereClause(pos Pos) (*PrewhereClause, error) {
|
|
if err := p.expectKeyword(KeywordPrewhere); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
expr, err := p.parseExpr(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &PrewhereClause{
|
|
PrewherePos: pos,
|
|
Expr: expr,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) tryParseWhereClause(pos Pos) (*WhereClause, error) {
|
|
if !p.matchKeyword(KeywordWhere) {
|
|
return nil, nil
|
|
}
|
|
return p.parseWhereClause(pos)
|
|
}
|
|
|
|
func (p *Parser) parseWhereClause(pos Pos) (*WhereClause, error) {
|
|
if err := p.expectKeyword(KeywordWhere); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
expr, err := p.parseExpr(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &WhereClause{
|
|
WherePos: pos,
|
|
Expr: expr,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) tryParseGroupByClause(pos Pos) (*GroupByClause, error) {
|
|
if !p.matchKeyword(KeywordGroup) {
|
|
return nil, nil
|
|
}
|
|
return p.parseGroupByClause(pos)
|
|
}
|
|
|
|
// syntax: groupByClause? (WITH (CUBE | ROLLUP))? (WITH TOTALS)?
|
|
func (p *Parser) parseGroupByClause(pos Pos) (*GroupByClause, error) {
|
|
if err := p.expectKeyword(KeywordGroup); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := p.expectKeyword(KeywordBy); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var expr Expr
|
|
var err error
|
|
aggregateType := ""
|
|
switch {
|
|
case p.matchKeyword(KeywordCube) || p.matchKeyword(KeywordRollup):
|
|
aggregateType = p.last().String
|
|
_ = p.lexer.consumeToken()
|
|
expr, err = p.parseFunctionParams(p.Pos())
|
|
case p.tryConsumeKeywords(KeywordGrouping, KeywordSets):
|
|
aggregateType = "GROUPING SETS"
|
|
expr, err = p.parseFunctionParams(p.Pos())
|
|
case p.tryConsumeKeywords(KeywordAll):
|
|
aggregateType = "ALL"
|
|
default:
|
|
expr, err = p.parseColumnExprListWithLParen(p.Pos())
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
groupBy := &GroupByClause{
|
|
GroupByPos: pos,
|
|
AggregateType: aggregateType,
|
|
Expr: expr,
|
|
}
|
|
|
|
// parse WITH CUBE, ROLLUP, TOTALS
|
|
for p.tryConsumeKeywords(KeywordWith) {
|
|
switch {
|
|
case p.tryConsumeKeywords(KeywordCube):
|
|
groupBy.WithCube = true
|
|
case p.tryConsumeKeywords(KeywordRollup):
|
|
groupBy.WithRollup = true
|
|
case p.tryConsumeKeywords(KeywordTotals):
|
|
groupBy.WithTotals = true
|
|
default:
|
|
return nil, fmt.Errorf("expected CUBE, ROLLUP or TOTALS, got %s", p.lastTokenKind())
|
|
}
|
|
}
|
|
groupBy.GroupByEnd = p.Pos()
|
|
|
|
return groupBy, nil
|
|
}
|
|
|
|
func (p *Parser) tryParseLimitClause(pos Pos) (*LimitClause, error) {
|
|
if !p.matchKeyword(KeywordLimit) {
|
|
return nil, nil
|
|
}
|
|
return p.parseLimitClause(pos)
|
|
}
|
|
|
|
func (p *Parser) parseLimitClause(pos Pos) (*LimitClause, error) {
|
|
if err := p.expectKeyword(KeywordLimit); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
limit, err := p.parseExpr(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var offset Expr
|
|
if p.tryConsumeKeywords(KeywordOffset) {
|
|
offset, err = p.parseExpr(p.Pos())
|
|
} else if p.tryConsumeTokenKind(TokenKindComma) != nil {
|
|
offset = limit
|
|
limit, err = p.parseExpr(p.Pos())
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &LimitClause{
|
|
LimitPos: pos,
|
|
Limit: limit,
|
|
Offset: offset,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) tryParseLimitByClause(pos Pos) (Expr, error) {
|
|
if !p.matchKeyword(KeywordLimit) {
|
|
return nil, nil
|
|
}
|
|
return p.parseLimitByClause(pos)
|
|
}
|
|
|
|
func (p *Parser) parseBetweenClause(expr Expr) (*BetweenClause, error) {
|
|
if err := p.expectKeyword(KeywordBetween); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
betweenExpr, err := p.parseSubExpr(p.Pos(), PrecedenceBetweenLike)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
andPos := p.Pos()
|
|
if err := p.expectKeyword(KeywordAnd); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
andExpr, err := p.parseSubExpr(p.Pos(), PrecedenceBetweenLike)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &BetweenClause{
|
|
Expr: expr,
|
|
Between: betweenExpr,
|
|
AndPos: andPos,
|
|
And: andExpr,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) parseLimitByClause(pos Pos) (Expr, error) {
|
|
limit, err := p.parseLimitClause(pos)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var by *ColumnExprList
|
|
if !p.tryConsumeKeywords(KeywordBy) {
|
|
return limit, nil
|
|
}
|
|
if by, err = p.parseColumnExprListWithLParen(p.Pos()); err != nil {
|
|
return nil, err
|
|
}
|
|
return &LimitByClause{
|
|
Limit: limit,
|
|
ByExpr: by,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) tryParseWindowFrameClause(pos Pos) (*WindowFrameClause, error) {
|
|
if !p.matchKeyword(KeywordRows) && !p.matchKeyword(KeywordRange) {
|
|
return nil, nil
|
|
}
|
|
return p.parseWindowFrameClause(pos)
|
|
}
|
|
|
|
func (p *Parser) parseWindowFrameClause(pos Pos) (*WindowFrameClause, error) {
|
|
var windowFrameType string
|
|
if p.matchKeyword(KeywordRows) || p.matchKeyword(KeywordRange) {
|
|
windowFrameType = p.last().String
|
|
_ = p.lexer.consumeToken()
|
|
} else {
|
|
return nil, fmt.Errorf("expected ROWS or RANGE for window frame")
|
|
}
|
|
|
|
var expr Expr
|
|
if p.tryConsumeKeywords(KeywordBetween) {
|
|
left, err := p.parseFrameExtent()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
andPos := p.Pos()
|
|
if err := p.expectKeyword(KeywordAnd); err != nil {
|
|
return nil, err
|
|
}
|
|
right, err := p.parseFrameExtent()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
expr = &BetweenClause{
|
|
Between: left,
|
|
AndPos: andPos,
|
|
And: right,
|
|
}
|
|
} else {
|
|
// single extent
|
|
extent, err := p.parseFrameExtent()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
expr = extent
|
|
}
|
|
|
|
return &WindowFrameClause{
|
|
FramePos: pos,
|
|
Type: windowFrameType,
|
|
Extend: expr,
|
|
}, nil
|
|
}
|
|
|
|
// parseFrameExtent parses a single frame extent
|
|
func (p *Parser) parseFrameExtent() (Expr, error) {
|
|
switch {
|
|
case p.matchKeyword(KeywordCurrent):
|
|
return p.parseFrameCurrentRow()
|
|
case p.matchKeyword(KeywordUnbounded):
|
|
return p.parseFrameUnbounded()
|
|
case p.matchTokenKind(TokenKindInt):
|
|
return p.parseFrameNumber()
|
|
case p.matchTokenKind(TokenKindLBrace):
|
|
return p.parseFrameParam()
|
|
case p.matchKeyword(KeywordInterval):
|
|
return p.parseFrameInterval()
|
|
default:
|
|
return nil, fmt.Errorf("expected UNBOUNDED, CURRENT ROW, integer, parameter, or interval")
|
|
}
|
|
}
|
|
|
|
func (p *Parser) parseFrameCurrentRow() (Expr, error) {
|
|
currentPos := p.Pos()
|
|
_ = p.lexer.consumeToken()
|
|
if err := p.expectKeyword(KeywordRow); err != nil {
|
|
return nil, err
|
|
}
|
|
rowEnd := p.End()
|
|
return &WindowFrameCurrentRow{
|
|
CurrentPos: currentPos,
|
|
RowEnd: rowEnd,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) parseFrameUnbounded() (Expr, error) {
|
|
unboundedPos := p.Pos()
|
|
_ = p.lexer.consumeToken()
|
|
|
|
direction, err := p.parseFrameDirection()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &WindowFrameUnbounded{
|
|
UnboundedPos: unboundedPos,
|
|
Direction: direction,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) parseFrameNumber() (Expr, error) {
|
|
number, err := p.parseNumber(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
direction, endPos, err := p.parseFrameDirectionWithEnd()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &WindowFrameNumber{
|
|
EndPos: endPos,
|
|
Number: number,
|
|
Direction: direction,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) parseFrameParam() (Expr, error) {
|
|
queryParam, err := p.parseQueryParam(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
direction, endPos, err := p.parseFrameDirectionWithEnd()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &WindowFrameParam{
|
|
Param: queryParam,
|
|
EndPos: endPos,
|
|
Direction: direction,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) parseFrameInterval() (Expr, error) {
|
|
intervalExpr, err := p.parseInterval(true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
direction, endPos, err := p.parseFrameDirectionWithEnd()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &WindowFrameExtendExpr{
|
|
Expr: intervalExpr,
|
|
Direction: direction,
|
|
EndPos: endPos,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) parseFrameDirection() (string, error) {
|
|
switch {
|
|
case p.matchKeyword(KeywordPreceding), p.matchKeyword(KeywordFollowing):
|
|
direction := p.last().String
|
|
_ = p.lexer.consumeToken()
|
|
return direction, nil
|
|
default:
|
|
return "", fmt.Errorf("expected PRECEDING or FOLLOWING, got %s", p.lastTokenKind())
|
|
}
|
|
}
|
|
|
|
func (p *Parser) parseFrameDirectionWithEnd() (string, Pos, error) {
|
|
if !p.matchKeyword(KeywordPreceding) && !p.matchKeyword(KeywordFollowing) {
|
|
return "", 0, fmt.Errorf("expected PRECEDING or FOLLOWING, got %s", p.lastTokenKind())
|
|
}
|
|
endPos := p.End()
|
|
direction := p.last().String
|
|
_ = p.lexer.consumeToken()
|
|
return direction, endPos, nil
|
|
}
|
|
|
|
func (p *Parser) tryParseWindowClause(pos Pos) (*WindowClause, error) {
|
|
if !p.matchKeyword(KeywordWindow) {
|
|
return nil, nil
|
|
}
|
|
return p.parseWindowClause(pos)
|
|
}
|
|
|
|
func (p *Parser) parseWindowCondition(pos Pos) (*WindowExpr, error) {
|
|
if err := p.expectTokenKind(TokenKindLParen); err != nil {
|
|
return nil, err
|
|
}
|
|
partitionBy, err := p.tryParsePartitionByClause(pos)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
orderBy, err := p.tryParseOrderByClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
frame, err := p.tryParseWindowFrameClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
rightParenPos := p.Pos()
|
|
if err := p.expectTokenKind(TokenKindRParen); err != nil {
|
|
return nil, err
|
|
}
|
|
return &WindowExpr{
|
|
LeftParenPos: pos,
|
|
RightParenPos: rightParenPos,
|
|
PartitionBy: partitionBy,
|
|
OrderBy: orderBy,
|
|
Frame: frame,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) parseWindowClause(pos Pos) (*WindowClause, error) {
|
|
if err := p.expectKeyword(KeywordWindow); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
windows := make([]*WindowDefinition, 0, 1)
|
|
for {
|
|
windowName, err := p.parseIdent()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
asPos := p.Pos()
|
|
if err := p.expectKeyword(KeywordAs); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
condition, err := p.parseWindowCondition(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
windows = append(windows, &WindowDefinition{
|
|
Name: windowName,
|
|
AsPos: asPos,
|
|
Expr: condition,
|
|
})
|
|
|
|
if p.tryConsumeTokenKind(TokenKindComma) == nil {
|
|
break
|
|
}
|
|
}
|
|
|
|
var endPos Pos
|
|
if len(windows) > 0 {
|
|
endPos = windows[len(windows)-1].End()
|
|
}
|
|
|
|
return &WindowClause{
|
|
WindowPos: pos,
|
|
EndPos: endPos,
|
|
Windows: windows,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) tryParseArrayJoinClause(pos Pos) (*ArrayJoinClause, error) {
|
|
if !p.matchKeyword(KeywordLeft) && !p.matchKeyword(KeywordInner) && !p.matchKeyword(KeywordArray) {
|
|
return nil, nil
|
|
}
|
|
return p.parseArrayJoinClause(pos)
|
|
}
|
|
|
|
func (p *Parser) parseArrayJoinClause(_ Pos) (*ArrayJoinClause, error) {
|
|
var typ string
|
|
switch {
|
|
case p.matchKeyword(KeywordLeft), p.matchKeyword(KeywordInner):
|
|
typ = p.last().String
|
|
_ = p.lexer.consumeToken()
|
|
}
|
|
arrayPos := p.Pos()
|
|
if err := p.expectKeyword(KeywordArray); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := p.expectKeyword(KeywordJoin); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
expr, err := p.parseColumnExprList(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &ArrayJoinClause{
|
|
ArrayPos: arrayPos,
|
|
Type: typ,
|
|
Expr: expr,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) tryParseHavingClause(pos Pos) (*HavingClause, error) {
|
|
if !p.matchKeyword(KeywordHaving) {
|
|
return nil, nil
|
|
}
|
|
return p.parseHavingClause(pos)
|
|
}
|
|
|
|
func (p *Parser) parseHavingClause(pos Pos) (*HavingClause, error) {
|
|
if err := p.expectKeyword(KeywordHaving); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
expr, err := p.parseColumnsExpr(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &HavingClause{
|
|
HavingPos: pos,
|
|
Expr: expr,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) parseSubQuery(_ Pos) (*SubQuery, error) {
|
|
|
|
hasParen := p.tryConsumeTokenKind(TokenKindLParen) != nil
|
|
|
|
selectQuery, err := p.parseSelectQuery(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if hasParen {
|
|
if err := p.expectTokenKind(TokenKindRParen); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return &SubQuery{
|
|
HasParen: hasParen,
|
|
Select: selectQuery,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) parseSelectQuery(_ Pos) (*SelectQuery, error) {
|
|
if !p.matchKeyword(KeywordSelect) && !p.matchKeyword(KeywordWith) && !p.matchTokenKind(TokenKindLParen) {
|
|
return nil, fmt.Errorf("expected SELECT, WITH or (, got %s", p.lastTokenKind())
|
|
}
|
|
|
|
hasParen := p.tryConsumeTokenKind(TokenKindLParen) != nil
|
|
selectStmt, err := p.parseSelectStmt(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch {
|
|
case p.tryConsumeKeywords(KeywordUnion):
|
|
switch {
|
|
case p.tryConsumeKeywords(KeywordAll):
|
|
unionAllExpr, err := p.parseSelectQuery(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
selectStmt.UnionAll = unionAllExpr
|
|
case p.tryConsumeKeywords(KeywordDistinct):
|
|
unionDistinctExpr, err := p.parseSelectQuery(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
selectStmt.UnionDistinct = unionDistinctExpr
|
|
default:
|
|
return nil, fmt.Errorf("expected ALL or DISTINCT, got %s", p.lastTokenKind())
|
|
}
|
|
case p.tryConsumeKeywords(KeywordExcept):
|
|
exceptExpr, err := p.parseSelectQuery(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
selectStmt.Except = exceptExpr
|
|
}
|
|
if hasParen {
|
|
if err := p.expectTokenKind(TokenKindRParen); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return selectStmt, nil
|
|
}
|
|
|
|
func (p *Parser) parseSelectStmt(pos Pos) (*SelectQuery, error) { // nolint: funlen
|
|
withClause, err := p.tryParseWithClause(pos)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := p.expectKeyword(KeywordSelect); err != nil {
|
|
return nil, err
|
|
}
|
|
// DISTINCT?
|
|
hasDistinct := p.tryConsumeKeywords(KeywordDistinct)
|
|
distinctOn, err := p.tryParseDistinctOn(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
top, err := p.tryParseTopClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
selectItems, err := p.parseSelectItems()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
statementEnd := pos
|
|
if len(selectItems) > 0 {
|
|
statementEnd = selectItems[len(selectItems)-1].End()
|
|
}
|
|
from, err := p.tryParseFromClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if from != nil {
|
|
statementEnd = from.End()
|
|
}
|
|
arrayJoin, err := p.tryParseArrayJoinClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if arrayJoin != nil {
|
|
statementEnd = arrayJoin.End()
|
|
}
|
|
prewhere, err := p.tryParsePrewhereClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if prewhere != nil {
|
|
statementEnd = prewhere.End()
|
|
}
|
|
where, err := p.tryParseWhereClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if where != nil {
|
|
statementEnd = where.End()
|
|
}
|
|
groupBy, err := p.tryParseGroupByClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if groupBy != nil {
|
|
statementEnd = groupBy.End()
|
|
}
|
|
withTotal := false
|
|
lastPos := p.Pos()
|
|
if p.tryConsumeKeywords(KeywordWith) {
|
|
if err := p.expectKeyword(KeywordTotals); err != nil {
|
|
return nil, err
|
|
}
|
|
withTotal = true
|
|
statementEnd = lastPos
|
|
}
|
|
having, err := p.tryParseHavingClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if having != nil {
|
|
statementEnd = having.End()
|
|
}
|
|
window, err := p.tryParseWindowClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if window != nil {
|
|
statementEnd = window.End()
|
|
}
|
|
orderBy, err := p.tryParseOrderByClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if orderBy != nil {
|
|
statementEnd = orderBy.End()
|
|
}
|
|
|
|
var limitBy *LimitByClause
|
|
var limit *LimitClause
|
|
parsedLimitBy, err := p.tryParseLimitByClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if parsedLimitBy != nil {
|
|
statementEnd = parsedLimitBy.End()
|
|
switch e := parsedLimitBy.(type) {
|
|
case *LimitByClause:
|
|
limitBy = e
|
|
limit, err = p.tryParseLimitClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if limit != nil {
|
|
statementEnd = limit.End()
|
|
}
|
|
case *LimitClause:
|
|
limit = e
|
|
}
|
|
}
|
|
|
|
settings, err := p.tryParseSettingsClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if settings != nil {
|
|
statementEnd = settings.End()
|
|
}
|
|
|
|
format, err := p.tryParseFormat(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if format != nil {
|
|
statementEnd = format.End()
|
|
}
|
|
|
|
return &SelectQuery{
|
|
With: withClause,
|
|
SelectPos: pos,
|
|
StatementEnd: statementEnd,
|
|
Top: top,
|
|
HasDistinct: hasDistinct,
|
|
DistinctOn: distinctOn,
|
|
SelectItems: selectItems,
|
|
From: from,
|
|
ArrayJoin: arrayJoin,
|
|
Window: window,
|
|
Prewhere: prewhere,
|
|
Where: where,
|
|
GroupBy: groupBy,
|
|
Having: having,
|
|
OrderBy: orderBy,
|
|
LimitBy: limitBy,
|
|
Limit: limit,
|
|
Settings: settings,
|
|
Format: format,
|
|
WithTotal: withTotal,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) parseCTEStmt(pos Pos) (*CTEStmt, error) {
|
|
expr, err := p.parseExpr(pos)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := p.expectKeyword(KeywordAs); err != nil {
|
|
return nil, err
|
|
}
|
|
if p.matchTokenKind(TokenKindLParen) {
|
|
selectQuery, err := p.parseSelectQuery(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &CTEStmt{
|
|
CTEPos: pos,
|
|
Expr: expr,
|
|
Alias: selectQuery,
|
|
}, nil
|
|
}
|
|
name, err := p.parseIdent()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &CTEStmt{
|
|
CTEPos: pos,
|
|
Expr: expr,
|
|
Alias: name,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) tryParseSampleClause(pos Pos) (*SampleClause, error) {
|
|
if !p.matchKeyword(KeywordSample) {
|
|
return nil, nil
|
|
}
|
|
return p.parseSampleClause(pos)
|
|
}
|
|
|
|
func (p *Parser) parseSampleClause(pos Pos) (*SampleClause, error) {
|
|
if err := p.expectKeyword(KeywordSample); err != nil {
|
|
return nil, err
|
|
}
|
|
ratio, err := p.parseRatioExpr(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var offset *RatioExpr
|
|
if p.matchKeyword(KeywordOffset) {
|
|
_ = p.lexer.consumeToken()
|
|
offset, err = p.parseRatioExpr(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return &SampleClause{
|
|
SamplePos: pos,
|
|
Ratio: ratio,
|
|
Offset: offset,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) parseExplainStmt(pos Pos) (*ExplainStmt, error) {
|
|
if err := p.expectKeyword(KeywordExplain); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var explainType string
|
|
switch {
|
|
case p.matchKeyword(KeywordSyntax),
|
|
p.matchKeyword(KeywordPipeline),
|
|
p.matchKeyword(KeywordEstimate),
|
|
p.matchKeyword(KeywordAst):
|
|
explainType = p.last().String
|
|
_ = p.lexer.consumeToken()
|
|
default:
|
|
return nil, fmt.Errorf("expected SYNTAX, PIPELINE, ESTIMATE or AST, got %s", p.lastTokenKind())
|
|
}
|
|
stmt, err := p.parseSelectQuery(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &ExplainStmt{
|
|
ExplainPos: pos,
|
|
Type: explainType,
|
|
Statement: stmt,
|
|
}, nil
|
|
}
|