From e01653485528375347ee1882ec6dc1f8ac84b146 Mon Sep 17 00:00:00 2001 From: Viktor Pentyukhov Date: Fri, 3 Oct 2025 17:22:28 +0300 Subject: [PATCH 1/5] Added Arrays support + rewrites some internal logic to handle sqlc.arg/narg/slice in ydb --- examples/authors/ydb/query.sql.go | 34 +- internal/codegen/golang/query.go | 66 ++- .../templates/ydb-go-sdk/queryCode.tmpl | 21 +- internal/codegen/golang/ydb_type.go | 2 +- internal/engine/ydb/convert.go | 436 ++++++++++++++---- internal/sql/rewrite/parameters.go | 4 +- 6 files changed, 418 insertions(+), 145 deletions(-) diff --git a/examples/authors/ydb/query.sql.go b/examples/authors/ydb/query.sql.go index e9b6b332a4..6d3c6743a9 100644 --- a/examples/authors/ydb/query.sql.go +++ b/examples/authors/ydb/query.sql.go @@ -24,16 +24,12 @@ type CreateOrUpdateAuthorParams struct { } func (q *Queries) CreateOrUpdateAuthor(ctx context.Context, arg CreateOrUpdateAuthorParams, opts ...query.ExecuteOption) error { + parameters := ydb.ParamsBuilder() + parameters = parameters.Param("$p0").Uint64(arg.P0) + parameters = parameters.Param("$p1").Text(arg.P1) + parameters = parameters.Param("$p2").BeginOptional().Text(arg.P2).EndOptional() err := q.db.Exec(ctx, createOrUpdateAuthor, - append(opts, - query.WithParameters( - ydb.ParamsBuilder(). - Param("$p0").Uint64(arg.P0). - Param("$p1").Text(arg.P1). - Param("$p2").BeginOptional().Text(arg.P2).EndOptional(). - Build(), - ), - )..., + append(opts, query.WithParameters(parameters.Build()))..., ) if err != nil { return xerrors.WithStackTrace(err) @@ -46,14 +42,10 @@ DELETE FROM authors WHERE id = $p0 ` func (q *Queries) DeleteAuthor(ctx context.Context, p0 uint64, opts ...query.ExecuteOption) error { + parameters := ydb.ParamsBuilder() + parameters = parameters.Param("$p0").Uint64(p0) err := q.db.Exec(ctx, deleteAuthor, - append(opts, - query.WithParameters( - ydb.ParamsBuilder(). - Param("$p0").Uint64(p0). - Build(), - ), - )..., + append(opts, query.WithParameters(parameters.Build()))..., ) if err != nil { return xerrors.WithStackTrace(err) @@ -79,14 +71,10 @@ WHERE id = $p0 LIMIT 1 ` func (q *Queries) GetAuthor(ctx context.Context, p0 uint64, opts ...query.ExecuteOption) (Author, error) { + parameters := ydb.ParamsBuilder() + parameters = parameters.Param("$p0").Uint64(p0) row, err := q.db.QueryRow(ctx, getAuthor, - append(opts, - query.WithParameters( - ydb.ParamsBuilder(). - Param("$p0").Uint64(p0). - Build(), - ), - )..., + append(opts, query.WithParameters(parameters.Build()))..., ) var i Author if err != nil { diff --git a/internal/codegen/golang/query.go b/internal/codegen/golang/query.go index 52be2ecceb..e9b0418e6d 100644 --- a/internal/codegen/golang/query.go +++ b/internal/codegen/golang/query.go @@ -198,7 +198,7 @@ func (v QueryValue) HasSqlcSlices() bool { func (v QueryValue) Scan() string { var out []string if v.Struct == nil { - if strings.HasPrefix(v.Typ, "[]") && v.Typ != "[]byte" && !v.SQLDriver.IsPGX() { + if strings.HasPrefix(v.Typ, "[]") && v.Typ != "[]byte" && !v.SQLDriver.IsPGX() && !v.SQLDriver.IsYDBGoSDK() { out = append(out, "pq.Array(&"+v.Name+")") } else { out = append(out, "&"+v.Name) @@ -269,32 +269,6 @@ func addDollarPrefix(name string) string { return "$" + name } -// YDBParamMapEntries returns entries for a map[string]any literal for YDB parameters. -func (v QueryValue) YDBParamMapEntries() string { - if v.isEmpty() { - return "" - } - - var parts []string - for _, field := range v.getParameterFields() { - if field.Column != nil && field.Column.IsNamedParam { - name := field.Column.GetName() - if name != "" { - key := fmt.Sprintf("%q", addDollarPrefix(name)) - variable := v.VariableForField(field) - parts = append(parts, key+": "+escape(variable)) - } - } - } - - if len(parts) == 0 { - return "" - } - - parts = append(parts, "") - return "\n" + strings.Join(parts, ",\n") -} - // ydbBuilderMethodForColumnType maps a YDB column data type to a ParamsBuilder method name. func ydbBuilderMethodForColumnType(dbType string) string { baseType := extractBaseType(strings.ToLower(dbType)) @@ -351,10 +325,6 @@ func ydbBuilderMethodForColumnType(dbType string) string { // YDBParamsBuilder emits Go code that constructs YDB params using ParamsBuilder. func (v QueryValue) YDBParamsBuilder() string { - if v.isEmpty() { - return "" - } - var lines []string for _, field := range v.getParameterFields() { @@ -373,6 +343,7 @@ func (v QueryValue) YDBParamsBuilder() string { goType := field.Type isPtr := strings.HasPrefix(goType, "*") + isArray := field.Column.IsArray || field.Column.IsSqlcSlice if isPtr { goType = strings.TrimPrefix(goType, "*") } @@ -381,20 +352,39 @@ func (v QueryValue) YDBParamsBuilder() string { panic(fmt.Sprintf("unknown YDB column type for param %s (goType=%s)", name, goType)) } - if isPtr { - lines = append(lines, fmt.Sprintf("\t\t\tParam(%s).BeginOptional().%s(%s).EndOptional().", paramName, method, variable)) + if isArray { + lines = append(lines, fmt.Sprintf("\tvar list = parameters.Param(%s).BeginList()", paramName)) + lines = append(lines, fmt.Sprintf("\tfor _, param := range %s {", variable)) + lines = append(lines, fmt.Sprintf("\t\tlist = list.Add().%s(param)", method)) + lines = append(lines, "\t}") + lines = append(lines, "\tparameters = list.EndList()") + } else if isPtr { + lines = append(lines, fmt.Sprintf("\tparameters = parameters.Param(%s).BeginOptional().%s(%s).EndOptional()", paramName, method, variable)) } else { - lines = append(lines, fmt.Sprintf("\t\t\tParam(%s).%s(%s).", paramName, method, variable)) + lines = append(lines, fmt.Sprintf("\tparameters = parameters.Param(%s).%s(%s)", paramName, method, variable)) } } } - if len(lines) == 0 { - return "" + params := strings.Join(lines, "\n") + return fmt.Sprintf("\tparameters := ydb.ParamsBuilder()\n%s", params) +} + +// YDBHasParams returns true if there are parameters to build. +func (v QueryValue) YDBHasParams() bool { + if v.isEmpty() { + return false } - params := strings.Join(lines, "\n") - return fmt.Sprintf("\nquery.WithParameters(\n\t\tydb.ParamsBuilder().\n%s\n\t\t\tBuild(),\n\t\t),\n", params) + for _, field := range v.getParameterFields() { + if field.Column != nil && field.Column.IsNamedParam { + name := field.Column.GetName() + if name != "" { + return true + } + } + } + return false } func (v QueryValue) getParameterFields() []Field { diff --git a/internal/codegen/golang/templates/ydb-go-sdk/queryCode.tmpl b/internal/codegen/golang/templates/ydb-go-sdk/queryCode.tmpl index c56fc953f8..8e2663f6e8 100644 --- a/internal/codegen/golang/templates/ydb-go-sdk/queryCode.tmpl +++ b/internal/codegen/golang/templates/ydb-go-sdk/queryCode.tmpl @@ -27,8 +27,13 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBA {{- if .Arg.IsEmpty }} row, err := {{$dbArg}}.QueryRow(ctx, {{.ConstantName}}, opts...) {{- else }} + {{.Arg.YDBParamsBuilder}} row, err := {{$dbArg}}.QueryRow(ctx, {{.ConstantName}}, - append(opts, {{.Arg.YDBParamsBuilder}})..., + {{- if .Arg.YDBHasParams }} + append(opts, query.WithParameters(parameters.Build()))..., + {{- else }} + opts..., + {{- end }} ) {{- end }} {{- if or (ne .Arg.Pair .Ret.Pair) (ne .Arg.DefineType .Ret.DefineType) }} @@ -63,8 +68,13 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBA {{- if .Arg.IsEmpty }} result, err := {{$dbArg}}.QueryResultSet(ctx, {{.ConstantName}}, opts...) {{- else }} + {{.Arg.YDBParamsBuilder}} result, err := {{$dbArg}}.QueryResultSet(ctx, {{.ConstantName}}, - append(opts, {{.Arg.YDBParamsBuilder}})..., + {{- if .Arg.YDBHasParams }} + append(opts, query.WithParameters(parameters.Build()))..., + {{- else }} + opts..., + {{- end }} ) {{- end }} if err != nil { @@ -116,8 +126,13 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBA {{- if .Arg.IsEmpty }} err := {{$dbArg}}.Exec(ctx, {{.ConstantName}}, opts...) {{- else }} + {{.Arg.YDBParamsBuilder}} err := {{$dbArg}}.Exec(ctx, {{.ConstantName}}, - append(opts, {{.Arg.YDBParamsBuilder}})..., + {{- if .Arg.YDBHasParams }} + append(opts, query.WithParameters(parameters.Build()))..., + {{- else }} + opts..., + {{- end }} ) {{- end }} if err != nil { diff --git a/internal/codegen/golang/ydb_type.go b/internal/codegen/golang/ydb_type.go index 0a4db80a3b..880cdb3106 100644 --- a/internal/codegen/golang/ydb_type.go +++ b/internal/codegen/golang/ydb_type.go @@ -12,7 +12,7 @@ import ( func YDBType(req *plugin.GenerateRequest, options *opts.Options, col *plugin.Column) string { columnType := strings.ToLower(sdk.DataType(col.Type)) - notNull := (col.NotNull || col.IsArray) && !isNullableType(columnType) + notNull := (col.NotNull || col.IsArray || col.IsSqlcSlice) && !isNullableType(columnType) emitPointersForNull := options.EmitPointersForNullTypes columnType = extractBaseType(columnType) diff --git a/internal/engine/ydb/convert.go b/internal/engine/ydb/convert.go index 0fa339fa56..235d55ec78 100755 --- a/internal/engine/ydb/convert.go +++ b/internal/engine/ydb/convert.go @@ -1754,6 +1754,11 @@ func (c *cc) VisitColumn_schema(n *parser.Column_schemaContext) interface{} { if !ok { return todo("VisitColumn_schema", tnb) } + if typeName.ArrayBounds != nil && len(typeName.ArrayBounds.Items) > 0 { + col.IsArray = true + col.ArrayDims = len(typeName.ArrayBounds.Items) + typeName.ArrayBounds = nil + } col.TypeName = typeName } if colCons := n.Opt_column_constraints(); colCons != nil { @@ -1892,21 +1897,7 @@ func (c *cc) VisitType_name_composite(n *parser.Type_name_compositeContext) inte } if tuple := n.Type_name_tuple(); tuple != nil { - if typeNames := tuple.AllType_name_or_bind(); len(typeNames) > 0 { - var items []ast.Node - for _, tn := range typeNames { - tnNode, ok := tn.Accept(c).(ast.Node) - if !ok { - return todo("VisitType_name_composite", tn) - } - items = append(items, tnNode) - } - return &ast.TypeName{ - Name: "Tuple", - TypeOid: 0, - Names: &ast.List{Items: items}, - } - } + return tuple.Accept(c) } if struct_ := n.Type_name_struct(); struct_ != nil { @@ -1940,19 +1931,7 @@ func (c *cc) VisitType_name_composite(n *parser.Type_name_compositeContext) inte } if list := n.Type_name_list(); list != nil { - if typeName := list.Type_name_or_bind(); typeName != nil { - tn, ok := typeName.Accept(c).(ast.Node) - if !ok { - return todo("VisitType_name_composite", typeName) - } - return &ast.TypeName{ - Name: "List", - TypeOid: 0, - Names: &ast.List{ - Items: []ast.Node{tn}, - }, - } - } + return list.Accept(c) } if stream := n.Type_name_stream(); stream != nil { @@ -2054,6 +2033,73 @@ func (c *cc) VisitType_name_optional(n *parser.Type_name_optionalContext) interf } } +func (c *cc) VisitType_name_list(n *parser.Type_name_listContext) interface{} { + if n == nil || n.Type_name_or_bind() == nil { + return todo("VisitType_name_list", n) + } + + tn, ok := n.Type_name_or_bind().Accept(c).(ast.Node) + if !ok { + return todo("VisitType_name_list", n.Type_name_or_bind()) + } + innerTypeName, ok := tn.(*ast.TypeName) + if !ok { + return todo("VisitType_name_list", n.Type_name_or_bind()) + } + + if innerTypeName.ArrayBounds == nil { + innerTypeName.ArrayBounds = &ast.List{} + } + + return &ast.TypeName{ + Name: innerTypeName.Name, + TypeOid: 0, + Names: innerTypeName.Names, + ArrayBounds: &ast.List{ + Items: append(innerTypeName.ArrayBounds.Items, &ast.TODO{}), + }, + } +} + +func (c *cc) VisitType_name_tuple(n *parser.Type_name_tupleContext) interface{} { + if n == nil || len(n.AllType_name_or_bind()) == 0 { + return todo("VisitType_name_tuple", n) + } + + var items []ast.Node + for _, tn := range n.AllType_name_or_bind() { + tnNode, ok := tn.Accept(c).(ast.Node) + if !ok { + return todo("VisitType_name_tuple", tn) + } + items = append(items, tnNode) + } + + var typeName string = "" + for _, node := range items { + switch innerTypeName := node.(type) { + case *ast.TypeName: + if typeName == "" { + typeName = innerTypeName.Name + } else if typeName != innerTypeName.Name { + typeName = "any" + break + } + default: + typeName = "any" + } + } + + fmt.Println("Debug: typeName", typeName) + + return &ast.TypeName{ + Name: typeName, + TypeOid: 0, + ArrayBounds: &ast.List{Items: []ast.Node{&ast.TODO{}}}, + Location: c.pos(n.GetStart()), + } + +} func (c *cc) VisitSql_stmt_core(n *parser.Sql_stmt_coreContext) interface{} { if n == nil { return todo("VisitSql_stmt_core", n) @@ -2359,21 +2405,17 @@ func (c *cc) VisitXor_subexpr(n *parser.Xor_subexprContext) interface{} { } if condCtx := n.Cond_expr(); condCtx != nil { - switch { case condCtx.IN() != nil: if inExpr := condCtx.In_expr(); inExpr != nil { - temp, ok := inExpr.Accept(c).(ast.Node) - if !ok { - return todo("VisitXor_subexpr", inExpr) - } - list, ok := temp.(*ast.List) + node, ok := inExpr.Accept(c).(ast.Node) if !ok { + log.Printf("VisitXor_subexpr 0") return todo("VisitXor_subexpr", inExpr) } return &ast.In{ Expr: base, - List: list.Items, + List: []ast.Node{node}, Not: condCtx.NOT() != nil, Location: c.pos(n.GetStart()), } @@ -2383,12 +2425,12 @@ func (c *cc) VisitXor_subexpr(n *parser.Xor_subexprContext) interface{} { first, ok := eqSubs[0].Accept(c).(ast.Node) if !ok { - return todo("VisitXor_subexpr", n) + return todo("VisitXor_subexpr 1", n) } second, ok := eqSubs[1].Accept(c).(ast.Node) if !ok { - return todo("VisitXor_subexpr", n) + return todo("VisitXor_subexpr 2", n) } return &ast.BetweenExpr{ @@ -2708,6 +2750,145 @@ func (c *cc) VisitCon_subexpr(n *parser.Con_subexprContext) interface{} { } +func (c *cc) VisitIn_expr(n *parser.In_exprContext) interface{} { + if n == nil || n.In_unary_subexpr() == nil { + return todo("VisitIn_expr", n) + } + return n.In_unary_subexpr().Accept(c) +} + +func (c *cc) VisitIn_unary_subexpr(n *parser.In_unary_subexprContext) interface{} { + if n == nil || (n.In_unary_casual_subexpr() == nil && n.Json_api_expr() == nil) { + return todo("VisitIn_unary_subexpr", n) + } + if unary := n.In_unary_casual_subexpr(); unary != nil { + expr, ok := unary.Accept(c).(ast.Node) + if !ok { + return todo("VisitIn_unary_subexpr", unary) + } + return expr + } + jsonExpr, ok := n.Json_api_expr().Accept(c).(ast.Node) + if !ok { + return todo("VisitIn_unary_subexpr", n.Json_api_expr()) + } + return jsonExpr +} + +func (c *cc) VisitIn_unary_casual_subexpr(n *parser.In_unary_casual_subexprContext) interface{} { + var current ast.Node + switch { + case n.Id_expr_in() != nil: + expr, ok := n.Id_expr_in().Accept(c).(ast.Node) + if !ok { + return todo("VisitIn_unary_casual_subexpr", n.Id_expr_in()) + } + current = expr + case n.In_atom_expr() != nil: + expr, ok := n.In_atom_expr().Accept(c).(ast.Node) + if !ok { + return todo("VisitIn_unary_casual_subexpr", n.In_atom_expr()) + } + current = expr + default: + return todo("VisitIn_unary_casual_subexpr", n) + } + + if suffix := n.Unary_subexpr_suffix(); suffix != nil { + current = c.processSuffixChain(current, suffix.(*parser.Unary_subexpr_suffixContext)) + } + + return current +} + +func (c *cc) VisitId_expr_in(n *parser.Id_expr_inContext) interface{} { + if n == nil { + return todo("VisitId_expr", n) + } + + ref := &ast.ColumnRef{ + Fields: &ast.List{}, + Location: c.pos(n.GetStart()), + } + + if id := n.Identifier(); id != nil { + ref.Fields.Items = append(ref.Fields.Items, NewIdentifier(id.GetText())) + return ref + } + + if keyword := n.Keyword_compat(); keyword != nil { + ref.Fields.Items = append(ref.Fields.Items, NewIdentifier(keyword.GetText())) + return ref + } + + if keyword := n.Keyword_alter_uncompat(); keyword != nil { + ref.Fields.Items = append(ref.Fields.Items, NewIdentifier(keyword.GetText())) + return ref + } + + if keyword := n.Keyword_window_uncompat(); keyword != nil { + ref.Fields.Items = append(ref.Fields.Items, NewIdentifier(keyword.GetText())) + return ref + } + + if keyword := n.Keyword_hint_uncompat(); keyword != nil { + ref.Fields.Items = append(ref.Fields.Items, NewIdentifier(keyword.GetText())) + return ref + } + + return todo("VisitId_expr_in", n) +} + +func (c *cc) VisitIn_atom_expr(n *parser.In_atom_exprContext) interface{} { + if n == nil { + return todo("VisitAtom_expr", n) + } + + switch { + case n.An_id_or_type() != nil: + if n.NAMESPACE() != nil { + return NewIdentifier(parseAnIdOrType(n.An_id_or_type()) + "::" + parseIdOrType(n.Id_or_type())) + } + return NewIdentifier(parseAnIdOrType(n.An_id_or_type())) + case n.Literal_value() != nil: + expr, ok := n.Literal_value().Accept(c).(ast.Node) + if !ok { + return todo("VisitAtom_expr", n.Literal_value()) + } + return expr + case n.Bind_parameter() != nil: + expr, ok := n.Bind_parameter().Accept(c).(ast.Node) + if !ok { + return todo("VisitAtom_expr", n.Bind_parameter()) + } + return expr + case n.Cast_expr() != nil: + expr, ok := n.Cast_expr().Accept(c).(ast.Node) + if !ok { + return todo("VisitAtom_expr", n.Cast_expr()) + } + return expr + + case n.LPAREN() != nil && n.Select_stmt() != nil && n.RPAREN() != nil: + selectStmt, ok := n.Select_stmt().Accept(c).(ast.Node) + if !ok { + return todo("VisitAtom_expr", n.Select_stmt()) + } + return selectStmt + + case n.List_literal() != nil: + list, ok := n.List_literal().Accept(c).(ast.Node) + if !ok { + return todo("VisitAtom_expr", n.List_literal()) + } + return list + + // TODO: check other cases + default: + return todo("VisitAtom_expr", n) + } +} + func (c *cc) VisitUnary_subexpr(n *parser.Unary_subexprContext) interface{} { if n == nil || (n.Unary_casual_subexpr() == nil && n.Json_api_expr() == nil) { return todo("VisitUnary_subexpr", n) @@ -2769,7 +2950,7 @@ func (c *cc) processSuffixChain(base ast.Node, suffix *parser.Unary_subexpr_suff case *parser.Key_exprContext: current = c.handleKeySuffix(current, elem) case *parser.Invoke_exprContext: - current = c.handleInvokeSuffix(current, elem, i) + current = c.handleInvokeSuffix(current, elem) case antlr.TerminalNode: if elem.GetText() == "." { current = c.handleDotSuffix(current, suffix, &i) @@ -2806,7 +2987,7 @@ func (c *cc) handleKeySuffix(base ast.Node, keyCtx *parser.Key_exprContext) ast. } } -func (c *cc) handleInvokeSuffix(base ast.Node, invokeCtx *parser.Invoke_exprContext, idx int) ast.Node { +func (c *cc) handleInvokeSuffix(base ast.Node, invokeCtx *parser.Invoke_exprContext) ast.Node { temp, ok := invokeCtx.Accept(c).(ast.Node) if !ok { return todo("VisitInvoke_expr", invokeCtx) @@ -2816,48 +2997,50 @@ func (c *cc) handleInvokeSuffix(base ast.Node, invokeCtx *parser.Invoke_exprCont return todo("VisitInvoke_expr", invokeCtx) } - if idx == 0 { - switch baseNode := base.(type) { - case *ast.ColumnRef: - if len(baseNode.Fields.Items) > 0 { - var nameParts []string - for _, item := range baseNode.Fields.Items { - if s, ok := item.(*ast.String); ok { - nameParts = append(nameParts, s.Str) - } + switch baseNode := base.(type) { + case *ast.ColumnRef: + if len(baseNode.Fields.Items) > 0 { + var nameParts []string + for _, item := range baseNode.Fields.Items { + if s, ok := item.(*ast.String); ok { + nameParts = append(nameParts, s.Str) } - funcName := strings.Join(nameParts, ".") + } + funcCall.Func = &ast.FuncName{} + if len(nameParts) == 2 { + funcCall.Func.Schema = nameParts[0] + funcCall.Func.Name = nameParts[1] + } else { + funcCall.Func.Name = strings.Join(nameParts, ".") + } - if funcName == "coalesce" || funcName == "nvl" { - return &ast.CoalesceExpr{ - Args: funcCall.Args, - Location: baseNode.Location, - } + if funcCall.Func.Name == "coalesce" || funcCall.Func.Name == "nvl" { + return &ast.CoalesceExpr{ + Args: funcCall.Args, + Location: baseNode.Location, } + } - if funcName == "greatest" || funcName == "max_of" { - return &ast.MinMaxExpr{ - Op: ast.MinMaxOp(1), - Args: funcCall.Args, - Location: baseNode.Location, - } + if funcCall.Func.Name == "greatest" || funcCall.Func.Name == "max_of" { + return &ast.MinMaxExpr{ + Op: ast.MinMaxOp(1), + Args: funcCall.Args, + Location: baseNode.Location, } - if funcName == "least" || funcName == "min_of" { - return &ast.MinMaxExpr{ - Op: ast.MinMaxOp(2), - Args: funcCall.Args, - Location: baseNode.Location, - } + } + if funcCall.Func.Name == "least" || funcCall.Func.Name == "min_of" { + return &ast.MinMaxExpr{ + Op: ast.MinMaxOp(2), + Args: funcCall.Args, + Location: baseNode.Location, } - - funcCall.Func = &ast.FuncName{Name: funcName} - funcCall.Funcname.Items = append(funcCall.Funcname.Items, &ast.String{Str: funcName}) - - return funcCall } - default: - return todo("VisitInvoke_expr", invokeCtx) + funcCall.Funcname.Items = append(funcCall.Funcname.Items, &ast.String{Str: funcCall.Func.Name}) + funcCall.Location = baseNode.Location + return funcCall } + default: + return todo("VisitInvoke_expr", invokeCtx) } stmt := &ast.FuncExpr{ @@ -3029,29 +3212,65 @@ func (c *cc) VisitAtom_expr(n *parser.Atom_exprContext) interface{} { } switch { - case n.An_id_or_type() != nil: - if n.NAMESPACE() != nil { - return NewIdentifier(parseAnIdOrType(n.An_id_or_type()) + "::" + parseIdOrType(n.Id_or_type())) - } - return NewIdentifier(parseAnIdOrType(n.An_id_or_type())) case n.Literal_value() != nil: expr, ok := n.Literal_value().Accept(c).(ast.Node) if !ok { return todo("VisitAtom_expr", n.Literal_value()) } return expr + case n.Bind_parameter() != nil: expr, ok := n.Bind_parameter().Accept(c).(ast.Node) if !ok { return todo("VisitAtom_expr", n.Bind_parameter()) } return expr + + case n.Lambda() != nil: + expr, ok := n.Lambda().Accept(c).(ast.Node) + if !ok { + return todo("VisitAtom_expr", n.Lambda()) + } + return expr + case n.Cast_expr() != nil: expr, ok := n.Cast_expr().Accept(c).(ast.Node) if !ok { return todo("VisitAtom_expr", n.Cast_expr()) } return expr + + case n.Exists_expr() != nil: + return todo("VisitAtom_expr", n.Exists_expr()) + + case n.Case_expr() != nil: + return todo("VisitAtom_expr", n.Case_expr()) + + case n.An_id_or_type() != nil: + if n.NAMESPACE() != nil { + return NewIdentifier(parseAnIdOrType(n.An_id_or_type()) + "::" + parseIdOrType(n.Id_or_type())) + } + return NewIdentifier(parseAnIdOrType(n.An_id_or_type())) + + case n.Value_constructor() != nil: + return todo("VisitAtom_expr", n.Value_constructor()) + + case n.Bitcast_expr() != nil: + return todo("VisitAtom_expr", n.Bitcast_expr()) + + case n.List_literal() != nil: + list, ok := n.List_literal().Accept(c).(ast.Node) + if !ok { + return todo("VisitAtom_expr", n.List_literal()) + } + return list + + case n.Dict_literal() != nil: + return todo("VisitAtom_expr", n.Dict_literal()) + + case n.Struct_literal() != nil: + return todo("VisitAtom_expr", n.Struct_literal()) + // TODO: check other cases default: return todo("VisitAtom_expr", n) @@ -3067,7 +3286,7 @@ func (c *cc) VisitCast_expr(n *parser.Cast_exprContext) interface{} { if !ok { return todo("VisitCast_expr", n.Expr()) } - + temp, ok := n.Type_name_or_bind().Accept(c).(ast.Node) if !ok { return todo("VisitCast_expr", n.Type_name_or_bind()) @@ -3084,6 +3303,27 @@ func (c *cc) VisitCast_expr(n *parser.Cast_exprContext) interface{} { } } +func (c *cc) VisitList_literal(n *parser.List_literalContext) interface{} { + if n == nil || n.LBRACE_SQUARE() == nil || n.RBRACE_SQUARE() == nil || n.Expr_list() == nil { + return todo("VisitList_literal", n) + } + + array := &ast.A_ArrayExpr{ + Elements: &ast.List{}, + Location: c.pos(n.GetStart()), + } + + for _, item := range n.Expr_list().AllExpr() { + expr, ok := item.Accept(c).(ast.Node) + if !ok { + return todo("VisitList_literal", item) + } + array.Elements.Items = append(array.Elements.Items, expr) + } + + return array +} + func (c *cc) VisitLiteral_value(n *parser.Literal_valueContext) interface{} { if n == nil { return todo("VisitLiteral_value", n) @@ -3149,6 +3389,44 @@ func (c *cc) VisitLiteral_value(n *parser.Literal_valueContext) interface{} { } } +func (c *cc) VisitLambda(n *parser.LambdaContext) interface{} { + if n == nil || n.Smart_parenthesis() == nil { + return todo("VisitLambda", n) + } + + if n.ARROW() != nil { + log.Panicln("Lambda stmts are not supported in Sqlc") + return todo("VisitLambda", n) + } + + lambdaBody, ok := n.Smart_parenthesis().Accept(c).(ast.Node) + if !ok { + return todo("VisitLambda", n.Smart_parenthesis()) + } + + return lambdaBody +} + +func (c *cc) VisitSmart_parenthesis(n *parser.Smart_parenthesisContext) interface{} { + if n == nil || n.Named_expr_list() == nil || n.LPAREN() == nil || n.RPAREN() == nil { + return todo("VisitSmart_parenthesis", n) + } + + var args ast.List + for _, namedExpr := range n.Named_expr_list().AllNamed_expr() { + expr, ok := namedExpr.Accept(c).(ast.Node) + if !ok { + return todo("VisitSmart_parenthesis", namedExpr) + } + args.Items = append(args.Items, expr) + } + + return &ast.A_ArrayExpr{ + Elements: &args, + Location: c.pos(n.GetStart()), + } +} + func (c *cc) VisitSql_stmt(n *parser.Sql_stmtContext) interface{} { if n == nil || n.Sql_stmt_core() == nil { return todo("VisitSql_stmt", n) diff --git a/internal/sql/rewrite/parameters.go b/internal/sql/rewrite/parameters.go index 9146d17e08..5f213ae6c4 100644 --- a/internal/sql/rewrite/parameters.go +++ b/internal/sql/rewrite/parameters.go @@ -102,7 +102,9 @@ func NamedParameters(engine config.Engine, raw *ast.RawStmt, numbs map[int]bool, }) var replace string - if engine == config.EngineMySQL || engine == config.EngineSQLite || !dollar { + if engine == config.EngineYDB { + replace = fmt.Sprintf("$%s", param.Name()) + } else if engine == config.EngineMySQL || engine == config.EngineSQLite || !dollar { if param.IsSqlcSlice() { // This sequence is also replicated in internal/codegen/golang.Field // since it's needed during template generation for replacement From 1e99462c185031e6703f6351fcf11ee48e3f9a8d Mon Sep 17 00:00:00 2001 From: Viktor Pentyukhov Date: Tue, 7 Oct 2025 16:25:28 +0300 Subject: [PATCH 2/5] Supported containertype named parameters --- internal/codegen/golang/query.go | 101 +++++++++++------- .../templates/ydb-go-sdk/queryCode.tmpl | 45 ++++++-- 2 files changed, 97 insertions(+), 49 deletions(-) diff --git a/internal/codegen/golang/query.go b/internal/codegen/golang/query.go index e9b0418e6d..3ca4bfd067 100644 --- a/internal/codegen/golang/query.go +++ b/internal/codegen/golang/query.go @@ -209,7 +209,7 @@ func (v QueryValue) Scan() string { // append any embedded fields if len(f.EmbedFields) > 0 { for _, embed := range f.EmbedFields { - if strings.HasPrefix(embed.Type, "[]") && embed.Type != "[]byte" && !v.SQLDriver.IsPGX() { + if strings.HasPrefix(embed.Type, "[]") && embed.Type != "[]byte" && !v.SQLDriver.IsPGX() && !v.SQLDriver.IsYDBGoSDK() { out = append(out, "pq.Array(&"+v.Name+"."+f.Name+"."+embed.Name+")") } else { out = append(out, "&"+v.Name+"."+f.Name+"."+embed.Name) @@ -218,7 +218,7 @@ func (v QueryValue) Scan() string { continue } - if strings.HasPrefix(f.Type, "[]") && f.Type != "[]byte" && !v.SQLDriver.IsPGX() { + if strings.HasPrefix(f.Type, "[]") && f.Type != "[]byte" && !v.SQLDriver.IsPGX() && !v.SQLDriver.IsYDBGoSDK() { out = append(out, "pq.Array(&"+v.Name+"."+f.Name+")") } else { out = append(out, "&"+v.Name+"."+f.Name) @@ -323,9 +323,12 @@ func ydbBuilderMethodForColumnType(dbType string) string { } } -// YDBParamsBuilder emits Go code that constructs YDB params using ParamsBuilder. -func (v QueryValue) YDBParamsBuilder() string { - var lines []string +// ydbIterateNamedParams iterates over named parameters and calls the provided function for each one. +// The function receives the field and method name, and should return true to continue iteration. +func (v QueryValue) ydbIterateNamedParams(fn func(field Field, method string) bool) bool { + if v.isEmpty() { + return false + } for _, field := range v.getParameterFields() { if field.Column != nil && field.Column.IsNamedParam { @@ -333,58 +336,76 @@ func (v QueryValue) YDBParamsBuilder() string { if name == "" { continue } - paramName := fmt.Sprintf("%q", addDollarPrefix(name)) - variable := escape(v.VariableForField(field)) var method string if field.Column != nil && field.Column.Type != nil { method = ydbBuilderMethodForColumnType(sdk.DataType(field.Column.Type)) } - goType := field.Type - isPtr := strings.HasPrefix(goType, "*") - isArray := field.Column.IsArray || field.Column.IsSqlcSlice - if isPtr { - goType = strings.TrimPrefix(goType, "*") + if !fn(field, method) { + return false } + } + } + return true +} - if method == "" { - panic(fmt.Sprintf("unknown YDB column type for param %s (goType=%s)", name, goType)) - } +// YDBHasComplexContainers returns true if there are complex container types that sqlc cannot handle automatically. +func (v QueryValue) YDBHasComplexContainers() bool { + hasComplex := false + v.ydbIterateNamedParams(func(field Field, method string) bool { + if method == "" { + hasComplex = true + return false + } + return true + }) + return hasComplex +} - if isArray { - lines = append(lines, fmt.Sprintf("\tvar list = parameters.Param(%s).BeginList()", paramName)) - lines = append(lines, fmt.Sprintf("\tfor _, param := range %s {", variable)) - lines = append(lines, fmt.Sprintf("\t\tlist = list.Add().%s(param)", method)) - lines = append(lines, "\t}") - lines = append(lines, "\tparameters = list.EndList()") - } else if isPtr { - lines = append(lines, fmt.Sprintf("\tparameters = parameters.Param(%s).BeginOptional().%s(%s).EndOptional()", paramName, method, variable)) - } else { - lines = append(lines, fmt.Sprintf("\tparameters = parameters.Param(%s).%s(%s)", paramName, method, variable)) - } +// YDBParamsBuilder emits Go code that constructs YDB params using ParamsBuilder. +func (v QueryValue) YDBParamsBuilder() string { + var lines []string + + v.ydbIterateNamedParams(func(field Field, method string) bool { + if method == "" { + return true } - } + + name := field.Column.GetName() + paramName := fmt.Sprintf("%q", addDollarPrefix(name)) + variable := escape(v.VariableForField(field)) + + goType := field.Type + isPtr := strings.HasPrefix(goType, "*") + isArray := field.Column.IsArray || field.Column.IsSqlcSlice + + if isArray { + lines = append(lines, fmt.Sprintf("\tvar list = parameters.Param(%s).BeginList()", paramName)) + lines = append(lines, fmt.Sprintf("\tfor _, param := range %s {", variable)) + lines = append(lines, fmt.Sprintf("\t\tlist = list.Add().%s(param)", method)) + lines = append(lines, "\t}") + lines = append(lines, "\tparameters = list.EndList()") + } else if isPtr { + lines = append(lines, fmt.Sprintf("\tparameters = parameters.Param(%s).BeginOptional().%s(%s).EndOptional()", paramName, method, variable)) + } else { + lines = append(lines, fmt.Sprintf("\tparameters = parameters.Param(%s).%s(%s)", paramName, method, variable)) + } + + return true + }) params := strings.Join(lines, "\n") return fmt.Sprintf("\tparameters := ydb.ParamsBuilder()\n%s", params) } -// YDBHasParams returns true if there are parameters to build. func (v QueryValue) YDBHasParams() bool { - if v.isEmpty() { + hasParams := false + v.ydbIterateNamedParams(func(field Field, method string) bool { + hasParams = true return false - } - - for _, field := range v.getParameterFields() { - if field.Column != nil && field.Column.IsNamedParam { - name := field.Column.GetName() - if name != "" { - return true - } - } - } - return false + }) + return hasParams } func (v QueryValue) getParameterFields() []Field { diff --git a/internal/codegen/golang/templates/ydb-go-sdk/queryCode.tmpl b/internal/codegen/golang/templates/ydb-go-sdk/queryCode.tmpl index 8e2663f6e8..2ffabb7adc 100644 --- a/internal/codegen/golang/templates/ydb-go-sdk/queryCode.tmpl +++ b/internal/codegen/golang/templates/ydb-go-sdk/queryCode.tmpl @@ -22,12 +22,21 @@ type {{.Ret.Type}} struct { {{- range .Ret.Struct.Fields}} {{if eq .Cmd ":one"}} {{range .Comments}}//{{.}} {{end -}} +{{if .Arg.YDBHasComplexContainers}} +func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBArgument}}db DBTX, {{end}}{{if not .Arg.IsEmpty}}{{.Arg.Pair}}, {{end}}manualParams ydb.Params, opts ...query.ExecuteOption) ({{.Ret.DefineType}}, error) { +{{else}} func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBArgument}}db DBTX, {{end}}{{if not .Arg.IsEmpty}}{{.Arg.Pair}}, {{end}}opts ...query.ExecuteOption) ({{.Ret.DefineType}}, error) { +{{end -}} {{- $dbArg := "q.db" }}{{- if $.EmitMethodsWithDBArgument }}{{- $dbArg = "db" }}{{- end -}} - {{- if .Arg.IsEmpty }} + {{- if .Arg.IsEmpty -}} row, err := {{$dbArg}}.QueryRow(ctx, {{.ConstantName}}, opts...) - {{- else }} - {{.Arg.YDBParamsBuilder}} + {{- else -}} + {{- .Arg.YDBParamsBuilder}} + {{- if .Arg.YDBHasComplexContainers }} + for name, value := range manualParams.Range() { + parameters = parameters.Param(name).Any(value) + } + {{- end }} row, err := {{$dbArg}}.QueryRow(ctx, {{.ConstantName}}, {{- if .Arg.YDBHasParams }} append(opts, query.WithParameters(parameters.Build()))..., @@ -63,12 +72,21 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBA {{if eq .Cmd ":many"}} {{range .Comments}}//{{.}} {{end -}} +{{if .Arg.YDBHasComplexContainers}} +func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBArgument}}db DBTX, {{end}}{{if not .Arg.IsEmpty}}{{.Arg.Pair}}, {{end}}manualParams ydb.Params, opts ...query.ExecuteOption) ([]{{.Ret.DefineType}}, error) { +{{else}} func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBArgument}}db DBTX, {{end}}{{if not .Arg.IsEmpty}}{{.Arg.Pair}}, {{end}}opts ...query.ExecuteOption) ([]{{.Ret.DefineType}}, error) { +{{end}} {{- $dbArg := "q.db" }}{{- if $.EmitMethodsWithDBArgument }}{{- $dbArg = "db" }}{{- end -}} - {{- if .Arg.IsEmpty }} + {{- if .Arg.IsEmpty -}} result, err := {{$dbArg}}.QueryResultSet(ctx, {{.ConstantName}}, opts...) - {{- else }} - {{.Arg.YDBParamsBuilder}} + {{- else -}} + {{- .Arg.YDBParamsBuilder}} + {{- if .Arg.YDBHasComplexContainers }} + for name, value := range manualParams.Range() { + parameters = parameters.Param(name).Any(value) + } + {{- end }} result, err := {{$dbArg}}.QueryResultSet(ctx, {{.ConstantName}}, {{- if .Arg.YDBHasParams }} append(opts, query.WithParameters(parameters.Build()))..., @@ -121,12 +139,21 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBA {{if eq .Cmd ":exec"}} {{range .Comments}}//{{.}} {{end -}} +{{if .Arg.YDBHasComplexContainers}} +func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBArgument}}db DBTX, {{end}}{{if not .Arg.IsEmpty}}{{.Arg.Pair}}, {{end}}manualParams ydb.Params, opts ...query.ExecuteOption) error { +{{else}} func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBArgument}}db DBTX, {{end}}{{if not .Arg.IsEmpty}}{{.Arg.Pair}}, {{end}}opts ...query.ExecuteOption) error { +{{end -}} {{- $dbArg := "q.db" }}{{- if $.EmitMethodsWithDBArgument }}{{- $dbArg = "db" }}{{- end -}} - {{- if .Arg.IsEmpty }} + {{- if .Arg.IsEmpty -}} err := {{$dbArg}}.Exec(ctx, {{.ConstantName}}, opts...) - {{- else }} - {{.Arg.YDBParamsBuilder}} + {{- else -}} + {{- .Arg.YDBParamsBuilder}} + {{- if .Arg.YDBHasComplexContainers }} + for name, value := range manualParams.Range() { + parameters = parameters.Param(name).Any(value) + } + {{- end }} err := {{$dbArg}}.Exec(ctx, {{.ConstantName}}, {{- if .Arg.YDBHasParams }} append(opts, query.WithParameters(parameters.Build()))..., From 401ee0a2289369274ae6114608aefb47eed98fa3 Mon Sep 17 00:00:00 2001 From: Viktor Pentyukhov Date: Tue, 7 Oct 2025 20:05:19 +0300 Subject: [PATCH 3/5] Rewrited params to handle compex types and comment if type is interface{} --- internal/codegen/golang/query.go | 70 ++++++++++++++++++- .../templates/ydb-go-sdk/queryCode.tmpl | 22 +++--- internal/codegen/golang/ydb_type.go | 3 +- internal/engine/ydb/convert.go | 69 ++++-------------- 4 files changed, 97 insertions(+), 67 deletions(-) diff --git a/internal/codegen/golang/query.go b/internal/codegen/golang/query.go index 3ca4bfd067..8e7c56f5ae 100644 --- a/internal/codegen/golang/query.go +++ b/internal/codegen/golang/query.go @@ -128,6 +128,27 @@ func (v QueryValue) UniqueFields() []Field { return fields } +// YDBUniqueFieldsWithComments returns unique fields for YDB struct generation with comments for interface{} fields +func (v QueryValue) YDBUniqueFieldsWithComments() []Field { + seen := map[string]struct{}{} + fields := make([]Field, 0, len(v.Struct.Fields)) + + for _, field := range v.Struct.Fields { + if _, found := seen[field.Name]; found { + continue + } + seen[field.Name] = struct{}{} + + if strings.HasSuffix(field.Type, "interface{}") { + field.Comment = "// sqlc couldn't resolve type, pass via params" + } + + fields = append(fields, field) + } + + return fields +} + func (v QueryValue) Params() string { if v.isEmpty() { return "" @@ -331,7 +352,7 @@ func (v QueryValue) ydbIterateNamedParams(fn func(field Field, method string) bo } for _, field := range v.getParameterFields() { - if field.Column != nil && field.Column.IsNamedParam { + if field.Column != nil { name := field.Column.GetName() if name == "" { continue @@ -422,6 +443,53 @@ func (v QueryValue) getParameterFields() []Field { return v.Struct.Fields } +// YDBPair returns the argument name and type for YDB query methods, filtering out interface{} parameters +// that are handled by manualParams instead. +func (v QueryValue) YDBPair() string { + if v.isEmpty() { + return "" + } + + var out []string + for _, arg := range v.YDBPairs() { + out = append(out, arg.Name+" "+arg.Type) + } + return strings.Join(out, ",") +} + +// YDBPairs returns the argument name and type for YDB query methods, filtering out interface{} parameters +// that are handled by manualParams instead. +func (v QueryValue) YDBPairs() []Argument { + if v.isEmpty() { + return nil + } + + if !v.EmitStruct() && v.IsStruct() { + var out []Argument + for _, f := range v.Struct.Fields { + if strings.HasSuffix(f.Type, "interface{}") { + continue + } + out = append(out, Argument{ + Name: escape(toLowerCase(f.Name)), + Type: f.Type, + }) + } + return out + } + + if strings.HasSuffix(v.Typ, "interface{}") { + return nil + } + + return []Argument{ + { + Name: escape(v.Name), + Type: v.DefineType(), + }, + } +} + // A struct used to generate methods and fields on the Queries struct type Query struct { Cmd string diff --git a/internal/codegen/golang/templates/ydb-go-sdk/queryCode.tmpl b/internal/codegen/golang/templates/ydb-go-sdk/queryCode.tmpl index 2ffabb7adc..9c60ded05f 100644 --- a/internal/codegen/golang/templates/ydb-go-sdk/queryCode.tmpl +++ b/internal/codegen/golang/templates/ydb-go-sdk/queryCode.tmpl @@ -6,8 +6,8 @@ const {{.ConstantName}} = {{$.Q}}-- name: {{.MethodName}} {{.Cmd}} {{$.Q}} {{if .Arg.EmitStruct}} -type {{.Arg.Type}} struct { {{- range .Arg.UniqueFields}} - {{.Name}} {{.Type}} {{if .Tag}}{{$.Q}}{{.Tag}}{{$.Q}}{{end}} +type {{.Arg.Type}} struct { {{- range .Arg.YDBUniqueFieldsWithComments}} + {{.Name}} {{.Type}} {{if .Tag}}{{$.Q}}{{.Tag}}{{$.Q}}{{end}}{{if .Comment}} {{.Comment}}{{end}} {{- end}} } {{end}} @@ -23,9 +23,9 @@ type {{.Ret.Type}} struct { {{- range .Ret.Struct.Fields}} {{range .Comments}}//{{.}} {{end -}} {{if .Arg.YDBHasComplexContainers}} -func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBArgument}}db DBTX, {{end}}{{if not .Arg.IsEmpty}}{{.Arg.Pair}}, {{end}}manualParams ydb.Params, opts ...query.ExecuteOption) ({{.Ret.DefineType}}, error) { +func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBArgument}}db DBTX, {{end}}{{if .Arg.YDBPair}}{{.Arg.YDBPair}}, {{end}}params ydb.Params, opts ...query.ExecuteOption) ({{.Ret.DefineType}}, error) { {{else}} -func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBArgument}}db DBTX, {{end}}{{if not .Arg.IsEmpty}}{{.Arg.Pair}}, {{end}}opts ...query.ExecuteOption) ({{.Ret.DefineType}}, error) { +func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBArgument}}db DBTX, {{end}}{{if .Arg.YDBPair}}{{.Arg.YDBPair}}, {{end}}opts ...query.ExecuteOption) ({{.Ret.DefineType}}, error) { {{end -}} {{- $dbArg := "q.db" }}{{- if $.EmitMethodsWithDBArgument }}{{- $dbArg = "db" }}{{- end -}} {{- if .Arg.IsEmpty -}} @@ -33,7 +33,7 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBA {{- else -}} {{- .Arg.YDBParamsBuilder}} {{- if .Arg.YDBHasComplexContainers }} - for name, value := range manualParams.Range() { + for name, value := range params.Range() { parameters = parameters.Param(name).Any(value) } {{- end }} @@ -73,9 +73,9 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBA {{range .Comments}}//{{.}} {{end -}} {{if .Arg.YDBHasComplexContainers}} -func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBArgument}}db DBTX, {{end}}{{if not .Arg.IsEmpty}}{{.Arg.Pair}}, {{end}}manualParams ydb.Params, opts ...query.ExecuteOption) ([]{{.Ret.DefineType}}, error) { +func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBArgument}}db DBTX, {{end}}{{if .Arg.YDBPair}}{{.Arg.YDBPair}}, {{end}}params ydb.Params, opts ...query.ExecuteOption) ([]{{.Ret.DefineType}}, error) { {{else}} -func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBArgument}}db DBTX, {{end}}{{if not .Arg.IsEmpty}}{{.Arg.Pair}}, {{end}}opts ...query.ExecuteOption) ([]{{.Ret.DefineType}}, error) { +func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBArgument}}db DBTX, {{end}}{{if .Arg.YDBPair}}{{.Arg.YDBPair}}, {{end}}opts ...query.ExecuteOption) ([]{{.Ret.DefineType}}, error) { {{end}} {{- $dbArg := "q.db" }}{{- if $.EmitMethodsWithDBArgument }}{{- $dbArg = "db" }}{{- end -}} {{- if .Arg.IsEmpty -}} @@ -83,7 +83,7 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBA {{- else -}} {{- .Arg.YDBParamsBuilder}} {{- if .Arg.YDBHasComplexContainers }} - for name, value := range manualParams.Range() { + for name, value := range params.Range() { parameters = parameters.Param(name).Any(value) } {{- end }} @@ -140,9 +140,9 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBA {{range .Comments}}//{{.}} {{end -}} {{if .Arg.YDBHasComplexContainers}} -func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBArgument}}db DBTX, {{end}}{{if not .Arg.IsEmpty}}{{.Arg.Pair}}, {{end}}manualParams ydb.Params, opts ...query.ExecuteOption) error { +func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBArgument}}db DBTX, {{end}}{{if .Arg.YDBPair}}{{.Arg.YDBPair}}, {{end}}params ydb.Params, opts ...query.ExecuteOption) error { {{else}} -func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBArgument}}db DBTX, {{end}}{{if not .Arg.IsEmpty}}{{.Arg.Pair}}, {{end}}opts ...query.ExecuteOption) error { +func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBArgument}}db DBTX, {{end}}{{if .Arg.YDBPair}}{{.Arg.YDBPair}}, {{end}}opts ...query.ExecuteOption) error { {{end -}} {{- $dbArg := "q.db" }}{{- if $.EmitMethodsWithDBArgument }}{{- $dbArg = "db" }}{{- end -}} {{- if .Arg.IsEmpty -}} @@ -150,7 +150,7 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{if $.EmitMethodsWithDBA {{- else -}} {{- .Arg.YDBParamsBuilder}} {{- if .Arg.YDBHasComplexContainers }} - for name, value := range manualParams.Range() { + for name, value := range params.Range() { parameters = parameters.Param(name).Any(value) } {{- end }} diff --git a/internal/codegen/golang/ydb_type.go b/internal/codegen/golang/ydb_type.go index 880cdb3106..e77c7bc823 100644 --- a/internal/codegen/golang/ydb_type.go +++ b/internal/codegen/golang/ydb_type.go @@ -12,7 +12,8 @@ import ( func YDBType(req *plugin.GenerateRequest, options *opts.Options, col *plugin.Column) string { columnType := strings.ToLower(sdk.DataType(col.Type)) - notNull := (col.NotNull || col.IsArray || col.IsSqlcSlice) && !isNullableType(columnType) + isArray := col.IsArray || col.IsSqlcSlice + notNull := (col.NotNull || isArray) && (!isNullableType(columnType) || isArray) emitPointersForNull := options.EmitPointersForNullTypes columnType = extractBaseType(columnType) diff --git a/internal/engine/ydb/convert.go b/internal/engine/ydb/convert.go index 235d55ec78..ac4aeef473 100755 --- a/internal/engine/ydb/convert.go +++ b/internal/engine/ydb/convert.go @@ -1797,7 +1797,7 @@ func (c *cc) VisitType_name_or_bind(n *parser.Type_name_or_bindContext) interfac if !ok { return todo("VisitType_name_or_bind", b) } - return &ast.TypeName{ + return &ast.TypeName{ // FIXME: this is not working right now for type definitions Names: &ast.List{ Items: []ast.Node{param}, }, @@ -1902,30 +1902,18 @@ func (c *cc) VisitType_name_composite(n *parser.Type_name_compositeContext) inte if struct_ := n.Type_name_struct(); struct_ != nil { if structArgs := struct_.AllStruct_arg(); len(structArgs) > 0 { - var items []ast.Node - for range structArgs { - // TODO: Handle struct field names and types - items = append(items, &ast.TODO{}) - } return &ast.TypeName{ - Name: "Struct", + Name: "any", TypeOid: 0, - Names: &ast.List{Items: items}, } } } if variant := n.Type_name_variant(); variant != nil { if variantArgs := variant.AllVariant_arg(); len(variantArgs) > 0 { - var items []ast.Node - for range variantArgs { - // TODO: Handle variant arguments - items = append(items, &ast.TODO{}) - } return &ast.TypeName{ - Name: "Variant", + Name: "any", TypeOid: 0, - Names: &ast.List{Items: items}, } } } @@ -1935,17 +1923,10 @@ func (c *cc) VisitType_name_composite(n *parser.Type_name_compositeContext) inte } if stream := n.Type_name_stream(); stream != nil { - if typeName := stream.Type_name_or_bind(); typeName != nil { - tn, ok := typeName.Accept(c).(ast.Node) - if !ok { - return todo("VisitType_name_composite", typeName) - } + if stream.Type_name_or_bind() != nil { return &ast.TypeName{ - Name: "Stream", + Name: "any", TypeOid: 0, - Names: &ast.List{ - Items: []ast.Node{tn}, - }, } } } @@ -1955,40 +1936,19 @@ func (c *cc) VisitType_name_composite(n *parser.Type_name_compositeContext) inte } if dict := n.Type_name_dict(); dict != nil { - if typeNames := dict.AllType_name_or_bind(); len(typeNames) >= 2 { - first, ok := typeNames[0].Accept(c).(ast.Node) - if !ok { - return todo("VisitType_name_composite", typeNames[0]) - } - second, ok := typeNames[1].Accept(c).(ast.Node) - if !ok { - return todo("VisitType_name_composite", typeNames[1]) - } + if dict.AllType_name_or_bind() != nil { return &ast.TypeName{ - Name: "Dict", + Name: "any", TypeOid: 0, - Names: &ast.List{ - Items: []ast.Node{ - first, - second, - }, - }, } } } if set := n.Type_name_set(); set != nil { - if typeName := set.Type_name_or_bind(); typeName != nil { - tn, ok := typeName.Accept(c).(ast.Node) - if !ok { - return todo("VisitType_name_composite", typeName) - } + if set.Type_name_or_bind() != nil { return &ast.TypeName{ - Name: "Set", + Name: "any", TypeOid: 0, - Names: &ast.List{ - Items: []ast.Node{tn}, - }, } } } @@ -2029,7 +1989,6 @@ func (c *cc) VisitType_name_optional(n *parser.Type_name_optionalContext) interf return &ast.TypeName{ Name: name, TypeOid: 0, - Names: &ast.List{}, } } @@ -2047,16 +2006,18 @@ func (c *cc) VisitType_name_list(n *parser.Type_name_listContext) interface{} { return todo("VisitType_name_list", n.Type_name_or_bind()) } - if innerTypeName.ArrayBounds == nil { - innerTypeName.ArrayBounds = &ast.List{} + if innerTypeName.ArrayBounds != nil { + return &ast.TypeName{ + Name: "any", + TypeOid: 0, + } } return &ast.TypeName{ Name: innerTypeName.Name, TypeOid: 0, - Names: innerTypeName.Names, ArrayBounds: &ast.List{ - Items: append(innerTypeName.ArrayBounds.Items, &ast.TODO{}), + Items: []ast.Node{&ast.TODO{}}, }, } } From 71869431274a3a79018fce2afb3490c875f75fd4 Mon Sep 17 00:00:00 2001 From: Viktor Pentyukhov Date: Tue, 7 Oct 2025 20:12:29 +0300 Subject: [PATCH 4/5] Fixed style and fignya fsyakaya --- internal/engine/ydb/convert.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/internal/engine/ydb/convert.go b/internal/engine/ydb/convert.go index ac4aeef473..2db1e2338c 100755 --- a/internal/engine/ydb/convert.go +++ b/internal/engine/ydb/convert.go @@ -1797,7 +1797,8 @@ func (c *cc) VisitType_name_or_bind(n *parser.Type_name_or_bindContext) interfac if !ok { return todo("VisitType_name_or_bind", b) } - return &ast.TypeName{ // FIXME: this is not working right now for type definitions + // FIXME: this is not working right now for type definitions + return &ast.TypeName{ Names: &ast.List{ Items: []ast.Node{param}, }, @@ -2051,8 +2052,6 @@ func (c *cc) VisitType_name_tuple(n *parser.Type_name_tupleContext) interface{} } } - fmt.Println("Debug: typeName", typeName) - return &ast.TypeName{ Name: typeName, TypeOid: 0, @@ -2371,7 +2370,6 @@ func (c *cc) VisitXor_subexpr(n *parser.Xor_subexprContext) interface{} { if inExpr := condCtx.In_expr(); inExpr != nil { node, ok := inExpr.Accept(c).(ast.Node) if !ok { - log.Printf("VisitXor_subexpr 0") return todo("VisitXor_subexpr", inExpr) } return &ast.In{ @@ -3356,7 +3354,7 @@ func (c *cc) VisitLambda(n *parser.LambdaContext) interface{} { } if n.ARROW() != nil { - log.Panicln("Lambda stmts are not supported in Sqlc") + log.Panicln("Lambda stmts are not supported in SQLC") return todo("VisitLambda", n) } From 1ca90c16bee3b7b20980f846d544a3d014f7f336 Mon Sep 17 00:00:00 2001 From: Viktor Pentyukhov Date: Tue, 7 Oct 2025 20:48:10 +0300 Subject: [PATCH 5/5] Fixed style and fignya fsyakaya --- internal/engine/ydb/convert.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/engine/ydb/convert.go b/internal/engine/ydb/convert.go index 2db1e2338c..fb818a5d5b 100755 --- a/internal/engine/ydb/convert.go +++ b/internal/engine/ydb/convert.go @@ -2037,7 +2037,7 @@ func (c *cc) VisitType_name_tuple(n *parser.Type_name_tupleContext) interface{} items = append(items, tnNode) } - var typeName string = "" + var typeName string for _, node := range items { switch innerTypeName := node.(type) { case *ast.TypeName: @@ -2384,12 +2384,12 @@ func (c *cc) VisitXor_subexpr(n *parser.Xor_subexprContext) interface{} { first, ok := eqSubs[0].Accept(c).(ast.Node) if !ok { - return todo("VisitXor_subexpr 1", n) + return todo("VisitXor_subexpr", n) } second, ok := eqSubs[1].Accept(c).(ast.Node) if !ok { - return todo("VisitXor_subexpr 2", n) + return todo("VisitXor_subexpr", n) } return &ast.BetweenExpr{