diff --git a/parser/ast.go b/parser/ast.go index 40492bb..e3c78d4 100644 --- a/parser/ast.go +++ b/parser/ast.go @@ -488,6 +488,7 @@ type AlterTableAddIndex struct { Index *TableIndex IfNotExists bool After *NestedIdentifier + Settings *SettingsClause } func (a *AlterTableAddIndex) Pos() Pos { @@ -495,6 +496,9 @@ func (a *AlterTableAddIndex) Pos() Pos { } func (a *AlterTableAddIndex) End() Pos { + if a.Settings != nil { + return a.Settings.End() + } return a.StatementEnd } @@ -513,6 +517,11 @@ func (a *AlterTableAddIndex) Accept(visitor ASTVisitor) error { return err } } + if a.Settings != nil { + if err := a.Settings.Accept(visitor); err != nil { + return err + } + } return visitor.VisitAlterTableAddIndex(a) } diff --git a/parser/format.go b/parser/format.go index 1e2067e..ed8bee2 100644 --- a/parser/format.go +++ b/parser/format.go @@ -249,15 +249,19 @@ func (a *AlterTableAddColumn) FormatSQL(formatter *Formatter) { } func (a *AlterTableAddIndex) FormatSQL(formatter *Formatter) { - formatter.WriteString("ADD ") + formatter.WriteString("ADD INDEX ") if a.IfNotExists { formatter.WriteString("IF NOT EXISTS ") } - formatter.WriteExpr(a.Index) + a.Index.formatDefinition(formatter) if a.After != nil { formatter.WriteString(" AFTER ") formatter.WriteExpr(a.After) } + if a.Settings != nil { + formatter.Break() + formatter.WriteExpr(a.Settings) + } } func (a *AlterTableAddProjection) FormatSQL(formatter *Formatter) { @@ -2656,6 +2660,12 @@ func (t *TableIdentifier) FormatSQL(formatter *Formatter) { func (a *TableIndex) FormatSQL(formatter *Formatter) { formatter.WriteString("INDEX") formatter.WriteByte(whitespace) + a.formatDefinition(formatter) +} + +// formatDefinition writes everything after the INDEX keyword, so ALTER TABLE +// can place IF NOT EXISTS between INDEX and the index name. +func (a *TableIndex) formatDefinition(formatter *Formatter) { formatter.WriteExpr(a.Name) // Add space only if column expression doesn't start with '(' columnExprStr := Format(a.ColumnExpr) diff --git a/parser/parser_alter.go b/parser/parser_alter.go index 9523f01..2637dff 100644 --- a/parser/parser_alter.go +++ b/parser/parser_alter.go @@ -160,12 +160,20 @@ func (p *Parser) parseAlterTableAddIndex(pos Pos) (*AlterTableAddIndex, error) { if after != nil { statementEnd = after.End() } + settings, err := p.tryParseSettingsClause(p.Pos()) + if err != nil { + return nil, err + } + if settings != nil { + statementEnd = settings.End() + } return &AlterTableAddIndex{ AddPos: pos, StatementEnd: statementEnd, IfNotExists: ifNotExists, Index: index, After: after, + Settings: settings, }, nil } diff --git a/parser/testdata/ddl/alter_table_add_index.sql b/parser/testdata/ddl/alter_table_add_index.sql index 65e0ca3..b67216c 100644 --- a/parser/testdata/ddl/alter_table_add_index.sql +++ b/parser/testdata/ddl/alter_table_add_index.sql @@ -3,3 +3,5 @@ ALTER TABLE test.events_local ON CLUSTER 'default_cluster' ADD INDEX api_id_idx ALTER TABLE test.events_local ON CLUSTER 'default_cluster' ADD INDEX arr_idx arr TYPE bloom_filter(0.01) GRANULARITY 3; ALTER TABLE test.events_local ON CLUSTER 'default_cluster' ADD INDEX content_idx content TYPE tokenbf_v1(30720, 2, 0) GRANULARITY 1; ALTER TABLE test.events_local ON CLUSTER 'default_cluster' ADD INDEX output_idx output TYPE ngrambf_v1(3, 10000, 2, 1) GRANULARITY 2; +ALTER TABLE test.events_local ON CLUSTER 'default_cluster' ADD INDEX created_at_idx created_at TYPE minmax GRANULARITY 1 SETTINGS alter_sync = 2; +ALTER TABLE test.events_local ON CLUSTER 'default_cluster' ADD INDEX IF NOT EXISTS created_at_idx created_at TYPE minmax GRANULARITY 1 SETTINGS alter_sync = 2; diff --git a/parser/testdata/ddl/format/alter_table_add_index.sql b/parser/testdata/ddl/format/alter_table_add_index.sql index e585d38..3806d3c 100644 --- a/parser/testdata/ddl/format/alter_table_add_index.sql +++ b/parser/testdata/ddl/format/alter_table_add_index.sql @@ -4,6 +4,8 @@ ALTER TABLE test.events_local ON CLUSTER 'default_cluster' ADD INDEX api_id_idx ALTER TABLE test.events_local ON CLUSTER 'default_cluster' ADD INDEX arr_idx arr TYPE bloom_filter(0.01) GRANULARITY 3; ALTER TABLE test.events_local ON CLUSTER 'default_cluster' ADD INDEX content_idx content TYPE tokenbf_v1(30720, 2, 0) GRANULARITY 1; ALTER TABLE test.events_local ON CLUSTER 'default_cluster' ADD INDEX output_idx output TYPE ngrambf_v1(3, 10000, 2, 1) GRANULARITY 2; +ALTER TABLE test.events_local ON CLUSTER 'default_cluster' ADD INDEX created_at_idx created_at TYPE minmax GRANULARITY 1 SETTINGS alter_sync = 2; +ALTER TABLE test.events_local ON CLUSTER 'default_cluster' ADD INDEX IF NOT EXISTS created_at_idx created_at TYPE minmax GRANULARITY 1 SETTINGS alter_sync = 2; -- Format SQL: @@ -12,3 +14,5 @@ ALTER TABLE test.events_local ON CLUSTER 'default_cluster' ADD INDEX api_id_idx ALTER TABLE test.events_local ON CLUSTER 'default_cluster' ADD INDEX arr_idx arr TYPE bloom_filter(0.01) GRANULARITY 3; ALTER TABLE test.events_local ON CLUSTER 'default_cluster' ADD INDEX content_idx content TYPE tokenbf_v1(30720, 2, 0) GRANULARITY 1; ALTER TABLE test.events_local ON CLUSTER 'default_cluster' ADD INDEX output_idx output TYPE ngrambf_v1(3, 10000, 2, 1) GRANULARITY 2; +ALTER TABLE test.events_local ON CLUSTER 'default_cluster' ADD INDEX created_at_idx created_at TYPE minmax GRANULARITY 1 SETTINGS alter_sync=2; +ALTER TABLE test.events_local ON CLUSTER 'default_cluster' ADD INDEX IF NOT EXISTS created_at_idx created_at TYPE minmax GRANULARITY 1 SETTINGS alter_sync=2; diff --git a/parser/testdata/ddl/format/beautify/alter_table_add_index.sql b/parser/testdata/ddl/format/beautify/alter_table_add_index.sql index 5ec84c6..2f9a0ae 100644 --- a/parser/testdata/ddl/format/beautify/alter_table_add_index.sql +++ b/parser/testdata/ddl/format/beautify/alter_table_add_index.sql @@ -4,6 +4,8 @@ ALTER TABLE test.events_local ON CLUSTER 'default_cluster' ADD INDEX api_id_idx ALTER TABLE test.events_local ON CLUSTER 'default_cluster' ADD INDEX arr_idx arr TYPE bloom_filter(0.01) GRANULARITY 3; ALTER TABLE test.events_local ON CLUSTER 'default_cluster' ADD INDEX content_idx content TYPE tokenbf_v1(30720, 2, 0) GRANULARITY 1; ALTER TABLE test.events_local ON CLUSTER 'default_cluster' ADD INDEX output_idx output TYPE ngrambf_v1(3, 10000, 2, 1) GRANULARITY 2; +ALTER TABLE test.events_local ON CLUSTER 'default_cluster' ADD INDEX created_at_idx created_at TYPE minmax GRANULARITY 1 SETTINGS alter_sync = 2; +ALTER TABLE test.events_local ON CLUSTER 'default_cluster' ADD INDEX IF NOT EXISTS created_at_idx created_at TYPE minmax GRANULARITY 1 SETTINGS alter_sync = 2; -- Beautify SQL: @@ -22,3 +24,13 @@ ADD INDEX content_idx content TYPE tokenbf_v1(30720, 2, 0) GRANULARITY 1; ALTER TABLE test.events_local ON CLUSTER 'default_cluster' ADD INDEX output_idx output TYPE ngrambf_v1(3, 10000, 2, 1) GRANULARITY 2; +ALTER TABLE test.events_local +ON CLUSTER 'default_cluster' +ADD INDEX created_at_idx created_at TYPE minmax GRANULARITY 1 +SETTINGS + alter_sync=2; +ALTER TABLE test.events_local +ON CLUSTER 'default_cluster' +ADD INDEX IF NOT EXISTS created_at_idx created_at TYPE minmax GRANULARITY 1 +SETTINGS + alter_sync=2; diff --git a/parser/testdata/ddl/output/alter_table_add_index.sql.golden.json b/parser/testdata/ddl/output/alter_table_add_index.sql.golden.json index 569f618..e761a08 100644 --- a/parser/testdata/ddl/output/alter_table_add_index.sql.golden.json +++ b/parser/testdata/ddl/output/alter_table_add_index.sql.golden.json @@ -79,7 +79,8 @@ } }, "IfNotExists": false, - "After": null + "After": null, + "Settings": null } ] }, @@ -158,7 +159,8 @@ } }, "IfNotExists": false, - "After": null + "After": null, + "Settings": null } ] }, @@ -237,7 +239,8 @@ } }, "IfNotExists": false, - "After": null + "After": null, + "Settings": null } ] }, @@ -328,7 +331,8 @@ } }, "IfNotExists": false, - "After": null + "After": null, + "Settings": null } ] }, @@ -425,7 +429,188 @@ } }, "IfNotExists": false, - "After": null + "After": null, + "Settings": null + } + ] + }, + { + "AlterPos": 615, + "StatementEnd": 759, + "TableIdentifier": { + "Database": { + "Name": "test", + "QuoteType": 1, + "NamePos": 627, + "NameEnd": 631 + }, + "Table": { + "Name": "events_local", + "QuoteType": 1, + "NamePos": 632, + "NameEnd": 644 + } + }, + "OnCluster": { + "OnPos": 645, + "Expr": { + "LiteralPos": 657, + "LiteralEnd": 672, + "Literal": "default_cluster" + } + }, + "AlterExprs": [ + { + "AddPos": 674, + "StatementEnd": 759, + "Index": { + "IndexPos": 678, + "Name": { + "Ident": { + "Name": "created_at_idx", + "QuoteType": 1, + "NamePos": 684, + "NameEnd": 698 + }, + "DotIdent": null + }, + "ColumnExpr": { + "Expr": { + "Name": "created_at", + "QuoteType": 1, + "NamePos": 699, + "NameEnd": 709 + }, + "Alias": null + }, + "ColumnType": { + "Name": { + "Name": "minmax", + "QuoteType": 1, + "NamePos": 715, + "NameEnd": 721 + } + }, + "Granularity": { + "NumPos": 734, + "NumEnd": 735, + "Literal": "1", + "Base": 10 + } + }, + "IfNotExists": false, + "After": null, + "Settings": { + "SettingsPos": 736, + "ListEnd": 759, + "Items": [ + { + "SettingsPos": 745, + "Name": { + "Name": "alter_sync", + "QuoteType": 1, + "NamePos": 745, + "NameEnd": 755 + }, + "Expr": { + "NumPos": 758, + "NumEnd": 759, + "Literal": "2", + "Base": 10 + } + } + ] + } + } + ] + }, + { + "AlterPos": 761, + "StatementEnd": 919, + "TableIdentifier": { + "Database": { + "Name": "test", + "QuoteType": 1, + "NamePos": 773, + "NameEnd": 777 + }, + "Table": { + "Name": "events_local", + "QuoteType": 1, + "NamePos": 778, + "NameEnd": 790 + } + }, + "OnCluster": { + "OnPos": 791, + "Expr": { + "LiteralPos": 803, + "LiteralEnd": 818, + "Literal": "default_cluster" + } + }, + "AlterExprs": [ + { + "AddPos": 820, + "StatementEnd": 919, + "Index": { + "IndexPos": 824, + "Name": { + "Ident": { + "Name": "created_at_idx", + "QuoteType": 1, + "NamePos": 844, + "NameEnd": 858 + }, + "DotIdent": null + }, + "ColumnExpr": { + "Expr": { + "Name": "created_at", + "QuoteType": 1, + "NamePos": 859, + "NameEnd": 869 + }, + "Alias": null + }, + "ColumnType": { + "Name": { + "Name": "minmax", + "QuoteType": 1, + "NamePos": 875, + "NameEnd": 881 + } + }, + "Granularity": { + "NumPos": 894, + "NumEnd": 895, + "Literal": "1", + "Base": 10 + } + }, + "IfNotExists": true, + "After": null, + "Settings": { + "SettingsPos": 896, + "ListEnd": 919, + "Items": [ + { + "SettingsPos": 905, + "Name": { + "Name": "alter_sync", + "QuoteType": 1, + "NamePos": 905, + "NameEnd": 915 + }, + "Expr": { + "NumPos": 918, + "NumEnd": 919, + "Literal": "2", + "Base": 10 + } + } + ] + } } ] } diff --git a/parser/walk.go b/parser/walk.go index 5cbcd7c..4d0802a 100644 --- a/parser/walk.go +++ b/parser/walk.go @@ -898,6 +898,9 @@ func Walk(node Expr, fn WalkFunc) bool { if !Walk(n.After, fn) { return false } + if !Walk(n.Settings, fn) { + return false + } case *AlterTableAddProjection: if !Walk(n.TableProjection, fn) { return false