mirror of
https://github.com/hanzo-ds/sqlparser.git
synced 2026-10-30 20:53:50 +00:00
936 lines
22 KiB
Go
936 lines
22 KiB
Go
package parser
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
func (p *Parser) parseAlterTable(pos Pos) (*AlterTable, error) {
|
|
alterTable := &AlterTable{
|
|
AlterPos: pos,
|
|
AlterExprs: make([]AlterTableClause, 0),
|
|
}
|
|
if err := p.expectKeyword(KeywordTable); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
tableIdentifier, err := p.parseTableIdentifier(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
alterTable.TableIdentifier = tableIdentifier
|
|
onCluster, err := p.tryParseClusterClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
alterTable.OnCluster = onCluster
|
|
|
|
for !p.lexer.isEOF() {
|
|
var alter AlterTableClause
|
|
switch {
|
|
case p.matchKeyword(KeywordAdd):
|
|
alter, err = p.parseAlterTableAdd(p.Pos())
|
|
case p.matchKeyword(KeywordDrop):
|
|
alter, err = p.parseAlterTableDrop(p.Pos())
|
|
case p.matchKeyword(KeywordAttach):
|
|
alter, err = p.parseAlterTableAttachPartition(p.Pos())
|
|
case p.matchKeyword(KeywordDetach):
|
|
_ = p.lexer.consumeToken()
|
|
alter, err = p.parseAlterTableDetachPartition(p.Pos())
|
|
case p.matchKeyword(KeywordFreeze):
|
|
alter, err = p.parseAlterTableFreezePartition(p.Pos())
|
|
case p.matchKeyword(KeywordRemove):
|
|
alter, err = p.parseAlterTableRemoveTTL(p.Pos())
|
|
case p.matchKeyword(KeywordRename):
|
|
alter, err = p.parseAlterTableRenameColumn(p.Pos())
|
|
case p.matchKeyword(KeywordClear):
|
|
alter, err = p.parseAlterTableClear(p.Pos())
|
|
case p.matchKeyword(KeywordModify):
|
|
alter, err = p.parseAlterTableModify(p.Pos())
|
|
case p.matchKeyword(KeywordReplace):
|
|
alter, err = p.parseAlterTableReplacePartition(p.Pos())
|
|
case p.matchKeyword(KeywordMaterialize):
|
|
alter, err = p.parseAlterTableMaterialize(p.Pos())
|
|
case p.matchKeyword(KeywordReset):
|
|
alter, err = p.parseAlterTableReset(p.Pos())
|
|
case p.matchKeyword(KeywordDelete):
|
|
alter, err = p.parseAlterTableDelete(p.Pos())
|
|
case p.matchKeyword(KeywordUpdate):
|
|
alter, err = p.parseAlterTableUpdate(p.Pos())
|
|
default:
|
|
return nil, errors.New("expected token: ADD|DROP|ATTACH|DETACH|FREEZE|REMOVE|CLEAR|MODIFY|REPLACE|MATERIALIZE|RESET|DELETE|UPDATE")
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
alterTable.AlterExprs = append(alterTable.AlterExprs, alter)
|
|
if p.tryConsumeTokenKind(TokenKindComma) == nil {
|
|
break
|
|
}
|
|
}
|
|
if len(alterTable.AlterExprs) == 0 {
|
|
return nil, errors.New("expected token: ADD|DROP")
|
|
}
|
|
alterTable.StatementEnd = alterTable.AlterExprs[len(alterTable.AlterExprs)-1].End()
|
|
|
|
return alterTable, nil
|
|
}
|
|
|
|
func (p *Parser) parseAlterTableAdd(pos Pos) (AlterTableClause, error) {
|
|
if err := p.expectKeyword(KeywordAdd); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
switch {
|
|
case p.matchKeyword(KeywordColumn):
|
|
return p.parseAlterTableAddColumn(pos)
|
|
case p.matchKeyword(KeywordIndex):
|
|
return p.parseAlterTableAddIndex(pos)
|
|
case p.matchKeyword(KeywordProjection):
|
|
return p.parseAlterTableAddProjection(pos)
|
|
default:
|
|
return nil, errors.New("expected token: COLUMN|INDEX|PROJECTION")
|
|
}
|
|
}
|
|
|
|
func (p *Parser) parseAlterTableAddColumn(pos Pos) (*AlterTableAddColumn, error) {
|
|
if err := p.expectKeyword(KeywordColumn); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ifNotExists, err := p.tryParseIfNotExists()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
column, err := p.parseTableColumnExpr(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
statementEnd := column.End()
|
|
|
|
after, err := p.tryParseAfterClause()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if after != nil {
|
|
statementEnd = after.End()
|
|
}
|
|
|
|
return &AlterTableAddColumn{
|
|
AddPos: pos,
|
|
StatementEnd: statementEnd,
|
|
Column: column,
|
|
IfNotExists: ifNotExists,
|
|
After: after,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) parseAlterTableAddIndex(pos Pos) (*AlterTableAddIndex, error) {
|
|
indexPos := p.Pos()
|
|
if err := p.expectKeyword(KeywordIndex); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ifNotExists, err := p.tryParseIfNotExists()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
index, err := p.parseTableIndex(indexPos)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
statementEnd := index.End()
|
|
after, err := p.tryParseAfterClause()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if after != nil {
|
|
statementEnd = after.End()
|
|
}
|
|
return &AlterTableAddIndex{
|
|
AddPos: pos,
|
|
StatementEnd: statementEnd,
|
|
IfNotExists: ifNotExists,
|
|
Index: index,
|
|
After: after,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) parseProjectionOrderBy(pos Pos) (*ProjectionOrderByClause, error) {
|
|
if err := p.expectKeyword(KeywordOrder); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := p.expectKeyword(KeywordBy); err != nil {
|
|
return nil, err
|
|
}
|
|
columns, err := p.parseColumnExprList(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &ProjectionOrderByClause{
|
|
OrderByPos: pos,
|
|
Columns: columns,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) parseProjectionSelect(pos Pos) (*ProjectionSelectStmt, error) {
|
|
if err := p.expectTokenKind(TokenKindLParen); err != nil {
|
|
return nil, err
|
|
}
|
|
with, err := p.tryParseWithClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := p.expectKeyword(KeywordSelect); err != nil {
|
|
return nil, err
|
|
}
|
|
columns, err := p.parseColumnExprList(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
groupBy, err := p.tryParseGroupByClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
orderBy, err := p.parseProjectionOrderBy(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
lastToken := p.last()
|
|
if err := p.expectTokenKind(TokenKindRParen); err != nil {
|
|
return nil, err
|
|
}
|
|
return &ProjectionSelectStmt{
|
|
LeftParenPos: pos,
|
|
RightParenPos: lastToken.Pos,
|
|
With: with,
|
|
SelectColumns: columns,
|
|
GroupBy: groupBy,
|
|
OrderBy: orderBy,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) parseTableProjection(pos Pos, includeProjectionKeyword bool) (*TableProjection, error) {
|
|
if includeProjectionKeyword {
|
|
if err := p.expectKeyword(KeywordProjection); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
identifier, err := p.ParseNestedIdentifier(pos)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
selectExpr, err := p.parseProjectionSelect(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &TableProjection{
|
|
IncludeProjectionKeyword: includeProjectionKeyword,
|
|
ProjectionPos: pos,
|
|
Identifier: identifier,
|
|
Select: selectExpr,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) parseAlterTableAddProjection(pos Pos) (*AlterTableAddProjection, error) {
|
|
if err := p.expectKeyword(KeywordProjection); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ifNotExists, err := p.tryParseIfNotExists()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tableProjection, err := p.parseTableProjection(p.Pos(), false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
statementEnd := tableProjection.End()
|
|
after, err := p.tryParseAfterClause()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if after != nil {
|
|
statementEnd = after.End()
|
|
}
|
|
return &AlterTableAddProjection{
|
|
AddPos: pos,
|
|
StatementEnd: statementEnd,
|
|
IfNotExists: ifNotExists,
|
|
TableProjection: tableProjection,
|
|
After: after,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) parseTableIndex(pos Pos) (*TableIndex, error) {
|
|
name, err := p.ParseNestedIdentifier(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
columnExpr, err := p.parseColumnsExpr(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := p.expectKeyword(KeywordType); err != nil {
|
|
return nil, err
|
|
}
|
|
columnType, err := p.parseColumnType(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := p.expectKeyword(KeywordGranularity); err != nil {
|
|
return nil, err
|
|
}
|
|
granularity, err := p.parseDecimal(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &TableIndex{
|
|
IndexPos: pos,
|
|
Name: name,
|
|
ColumnExpr: columnExpr,
|
|
ColumnType: columnType,
|
|
Granularity: granularity,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) parseAlterTableDrop(pos Pos) (AlterTableClause, error) {
|
|
if err := p.expectKeyword(KeywordDrop); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
switch {
|
|
case p.matchKeyword(KeywordColumn), p.matchKeyword(KeywordIndex), p.matchKeyword(KeywordProjection):
|
|
return p.parseAlterTableDropClause(pos)
|
|
case p.matchKeyword(KeywordDetached), p.matchKeyword(KeywordPartition):
|
|
return p.parseAlterTableDropPartition(pos)
|
|
default:
|
|
return nil, errors.New("expected keyword: COLUMN|INDEX|PROJECTION|DETACHED|PARTITION")
|
|
}
|
|
}
|
|
|
|
// Syntax: ALTER TABLE DETACH partitionClause
|
|
func (p *Parser) parseAlterTableDetachPartition(pos Pos) (AlterTableClause, error) {
|
|
partitionPos := p.Pos()
|
|
if err := p.expectKeyword(KeywordPartition); err != nil {
|
|
return nil, err
|
|
}
|
|
partition := &PartitionClause{
|
|
PartitionPos: partitionPos,
|
|
}
|
|
expr, err := p.parseExpr(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
partition.Expr = expr
|
|
|
|
settings, err := p.tryParseSettingsClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &AlterTableDetachPartition{
|
|
DetachPos: pos,
|
|
Partition: partition,
|
|
Settings: settings,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) tryParsePartitionClause(pos Pos) (*PartitionClause, error) {
|
|
if !p.matchKeyword(KeywordPartition) {
|
|
return nil, nil // nolint
|
|
}
|
|
return p.parsePartitionClause(pos)
|
|
}
|
|
|
|
func (p *Parser) parsePartitionClause(pos Pos) (*PartitionClause, error) {
|
|
if err := p.expectKeyword(KeywordPartition); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
partition := &PartitionClause{
|
|
PartitionPos: pos,
|
|
}
|
|
if p.tryConsumeKeywords(KeywordId) {
|
|
id, err := p.parseString(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
partition.ID = id
|
|
} else if p.tryConsumeKeywords(KeywordAll) {
|
|
partition.All = true
|
|
} else {
|
|
expr, err := p.parseExpr(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
partition.Expr = expr
|
|
}
|
|
return partition, nil
|
|
}
|
|
|
|
// Syntax: ALTER TABLE ATTACH partitionClause (FROM tableIdentifier)?
|
|
func (p *Parser) parseAlterTableAttachPartition(pos Pos) (AlterTableClause, error) {
|
|
alterTable := &AlterTableAttachPartition{AttachPos: pos}
|
|
|
|
if err := p.expectKeyword(KeywordAttach); err != nil {
|
|
return nil, err
|
|
}
|
|
partition, err := p.parsePartitionClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
alterTable.Partition = partition
|
|
// FROM [db.]table?
|
|
if p.tryConsumeKeywords(KeywordFrom) {
|
|
tableIdentifier, err := p.parseTableIdentifier(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
alterTable.From = tableIdentifier
|
|
}
|
|
return alterTable, nil
|
|
}
|
|
|
|
func (p *Parser) parseAlterTableDropClause(pos Pos) (AlterTableClause, error) {
|
|
var kind string
|
|
switch {
|
|
case p.matchKeyword(KeywordColumn):
|
|
kind = KeywordColumn
|
|
case p.matchKeyword(KeywordIndex):
|
|
kind = KeywordIndex
|
|
case p.matchKeyword(KeywordProjection):
|
|
kind = KeywordProjection
|
|
default:
|
|
return nil, fmt.Errorf("expected token: COLUMN|INDEX|PROJECTION, but got %s", p.lastTokenKind())
|
|
}
|
|
_ = p.lexer.consumeToken()
|
|
|
|
ifExists, err := p.tryParseIfExists()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
name, err := p.ParseNestedIdentifier(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if kind == KeywordProjection {
|
|
return &AlterTableDropProjection{
|
|
DropPos: pos,
|
|
ProjectionName: name,
|
|
IfExists: ifExists,
|
|
}, nil
|
|
} else if kind == KeywordColumn {
|
|
return &AlterTableDropColumn{
|
|
DropPos: pos,
|
|
ColumnName: name,
|
|
IfExists: ifExists,
|
|
}, nil
|
|
} else {
|
|
return &AlterTableDropIndex{
|
|
DropPos: pos,
|
|
IndexName: name,
|
|
IfExists: ifExists,
|
|
}, nil
|
|
}
|
|
}
|
|
|
|
func (p *Parser) tryParseAfterClause() (*NestedIdentifier, error) {
|
|
if !p.tryConsumeKeywords(KeywordAfter) {
|
|
return nil, nil // nolint
|
|
}
|
|
|
|
return p.ParseNestedIdentifier(p.Pos())
|
|
}
|
|
|
|
// Syntax: ALTER TABLE DROP partitionClause
|
|
func (p *Parser) parseAlterTableDropPartition(pos Pos) (AlterTableClause, error) {
|
|
var hasDetached bool
|
|
if p.matchKeyword(KeywordDetached) {
|
|
_ = p.lexer.consumeToken()
|
|
hasDetached = true
|
|
}
|
|
partitionPos := p.Pos()
|
|
if err := p.expectKeyword(KeywordPartition); err != nil {
|
|
return nil, err
|
|
}
|
|
partition := &PartitionClause{
|
|
PartitionPos: partitionPos,
|
|
}
|
|
expr, err := p.parseExpr(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
partition.Expr = expr
|
|
|
|
settings, err := p.tryParseSettingsClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &AlterTableDropPartition{
|
|
DropPos: pos,
|
|
Partition: partition,
|
|
HasDetached: hasDetached,
|
|
Settings: settings,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) parseAlterTableFreezePartition(pos Pos) (AlterTableClause, error) {
|
|
if err := p.expectKeyword(KeywordFreeze); err != nil {
|
|
return nil, err
|
|
}
|
|
alterTable := &AlterTableFreezePartition{
|
|
FreezePos: pos,
|
|
StatementEnd: p.Pos(),
|
|
}
|
|
if p.matchKeyword(KeywordPartition) {
|
|
partition, err := p.parsePartitionClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
alterTable.Partition = partition
|
|
alterTable.StatementEnd = partition.End()
|
|
}
|
|
|
|
return alterTable, nil
|
|
}
|
|
|
|
func (p *Parser) parseAlterTableRemoveTTL(pos Pos) (AlterTableClause, error) {
|
|
if err := p.expectKeyword(KeywordRemove); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := p.expectKeyword(KeywordTtl); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &AlterTableRemoveTTL{
|
|
RemovePos: pos,
|
|
StatementEnd: p.Pos(),
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) parseAlterTableClear(pos Pos) (AlterTableClause, error) {
|
|
if err := p.expectKeyword(KeywordClear); err != nil {
|
|
return nil, err
|
|
}
|
|
return p.parseAlterTableClearClause(pos)
|
|
}
|
|
|
|
// Syntax: ALTER TABLE CLEAR COLUMN|INDEX|PROJECTION (IF EXISTS)? nestedIdentifier (IN partitionClause)?
|
|
func (p *Parser) parseAlterTableClearClause(pos Pos) (AlterTableClause, error) {
|
|
var kind string
|
|
switch {
|
|
case p.matchKeyword(KeywordColumn):
|
|
kind = KeywordColumn
|
|
case p.matchKeyword(KeywordIndex):
|
|
kind = KeywordIndex
|
|
case p.matchKeyword(KeywordProjection):
|
|
kind = KeywordProjection
|
|
default:
|
|
return nil, fmt.Errorf("expected keyword: COLUMN|INDEX|PROJECTION, but got %q", p.lastTokenKind())
|
|
}
|
|
_ = p.lexer.consumeToken()
|
|
|
|
ifExists, err := p.tryParseIfExists()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
name, err := p.ParseNestedIdentifier(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
statementEnd := name.End()
|
|
|
|
var partition *PartitionClause
|
|
if p.tryConsumeKeywords(KeywordIn) {
|
|
partition, err = p.tryParsePartitionClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if partition != nil {
|
|
statementEnd = partition.End()
|
|
}
|
|
}
|
|
|
|
if kind == KeywordProjection {
|
|
return &AlterTableClearProjection{
|
|
ClearPos: pos,
|
|
StatementEnd: statementEnd,
|
|
IfExists: ifExists,
|
|
ProjectionName: name,
|
|
PartitionExpr: partition,
|
|
}, nil
|
|
} else if kind == KeywordColumn {
|
|
return &AlterTableClearColumn{
|
|
ClearPos: pos,
|
|
StatementEnd: statementEnd,
|
|
IfExists: ifExists,
|
|
ColumnName: name,
|
|
PartitionExpr: partition,
|
|
}, nil
|
|
} else {
|
|
return &AlterTableClearIndex{
|
|
ClearPos: pos,
|
|
StatementEnd: statementEnd,
|
|
IfExists: ifExists,
|
|
IndexName: name,
|
|
PartitionExpr: partition,
|
|
}, nil
|
|
}
|
|
}
|
|
|
|
// Syntax: ALTER TABLE RENAME COLUMN (IF EXISTS)? nestedIdentifier TO nestedIdentifier
|
|
func (p *Parser) parseAlterTableRenameColumn(pos Pos) (AlterTableClause, error) {
|
|
if err := p.expectKeyword(KeywordRename); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := p.expectKeyword(KeywordColumn); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ifExists, err := p.tryParseIfExists()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
oldColumnName, err := p.ParseNestedIdentifier(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err = p.expectKeyword(KeywordTo); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
newColumnName, err := p.ParseNestedIdentifier(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &AlterTableRenameColumn{
|
|
RenamePos: pos,
|
|
IfExists: ifExists,
|
|
OldColumnName: oldColumnName,
|
|
NewColumnName: newColumnName,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) parseAlterTableModify(pos Pos) (AlterTableClause, error) {
|
|
if err := p.expectKeyword(KeywordModify); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
switch {
|
|
case p.matchKeyword(KeywordColumn):
|
|
return p.parseAlterTableModifyColumn(pos)
|
|
case p.matchKeyword(KeywordTtl):
|
|
_ = p.lexer.consumeToken()
|
|
ttlExpr, err := p.parseTTLExpr(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &AlterTableModifyTTL{
|
|
ModifyPos: pos,
|
|
StatementEnd: ttlExpr.End(),
|
|
TTL: ttlExpr,
|
|
}, nil
|
|
case p.matchKeyword(KeywordQuery):
|
|
_ = p.lexer.consumeToken()
|
|
selectQuery, _ := p.parseSelectQuery(pos)
|
|
return &AlterTableModifyQuery{
|
|
ModifyPos: pos,
|
|
StatementEnd: selectQuery.End(),
|
|
SelectExpr: selectQuery,
|
|
}, nil
|
|
case p.matchKeyword(KeywordSetting):
|
|
_ = p.lexer.consumeToken() // consume "SETTING"
|
|
settings, err := p.parseSettingsList(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// settings must not be empty
|
|
statementEnd := settings[len(settings)-1].End()
|
|
return &AlterTableModifySetting{
|
|
ModifyPos: pos,
|
|
StatementEnd: statementEnd,
|
|
Settings: settings,
|
|
}, nil
|
|
default:
|
|
return nil, fmt.Errorf("expected keyword: COLUMN|TTL|QUERY|SETTING, but got %q",
|
|
p.last().String)
|
|
}
|
|
|
|
}
|
|
|
|
// syntax: MODIFY COLUMN (IF EXISTS)? tableColumnDfnt
|
|
func (p *Parser) parseAlterTableModifyColumn(pos Pos) (AlterTableClause, error) {
|
|
if err := p.expectKeyword(KeywordColumn); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ifExists, err := p.tryParseIfExists()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// at least parse out column name
|
|
column, err := p.parseTableColumnExpr(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
alterTableModifyColumn := &AlterTableModifyColumn{
|
|
ModifyPos: pos,
|
|
StatementEnd: column.End(),
|
|
IfExists: ifExists,
|
|
Column: column,
|
|
}
|
|
|
|
// syntax: MODIFY COLUMN (IF EXISTS)? nestedIdentifier REMOVE tableColumnPropertyType
|
|
removePropertyType, err := p.tryParseRemovePropertyTypeExpr(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
alterTableModifyColumn.RemovePropertyType = removePropertyType
|
|
|
|
return alterTableModifyColumn, nil
|
|
}
|
|
|
|
func (p *Parser) tryParseRemovePropertyTypeExpr(pos Pos) (*RemovePropertyType, error) {
|
|
if !p.matchKeyword(KeywordRemove) {
|
|
return nil, nil
|
|
}
|
|
|
|
if err := p.expectKeyword(KeywordRemove); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
columnPropertyType, err := p.parseColumnPropertyType(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &RemovePropertyType{
|
|
RemovePos: pos,
|
|
PropertyType: columnPropertyType,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) parseAlterTableReplacePartition(pos Pos) (AlterTableClause, error) {
|
|
if err := p.expectKeyword(KeywordReplace); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
partition, err := p.parsePartitionClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err = p.expectKeyword(KeywordFrom); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
table, err := p.parseTableIdentifier(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &AlterTableReplacePartition{
|
|
ReplacePos: pos,
|
|
Partition: partition,
|
|
Table: table,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) parseAlterTableMaterialize(pos Pos) (AlterTableClause, error) {
|
|
if err := p.expectKeyword(KeywordMaterialize); err != nil {
|
|
return nil, err
|
|
}
|
|
var kind string
|
|
switch {
|
|
case p.matchKeyword(KeywordIndex):
|
|
kind = KeywordIndex
|
|
case p.matchKeyword(KeywordProjection):
|
|
kind = KeywordProjection
|
|
default:
|
|
return nil, fmt.Errorf("expected keyword: INDEX|PROJECTION, but got %q", p.lastTokenKind())
|
|
}
|
|
_ = p.lexer.consumeToken()
|
|
|
|
ifExists, err := p.tryParseIfExists()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
name, err := p.ParseNestedIdentifier(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
statementEnd := name.End()
|
|
var partition *PartitionClause
|
|
if p.tryConsumeKeywords(KeywordIn) {
|
|
partition, err = p.tryParsePartitionClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
statementEnd = partition.End()
|
|
}
|
|
if kind == KeywordIndex {
|
|
return &AlterTableMaterializeIndex{
|
|
MaterializedPos: pos,
|
|
StatementEnd: statementEnd,
|
|
IfExists: ifExists,
|
|
IndexName: name,
|
|
Partition: partition,
|
|
}, nil
|
|
}
|
|
return &AlterTableMaterializeProjection{
|
|
MaterializedPos: pos,
|
|
StatementEnd: statementEnd,
|
|
IfExists: ifExists,
|
|
ProjectionName: name,
|
|
Partition: partition,
|
|
}, nil
|
|
}
|
|
|
|
func (p *Parser) parseAlterTableReset(pos Pos) (AlterTableClause, error) {
|
|
if err := p.expectKeyword(KeywordReset); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := p.expectKeyword(KeywordSetting); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Parse comma-separated setting names inline
|
|
var settings []*Ident
|
|
setting, err := p.parseIdent()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
settings = append(settings, setting)
|
|
|
|
for p.tryConsumeTokenKind(TokenKindComma) != nil {
|
|
setting, err = p.parseIdent()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
settings = append(settings, setting)
|
|
}
|
|
|
|
statementEnd := settings[len(settings)-1].End()
|
|
|
|
return &AlterTableResetSetting{
|
|
ResetPos: pos,
|
|
StatementEnd: statementEnd,
|
|
Settings: settings,
|
|
}, nil
|
|
}
|
|
|
|
// Syntax: ALTER TABLE DELETE WHERE condition
|
|
func (p *Parser) parseAlterTableDelete(pos Pos) (AlterTableClause, error) {
|
|
if err := p.expectKeyword(KeywordDelete); 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 &AlterTableDelete{
|
|
DeletePos: pos,
|
|
StatementEnd: whereExpr.End(),
|
|
WhereClause: whereExpr,
|
|
}, nil
|
|
}
|
|
|
|
// Syntax: ALTER TABLE UPDATE column1 = expr1 [, column2 = expr2, ...] [IN PARTITION partition_id] WHERE condition
|
|
func (p *Parser) parseAlterTableUpdate(pos Pos) (AlterTableClause, error) {
|
|
if err := p.expectKeyword(KeywordUpdate); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Parse at least one assignment
|
|
assignments := make([]*UpdateAssignment, 0)
|
|
assignment, err := p.parseUpdateAssignment(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
assignments = append(assignments, assignment)
|
|
|
|
// Parse additional comma-separated assignments
|
|
for p.tryConsumeTokenKind(TokenKindComma) != nil {
|
|
assignment, err = p.parseUpdateAssignment(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
assignments = append(assignments, assignment)
|
|
}
|
|
|
|
var inPartition *PartitionClause
|
|
if p.tryConsumeKeywords(KeywordIn) {
|
|
inPartition, err = p.parsePartitionClause(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 &AlterTableUpdate{
|
|
UpdatePos: pos,
|
|
StatementEnd: whereExpr.End(),
|
|
Assignments: assignments,
|
|
InPartition: inPartition,
|
|
WhereClause: whereExpr,
|
|
}, nil
|
|
}
|
|
|
|
// Parse column = expression assignment
|
|
func (p *Parser) parseUpdateAssignment(pos Pos) (*UpdateAssignment, error) {
|
|
column, err := p.ParseNestedIdentifier(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := p.expectTokenKind(TokenKindSingleEQ); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Why don't we use parseExpr here? Because `ALTER TABLE UPDATE` syntax allows to
|
|
// use `IN PARTITION` keywords after assignments. So we need to limit the precedence
|
|
// to avoid parsing `IN PARTITION` as part of the expression.
|
|
expr, err := p.parseSubExpr(p.Pos(), precedenceIn)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &UpdateAssignment{
|
|
AssignmentPos: pos,
|
|
Column: column,
|
|
Expr: expr,
|
|
}, nil
|
|
}
|