Skip to content

Commit 2811800

Browse files
committed
fix(dolphin): preserve NOT EXISTS expressions
1 parent e209d86 commit 2811800

2 files changed

Lines changed: 79 additions & 1 deletion

File tree

internal/engine/dolphin/convert.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,13 +397,22 @@ func (c *cc) convertRenameTableStmt(n *pcast.RenameTableStmt) ast.Node {
397397
return list
398398
}
399399

400-
func (c *cc) convertExistsSubqueryExpr(n *pcast.ExistsSubqueryExpr) *ast.SubLink {
400+
func (c *cc) convertExistsSubqueryExpr(n *pcast.ExistsSubqueryExpr) ast.Node {
401401
sublink := &ast.SubLink{
402402
SubLinkType: ast.EXISTS_SUBLINK,
403403
}
404404
if n.Sel != nil {
405405
sublink.Subselect = c.convert(n.Sel)
406406
}
407+
if n.Not {
408+
return &ast.BoolExpr{
409+
Boolop: ast.BoolExprTypeNot,
410+
Args: &ast.List{
411+
Items: []ast.Node{sublink},
412+
},
413+
Location: n.OriginTextPosition(),
414+
}
415+
}
407416
return sublink
408417
}
409418

@@ -1556,6 +1565,15 @@ func (c *cc) convertTruncateTableStmt(n *pcast.TruncateTableStmt) *ast.TruncateS
15561565
}
15571566

15581567
func (c *cc) convertUnaryOperationExpr(n *pcast.UnaryOperationExpr) ast.Node {
1568+
if n.Op == opcode.Not {
1569+
return &ast.BoolExpr{
1570+
Boolop: ast.BoolExprTypeNot,
1571+
Args: &ast.List{
1572+
Items: []ast.Node{c.convert(n.V)},
1573+
},
1574+
Location: n.OriginTextPosition(),
1575+
}
1576+
}
15591577
return todo(n)
15601578
}
15611579

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package dolphin
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/sqlc-dev/sqlc/internal/sql/ast"
8+
)
9+
10+
func TestConvertNotExistsSubquery(t *testing.T) {
11+
const query = `UPDATE target_table
12+
SET active = 0
13+
WHERE NOT (
14+
EXISTS (
15+
SELECT 1
16+
FROM source_table s
17+
WHERE s.updated_at >= sqlc.arg(updated_after)
18+
)
19+
)`
20+
21+
parser := NewParser()
22+
stmts, err := parser.Parse(strings.NewReader(query))
23+
if err != nil {
24+
t.Fatal(err)
25+
}
26+
if len(stmts) != 1 {
27+
t.Fatalf("expected one statement, got %d", len(stmts))
28+
}
29+
30+
formatted := ast.Format(stmts[0].Raw, parser)
31+
if !strings.Contains(formatted, "NOT EXISTS") {
32+
t.Errorf("expected NOT EXISTS to be preserved, got:\n%s", formatted)
33+
}
34+
if !strings.Contains(formatted, "sqlc.arg(updated_after)") {
35+
t.Errorf("expected named parameter to be preserved, got:\n%s", formatted)
36+
}
37+
}
38+
39+
func TestConvertNotExistsSubqueryWithoutParentheses(t *testing.T) {
40+
const query = `SELECT 1
41+
WHERE NOT EXISTS (
42+
SELECT 1
43+
FROM source_table s
44+
WHERE s.updated_at >= sqlc.arg(updated_after)
45+
)`
46+
47+
parser := NewParser()
48+
stmts, err := parser.Parse(strings.NewReader(query))
49+
if err != nil {
50+
t.Fatal(err)
51+
}
52+
if len(stmts) != 1 {
53+
t.Fatalf("expected one statement, got %d", len(stmts))
54+
}
55+
56+
formatted := ast.Format(stmts[0].Raw, parser)
57+
if !strings.Contains(formatted, "NOT EXISTS") {
58+
t.Errorf("expected NOT EXISTS to be preserved, got:\n%s", formatted)
59+
}
60+
}

0 commit comments

Comments
 (0)