Skip to content

Commit cb711f7

Browse files
authored
Fix INTERSECT/EXCEPT operator precedence in parser (#108)
1 parent 918155d commit cb711f7

File tree

361 files changed

+767
-1938
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

361 files changed

+767
-1938
lines changed

ast/ast.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ type SelectQuery struct {
6464
PreWhere Expression `json:"prewhere,omitempty"`
6565
Where Expression `json:"where,omitempty"`
6666
GroupBy []Expression `json:"group_by,omitempty"`
67+
GroupingSets bool `json:"grouping_sets,omitempty"` // true if GROUP BY uses GROUPING SETS
6768
WithRollup bool `json:"with_rollup,omitempty"`
6869
WithCube bool `json:"with_cube,omitempty"`
6970
WithTotals bool `json:"with_totals,omitempty"`
@@ -503,6 +504,7 @@ type AlterQuery struct {
503504
Table string `json:"table"`
504505
Commands []*AlterCommand `json:"commands"`
505506
OnCluster string `json:"on_cluster,omitempty"`
507+
Settings []*SettingExpr `json:"settings,omitempty"`
506508
}
507509

508510
func (a *AlterQuery) Pos() token.Position { return a.Position }

internal/explain/explain.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,9 @@ func Column(sb *strings.Builder, col *ast.ColumnDeclaration, depth int) {
279279
if col.Default != nil || hasEphemeralDefault {
280280
children++
281281
}
282+
if col.TTL != nil {
283+
children++
284+
}
282285
if col.Codec != nil {
283286
children++
284287
}
@@ -295,6 +298,9 @@ func Column(sb *strings.Builder, col *ast.ColumnDeclaration, depth int) {
295298
// EPHEMERAL columns without explicit default value show defaultValueOfTypeName function
296299
fmt.Fprintf(sb, "%s Function defaultValueOfTypeName\n", indent)
297300
}
301+
if col.TTL != nil {
302+
Node(sb, col.TTL, depth+1)
303+
}
298304
if col.Codec != nil {
299305
explainCodecExpr(sb, col.Codec, indent+" ", depth+1)
300306
}

internal/explain/expressions.go

Lines changed: 92 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,16 @@ func explainLiteral(sb *strings.Builder, n *ast.Literal, indent string, depth in
6565
for _, e := range exprs {
6666
// Simple literals (numbers, strings, etc.) are OK
6767
if lit, isLit := e.(*ast.Literal); isLit {
68-
// Nested tuples/arrays are complex
69-
if lit.Type == ast.LiteralTuple || lit.Type == ast.LiteralArray {
68+
// Nested tuples that contain only primitive literals are OK
69+
if lit.Type == ast.LiteralTuple {
70+
if !containsOnlyPrimitiveLiteralsWithUnary(lit) {
71+
hasComplexExpr = true
72+
break
73+
}
74+
continue
75+
}
76+
// Arrays are always complex in tuple context
77+
if lit.Type == ast.LiteralArray {
7078
hasComplexExpr = true
7179
break
7280
}
@@ -114,36 +122,28 @@ func explainLiteral(sb *strings.Builder, n *ast.Literal, indent string, depth in
114122
// This happens when:
115123
// 1. Contains non-literal, non-negation expressions OR
116124
// 2. Contains tuples OR
117-
// 3. Contains nested arrays that all have exactly 1 element (homogeneous single-element arrays) OR
118-
// 4. Contains nested arrays with non-literal expressions OR
119-
// 5. Contains nested arrays that are empty or contain tuples/non-literals
125+
// 3. Contains nested arrays with non-literal expressions OR
126+
// 4. Contains nested arrays that are empty or contain tuples/non-literals
120127
shouldUseFunctionArray := false
121-
allAreSingleElementArrays := true
122128
hasNestedArrays := false
123129
nestedArraysNeedFunctionFormat := false
124130

125131
for _, e := range exprs {
126132
if lit, ok := e.(*ast.Literal); ok {
127133
if lit.Type == ast.LiteralArray {
128134
hasNestedArrays = true
129-
// Check if this inner array has exactly 1 element
135+
// Check if inner array needs Function array format:
136+
// - Contains non-literal expressions OR
137+
// - Contains tuples OR
138+
// - Is empty OR
139+
// - Contains empty arrays
130140
if innerExprs, ok := lit.Value.([]ast.Expression); ok {
131-
if len(innerExprs) != 1 {
132-
allAreSingleElementArrays = false
133-
}
134-
// Check if inner array needs Function array format:
135-
// - Contains non-literal expressions OR
136-
// - Contains tuples OR
137-
// - Is empty OR
138-
// - Contains empty arrays
139141
if containsNonLiteralExpressions(innerExprs) ||
140142
len(innerExprs) == 0 ||
141143
containsTuples(innerExprs) ||
142144
containsEmptyArrays(innerExprs) {
143145
nestedArraysNeedFunctionFormat = true
144146
}
145-
} else {
146-
allAreSingleElementArrays = false
147147
}
148148
} else if lit.Type == ast.LiteralTuple {
149149
// Tuples are complex
@@ -155,9 +155,17 @@ func explainLiteral(sb *strings.Builder, n *ast.Literal, indent string, depth in
155155
}
156156

157157
// Use Function array when:
158-
// - nested arrays that are ALL single-element
159-
// - nested arrays that need Function format (contain non-literals, tuples, or empty arrays)
160-
if hasNestedArrays && (allAreSingleElementArrays || nestedArraysNeedFunctionFormat) {
158+
// - nested arrays that need Function format (contain non-literals, tuples, or empty arrays at any depth)
159+
// Note: nested arrays that are ALL single-element should still be Literal format
160+
if hasNestedArrays && nestedArraysNeedFunctionFormat {
161+
shouldUseFunctionArray = true
162+
}
163+
// Also check for empty arrays at any depth within nested arrays
164+
if hasNestedArrays && containsEmptyArraysRecursive(exprs) {
165+
shouldUseFunctionArray = true
166+
}
167+
// Also check for tuples at any depth within nested arrays
168+
if hasNestedArrays && containsTuplesRecursive(exprs) {
161169
shouldUseFunctionArray = true
162170
}
163171

@@ -249,6 +257,43 @@ func containsEmptyArrays(exprs []ast.Expression) bool {
249257
return false
250258
}
251259

260+
// containsEmptyArraysRecursive checks if any nested array at any depth is empty
261+
func containsEmptyArraysRecursive(exprs []ast.Expression) bool {
262+
for _, e := range exprs {
263+
if lit, ok := e.(*ast.Literal); ok && lit.Type == ast.LiteralArray {
264+
if innerExprs, ok := lit.Value.([]ast.Expression); ok {
265+
if len(innerExprs) == 0 {
266+
return true
267+
}
268+
// Recursively check nested arrays
269+
if containsEmptyArraysRecursive(innerExprs) {
270+
return true
271+
}
272+
}
273+
}
274+
}
275+
return false
276+
}
277+
278+
// containsTuplesRecursive checks if any nested array contains tuples at any depth
279+
func containsTuplesRecursive(exprs []ast.Expression) bool {
280+
for _, e := range exprs {
281+
if lit, ok := e.(*ast.Literal); ok {
282+
if lit.Type == ast.LiteralTuple {
283+
return true
284+
}
285+
if lit.Type == ast.LiteralArray {
286+
if innerExprs, ok := lit.Value.([]ast.Expression); ok {
287+
if containsTuplesRecursive(innerExprs) {
288+
return true
289+
}
290+
}
291+
}
292+
}
293+
}
294+
return false
295+
}
296+
252297
func explainBinaryExpr(sb *strings.Builder, n *ast.BinaryExpr, indent string, depth int) {
253298
// Convert operator to function name
254299
fnName := OperatorToFunction(n.Op)
@@ -377,14 +422,18 @@ func explainAliasedExpr(sb *strings.Builder, n *ast.AliasedExpr, depth int) {
377422
// Check if this is a tuple with complex expressions that should be rendered as Function tuple
378423
if e.Type == ast.LiteralTuple {
379424
if exprs, ok := e.Value.([]ast.Expression); ok {
380-
hasComplexExpr := false
425+
needsFunctionFormat := false
426+
// Empty tuples always use Function tuple format
427+
if len(exprs) == 0 {
428+
needsFunctionFormat = true
429+
}
381430
for _, expr := range exprs {
382431
if _, isLit := expr.(*ast.Literal); !isLit {
383-
hasComplexExpr = true
432+
needsFunctionFormat = true
384433
break
385434
}
386435
}
387-
if hasComplexExpr {
436+
if needsFunctionFormat {
388437
// Render as Function tuple with alias
389438
fmt.Fprintf(sb, "%sFunction tuple (alias %s) (children %d)\n", indent, escapeAlias(n.Alias), 1)
390439
fmt.Fprintf(sb, "%s ExpressionList (children %d)\n", indent, len(exprs))
@@ -489,6 +538,12 @@ func explainAliasedExpr(sb *strings.Builder, n *ast.AliasedExpr, depth int) {
489538
case *ast.FunctionCall:
490539
// Function calls already handle aliases
491540
explainFunctionCallWithAlias(sb, e, n.Alias, indent, depth)
541+
case *ast.Lambda:
542+
// Lambda expressions with alias
543+
explainLambdaWithAlias(sb, e, n.Alias, indent, depth)
544+
case *ast.ExtractExpr:
545+
// EXTRACT expressions with alias
546+
explainExtractExprWithAlias(sb, e, n.Alias, indent, depth)
492547
case *ast.Identifier:
493548
// Identifiers with alias
494549
fmt.Fprintf(sb, "%sIdentifier %s (alias %s)\n", indent, e.Name(), escapeAlias(n.Alias))
@@ -632,6 +687,18 @@ func explainWithElement(sb *strings.Builder, n *ast.WithElement, indent string,
632687
// When name is empty, don't show the alias part
633688
switch e := n.Query.(type) {
634689
case *ast.Literal:
690+
// Empty tuples should be rendered as Function tuple, not Literal
691+
if e.Type == ast.LiteralTuple {
692+
if exprs, ok := e.Value.([]ast.Expression); ok && len(exprs) == 0 {
693+
if n.Name != "" {
694+
fmt.Fprintf(sb, "%sFunction tuple (alias %s) (children %d)\n", indent, n.Name, 1)
695+
} else {
696+
fmt.Fprintf(sb, "%sFunction tuple (children %d)\n", indent, 1)
697+
}
698+
fmt.Fprintf(sb, "%s ExpressionList\n", indent)
699+
return
700+
}
701+
}
635702
if n.Name != "" {
636703
fmt.Fprintf(sb, "%sLiteral %s (alias %s)\n", indent, FormatLiteral(e), n.Name)
637704
} else {
@@ -645,6 +712,8 @@ func explainWithElement(sb *strings.Builder, n *ast.WithElement, indent string,
645712
}
646713
case *ast.FunctionCall:
647714
explainFunctionCallWithAlias(sb, e, n.Name, indent, depth)
715+
case *ast.Lambda:
716+
explainLambdaWithAlias(sb, e, n.Name, indent, depth)
648717
case *ast.BinaryExpr:
649718
// Binary expressions become functions
650719
fnName := OperatorToFunction(e.Op)

0 commit comments

Comments
 (0)