From 152bb611327b85d2230af81895f321d131c8415f Mon Sep 17 00:00:00 2001 From: Bastien Riviere Date: Tue, 23 Sep 2025 18:15:01 +0200 Subject: [PATCH] fix: let sqlc output nullable array This allow postgres to output nullable array. --- docs/reference/config.md | 16 ++-- internal/codegen/golang/mysql_type.go | 2 +- internal/codegen/golang/opts/options.go | 1 + internal/codegen/golang/postgresql_type.go | 2 +- internal/codegen/golang/sqlite_type.go | 2 +- internal/config/v_one.go | 2 + .../unnest_null/postgresql/pgx/v4/go/db.go | 32 ++++++++ .../postgresql/pgx/v4/go/models.go | 23 ++++++ .../postgresql/pgx/v4/go/querier.go | 18 +++++ .../postgresql/pgx/v4/go/query.sql.go | 69 +++++++++++++++++ .../unnest_null/postgresql/pgx/v4/query.sql | 9 +++ .../unnest_null/postgresql/pgx/v4/schema.sql | 10 +++ .../unnest_null/postgresql/pgx/v4/sqlc.json | 15 ++++ .../unnest_null/postgresql/pgx/v5/go/db.go | 32 ++++++++ .../postgresql/pgx/v5/go/models.go | 20 +++++ .../postgresql/pgx/v5/go/querier.go | 18 +++++ .../postgresql/pgx/v5/go/query.sql.go | 69 +++++++++++++++++ .../unnest_null/postgresql/pgx/v5/query.sql | 9 +++ .../unnest_null/postgresql/pgx/v5/schema.sql | 10 +++ .../unnest_null/postgresql/pgx/v5/sqlc.json | 15 ++++ .../unnest_null/postgresql/stdlib/go/db.go | 31 ++++++++ .../postgresql/stdlib/go/models.go | 23 ++++++ .../postgresql/stdlib/go/querier.go | 18 +++++ .../postgresql/stdlib/go/query.sql.go | 76 +++++++++++++++++++ .../unnest_null/postgresql/stdlib/query.sql | 9 +++ .../unnest_null/postgresql/stdlib/schema.sql | 10 +++ .../unnest_null/postgresql/stdlib/sqlc.json | 14 ++++ 27 files changed, 545 insertions(+), 10 deletions(-) create mode 100644 internal/endtoend/testdata/unnest_null/postgresql/pgx/v4/go/db.go create mode 100644 internal/endtoend/testdata/unnest_null/postgresql/pgx/v4/go/models.go create mode 100644 internal/endtoend/testdata/unnest_null/postgresql/pgx/v4/go/querier.go create mode 100644 internal/endtoend/testdata/unnest_null/postgresql/pgx/v4/go/query.sql.go create mode 100644 internal/endtoend/testdata/unnest_null/postgresql/pgx/v4/query.sql create mode 100644 internal/endtoend/testdata/unnest_null/postgresql/pgx/v4/schema.sql create mode 100644 internal/endtoend/testdata/unnest_null/postgresql/pgx/v4/sqlc.json create mode 100644 internal/endtoend/testdata/unnest_null/postgresql/pgx/v5/go/db.go create mode 100644 internal/endtoend/testdata/unnest_null/postgresql/pgx/v5/go/models.go create mode 100644 internal/endtoend/testdata/unnest_null/postgresql/pgx/v5/go/querier.go create mode 100644 internal/endtoend/testdata/unnest_null/postgresql/pgx/v5/go/query.sql.go create mode 100644 internal/endtoend/testdata/unnest_null/postgresql/pgx/v5/query.sql create mode 100644 internal/endtoend/testdata/unnest_null/postgresql/pgx/v5/schema.sql create mode 100644 internal/endtoend/testdata/unnest_null/postgresql/pgx/v5/sqlc.json create mode 100644 internal/endtoend/testdata/unnest_null/postgresql/stdlib/go/db.go create mode 100644 internal/endtoend/testdata/unnest_null/postgresql/stdlib/go/models.go create mode 100644 internal/endtoend/testdata/unnest_null/postgresql/stdlib/go/querier.go create mode 100644 internal/endtoend/testdata/unnest_null/postgresql/stdlib/go/query.sql.go create mode 100644 internal/endtoend/testdata/unnest_null/postgresql/stdlib/query.sql create mode 100644 internal/endtoend/testdata/unnest_null/postgresql/stdlib/schema.sql create mode 100644 internal/endtoend/testdata/unnest_null/postgresql/stdlib/sqlc.json diff --git a/docs/reference/config.md b/docs/reference/config.md index ff8bcd0890..67e89d258d 100644 --- a/docs/reference/config.md +++ b/docs/reference/config.md @@ -14,7 +14,7 @@ sql: queries: "postgresql/query.sql" engine: "postgresql" gen: - go: + go: package: "authors" out: "postgresql" database: @@ -122,7 +122,7 @@ The `analyzer` mapping supports the following keys: - `database`: - If false, do not use the configured database for query analysis. Defaults to `true`. - + ### gen The `gen` mapping supports the following keys: @@ -159,6 +159,8 @@ The `gen` mapping supports the following keys: - If true, generated methods will accept a DBTX argument instead of storing a DBTX on the `*Queries` struct. Defaults to `false`. - `emit_pointers_for_null_types`: - If true, generated types for nullable columns are emitted as pointers (ie. `*string`) instead of `database/sql` null types (ie. `NullString`). Currently only supported for PostgreSQL if `sql_package` is `pgx/v4` or `pgx/v5`, and for SQLite. Defaults to `false`. +- `emit_nullable_for_null_arrays`: + - If true, generated types for nullable columns with array types are emitted as list of nullable instead of a list of non-nullable. For example, `bool[]` SQL type is emitted as `[]sql.NullBool` instead of `[]bool` when the flag is set. Defaults to `false`. - `emit_enum_valid_method`: - If true, generate a Valid method on enum types, indicating whether a string is a valid enum value. @@ -255,18 +257,18 @@ Each mapping in the `plugins` collection has the following keys: - The URL to fetch the WASM file. Supports the `https://` or `file://` schemes. - `sha256` - The SHA256 checksum for the downloaded file. - + ```yaml version: "2" plugins: - name: "py" - wasm: + wasm: url: "https://github.com/sqlc-dev/sqlc-gen-python/releases/download/v0.16.0-alpha/sqlc-gen-python.wasm" sha256: "428476c7408fd4c032da4ec74e8a7344f4fa75e0f98a5a3302f238283b9b95f2" - name: "js" env: - PATH - process: + process: cmd: "sqlc-gen-json" ``` @@ -283,7 +285,7 @@ Each mapping in the `rules` collection has the following keys: See the [vet](../howto/vet.md) documentation for a list of built-in rules and help writing custom rules. - + ```yaml version: "2" sql: @@ -317,7 +319,7 @@ rules: rule: | query.cmd == "exec" ``` - + ### Global overrides Sometimes, the same configuration must be done across various specifications of diff --git a/internal/codegen/golang/mysql_type.go b/internal/codegen/golang/mysql_type.go index b8e8aa43c7..5529bf5c29 100644 --- a/internal/codegen/golang/mysql_type.go +++ b/internal/codegen/golang/mysql_type.go @@ -11,7 +11,7 @@ import ( func mysqlType(req *plugin.GenerateRequest, options *opts.Options, col *plugin.Column) string { columnType := sdk.DataType(col.Type) - notNull := col.NotNull || col.IsArray + notNull := col.NotNull || (col.IsArray && !options.EmitNullableForNullArrays) unsigned := col.Unsigned switch columnType { diff --git a/internal/codegen/golang/opts/options.go b/internal/codegen/golang/opts/options.go index 0d5d51c2dd..5b2d46dfd5 100644 --- a/internal/codegen/golang/opts/options.go +++ b/internal/codegen/golang/opts/options.go @@ -22,6 +22,7 @@ type Options struct { EmitParamsStructPointers bool `json:"emit_params_struct_pointers" yaml:"emit_params_struct_pointers"` EmitMethodsWithDbArgument bool `json:"emit_methods_with_db_argument,omitempty" yaml:"emit_methods_with_db_argument"` EmitPointersForNullTypes bool `json:"emit_pointers_for_null_types" yaml:"emit_pointers_for_null_types"` + EmitNullableForNullArrays bool `json:"emit_nullable_for_null_arrays" yaml:"emit_nullable_for_null_arrays"` EmitEnumValidMethod bool `json:"emit_enum_valid_method,omitempty" yaml:"emit_enum_valid_method"` EmitAllEnumValues bool `json:"emit_all_enum_values,omitempty" yaml:"emit_all_enum_values"` EmitSqlAsComment bool `json:"emit_sql_as_comment,omitempty" yaml:"emit_sql_as_comment"` diff --git a/internal/codegen/golang/postgresql_type.go b/internal/codegen/golang/postgresql_type.go index 398d01e2e8..440c69e0cf 100644 --- a/internal/codegen/golang/postgresql_type.go +++ b/internal/codegen/golang/postgresql_type.go @@ -36,7 +36,7 @@ func parseIdentifierString(name string) (*plugin.Identifier, error) { func postgresType(req *plugin.GenerateRequest, options *opts.Options, col *plugin.Column) string { columnType := sdk.DataType(col.Type) - notNull := col.NotNull || col.IsArray + notNull := col.NotNull || (col.IsArray && !options.EmitNullableForNullArrays) driver := parseDriver(options.SqlPackage) emitPointersForNull := driver.IsPGX() && options.EmitPointersForNullTypes diff --git a/internal/codegen/golang/sqlite_type.go b/internal/codegen/golang/sqlite_type.go index 8a22aaa262..b6a384e732 100644 --- a/internal/codegen/golang/sqlite_type.go +++ b/internal/codegen/golang/sqlite_type.go @@ -12,7 +12,7 @@ import ( func sqliteType(req *plugin.GenerateRequest, options *opts.Options, col *plugin.Column) string { dt := strings.ToLower(sdk.DataType(col.Type)) - notNull := col.NotNull || col.IsArray + notNull := col.NotNull || (col.IsArray && !options.EmitNullableForNullArrays) emitPointersForNull := options.EmitPointersForNullTypes switch dt { diff --git a/internal/config/v_one.go b/internal/config/v_one.go index 8efa9f42fc..56d7bc5c70 100644 --- a/internal/config/v_one.go +++ b/internal/config/v_one.go @@ -39,6 +39,7 @@ type v1PackageSettings struct { EmitParamsStructPointers bool `json:"emit_params_struct_pointers" yaml:"emit_params_struct_pointers"` EmitMethodsWithDBArgument bool `json:"emit_methods_with_db_argument" yaml:"emit_methods_with_db_argument"` EmitPointersForNullTypes bool `json:"emit_pointers_for_null_types" yaml:"emit_pointers_for_null_types"` + EmitNullableForNullArrays bool `json:"emit_nullable_for_null_arrays" yaml:"emit_nullable_for_null_arrays"` EmitEnumValidMethod bool `json:"emit_enum_valid_method,omitempty" yaml:"emit_enum_valid_method"` EmitAllEnumValues bool `json:"emit_all_enum_values,omitempty" yaml:"emit_all_enum_values"` EmitSqlAsComment bool `json:"emit_sql_as_comment,omitempty" yaml:"emit_sql_as_comment"` @@ -149,6 +150,7 @@ func (c *V1GenerateSettings) Translate() Config { EmitParamsStructPointers: pkg.EmitParamsStructPointers, EmitMethodsWithDbArgument: pkg.EmitMethodsWithDBArgument, EmitPointersForNullTypes: pkg.EmitPointersForNullTypes, + EmitNullableForNullArrays: pkg.EmitNullableForNullArrays, EmitEnumValidMethod: pkg.EmitEnumValidMethod, EmitAllEnumValues: pkg.EmitAllEnumValues, EmitSqlAsComment: pkg.EmitSqlAsComment, diff --git a/internal/endtoend/testdata/unnest_null/postgresql/pgx/v4/go/db.go b/internal/endtoend/testdata/unnest_null/postgresql/pgx/v4/go/db.go new file mode 100644 index 0000000000..3895084dc3 --- /dev/null +++ b/internal/endtoend/testdata/unnest_null/postgresql/pgx/v4/go/db.go @@ -0,0 +1,32 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 + +package querytest + +import ( + "context" + + "github.com/jackc/pgconn" + "github.com/jackc/pgx/v4" +) + +type DBTX interface { + Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) + Query(context.Context, string, ...interface{}) (pgx.Rows, error) + QueryRow(context.Context, string, ...interface{}) pgx.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx pgx.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/unnest_null/postgresql/pgx/v4/go/models.go b/internal/endtoend/testdata/unnest_null/postgresql/pgx/v4/go/models.go new file mode 100644 index 0000000000..464c1d3f07 --- /dev/null +++ b/internal/endtoend/testdata/unnest_null/postgresql/pgx/v4/go/models.go @@ -0,0 +1,23 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 + +package querytest + +import ( + "database/sql" + "time" + + "github.com/google/uuid" +) + +type Memory struct { + ID uuid.UUID + VampireID uuid.NullUUID + CreatedAt time.Time + UpdatedAt sql.NullTime +} + +type Vampire struct { + ID uuid.UUID +} diff --git a/internal/endtoend/testdata/unnest_null/postgresql/pgx/v4/go/querier.go b/internal/endtoend/testdata/unnest_null/postgresql/pgx/v4/go/querier.go new file mode 100644 index 0000000000..972d9d6857 --- /dev/null +++ b/internal/endtoend/testdata/unnest_null/postgresql/pgx/v4/go/querier.go @@ -0,0 +1,18 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 + +package querytest + +import ( + "context" + + "github.com/google/uuid" +) + +type Querier interface { + CreateMemories(ctx context.Context, vamprieID []uuid.NullUUID) ([]Memory, error) + GetVampireIDs(ctx context.Context, vampireID []uuid.NullUUID) ([]uuid.UUID, error) +} + +var _ Querier = (*Queries)(nil) diff --git a/internal/endtoend/testdata/unnest_null/postgresql/pgx/v4/go/query.sql.go b/internal/endtoend/testdata/unnest_null/postgresql/pgx/v4/go/query.sql.go new file mode 100644 index 0000000000..ba052887f6 --- /dev/null +++ b/internal/endtoend/testdata/unnest_null/postgresql/pgx/v4/go/query.sql.go @@ -0,0 +1,69 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: query.sql + +package querytest + +import ( + "context" + + "github.com/google/uuid" +) + +const createMemories = `-- name: CreateMemories :many +INSERT INTO memories (vampire_id) +SELECT + unnest($1::uuid[]) AS vampire_id +RETURNING + id, vampire_id, created_at, updated_at +` + +func (q *Queries) CreateMemories(ctx context.Context, vamprieID []uuid.NullUUID) ([]Memory, error) { + rows, err := q.db.Query(ctx, createMemories, vamprieID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Memory + for rows.Next() { + var i Memory + if err := rows.Scan( + &i.ID, + &i.VampireID, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getVampireIDs = `-- name: GetVampireIDs :many +SELECT vampires.id::uuid FROM unnest($1::uuid[]) AS vampires (id) +` + +func (q *Queries) GetVampireIDs(ctx context.Context, vampireID []uuid.NullUUID) ([]uuid.UUID, error) { + rows, err := q.db.Query(ctx, getVampireIDs, vampireID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []uuid.UUID + for rows.Next() { + var vampires_id uuid.UUID + if err := rows.Scan(&vampires_id); err != nil { + return nil, err + } + items = append(items, vampires_id) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/endtoend/testdata/unnest_null/postgresql/pgx/v4/query.sql b/internal/endtoend/testdata/unnest_null/postgresql/pgx/v4/query.sql new file mode 100644 index 0000000000..0698875a86 --- /dev/null +++ b/internal/endtoend/testdata/unnest_null/postgresql/pgx/v4/query.sql @@ -0,0 +1,9 @@ +-- name: CreateMemories :many +INSERT INTO memories (vampire_id) +SELECT + unnest(sqlc.narg(vamprie_id)::uuid[]) AS vampire_id +RETURNING + *; + +-- name: GetVampireIDs :many +SELECT vampires.id::uuid FROM unnest(sqlc.narg(vampire_id)::uuid[]) AS vampires (id); diff --git a/internal/endtoend/testdata/unnest_null/postgresql/pgx/v4/schema.sql b/internal/endtoend/testdata/unnest_null/postgresql/pgx/v4/schema.sql new file mode 100644 index 0000000000..a59b9b26b6 --- /dev/null +++ b/internal/endtoend/testdata/unnest_null/postgresql/pgx/v4/schema.sql @@ -0,0 +1,10 @@ +CREATE TABLE vampires ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid () +); + +CREATE TABLE memories ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid (), + vampire_id uuid REFERENCES vampires (id), + created_at timestamp NOT NULL DEFAULT NOW(), + updated_at timestamp +); diff --git a/internal/endtoend/testdata/unnest_null/postgresql/pgx/v4/sqlc.json b/internal/endtoend/testdata/unnest_null/postgresql/pgx/v4/sqlc.json new file mode 100644 index 0000000000..a9049714b3 --- /dev/null +++ b/internal/endtoend/testdata/unnest_null/postgresql/pgx/v4/sqlc.json @@ -0,0 +1,15 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "postgresql", + "sql_package": "pgx/v4", + "name": "querytest", + "schema": "schema.sql", + "queries": "query.sql", + "emit_interface": true, + "emit_nullable_for_null_arrays": true + } + ] +} diff --git a/internal/endtoend/testdata/unnest_null/postgresql/pgx/v5/go/db.go b/internal/endtoend/testdata/unnest_null/postgresql/pgx/v5/go/db.go new file mode 100644 index 0000000000..1e00549714 --- /dev/null +++ b/internal/endtoend/testdata/unnest_null/postgresql/pgx/v5/go/db.go @@ -0,0 +1,32 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 + +package querytest + +import ( + "context" + + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" +) + +type DBTX interface { + Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) + Query(context.Context, string, ...interface{}) (pgx.Rows, error) + QueryRow(context.Context, string, ...interface{}) pgx.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx pgx.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/unnest_null/postgresql/pgx/v5/go/models.go b/internal/endtoend/testdata/unnest_null/postgresql/pgx/v5/go/models.go new file mode 100644 index 0000000000..4d78ae55e9 --- /dev/null +++ b/internal/endtoend/testdata/unnest_null/postgresql/pgx/v5/go/models.go @@ -0,0 +1,20 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 + +package querytest + +import ( + "github.com/jackc/pgx/v5/pgtype" +) + +type Memory struct { + ID pgtype.UUID + VampireID pgtype.UUID + CreatedAt pgtype.Timestamp + UpdatedAt pgtype.Timestamp +} + +type Vampire struct { + ID pgtype.UUID +} diff --git a/internal/endtoend/testdata/unnest_null/postgresql/pgx/v5/go/querier.go b/internal/endtoend/testdata/unnest_null/postgresql/pgx/v5/go/querier.go new file mode 100644 index 0000000000..8f057d9828 --- /dev/null +++ b/internal/endtoend/testdata/unnest_null/postgresql/pgx/v5/go/querier.go @@ -0,0 +1,18 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 + +package querytest + +import ( + "context" + + "github.com/jackc/pgx/v5/pgtype" +) + +type Querier interface { + CreateMemories(ctx context.Context, vampireID []pgtype.UUID) ([]Memory, error) + GetVampireIDs(ctx context.Context, vampireID []pgtype.UUID) ([]pgtype.UUID, error) +} + +var _ Querier = (*Queries)(nil) diff --git a/internal/endtoend/testdata/unnest_null/postgresql/pgx/v5/go/query.sql.go b/internal/endtoend/testdata/unnest_null/postgresql/pgx/v5/go/query.sql.go new file mode 100644 index 0000000000..3e8cfe29e3 --- /dev/null +++ b/internal/endtoend/testdata/unnest_null/postgresql/pgx/v5/go/query.sql.go @@ -0,0 +1,69 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: query.sql + +package querytest + +import ( + "context" + + "github.com/jackc/pgx/v5/pgtype" +) + +const createMemories = `-- name: CreateMemories :many +INSERT INTO memories (vampire_id) +SELECT + unnest($1::uuid[]) AS vampire_id +RETURNING + id, vampire_id, created_at, updated_at +` + +func (q *Queries) CreateMemories(ctx context.Context, vampireID []pgtype.UUID) ([]Memory, error) { + rows, err := q.db.Query(ctx, createMemories, vampireID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Memory + for rows.Next() { + var i Memory + if err := rows.Scan( + &i.ID, + &i.VampireID, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getVampireIDs = `-- name: GetVampireIDs :many +SELECT vampires.id::uuid FROM unnest($1::uuid[]) AS vampires (id) +` + +func (q *Queries) GetVampireIDs(ctx context.Context, vampireID []pgtype.UUID) ([]pgtype.UUID, error) { + rows, err := q.db.Query(ctx, getVampireIDs, vampireID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []pgtype.UUID + for rows.Next() { + var vampires_id pgtype.UUID + if err := rows.Scan(&vampires_id); err != nil { + return nil, err + } + items = append(items, vampires_id) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/endtoend/testdata/unnest_null/postgresql/pgx/v5/query.sql b/internal/endtoend/testdata/unnest_null/postgresql/pgx/v5/query.sql new file mode 100644 index 0000000000..ade4997620 --- /dev/null +++ b/internal/endtoend/testdata/unnest_null/postgresql/pgx/v5/query.sql @@ -0,0 +1,9 @@ +-- name: CreateMemories :many +INSERT INTO memories (vampire_id) +SELECT + unnest(sqlc.narg(vampire_id)::uuid[]) AS vampire_id +RETURNING + *; + +-- name: GetVampireIDs :many +SELECT vampires.id::uuid FROM unnest(sqlc.narg(vampire_id)::uuid[]) AS vampires (id); diff --git a/internal/endtoend/testdata/unnest_null/postgresql/pgx/v5/schema.sql b/internal/endtoend/testdata/unnest_null/postgresql/pgx/v5/schema.sql new file mode 100644 index 0000000000..a59b9b26b6 --- /dev/null +++ b/internal/endtoend/testdata/unnest_null/postgresql/pgx/v5/schema.sql @@ -0,0 +1,10 @@ +CREATE TABLE vampires ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid () +); + +CREATE TABLE memories ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid (), + vampire_id uuid REFERENCES vampires (id), + created_at timestamp NOT NULL DEFAULT NOW(), + updated_at timestamp +); diff --git a/internal/endtoend/testdata/unnest_null/postgresql/pgx/v5/sqlc.json b/internal/endtoend/testdata/unnest_null/postgresql/pgx/v5/sqlc.json new file mode 100644 index 0000000000..a22c260ad7 --- /dev/null +++ b/internal/endtoend/testdata/unnest_null/postgresql/pgx/v5/sqlc.json @@ -0,0 +1,15 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "postgresql", + "sql_package": "pgx/v5", + "name": "querytest", + "schema": "schema.sql", + "queries": "query.sql", + "emit_interface": true, + "emit_nullable_for_null_arrays": true + } + ] +} diff --git a/internal/endtoend/testdata/unnest_null/postgresql/stdlib/go/db.go b/internal/endtoend/testdata/unnest_null/postgresql/stdlib/go/db.go new file mode 100644 index 0000000000..3b320aa168 --- /dev/null +++ b/internal/endtoend/testdata/unnest_null/postgresql/stdlib/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 + +package querytest + +import ( + "context" + "database/sql" +) + +type DBTX interface { + ExecContext(context.Context, string, ...interface{}) (sql.Result, error) + PrepareContext(context.Context, string) (*sql.Stmt, error) + QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) + QueryRowContext(context.Context, string, ...interface{}) *sql.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/unnest_null/postgresql/stdlib/go/models.go b/internal/endtoend/testdata/unnest_null/postgresql/stdlib/go/models.go new file mode 100644 index 0000000000..464c1d3f07 --- /dev/null +++ b/internal/endtoend/testdata/unnest_null/postgresql/stdlib/go/models.go @@ -0,0 +1,23 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 + +package querytest + +import ( + "database/sql" + "time" + + "github.com/google/uuid" +) + +type Memory struct { + ID uuid.UUID + VampireID uuid.NullUUID + CreatedAt time.Time + UpdatedAt sql.NullTime +} + +type Vampire struct { + ID uuid.UUID +} diff --git a/internal/endtoend/testdata/unnest_null/postgresql/stdlib/go/querier.go b/internal/endtoend/testdata/unnest_null/postgresql/stdlib/go/querier.go new file mode 100644 index 0000000000..f5128c8ac9 --- /dev/null +++ b/internal/endtoend/testdata/unnest_null/postgresql/stdlib/go/querier.go @@ -0,0 +1,18 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 + +package querytest + +import ( + "context" + + "github.com/google/uuid" +) + +type Querier interface { + CreateMemories(ctx context.Context, vampireID []uuid.NullUUID) ([]Memory, error) + GetVampireIDs(ctx context.Context, vampireID []uuid.NullUUID) ([]uuid.UUID, error) +} + +var _ Querier = (*Queries)(nil) diff --git a/internal/endtoend/testdata/unnest_null/postgresql/stdlib/go/query.sql.go b/internal/endtoend/testdata/unnest_null/postgresql/stdlib/go/query.sql.go new file mode 100644 index 0000000000..4e3f69746e --- /dev/null +++ b/internal/endtoend/testdata/unnest_null/postgresql/stdlib/go/query.sql.go @@ -0,0 +1,76 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: query.sql + +package querytest + +import ( + "context" + + "github.com/google/uuid" + "github.com/lib/pq" +) + +const createMemories = `-- name: CreateMemories :many +INSERT INTO memories (vampire_id) +SELECT + unnest($1::uuid[]) AS vampire_id +RETURNING + id, vampire_id, created_at, updated_at +` + +func (q *Queries) CreateMemories(ctx context.Context, vampireID []uuid.NullUUID) ([]Memory, error) { + rows, err := q.db.QueryContext(ctx, createMemories, pq.Array(vampireID)) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Memory + for rows.Next() { + var i Memory + if err := rows.Scan( + &i.ID, + &i.VampireID, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const getVampireIDs = `-- name: GetVampireIDs :many +SELECT vampires.id::uuid FROM unnest($1::uuid[]) AS vampires (id) +` + +func (q *Queries) GetVampireIDs(ctx context.Context, vampireID []uuid.NullUUID) ([]uuid.UUID, error) { + rows, err := q.db.QueryContext(ctx, getVampireIDs, pq.Array(vampireID)) + if err != nil { + return nil, err + } + defer rows.Close() + var items []uuid.UUID + for rows.Next() { + var vampires_id uuid.UUID + if err := rows.Scan(&vampires_id); err != nil { + return nil, err + } + items = append(items, vampires_id) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/endtoend/testdata/unnest_null/postgresql/stdlib/query.sql b/internal/endtoend/testdata/unnest_null/postgresql/stdlib/query.sql new file mode 100644 index 0000000000..ade4997620 --- /dev/null +++ b/internal/endtoend/testdata/unnest_null/postgresql/stdlib/query.sql @@ -0,0 +1,9 @@ +-- name: CreateMemories :many +INSERT INTO memories (vampire_id) +SELECT + unnest(sqlc.narg(vampire_id)::uuid[]) AS vampire_id +RETURNING + *; + +-- name: GetVampireIDs :many +SELECT vampires.id::uuid FROM unnest(sqlc.narg(vampire_id)::uuid[]) AS vampires (id); diff --git a/internal/endtoend/testdata/unnest_null/postgresql/stdlib/schema.sql b/internal/endtoend/testdata/unnest_null/postgresql/stdlib/schema.sql new file mode 100644 index 0000000000..a59b9b26b6 --- /dev/null +++ b/internal/endtoend/testdata/unnest_null/postgresql/stdlib/schema.sql @@ -0,0 +1,10 @@ +CREATE TABLE vampires ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid () +); + +CREATE TABLE memories ( + id uuid PRIMARY KEY DEFAULT gen_random_uuid (), + vampire_id uuid REFERENCES vampires (id), + created_at timestamp NOT NULL DEFAULT NOW(), + updated_at timestamp +); diff --git a/internal/endtoend/testdata/unnest_null/postgresql/stdlib/sqlc.json b/internal/endtoend/testdata/unnest_null/postgresql/stdlib/sqlc.json new file mode 100644 index 0000000000..1bb0c446f0 --- /dev/null +++ b/internal/endtoend/testdata/unnest_null/postgresql/stdlib/sqlc.json @@ -0,0 +1,14 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "postgresql", + "name": "querytest", + "schema": "schema.sql", + "queries": "query.sql", + "emit_interface": true, + "emit_nullable_for_null_arrays": true + } + ] +}