Skip to content

Commit 6274819

Browse files
Ajit Pratap Singhclaude
authored andcommitted
fix: correct NOT token handling with look-ahead parsing
Address NOT token handling issue (same fix as PR #117). Changes: - Add peekToken() helper for look-ahead without advancing position - Fix parseComparisonExpression() to only consume NOT when followed by BETWEEN, LIKE, ILIKE, or IN operators - Update NOT case in parsePrimaryExpression() to use UnaryExpression with parseComparisonExpression for proper precedence This properly handles all NOT use cases: - NOT LIKE: WHERE name NOT LIKE '%Admin%' - NOT BETWEEN: WHERE price NOT BETWEEN 10 AND 100 - NOT IN: WHERE status NOT IN ('cancelled', 'deleted') - Boolean negation: WHERE NOT active AND name LIKE '%' - NOT EXISTS: WHERE NOT EXISTS (SELECT ...) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 9d7244b commit 6274819

1 file changed

Lines changed: 23 additions & 9 deletions

File tree

pkg/sql/parser/parser.go

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,16 @@ func (p *Parser) advance() {
238238
}
239239
}
240240

241+
// peekToken returns the next token without advancing the parser position.
242+
// Returns an empty token if at the end of input.
243+
func (p *Parser) peekToken() token.Token {
244+
nextPos := p.currentPos + 1
245+
if nextPos < len(p.tokens) {
246+
return p.tokens[nextPos]
247+
}
248+
return token.Token{}
249+
}
250+
241251
// expectedError returns an error for unexpected token
242252
func (p *Parser) expectedError(expected string) error {
243253
return fmt.Errorf("expected %s, got %s", expected, p.currentToken.Type)
@@ -361,10 +371,15 @@ func (p *Parser) parseComparisonExpression() (ast.Expression, error) {
361371
}
362372

363373
// Check for NOT prefix for BETWEEN, LIKE, IN operators
374+
// Only consume NOT if followed by BETWEEN, LIKE, ILIKE, or IN
375+
// This prevents breaking cases like: WHERE NOT active AND name LIKE '%'
364376
notPrefix := false
365377
if p.currentToken.Type == "NOT" {
366-
notPrefix = true
367-
p.advance()
378+
nextToken := p.peekToken()
379+
if nextToken.Type == "BETWEEN" || nextToken.Type == "LIKE" || nextToken.Type == "ILIKE" || nextToken.Type == "IN" {
380+
notPrefix = true
381+
p.advance() // Consume NOT only if followed by valid operator
382+
}
368383
}
369384

370385
// Check for BETWEEN operator
@@ -734,16 +749,15 @@ func (p *Parser) parsePrimaryExpression() (ast.Expression, error) {
734749
}, nil
735750
}
736751

737-
// NOT followed by other expression
738-
expr, err := p.parsePrimaryExpression()
752+
// NOT followed by other expression (boolean negation)
753+
// Parse at comparison level for proper precedence: NOT (a > b), NOT active
754+
expr, err := p.parseComparisonExpression()
739755
if err != nil {
740756
return nil, err
741757
}
742-
return &ast.BinaryExpression{
743-
Left: expr,
744-
Operator: "NOT",
745-
Right: nil,
746-
Not: true,
758+
return &ast.UnaryExpression{
759+
Operator: ast.Not,
760+
Expr: expr,
747761
}, nil
748762

749763
default:

0 commit comments

Comments
 (0)