Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions parser/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -5803,6 +5803,108 @@ func (c *CreateDictionary) Accept(visitor ASTVisitor) error {
return visitor.VisitCreateDictionary(c)
}

type CreateNamedCollection struct {
CreatePos Pos
StatementEnd Pos
Name *Ident
IfNotExists bool
OnCluster *ClusterClause
Params []*NamedCollectionParam
}

func (c *CreateNamedCollection) Pos() Pos {
return c.CreatePos
}

func (c *CreateNamedCollection) End() Pos {
return c.StatementEnd
}

func (c *CreateNamedCollection) Type() string {
return "NAMED COLLECTION"
}

func (c *CreateNamedCollection) String() string {
var builder strings.Builder
builder.WriteString("CREATE NAMED COLLECTION ")
if c.IfNotExists {
builder.WriteString("IF NOT EXISTS ")
}
builder.WriteString(c.Name.String())
if c.OnCluster != nil {
builder.WriteString(" ")
builder.WriteString(c.OnCluster.String())
}
builder.WriteString(" AS ")
for i, param := range c.Params {
if i > 0 {
builder.WriteString(", ")
}
builder.WriteString(param.String())
}
return builder.String()
}

func (c *CreateNamedCollection) Accept(visitor ASTVisitor) error {
visitor.Enter(c)
defer visitor.Leave(c)
if err := c.Name.Accept(visitor); err != nil {
return err
}
if c.OnCluster != nil {
if err := c.OnCluster.Accept(visitor); err != nil {
return err
}
}
for _, param := range c.Params {
if err := param.Accept(visitor); err != nil {
return err
}
}
return visitor.VisitCreateNamedCollection(c)
}

type NamedCollectionParam struct {
ParamPos Pos
Name *Ident
Value Expr
Overridable bool
NotOverridable bool
}

func (n *NamedCollectionParam) Pos() Pos {
return n.ParamPos
}

func (n *NamedCollectionParam) End() Pos {
return n.Value.End()
}

func (n *NamedCollectionParam) String() string {
var builder strings.Builder
builder.WriteString(n.Name.String())
builder.WriteString(" = ")
builder.WriteString(n.Value.String())
if n.NotOverridable {
builder.WriteString(" NOT OVERRIDABLE")
} else if n.Overridable {
builder.WriteString(" OVERRIDABLE")
}
return builder.String()
}

func (n *NamedCollectionParam) Accept(visitor ASTVisitor) error {
visitor.Enter(n)
defer visitor.Leave(n)
if err := n.Name.Accept(visitor); err != nil {
return err
}
if err := n.Value.Accept(visitor); err != nil {
return err
}
return visitor.VisitNamedCollectionParam(n)
}

type DictionarySchemaClause struct {
SchemaPos Pos
Attributes []*DictionaryAttribute
Expand Down
16 changes: 16 additions & 0 deletions parser/ast_visitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ type ASTVisitor interface {
VisitTopExpr(expr *TopClause) error
VisitCreateLiveView(expr *CreateLiveView) error
VisitCreateDictionary(expr *CreateDictionary) error
VisitCreateNamedCollection(expr *CreateNamedCollection) error
VisitNamedCollectionParam(expr *NamedCollectionParam) error
VisitDictionarySchemaClause(expr *DictionarySchemaClause) error
VisitDictionaryAttribute(expr *DictionaryAttribute) error
VisitDictionaryEngineClause(expr *DictionaryEngineClause) error
Expand Down Expand Up @@ -1031,6 +1033,20 @@ func (v *DefaultASTVisitor) VisitCreateDictionary(expr *CreateDictionary) error
return nil
}

func (v *DefaultASTVisitor) VisitCreateNamedCollection(expr *CreateNamedCollection) error {
if v.Visit != nil {
return v.Visit(expr)
}
return nil
}

func (v *DefaultASTVisitor) VisitNamedCollectionParam(expr *NamedCollectionParam) error {
if v.Visit != nil {
return v.Visit(expr)
}
return nil
}

func (v *DefaultASTVisitor) VisitDictionarySchemaClause(expr *DictionarySchemaClause) error {
if v.Visit != nil {
return v.Visit(expr)
Expand Down
6 changes: 6 additions & 0 deletions parser/keyword.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const (
KeywordCluster = "CLUSTER"
KeywordCodec = "CODEC"
KeywordCollate = "COLLATE"
KeywordCollection = "COLLECTION"
KeywordColumn = "COLUMN"
KeywordColumns = "COLUMNS"
KeywordComment = "COMMENT"
Expand Down Expand Up @@ -145,6 +146,7 @@ const (
KeywordMoves = "MOVES"
KeywordMutation = "MUTATION"
KeywordName = "NAME"
KeywordNamed = "NAMED"
KeywordNan_sql = "NAN_SQL"
KeywordNo = "NO"
KeywordNone = "NONE"
Expand All @@ -157,6 +159,7 @@ const (
KeywordOption = "OPTION"
KeywordOr = "OR"
KeywordOrder = "ORDER"
KeywordOverridable = "OVERRIDABLE"
KeywordOuter = "OUTER"
KeywordOutfile = "OUTFILE"
KeywordOver = "OVER"
Expand Down Expand Up @@ -285,6 +288,7 @@ var keywords = NewSet(
KeywordCluster,
KeywordCodec,
KeywordCollate,
KeywordCollection,
KeywordColumn,
KeywordColumns,
KeywordComment,
Expand Down Expand Up @@ -399,6 +403,7 @@ var keywords = NewSet(
KeywordMoves,
KeywordMutation,
KeywordName,
KeywordNamed,
KeywordNan_sql,
KeywordNo,
KeywordNone,
Expand All @@ -412,6 +417,7 @@ var keywords = NewSet(
KeywordOr,
KeywordOrder,
KeywordOuter,
KeywordOverridable,
KeywordOutfile,
KeywordOver,
KeywordPartition,
Expand Down
124 changes: 123 additions & 1 deletion parser/parser_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ func (p *Parser) parseDDL(pos Pos) (DDL, error) {
return nil, fmt.Errorf("expected keyword: TEMPORARY|TABLE|VIEW|FUNCTION|DICTIONARY, but got %q", p.last().String)
}
switch {
case p.matchKeyword(KeywordNamed):
return p.parseCreateNamedCollection(pos)
case p.matchKeyword(KeywordDatabase):
return p.parseCreateDatabase(pos)
case p.matchKeyword(KeywordDictionary):
Expand All @@ -34,7 +36,7 @@ func (p *Parser) parseDDL(pos Pos) (DDL, error) {
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",
return nil, fmt.Errorf("expected keyword: NAMED|DATABASE|DICTIONARY|TABLE|VIEW|ROLE|USER|FUNCTION|MATERIALIZED, but got %q",
p.lastTokenKind())
}
case p.matchKeyword(KeywordAlter):
Expand Down Expand Up @@ -183,6 +185,126 @@ func (p *Parser) parseCreateDictionary(pos Pos, orReplace bool) (*CreateDictiona
return createDict, nil
}

func (p *Parser) parseCreateNamedCollection(pos Pos) (*CreateNamedCollection, error) {
if err := p.expectKeyword(KeywordNamed); err != nil {
return nil, err
}

if err := p.expectKeyword(KeywordCollection); err != nil {
return nil, err
}

createCollection := &CreateNamedCollection{
CreatePos: pos,
}

// parse IF NOT EXISTS clause if exists
var err error
createCollection.IfNotExists, err = p.tryParseIfNotExists()
if err != nil {
return nil, err
}

// parse collection name
name, err := p.parseIdent()
if err != nil {
return nil, err
}
createCollection.Name = name

// parse ON CLUSTER clause if exists
onCluster, err := p.tryParseClusterClause(p.Pos())
if err != nil {
return nil, err
}
createCollection.OnCluster = onCluster

// parse AS keyword
if err := p.expectKeyword(KeywordAs); err != nil {
return nil, err
}

// parse parameters
params := make([]*NamedCollectionParam, 0)
for !p.lexer.isEOF() {
param, err := p.parseNamedCollectionParam(p.Pos())
if err != nil {
return nil, err
}
params = append(params, param)

// Check if there's another parameter
if p.tryConsumeTokenKind(TokenKindComma) == nil {
break
}
}
createCollection.Params = params

if len(params) > 0 {
createCollection.StatementEnd = params[len(params)-1].End()
} else if onCluster != nil {
createCollection.StatementEnd = onCluster.End()
} else {
createCollection.StatementEnd = name.End()
}

return createCollection, nil
}

func (p *Parser) parseNamedCollectionParam(pos Pos) (*NamedCollectionParam, error) {
name, err := p.parseIdent()
if err != nil {
return nil, err
}

if err := p.expectTokenKind(TokenKindSingleEQ); err != nil {
return nil, err
}

// Parse the value - can be string, number, or identifier
var value Expr
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
}
value = ident
default:
return nil, fmt.Errorf("expected string, number or identifier in named collection parameter, got %s", p.lastTokenKind())
}

param := &NamedCollectionParam{
ParamPos: pos,
Name: name,
Value: value,
}

// Parse optional [NOT] OVERRIDABLE clause
if p.tryConsumeKeywords(KeywordNot) {
param.NotOverridable = true
if err := p.expectKeyword(KeywordOverridable); err != nil {
return nil, err
}
} else if p.tryConsumeKeywords(KeywordOverridable) {
param.Overridable = true
}

return param, nil
}

func (p *Parser) parseCreateTable(pos Pos, orReplace bool) (*CreateTable, error) {
createTable := &CreateTable{CreatePos: pos, OrReplace: orReplace}
createTable.HasTemporary = p.tryConsumeKeywords(KeywordTemporary)
Expand Down
4 changes: 4 additions & 0 deletions parser/testdata/ddl/create_named_collection_basic.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
CREATE NAMED COLLECTION IF NOT EXISTS servercore_s3_config
AS url = 'http://local-minio:9000/*',
access_key_id = 'minioadmin',
secret_access_key = 'minioadmin';
3 changes: 3 additions & 0 deletions parser/testdata/ddl/create_named_collection_simple.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CREATE NAMED COLLECTION my_collection
AS key1 = 'value1',
key2 = 'value2';
4 changes: 4 additions & 0 deletions parser/testdata/ddl/create_named_collection_with_cluster.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
CREATE NAMED COLLECTION IF NOT EXISTS my_collection ON CLUSTER my_cluster
AS key1 = 'value1' OVERRIDABLE,
key2 = 'value2' NOT OVERRIDABLE,
key3 = 'value3';
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
CREATE NAMED COLLECTION test_collection
AS url = 'http://example.com' OVERRIDABLE,
access_key = 'key123' NOT OVERRIDABLE,
secret_key = 'secret456';
9 changes: 9 additions & 0 deletions parser/testdata/ddl/format/create_named_collection_basic.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-- Origin SQL:
CREATE NAMED COLLECTION IF NOT EXISTS servercore_s3_config
AS url = 'http://local-minio:9000/*',
access_key_id = 'minioadmin',
secret_access_key = 'minioadmin';


-- Format SQL:
CREATE NAMED COLLECTION IF NOT EXISTS servercore_s3_config AS url = 'http://local-minio:9000/*', access_key_id = 'minioadmin', secret_access_key = 'minioadmin';
8 changes: 8 additions & 0 deletions parser/testdata/ddl/format/create_named_collection_simple.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-- Origin SQL:
CREATE NAMED COLLECTION my_collection
AS key1 = 'value1',
key2 = 'value2';


-- Format SQL:
CREATE NAMED COLLECTION my_collection AS key1 = 'value1', key2 = 'value2';
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-- Origin SQL:
CREATE NAMED COLLECTION IF NOT EXISTS my_collection ON CLUSTER my_cluster
AS key1 = 'value1' OVERRIDABLE,
key2 = 'value2' NOT OVERRIDABLE,
key3 = 'value3';


-- Format SQL:
CREATE NAMED COLLECTION IF NOT EXISTS my_collection ON CLUSTER my_cluster AS key1 = 'value1' OVERRIDABLE, key2 = 'value2' NOT OVERRIDABLE, key3 = 'value3';
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-- Origin SQL:
CREATE NAMED COLLECTION test_collection
AS url = 'http://example.com' OVERRIDABLE,
access_key = 'key123' NOT OVERRIDABLE,
secret_key = 'secret456';


-- Format SQL:
CREATE NAMED COLLECTION test_collection AS url = 'http://example.com' OVERRIDABLE, access_key = 'key123' NOT OVERRIDABLE, secret_key = 'secret456';
Loading