From 0056e75d396227cb23b14b4a5badcf44df68d2da Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 30 May 2025 15:12:20 +0000 Subject: [PATCH 1/3] Initial plan for issue From 22f9925b5df059691c687463a6ff54b9182b6f35 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 30 May 2025 15:15:57 +0000 Subject: [PATCH 2/3] Fix parsing failure with parenthesis in WHERE clause Co-authored-by: otoolep <536312+otoolep@users.noreply.github.com> --- parser.go | 42 +++++++++++++++++++++++++++++++++++------- simple_test.go | 18 ++++++++++++++++++ update_test.go | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 7 deletions(-) create mode 100644 simple_test.go create mode 100644 update_test.go diff --git a/parser.go b/parser.go index b8dcfaee..160f78db 100644 --- a/parser.go +++ b/parser.go @@ -2807,14 +2807,42 @@ func (p *Parser) parseFrameSpec() (_ *FrameSpec, err error) { return &spec, nil } -func (p *Parser) parseParenExpr() (_ *ParenExpr, err error) { - var expr ParenExpr - expr.Lparen, _, _ = p.scan() - if expr.X, err = p.ParseExpr(); err != nil { - return &expr, err +func (p *Parser) parseParenExpr() (Expr, error) { + lparen, _, _ := p.scan() + + // Parse the first expression + x, err := p.ParseExpr() + if err != nil { + return nil, err } - expr.Rparen, _, _ = p.scan() - return &expr, nil + + // If there's no comma after the first expression, treat it as a normal parenthesized expression + if p.peek() != COMMA { + rparen, _, _ := p.scan() + return &ParenExpr{Lparen: lparen, X: x, Rparen: rparen}, nil + } + + // If there's a comma, we're dealing with an expression list + var list ExprList + list.Lparen = lparen + list.Exprs = append(list.Exprs, x) + + for p.peek() == COMMA { + p.scan() // consume the comma + + expr, err := p.ParseExpr() + if err != nil { + return &list, err + } + list.Exprs = append(list.Exprs, expr) + } + + if p.peek() != RP { + return &list, p.errorExpected(p.pos, p.tok, "right paren") + } + list.Rparen, _, _ = p.scan() + + return &list, nil } func (p *Parser) parseCastExpr() (_ *CastExpr, err error) { diff --git a/simple_test.go b/simple_test.go new file mode 100644 index 00000000..81d62423 --- /dev/null +++ b/simple_test.go @@ -0,0 +1,18 @@ +package sql + +import ( + "strings" + "testing" +) + +func TestSimpleParenList(t *testing.T) { + s := `UPDATE table1 SET col1 = 'value' WHERE (col1, col2) = ('a', 'b')` + stmt, err := NewParser(strings.NewReader(s)).ParseStatement() + if err != nil { + t.Fatalf("failed: %v", err) + } + _, ok := stmt.(*UpdateStatement) + if !ok { + t.Fatalf("failed: expected UpdateStatement") + } +} \ No newline at end of file diff --git a/update_test.go b/update_test.go new file mode 100644 index 00000000..ccd2beb8 --- /dev/null +++ b/update_test.go @@ -0,0 +1,37 @@ +package sql + +import ( + "strings" + "testing" +) + +func TestUpdate(t *testing.T) { + s := `UPDATE asynq_tasks +SET state='active', + pending_since=NULL, + affinity_timeout=server_affinity, + deadline=iif(task_deadline=0, task_timeout+1687276020, task_deadline) +WHERE asynq_tasks.state='pending' + AND (task_uuid, + ndx, + pndx, + task_msg, + task_timeout, + task_deadline)= + (SELECT task_uuid, + ndx, + pndx, + task_msg, + task_timeout, + task_deadline + FROM asynq_tasks) +` + stmt, err := NewParser(strings.NewReader(s)).ParseStatement() + if err != nil { + t.Fatalf("failed: %v", err) + } + _, ok := stmt.(*UpdateStatement) + if !ok { + t.Fatalf("failed: expected UpdateStatement") + } +} \ No newline at end of file From 3825dc96d5c838030b8003af7434657810f42ce0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 30 May 2025 15:40:11 +0000 Subject: [PATCH 3/3] Run go fmt ./... to format code Co-authored-by: otoolep <536312+otoolep@users.noreply.github.com> --- parser.go | 14 +++++++------- simple_test.go | 2 +- update_test.go | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/parser.go b/parser.go index 160f78db..2430e020 100644 --- a/parser.go +++ b/parser.go @@ -2809,39 +2809,39 @@ func (p *Parser) parseFrameSpec() (_ *FrameSpec, err error) { func (p *Parser) parseParenExpr() (Expr, error) { lparen, _, _ := p.scan() - + // Parse the first expression x, err := p.ParseExpr() if err != nil { return nil, err } - + // If there's no comma after the first expression, treat it as a normal parenthesized expression if p.peek() != COMMA { rparen, _, _ := p.scan() return &ParenExpr{Lparen: lparen, X: x, Rparen: rparen}, nil } - + // If there's a comma, we're dealing with an expression list var list ExprList list.Lparen = lparen list.Exprs = append(list.Exprs, x) - + for p.peek() == COMMA { p.scan() // consume the comma - + expr, err := p.ParseExpr() if err != nil { return &list, err } list.Exprs = append(list.Exprs, expr) } - + if p.peek() != RP { return &list, p.errorExpected(p.pos, p.tok, "right paren") } list.Rparen, _, _ = p.scan() - + return &list, nil } diff --git a/simple_test.go b/simple_test.go index 81d62423..74ed2acd 100644 --- a/simple_test.go +++ b/simple_test.go @@ -15,4 +15,4 @@ func TestSimpleParenList(t *testing.T) { if !ok { t.Fatalf("failed: expected UpdateStatement") } -} \ No newline at end of file +} diff --git a/update_test.go b/update_test.go index ccd2beb8..bcb4d8ef 100644 --- a/update_test.go +++ b/update_test.go @@ -34,4 +34,4 @@ WHERE asynq_tasks.state='pending' if !ok { t.Fatalf("failed: expected UpdateStatement") } -} \ No newline at end of file +}