diff --git a/internal/compiler/output_columns.go b/internal/compiler/output_columns.go index dbd486359a..7876961316 100644 --- a/internal/compiler/output_columns.go +++ b/internal/compiler/output_columns.go @@ -424,6 +424,13 @@ func isTableRequired(n ast.Node, col *Column, prior int) int { if aliasMatch && tableMatch { return prior } + case *ast.RangeSubselect: + // For subqueries, match only by alias since there's no table name + if n.Alias != nil && col.TableAlias != "" { + if *n.Alias.Aliasname == col.TableAlias { + return prior + } + } case *ast.JoinExpr: helper := func(l, r int) int { if res := isTableRequired(n.Larg, col, l); res != tableNotFound { diff --git a/internal/endtoend/testdata/join_left_subquery/postgresql/go/db.go b/internal/endtoend/testdata/join_left_subquery/postgresql/go/db.go new file mode 100644 index 0000000000..3b320aa168 --- /dev/null +++ b/internal/endtoend/testdata/join_left_subquery/postgresql/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 + +package querytest + +import ( + "context" + "database/sql" +) + +type DBTX interface { + ExecContext(context.Context, string, ...interface{}) (sql.Result, error) + PrepareContext(context.Context, string) (*sql.Stmt, error) + QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) + QueryRowContext(context.Context, string, ...interface{}) *sql.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/join_left_subquery/postgresql/go/models.go b/internal/endtoend/testdata/join_left_subquery/postgresql/go/models.go new file mode 100644 index 0000000000..90818430f4 --- /dev/null +++ b/internal/endtoend/testdata/join_left_subquery/postgresql/go/models.go @@ -0,0 +1,22 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 + +package querytest + +import ( + "database/sql" + + "github.com/google/uuid" +) + +type A struct { + ID uuid.UUID + Name sql.NullString +} + +type B struct { + ID uuid.UUID + AID uuid.UUID + Name sql.NullString +} diff --git a/internal/endtoend/testdata/join_left_subquery/postgresql/go/query.sql.go b/internal/endtoend/testdata/join_left_subquery/postgresql/go/query.sql.go new file mode 100644 index 0000000000..09f2ffa4c8 --- /dev/null +++ b/internal/endtoend/testdata/join_left_subquery/postgresql/go/query.sql.go @@ -0,0 +1,229 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: query.sql + +package querytest + +import ( + "context" + "database/sql" + + "github.com/google/uuid" +) + +const fullOuterJoinSubquery = `-- name: FullOuterJoinSubquery :many +SELECT table_a.id, table_a.name, si.id, a_id, si.name FROM a AS table_a +FULL OUTER JOIN (SELECT id, a_id, name FROM b WHERE b.name IS NOT NULL) si ON si.a_id = table_a.id +` + +type FullOuterJoinSubqueryRow struct { + ID uuid.NullUUID + Name sql.NullString + ID_2 uuid.NullUUID + AID uuid.NullUUID + Name_2 sql.NullString +} + +func (q *Queries) FullOuterJoinSubquery(ctx context.Context) ([]FullOuterJoinSubqueryRow, error) { + rows, err := q.db.QueryContext(ctx, fullOuterJoinSubquery) + if err != nil { + return nil, err + } + defer rows.Close() + var items []FullOuterJoinSubqueryRow + for rows.Next() { + var i FullOuterJoinSubqueryRow + if err := rows.Scan( + &i.ID, + &i.Name, + &i.ID_2, + &i.AID, + &i.Name_2, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const leftJoinSubquery = `-- name: LeftJoinSubquery :many +SELECT table_a.id, table_a.name, si.id, a_id, si.name FROM a AS table_a +LEFT JOIN (SELECT id, a_id, name FROM b WHERE b.name IS NOT NULL) si ON si.a_id = table_a.id +` + +type LeftJoinSubqueryRow struct { + ID uuid.UUID + Name sql.NullString + ID_2 uuid.NullUUID + AID uuid.NullUUID + Name_2 sql.NullString +} + +func (q *Queries) LeftJoinSubquery(ctx context.Context) ([]LeftJoinSubqueryRow, error) { + rows, err := q.db.QueryContext(ctx, leftJoinSubquery) + if err != nil { + return nil, err + } + defer rows.Close() + var items []LeftJoinSubqueryRow + for rows.Next() { + var i LeftJoinSubqueryRow + if err := rows.Scan( + &i.ID, + &i.Name, + &i.ID_2, + &i.AID, + &i.Name_2, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const leftJoinSubqueryExplicitColumns = `-- name: LeftJoinSubqueryExplicitColumns :many +SELECT + table_a.id, + table_a.name, + si.id, + si.a_id, + si.name +FROM a AS table_a +LEFT JOIN (SELECT id, a_id, name FROM b WHERE b.name IS NOT NULL) si ON si.a_id = table_a.id +` + +type LeftJoinSubqueryExplicitColumnsRow struct { + ID uuid.UUID + Name sql.NullString + ID_2 uuid.NullUUID + AID uuid.NullUUID + Name_2 sql.NullString +} + +func (q *Queries) LeftJoinSubqueryExplicitColumns(ctx context.Context) ([]LeftJoinSubqueryExplicitColumnsRow, error) { + rows, err := q.db.QueryContext(ctx, leftJoinSubqueryExplicitColumns) + if err != nil { + return nil, err + } + defer rows.Close() + var items []LeftJoinSubqueryExplicitColumnsRow + for rows.Next() { + var i LeftJoinSubqueryExplicitColumnsRow + if err := rows.Scan( + &i.ID, + &i.Name, + &i.ID_2, + &i.AID, + &i.Name_2, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const leftJoinSubqueryNoAlias = `-- name: LeftJoinSubqueryNoAlias :many +SELECT a.id, a.name, subquery.id, a_id, subquery.name FROM a +LEFT JOIN (SELECT id, a_id, name FROM b) subquery ON subquery.a_id = a.id +` + +type LeftJoinSubqueryNoAliasRow struct { + ID uuid.UUID + Name sql.NullString + ID_2 uuid.NullUUID + AID uuid.NullUUID + Name_2 sql.NullString +} + +func (q *Queries) LeftJoinSubqueryNoAlias(ctx context.Context) ([]LeftJoinSubqueryNoAliasRow, error) { + rows, err := q.db.QueryContext(ctx, leftJoinSubqueryNoAlias) + if err != nil { + return nil, err + } + defer rows.Close() + var items []LeftJoinSubqueryNoAliasRow + for rows.Next() { + var i LeftJoinSubqueryNoAliasRow + if err := rows.Scan( + &i.ID, + &i.Name, + &i.ID_2, + &i.AID, + &i.Name_2, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const rightJoinSubquery = `-- name: RightJoinSubquery :many +SELECT table_a.id, table_a.name, si.id, a_id, si.name FROM a AS table_a +RIGHT JOIN (SELECT id, a_id, name FROM b WHERE b.name IS NOT NULL) si ON si.a_id = table_a.id +` + +type RightJoinSubqueryRow struct { + ID uuid.NullUUID + Name sql.NullString + ID_2 uuid.UUID + AID uuid.UUID + Name_2 sql.NullString +} + +func (q *Queries) RightJoinSubquery(ctx context.Context) ([]RightJoinSubqueryRow, error) { + rows, err := q.db.QueryContext(ctx, rightJoinSubquery) + if err != nil { + return nil, err + } + defer rows.Close() + var items []RightJoinSubqueryRow + for rows.Next() { + var i RightJoinSubqueryRow + if err := rows.Scan( + &i.ID, + &i.Name, + &i.ID_2, + &i.AID, + &i.Name_2, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/endtoend/testdata/join_left_subquery/postgresql/query.sql b/internal/endtoend/testdata/join_left_subquery/postgresql/query.sql new file mode 100644 index 0000000000..6a3543c43a --- /dev/null +++ b/internal/endtoend/testdata/join_left_subquery/postgresql/query.sql @@ -0,0 +1,25 @@ +-- name: LeftJoinSubquery :many +SELECT * FROM a AS table_a +LEFT JOIN (SELECT * FROM b WHERE b.name IS NOT NULL) si ON si.a_id = table_a.id; + +-- name: LeftJoinSubqueryExplicitColumns :many +SELECT + table_a.id, + table_a.name, + si.id, + si.a_id, + si.name +FROM a AS table_a +LEFT JOIN (SELECT id, a_id, name FROM b WHERE b.name IS NOT NULL) si ON si.a_id = table_a.id; + +-- name: LeftJoinSubqueryNoAlias :many +SELECT * FROM a +LEFT JOIN (SELECT * FROM b) subquery ON subquery.a_id = a.id; + +-- name: RightJoinSubquery :many +SELECT * FROM a AS table_a +RIGHT JOIN (SELECT * FROM b WHERE b.name IS NOT NULL) si ON si.a_id = table_a.id; + +-- name: FullOuterJoinSubquery :many +SELECT * FROM a AS table_a +FULL OUTER JOIN (SELECT * FROM b WHERE b.name IS NOT NULL) si ON si.a_id = table_a.id; diff --git a/internal/endtoend/testdata/join_left_subquery/postgresql/schema.sql b/internal/endtoend/testdata/join_left_subquery/postgresql/schema.sql new file mode 100644 index 0000000000..d7bf35f9ee --- /dev/null +++ b/internal/endtoend/testdata/join_left_subquery/postgresql/schema.sql @@ -0,0 +1,11 @@ +-- https://github.com/sqlc-dev/sqlc/issues/4117 +CREATE TABLE a ( + id uuid PRIMARY KEY, + name TEXT +); + +CREATE TABLE b ( + id uuid PRIMARY KEY, + a_id uuid NOT NULL REFERENCES a (id), + name TEXT +); diff --git a/internal/endtoend/testdata/join_left_subquery/postgresql/sqlc.json b/internal/endtoend/testdata/join_left_subquery/postgresql/sqlc.json new file mode 100644 index 0000000000..f717ca2e66 --- /dev/null +++ b/internal/endtoend/testdata/join_left_subquery/postgresql/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "postgresql", + "name": "querytest", + "schema": "schema.sql", + "queries": "query.sql" + } + ] +}