Files
upstream-sqlc/internal/compiler/expand_core.go
upstream-mirror 0f4df23290
Some checks failed
kotlin / test (push) Has been skipped
python / test (push) Has been skipped
typescript / test (push) Has been skipped
go / build darwin/amd64 (push) Has been cancelled
go / build linux/amd64 (push) Has been cancelled
go / build windows/amd64 (push) Has been cancelled
go / build darwin/arm64 (push) Has been cancelled
go / build linux/arm64 (push) Has been cancelled
go / build windows/arm64 (push) Has been cancelled
go / test (push) Has been cancelled
go / vuln_check (push) Has been cancelled
snapshot of https://github.com/sqlc-dev/sqlc.git (main, 2026-09-16; blobs > 131072B omitted)
2026-09-16 02:17:34 +05:00

77 lines
2.4 KiB
Go

package compiler
import (
"strings"
"github.com/sqlc-dev/sqlc/internal/core"
"github.com/sqlc-dev/sqlc/internal/source"
"github.com/sqlc-dev/sqlc/internal/sql/ast"
)
// expandCore rewrites the stars in a query's text with the columns the core
// analyzer resolved them to. The analyzer has already walked the statement and
// its subqueries, so there is nothing left to look up here: what remains is
// deciding how each name is written, which is the engine's business and not
// the core's.
func (c *Compiler) expandCore(raw *ast.RawStmt, stars []core.StarExpansion) []source.Edit {
if len(stars) == 0 {
return nil
}
edits := make([]source.Edit, 0, len(stars))
seen := make(map[int]bool, len(stars))
for _, star := range stars {
// The analyzer types an expression again when a later clause refers to
// the output name it was given, so "SELECT (SELECT * FROM t) AS x ...
// GROUP BY x" reports the star in the subquery once per pass. The
// passes agree on what the star covers, and editing the same reference
// twice would overlap, so only the first is kept.
if seen[star.Location] {
continue
}
seen[star.Location] = true
// Everything before the star qualifies it: "foo.*" is scoped to foo,
// while a bare "*" covers every relation in the FROM clause.
scope := strings.Join(star.Fields[:len(star.Fields)-1], ".")
// An unqualified star that covers more than one relation may name the
// same column twice, so those are written with their relation.
counts := map[string]int{}
if scope == "" {
for _, col := range star.Columns {
counts[col.Name]++
}
}
cols := make([]string, 0, len(star.Columns))
for _, col := range star.Columns {
cname := col.Name
if star.Alias != "" {
cname = star.Alias
}
cname = c.quoteIdent(cname)
if scope != "" {
cname = c.quoteIdent(scope) + "." + cname
}
if counts[cname] > 1 {
cname = c.quoteIdent(col.Relation) + "." + cname
}
// This is important for SQLite in particular which needs to wrap
// jsonb column values with `json(colname)` so they're in a publicly
// usable format (i.e. not jsonb).
cols = append(cols, c.selector.ColumnExpr(cname, &Column{
Name: col.Name,
DataType: col.DataType,
}))
}
edits = append(edits, source.Edit{
Location: star.Location - raw.StmtLocation,
OldFunc: c.starOldFunc(star.Fields),
New: strings.Join(cols, ", "),
})
}
return edits
}