Skip to content

Commit 74d4036

Browse files
fix: cte and subquery relation name and columns should be registered on query catalog
1 parent a05cfce commit 74d4036

File tree

18 files changed

+467
-7
lines changed

18 files changed

+467
-7
lines changed

internal/compiler/analyze.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ func (c *Compiler) _analyzeQuery(raw *ast.RawStmt, query string, failfast bool)
154154
return nil, err
155155
}
156156
rvs := rangeVars(raw.Stmt)
157+
rss := rangeSubSelects(raw.Stmt)
157158
refs, errs := findParameters(raw.Stmt)
158159
if len(errs) > 0 {
159160
if failfast {
@@ -173,7 +174,7 @@ func (c *Compiler) _analyzeQuery(raw *ast.RawStmt, query string, failfast bool)
173174
return nil, err
174175
}
175176

176-
params, err := c.resolveCatalogRefs(qc, rvs, refs, namedParams, embeds)
177+
params, err := c.resolveCatalogRefs(qc, rvs, refs, namedParams, embeds, rss)
177178
if err := check(err); err != nil {
178179
return nil, err
179180
}

internal/compiler/compat.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,15 @@ func parseRelation(node ast.Node) (*Relation, error) {
8484
return &Relation{Name: n.Name}, nil
8585
}
8686

87+
case *ast.RangeSubselect:
88+
if n == nil {
89+
return nil, fmt.Errorf("unexpected nil in %T node", n)
90+
}
91+
if n.Alias != nil && n.Alias.Aliasname != nil {
92+
return &Relation{Name: *n.Alias.Aliasname}, nil
93+
}
94+
return nil, fmt.Errorf("no alias in subquery")
95+
8796
default:
8897
return nil, fmt.Errorf("unexpected node type: %T", node)
8998
}

internal/compiler/parse.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,18 @@ func rangeVars(root ast.Node) []*ast.RangeVar {
144144
return vars
145145
}
146146

147+
func rangeSubSelects(root ast.Node) []*ast.RangeSubselect {
148+
var rss []*ast.RangeSubselect
149+
find := astutils.VisitorFunc(func(node ast.Node) {
150+
switch n := node.(type) {
151+
case *ast.RangeSubselect:
152+
rss = append(rss, n)
153+
}
154+
})
155+
astutils.Walk(find, root)
156+
return rss
157+
}
158+
147159
func uniqueParamRefs(in []paramRef, dollar bool) []paramRef {
148160
m := make(map[int]bool, len(in))
149161
o := make([]paramRef, 0, len(in))

internal/compiler/query.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,32 @@ type Parameter struct {
5959
Number int
6060
Column *Column
6161
}
62+
63+
func (t *Table) toCatalogTable() catalog.Table {
64+
var catalogCols []*catalog.Column
65+
for _, qcol := range t.Columns {
66+
catalogColType := ast.TypeName{}
67+
if qcol.Type != nil {
68+
catalogColType = *qcol.Type
69+
}
70+
71+
catalogCol := &catalog.Column{
72+
Name: qcol.Name,
73+
Type: catalogColType,
74+
IsNotNull: qcol.NotNull,
75+
IsUnsigned: qcol.Unsigned,
76+
IsArray: qcol.IsArray,
77+
ArrayDims: qcol.ArrayDims,
78+
Comment: qcol.Comment,
79+
Length: qcol.Length,
80+
}
81+
82+
catalogCols = append(catalogCols, catalogCol)
83+
}
84+
85+
return catalog.Table{
86+
Rel: t.Rel,
87+
Columns: catalogCols,
88+
Comment: "",
89+
}
90+
}

internal/compiler/resolve.go

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ func dataType(n *ast.TypeName) string {
2121
}
2222
}
2323

24-
func (comp *Compiler) resolveCatalogRefs(qc *QueryCatalog, rvs []*ast.RangeVar, args []paramRef, params *named.ParamSet, embeds rewrite.EmbedSet) ([]Parameter, error) {
24+
func (comp *Compiler) resolveCatalogRefs(qc *QueryCatalog, rvs []*ast.RangeVar, args []paramRef, params *named.ParamSet,
25+
embeds rewrite.EmbedSet, rss []*ast.RangeSubselect) ([]Parameter, error) {
2526
c := comp.catalog
2627

2728
aliasMap := map[string]*ast.TableName{}
@@ -67,10 +68,12 @@ func (comp *Compiler) resolveCatalogRefs(qc *QueryCatalog, rvs []*ast.RangeVar,
6768
continue
6869
}
6970
// If the table name doesn't exist, first check if it's a CTE
70-
if _, qcerr := qc.GetTable(fqn); qcerr != nil {
71-
return nil, err
71+
var qcTable *Table
72+
var qcerr error
73+
if qcTable, qcerr = qc.GetTable(fqn); qcerr != nil {
74+
return nil, qcerr
7275
}
73-
continue
76+
table = qcTable.toCatalogTable()
7477
}
7578
err = indexTable(table)
7679
if err != nil {
@@ -81,6 +84,23 @@ func (comp *Compiler) resolveCatalogRefs(qc *QueryCatalog, rvs []*ast.RangeVar,
8184
}
8285
}
8386

87+
for _, rs := range rss {
88+
fqn, err := ParseTableName(rs)
89+
if err != nil {
90+
return nil, err
91+
}
92+
93+
cols, err := comp.outputColumns(qc, rs.Subquery)
94+
if err != nil {
95+
return nil, err
96+
}
97+
rsTable := Table{Rel: fqn, Columns: cols}
98+
err = indexTable(rsTable.toCatalogTable())
99+
if err != nil {
100+
return nil, err
101+
}
102+
}
103+
84104
// resolve a table for an embed
85105
for _, embed := range embeds {
86106
table, err := c.GetTable(embed.Table)
@@ -268,7 +288,6 @@ func (comp *Compiler) resolveCatalogRefs(qc *QueryCatalog, rvs []*ast.RangeVar,
268288

269289
case *ast.BetweenExpr:
270290
if n == nil || n.Expr == nil || n.Left == nil || n.Right == nil {
271-
fmt.Println("ast.BetweenExpr is nil")
272291
continue
273292
}
274293

@@ -527,7 +546,6 @@ func (comp *Compiler) resolveCatalogRefs(qc *QueryCatalog, rvs []*ast.RangeVar,
527546

528547
case *ast.In:
529548
if n == nil || n.List == nil {
530-
fmt.Println("ast.In is nil")
531549
continue
532550
}
533551

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
https://github.com/sqlc-dev/sqlc/issues/3720

internal/endtoend/testdata/cte_and_subquery_column_alias/mysql/db/db.go

Lines changed: 31 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/endtoend/testdata/cte_and_subquery_column_alias/mysql/db/models.go

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/endtoend/testdata/cte_and_subquery_column_alias/mysql/db/query.sql.go

Lines changed: 106 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
-- name: GetFullNames :many
2+
SELECT
3+
full_name
4+
FROM
5+
(
6+
SELECT
7+
concat(first_name, ' ', last_name) as full_name
8+
FROM
9+
customers
10+
) subquery
11+
WHERE
12+
full_name IN (sqlc.slice ("full_names"));
13+
14+
-- name: GetFullNames2 :many
15+
WITH subquery AS (
16+
SELECT
17+
concat(first_name, ' ', last_name) as full_name
18+
FROM
19+
customers
20+
)
21+
SELECT
22+
full_name
23+
FROM
24+
subquery
25+
WHERE
26+
full_name IN (sqlc.slice ("full_names"));
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
CREATE TABLE customers (
2+
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
3+
first_name varchar(255) not null,
4+
last_name varchar(255) not null
5+
) ENGINE = INNODB DEFAULT CHARSET = utf8mb4;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"version": "2",
3+
"sql": [
4+
{
5+
"schema": "schema.sql",
6+
"queries": "query.sql",
7+
"engine": "mysql",
8+
"gen": {
9+
"go": {
10+
"package": "db",
11+
"out": "db"
12+
}
13+
}
14+
}
15+
]
16+
}

internal/endtoend/testdata/cte_and_subquery_column_alias/postgresql/db/db.go

Lines changed: 31 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)