Files

2297 lines
53 KiB
Go

package parser
import (
"fmt"
)
func (p *Parser) parseDDL(pos Pos) (DDL, error) {
switch {
case p.matchKeyword(KeywordCreate),
p.matchKeyword(KeywordAttach):
_ = p.lexer.consumeToken()
orReplace := p.tryConsumeKeywords(KeywordOr, KeywordReplace)
if orReplace && !p.matchOneOfKeywords(KeywordTemporary, KeywordTable, KeywordView, KeywordFunction, KeywordDictionary) {
return nil, fmt.Errorf("expected keyword: TEMPORARY|TABLE|VIEW|FUNCTION|DICTIONARY, but got %q", p.last().String)
}
switch {
case p.matchKeyword(KeywordDatabase):
return p.parseCreateDatabase(pos)
case p.matchKeyword(KeywordDictionary):
return p.parseCreateDictionary(pos, orReplace)
case p.matchKeyword(KeywordTable),
p.matchKeyword(KeywordTemporary):
return p.parseCreateTable(pos, orReplace)
case p.matchKeyword(KeywordFunction):
return p.parseCreateFunction(pos, orReplace)
case p.matchKeyword(KeywordMaterialized):
return p.parseCreateMaterializedView(pos)
case p.matchKeyword(KeywordLive):
return p.parseCreateLiveView(pos)
case p.matchKeyword(KeywordView):
return p.parseCreateView(pos, orReplace)
case p.matchKeyword(KeywordRole):
return p.parseCreateRole(pos)
case p.matchKeyword(KeywordUser):
return p.parseCreateUser(pos)
default:
return nil, fmt.Errorf("expected keyword: DATABASE|DICTIONARY|TABLE|VIEW|ROLE|USER|FUNCTION|MATERIALIZED, but got %q",
p.lastTokenKind())
}
case p.matchKeyword(KeywordAlter):
_ = p.lexer.consumeToken()
switch {
case p.matchKeyword(KeywordRole):
return p.parseAlterRole(pos)
case p.matchKeyword(KeywordTable):
return p.parseAlterTable(pos)
default:
return nil, fmt.Errorf("expected keyword: TABLE|ROLE, but got %q", p.last().String)
}
case p.matchKeyword(KeywordDrop),
p.matchKeyword(KeywordDetach):
_ = p.lexer.consumeToken()
switch {
case p.matchKeyword(KeywordDatabase):
return p.parseDropDatabase(pos)
case p.matchKeyword(KeywordTemporary),
p.matchKeyword(KeywordView),
p.matchKeyword(KeywordDictionary),
p.matchKeyword(KeywordTable):
return p.parseDropStmt(pos)
case p.matchKeyword(KeywordUser),
p.matchKeyword(KeywordRole):
return p.parserDropUserOrRole(pos)
default:
return nil, fmt.Errorf("expected keyword: DATABASE|TABLE, but got %q", p.last().String)
}
case p.matchKeyword(KeywordTruncate):
return p.parseTruncateTable(pos)
case p.matchKeyword(KeywordRename):
return p.parseRenameStmt(pos)
}
return nil, nil // nolint
}
func (p *Parser) parseCreateDatabase(pos Pos) (*CreateDatabase, error) {
if err := p.expectKeyword(KeywordDatabase); err != nil {
return nil, err
}
// try to parse IF NOT EXISTS clause
ifNotExists, err := p.tryParseIfNotExists()
if err != nil {
return nil, err
}
// parse database name
name, err := p.parseIdent()
if err != nil {
return nil, err
}
StatementEnd := name.End()
onCluster, err := p.tryParseClusterClause(p.Pos())
if err != nil {
return nil, err
}
if onCluster != nil {
StatementEnd = onCluster.End()
}
engineExpr, err := p.tryParseEngineExpr(p.Pos())
if err != nil {
return nil, err
}
if engineExpr != nil {
StatementEnd = onCluster.End()
}
commentExpr, err := p.tryParseComment()
if err != nil {
return nil, err
}
return &CreateDatabase{
CreatePos: pos,
StatementEnd: StatementEnd,
Name: name,
IfNotExists: ifNotExists,
OnCluster: onCluster,
Engine: engineExpr,
Comment: commentExpr,
}, nil
}
func (p *Parser) parseCreateDictionary(pos Pos, orReplace bool) (*CreateDictionary, error) {
if err := p.expectKeyword(KeywordDictionary); err != nil {
return nil, err
}
createDict := &CreateDictionary{
CreatePos: pos,
OrReplace: orReplace,
}
// parse IF NOT EXISTS clause if exists
var err error
createDict.IfNotExists, err = p.tryParseIfNotExists()
if err != nil {
return nil, err
}
// parse dictionary name
name, err := p.parseTableIdentifier(p.Pos())
if err != nil {
return nil, err
}
createDict.Name = name
// try parse UUID clause if exists
uuid, err := p.tryParseUUID()
if err != nil {
return nil, err
}
createDict.UUID = uuid
// parse ON CLUSTER clause if exists
onCluster, err := p.tryParseClusterClause(p.Pos())
if err != nil {
return nil, err
}
createDict.OnCluster = onCluster
// parse dictionary schema clause (required)
schema, err := p.parseDictionarySchemaClause(p.Pos())
if err != nil {
return nil, err
}
createDict.Schema = schema
// parse dictionary engine clause (required)
engine, err := p.parseDictionaryEngineClause(p.Pos())
if err != nil {
return nil, err
}
createDict.Engine = engine
createDict.StatementEnd = engine.End()
// parse COMMENT clause if exists
comment, err := p.tryParseComment()
if err != nil {
return nil, err
}
createDict.Comment = comment
if comment != nil {
createDict.StatementEnd = comment.End()
}
return createDict, nil
}
func (p *Parser) parseCreateTable(pos Pos, orReplace bool) (*CreateTable, error) {
createTable := &CreateTable{CreatePos: pos, OrReplace: orReplace}
createTable.HasTemporary = p.tryConsumeKeywords(KeywordTemporary)
if err := p.expectKeyword(KeywordTable); err != nil {
return nil, err
}
// parse IF NOT EXISTS clause if exists
var err error
createTable.IfNotExists, err = p.tryParseIfNotExists()
if err != nil {
return nil, err
}
tableIdentifier, err := p.parseTableIdentifier(p.Pos())
if err != nil {
return nil, err
}
createTable.Name = tableIdentifier
// try parse UUID clause if exists
uuid, err := p.tryParseUUID()
if err != nil {
return nil, err
}
createTable.UUID = uuid
// parse ON CLUSTER clause if exists
onCluster, err := p.tryParseClusterClause(p.Pos())
if err != nil {
return nil, err
}
createTable.OnCluster = onCluster
tableSchema, err := p.parseTableSchemaClause(p.Pos())
if err != nil {
return nil, err
}
createTable.TableSchema = tableSchema
engineExpr, err := p.tryParseEngineExpr(p.Pos())
if err != nil {
return nil, err
}
if engineExpr != nil {
createTable.Engine = engineExpr
createTable.StatementEnd = engineExpr.End()
}
if p.tryConsumeKeywords(KeywordAs) {
subQuery, err := p.parseSubQuery(p.Pos())
if err != nil {
return nil, err
}
createTable.SubQuery = subQuery
createTable.StatementEnd = subQuery.End()
}
comment, err := p.tryParseComment()
if err != nil {
return nil, err
}
createTable.Comment = comment
return createTable, nil
}
func (p *Parser) parseIdentOrFunction(_ Pos) (Expr, error) {
ident, err := p.parseIdent()
if err != nil {
return nil, err
}
switch {
case p.matchTokenKind(TokenKindLBracket):
params, err := p.parseArrayParams(p.Pos())
if err != nil {
return nil, err
}
return &ObjectParams{
Object: ident,
Params: params,
}, nil
case p.matchTokenKind(TokenKindLParen):
params, err := p.parseFunctionParams(p.Pos())
if err != nil {
return nil, err
}
funcExpr := &FunctionExpr{
Name: ident,
Params: params,
}
overPos := p.Pos()
if p.tryConsumeKeywords(KeywordOver) {
var overExpr Expr
switch {
case p.matchTokenKind(TokenKindIdent):
overExpr, err = p.parseIdent()
case p.matchTokenKind(TokenKindLParen):
overExpr, err = p.parseWindowCondition(p.Pos())
if err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("expected window name or (, but got %q", p.lastTokenKind())
}
if err != nil {
return nil, err
}
return &WindowFunctionExpr{
Function: funcExpr,
OverPos: overPos,
OverExpr: overExpr,
}, nil
}
return funcExpr, nil
case p.tryConsumeTokenKind(TokenKindDot) != nil:
switch {
case p.matchTokenKind(TokenKindIdent):
fields := []*Ident{ident}
for {
child, err := p.parseIdent()
if err != nil {
return nil, err
}
fields = append(fields, child)
if p.tryConsumeTokenKind(TokenKindDot) == nil {
break
}
}
return &Path{Fields: fields}, nil
case p.matchTokenKind("*"):
nextIdent, err := p.parseColumnStar(p.Pos())
if err != nil {
return nil, err
}
return &NestedIdentifier{
Ident: ident,
DotIdent: nextIdent,
}, nil
case p.matchTokenKind(TokenKindInt):
i, err := p.parseNumber(p.Pos())
if err != nil {
return nil, err
}
return &IndexOperation{
Object: ident,
Operation: TokenKindDot,
Index: i,
}, nil
default:
return nil, fmt.Errorf("expected IDENT, NUMBER or *, but got %q", p.lastTokenKind())
}
}
return ident, nil
}
func (p *Parser) parseTableIdentifier(_ Pos) (*TableIdentifier, error) {
ident, err := p.parseIdentOrString()
if err != nil {
return nil, err
}
dotIdent, err := p.tryParseDotIdentOrString(p.Pos())
if err != nil {
return nil, err
}
if dotIdent != nil {
return &TableIdentifier{
Database: ident,
Table: dotIdent,
}, nil
}
return &TableIdentifier{
Table: ident,
}, nil
}
func (p *Parser) parseTableSchemaClause(pos Pos) (*TableSchemaClause, error) {
switch {
case p.matchTokenKind(TokenKindLParen):
// parse column definitions
if err := p.expectTokenKind(TokenKindLParen); err != nil {
return nil, err
}
columns, err := p.parseTableColumns()
if err != nil {
return nil, err
}
rightParenPos := p.Pos()
if err := p.expectTokenKind(TokenKindRParen); err != nil {
return nil, err
}
return &TableSchemaClause{
SchemaPos: pos,
SchemaEnd: rightParenPos,
Columns: columns,
}, nil
case p.tryConsumeKeywords(KeywordAs):
switch {
case p.matchTokenKind(TokenKindIdent):
ident, err := p.parseIdent()
if err != nil {
return nil, err
}
switch {
case p.matchTokenKind(TokenKindDot):
// it's a database.table
dotIdent, err := p.tryParseDotIdent(p.Pos())
if err != nil {
return nil, err
}
return &TableSchemaClause{
SchemaPos: pos,
SchemaEnd: dotIdent.End(),
AliasTable: &TableIdentifier{
Database: ident,
Table: dotIdent,
},
}, nil
case p.matchTokenKind(TokenKindLParen):
// it's a table function
argsExpr, err := p.parseTableArgList(pos)
if err != nil {
return nil, err
}
return &TableSchemaClause{
SchemaPos: pos,
SchemaEnd: p.End(),
TableFunction: &TableFunctionExpr{
Name: ident,
Args: argsExpr,
},
}, nil
default:
return &TableSchemaClause{
SchemaPos: pos,
SchemaEnd: p.End(),
AliasTable: &TableIdentifier{
Table: ident,
},
}, nil
}
}
}
// no schema is ok for MATERIALIZED VIEW
return nil, nil
}
func (p *Parser) parseTableColumns() ([]Expr, error) {
columns := make([]Expr, 0)
for !p.lexer.isEOF() {
switch {
case p.matchKeyword(KeywordIndex):
indexPos := p.Pos()
_ = p.lexer.consumeToken()
index, err := p.parseTableIndex(indexPos)
if err != nil {
return nil, err
}
columns = append(columns, index)
case p.matchKeyword(KeywordConstraint):
constraintPos := p.Pos()
_ = p.lexer.consumeToken()
ident, err := p.parseIdent()
if err != nil {
return nil, err
}
if err := p.expectKeyword(KeywordCheck); err != nil {
return nil, err
}
expr, err := p.parseExpr(p.Pos())
if err != nil {
return nil, err
}
columns = append(columns, &ConstraintClause{
ConstraintPos: constraintPos,
Constraint: ident,
Expr: expr,
})
case p.matchKeyword(KeywordProjection):
projection, err := p.parseTableProjection(p.Pos(), true)
if err != nil {
return nil, err
}
columns = append(columns, projection)
default:
column, err := p.tryParseTableColumnExpr(p.Pos())
if err != nil {
return nil, err
}
if column == nil {
break
}
columns = append(columns, column)
}
if p.tryConsumeTokenKind(TokenKindComma) == nil {
break
}
}
// end of column definitions
return columns, nil
}
func (p *Parser) tryParseTableColumnExpr(pos Pos) (*ColumnDef, error) {
if !p.matchTokenKind(TokenKindIdent) {
return nil, nil // nolint
}
return p.parseTableColumnExpr(pos)
}
func (p *Parser) parseTableColumnExpr(pos Pos) (*ColumnDef, error) {
// Not a column definition, just return
column := &ColumnDef{NamePos: pos}
// parse column name
name, err := p.ParseNestedIdentifier(p.Pos())
if err != nil {
return nil, err
}
column.Name = name
columnEnd := name.End()
if p.matchTokenKind(TokenKindIdent) && !p.matchKeyword(KeywordRemove) {
columnType, err := p.parseColumnType(p.Pos())
if err != nil {
return nil, err
}
column.Type = columnType
columnEnd = columnType.End()
}
nullable := p.tryParseNull(p.Pos())
if nullable != nil {
columnEnd = nullable.End()
}
notNull, err := p.tryParseNotNull(p.Pos())
if err != nil {
return nil, err
}
if notNull != nil {
columnEnd = notNull.End()
}
switch {
case p.tryConsumeKeywords(KeywordDefault):
column.DefaultExpr, err = p.parseExpr(p.Pos())
columnEnd = column.DefaultExpr.End()
case p.tryConsumeKeywords(KeywordMaterialized):
column.MaterializedExpr, err = p.parseExpr(p.Pos())
columnEnd = column.MaterializedExpr.End()
case p.tryConsumeKeywords(KeywordAlias):
column.AliasExpr, err = p.parseExpr(p.Pos())
columnEnd = column.AliasExpr.End()
}
if err != nil {
return nil, err
}
comment, err := p.tryParseColumnComment(p.Pos())
if err != nil {
return nil, err
}
if comment != nil {
columnEnd = comment.End()
}
codec, err := p.tryParseCompressionCodecs(p.Pos())
if err != nil {
return nil, err
}
if codec != nil {
columnEnd = codec.End()
}
ttl, err := p.tryParseTTLClause(p.Pos(), false)
if err != nil {
return nil, err
}
if ttl != nil {
columnEnd = ttl.End()
}
column.TTL = ttl
column.ColumnEnd = columnEnd
column.Comment = comment
column.Codec = codec
column.Nullable = nullable
column.NotNull = notNull
return column, nil
}
func (p *Parser) parseTableArgExpr(pos Pos) (Expr, error) {
switch {
case p.matchTokenKind(TokenKindIdent):
ident, err := p.parseIdent()
if err != nil {
return nil, err
}
switch {
// nest identifier
case p.matchTokenKind(TokenKindDot):
dotIdent, err := p.tryParseDotIdent(p.Pos())
if err != nil {
return nil, err
}
return &NestedIdentifier{
Ident: ident,
DotIdent: dotIdent,
}, nil
case p.matchTokenKind(TokenKindLParen):
argsExpr, err := p.parseTableArgList(pos)
if err != nil {
return nil, err
}
return &TableFunctionExpr{
Name: ident,
Args: argsExpr,
}, nil
default:
return ident, nil
}
case p.matchTokenKind(TokenKindLParen):
return p.parseSubQuery(p.Pos())
case p.matchTokenKind(TokenKindInt), p.matchTokenKind(TokenKindString), p.matchKeyword(KeywordNull):
return p.parseLiteral(p.Pos())
default:
return nil, fmt.Errorf("unexpected token: %q, expected <Name>, <literal>", p.last().String)
}
}
func (p *Parser) parseTableArgList(pos Pos) (*TableArgListExpr, error) {
if err := p.expectTokenKind(TokenKindLParen); err != nil {
return nil, err
}
args := make([]Expr, 0)
for !p.lexer.isEOF() {
arg, err := p.parseTableArgExpr(p.Pos())
if err != nil {
return nil, err
}
args = append(args, arg)
if p.tryConsumeTokenKind(TokenKindComma) == nil {
break
}
}
rightParenPos := p.Pos()
if err := p.expectTokenKind(TokenKindRParen); err != nil {
return nil, err
}
return &TableArgListExpr{
LeftParenPos: pos,
RightParenPos: rightParenPos,
Args: args,
}, nil
}
func (p *Parser) tryParseClusterClause(pos Pos) (*ClusterClause, error) {
if !p.tryConsumeKeywords(KeywordOn) {
return nil, nil // nolint
}
if err := p.expectKeyword(KeywordCluster); err != nil {
return nil, err
}
var expr Expr
var err error
switch {
case p.matchTokenKind(TokenKindIdent):
expr, err = p.parseIdent()
case p.matchTokenKind(TokenKindString):
expr, err = p.parseString(p.Pos())
default:
return nil, fmt.Errorf("unexpected token: %q, expected <IDENT> or <STRING>", p.last().String)
}
if err != nil {
return nil, err
}
return &ClusterClause{
OnPos: pos,
Expr: expr,
}, nil
}
func (p *Parser) tryParsePartitionByClause(pos Pos) (*PartitionByClause, error) {
if !p.tryConsumeKeywords(KeywordPartition) {
return nil, nil // nolint
}
if err := p.expectKeyword(KeywordBy); err != nil {
return nil, err
}
// parse partition key list
columnExpr, err := p.parseColumnExprListWithLParen(p.Pos())
if err != nil {
return nil, err
}
return &PartitionByClause{
PartitionPos: pos,
Expr: columnExpr,
}, nil
}
func (p *Parser) tryParsePrimaryKeyClause(pos Pos) (*PrimaryKeyClause, error) {
if !p.tryConsumeKeywords(KeywordPrimary) {
return nil, nil // nolint
}
if err := p.expectKeyword(KeywordKey); err != nil {
return nil, err
}
// parse partition key list
columnExpr, err := p.parseExpr(p.Pos())
if err != nil {
return nil, err
}
return &PrimaryKeyClause{
PrimaryPos: pos,
Expr: columnExpr,
}, nil
}
func (p *Parser) tryParseOrderByClause(pos Pos) (*OrderByClause, error) {
if !p.tryConsumeKeywords(KeywordOrder) {
return nil, nil // nolint
}
if err := p.expectKeyword(KeywordBy); err != nil {
return nil, err
}
return p.parseOrderByClause(pos)
}
func (p *Parser) parseOrderByClause(pos Pos) (*OrderByClause, error) {
orderByListExpr := &OrderByClause{OrderPos: pos, ListEnd: pos}
items := make([]Expr, 0)
for {
expr, err := p.parseOrderExpr(pos)
if err != nil {
return nil, err
}
if expr == nil {
break
}
items = append(items, expr)
if p.lexer.isEOF() || p.tryConsumeTokenKind(TokenKindComma) == nil {
break
}
}
if len(items) > 0 {
orderByListExpr.ListEnd = items[len(items)-1].End()
}
orderByListExpr.Items = items
// Parse optional INTERPOLATE clause
if p.matchKeyword(KeywordInterpolate) {
interpolatePos := p.Pos()
_ = p.lexer.consumeToken()
interpolate, err := p.parseInterpolateClause(interpolatePos)
if err != nil {
return nil, err
}
orderByListExpr.Interpolate = interpolate
orderByListExpr.ListEnd = interpolate.End()
}
return orderByListExpr, nil
}
func (p *Parser) parseOrderExpr(pos Pos) (*OrderExpr, error) {
// parse column expr
columnExpr, err := p.parseExpr(pos)
if err != nil {
return nil, err
}
var alias *Ident
if p.matchKeyword(KeywordAs) {
// It should be a subquery instead of an order by alias if the `AS` is followed by `SELECT` keyword.
if nextToken, err := p.lexer.peekToken(); err == nil && nextToken.ToString() == KeywordSelect {
return &OrderExpr{
OrderPos: pos,
Expr: columnExpr,
}, nil
}
// consume the `AS` keyword
_ = p.lexer.consumeToken()
alias, err = p.parseIdent()
if err != nil {
return nil, err
}
} else if p.matchKeyword(KeywordTtl) {
return &OrderExpr{
OrderPos: pos,
Expr: columnExpr,
}, nil
}
direction := OrderDirectionNone
switch {
case p.matchKeyword(KeywordAsc), p.matchKeyword(KeywordAscending):
direction = OrderDirectionAsc
_ = p.lexer.consumeToken()
case p.matchKeyword(KeywordDesc), p.matchKeyword(KeywordDescending):
direction = OrderDirectionDesc
_ = p.lexer.consumeToken()
}
// Parse optional WITH FILL clause
var fill *Fill
if p.tryConsumeKeywords(KeywordWith, KeywordFill) {
fillPos := p.Pos()
fill, err = p.parseFillClause(fillPos)
if err != nil {
return nil, err
}
}
return &OrderExpr{
OrderPos: pos,
Alias: alias,
Expr: columnExpr,
Direction: direction,
Fill: fill,
}, nil
}
func (p *Parser) parseFillClause(fillPos Pos) (*Fill, error) {
fill := &Fill{FillPos: fillPos}
// Parse optional FROM clause
if p.tryConsumeKeywords(KeywordFrom) {
fromExpr, err := p.parseExpr(fillPos)
if err != nil {
return nil, err
}
fill.From = fromExpr
}
// Parse optional TO clause
if p.tryConsumeKeywords(KeywordTo) {
toExpr, err := p.parseExpr(fillPos)
if err != nil {
return nil, err
}
fill.To = toExpr
}
// Parse optional STEP clause
if p.tryConsumeKeywords(KeywordStep) {
stepExpr, err := p.parseExpr(fillPos)
if err != nil {
return nil, err
}
fill.Step = stepExpr
}
// Parse optional STALENESS clause
if p.tryConsumeKeywords(KeywordStaleness) {
stalenessExpr, err := p.parseExpr(fillPos)
if err != nil {
return nil, err
}
fill.Staleness = stalenessExpr
}
return fill, nil
}
func (p *Parser) parseInterpolateClause(interpolatePos Pos) (*InterpolateClause, error) {
interpolate := &InterpolateClause{
InterpolatePos: interpolatePos,
ListEnd: interpolatePos + Pos(len("INTERPOLATE")),
}
if p.tryConsumeTokenKind(TokenKindLParen) == nil {
// INTERPOLATE without columns is valid
return interpolate, nil
}
items := make([]*InterpolateItem, 0)
for {
column, err := p.parseIdent()
if err != nil {
return nil, err
}
item := &InterpolateItem{Column: column}
if p.tryConsumeKeywords(KeywordAs) {
expr, err := p.parseExpr(interpolatePos)
if err != nil {
return nil, err
}
item.Expr = expr
}
items = append(items, item)
if p.tryConsumeTokenKind(TokenKindComma) == nil {
break
}
}
rparen := p.tryConsumeTokenKind(TokenKindRParen)
if rparen == nil {
return nil, fmt.Errorf("expected ')' after INTERPOLATE column list")
}
interpolate.Items = items
interpolate.ListEnd = rparen.End
return interpolate, nil
}
func (p *Parser) tryParseTTLClause(pos Pos, allowMultiValues bool) (*TTLClause, error) {
if !p.tryConsumeKeywords(KeywordTtl) {
return nil, nil // nolint
}
ttlExprList := &TTLClause{TTLPos: pos, ListEnd: pos}
// accept the TTL keyword
items, err := p.parseTTLClause(pos, allowMultiValues)
if err != nil {
return nil, err
}
if len(items) > 0 {
ttlExprList.ListEnd = items[len(items)-1].End()
}
ttlExprList.Items = items
return ttlExprList, nil
}
// parseTTLClause parses the TTL clause.
// allowMultiValues is used to determine whether to allow multiple TTL values.
func (p *Parser) parseTTLClause(pos Pos, allowMultiValues bool) ([]*TTLExpr, error) {
items := make([]*TTLExpr, 0)
expr, err := p.parseTTLExpr(pos)
if err != nil {
return nil, err
}
items = append(items, expr)
for allowMultiValues && !p.lexer.isEOF() && p.tryConsumeTokenKind(TokenKindComma) != nil {
expr, err = p.parseTTLExpr(pos)
if err != nil {
return nil, err
}
items = append(items, expr)
}
return items, nil
}
func (p *Parser) tryParseTTLPolicy(pos Pos) (*TTLPolicy, error) {
var rule *TTLPolicyRule
switch {
case p.tryConsumeKeywords(KeywordTo):
if p.tryConsumeKeywords(KeywordDisk) {
value, err := p.parseString(p.Pos())
if err != nil {
return nil, err
}
rule = &TTLPolicyRule{RulePos: pos, ToDisk: value}
} else if p.tryConsumeKeywords(KeywordVolume) {
value, err := p.parseString(p.Pos())
if err != nil {
return nil, err
}
rule = &TTLPolicyRule{RulePos: pos, ToVolume: value}
} else {
return nil, fmt.Errorf("unexpected token: %q, expected DISK or VOLUME", p.lastTokenKind())
}
case p.matchKeyword(KeywordDelete), p.matchKeyword(KeywordRecompress):
token := p.last()
_ = p.lexer.consumeToken()
action := &TTLPolicyRuleAction{
ActionPos: token.Pos,
ActionEnd: token.End,
Action: token.ToString(),
}
codec, err := p.tryParseCompressionCodecs(p.Pos())
if err != nil {
return nil, err
}
action.Codec = codec
rule = &TTLPolicyRule{RulePos: pos, Action: action}
default:
return nil, nil // nolint
}
policy := &TTLPolicy{Item: rule}
where, err := p.tryParseWhereClause(p.Pos())
if err != nil {
return nil, err
}
policy.Where = where
groupBy, err := p.tryParseGroupByClause(p.Pos())
if err != nil {
return nil, err
}
policy.GroupBy = groupBy
return policy, nil
}
func (p *Parser) parseTTLExpr(pos Pos) (*TTLExpr, error) {
columnExpr, err := p.parseExpr(pos)
if err != nil {
return nil, err
}
policy, err := p.tryParseTTLPolicy(p.Pos())
if err != nil {
return nil, err
}
return &TTLExpr{
TTLPos: pos,
Expr: columnExpr,
Policy: policy,
}, nil
}
func (p *Parser) tryParseSampleByClause(pos Pos) (*SampleByClause, error) {
if !p.tryConsumeKeywords(KeywordSample) {
return nil, nil // nolint
}
if err := p.expectKeyword(KeywordBy); err != nil {
return nil, err
}
// parse sample by expr
columnExpr, err := p.parseExpr(p.Pos())
if err != nil {
return nil, err
}
return &SampleByClause{
SamplePos: pos,
Expr: columnExpr,
}, nil
}
func (p *Parser) tryParseSettingsClause(pos Pos) (*SettingsClause, error) {
if !p.tryConsumeKeywords(KeywordSettings) {
return nil, nil // nolint
}
return p.parseSettingsClause(pos)
}
func (p *Parser) parseSettingsClause(pos Pos) (*SettingsClause, error) {
settings := &SettingsClause{SettingsPos: pos, ListEnd: pos}
items, err := p.parseSettingsList(p.Pos())
if err != nil {
return nil, err
}
if len(items) == 0 {
return nil, fmt.Errorf("settings list is empty")
}
settings.ListEnd = items[len(items)-1].End()
settings.Items = items
return settings, nil
}
func (p *Parser) parseSettingsList(pos Pos) ([]*SettingExpr, error) {
items := make([]*SettingExpr, 0)
expr, err := p.parseSettingsExpr(pos)
if err != nil {
return nil, err
}
items = append(items, expr)
for p.tryConsumeTokenKind(TokenKindComma) != nil {
expr, err = p.parseSettingsExpr(p.Pos())
if err != nil {
return nil, err
}
items = append(items, expr)
}
return items, nil
}
func (p *Parser) parseSettingsExpr(pos Pos) (*SettingExpr, error) {
ident, err := p.parseIdent()
if err != nil {
return nil, err
}
if err := p.expectTokenKind(TokenKindSingleEQ); err != nil {
return nil, err
}
var expr Expr
switch {
case p.matchTokenKind(TokenKindInt):
number, err := p.parseNumber(p.Pos())
if err != nil {
return nil, err
}
expr = number
case p.matchTokenKind(TokenKindString):
str, err := p.parseString(p.Pos())
expr = str
if err != nil {
return nil, err
}
case p.matchTokenKind(TokenKindLBrace):
m, err := p.parseMapLiteral(p.Pos())
if err != nil {
return nil, err
}
expr = m
default:
return nil, fmt.Errorf("unexpected token: %q, expected <number> or <string>", p.last().String)
}
return &SettingExpr{
SettingsPos: pos,
Name: ident,
Expr: expr,
}, nil
}
func (p *Parser) parseDestinationClause(pos Pos) (*DestinationClause, error) {
if err := p.expectKeyword(KeywordTo); err != nil {
return nil, err
}
tableIdentifier, err := p.parseTableIdentifier(p.Pos())
if err != nil {
return nil, err
}
return &DestinationClause{
ToPos: pos,
TableIdentifier: tableIdentifier,
}, nil
}
func (p *Parser) tryParseEngineExpr(pos Pos) (*EngineExpr, error) {
if !p.matchKeyword(KeywordEngine) {
return nil, nil // nolint
}
return p.parseEngineExpr(pos)
}
func (p *Parser) parseEngineExpr(pos Pos) (*EngineExpr, error) {
if err := p.expectKeyword(KeywordEngine); err != nil {
return nil, err
}
_ = p.tryConsumeTokenKind(TokenKindSingleEQ)
engineExpr := &EngineExpr{EnginePos: pos}
var engineEnd Pos
switch {
case p.matchTokenKind(TokenKindIdent):
ident, err := p.parseIdent()
if err != nil {
return nil, err
}
engineExpr.Name = ident.Name
engineEnd = ident.End()
if p.matchTokenKind(TokenKindLParen) {
params, err := p.parseFunctionParams(p.Pos())
if err != nil {
return nil, err
}
engineExpr.Params = params
engineExpr.EngineEnd = params.End()
}
default:
return nil, fmt.Errorf("unexpected token: %s", p.lastTokenKind())
}
for !p.lexer.isEOF() {
switch {
case p.matchKeyword(KeywordOrder):
orderBy, err := p.tryParseOrderByClause(p.Pos())
if err != nil {
return nil, err
}
engineExpr.OrderBy = orderBy
engineEnd = orderBy.End()
case p.matchKeyword(KeywordPartition):
partitionBy, err := p.tryParsePartitionByClause(p.Pos())
if err != nil {
return nil, err
}
engineExpr.PartitionBy = partitionBy
engineEnd = partitionBy.End()
case p.matchKeyword(KeywordPrimary):
primaryKey, err := p.tryParsePrimaryKeyClause(p.Pos())
if err != nil {
return nil, err
}
engineExpr.PrimaryKey = primaryKey
engineEnd = primaryKey.End()
case p.matchKeyword(KeywordSample):
sampleBy, err := p.tryParseSampleByClause(p.Pos())
if err != nil {
return nil, err
}
engineExpr.SampleBy = sampleBy
engineEnd = sampleBy.End()
case p.matchKeyword(KeywordTtl):
ttl, err := p.tryParseTTLClause(p.Pos(), true)
if err != nil {
return nil, err
}
engineExpr.TTL = ttl
engineEnd = ttl.End()
case p.matchKeyword(KeywordSettings):
settingsClause, err := p.tryParseSettingsClause(p.Pos())
if err != nil {
return nil, err
}
engineExpr.Settings = settingsClause
engineEnd = settingsClause.End()
default:
engineExpr.EngineEnd = engineEnd
return engineExpr, nil
}
}
engineExpr.EngineEnd = engineEnd
return engineExpr, nil
}
func (p *Parser) parseStmt(pos Pos) (Expr, error) {
var err error
var expr Expr
switch {
case p.matchKeyword(KeywordCreate),
p.matchKeyword(KeywordAttach),
p.matchKeyword(KeywordAlter),
p.matchKeyword(KeywordDrop),
p.matchKeyword(KeywordDetach),
p.matchKeyword(KeywordTruncate),
p.matchKeyword(KeywordRename):
expr, err = p.parseDDL(pos)
case p.matchKeyword(KeywordSelect), p.matchKeyword(KeywordWith):
expr, err = p.parseSelectQuery(pos)
case p.matchKeyword(KeywordDelete):
expr, err = p.parseDeleteClause(pos)
case p.matchKeyword(KeywordInsert):
expr, err = p.parseInsertStmt(p.Pos())
case p.matchKeyword(KeywordUse):
expr, err = p.parseUseStmt(pos)
case p.matchKeyword(KeywordSet):
expr, err = p.parseSetStmt(pos)
case p.matchKeyword(KeywordSystem):
expr, err = p.parseSystemStmt(pos)
case p.matchKeyword(KeywordOptimize):
expr, err = p.parseOptimizeStmt(pos)
case p.matchKeyword(KeywordCheck):
expr, err = p.parseCheckStmt(pos)
case p.matchKeyword(KeywordExplain):
expr, err = p.parseExplainStmt(pos)
case p.matchKeyword(KeywordGrant):
expr, err = p.parseGrantPrivilegeStmt(pos)
case p.matchKeyword(KeywordShow):
expr, err = p.parseShowStmt(pos)
case p.matchKeyword(KeywordDesc), p.matchKeyword(KeywordDescribe):
expr, err = p.parseDescribeStmt(pos)
default:
return nil, fmt.Errorf("unexpected token: %q", p.last().String)
}
if err != nil {
return nil, err
}
_, err = p.tryParseFormat(p.Pos())
if err != nil {
return nil, err
}
// Statement can be terminated by ';' or EOF
if p.last() != nil && !p.matchTokenKind(";") {
return nil, fmt.Errorf("<EOF> or ';' was expected, but got: %q", p.last().String)
}
return expr, nil
}
func (p *Parser) ParseStmts() ([]Expr, error) {
var stmts []Expr
for {
_ = p.lexer.consumeToken()
if p.lexer.isEOF() {
break
}
if p.matchTokenKind(";") {
continue
}
stmt, err := p.parseStmt(p.Pos())
if err != nil {
return nil, p.wrapError(err)
}
stmts = append(stmts, stmt)
}
return stmts, nil
}
func (p *Parser) parseUseStmt(pos Pos) (*UseStmt, error) {
if err := p.expectKeyword(KeywordUse); err != nil {
return nil, err
}
database, err := p.parseIdent()
if err != nil {
return nil, err
}
return &UseStmt{
UsePos: pos,
Database: database,
StatementEnd: database.End(),
}, nil
}
func (p *Parser) parseShowStmt(pos Pos) (*ShowStmt, error) {
if err := p.expectKeyword(KeywordShow); err != nil {
return nil, err
}
var showType string
var target *TableIdentifier
// Parse the type of SHOW statement
switch {
case p.matchKeyword(KeywordCreate):
// SHOW CREATE TABLE table_name
showType = "CREATE"
_ = p.lexer.consumeToken()
if err := p.expectKeyword(KeywordTable); err != nil {
return nil, err
}
showType += " TABLE"
tableIdent, err := p.parseTableIdentifier(p.Pos())
if err != nil {
return nil, err
}
target = tableIdent
case p.matchKeyword(KeywordDatabases):
// SHOW DATABASES [optional clauses]
showType = "DATABASES"
_ = p.lexer.consumeToken()
case p.matchKeyword(KeywordTables):
// SHOW TABLES
showType = "TABLES"
_ = p.lexer.consumeToken()
default:
return nil, fmt.Errorf("expected CREATE, DATABASES, or TABLES after SHOW, got %q", p.last().String)
}
stmt := &ShowStmt{
ShowPos: pos,
ShowType: showType,
Target: target,
}
// Parse optional clauses for SHOW DATABASES
if showType == "DATABASES" {
// Parse [[NOT] LIKE | ILIKE '<pattern>']
if p.matchKeyword(KeywordNot) {
stmt.NotLike = true
_ = p.lexer.consumeToken()
}
if p.matchKeyword(KeywordLike) || p.matchKeyword(KeywordIlike) {
if p.matchKeyword(KeywordLike) {
stmt.LikeType = "LIKE"
} else {
stmt.LikeType = "ILIKE"
}
_ = p.lexer.consumeToken()
// Parse pattern expression
pattern, err := p.parseExpr(p.Pos())
if err != nil {
return nil, err
}
stmt.LikePattern = pattern
}
// Parse [LIMIT <N>]
if p.matchKeyword(KeywordLimit) {
_ = p.lexer.consumeToken()
limit, err := p.parseExpr(p.Pos())
if err != nil {
return nil, err
}
stmt.Limit = limit
}
// Parse [INTO OUTFILE filename]
if p.matchKeyword(KeywordInto) {
_ = p.lexer.consumeToken()
if err := p.expectKeyword(KeywordOutfile); err != nil {
return nil, err
}
// Parse filename as a string literal
outFile, err := p.parseString(p.Pos())
if err != nil {
return nil, err
}
stmt.OutFile = outFile
}
// Parse [FORMAT format]
if p.matchKeyword(KeywordFormat) {
_ = p.lexer.consumeToken()
// Format can be an identifier or a string
if p.matchTokenKind(TokenKindString) {
format, err := p.parseString(p.Pos())
if err != nil {
return nil, err
}
stmt.Format = format
} else if p.matchTokenKind(TokenKindIdent) {
// Handle format as identifier (like JSON, CSV, etc.)
token := p.last()
_ = p.lexer.consumeToken()
stmt.Format = &StringLiteral{
LiteralPos: token.Pos,
LiteralEnd: token.End,
Literal: token.String,
}
} else {
return nil, fmt.Errorf("expected format specification after FORMAT, got %q", p.last().String)
}
}
}
// Set statement end position
stmt.StatementEnd = p.End()
return stmt, nil
}
func (p *Parser) parseDescribeStmt(pos Pos) (*DescribeStmt, error) {
// DESC and DESCRIBE are both supported
if !p.matchKeyword(KeywordDesc) && !p.matchKeyword(KeywordDescribe) {
return nil, fmt.Errorf("expected DESC or DESCRIBE")
}
_ = p.lexer.consumeToken()
// TABLE keyword is optional after DESC/DESCRIBE
var describeType string
if p.matchKeyword(KeywordTable) {
_ = p.lexer.consumeToken()
describeType = "TABLE"
}
tableIdent, err := p.parseTableIdentifier(p.Pos())
if err != nil {
return nil, err
}
return &DescribeStmt{
DescribePos: pos,
StatementEnd: tableIdent.End(),
DescribeType: describeType,
Target: tableIdent,
}, nil
}
// syntax: TRUNCATE TEMPORARY? TABLE (IF EXISTS)? tableIdentifier clusterClause?;
func (p *Parser) parseTruncateTable(pos Pos) (*TruncateTable, error) {
if err := p.expectKeyword(KeywordTruncate); err != nil {
return nil, err
}
isTemporary := p.tryConsumeKeywords(KeywordTemporary)
if err := p.expectKeyword(KeywordTable); err != nil {
return nil, err
}
ifExists, err := p.tryParseIfExists()
if err != nil {
return nil, err
}
tableName, err := p.parseTableIdentifier(p.Pos())
if err != nil {
return nil, err
}
onCluster, err := p.tryParseClusterClause(p.Pos())
if err != nil {
return nil, err
}
truncateTable := &TruncateTable{
TruncatePos: pos,
IsTemporary: isTemporary,
IfExists: ifExists,
Name: tableName,
OnCluster: onCluster,
StatementEnd: tableName.End(),
}
if onCluster != nil {
truncateTable.StatementEnd = onCluster.End()
}
return truncateTable, nil
}
func (p *Parser) parseDeleteClause(pos Pos) (*DeleteClause, error) {
if err := p.expectKeyword(KeywordDelete); err != nil {
return nil, err
}
if err := p.expectKeyword(KeywordFrom); err != nil {
return nil, err
}
tableIdentifier, err := p.parseTableIdentifier(p.Pos())
if err != nil {
return nil, err
}
onCluster, err := p.tryParseClusterClause(p.Pos())
if err != nil {
return nil, err
}
if err := p.expectKeyword(KeywordWhere); err != nil {
return nil, err
}
whereExpr, err := p.parseExpr(p.Pos())
if err != nil {
return nil, err
}
return &DeleteClause{
DeletePos: pos,
Table: tableIdentifier,
OnCluster: onCluster,
WhereExpr: whereExpr,
}, nil
}
func (p *Parser) parseColumnNamesExpr(pos Pos) (*ColumnNamesExpr, error) {
if err := p.expectTokenKind(TokenKindLParen); err != nil {
return nil, err
}
var columnNames []NestedIdentifier
for !p.lexer.isEOF() && p.tryConsumeTokenKind(TokenKindRParen) == nil {
name, err := p.ParseNestedIdentifier(p.Pos())
if err != nil {
return nil, err
}
columnNames = append(columnNames, *name)
if p.tryConsumeTokenKind(TokenKindComma) == nil {
break
}
}
rightParenPos := p.Pos()
if err := p.expectTokenKind(TokenKindRParen); err != nil {
return nil, err
}
return &ColumnNamesExpr{
LeftParenPos: pos,
RightParenPos: rightParenPos,
ColumnNames: columnNames,
}, nil
}
func (p *Parser) parseTypedPlaceholder(pos Pos) (Expr, error) {
if err := p.expectTokenKind(TokenKindLBrace); err != nil {
return nil, err
}
name, err := p.parseIdent()
if err != nil {
return nil, err
}
if err := p.expectTokenKind(TokenKindColon); err != nil {
return nil, err
}
columnType, err := p.parseColumnType(p.Pos())
if err != nil {
return nil, err
}
if err := p.expectTokenKind(TokenKindRBrace); err != nil {
return nil, err
}
return &TypedPlaceholder{
LeftBracePos: pos,
RightBracePos: p.Pos(),
Name: name,
Type: columnType,
}, nil
}
func (p *Parser) parseAssignmentValues(pos Pos) (*AssignmentValues, error) {
if err := p.expectTokenKind(TokenKindLParen); err != nil {
return nil, err
}
var value Expr
var err error
values := make([]Expr, 0)
for !p.lexer.isEOF() && p.tryConsumeTokenKind(TokenKindRParen) == nil {
switch {
case p.matchTokenKind(TokenKindLParen):
value, err = p.parseAssignmentValues(p.Pos())
case p.matchTokenKind(TokenKindLBrace):
// placeholder with type, e.g. {a :Int32}, {b :DateTime(6)}
value, err = p.parseTypedPlaceholder(p.Pos())
default:
value, err = p.parseExpr(p.Pos())
}
if err != nil {
return nil, err
}
values = append(values, value)
if p.tryConsumeTokenKind(TokenKindComma) == nil {
break
}
}
rightParenPos := p.Pos()
if err := p.expectTokenKind(TokenKindRParen); err != nil {
return nil, err
}
return &AssignmentValues{
LeftParenPos: pos,
RightParenPos: rightParenPos,
Values: values,
}, nil
}
func (p *Parser) parseInsertStmt(pos Pos) (*InsertStmt, error) {
if err := p.expectKeyword(KeywordInsert); err != nil {
return nil, err
}
if err := p.expectKeyword(KeywordInto); err != nil {
return nil, err
}
insertExpr := &InsertStmt{InsertPos: pos}
insertExpr.HasTableKeyword = p.tryConsumeKeywords(KeywordTable)
var table Expr
var err error
if p.tryConsumeKeywords(KeywordFunction) {
table, err = p.parseFunctionExpr(p.Pos())
} else {
table, err = p.parseTableIdentifier(p.Pos())
}
if err != nil {
return nil, err
}
insertExpr.Table = table
if p.matchTokenKind(TokenKindLParen) {
// parse column names
insertExpr.ColumnNames, err = p.parseColumnNamesExpr(p.Pos())
if err != nil {
return nil, err
}
}
switch {
case p.matchKeyword(KeywordFormat):
insertExpr.Format, err = p.parseFormat(p.Pos())
case p.matchKeyword(KeywordValues):
// consume VALUES keyword
_ = p.lexer.consumeToken()
values := make([]*AssignmentValues, 0)
for !p.lexer.isEOF() {
value, err := p.parseAssignmentValues(p.Pos())
if err != nil {
return nil, err
}
values = append(values, value)
if p.tryConsumeTokenKind(TokenKindComma) == nil {
break
}
}
insertExpr.Values = values
case p.matchKeyword(KeywordSelect):
insertExpr.SelectExpr, err = p.parseSelectQuery(p.Pos())
default:
// do nothing
}
if err != nil {
return nil, err
}
return insertExpr, nil
}
func (p *Parser) parseRenameStmt(pos Pos) (*RenameStmt, error) {
if err := p.expectKeyword(KeywordRename); err != nil {
return nil, err
}
renameTarget := KeywordTable
switch {
case p.tryConsumeKeywords(KeywordDictionary):
renameTarget = KeywordDictionary
case p.tryConsumeKeywords(KeywordDatabase):
renameTarget = KeywordDatabase
default:
if err := p.expectKeyword(KeywordTable); err != nil {
return nil, err
}
}
targetPair, err := p.parseTargetPair(p.Pos())
if err != nil {
return nil, err
}
tablePairList := []*TargetPair{targetPair}
for p.tryConsumeTokenKind(TokenKindComma) != nil {
tablePair, err := p.parseTargetPair(p.Pos())
if err != nil {
return nil, err
}
tablePairList = append(tablePairList, tablePair)
}
renameStmt := &RenameStmt{
RenamePos: pos,
StatementEnd: tablePairList[len(tablePairList)-1].End(),
RenameTarget: renameTarget,
TargetPairList: tablePairList,
}
onCluster, err := p.tryParseClusterClause(p.Pos())
if err != nil {
return nil, err
}
if onCluster != nil {
renameStmt.OnCluster = onCluster
renameStmt.StatementEnd = onCluster.End()
}
return renameStmt, nil
}
func (p *Parser) parseTargetPair(_ Pos) (*TargetPair, error) {
oldTable, err := p.parseTableIdentifier(p.Pos())
if err != nil {
return nil, err
}
if err = p.expectKeyword(KeywordTo); err != nil {
return nil, err
}
newTable, err := p.parseTableIdentifier(p.Pos())
if err != nil {
return nil, err
}
return &TargetPair{
Old: oldTable,
New: newTable,
}, nil
}
func (p *Parser) parseCreateFunction(pos Pos, orReplace bool) (*CreateFunction, error) {
if err := p.expectKeyword(KeywordFunction); err != nil {
return nil, err
}
ifNotExists, err := p.tryParseIfNotExists()
if err != nil {
return nil, err
}
functionName, err := p.parseIdent()
if err != nil {
return nil, err
}
onCluster, err := p.tryParseClusterClause(p.Pos())
if err != nil {
return nil, err
}
if err := p.expectKeyword(KeywordAs); err != nil {
return nil, err
}
params, err := p.parseFunctionParams(p.Pos())
if err != nil {
return nil, err
}
if err := p.expectTokenKind(TokenKindArrow); err != nil {
return nil, err
}
expr, err := p.parseExpr(p.Pos())
if err != nil {
return nil, err
}
return &CreateFunction{
CreatePos: pos,
OrReplace: orReplace,
IfNotExists: ifNotExists,
FunctionName: functionName,
OnCluster: onCluster,
Params: params,
Expr: expr,
}, nil
}
// Dictionary parsing functions
func (p *Parser) parseDictionarySchemaClause(pos Pos) (*DictionarySchemaClause, error) {
if err := p.expectTokenKind(TokenKindLParen); err != nil {
return nil, err
}
schema := &DictionarySchemaClause{
SchemaPos: pos,
}
// Parse first attribute
attr, err := p.parseDictionaryAttribute(p.Pos())
if err != nil {
return nil, err
}
schema.Attributes = append(schema.Attributes, attr)
// Parse additional attributes
for p.tryConsumeTokenKind(TokenKindComma) != nil {
attr, err := p.parseDictionaryAttribute(p.Pos())
if err != nil {
return nil, err
}
schema.Attributes = append(schema.Attributes, attr)
}
rParenPos := p.Pos()
if err := p.expectTokenKind(TokenKindRParen); err != nil {
return nil, err
}
schema.RParenPos = rParenPos
return schema, nil
}
func (p *Parser) parseDictionaryAttribute(pos Pos) (*DictionaryAttribute, error) {
name, err := p.parseIdent()
if err != nil {
return nil, err
}
columnType, err := p.parseColumnType(p.Pos())
if err != nil {
return nil, err
}
attr := &DictionaryAttribute{
NamePos: pos,
Name: name,
Type: columnType,
}
// Parse optional attribute properties
for {
switch {
case p.tryConsumeKeywords(KeywordDefault):
if attr.Default != nil {
return nil, fmt.Errorf("duplicate DEFAULT clause")
}
literal, err := p.parseLiteral(p.Pos())
if err != nil {
return nil, err
}
attr.Default = literal
case p.tryConsumeKeywords(KeywordExpression):
if attr.Expression != nil {
return nil, fmt.Errorf("duplicate EXPRESSION clause")
}
expr, err := p.parseExpr(p.Pos())
if err != nil {
return nil, err
}
attr.Expression = expr
case p.tryConsumeKeywords(KeywordHierarchical):
if attr.Hierarchical {
return nil, fmt.Errorf("duplicate HIERARCHICAL clause")
}
attr.Hierarchical = true
case p.tryConsumeKeywords(KeywordInjective):
if attr.Injective {
return nil, fmt.Errorf("duplicate INJECTIVE clause")
}
attr.Injective = true
case p.tryConsumeKeywords(KeywordIs_object_id):
if attr.IsObjectId {
return nil, fmt.Errorf("duplicate IS_OBJECT_ID clause")
}
attr.IsObjectId = true
default:
// No more attribute properties
return attr, nil
}
}
}
func (p *Parser) parseDictionaryEngineClause(pos Pos) (*DictionaryEngineClause, error) {
engine := &DictionaryEngineClause{
EnginePos: pos,
}
// Parse PRIMARY KEY clause (optional)
if p.matchKeyword(KeywordPrimary) {
primaryKey, err := p.parseDictionaryPrimaryKeyClause(p.Pos())
if err != nil {
return nil, err
}
engine.PrimaryKey = primaryKey
}
// Parse engine clauses (SOURCE, LIFETIME, LAYOUT, RANGE, SETTINGS)
for {
switch {
case p.matchKeyword(KeywordSource):
if engine.Source != nil {
return nil, fmt.Errorf("duplicate SOURCE clause")
}
source, err := p.parseDictionarySourceClause(p.Pos())
if err != nil {
return nil, err
}
engine.Source = source
case p.matchKeyword(KeywordLifetime):
if engine.Lifetime != nil {
return nil, fmt.Errorf("duplicate LIFETIME clause")
}
lifetime, err := p.parseDictionaryLifetimeClause(p.Pos())
if err != nil {
return nil, err
}
engine.Lifetime = lifetime
case p.matchKeyword(KeywordLayout):
if engine.Layout != nil {
return nil, fmt.Errorf("duplicate LAYOUT clause")
}
layout, err := p.parseDictionaryLayoutClause(p.Pos())
if err != nil {
return nil, err
}
engine.Layout = layout
case p.matchKeyword(KeywordRange):
if engine.Range != nil {
return nil, fmt.Errorf("duplicate RANGE clause")
}
rangeClause, err := p.parseDictionaryRangeClause(p.Pos())
if err != nil {
return nil, err
}
engine.Range = rangeClause
case p.matchKeyword(KeywordSettings):
if engine.Settings != nil {
return nil, fmt.Errorf("duplicate SETTINGS clause")
}
settings, err := p.parseDictionarySettingsClause(p.Pos())
if err != nil {
return nil, err
}
engine.Settings = settings
default:
// No more engine clauses
if engine.Source == nil {
return nil, fmt.Errorf("SOURCE clause is required for dictionary")
}
return engine, nil
}
}
}
func (p *Parser) parseDictionaryPrimaryKeyClause(pos Pos) (*DictionaryPrimaryKeyClause, error) {
if err := p.expectKeyword(KeywordPrimary); err != nil {
return nil, err
}
if err := p.expectKeyword(KeywordKey); err != nil {
return nil, err
}
keys, err := p.parseColumnExprList(p.Pos())
if err != nil {
return nil, err
}
return &DictionaryPrimaryKeyClause{
PrimaryKeyPos: pos,
Keys: keys,
RParenPos: keys.End() - 1,
}, nil
}
func (p *Parser) parseDictionarySourceClause(pos Pos) (*DictionarySourceClause, error) {
if err := p.expectKeyword(KeywordSource); err != nil {
return nil, err
}
if err := p.expectTokenKind(TokenKindLParen); err != nil {
return nil, err
}
sourceName, err := p.parseIdent()
if err != nil {
return nil, err
}
if err := p.expectTokenKind(TokenKindLParen); err != nil {
return nil, err
}
var args []*DictionaryArgExpr
// Parse optional arguments
for !p.matchTokenKind(TokenKindRParen) {
arg, err := p.parseDictionaryArgExpr(p.Pos())
if err != nil {
return nil, err
}
args = append(args, arg)
// If there's no right paren, we expect another arg (no comma needed)
if p.matchTokenKind(TokenKindRParen) {
break
}
}
if err := p.expectTokenKind(TokenKindRParen); err != nil {
return nil, err
}
rParenPos := p.Pos()
if err := p.expectTokenKind(TokenKindRParen); err != nil {
return nil, err
}
return &DictionarySourceClause{
SourcePos: pos,
Source: sourceName,
Args: args,
RParenPos: rParenPos,
}, nil
}
func (p *Parser) parseDictionaryArgExpr(pos Pos) (*DictionaryArgExpr, error) {
name, err := p.parseIdent()
if err != nil {
return nil, err
}
var value Expr
// Parse the value part
switch {
case p.matchTokenKind(TokenKindString):
literal, err := p.parseLiteral(p.Pos())
if err != nil {
return nil, err
}
value = literal
case p.matchTokenKind(TokenKindInt), p.matchTokenKind(TokenKindFloat):
literal, err := p.parseLiteral(p.Pos())
if err != nil {
return nil, err
}
value = literal
case p.matchTokenKind(TokenKindIdent):
ident, err := p.parseIdent()
if err != nil {
return nil, err
}
// Check if it's followed by optional parentheses
if p.matchTokenKind(TokenKindLParen) {
_ = p.lexer.consumeToken() // consume (
if err := p.expectTokenKind(TokenKindRParen); err != nil {
return nil, err
}
value = &FunctionExpr{
Name: ident,
}
} else {
value = ident
}
default:
return nil, fmt.Errorf("expected identifier, string or number in dictionary argument, got %s", p.lastTokenKind())
}
return &DictionaryArgExpr{
ArgPos: pos,
Name: name,
Value: value,
}, nil
}
func (p *Parser) parseDictionaryLifetimeClause(pos Pos) (*DictionaryLifetimeClause, error) {
if err := p.expectKeyword(KeywordLifetime); err != nil {
return nil, err
}
if err := p.expectTokenKind(TokenKindLParen); err != nil {
return nil, err
}
lifetime := &DictionaryLifetimeClause{
LifetimePos: pos,
}
// Check for MIN/MAX form
if p.matchKeyword(KeywordMin) || p.matchKeyword(KeywordMax) {
isMinFirst := p.matchKeyword(KeywordMin)
_ = p.lexer.consumeToken() // consume MIN or MAX
first, err := p.parseNumber(p.Pos())
if err != nil {
return nil, err
}
// Expect the other keyword
if isMinFirst {
if err := p.expectKeyword(KeywordMax); err != nil {
return nil, err
}
} else {
if err := p.expectKeyword(KeywordMin); err != nil {
return nil, err
}
}
second, err := p.parseNumber(p.Pos())
if err != nil {
return nil, err
}
if isMinFirst {
lifetime.Min = first
lifetime.Max = second
} else {
lifetime.Min = second
lifetime.Max = first
}
} else {
// Simple form: LIFETIME(value)
value, err := p.parseNumber(p.Pos())
if err != nil {
return nil, err
}
lifetime.Value = value
}
rParenPos := p.Pos()
if err := p.expectTokenKind(TokenKindRParen); err != nil {
return nil, err
}
lifetime.RParenPos = rParenPos
return lifetime, nil
}
func (p *Parser) parseDictionaryLayoutClause(pos Pos) (*DictionaryLayoutClause, error) {
if err := p.expectKeyword(KeywordLayout); err != nil {
return nil, err
}
if err := p.expectTokenKind(TokenKindLParen); err != nil {
return nil, err
}
layoutName, err := p.parseIdent()
if err != nil {
return nil, err
}
if err := p.expectTokenKind(TokenKindLParen); err != nil {
return nil, err
}
var args []*DictionaryArgExpr
// Parse optional arguments
for !p.matchTokenKind(TokenKindRParen) {
arg, err := p.parseDictionaryArgExpr(p.Pos())
if err != nil {
return nil, err
}
args = append(args, arg)
// If there's no right paren, we expect another arg (no comma needed)
if p.matchTokenKind(TokenKindRParen) {
break
}
}
if err := p.expectTokenKind(TokenKindRParen); err != nil {
return nil, err
}
rParenPos := p.Pos()
if err := p.expectTokenKind(TokenKindRParen); err != nil {
return nil, err
}
return &DictionaryLayoutClause{
LayoutPos: pos,
Layout: layoutName,
Args: args,
RParenPos: rParenPos,
}, nil
}
func (p *Parser) parseDictionaryRangeClause(pos Pos) (*DictionaryRangeClause, error) {
if err := p.expectKeyword(KeywordRange); err != nil {
return nil, err
}
if err := p.expectTokenKind(TokenKindLParen); err != nil {
return nil, err
}
rangeClause := &DictionaryRangeClause{
RangePos: pos,
}
// Parse MIN identifier MAX identifier or MAX identifier MIN identifier
if p.matchKeyword(KeywordMin) {
if err := p.expectKeyword(KeywordMin); err != nil {
return nil, err
}
min, err := p.parseIdent()
if err != nil {
return nil, err
}
if err := p.expectKeyword(KeywordMax); err != nil {
return nil, err
}
max, err := p.parseIdent()
if err != nil {
return nil, err
}
rangeClause.Min = min
rangeClause.Max = max
} else if p.matchKeyword(KeywordMax) {
if err := p.expectKeyword(KeywordMax); err != nil {
return nil, err
}
max, err := p.parseIdent()
if err != nil {
return nil, err
}
if err := p.expectKeyword(KeywordMin); err != nil {
return nil, err
}
min, err := p.parseIdent()
if err != nil {
return nil, err
}
rangeClause.Min = min
rangeClause.Max = max
} else {
return nil, fmt.Errorf("expected MIN or MAX in RANGE clause")
}
rParenPos := p.Pos()
if err := p.expectTokenKind(TokenKindRParen); err != nil {
return nil, err
}
rangeClause.RParenPos = rParenPos
return rangeClause, nil
}
func (p *Parser) parseDictionarySettingsClause(pos Pos) (*SettingsClause, error) {
if err := p.expectKeyword(KeywordSettings); err != nil {
return nil, err
}
if err := p.expectTokenKind(TokenKindLParen); err != nil {
return nil, err
}
settings := &SettingsClause{SettingsPos: pos, ListEnd: pos}
items := make([]*SettingExpr, 0)
// Parse first setting
expr, err := p.parseSettingsExpr(p.Pos())
if err != nil {
return nil, err
}
items = append(items, expr)
// Parse additional settings
for p.tryConsumeTokenKind(TokenKindComma) != nil {
expr, err := p.parseSettingsExpr(p.Pos())
if err != nil {
return nil, err
}
items = append(items, expr)
}
if len(items) > 0 {
settings.ListEnd = items[len(items)-1].End()
}
settings.Items = items
if err := p.expectTokenKind(TokenKindRParen); err != nil {
return nil, err
}
return settings, nil
}