Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 81 additions & 3 deletions internal/compiler/output_columns.go
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,13 @@ func (c *Compiler) outputColumns(qc *QueryCatalog, node ast.Node) ([]*Column, er
if res.Name != nil {
name = *res.Name
}
if n.Args != nil {
for _, arg := range n.Args.Items {
if err := checkStarRelation(qc, node, arg, tables, rtables); err != nil {
return nil, err
}
}
}
fun, err := qc.catalog.ResolveFuncCall(n)
if err == nil {
cols = append(cols, &Column{
Expand All @@ -345,7 +352,7 @@ func (c *Compiler) outputColumns(qc *QueryCatalog, node ast.Node) ([]*Column, er
case ast.EXISTS_SUBLINK:
cols = append(cols, &Column{Name: name, DataType: "bool", NotNull: true})
case ast.EXPR_SUBLINK:
subcols, err := c.outputColumns(qc, n.Subselect)
subcols, err := c.subqueryOutputColumns(qc, n.Subselect, tables)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -381,7 +388,7 @@ func (c *Compiler) outputColumns(qc *QueryCatalog, node ast.Node) ([]*Column, er
cols = append(cols, col)

case *ast.SelectStmt:
subcols, err := c.outputColumns(qc, n)
subcols, err := c.subqueryOutputColumns(qc, n, tables)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -610,7 +617,7 @@ func (c *Compiler) sourceTables(qc *QueryCatalog, node ast.Node) ([]*Table, erro
tables = append(tables, table)

case *ast.RangeSubselect:
cols, err := c.outputColumns(qc, n.Subquery)
cols, err := c.subqueryOutputColumns(qc, n.Subquery, tables)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -658,6 +665,77 @@ func (c *Compiler) sourceTables(qc *QueryCatalog, node ast.Node) ([]*Table, erro
return tables, nil
}

// subqueryOutputColumns computes the output columns of a nested statement.
// The relations of the enclosing statement stay visible to it, because a
// correlated subquery may refer to them.
func (c *Compiler) subqueryOutputColumns(qc *QueryCatalog, node ast.Node, outer []*Table) ([]*Column, error) {
saved := qc.outer
qc.outer = append(append([]*Table{}, saved...), outer...)
defer func() { qc.outer = saved }()
return c.outputColumns(qc, node)
}

// fromNameVisitor collects the names that FROM items of a statement can be
// referred to by, including set-returning functions that sqlc cannot resolve
// and therefore does not register as tables.
type fromNameVisitor struct {
names map[string]struct{}
}

func (v *fromNameVisitor) Visit(n ast.Node) astutils.Visitor {
var alias *ast.Alias
switch n := n.(type) {
case *ast.RangeVar:
alias = n.Alias
if alias == nil && n.Relname != nil {
v.names[*n.Relname] = struct{}{}
}
case *ast.RangeFunction:
alias = n.Alias
case *ast.RangeSubselect:
alias = n.Alias
}
if alias != nil && alias.Aliasname != nil {
v.names[*alias.Aliasname] = struct{}{}
}
return v
}

// checkStarRelation returns an error when arg is a qualified star such as
// "whatever.*" and the qualifier does not name a relation in scope. It
// reports the same error as other references to an unknown table alias.
func checkStarRelation(qc *QueryCatalog, node, arg ast.Node, tables, rtables []*Table) error {
ref, ok := arg.(*ast.ColumnRef)
if !ok || !hasStarRef(ref) {
return nil
}
parts := stringSlice(ref.Fields)
if len(parts) != 1 {
return nil
}
scope := parts[0]
for _, list := range [][]*Table{tables, qc.outer} {
for _, t := range list {
if t.Rel != nil && t.Rel.Name == scope {
return nil
}
}
}
if returningTableForScope(tables, rtables, scope) != nil {
return nil
}
v := &fromNameVisitor{names: map[string]struct{}{}}
astutils.Walk(v, node)
if _, ok := v.names[scope]; ok {
return nil
}
return &sqlerr.Error{
Code: "42703",
Message: fmt.Sprintf("table alias %q does not exist", scope),
Location: ref.Location,
}
}

func outputColumnRefs(res *ast.ResTarget, tables []*Table, node *ast.ColumnRef) ([]*Column, error) {
parts := stringSlice(node.Fields)
var schema, name, alias string
Expand Down
4 changes: 4 additions & 0 deletions internal/compiler/query_catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ type QueryCatalog struct {
catalog *catalog.Catalog
ctes map[string]*Table
embeds preprocess.EmbedSet

// outer holds the relations of the enclosing statements while the output
// columns of a nested statement are computed.
outer []*Table
}

func (comp *Compiler) buildQueryCatalog(c *catalog.Catalog, node ast.Node, embeds preprocess.EmbedSet) (*QueryCatalog, error) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"contexts": ["base"]
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- name: Correlated :many
SELECT (SELECT ARRAY_AGG(o.*) FROM b) FROM o;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
CREATE TABLE o (
x INT NOT NULL
);

CREATE TABLE b (
x INT NOT NULL
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": "1",
"packages": [
{
"path": "go",
"engine": "postgresql",
"name": "querytest",
"schema": "schema.sql",
"queries": "query.sql"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- name: BugDemo :exec
SELECT id, ARRAY_AGG(whatever.*) FROM worker;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CREATE TABLE worker (
id INT NOT NULL
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": "1",
"packages": [
{
"path": "go",
"engine": "postgresql",
"name": "querytest",
"schema": "schema.sql",
"queries": "query.sql"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# package querytest
query.sql:2:22: table alias "whatever" does not exist
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# package querytest
query.sql:2:22: missing FROM-clause entry for table "whatever"
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"contexts": ["base"]
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- name: UnresolvedFuncAlias :exec
SELECT ARRAY_AGG(u.*) FROM some_unknown_func() AS u;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CREATE TABLE o (
x INT NOT NULL
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": "1",
"packages": [
{
"path": "go",
"engine": "postgresql",
"name": "querytest",
"schema": "schema.sql",
"queries": "query.sql"
}
]
}
Loading