Files
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

212 lines
4.9 KiB
Go

package analyzer
import (
"fmt"
"github.com/sqlc-dev/sqlc/internal/core"
"github.com/sqlc-dev/sqlc/internal/sql/ast"
)
func (a *analyzer) analyzeInsert(s *ast.InsertStmt) error {
if s.Relation == nil {
return fmt.Errorf("insert: missing relation")
}
rel, err := a.bindRangeVar(s.Relation)
if err != nil {
return err
}
a.scope = &scope{rels: []scopeRel{rel}}
targets, err := insertTargets(rel, s.Cols)
if err != nil {
return err
}
if err := a.bindInsertValues(s.SelectStmt, rel, targets); err != nil {
return err
}
return a.projectReturning(s.ReturningList)
}
func (a *analyzer) analyzeUpdate(s *ast.UpdateStmt) error {
if err := a.bindCTEs(s.WithClause); err != nil {
return err
}
sc, err := a.relationScope(s.Relations, s.FromClause, nil)
if err != nil {
return err
}
a.scope = sc
target := sc.rels[0]
for _, item := range listItems(s.TargetList) {
rt, ok := item.(*ast.ResTarget)
if !ok || rt.Name == nil {
continue
}
col, ok := findColumn(target, *rt.Name)
if !ok {
return fmt.Errorf("unknown column %q", *rt.Name)
}
if err := a.bindValue(target, &col, rt.Val); err != nil {
return fmt.Errorf("set %s: %w", *rt.Name, err)
}
}
if s.WhereClause != nil {
if _, err := a.typeExpr(s.WhereClause); err != nil {
return fmt.Errorf("where: %w", err)
}
}
return a.projectReturning(s.ReturningList)
}
func (a *analyzer) analyzeDelete(s *ast.DeleteStmt) error {
if err := a.bindCTEs(s.WithClause); err != nil {
return err
}
sc, err := a.relationScope(s.Relations, s.UsingClause, s.FromClause)
if err != nil {
return err
}
a.scope = sc
if s.WhereClause != nil {
if _, err := a.typeExpr(s.WhereClause); err != nil {
return fmt.Errorf("where: %w", err)
}
}
return a.projectReturning(s.ReturningList)
}
// relationScope builds the scope a DML statement operates on: the relations it
// targets, whatever a USING or FROM clause joins in, and — for the engines that
// report a multi-table DELETE that way — a single FROM node.
func (a *analyzer) relationScope(relations, extra *ast.List, from ast.Node) (*scope, error) {
sc := &scope{}
defer a.binding(sc)()
for _, item := range listItems(relations) {
if err := a.appendFromItem(sc, item); err != nil {
return nil, err
}
}
for _, item := range listItems(extra) {
if err := a.appendFromItem(sc, item); err != nil {
return nil, err
}
}
if from != nil {
if err := a.appendFromItem(sc, from); err != nil {
return nil, err
}
}
if len(sc.rels) == 0 {
return nil, fmt.Errorf("missing target relation")
}
return sc, nil
}
func insertTargets(rel scopeRel, cols *ast.List) ([]core.ClassColumn, error) {
items := listItems(cols)
if len(items) == 0 {
out := make([]core.ClassColumn, 0, len(rel.cols))
for _, col := range rel.cols {
if !col.Hidden {
out = append(out, col)
}
}
return out, nil
}
out := make([]core.ClassColumn, 0, len(items))
for _, item := range items {
rt, ok := item.(*ast.ResTarget)
if !ok || rt.Name == nil {
return nil, fmt.Errorf("insert: unsupported column target %T", item)
}
col, ok := findColumn(rel, *rt.Name)
if !ok {
return nil, fmt.Errorf("unknown column %q", *rt.Name)
}
out = append(out, col)
}
return out, nil
}
func (a *analyzer) bindInsertValues(n ast.Node, rel scopeRel, targets []core.ClassColumn) error {
if n == nil {
return nil
}
sel, ok := n.(*ast.SelectStmt)
if !ok {
return fmt.Errorf("insert: unsupported source %T", n)
}
// INSERT ... SELECT inserts whatever the query returns. The rows are not
// the statement's result, but the query still holds placeholders.
if sel.ValuesLists == nil {
_, err := a.subqueryColumns(sel)
return err
}
for _, row := range listItems(sel.ValuesLists) {
values, ok := row.(*ast.List)
if !ok {
continue
}
for i, v := range values.Items {
var target *core.ClassColumn
if i < len(targets) {
target = &targets[i]
}
if err := a.bindValue(rel, target, v); err != nil {
return err
}
}
}
return nil
}
func (a *analyzer) bindValue(rel scopeRel, target *core.ClassColumn, v ast.Node) error {
if target != nil {
switch value := v.(type) {
case *ast.ParamRef:
a.inferParam(value.Number, columnType(rel, *target))
return nil
case *ast.A_Const:
return nil
}
}
_, err := a.typeExpr(v)
return err
}
func (a *analyzer) projectReturning(l *ast.List) error {
for _, item := range listItems(l) {
rt, ok := item.(*ast.ResTarget)
if !ok {
continue
}
if err := a.projectTarget(rt); err != nil {
return err
}
}
return nil
}
func findColumn(rel scopeRel, name string) (core.ClassColumn, bool) {
for _, col := range rel.cols {
if col.Name == name {
return col, true
}
}
return core.ClassColumn{}, false
}
func columnType(rel scopeRel, col core.ClassColumn) exprType {
return exprType{
typeOID: col.TypeOID,
expr: col.Type,
nullable: !col.NotNull,
sourceClassOID: rel.classOID,
sourceAttributeOID: col.AttOID,
sourceTableAlias: rel.alias,
}
}