mirror of
https://github.com/hanzo-ds/sqlparser.git
synced 2026-10-30 20:53:50 +00:00
336 lines
8.8 KiB
Go
336 lines
8.8 KiB
Go
package parser
|
|
|
|
import "fmt"
|
|
|
|
// parseCreateMaterializedView parses a CREATE MATERIALIZED VIEW statement.
|
|
//
|
|
// The syntax is as follows:
|
|
// CREATE MATERIALIZED VIEW [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster]
|
|
// REFRESH EVERY|AFTER interval [OFFSET interval]
|
|
// [RANDOMIZE FOR interval]
|
|
// [DEPENDS ON [db.]name [, [db.]name [, ...]]]
|
|
// [SETTINGS name = value [, name = value [, ...]]]
|
|
// [APPEND]
|
|
// [TO[db.]name] [(columns)] [ENGINE = engine]
|
|
// [EMPTY]
|
|
// [DEFINER = { user | CURRENT_USER }] [SQL SECURITY { DEFINER | NONE }]
|
|
// AS SELECT ...
|
|
// [COMMENT 'comment']
|
|
func (p *Parser) parseCreateMaterializedView(pos Pos) (*CreateMaterializedView, error) {
|
|
if err := p.expectKeyword(KeywordMaterialized); err != nil {
|
|
return nil, err
|
|
}
|
|
if err := p.expectKeyword(KeywordView); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
createMaterializedView := &CreateMaterializedView{CreatePos: pos}
|
|
|
|
// parse IF NOT EXISTS clause if exists
|
|
var err error
|
|
createMaterializedView.IfNotExists, err = p.tryParseIfNotExists()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
tableIdentifier, err := p.parseTableIdentifier(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
createMaterializedView.Name = tableIdentifier
|
|
|
|
onCluster, err := p.tryParseClusterClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
createMaterializedView.OnCluster = onCluster
|
|
|
|
refreshExpr, err := p.tryParseRefreshExpr(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
createMaterializedView.Refresh = refreshExpr
|
|
|
|
if p.tryConsumeKeywords(KeywordRandomize, KeywordFor) {
|
|
randomizeFor, err := p.parseInterval(false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
createMaterializedView.RandomizeFor = randomizeFor
|
|
}
|
|
if p.tryConsumeKeywords(KeywordDepends, KeywordOn) {
|
|
dependsOnTables := make([]*TableIdentifier, 0)
|
|
table, err := p.parseTableIdentifier(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dependsOnTables = append(dependsOnTables, table)
|
|
for p.matchTokenKind(TokenKindComma) {
|
|
table, err := p.parseTableIdentifier(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dependsOnTables = append(dependsOnTables, table)
|
|
}
|
|
createMaterializedView.DependsOn = dependsOnTables
|
|
}
|
|
settings, err := p.tryParseSettingsClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
createMaterializedView.Settings = settings
|
|
createMaterializedView.HasAppend = p.tryConsumeKeywords(KeywordAppend)
|
|
|
|
switch {
|
|
case p.matchKeyword(KeywordTo):
|
|
destination, err := p.parseDestinationClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
createMaterializedView.Destination = destination
|
|
createMaterializedView.StatementEnd = destination.End()
|
|
if p.matchTokenKind(TokenKindLParen) {
|
|
tableSchema, err := p.parseTableSchemaClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
createMaterializedView.Destination.TableSchema = tableSchema
|
|
}
|
|
case p.matchKeyword(KeywordEngine):
|
|
engineExpr, err := p.parseEngineExpr(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
createMaterializedView.Engine = engineExpr
|
|
createMaterializedView.StatementEnd = engineExpr.End()
|
|
default:
|
|
return nil, fmt.Errorf("unexpected token: %q, expected TO or ENGINE", p.lastTokenKind())
|
|
}
|
|
createMaterializedView.HasEmpty = p.tryConsumeKeywords(KeywordEmpty)
|
|
|
|
// Parse DEFINER clause
|
|
if p.tryConsumeKeywords(KeywordDefiner) {
|
|
if err := p.expectTokenKind(TokenKindSingleEQ); err != nil {
|
|
return nil, err
|
|
}
|
|
definer, err := p.parseIdent()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
createMaterializedView.Definer = definer
|
|
}
|
|
|
|
// Parse SQL SECURITY clause
|
|
if p.tryConsumeKeywords(KeywordSQL, KeywordSecurity) {
|
|
if !p.matchOneOfKeywords(KeywordDefiner, KeywordNone) {
|
|
return nil, fmt.Errorf("expected DEFINER or NONE after SQL SECURITY, got %q", p.lastTokenKind())
|
|
}
|
|
createMaterializedView.SQLSecurity = p.last().String
|
|
_ = p.lexer.consumeToken()
|
|
}
|
|
|
|
// Check for POPULATE before AS SELECT - only valid with ENGINE and no Destination
|
|
if p.tryConsumeKeywords(KeywordPopulate) {
|
|
if createMaterializedView.Destination != nil {
|
|
return nil, fmt.Errorf("POPULATE is only allowed when using ENGINE, not with TO clause")
|
|
}
|
|
if createMaterializedView.Engine == nil {
|
|
return nil, fmt.Errorf("POPULATE requires ENGINE to be specified")
|
|
}
|
|
createMaterializedView.Populate = true
|
|
createMaterializedView.StatementEnd = p.Pos()
|
|
}
|
|
|
|
if p.tryConsumeKeywords(KeywordAs) {
|
|
subQuery, err := p.parseSubQuery(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
createMaterializedView.SubQuery = subQuery
|
|
createMaterializedView.StatementEnd = subQuery.End()
|
|
}
|
|
|
|
comment, err := p.tryParseComment()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
createMaterializedView.Comment = comment
|
|
return createMaterializedView, nil
|
|
}
|
|
|
|
func (p *Parser) tryParseRefreshExpr(pos Pos) (*RefreshExpr, error) {
|
|
if !p.tryConsumeKeywords(KeywordRefresh) {
|
|
return nil, nil // nolint
|
|
}
|
|
|
|
// REFRESH EVERY|AFTER interval
|
|
refreshExpr := &RefreshExpr{RefreshPos: pos}
|
|
if !p.matchOneOfKeywords(KeywordEvery, KeywordAfter) {
|
|
return nil, fmt.Errorf("expected EVERY or AFTER, but got %q", p.lastTokenKind())
|
|
}
|
|
refreshExpr.Frequency = p.last().String
|
|
_ = p.lexer.consumeToken()
|
|
|
|
interval, err := p.parseInterval(false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
refreshExpr.Interval = interval
|
|
|
|
// [OFFSET interval]
|
|
if p.tryConsumeKeywords(KeywordOffset) {
|
|
offset, err := p.parseInterval(false)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
refreshExpr.Offset = offset
|
|
}
|
|
|
|
return refreshExpr, nil
|
|
}
|
|
|
|
// (ATTACH | CREATE) (OR REPLACE)? VIEW (IF NOT EXISTS)? tableIdentifier uuidClause? clusterClause? tableSchemaClause? subqueryClause
|
|
func (p *Parser) parseCreateView(pos Pos, orReplace bool) (*CreateView, error) {
|
|
createView := &CreateView{CreatePos: pos, OrReplace: orReplace}
|
|
if err := p.expectKeyword(KeywordView); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var err error
|
|
createView.IfNotExists, err = p.tryParseIfNotExists()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
tableIdentifier, err := p.parseTableIdentifier(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
createView.Name = tableIdentifier
|
|
|
|
uuid, err := p.tryParseUUID()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
createView.UUID = uuid
|
|
|
|
onCluster, err := p.tryParseClusterClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
createView.OnCluster = onCluster
|
|
|
|
if p.matchTokenKind(TokenKindLParen) {
|
|
tableSchema, err := p.parseTableSchemaClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
createView.TableSchema = tableSchema
|
|
}
|
|
|
|
if p.tryConsumeKeywords(KeywordAs) {
|
|
subQuery, err := p.parseSubQuery(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
createView.SubQuery = subQuery
|
|
createView.StatementEnd = subQuery.End()
|
|
}
|
|
|
|
return createView, nil
|
|
}
|
|
|
|
// # CreateLiveViewStmt
|
|
// (ATTACH | CREATE) LIVE VIEW (IF NOT EXISTS)? tableIdentifier uuidClause?
|
|
// clusterClause? (WITH TIMEOUT DECIMAL_LITERAL?)? destinationClause? tableSchemaClause? subqueryClause
|
|
func (p *Parser) parseCreateLiveView(pos Pos) (*CreateLiveView, error) {
|
|
if err := p.expectKeyword(KeywordLive); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := p.expectKeyword(KeywordView); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
createLiveView := &CreateLiveView{CreatePos: pos}
|
|
// parse IF NOT EXISTS clause if exists
|
|
var err error
|
|
createLiveView.IfNotExists, err = p.tryParseIfNotExists()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
tableIdentifier, err := p.parseTableIdentifier(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
createLiveView.Name = tableIdentifier
|
|
|
|
// try parse UUID clause if exists
|
|
uuid, err := p.tryParseUUID()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
createLiveView.UUID = uuid
|
|
// parse ON CLUSTER clause if exists
|
|
onCluster, err := p.tryParseClusterClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
createLiveView.OnCluster = onCluster
|
|
|
|
withTimeout, err := p.tryParseWithTimeout(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
createLiveView.WithTimeout = withTimeout
|
|
|
|
if p.matchKeyword(KeywordTo) {
|
|
destination, err := p.parseDestinationClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
createLiveView.Destination = destination
|
|
}
|
|
|
|
if p.matchTokenKind(TokenKindLParen) {
|
|
tableSchema, err := p.parseTableSchemaClause(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
createLiveView.TableSchema = tableSchema
|
|
}
|
|
|
|
if p.tryConsumeKeywords(KeywordAs) {
|
|
subQuery, err := p.parseSubQuery(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
createLiveView.SubQuery = subQuery
|
|
createLiveView.StatementEnd = subQuery.End()
|
|
}
|
|
|
|
return createLiveView, nil
|
|
}
|
|
|
|
func (p *Parser) tryParseWithTimeout(pos Pos) (*WithTimeoutClause, error) {
|
|
if !p.tryConsumeKeywords(KeywordWith) {
|
|
return nil, nil // nolint
|
|
}
|
|
if err := p.expectKeyword(KeywordTimeout); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
withTimeout := &WithTimeoutClause{WithTimeoutPos: pos}
|
|
|
|
if p.matchTokenKind(TokenKindInt) {
|
|
decimalNumber, err := p.parseDecimal(p.Pos())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
withTimeout.Number = decimalNumber
|
|
}
|
|
|
|
return withTimeout, nil
|
|
}
|