Skip to content

Commit 205daf7

Browse files
committed
plugin: pass the placeholder's name on Parameter rather than a flag
The name a placeholder carries, @name or {name:Type}, is what the driver binds by, so the plugin Parameter carries it as name, collected from the query's ParamRef nodes by the compiler rather than taken from the analyzer, which also names a parameter after the function it is compared with. The Go codegen passes it to sql.Named instead of the column's name. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015rfiHyC4iRLyRWc3UmYEtT
1 parent 4257811 commit 205daf7

11 files changed

Lines changed: 101 additions & 82 deletions

File tree

internal/cmd/shim.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ func pluginQueryParam(p compiler.Parameter) *plugin.Parameter {
258258
return &plugin.Parameter{
259259
Number: int32(p.Number),
260260
Column: pluginQueryColumn(p.Column),
261-
Named: p.Named,
261+
Name: p.Name,
262262
}
263263
}
264264

internal/codegen/golang/field.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@ import (
1111
)
1212

1313
type Field struct {
14-
Name string // CamelCased name for Go
15-
DBName string // Name as used in the DB
16-
Type string
17-
Tags map[string]string
18-
Comment string
19-
Column *plugin.Column
14+
Name string // CamelCased name for Go
15+
DBName string // Name as used in the DB
16+
// ParamName is the name of the placeholder a parameter is bound by,
17+
// when the query names it; empty otherwise and for row fields.
18+
ParamName string
19+
Type string
20+
Tags map[string]string
21+
Comment string
22+
Column *plugin.Column
2023
// EmbedFields contains the embedded fields that require scanning.
2124
EmbedFields []Field
2225
}

internal/codegen/golang/query.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ type QueryValue struct {
1515
EmitPointer bool
1616
Name string
1717
DBName string // The name of the field in the database. Only set if Struct==nil.
18+
ParamName string // The name of the placeholder a parameter is bound by, when the query names it. Only set if Struct==nil.
1819
Struct *Struct
1920
Typ string
2021
SQLDriver opts.SQLDriver
@@ -165,14 +166,14 @@ func (v QueryValue) Params() string {
165166
if !v.Column.IsSqlcSlice && strings.HasPrefix(v.Typ, "[]") && v.Typ != "[]byte" && v.pqArrays() {
166167
out = append(out, "pq.Array("+escape(v.Name)+")")
167168
} else {
168-
out = append(out, v.namedArg(v.DBName, escape(v.Name)))
169+
out = append(out, v.namedArg(v.ParamName, escape(v.Name)))
169170
}
170171
} else {
171172
for _, f := range v.Struct.Fields {
172173
if !f.HasSqlcSlice() && strings.HasPrefix(f.Type, "[]") && f.Type != "[]byte" && v.pqArrays() {
173174
out = append(out, "pq.Array("+escape(v.VariableForField(f))+")")
174175
} else {
175-
out = append(out, v.namedArg(f.DBName, escape(v.VariableForField(f))))
176+
out = append(out, v.namedArg(f.ParamName, escape(v.VariableForField(f))))
176177
}
177178
}
178179
}

internal/codegen/golang/result.go

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@ type goColumn struct {
115115
id int
116116
*plugin.Column
117117
embed *goEmbed
118+
// paramName is the name of the placeholder a parameter is bound by,
119+
// when the query names it.
120+
paramName string
118121
}
119122

120123
type goEmbed struct {
@@ -238,6 +241,7 @@ func buildQueries(req *plugin.GenerateRequest, options *opts.Options, enums []En
238241
gq.Arg = QueryValue{
239242
Name: escape(paramName(p)),
240243
DBName: p.Column.GetName(),
244+
ParamName: p.Name,
241245
Typ: qualifyType(goParamType(req, options, p.Column), models, qualifier),
242246
SQLDriver: sqlpkg,
243247
Engine: req.Settings.Engine,
@@ -249,8 +253,9 @@ func buildQueries(req *plugin.GenerateRequest, options *opts.Options, enums []En
249253
var cols []goColumn
250254
for _, p := range query.Params {
251255
cols = append(cols, goColumn{
252-
id: int(p.Number),
253-
Column: p.Column,
256+
id: int(p.Number),
257+
Column: p.Column,
258+
paramName: p.Name,
254259
})
255260
}
256261
s, err := columnsToStruct(req, options, gq.MethodName+"Params", cols, false, models, qualifier)
@@ -422,10 +427,11 @@ func columnsToStruct(req *plugin.GenerateRequest, options *opts.Options, name st
422427
}
423428
addExtraGoStructTags(tags, req, options, c.Column)
424429
f := Field{
425-
Name: fieldName,
426-
DBName: colName,
427-
Tags: tags,
428-
Column: c.Column,
430+
Name: fieldName,
431+
DBName: colName,
432+
ParamName: c.paramName,
433+
Tags: tags,
434+
Column: c.Column,
429435
}
430436
if c.embed == nil {
431437
// A row struct is scanned into, a params struct is passed as
@@ -487,16 +493,16 @@ func checkIncompatibleFieldTypes(fields []Field) error {
487493
}
488494

489495
// placeholdersAreNamed reports whether every parameter of a query is bound
490-
// by name, which the compiler marks on a parameter whose placeholder names
491-
// it: SQL Server's and Spanner's @name and ClickHouse's {name:Type}, which
492-
// their drivers take as sql.Named. A query written with ? is bound by
493-
// position, so nothing is named unless every parameter is.
496+
// by name: the compiler passes the name a placeholder carries, SQL
497+
// Server's and Spanner's @name and ClickHouse's {name:Type}, which their
498+
// drivers take as sql.Named. A query written with ? is bound by position,
499+
// so nothing is named unless every parameter is.
494500
func placeholdersAreNamed(params []*plugin.Parameter) bool {
495501
if len(params) == 0 {
496502
return false
497503
}
498504
for _, p := range params {
499-
if !p.Named || p.Column.GetName() == "" {
505+
if p.Name == "" {
500506
return false
501507
}
502508
}

internal/compiler/parse_core.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/sqlc-dev/sqlc/internal/metadata"
1111
"github.com/sqlc-dev/sqlc/internal/source"
1212
"github.com/sqlc-dev/sqlc/internal/sql/ast"
13+
"github.com/sqlc-dev/sqlc/internal/sql/astutils"
1314
"github.com/sqlc-dev/sqlc/internal/sql/named"
1415
"github.com/sqlc-dev/sqlc/internal/sql/preprocess"
1516
"github.com/sqlc-dev/sqlc/internal/sql/validate"
@@ -62,8 +63,9 @@ func (c *Compiler) parseQueryCore(raw *ast.RawStmt, src string, pre *preprocess.
6263
for _, col := range res.Columns {
6364
cols = append(cols, coreColumn(col))
6465
}
66+
placeholders := placeholderNames(raw)
6567
for _, p := range res.Parameters {
66-
params = append(params, Parameter{Number: p.Number, Column: coreParamColumn(p, namedParams), Named: p.Name != ""})
68+
params = append(params, Parameter{Number: p.Number, Column: coreParamColumn(p, namedParams), Name: placeholders[p.Number]})
6769
}
6870
expanded, err = source.Mutate(rawSQL, c.expandCore(raw, res.Stars))
6971
if err != nil {
@@ -136,6 +138,19 @@ func describeType(col *Column, t *core.TypeExpr) {
136138
col.Unsigned = strings.HasSuffix(inner.Name, " unsigned")
137139
}
138140

141+
// placeholderNames maps each parameter number to the name its placeholder
142+
// carries, for the placeholders that have one: @name and {name:Type}.
143+
func placeholderNames(root ast.Node) map[int]string {
144+
names := map[int]string{}
145+
astutils.Apply(root, func(c *astutils.Cursor) bool {
146+
if pr, ok := c.Node().(*ast.ParamRef); ok && pr.Name != "" {
147+
names[pr.Number] = pr.Name
148+
}
149+
return true
150+
}, nil)
151+
return names
152+
}
153+
139154
func coreParamColumn(p core.Parameter, params *named.ParamSet) *Column {
140155
col := &Column{
141156
Name: p.Name,

internal/compiler/query.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ type Query struct {
6464
type Parameter struct {
6565
Number int
6666
Column *Column
67-
// Named is set when the query binds the parameter by name rather than
68-
// by position: its placeholder is @name or {name:Type}.
69-
Named bool
67+
// Name is the name the placeholder itself carries, @name or
68+
// {name:Type}, by which the query binds the parameter. It is empty for
69+
// a positional placeholder.
70+
Name string
7071
}

internal/endtoend/testdata/codegen_json/gen/codegen.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68855,7 +68855,7 @@
6885568855
"array_dims": 0,
6885668856
"type_expr": null
6885768857
},
68858-
"named": false
68858+
"name": ""
6885968859
}
6886068860
],
6886168861
"comments": [],
@@ -69071,7 +69071,7 @@
6907169071
"array_dims": 0,
6907269072
"type_expr": null
6907369073
},
69074-
"named": false
69074+
"name": ""
6907569075
},
6907669076
{
6907769077
"number": 2,
@@ -69102,7 +69102,7 @@
6910269102
"array_dims": 0,
6910369103
"type_expr": null
6910469104
},
69105-
"named": false
69105+
"name": ""
6910669106
}
6910769107
],
6910869108
"comments": [],
@@ -69148,7 +69148,7 @@
6914869148
"array_dims": 0,
6914969149
"type_expr": null
6915069150
},
69151-
"named": false
69151+
"name": ""
6915269152
}
6915369153
],
6915469154
"comments": [],

internal/endtoend/testdata/codegen_json_type_expr/gen/codegen.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -752,7 +752,7 @@
752752
"args": []
753753
}
754754
},
755-
"named": false
755+
"name": ""
756756
},
757757
{
758758
"number": 2,
@@ -796,7 +796,7 @@
796796
]
797797
}
798798
},
799-
"named": false
799+
"name": ""
800800
}
801801
],
802802
"comments": [],

internal/endtoend/testdata/process_plugin_sqlc_gen_json/gen/codegen.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68857,7 +68857,7 @@
6885768857
"array_dims": 0,
6885868858
"type_expr": null
6885968859
},
68860-
"named": false
68860+
"name": ""
6886168861
}
6886268862
],
6886368863
"comments": [],
@@ -69073,7 +69073,7 @@
6907369073
"array_dims": 0,
6907469074
"type_expr": null
6907569075
},
69076-
"named": false
69076+
"name": ""
6907769077
},
6907869078
{
6907969079
"number": 2,
@@ -69104,7 +69104,7 @@
6910469104
"array_dims": 0,
6910569105
"type_expr": null
6910669106
},
69107-
"named": false
69107+
"name": ""
6910869108
}
6910969109
],
6911069110
"comments": [],
@@ -69150,7 +69150,7 @@
6915069150
"array_dims": 0,
6915169151
"type_expr": null
6915269152
},
69153-
"named": false
69153+
"name": ""
6915469154
}
6915569155
],
6915669156
"comments": [],

internal/plugin/codegen.pb.go

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

0 commit comments

Comments
 (0)