From 9e8d31d2e9e953d1743d530a8478dd63166e162c Mon Sep 17 00:00:00 2001 From: Nick Bruun Date: Mon, 25 Apr 2022 20:37:43 -0700 Subject: [PATCH] Implement emission of pointer types for nullable columns. --- Makefile | 2 +- docs/reference/config.md | 3 + internal/cmd/shim.go | 1 + internal/codegen/golang/postgresql_type.go | 55 +++++++++ internal/config/config.go | 1 + internal/config/v_one.go | 2 + .../emit_pointers_for_null_types/pgx/go/db.go | 32 +++++ .../pgx/go/models.go | 111 +++++++++++++++++ .../pgx/sql/character.sql | 17 +++ .../pgx/sql/datetime.sql | 23 ++++ .../pgx/sql/net-types.sql | 13 ++ .../pgx/sql/numeric.sql | 36 ++++++ .../pgx/sql/query.sql | 1 + .../pgx/sql/rangetypes.sql | 19 +++ .../pgx/sqlc.json | 14 +++ .../stdlib/go/db.go | 31 +++++ .../stdlib/go/models.go | 112 ++++++++++++++++++ .../stdlib/sql/character.sql | 17 +++ .../stdlib/sql/datetime.sql | 23 ++++ .../stdlib/sql/net-types.sql | 13 ++ .../stdlib/sql/numeric.sql | 36 ++++++ .../stdlib/sql/query.sql | 1 + .../stdlib/sql/rangetypes.sql | 19 +++ .../stdlib/sqlc.json | 12 ++ internal/plugin/codegen.pb.go | 18 ++- internal/plugin/codegen_vtproto.pb.go | 37 +++++- protos/plugin/codegen.proto | 1 + 27 files changed, 645 insertions(+), 5 deletions(-) create mode 100644 internal/endtoend/testdata/emit_pointers_for_null_types/pgx/go/db.go create mode 100644 internal/endtoend/testdata/emit_pointers_for_null_types/pgx/go/models.go create mode 100644 internal/endtoend/testdata/emit_pointers_for_null_types/pgx/sql/character.sql create mode 100644 internal/endtoend/testdata/emit_pointers_for_null_types/pgx/sql/datetime.sql create mode 100644 internal/endtoend/testdata/emit_pointers_for_null_types/pgx/sql/net-types.sql create mode 100644 internal/endtoend/testdata/emit_pointers_for_null_types/pgx/sql/numeric.sql create mode 100644 internal/endtoend/testdata/emit_pointers_for_null_types/pgx/sql/query.sql create mode 100644 internal/endtoend/testdata/emit_pointers_for_null_types/pgx/sql/rangetypes.sql create mode 100644 internal/endtoend/testdata/emit_pointers_for_null_types/pgx/sqlc.json create mode 100644 internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/go/db.go create mode 100644 internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/go/models.go create mode 100644 internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/sql/character.sql create mode 100644 internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/sql/datetime.sql create mode 100644 internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/sql/net-types.sql create mode 100644 internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/sql/numeric.sql create mode 100644 internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/sql/query.sql create mode 100644 internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/sql/rangetypes.sql create mode 100644 internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/sqlc.json diff --git a/Makefile b/Makefile index a6f94a1cb6..185cc900f6 100644 --- a/Makefile +++ b/Makefile @@ -38,7 +38,7 @@ mysqlsh: # $ protoc --version # libprotoc 3.19.1 # $ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest -# $ go install github.com/planetscale/vtprotobuf/cmd/protoc-gen-go-vtproto +# $ go install github.com/planetscale/vtprotobuf/cmd/protoc-gen-go-vtproto@latest proto: internal/plugin/codegen.pb.go internal/python/ast/ast.pb.go internal/plugin/codegen.pb.go: protos/plugin/codegen.proto diff --git a/docs/reference/config.md b/docs/reference/config.md index 07f5b73bcf..df98667e8a 100644 --- a/docs/reference/config.md +++ b/docs/reference/config.md @@ -20,6 +20,7 @@ packages: emit_result_struct_pointers: false emit_params_struct_pointers: false emit_methods_with_db_argument: false + emit_pointers_for_null_types: false json_tags_case_style: "camel" output_db_file_name: "db.go" output_models_file_name: "models.go" @@ -60,6 +61,8 @@ Each package document has the following keys: - If true, parameters are passed as pointers to structs. Defaults to `false`. - `emit_methods_with_db_argument`: - 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 and `sql_package` is set to `pgx/v4`, generated types for nullable columns are emitted as pointers (ie. `*string`) instead of `database/sql` null types (ie. `NullString`). 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`. - `output_db_file_name`: diff --git a/internal/cmd/shim.go b/internal/cmd/shim.go index 7eb0220d31..fb36c9a6a4 100644 --- a/internal/cmd/shim.go +++ b/internal/cmd/shim.go @@ -85,6 +85,7 @@ func pluginGoCode(s config.SQLGo) *plugin.GoCode { EmitResultStructPointers: s.EmitResultStructPointers, EmitParamsStructPointers: s.EmitParamsStructPointers, EmitMethodsWithDbArgument: s.EmitMethodsWithDBArgument, + EmitPointersForNullTypes: s.EmitPointersForNullTypes, JsonTagsCaseStyle: s.JSONTagsCaseStyle, Package: s.Package, Out: s.Out, diff --git a/internal/codegen/golang/postgresql_type.go b/internal/codegen/golang/postgresql_type.go index afa5a21aa7..bb589ad3c3 100644 --- a/internal/codegen/golang/postgresql_type.go +++ b/internal/codegen/golang/postgresql_type.go @@ -37,54 +37,79 @@ func postgresType(req *plugin.CodeGenRequest, col *plugin.Column) string { columnType := sdk.DataType(col.Type) notNull := col.NotNull || col.IsArray driver := parseDriver(req.Settings) + emitPointersForNull := driver == SQLDriverPGXV4 && req.Settings.Go.EmitPointersForNullTypes switch columnType { case "serial", "serial4", "pg_catalog.serial4": if notNull { return "int32" } + if emitPointersForNull { + return "*int32" + } return "sql.NullInt32" case "bigserial", "serial8", "pg_catalog.serial8": if notNull { return "int64" } + if emitPointersForNull { + return "*int64" + } return "sql.NullInt64" case "smallserial", "serial2", "pg_catalog.serial2": if notNull { return "int16" } + if emitPointersForNull { + return "*int16" + } return "sql.NullInt16" case "integer", "int", "int4", "pg_catalog.int4": if notNull { return "int32" } + if emitPointersForNull { + return "*int32" + } return "sql.NullInt32" case "bigint", "int8", "pg_catalog.int8": if notNull { return "int64" } + if emitPointersForNull { + return "*int64" + } return "sql.NullInt64" case "smallint", "int2", "pg_catalog.int2": if notNull { return "int16" } + if emitPointersForNull { + return "*int16" + } return "sql.NullInt16" case "float", "double precision", "float8", "pg_catalog.float8": if notNull { return "float64" } + if emitPointersForNull { + return "*float64" + } return "sql.NullFloat64" case "real", "float4", "pg_catalog.float4": if notNull { return "float32" } + if emitPointersForNull { + return "*float32" + } return "sql.NullFloat64" // TODO: Change to sql.NullFloat32 after updating the go.mod file case "numeric", "pg_catalog.numeric", "money": @@ -98,12 +123,18 @@ func postgresType(req *plugin.CodeGenRequest, col *plugin.Column) string { if notNull { return "string" } + if emitPointersForNull { + return "*string" + } return "sql.NullString" case "boolean", "bool", "pg_catalog.bool": if notNull { return "bool" } + if emitPointersForNull { + return "*bool" + } return "sql.NullBool" case "json": @@ -141,30 +172,45 @@ func postgresType(req *plugin.CodeGenRequest, col *plugin.Column) string { if notNull { return "time.Time" } + if emitPointersForNull { + return "*time.Time" + } return "sql.NullTime" case "pg_catalog.time", "pg_catalog.timetz": if notNull { return "time.Time" } + if emitPointersForNull { + return "*time.Time" + } return "sql.NullTime" case "pg_catalog.timestamp", "pg_catalog.timestamptz", "timestamptz": if notNull { return "time.Time" } + if emitPointersForNull { + return "*time.Time" + } return "sql.NullTime" case "text", "pg_catalog.varchar", "pg_catalog.bpchar", "string": if notNull { return "string" } + if emitPointersForNull { + return "*string" + } return "sql.NullString" case "uuid": if notNull { return "uuid.UUID" } + if emitPointersForNull { + return "*uuid.UUID" + } return "uuid.NullUUID" case "inet": @@ -206,12 +252,18 @@ func postgresType(req *plugin.CodeGenRequest, col *plugin.Column) string { if notNull { return "string" } + if emitPointersForNull { + return "*string" + } return "sql.NullString" case "interval", "pg_catalog.interval": if notNull { return "int64" } + if emitPointersForNull { + return "*int64" + } return "sql.NullInt64" case "daterange": @@ -292,6 +344,9 @@ func postgresType(req *plugin.CodeGenRequest, col *plugin.Column) string { if notNull { return "string" } + if emitPointersForNull { + return "*string" + } return "sql.NullString" } } diff --git a/internal/config/config.go b/internal/config/config.go index 08d6a57f1c..25dd00da41 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -127,6 +127,7 @@ type SQLGo struct { EmitResultStructPointers bool `json:"emit_result_struct_pointers" yaml:"emit_result_struct_pointers"` 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"` JSONTagsCaseStyle string `json:"json_tags_case_style,omitempty" yaml:"json_tags_case_style"` Package string `json:"package" yaml:"package"` Out string `json:"out" yaml:"out"` diff --git a/internal/config/v_one.go b/internal/config/v_one.go index 5b357255bc..9880fdc4ab 100644 --- a/internal/config/v_one.go +++ b/internal/config/v_one.go @@ -32,6 +32,7 @@ type v1PackageSettings struct { EmitResultStructPointers bool `json:"emit_result_struct_pointers" yaml:"emit_result_struct_pointers"` 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"` JSONTagsCaseStyle string `json:"json_tags_case_style,omitempty" yaml:"json_tags_case_style"` SQLPackage string `json:"sql_package" yaml:"sql_package"` Overrides []Override `json:"overrides" yaml:"overrides"` @@ -126,6 +127,7 @@ func (c *V1GenerateSettings) Translate() Config { EmitResultStructPointers: pkg.EmitResultStructPointers, EmitParamsStructPointers: pkg.EmitParamsStructPointers, EmitMethodsWithDBArgument: pkg.EmitMethodsWithDBArgument, + EmitPointersForNullTypes: pkg.EmitPointersForNullTypes, Package: pkg.Name, Out: pkg.Path, SQLPackage: pkg.SQLPackage, diff --git a/internal/endtoend/testdata/emit_pointers_for_null_types/pgx/go/db.go b/internal/endtoend/testdata/emit_pointers_for_null_types/pgx/go/db.go new file mode 100644 index 0000000000..525cd070a1 --- /dev/null +++ b/internal/endtoend/testdata/emit_pointers_for_null_types/pgx/go/db.go @@ -0,0 +1,32 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.13.0 + +package datatype + +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/emit_pointers_for_null_types/pgx/go/models.go b/internal/endtoend/testdata/emit_pointers_for_null_types/pgx/go/models.go new file mode 100644 index 0000000000..6c6fa8b167 --- /dev/null +++ b/internal/endtoend/testdata/emit_pointers_for_null_types/pgx/go/models.go @@ -0,0 +1,111 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.13.0 + +package datatype + +import ( + "time" + + "github.com/jackc/pgtype" +) + +type DtCharacter struct { + A *string + B *string + C *string + D *string + E *string +} + +type DtCharacterNotNull struct { + A string + B string + C string + D string + E string +} + +type DtDatetime struct { + A *time.Time + B *time.Time + C *time.Time + D *time.Time + E *time.Time + F *time.Time + G *time.Time + H *time.Time +} + +type DtDatetimeNotNull struct { + A time.Time + B time.Time + C time.Time + D time.Time + E time.Time + F time.Time + G time.Time + H time.Time +} + +type DtNetType struct { + A pgtype.Inet + B pgtype.CIDR + C pgtype.Macaddr +} + +type DtNetTypesNotNull struct { + A pgtype.Inet + B pgtype.CIDR + C pgtype.Macaddr +} + +type DtNumeric struct { + A *int16 + B *int32 + C *int64 + D pgtype.Numeric + E pgtype.Numeric + F *float32 + G *float64 + H *int16 + I *int32 + J *int64 + K *int16 + L *int32 + M *int64 +} + +type DtNumericNotNull struct { + A int16 + B int32 + C int64 + D pgtype.Numeric + E pgtype.Numeric + F float32 + G float64 + H int16 + I int32 + J int64 + K int16 + L int32 + M int64 +} + +type DtRange struct { + A pgtype.Int4range + B pgtype.Int8range + C pgtype.Numrange + D pgtype.Tsrange + E pgtype.Tstzrange + F pgtype.Daterange +} + +type DtRangeNotNull struct { + A pgtype.Int4range + B pgtype.Int8range + C pgtype.Numrange + D pgtype.Tsrange + E pgtype.Tstzrange + F pgtype.Daterange +} diff --git a/internal/endtoend/testdata/emit_pointers_for_null_types/pgx/sql/character.sql b/internal/endtoend/testdata/emit_pointers_for_null_types/pgx/sql/character.sql new file mode 100644 index 0000000000..e3f41dac81 --- /dev/null +++ b/internal/endtoend/testdata/emit_pointers_for_null_types/pgx/sql/character.sql @@ -0,0 +1,17 @@ +-- Character Types +-- https://www.postgresql.org/docs/current/datatype-character.html +CREATE TABLE dt_character ( + a text, + b character varying(32), + c varchar(32), + d character(32), + e char(32) +); + +CREATE TABLE dt_character_not_null ( + a text NOT NULL, + b character varying(32) NOT NULL, + c varchar(32) NOT NULL, + d character(32) NOT NULL, + e char(32) NOT NULL +); diff --git a/internal/endtoend/testdata/emit_pointers_for_null_types/pgx/sql/datetime.sql b/internal/endtoend/testdata/emit_pointers_for_null_types/pgx/sql/datetime.sql new file mode 100644 index 0000000000..5e6bcf033f --- /dev/null +++ b/internal/endtoend/testdata/emit_pointers_for_null_types/pgx/sql/datetime.sql @@ -0,0 +1,23 @@ +-- Date/Time Types +-- https://www.postgresql.org/docs/current/datatype-datetime.html +CREATE TABLE dt_datetime ( + a DATE, + b TIME, + c TIME WITHOUT TIME ZONE, + d TIME WITH TIME ZONE, + e TIMESTAMP, + f TIMESTAMP WITHOUT TIME ZONE, + g TIMESTAMP WITH TIME ZONE, + h timestamptz +); + +CREATE TABLE dt_datetime_not_null ( + a DATE NOT NULL, + b TIME NOT NULL, + c TIME WITHOUT TIME ZONE NOT NULL, + d TIME WITH TIME ZONE NOT NULL, + e TIMESTAMP NOT NULL, + f TIMESTAMP WITHOUT TIME ZONE NOT NULL, + g TIMESTAMP WITH TIME ZONE NOT NULL, + h timestamptz NOT NULL +); diff --git a/internal/endtoend/testdata/emit_pointers_for_null_types/pgx/sql/net-types.sql b/internal/endtoend/testdata/emit_pointers_for_null_types/pgx/sql/net-types.sql new file mode 100644 index 0000000000..6239b6f9f6 --- /dev/null +++ b/internal/endtoend/testdata/emit_pointers_for_null_types/pgx/sql/net-types.sql @@ -0,0 +1,13 @@ +-- Network Address Types +-- https://www.postgresql.org/docs/current/datatype-net-types.html +CREATE TABLE dt_net_types ( + a inet, + b cidr, + c macaddr +); + +CREATE TABLE dt_net_types_not_null ( + a inet NOT NULL, + b cidr NOT NULL, + c macaddr NOT NULL +); diff --git a/internal/endtoend/testdata/emit_pointers_for_null_types/pgx/sql/numeric.sql b/internal/endtoend/testdata/emit_pointers_for_null_types/pgx/sql/numeric.sql new file mode 100644 index 0000000000..9df14947f3 --- /dev/null +++ b/internal/endtoend/testdata/emit_pointers_for_null_types/pgx/sql/numeric.sql @@ -0,0 +1,36 @@ +-- Numeric Types +-- https://www.postgresql.org/docs/current/datatype-numeric.html +CREATE TABLE dt_numeric ( + -- TODO: this maps incorrectly to int16, not NullInt16 + a smallint, + b integer, + c bigint, + d decimal, + e numeric, + f real, + g double precision, + -- TODO: this maps incorrectly to int16, not NullInt16 + h smallserial, + i serial, + j bigserial, + -- TODO: this maps incorrectly to int16, not NullInt16 + k int2, + l int4, + m int8 +); + +CREATE TABLE dt_numeric_not_null ( + a smallint NOT NULL, + b integer NOT NULL, + c bigint NOT NULL, + d decimal NOT NULL, + e numeric NOT NULL, + f real NOT NULL, + g double precision NOT NULL, + h smallserial NOT NULL, + i serial NOT NULL, + j bigserial NOT NULL, + k int2 NOT NULL, + l int4 NOT NULL, + m int8 NOT NULL +); diff --git a/internal/endtoend/testdata/emit_pointers_for_null_types/pgx/sql/query.sql b/internal/endtoend/testdata/emit_pointers_for_null_types/pgx/sql/query.sql new file mode 100644 index 0000000000..e0ac49d1ec --- /dev/null +++ b/internal/endtoend/testdata/emit_pointers_for_null_types/pgx/sql/query.sql @@ -0,0 +1 @@ +SELECT 1; diff --git a/internal/endtoend/testdata/emit_pointers_for_null_types/pgx/sql/rangetypes.sql b/internal/endtoend/testdata/emit_pointers_for_null_types/pgx/sql/rangetypes.sql new file mode 100644 index 0000000000..7e61b658a5 --- /dev/null +++ b/internal/endtoend/testdata/emit_pointers_for_null_types/pgx/sql/rangetypes.sql @@ -0,0 +1,19 @@ +-- Range Types +-- https://www.postgresql.org/docs/current/rangetypes.html +CREATE TABLE dt_range ( + a int4range, + b int8range, + c numrange, + d tsrange, + e tstzrange, + f daterange +); + +CREATE TABLE dt_range_not_null ( + a int4range NOT NULL, + b int8range NOT NULL, + c numrange NOT NULL, + d tsrange NOT NULL, + e tstzrange NOT NULL, + f daterange NOT NULL +); diff --git a/internal/endtoend/testdata/emit_pointers_for_null_types/pgx/sqlc.json b/internal/endtoend/testdata/emit_pointers_for_null_types/pgx/sqlc.json new file mode 100644 index 0000000000..6fd3f734ff --- /dev/null +++ b/internal/endtoend/testdata/emit_pointers_for_null_types/pgx/sqlc.json @@ -0,0 +1,14 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "postgresql", + "sql_package": "pgx/v4", + "name": "datatype", + "schema": "sql/", + "queries": "sql/", + "emit_pointers_for_null_types": true + } + ] +} diff --git a/internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/go/db.go b/internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/go/db.go new file mode 100644 index 0000000000..b0b51a1d7d --- /dev/null +++ b/internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.13.0 + +package datatype + +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/emit_pointers_for_null_types/stdlib/go/models.go b/internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/go/models.go new file mode 100644 index 0000000000..0916ca8931 --- /dev/null +++ b/internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/go/models.go @@ -0,0 +1,112 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.13.0 + +package datatype + +import ( + "database/sql" + "time" + + "github.com/tabbed/pqtype" +) + +type DtCharacter struct { + A sql.NullString + B sql.NullString + C sql.NullString + D sql.NullString + E sql.NullString +} + +type DtCharacterNotNull struct { + A string + B string + C string + D string + E string +} + +type DtDatetime struct { + A sql.NullTime + B sql.NullTime + C sql.NullTime + D sql.NullTime + E sql.NullTime + F sql.NullTime + G sql.NullTime + H sql.NullTime +} + +type DtDatetimeNotNull struct { + A time.Time + B time.Time + C time.Time + D time.Time + E time.Time + F time.Time + G time.Time + H time.Time +} + +type DtNetType struct { + A pqtype.Inet + B pqtype.CIDR + C pqtype.Macaddr +} + +type DtNetTypesNotNull struct { + A pqtype.Inet + B pqtype.CIDR + C pqtype.Macaddr +} + +type DtNumeric struct { + A sql.NullInt16 + B sql.NullInt32 + C sql.NullInt64 + D sql.NullString + E sql.NullString + F sql.NullFloat64 + G sql.NullFloat64 + H sql.NullInt16 + I sql.NullInt32 + J sql.NullInt64 + K sql.NullInt16 + L sql.NullInt32 + M sql.NullInt64 +} + +type DtNumericNotNull struct { + A int16 + B int32 + C int64 + D string + E string + F float32 + G float64 + H int16 + I int32 + J int64 + K int16 + L int32 + M int64 +} + +type DtRange struct { + A interface{} + B interface{} + C interface{} + D interface{} + E interface{} + F interface{} +} + +type DtRangeNotNull struct { + A interface{} + B interface{} + C interface{} + D interface{} + E interface{} + F interface{} +} diff --git a/internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/sql/character.sql b/internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/sql/character.sql new file mode 100644 index 0000000000..e3f41dac81 --- /dev/null +++ b/internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/sql/character.sql @@ -0,0 +1,17 @@ +-- Character Types +-- https://www.postgresql.org/docs/current/datatype-character.html +CREATE TABLE dt_character ( + a text, + b character varying(32), + c varchar(32), + d character(32), + e char(32) +); + +CREATE TABLE dt_character_not_null ( + a text NOT NULL, + b character varying(32) NOT NULL, + c varchar(32) NOT NULL, + d character(32) NOT NULL, + e char(32) NOT NULL +); diff --git a/internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/sql/datetime.sql b/internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/sql/datetime.sql new file mode 100644 index 0000000000..5e6bcf033f --- /dev/null +++ b/internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/sql/datetime.sql @@ -0,0 +1,23 @@ +-- Date/Time Types +-- https://www.postgresql.org/docs/current/datatype-datetime.html +CREATE TABLE dt_datetime ( + a DATE, + b TIME, + c TIME WITHOUT TIME ZONE, + d TIME WITH TIME ZONE, + e TIMESTAMP, + f TIMESTAMP WITHOUT TIME ZONE, + g TIMESTAMP WITH TIME ZONE, + h timestamptz +); + +CREATE TABLE dt_datetime_not_null ( + a DATE NOT NULL, + b TIME NOT NULL, + c TIME WITHOUT TIME ZONE NOT NULL, + d TIME WITH TIME ZONE NOT NULL, + e TIMESTAMP NOT NULL, + f TIMESTAMP WITHOUT TIME ZONE NOT NULL, + g TIMESTAMP WITH TIME ZONE NOT NULL, + h timestamptz NOT NULL +); diff --git a/internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/sql/net-types.sql b/internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/sql/net-types.sql new file mode 100644 index 0000000000..6239b6f9f6 --- /dev/null +++ b/internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/sql/net-types.sql @@ -0,0 +1,13 @@ +-- Network Address Types +-- https://www.postgresql.org/docs/current/datatype-net-types.html +CREATE TABLE dt_net_types ( + a inet, + b cidr, + c macaddr +); + +CREATE TABLE dt_net_types_not_null ( + a inet NOT NULL, + b cidr NOT NULL, + c macaddr NOT NULL +); diff --git a/internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/sql/numeric.sql b/internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/sql/numeric.sql new file mode 100644 index 0000000000..9df14947f3 --- /dev/null +++ b/internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/sql/numeric.sql @@ -0,0 +1,36 @@ +-- Numeric Types +-- https://www.postgresql.org/docs/current/datatype-numeric.html +CREATE TABLE dt_numeric ( + -- TODO: this maps incorrectly to int16, not NullInt16 + a smallint, + b integer, + c bigint, + d decimal, + e numeric, + f real, + g double precision, + -- TODO: this maps incorrectly to int16, not NullInt16 + h smallserial, + i serial, + j bigserial, + -- TODO: this maps incorrectly to int16, not NullInt16 + k int2, + l int4, + m int8 +); + +CREATE TABLE dt_numeric_not_null ( + a smallint NOT NULL, + b integer NOT NULL, + c bigint NOT NULL, + d decimal NOT NULL, + e numeric NOT NULL, + f real NOT NULL, + g double precision NOT NULL, + h smallserial NOT NULL, + i serial NOT NULL, + j bigserial NOT NULL, + k int2 NOT NULL, + l int4 NOT NULL, + m int8 NOT NULL +); diff --git a/internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/sql/query.sql b/internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/sql/query.sql new file mode 100644 index 0000000000..e0ac49d1ec --- /dev/null +++ b/internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/sql/query.sql @@ -0,0 +1 @@ +SELECT 1; diff --git a/internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/sql/rangetypes.sql b/internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/sql/rangetypes.sql new file mode 100644 index 0000000000..7e61b658a5 --- /dev/null +++ b/internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/sql/rangetypes.sql @@ -0,0 +1,19 @@ +-- Range Types +-- https://www.postgresql.org/docs/current/rangetypes.html +CREATE TABLE dt_range ( + a int4range, + b int8range, + c numrange, + d tsrange, + e tstzrange, + f daterange +); + +CREATE TABLE dt_range_not_null ( + a int4range NOT NULL, + b int8range NOT NULL, + c numrange NOT NULL, + d tsrange NOT NULL, + e tstzrange NOT NULL, + f daterange NOT NULL +); diff --git a/internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/sqlc.json b/internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/sqlc.json new file mode 100644 index 0000000000..be5e301859 --- /dev/null +++ b/internal/endtoend/testdata/emit_pointers_for_null_types/stdlib/sqlc.json @@ -0,0 +1,12 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "name": "datatype", + "schema": "sql/", + "queries": "sql/", + "emit_pointers_for_null_types": true + } + ] +} diff --git a/internal/plugin/codegen.pb.go b/internal/plugin/codegen.pb.go index c199ee3978..c2c66f6355 100644 --- a/internal/plugin/codegen.pb.go +++ b/internal/plugin/codegen.pb.go @@ -1,6 +1,6 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.27.1 +// protoc-gen-go v1.28.0 // protoc v3.19.4 // source: plugin/codegen.proto @@ -585,6 +585,7 @@ type GoCode struct { EmitResultStructPointers bool `protobuf:"varint,8,opt,name=emit_result_struct_pointers,json=emitResultStructPointers,proto3" json:"emit_result_struct_pointers,omitempty"` EmitParamsStructPointers bool `protobuf:"varint,9,opt,name=emit_params_struct_pointers,json=emitParamsStructPointers,proto3" json:"emit_params_struct_pointers,omitempty"` EmitMethodsWithDbArgument bool `protobuf:"varint,10,opt,name=emit_methods_with_db_argument,json=emitMethodsWithDbArgument,proto3" json:"emit_methods_with_db_argument,omitempty"` + EmitPointersForNullTypes bool `protobuf:"varint,19,opt,name=emit_pointers_for_null_types,json=emitPointersForNullTypes,proto3" json:"emit_pointers_for_null_types,omitempty"` JsonTagsCaseStyle string `protobuf:"bytes,11,opt,name=json_tags_case_style,json=jsonTagsCaseStyle,proto3" json:"json_tags_case_style,omitempty"` Package string `protobuf:"bytes,12,opt,name=package,proto3" json:"package,omitempty"` Out string `protobuf:"bytes,13,opt,name=out,proto3" json:"out,omitempty"` @@ -697,6 +698,13 @@ func (x *GoCode) GetEmitMethodsWithDbArgument() bool { return false } +func (x *GoCode) GetEmitPointersForNullTypes() bool { + if x != nil { + return x.EmitPointersForNullTypes + } + return false +} + func (x *GoCode) GetJsonTagsCaseStyle() string { if x != nil { return x.JsonTagsCaseStyle @@ -1637,7 +1645,7 @@ var file_plugin_codegen_proto_rawDesc = []byte{ 0x54, 0x61, 0x62, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6f, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6f, 0x75, 0x74, 0x22, 0xcd, 0x06, 0x0a, 0x06, 0x47, 0x6f, 0x43, 0x6f, 0x64, + 0x09, 0x52, 0x03, 0x6f, 0x75, 0x74, 0x22, 0x8d, 0x07, 0x0a, 0x06, 0x47, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x6d, 0x69, 0x74, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x65, 0x6d, 0x69, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x66, 0x61, 0x63, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x65, 0x6d, 0x69, 0x74, @@ -1669,7 +1677,11 @@ var file_plugin_codegen_proto_rawDesc = []byte{ 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x5f, 0x77, 0x69, 0x74, 0x68, 0x5f, 0x64, 0x62, 0x5f, 0x61, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x08, 0x52, 0x19, 0x65, 0x6d, 0x69, 0x74, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x57, 0x69, 0x74, 0x68, 0x44, - 0x62, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x2f, 0x0a, 0x14, 0x6a, 0x73, 0x6f, + 0x62, 0x41, 0x72, 0x67, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x3e, 0x0a, 0x1c, 0x65, 0x6d, 0x69, + 0x74, 0x5f, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x6e, + 0x75, 0x6c, 0x6c, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x13, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x18, 0x65, 0x6d, 0x69, 0x74, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x46, 0x6f, 0x72, + 0x4e, 0x75, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x14, 0x6a, 0x73, 0x6f, 0x6e, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x5f, 0x63, 0x61, 0x73, 0x65, 0x5f, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x6a, 0x73, 0x6f, 0x6e, 0x54, 0x61, 0x67, 0x73, 0x43, 0x61, 0x73, 0x65, 0x53, 0x74, 0x79, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, diff --git a/internal/plugin/codegen_vtproto.pb.go b/internal/plugin/codegen_vtproto.pb.go index 9bd89db83a..65de784c24 100644 --- a/internal/plugin/codegen_vtproto.pb.go +++ b/internal/plugin/codegen_vtproto.pb.go @@ -1,5 +1,5 @@ // Code generated by protoc-gen-go-vtproto. DO NOT EDIT. -// protoc-gen-go-vtproto version: v0.2.0 +// protoc-gen-go-vtproto version: v0.3.0 // source: plugin/codegen.proto package plugin @@ -577,6 +577,18 @@ func (m *GoCode) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if m.EmitPointersForNullTypes { + i-- + if m.EmitPointersForNullTypes { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x1 + i-- + dAtA[i] = 0x98 + } if len(m.OutputFilesSuffix) > 0 { i -= len(m.OutputFilesSuffix) copy(dAtA[i:], m.OutputFilesSuffix) @@ -1807,6 +1819,9 @@ func (m *GoCode) SizeVT() (n int) { if l > 0 { n += 2 + l + sov(uint64(l)) } + if m.EmitPointersForNullTypes { + n += 3 + } if m.unknownFields != nil { n += len(m.unknownFields) } @@ -4120,6 +4135,26 @@ func (m *GoCode) UnmarshalVT(dAtA []byte) error { } m.OutputFilesSuffix = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 19: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field EmitPointersForNullTypes", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.EmitPointersForNullTypes = bool(v != 0) default: iNdEx = preIndex skippy, err := skip(dAtA[iNdEx:]) diff --git a/protos/plugin/codegen.proto b/protos/plugin/codegen.proto index adfa5ffe98..930f71c08d 100644 --- a/protos/plugin/codegen.proto +++ b/protos/plugin/codegen.proto @@ -90,6 +90,7 @@ message GoCode bool emit_result_struct_pointers = 8; bool emit_params_struct_pointers = 9; bool emit_methods_with_db_argument = 10; + bool emit_pointers_for_null_types = 19; string json_tags_case_style = 11; string package = 12; string out = 13;