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

Issue:
nil pointer dereference on a simple query with no table name after FROM
query: `SELECT * FROM` 

```
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
	panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x2 addr=0x10 pc=0x102ee9d80]

[redacted]
github.com/AfterShip/clickhouse-sql-parser/parser.(*Parser).parseJoinTableExpr(0x1400009ef30, 0x102ee2e74?)
	/pkg/mod/github.com/AfterShip/clickhouse-sql-parser@v0.0.0-20250301225821-9825d50f553f/parser/parser_query.go:217 +0x2d0
[redacted]
```

the problematic code line:
```
return nil, fmt.Errorf("expected table name or subquery, got %s", p.last().Kind)
```
i would guess `p.last()` is nil

Pull request:
Fix panic with the invalid SQL
This closes #129 

Patch:
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index b11ad71..5efe351 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -29,7 +29,7 @@ jobs:
           fetch-depth: '0'
 
       - name: Restore Go Module Cache
-        uses: actions/cache@v2
+        uses: actions/cache@v4
         with:
           path: ~/go/internal/mod
           key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
diff --git a/parser/parser_common.go b/parser/parser_common.go
index 07b8d4f..dbb181e 100644
--- a/parser/parser_common.go
+++ b/parser/parser_common.go
@@ -274,7 +274,7 @@ func (p *Parser) parseLiteral(pos Pos) (Literal, error) {
 		// accept the NULL keyword
 		return &NullLiteral{NullPos: pos}, nil
 	default:
-		return nil, fmt.Errorf("expected <int>, <string> or keyword <NULL>, but got %q", p.last().Kind)
+		return nil, fmt.Errorf("expected <int>, <string> or keyword <NULL>, but got %q", p.lastTokenKind())
 	}
 }
 
diff --git a/parser/parser_query.go b/parser/parser_query.go
index 79b6d28..b82bbfd 100644
--- a/parser/parser_query.go
+++ b/parser/parser_query.go
@@ -214,7 +214,7 @@ func (p *Parser) parseJoinTableExpr(_ Pos) (Expr, error) {
 			StatementEnd: statementEnd,
 		}, nil
 	default:
-		return nil, fmt.Errorf("expected table name or subquery, got %s", p.last().Kind)
+		return nil, fmt.Errorf("expected table name or subquery, got %s", fmt.Sprintf("%v", p.lastTokenKind()))
 	}
 }
 


Test patch:
diff --git a/parser/parser_test.go b/parser/parser_test.go
index 389b055..7b5044b 100644
--- a/parser/parser_test.go
+++ b/parser/parser_test.go
@@ -127,3 +127,14 @@ func validFormatSQL(t *testing.T, sql string) {
 	}
 	require.Equal(t, sql, builder.String())
 }
+
+func TestParser_InvalidSyntax(t *testing.T) {
+	invalidSQLs := []string{
+		"SELECT * FROM",
+	}
+	for _, sql := range invalidSQLs {
+		parser := NewParser(sql)
+		_, err := parser.ParseStmts()
+		require.Error(t, err)
+	}
+}