Skip to content

Commit 5519571

Browse files
committed
Add scalar subquery parsing in boolean expressions
- Handle (SELECT ...) = value comparisons in boolean expressions - Add isComparisonOperator and parseComparisonAfterLeft helpers - Scalar subqueries in IF conditions now parse correctly
1 parent 9561bbd commit 5519571

2 files changed

Lines changed: 72 additions & 2 deletions

File tree

parser/parse_select.go

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2370,8 +2370,33 @@ func (p *Parser) parseBooleanAndExpression() (ast.BooleanExpression, error) {
23702370
}
23712371

23722372
func (p *Parser) parseBooleanPrimaryExpression() (ast.BooleanExpression, error) {
2373-
// Check for parenthesized boolean expression
2373+
// Check for parenthesized expression - could be boolean or scalar subquery
23742374
if p.curTok.Type == TokenLParen {
2375+
// Peek ahead to see if it's a subquery (SELECT)
2376+
if p.peekTok.Type == TokenSelect {
2377+
// Parse as scalar subquery that will be used in a comparison
2378+
p.nextToken() // consume (
2379+
qe, err := p.parseQueryExpression()
2380+
if err != nil {
2381+
return nil, err
2382+
}
2383+
if p.curTok.Type != TokenRParen {
2384+
return nil, fmt.Errorf("expected ), got %s", p.curTok.Literal)
2385+
}
2386+
p.nextToken() // consume )
2387+
2388+
subquery := &ast.ScalarSubquery{QueryExpression: qe}
2389+
2390+
// Now check for comparison operators
2391+
if p.isComparisonOperator() {
2392+
return p.parseComparisonAfterLeft(subquery)
2393+
}
2394+
// If no comparison, this might be used in other contexts
2395+
// For now, treat it as an error if used standalone
2396+
return nil, fmt.Errorf("scalar subquery must be followed by a comparison operator")
2397+
}
2398+
2399+
// Parse as parenthesized boolean expression
23752400
p.nextToken() // consume (
23762401

23772402
// Parse inner boolean expression
@@ -2567,6 +2592,51 @@ func (p *Parser) parseBooleanPrimaryExpression() (ast.BooleanExpression, error)
25672592
}, nil
25682593
}
25692594

2595+
// isComparisonOperator checks if the current token is a comparison operator
2596+
func (p *Parser) isComparisonOperator() bool {
2597+
switch p.curTok.Type {
2598+
case TokenEquals, TokenNotEqual, TokenLessThan, TokenGreaterThan,
2599+
TokenLessOrEqual, TokenGreaterOrEqual:
2600+
return true
2601+
default:
2602+
return false
2603+
}
2604+
}
2605+
2606+
// parseComparisonAfterLeft parses a comparison expression after the left operand is already parsed
2607+
func (p *Parser) parseComparisonAfterLeft(left ast.ScalarExpression) (ast.BooleanExpression, error) {
2608+
var compType string
2609+
switch p.curTok.Type {
2610+
case TokenEquals:
2611+
compType = "Equals"
2612+
case TokenNotEqual:
2613+
compType = "NotEqualToBrackets"
2614+
case TokenLessThan:
2615+
compType = "LessThan"
2616+
case TokenGreaterThan:
2617+
compType = "GreaterThan"
2618+
case TokenLessOrEqual:
2619+
compType = "LessThanOrEqualTo"
2620+
case TokenGreaterOrEqual:
2621+
compType = "GreaterThanOrEqualTo"
2622+
default:
2623+
return nil, fmt.Errorf("expected comparison operator, got %s", p.curTok.Literal)
2624+
}
2625+
p.nextToken()
2626+
2627+
// Parse right scalar expression
2628+
right, err := p.parseScalarExpression()
2629+
if err != nil {
2630+
return nil, err
2631+
}
2632+
2633+
return &ast.BooleanComparisonExpression{
2634+
ComparisonType: compType,
2635+
FirstExpression: left,
2636+
SecondExpression: right,
2637+
}, nil
2638+
}
2639+
25702640
// identifiersToSchemaObjectName converts a slice of identifiers to a SchemaObjectName.
25712641
// For 1 identifier: BaseIdentifier
25722642
// For 2 identifiers: SchemaIdentifier.BaseIdentifier
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"todo": true}
1+
{}

0 commit comments

Comments
 (0)