Skip to content

Commit 816eda8

Browse files
authored
Ydb-go-sdk codegen: Containers support in ydb.ParamsBuilder() (#15)
* Added Arrays support + rewrites some internal logic to handle sqlc.arg/narg/slice in ydb * Supported containertype named parameters * Rewrited params to handle compex types and comment if type is interface{}
1 parent a69429c commit 816eda8

File tree

6 files changed

+568
-219
lines changed

6 files changed

+568
-219
lines changed

examples/authors/ydb/query.sql.go

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

internal/codegen/golang/query.go

Lines changed: 133 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,27 @@ func (v QueryValue) UniqueFields() []Field {
128128
return fields
129129
}
130130

131+
// YDBUniqueFieldsWithComments returns unique fields for YDB struct generation with comments for interface{} fields
132+
func (v QueryValue) YDBUniqueFieldsWithComments() []Field {
133+
seen := map[string]struct{}{}
134+
fields := make([]Field, 0, len(v.Struct.Fields))
135+
136+
for _, field := range v.Struct.Fields {
137+
if _, found := seen[field.Name]; found {
138+
continue
139+
}
140+
seen[field.Name] = struct{}{}
141+
142+
if strings.HasSuffix(field.Type, "interface{}") {
143+
field.Comment = "// sqlc couldn't resolve type, pass via params"
144+
}
145+
146+
fields = append(fields, field)
147+
}
148+
149+
return fields
150+
}
151+
131152
func (v QueryValue) Params() string {
132153
if v.isEmpty() {
133154
return ""
@@ -198,7 +219,7 @@ func (v QueryValue) HasSqlcSlices() bool {
198219
func (v QueryValue) Scan() string {
199220
var out []string
200221
if v.Struct == nil {
201-
if strings.HasPrefix(v.Typ, "[]") && v.Typ != "[]byte" && !v.SQLDriver.IsPGX() {
222+
if strings.HasPrefix(v.Typ, "[]") && v.Typ != "[]byte" && !v.SQLDriver.IsPGX() && !v.SQLDriver.IsYDBGoSDK() {
202223
out = append(out, "pq.Array(&"+v.Name+")")
203224
} else {
204225
out = append(out, "&"+v.Name)
@@ -209,7 +230,7 @@ func (v QueryValue) Scan() string {
209230
// append any embedded fields
210231
if len(f.EmbedFields) > 0 {
211232
for _, embed := range f.EmbedFields {
212-
if strings.HasPrefix(embed.Type, "[]") && embed.Type != "[]byte" && !v.SQLDriver.IsPGX() {
233+
if strings.HasPrefix(embed.Type, "[]") && embed.Type != "[]byte" && !v.SQLDriver.IsPGX() && !v.SQLDriver.IsYDBGoSDK() {
213234
out = append(out, "pq.Array(&"+v.Name+"."+f.Name+"."+embed.Name+")")
214235
} else {
215236
out = append(out, "&"+v.Name+"."+f.Name+"."+embed.Name)
@@ -218,7 +239,7 @@ func (v QueryValue) Scan() string {
218239
continue
219240
}
220241

221-
if strings.HasPrefix(f.Type, "[]") && f.Type != "[]byte" && !v.SQLDriver.IsPGX() {
242+
if strings.HasPrefix(f.Type, "[]") && f.Type != "[]byte" && !v.SQLDriver.IsPGX() && !v.SQLDriver.IsYDBGoSDK() {
222243
out = append(out, "pq.Array(&"+v.Name+"."+f.Name+")")
223244
} else {
224245
out = append(out, "&"+v.Name+"."+f.Name)
@@ -269,32 +290,6 @@ func addDollarPrefix(name string) string {
269290
return "$" + name
270291
}
271292

272-
// YDBParamMapEntries returns entries for a map[string]any literal for YDB parameters.
273-
func (v QueryValue) YDBParamMapEntries() string {
274-
if v.isEmpty() {
275-
return ""
276-
}
277-
278-
var parts []string
279-
for _, field := range v.getParameterFields() {
280-
if field.Column != nil && field.Column.IsNamedParam {
281-
name := field.Column.GetName()
282-
if name != "" {
283-
key := fmt.Sprintf("%q", addDollarPrefix(name))
284-
variable := v.VariableForField(field)
285-
parts = append(parts, key+": "+escape(variable))
286-
}
287-
}
288-
}
289-
290-
if len(parts) == 0 {
291-
return ""
292-
}
293-
294-
parts = append(parts, "")
295-
return "\n" + strings.Join(parts, ",\n")
296-
}
297-
298293
// ydbBuilderMethodForColumnType maps a YDB column data type to a ParamsBuilder method name.
299294
func ydbBuilderMethodForColumnType(dbType string) string {
300295
baseType := extractBaseType(strings.ToLower(dbType))
@@ -353,52 +348,89 @@ func ydbBuilderMethodForColumnType(dbType string) string {
353348
}
354349
}
355350

356-
// YDBParamsBuilder emits Go code that constructs YDB params using ParamsBuilder.
357-
func (v QueryValue) YDBParamsBuilder() string {
351+
// ydbIterateNamedParams iterates over named parameters and calls the provided function for each one.
352+
// The function receives the field and method name, and should return true to continue iteration.
353+
func (v QueryValue) ydbIterateNamedParams(fn func(field Field, method string) bool) bool {
358354
if v.isEmpty() {
359-
return ""
355+
return false
360356
}
361357

362-
var lines []string
363-
364358
for _, field := range v.getParameterFields() {
365-
if field.Column != nil && field.Column.IsNamedParam {
359+
if field.Column != nil {
366360
name := field.Column.GetName()
367361
if name == "" {
368362
continue
369363
}
370-
paramName := fmt.Sprintf("%q", addDollarPrefix(name))
371-
variable := escape(v.VariableForField(field))
372364

373365
var method string
374366
if field.Column != nil && field.Column.Type != nil {
375367
method = ydbBuilderMethodForColumnType(sdk.DataType(field.Column.Type))
376368
}
377369

378-
goType := field.Type
379-
isPtr := strings.HasPrefix(goType, "*")
380-
if isPtr {
381-
goType = strings.TrimPrefix(goType, "*")
370+
if !fn(field, method) {
371+
return false
382372
}
373+
}
374+
}
375+
return true
376+
}
383377

384-
if method == "" {
385-
panic(fmt.Sprintf("unknown YDB column type for param %s (goType=%s)", name, goType))
386-
}
378+
// YDBHasComplexContainers returns true if there are complex container types that sqlc cannot handle automatically.
379+
func (v QueryValue) YDBHasComplexContainers() bool {
380+
hasComplex := false
381+
v.ydbIterateNamedParams(func(field Field, method string) bool {
382+
if method == "" {
383+
hasComplex = true
384+
return false
385+
}
386+
return true
387+
})
388+
return hasComplex
389+
}
387390

388-
if isPtr {
389-
lines = append(lines, fmt.Sprintf("\t\t\tParam(%s).BeginOptional().%s(%s).EndOptional().", paramName, method, variable))
390-
} else {
391-
lines = append(lines, fmt.Sprintf("\t\t\tParam(%s).%s(%s).", paramName, method, variable))
392-
}
391+
// YDBParamsBuilder emits Go code that constructs YDB params using ParamsBuilder.
392+
func (v QueryValue) YDBParamsBuilder() string {
393+
var lines []string
394+
395+
v.ydbIterateNamedParams(func(field Field, method string) bool {
396+
if method == "" {
397+
return true
393398
}
394-
}
395399

396-
if len(lines) == 0 {
397-
return ""
398-
}
400+
name := field.Column.GetName()
401+
paramName := fmt.Sprintf("%q", addDollarPrefix(name))
402+
variable := escape(v.VariableForField(field))
403+
404+
goType := field.Type
405+
isPtr := strings.HasPrefix(goType, "*")
406+
isArray := field.Column.IsArray || field.Column.IsSqlcSlice
407+
408+
if isArray {
409+
lines = append(lines, fmt.Sprintf("\tvar list = parameters.Param(%s).BeginList()", paramName))
410+
lines = append(lines, fmt.Sprintf("\tfor _, param := range %s {", variable))
411+
lines = append(lines, fmt.Sprintf("\t\tlist = list.Add().%s(param)", method))
412+
lines = append(lines, "\t}")
413+
lines = append(lines, "\tparameters = list.EndList()")
414+
} else if isPtr {
415+
lines = append(lines, fmt.Sprintf("\tparameters = parameters.Param(%s).BeginOptional().%s(%s).EndOptional()", paramName, method, variable))
416+
} else {
417+
lines = append(lines, fmt.Sprintf("\tparameters = parameters.Param(%s).%s(%s)", paramName, method, variable))
418+
}
419+
420+
return true
421+
})
399422

400423
params := strings.Join(lines, "\n")
401-
return fmt.Sprintf("\nquery.WithParameters(\n\t\tydb.ParamsBuilder().\n%s\n\t\t\tBuild(),\n\t\t),\n", params)
424+
return fmt.Sprintf("\tparameters := ydb.ParamsBuilder()\n%s", params)
425+
}
426+
427+
func (v QueryValue) YDBHasParams() bool {
428+
hasParams := false
429+
v.ydbIterateNamedParams(func(field Field, method string) bool {
430+
hasParams = true
431+
return false
432+
})
433+
return hasParams
402434
}
403435

404436
func (v QueryValue) getParameterFields() []Field {
@@ -415,6 +447,53 @@ func (v QueryValue) getParameterFields() []Field {
415447
return v.Struct.Fields
416448
}
417449

450+
// YDBPair returns the argument name and type for YDB query methods, filtering out interface{} parameters
451+
// that are handled by manualParams instead.
452+
func (v QueryValue) YDBPair() string {
453+
if v.isEmpty() {
454+
return ""
455+
}
456+
457+
var out []string
458+
for _, arg := range v.YDBPairs() {
459+
out = append(out, arg.Name+" "+arg.Type)
460+
}
461+
return strings.Join(out, ",")
462+
}
463+
464+
// YDBPairs returns the argument name and type for YDB query methods, filtering out interface{} parameters
465+
// that are handled by manualParams instead.
466+
func (v QueryValue) YDBPairs() []Argument {
467+
if v.isEmpty() {
468+
return nil
469+
}
470+
471+
if !v.EmitStruct() && v.IsStruct() {
472+
var out []Argument
473+
for _, f := range v.Struct.Fields {
474+
if strings.HasSuffix(f.Type, "interface{}") {
475+
continue
476+
}
477+
out = append(out, Argument{
478+
Name: escape(toLowerCase(f.Name)),
479+
Type: f.Type,
480+
})
481+
}
482+
return out
483+
}
484+
485+
if strings.HasSuffix(v.Typ, "interface{}") {
486+
return nil
487+
}
488+
489+
return []Argument{
490+
{
491+
Name: escape(v.Name),
492+
Type: v.DefineType(),
493+
},
494+
}
495+
}
496+
418497
// A struct used to generate methods and fields on the Queries struct
419498
type Query struct {
420499
Cmd string

0 commit comments

Comments
 (0)