From 81dd475b48d5e121a47e13e62e59b08742847ab7 Mon Sep 17 00:00:00 2001 From: abdullah2993 Date: Wed, 10 Jan 2024 02:34:16 +0500 Subject: [PATCH] Add support for omitempty in JSON tags Adds a new configuration option json_tags_omit_empty --- docs/reference/config.md | 2 + internal/codegen/golang/field.go | 11 ++++-- internal/codegen/golang/opts/options.go | 1 + internal/config/v_one.go | 2 + .../omit_empty/postgresql/pgx/v4/go/db.go | 32 ++++++++++++++++ .../omit_empty/postgresql/pgx/v4/go/models.go | 15 ++++++++ .../postgresql/pgx/v4/go/query.sql.go | 34 +++++++++++++++++ .../omit_empty/postgresql/pgx/v4/query.sql | 2 + .../omit_empty/postgresql/pgx/v4/schema.sql | 6 +++ .../omit_empty/postgresql/pgx/v4/sqlc.json | 15 ++++++++ .../omit_empty/postgresql/pgx/v5/go/db.go | 32 ++++++++++++++++ .../omit_empty/postgresql/pgx/v5/go/models.go | 15 ++++++++ .../postgresql/pgx/v5/go/query.sql.go | 34 +++++++++++++++++ .../omit_empty/postgresql/pgx/v5/query.sql | 2 + .../omit_empty/postgresql/pgx/v5/schema.sql | 6 +++ .../omit_empty/postgresql/pgx/v5/sqlc.json | 15 ++++++++ .../omit_empty/postgresql/stdlib/go/db.go | 31 ++++++++++++++++ .../omit_empty/postgresql/stdlib/go/models.go | 15 ++++++++ .../postgresql/stdlib/go/query.sql.go | 37 +++++++++++++++++++ .../omit_empty/postgresql/stdlib/query.sql | 2 + .../omit_empty/postgresql/stdlib/schema.sql | 6 +++ .../omit_empty/postgresql/stdlib/sqlc.json | 14 +++++++ 22 files changed, 325 insertions(+), 4 deletions(-) create mode 100644 internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v4/go/db.go create mode 100644 internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v4/go/models.go create mode 100644 internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v4/go/query.sql.go create mode 100644 internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v4/query.sql create mode 100644 internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v4/schema.sql create mode 100644 internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v4/sqlc.json create mode 100644 internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v5/go/db.go create mode 100644 internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v5/go/models.go create mode 100644 internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v5/go/query.sql.go create mode 100644 internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v5/query.sql create mode 100644 internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v5/schema.sql create mode 100644 internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v5/sqlc.json create mode 100644 internal/endtoend/testdata/json_tags/omit_empty/postgresql/stdlib/go/db.go create mode 100644 internal/endtoend/testdata/json_tags/omit_empty/postgresql/stdlib/go/models.go create mode 100644 internal/endtoend/testdata/json_tags/omit_empty/postgresql/stdlib/go/query.sql.go create mode 100644 internal/endtoend/testdata/json_tags/omit_empty/postgresql/stdlib/query.sql create mode 100644 internal/endtoend/testdata/json_tags/omit_empty/postgresql/stdlib/schema.sql create mode 100644 internal/endtoend/testdata/json_tags/omit_empty/postgresql/stdlib/sqlc.json diff --git a/docs/reference/config.md b/docs/reference/config.md index 85e68cf05b..3d55878996 100644 --- a/docs/reference/config.md +++ b/docs/reference/config.md @@ -171,6 +171,8 @@ The `gen` mapping supports the following keys: - If true, "Id" in json tags will be uppercase. If false, will be camelcase. Defaults to `false` - `json_tags_case_style`: - `camel` for camelCase, `pascal` for PascalCase, `snake` for snake_case or `none` to use the column name in the DB. Defaults to `none`. +- `json_tags_omit_empty`: + - If true will add `omitempty` to JSON tags. Defaults to `false`. - `omit_unused_structs`: - If `true`, sqlc won't generate table and enum structs that aren't used in queries for a given package. Defaults to `false`. - `output_batch_file_name`: diff --git a/internal/codegen/golang/field.go b/internal/codegen/golang/field.go index 2a63b6d342..941bbc3c8a 100644 --- a/internal/codegen/golang/field.go +++ b/internal/codegen/golang/field.go @@ -44,11 +44,14 @@ func TagsToString(tags map[string]string) string { func JSONTagName(name string, options *opts.Options) string { style := options.JsonTagsCaseStyle idUppercase := options.JsonTagsIdUppercase - if style == "" || style == "none" { - return name - } else { - return SetJSONCaseStyle(name, style, idUppercase) + addOmitEmpty := options.JsonTagsOmitEmpty + if style != "" && style != "none" { + name = SetJSONCaseStyle(name, style, idUppercase) } + if addOmitEmpty { + name = name + ",omitempty" + } + return name } func SetCaseStyle(name string, style string) string { diff --git a/internal/codegen/golang/opts/options.go b/internal/codegen/golang/opts/options.go index 0b66975506..9a65541165 100644 --- a/internal/codegen/golang/opts/options.go +++ b/internal/codegen/golang/opts/options.go @@ -26,6 +26,7 @@ type Options struct { 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"` JsonTagsCaseStyle string `json:"json_tags_case_style,omitempty" yaml:"json_tags_case_style"` + JsonTagsOmitEmpty bool `json:"json_tags_omit_empty,omitempty" yaml:"json_tags_omit_empty"` Package string `json:"package" yaml:"package"` Out string `json:"out" yaml:"out"` Overrides []Override `json:"overrides,omitempty" yaml:"overrides"` diff --git a/internal/config/v_one.go b/internal/config/v_one.go index 8efa9f42fc..cf7b22e519 100644 --- a/internal/config/v_one.go +++ b/internal/config/v_one.go @@ -43,6 +43,7 @@ type v1PackageSettings struct { 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"` JSONTagsCaseStyle string `json:"json_tags_case_style,omitempty" yaml:"json_tags_case_style"` + JSONTagsOmitEmpty bool `json:"json_tags_omit_empty,omitempty" yaml:"json_tags_omit_empty"` SQLPackage string `json:"sql_package" yaml:"sql_package"` SQLDriver string `json:"sql_driver" yaml:"sql_driver"` Overrides []golang.Override `json:"overrides" yaml:"overrides"` @@ -158,6 +159,7 @@ func (c *V1GenerateSettings) Translate() Config { SqlDriver: pkg.SQLDriver, Overrides: pkg.Overrides, JsonTagsCaseStyle: pkg.JSONTagsCaseStyle, + JsonTagsOmitEmpty: pkg.JSONTagsOmitEmpty, OutputBatchFileName: pkg.OutputBatchFileName, OutputDbFileName: pkg.OutputDBFileName, OutputModelsFileName: pkg.OutputModelsFileName, diff --git a/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v4/go/db.go b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v4/go/db.go new file mode 100644 index 0000000000..64a2dde21d --- /dev/null +++ b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v4/go/db.go @@ -0,0 +1,32 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.25.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/json_tags/omit_empty/postgresql/pgx/v4/go/models.go b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v4/go/models.go new file mode 100644 index 0000000000..334fc1d3fc --- /dev/null +++ b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v4/go/models.go @@ -0,0 +1,15 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.25.0 + +package querytest + +import ( + "database/sql" +) + +type User struct { + FirstName sql.NullString `json:"first_name,omitempty"` + LastName sql.NullString `json:"last_name,omitempty"` + Age sql.NullInt16 `json:"age,omitempty"` +} diff --git a/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v4/go/query.sql.go b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v4/go/query.sql.go new file mode 100644 index 0000000000..3f6d25f6d6 --- /dev/null +++ b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v4/go/query.sql.go @@ -0,0 +1,34 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.25.0 +// source: query.sql + +package querytest + +import ( + "context" +) + +const getAll = `-- name: GetAll :many +SELECT first_name, last_name, age FROM users +` + +func (q *Queries) GetAll(ctx context.Context) ([]User, error) { + rows, err := q.db.Query(ctx, getAll) + if err != nil { + return nil, err + } + defer rows.Close() + var items []User + for rows.Next() { + var i User + if err := rows.Scan(&i.FirstName, &i.LastName, &i.Age); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v4/query.sql b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v4/query.sql new file mode 100644 index 0000000000..237b20193b --- /dev/null +++ b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v4/query.sql @@ -0,0 +1,2 @@ +-- name: GetAll :many +SELECT * FROM users; diff --git a/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v4/schema.sql b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v4/schema.sql new file mode 100644 index 0000000000..8ca8f26d32 --- /dev/null +++ b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v4/schema.sql @@ -0,0 +1,6 @@ +CREATE TABLE users ( + first_name varchar(255), + last_name varchar(255), + age smallint +); + diff --git a/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v4/sqlc.json b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v4/sqlc.json new file mode 100644 index 0000000000..a6cd874015 --- /dev/null +++ b/internal/endtoend/testdata/json_tags/omit_empty/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_json_tags": true, + "json_tags_omit_empty": true + } + ] +} diff --git a/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v5/go/db.go b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v5/go/db.go new file mode 100644 index 0000000000..4b7184a242 --- /dev/null +++ b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v5/go/db.go @@ -0,0 +1,32 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.25.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/json_tags/omit_empty/postgresql/pgx/v5/go/models.go b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v5/go/models.go new file mode 100644 index 0000000000..e6909e9e0d --- /dev/null +++ b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v5/go/models.go @@ -0,0 +1,15 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.25.0 + +package querytest + +import ( + "github.com/jackc/pgx/v5/pgtype" +) + +type User struct { + FirstName pgtype.Text `json:"first_name,omitempty"` + LastName pgtype.Text `json:"last_name,omitempty"` + Age pgtype.Int2 `json:"age,omitempty"` +} diff --git a/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v5/go/query.sql.go b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v5/go/query.sql.go new file mode 100644 index 0000000000..3f6d25f6d6 --- /dev/null +++ b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v5/go/query.sql.go @@ -0,0 +1,34 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.25.0 +// source: query.sql + +package querytest + +import ( + "context" +) + +const getAll = `-- name: GetAll :many +SELECT first_name, last_name, age FROM users +` + +func (q *Queries) GetAll(ctx context.Context) ([]User, error) { + rows, err := q.db.Query(ctx, getAll) + if err != nil { + return nil, err + } + defer rows.Close() + var items []User + for rows.Next() { + var i User + if err := rows.Scan(&i.FirstName, &i.LastName, &i.Age); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v5/query.sql b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v5/query.sql new file mode 100644 index 0000000000..237b20193b --- /dev/null +++ b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v5/query.sql @@ -0,0 +1,2 @@ +-- name: GetAll :many +SELECT * FROM users; diff --git a/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v5/schema.sql b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v5/schema.sql new file mode 100644 index 0000000000..8ca8f26d32 --- /dev/null +++ b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v5/schema.sql @@ -0,0 +1,6 @@ +CREATE TABLE users ( + first_name varchar(255), + last_name varchar(255), + age smallint +); + diff --git a/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v5/sqlc.json b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/pgx/v5/sqlc.json new file mode 100644 index 0000000000..5f27d5fb58 --- /dev/null +++ b/internal/endtoend/testdata/json_tags/omit_empty/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_json_tags": true, + "json_tags_omit_empty": true + } + ] +} diff --git a/internal/endtoend/testdata/json_tags/omit_empty/postgresql/stdlib/go/db.go b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/stdlib/go/db.go new file mode 100644 index 0000000000..df0488f428 --- /dev/null +++ b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/stdlib/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.25.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/json_tags/omit_empty/postgresql/stdlib/go/models.go b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/stdlib/go/models.go new file mode 100644 index 0000000000..334fc1d3fc --- /dev/null +++ b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/stdlib/go/models.go @@ -0,0 +1,15 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.25.0 + +package querytest + +import ( + "database/sql" +) + +type User struct { + FirstName sql.NullString `json:"first_name,omitempty"` + LastName sql.NullString `json:"last_name,omitempty"` + Age sql.NullInt16 `json:"age,omitempty"` +} diff --git a/internal/endtoend/testdata/json_tags/omit_empty/postgresql/stdlib/go/query.sql.go b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/stdlib/go/query.sql.go new file mode 100644 index 0000000000..c936e761b6 --- /dev/null +++ b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/stdlib/go/query.sql.go @@ -0,0 +1,37 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.25.0 +// source: query.sql + +package querytest + +import ( + "context" +) + +const getAll = `-- name: GetAll :many +SELECT first_name, last_name, age FROM users +` + +func (q *Queries) GetAll(ctx context.Context) ([]User, error) { + rows, err := q.db.QueryContext(ctx, getAll) + if err != nil { + return nil, err + } + defer rows.Close() + var items []User + for rows.Next() { + var i User + if err := rows.Scan(&i.FirstName, &i.LastName, &i.Age); 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 +} diff --git a/internal/endtoend/testdata/json_tags/omit_empty/postgresql/stdlib/query.sql b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/stdlib/query.sql new file mode 100644 index 0000000000..237b20193b --- /dev/null +++ b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/stdlib/query.sql @@ -0,0 +1,2 @@ +-- name: GetAll :many +SELECT * FROM users; diff --git a/internal/endtoend/testdata/json_tags/omit_empty/postgresql/stdlib/schema.sql b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/stdlib/schema.sql new file mode 100644 index 0000000000..8ca8f26d32 --- /dev/null +++ b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/stdlib/schema.sql @@ -0,0 +1,6 @@ +CREATE TABLE users ( + first_name varchar(255), + last_name varchar(255), + age smallint +); + diff --git a/internal/endtoend/testdata/json_tags/omit_empty/postgresql/stdlib/sqlc.json b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/stdlib/sqlc.json new file mode 100644 index 0000000000..c5da08c629 --- /dev/null +++ b/internal/endtoend/testdata/json_tags/omit_empty/postgresql/stdlib/sqlc.json @@ -0,0 +1,14 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "postgresql", + "name": "querytest", + "schema": "schema.sql", + "queries": "query.sql", + "emit_json_tags": true, + "json_tags_omit_empty": true + } + ] +}