Skip to content

Commit 8941020

Browse files
kyleconroyclaude
andcommitted
Continue parsing binary operators after parenthesized ORDER BY expressions (#117)
Fixes ORDER BY expressions like `(a + b) * c` being truncated to just `(a + b)`. - Add isBinaryOperatorToken() to detect binary operators after parenthesized expressions - Add parseExpressionFrom() to continue Pratt parsing from an existing left operand - Check for binary operators after parsing parenthesized expressions in ORDER BY Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 26a512c commit 8941020

3 files changed

Lines changed: 39 additions & 7 deletions

File tree

parser/expression.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,32 @@ func (p *Parser) parseExpressionList() []ast.Expression {
133133
return exprs
134134
}
135135

136+
// isBinaryOperatorToken checks if a token is a binary operator that could continue an expression
137+
func isBinaryOperatorToken(t token.Token) bool {
138+
switch t {
139+
case token.PLUS, token.MINUS, token.ASTERISK, token.SLASH, token.PERCENT,
140+
token.EQ, token.NEQ, token.LT, token.GT, token.LTE, token.GTE,
141+
token.AND, token.OR, token.CONCAT, token.DIV, token.MOD:
142+
return true
143+
}
144+
return false
145+
}
146+
147+
// parseExpressionFrom continues parsing an expression from an existing left operand
148+
func (p *Parser) parseExpressionFrom(left ast.Expression, precedence int) ast.Expression {
149+
for !p.currentIs(token.EOF) && precedence < p.precedenceForCurrent() {
150+
startPos := p.current.Pos
151+
left = p.parseInfixExpression(left)
152+
if left == nil {
153+
return nil
154+
}
155+
if p.current.Pos == startPos {
156+
break
157+
}
158+
}
159+
return left
160+
}
161+
136162
// parseCreateOrderByExpressions parses expressions for CREATE TABLE ORDER BY clause.
137163
// Returns the expressions and a boolean indicating if any ASC/DESC modifier was found.
138164
// This is different from regular expression list parsing because ORDER BY in CREATE TABLE

parser/parser.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2627,8 +2627,18 @@ func (p *Parser) parseTableOptions(create *ast.CreateQuery) {
26272627
Value: exprs,
26282628
}}
26292629
} else {
2630-
// Single expression in parentheses without modifiers - just extract it
2631-
create.OrderBy = exprs
2630+
// Single expression in parentheses without modifiers
2631+
// Check if there's a binary operator continuing the expression (e.g., (a + b) * c)
2632+
expr := exprs[0]
2633+
if isBinaryOperatorToken(p.current.Token) {
2634+
// Mark the expression as parenthesized and continue parsing
2635+
if binExpr, ok := expr.(*ast.BinaryExpr); ok {
2636+
binExpr.Parenthesized = true
2637+
}
2638+
// Continue parsing from this expression as left operand
2639+
expr = p.parseExpressionFrom(expr, LOWEST)
2640+
}
2641+
create.OrderBy = []ast.Expression{expr}
26322642
}
26332643
} else {
26342644
// Use ALIAS_PREC to avoid consuming AS keyword (for AS SELECT)
Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1 @@
1-
{
2-
"explain_todo": {
3-
"stmt63": true
4-
}
5-
}
1+
{}

0 commit comments

Comments
 (0)