Repository: AfterShip/clickhouse-sql-parser (go)

Issue:
Accessing tuple in SELECT clause with dot notation yields error
Parsing `SELECT t.1 FROM test` yields:

```
        	            	line 0:9 <EOF> or ';' was expected, but got: "1"
        	            	SELECT t.1 FROM test 
        	            	         ^
```

Link to fully working example: https://fiddle.clickhouse.com/abc5451d-1843-4659-b2f1-a13bf1cbb29d

As a workaround the `tupleElement` function works and `SELECT tupleElement(t, 1) FROM test` gets parsed properly, but for convenience I would appreciate tuple aliases in `SELECT` clauses with dot notation being supported.



Pull request description:
Fix column index style wasn't supported in query
This closes #118.

Patch:
diff --git a/parser/ast.go b/parser/ast.go
index 548f1b3..a4719ee 100644
--- a/parser/ast.go
+++ b/parser/ast.go
@@ -188,7 +188,7 @@ func (p *BinaryOperation) Accept(visitor ASTVisitor) error {
 }
 
 type IndexOperation struct {
-	LeftExpr  Expr
+	Object    Expr
 	Operation TokenKind
 	Index     Expr
 }
@@ -196,7 +196,7 @@ type IndexOperation struct {
 func (i *IndexOperation) Accept(visitor ASTVisitor) error {
 	visitor.enter(i)
 	defer visitor.leave(i)
-	if err := i.LeftExpr.Accept(visitor); err != nil {
+	if err := i.Object.Accept(visitor); err != nil {
 		return err
 	}
 	if err := i.Index.Accept(visitor); err != nil {
@@ -206,7 +206,7 @@ func (i *IndexOperation) Accept(visitor ASTVisitor) error {
 }
 
 func (i *IndexOperation) Pos() Pos {
-	return i.LeftExpr.Pos()
+	return i.Object.Pos()
 }
 
 func (i *IndexOperation) End() Pos {
@@ -215,7 +215,7 @@ func (i *IndexOperation) End() Pos {
 
 func (i *IndexOperation) String() string {
 	var builder strings.Builder
-	builder.WriteString(i.LeftExpr.String())
+	builder.WriteString(i.Object.String())
 	builder.WriteString(string(i.Operation))
 	builder.WriteString(i.Index.String())
 	return builder.String()
diff --git a/parser/parser_column.go b/parser/parser_column.go
index 6aafb8d..1db650c 100644
--- a/parser/parser_column.go
+++ b/parser/parser_column.go
@@ -41,6 +41,8 @@ func (p *Parser) getNextPrecedence() int {
 		return PrecedenceIs
 	case p.matchKeyword(KeywordNot):
 		return PrecedenceNot
+	case p.matchTokenKind(TokenKindDot):
+		return PrecedenceDot
 	case p.matchTokenKind(TokenKindDash):
 		return PrecedenceDoubleColon
 	case p.matchTokenKind(TokenKindSingleEQ), p.matchTokenKind(TokenKindLT), p.matchTokenKind(TokenKindLE),
@@ -155,7 +157,7 @@ func (p *Parser) parseInfix(expr Expr, precedence int) (Expr, error) {
 			return nil, err
 		}
 		return &IndexOperation{
-			LeftExpr:  expr,
+			Object:    expr,
 			Operation: TokenKindDot,
 			Index:     rightExpr,
 		}, nil
diff --git a/parser/parser_table.go b/parser/parser_table.go
index faf6649..f6bcba4 100644
--- a/parser/parser_table.go
+++ b/parser/parser_table.go
@@ -254,8 +254,19 @@ func (p *Parser) parseIdentOrFunction(_ Pos) (Expr, error) {
 				Ident:    ident,
 				DotIdent: nextIdent,
 			}, nil
+		case p.matchTokenKind(TokenKindInt):
+			i, err := p.parseNumber(p.Pos())
+			if err != nil {
+				return nil, err
+			}
+			return &IndexOperation{
+				Object:    ident,
+				Operation: TokenKindDot,
+				Index:     i,
+			}, nil
+		default:
+			return nil, fmt.Errorf("expected IDENT, NUMBER or *, but got %q", p.lastTokenKind())
 		}
-
 	}
 	return ident, nil
 }


Test patch:
diff --git a/parser/testdata/query/format/select_with_number_field.sql b/parser/testdata/query/format/select_with_number_field.sql
new file mode 100644
index 0000000..bc9df57
--- /dev/null
+++ b/parser/testdata/query/format/select_with_number_field.sql
@@ -0,0 +1,5 @@
+-- Origin SQL:
+SELECT foo, bar.1, foo.2 FROM foo ARRAY JOIN m as bar
+
+-- Format SQL:
+SELECT foo, bar.1, foo.2 FROM foo  ARRAY JOIN m AS bar;
diff --git a/parser/testdata/query/output/access_tuple_with_dot.sql.golden.json b/parser/testdata/query/output/access_tuple_with_dot.sql.golden.json
index 563e409..f290a1d 100644
--- a/parser/testdata/query/output/access_tuple_with_dot.sql.golden.json
+++ b/parser/testdata/query/output/access_tuple_with_dot.sql.golden.json
@@ -7,7 +7,7 @@
     "SelectItems": [
       {
         "Expr": {
-          "LeftExpr": {
+          "Object": {
             "Name": {
               "Name": "tuple",
               "QuoteType": 1,
@@ -291,7 +291,7 @@
       },
       {
         "Expr": {
-          "LeftExpr": {
+          "Object": {
             "LeftParenPos": 161,
             "RightParenPos": 257,
             "Items": {
@@ -485,7 +485,7 @@
       },
       {
         "Expr": {
-          "LeftExpr": {
+          "Object": {
             "Name": {
               "Name": "tuple",
               "QuoteType": 1,
diff --git a/parser/testdata/query/output/select_with_number_field.sql.golden.json b/parser/testdata/query/output/select_with_number_field.sql.golden.json
new file mode 100644
index 0000000..27387eb
--- /dev/null
+++ b/parser/testdata/query/output/select_with_number_field.sql.golden.json
@@ -0,0 +1,120 @@
+[
+  {
+    "SelectPos": 0,
+    "StatementEnd": 53,
+    "With": null,
+    "Top": null,
+    "SelectItems": [
+      {
+        "Expr": {
+          "Name": "foo",
+          "QuoteType": 1,
+          "NamePos": 7,
+          "NameEnd": 10
+        },
+        "Modifiers": [],
+        "Alias": null
+      },
+      {
+        "Expr": {
+          "Object": {
+            "Name": "bar",
+            "QuoteType": 1,
+            "NamePos": 12,
+            "NameEnd": 15
+          },
+          "Operation": ".",
+          "Index": {
+            "NumPos": 16,
+            "NumEnd": 17,
+            "Literal": "1",
+            "Base": 10
+          }
+        },
+        "Modifiers": [],
+        "Alias": null
+      },
+      {
+        "Expr": {
+          "Object": {
+            "Name": "foo",
+            "QuoteType": 1,
+            "NamePos": 19,
+            "NameEnd": 22
+          },
+          "Operation": ".",
+          "Index": {
+            "NumPos": 23,
+            "NumEnd": 24,
+            "Literal": "2",
+            "Base": 10
+          }
+        },
+        "Modifiers": [],
+        "Alias": null
+      }
+    ],
+    "From": {
+      "FromPos": 25,
+      "Expr": {
+        "Table": {
+          "TablePos": 30,
+          "TableEnd": 33,
+          "Alias": null,
+          "Expr": {
+            "Database": null,
+            "Table": {
+              "Name": "foo",
+              "QuoteType": 1,
+              "NamePos": 30,
+              "NameEnd": 33
+            }
+          },
+          "HasFinal": false
+        },
+        "StatementEnd": 33,
+        "SampleRatio": null,
+        "HasFinal": false
+      }
+    },
+    "ArrayJoin": {
+      "ArrayPos": 34,
+      "Type": "",
+      "Expr": {
+        "ListPos": 45,
+        "ListEnd": 53,
+        "HasDistinct": false,
+        "Items": [
+          {
+            "Expr": {
+              "Name": "m",
+              "QuoteType": 1,
+              "NamePos": 45,
+              "NameEnd": 46
+            },
+            "Alias": {
+              "Name": "bar",
+              "QuoteType": 1,
+              "NamePos": 50,
+              "NameEnd": 53
+            }
+          }
+        ]
+      }
+    },
+    "Window": null,
+    "Prewhere": null,
+    "Where": null,
+    "GroupBy": null,
+    "WithTotal": false,
+    "Having": null,
+    "OrderBy": null,
+    "LimitBy": null,
+    "Limit": null,
+    "Settings": null,
+    "Format": null,
+    "UnionAll": null,
+    "UnionDistinct": null,
+    "Except": null
+  }
+]
\ No newline at end of file
diff --git a/parser/testdata/query/select_with_number_field.sql b/parser/testdata/query/select_with_number_field.sql
new file mode 100644
index 0000000..4d21dbe
--- /dev/null
+++ b/parser/testdata/query/select_with_number_field.sql
@@ -0,0 +1,1 @@
+SELECT foo, bar.1, foo.2 FROM foo ARRAY JOIN m as bar
\ No newline at end of file