Repository: AfterShip/clickhouse-sql-parser
Pull request #63 (2024-04-03 06:50:52)

Add support of the DROP PROJECTION statement
[3/4] part of #59 


Patch:
diff --git a/parser/ast.go b/parser/ast.go
index 8b7b4b5..9169d46 100644
--- a/parser/ast.go
+++ b/parser/ast.go
@@ -739,6 +739,43 @@ func (a *AlterTableDropIndex) Accept(visitor ASTVisitor) error {
 	return visitor.VisitAlterTableDropIndex(a)
 }
 
+type AlterTableDropProjection struct {
+	DropPos        Pos
+	ProjectionName *NestedIdentifier
+	IfExists       bool
+}
+
+func (a *AlterTableDropProjection) Pos() Pos {
+	return a.DropPos
+}
+
+func (a *AlterTableDropProjection) End() Pos {
+	return a.ProjectionName.End()
+}
+
+func (a *AlterTableDropProjection) AlterType() string {
+	return "DROP_PROJECTION"
+}
+
+func (a *AlterTableDropProjection) String(level int) string {
+	var builder strings.Builder
+	builder.WriteString("DROP PROJECTION ")
+	builder.WriteString(a.ProjectionName.String(level))
+	if a.IfExists {
+		builder.WriteString(" IF EXISTS")
+	}
+	return builder.String()
+}
+
+func (a *AlterTableDropProjection) Accept(visitor ASTVisitor) error {
+	visitor.enter(a)
+	defer visitor.leave(a)
+	if err := a.ProjectionName.Accept(visitor); err != nil {
+		return err
+	}
+	return visitor.VisitAlterTableDropProjection(a)
+}
+
 type AlterTableRemoveTTL struct {
 	RemovePos    Pos
 	StatementEnd Pos
diff --git a/parser/ast_visitor.go b/parser/ast_visitor.go
index 658dade..3df2a8a 100644
--- a/parser/ast_visitor.go
+++ b/parser/ast_visitor.go
@@ -17,6 +17,7 @@ type ASTVisitor interface {
 	VisitProjectionSelect(expr *ProjectionSelect) error
 	VisitAlterTableDropColumn(expr *AlterTableDropColumn) error
 	VisitAlterTableDropIndex(expr *AlterTableDropIndex) error
+	VisitAlterTableDropProjection(expr *AlterTableDropProjection) error
 	VisitAlterTableRemoveTTL(expr *AlterTableRemoveTTL) error
 	VisitAlterTableClearColumn(expr *AlterTableClearColumn) error
 	VisitAlterTableClearIndex(expr *AlterTableClearIndex) error
@@ -279,6 +280,13 @@ func (v *DefaultASTVisitor) VisitAlterTableDropIndex(expr *AlterTableDropIndex)
 	return nil
 }
 
+func (v *DefaultASTVisitor) VisitAlterTableDropProjection(expr *AlterTableDropProjection) error {
+	if v.Visit != nil {
+		return v.Visit(expr)
+	}
+	return nil
+}
+
 func (v *DefaultASTVisitor) VisitAlterTableRemoveTTL(expr *AlterTableRemoveTTL) error {
 	if v.Visit != nil {
 		return v.Visit(expr)
diff --git a/parser/parser_alter.go b/parser/parser_alter.go
index 9dc2e19..dc1762a 100644
--- a/parser/parser_alter.go
+++ b/parser/parser_alter.go
@@ -292,17 +292,15 @@ func (p *Parser) parseAlterTableDrop(pos Pos) (AlterTableExpr, error) {
 	}
 
 	switch {
-	case p.matchKeyword(KeywordColumn):
-		return p.parseAlterTableDropColumn(pos)
-	case p.matchKeyword(KeywordIndex):
-		return p.parseAlterTableDropIndex(pos)
+	case p.matchKeyword(KeywordColumn), p.matchKeyword(KeywordIndex), p.matchKeyword(KeywordProjection):
+		return p.parseAlterTableDropStatement(pos)
 	case p.matchKeyword(KeywordDetached):
 		_ = p.lexer.consumeToken()
 		return p.parseAlterTableDetachPartition(pos)
 	case p.matchKeyword(KeywordPartition):
 		return p.parseAlterTableDropPartition(pos)
 	default:
-		return nil, errors.New("expected keyword: COLUMN|INDEX|DETACH")
+		return nil, errors.New("expected keyword: COLUMN|INDEX|PROJECTION|DETACHED|PARTITION")
 	}
 }
 
@@ -389,10 +387,19 @@ func (p *Parser) parseAlterTableAttachPartition(pos Pos) (AlterTableExpr, error)
 	return alterTable, nil
 }
 
-func (p *Parser) parseAlterTableDropColumn(pos Pos) (AlterTableExpr, error) {
-	if err := p.consumeKeyword(KeywordColumn); err != nil {
-		return nil, err
+func (p *Parser) parseAlterTableDropStatement(pos Pos) (AlterTableExpr, 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 {
@@ -404,33 +411,25 @@ func (p *Parser) parseAlterTableDropColumn(pos Pos) (AlterTableExpr, error) {
 		return nil, err
 	}
 
-	return &AlterTableDropColumn{
-		DropPos:    pos,
-		ColumnName: name,
-		IfExists:   ifExists,
-	}, nil
-}
-
-func (p *Parser) parseAlterTableDropIndex(pos Pos) (AlterTableExpr, error) {
-	if err := p.consumeKeyword(KeywordIndex); err != nil {
-		return nil, err
-	}
-
-	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
 	}
-
-	return &AlterTableDropIndex{
-		DropPos:   pos,
-		IndexName: name,
-		IfExists:  ifExists,
-	}, nil
 }
 
 func (p *Parser) tryParseAfterClause() (*NestedIdentifier, error) {


Test patch:
diff --git a/parser/testdata/ddl/alter_table_drop_projection.sql b/parser/testdata/ddl/alter_table_drop_projection.sql
new file mode 100644
index 0000000..d510723
--- /dev/null
+++ b/parser/testdata/ddl/alter_table_drop_projection.sql
@@ -0,0 +1,1 @@
+ALTER TABLE test.event_local ON CLUSTER 'default_cluster' DROP PROJECTION f1;
\ No newline at end of file
diff --git a/parser/testdata/ddl/format/alter_table_drop_projection.sql b/parser/testdata/ddl/format/alter_table_drop_projection.sql
new file mode 100644
index 0000000..c594cd2
--- /dev/null
+++ b/parser/testdata/ddl/format/alter_table_drop_projection.sql
@@ -0,0 +1,7 @@
+-- Origin SQL:
+ALTER TABLE test.event_local ON CLUSTER 'default_cluster' DROP PROJECTION f1;
+
+-- Format SQL:
+ALTER TABLE test.event_local
+ON CLUSTER 'default_cluster'
+DROP PROJECTION f1;
diff --git a/parser/testdata/ddl/output/alter_table_drop_projection.sql.golden.json b/parser/testdata/ddl/output/alter_table_drop_projection.sql.golden.json
new file mode 100644
index 0000000..9be7d08
--- /dev/null
+++ b/parser/testdata/ddl/output/alter_table_drop_projection.sql.golden.json
@@ -0,0 +1,43 @@
+[
+  {
+    "AlterPos": 0,
+    "StatementEnd": 76,
+    "TableIdentifier": {
+      "Database": {
+        "Name": "test",
+        "QuoteType": 1,
+        "NamePos": 12,
+        "NameEnd": 16
+      },
+      "Table": {
+        "Name": "event_local",
+        "QuoteType": 1,
+        "NamePos": 17,
+        "NameEnd": 28
+      }
+    },
+    "OnCluster": {
+      "OnPos": 29,
+      "Expr": {
+        "LiteralPos": 41,
+        "LiteralEnd": 56,
+        "Literal": "default_cluster"
+      }
+    },
+    "AlterExprs": [
+      {
+        "DropPos": 58,
+        "ProjectionName": {
+          "Ident": {
+            "Name": "f1",
+            "QuoteType": 1,
+            "NamePos": 74,
+            "NameEnd": 76
+          },
+          "DotIdent": null
+        },
+        "IfExists": false
+      }
+    ]
+  }
+]
\ No newline at end of file