Skip to content

Commit 7fff627

Browse files
committed
Fix multiple parser and explain output issues
- Add HasParentheses field to DataType to track empty parentheses (e.g., Tuple()) - Add NameTypePair AST node for Nested type column declarations - Update array expansion to treat tuple/array literals as complex expressions - Change boolean literal format from UInt8 to Bool - Fix InsertQuery to include Settings from SELECT clause as child - Add nil safety checks for InsertQuery SELECT parsing
1 parent 3949190 commit 7fff627

6 files changed

Lines changed: 67 additions & 13 deletions

File tree

ast/ast.go

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -266,15 +266,27 @@ func (c *ColumnDeclaration) End() token.Position { return c.Position }
266266

267267
// DataType represents a data type.
268268
type DataType struct {
269-
Position token.Position `json:"-"`
270-
Name string `json:"name"`
271-
Parameters []Expression `json:"parameters,omitempty"`
269+
Position token.Position `json:"-"`
270+
Name string `json:"name"`
271+
Parameters []Expression `json:"parameters,omitempty"`
272+
HasParentheses bool `json:"has_parentheses,omitempty"`
272273
}
273274

274275
func (d *DataType) Pos() token.Position { return d.Position }
275276
func (d *DataType) End() token.Position { return d.Position }
276277
func (d *DataType) expressionNode() {}
277278

279+
// NameTypePair represents a named type pair, used in Nested types.
280+
type NameTypePair struct {
281+
Position token.Position `json:"-"`
282+
Name string `json:"name"`
283+
Type *DataType `json:"type"`
284+
}
285+
286+
func (n *NameTypePair) Pos() token.Position { return n.Position }
287+
func (n *NameTypePair) End() token.Position { return n.Position }
288+
func (n *NameTypePair) expressionNode() {}
289+
278290
// CodecExpr represents a CODEC expression.
279291
type CodecExpr struct {
280292
Position token.Position `json:"-"`

internal/explain/explain.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ func Node(sb *strings.Builder, node interface{}, depth int) {
121121
// Types
122122
case *ast.DataType:
123123
explainDataType(sb, n, indent, depth)
124+
case *ast.NameTypePair:
125+
explainNameTypePair(sb, n, indent, depth)
124126
case *ast.Parameter:
125127
explainParameter(sb, n, indent)
126128

internal/explain/expressions.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ func explainLiteral(sb *strings.Builder, n *ast.Literal, indent string, depth in
6161
}
6262
hasComplexExpr := false
6363
for _, e := range exprs {
64-
if _, isLit := e.(*ast.Literal); !isLit {
64+
lit, isLit := e.(*ast.Literal)
65+
// Non-literals or tuple/array literals count as complex
66+
if !isLit || (isLit && (lit.Type == ast.LiteralTuple || lit.Type == ast.LiteralArray)) {
6567
hasComplexExpr = true
6668
break
6769
}

internal/explain/format.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ func FormatLiteral(lit *ast.Literal) string {
3333
return fmt.Sprintf("\\'%s\\'", s)
3434
case ast.LiteralBoolean:
3535
if lit.Value.(bool) {
36-
return "UInt8_1"
36+
return "Bool_1"
3737
}
38-
return "UInt8_0"
38+
return "Bool_0"
3939
case ast.LiteralNull:
4040
return "NULL"
4141
case ast.LiteralArray:

internal/explain/statements.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,11 +182,20 @@ func explainDataType(sb *strings.Builder, n *ast.DataType, indent string, depth
182182
for _, p := range n.Parameters {
183183
Node(sb, p, depth+2)
184184
}
185+
} else if n.HasParentheses {
186+
// Empty parentheses, e.g., Tuple()
187+
fmt.Fprintf(sb, "%sDataType %s (children %d)\n", indent, n.Name, 1)
188+
fmt.Fprintf(sb, "%s ExpressionList\n", indent)
185189
} else {
186190
fmt.Fprintf(sb, "%sDataType %s\n", indent, n.Name)
187191
}
188192
}
189193

194+
func explainNameTypePair(sb *strings.Builder, n *ast.NameTypePair, indent string, depth int) {
195+
fmt.Fprintf(sb, "%sNameTypePair %s (children %d)\n", indent, n.Name, 1)
196+
Node(sb, n.Type, depth+1)
197+
}
198+
190199
func explainParameter(sb *strings.Builder, n *ast.Parameter, indent string) {
191200
if n.Name != "" {
192201
fmt.Fprintf(sb, "%sQueryParameter %s\n", indent, n.Name)

parser/parser.go

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -173,11 +173,23 @@ func (p *Parser) parseSelectWithUnion() *ast.SelectWithUnionQuery {
173173
p.nextToken()
174174
}
175175
query.UnionModes = append(query.UnionModes, mode)
176-
sel := p.parseSelect()
177-
if sel == nil {
178-
break
176+
177+
// Handle parenthesized subqueries: UNION ALL (SELECT ... UNION ALL SELECT ...)
178+
if p.currentIs(token.LPAREN) {
179+
p.nextToken() // skip (
180+
nested := p.parseSelectWithUnion()
181+
p.expect(token.RPAREN)
182+
// Flatten nested union selects into current query
183+
for _, s := range nested.Selects {
184+
query.Selects = append(query.Selects, s)
185+
}
186+
} else {
187+
sel := p.parseSelect()
188+
if sel == nil {
189+
break
190+
}
191+
query.Selects = append(query.Selects, sel)
179192
}
180-
query.Selects = append(query.Selects, sel)
181193
}
182194

183195
return query
@@ -917,6 +929,17 @@ func (p *Parser) parseInsert() *ast.InsertQuery {
917929
}
918930
} else if p.currentIs(token.SELECT) || p.currentIs(token.WITH) {
919931
ins.Select = p.parseSelectWithUnion()
932+
// If the SELECT has settings, mark the INSERT as having settings too
933+
if ins.Select != nil {
934+
if sel, ok := ins.Select.(*ast.SelectWithUnionQuery); ok && sel != nil && len(sel.Selects) > 0 {
935+
lastSel := sel.Selects[len(sel.Selects)-1]
936+
if lastSel != nil {
937+
if selQuery, ok := lastSel.(*ast.SelectQuery); ok && selQuery != nil && len(selQuery.Settings) > 0 {
938+
ins.HasSettings = true
939+
}
940+
}
941+
}
942+
}
920943
}
921944

922945
// Parse FORMAT (format names can be keywords like Null, JSON, etc.)
@@ -1314,21 +1337,27 @@ func (p *Parser) parseDataType() *ast.DataType {
13141337

13151338
// Parse type parameters
13161339
if p.currentIs(token.LPAREN) {
1340+
dt.HasParentheses = true
13171341
p.nextToken()
13181342

13191343
// Special handling for Nested type - it contains column declarations, not just types
13201344
if strings.ToUpper(dt.Name) == "NESTED" {
13211345
for !p.currentIs(token.RPAREN) && !p.currentIs(token.EOF) {
13221346
// Parse as column name + type
13231347
if p.currentIs(token.IDENT) || p.current.Token.IsKeyword() {
1348+
pos := p.current.Pos
13241349
colName := p.current.Value
13251350
p.nextToken()
13261351
// Parse the type for this column
13271352
colType := p.parseDataType()
13281353
if colType != nil {
1329-
// Wrap in a special format or just store as data type
1330-
colType.Name = colName + " " + colType.Name
1331-
dt.Parameters = append(dt.Parameters, colType)
1354+
// Use NameTypePair for Nested column declarations
1355+
ntp := &ast.NameTypePair{
1356+
Position: pos,
1357+
Name: colName,
1358+
Type: colType,
1359+
}
1360+
dt.Parameters = append(dt.Parameters, ntp)
13321361
}
13331362
}
13341363
if p.currentIs(token.COMMA) {

0 commit comments

Comments
 (0)