From a0eb6971215a410aefd5e67d93930b90374d9700 Mon Sep 17 00:00:00 2001 From: yoRyuuuuu Date: Tue, 11 Mar 2025 18:22:02 +0900 Subject: [PATCH 1/6] feat: add support for emitting pointers for nullable types in MySQL code generation --- internal/codegen/golang/mysql_type.go | 46 +++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/internal/codegen/golang/mysql_type.go b/internal/codegen/golang/mysql_type.go index b8e8aa43c7..9364dfa1bf 100644 --- a/internal/codegen/golang/mysql_type.go +++ b/internal/codegen/golang/mysql_type.go @@ -13,6 +13,7 @@ func mysqlType(req *plugin.GenerateRequest, options *opts.Options, col *plugin.C columnType := sdk.DataType(col.Type) notNull := col.NotNull || col.IsArray unsigned := col.Unsigned + emitPointersForNull := options.EmitPointersForNullTypes switch columnType { @@ -20,6 +21,9 @@ func mysqlType(req *plugin.GenerateRequest, options *opts.Options, col *plugin.C if notNull { return "string" } + if emitPointersForNull { + return "*string" + } return "sql.NullString" case "tinyint": @@ -27,6 +31,9 @@ func mysqlType(req *plugin.GenerateRequest, options *opts.Options, col *plugin.C if notNull { return "bool" } + if emitPointersForNull { + return "*bool" + } return "sql.NullBool" } else { if notNull { @@ -35,6 +42,12 @@ func mysqlType(req *plugin.GenerateRequest, options *opts.Options, col *plugin.C } return "int8" } + if emitPointersForNull { + if unsigned { + return "*uint8" + } + return "*int8" + } // The database/sql package does not have a sql.NullInt8 type, so we // use the smallest type they have which is NullInt16 return "sql.NullInt16" @@ -44,6 +57,9 @@ func mysqlType(req *plugin.GenerateRequest, options *opts.Options, col *plugin.C if notNull { return "int16" } + if emitPointersForNull { + return "*int16" + } return "sql.NullInt16" case "smallint": @@ -53,6 +69,12 @@ func mysqlType(req *plugin.GenerateRequest, options *opts.Options, col *plugin.C } return "int16" } + if emitPointersForNull { + if unsigned { + return "*uint16" + } + return "*int16" + } return "sql.NullInt16" case "int", "integer", "mediumint": @@ -62,6 +84,12 @@ func mysqlType(req *plugin.GenerateRequest, options *opts.Options, col *plugin.C } return "int32" } + if emitPointersForNull { + if unsigned { + return "*uint32" + } + return "*int32" + } return "sql.NullInt32" case "bigint": @@ -71,6 +99,12 @@ func mysqlType(req *plugin.GenerateRequest, options *opts.Options, col *plugin.C } return "int64" } + if emitPointersForNull { + if unsigned { + return "*uint64" + } + return "*int64" + } return "sql.NullInt64" case "blob", "binary", "varbinary", "tinyblob", "mediumblob", "longblob": @@ -83,12 +117,18 @@ func mysqlType(req *plugin.GenerateRequest, options *opts.Options, col *plugin.C if notNull { return "float64" } + if emitPointersForNull { + return "*float64" + } return "sql.NullFloat64" case "decimal", "dec", "fixed": if notNull { return "string" } + if emitPointersForNull { + return "*string" + } return "sql.NullString" case "enum": @@ -99,12 +139,18 @@ func mysqlType(req *plugin.GenerateRequest, options *opts.Options, col *plugin.C if notNull { return "time.Time" } + if emitPointersForNull { + return "*time.Time" + } return "sql.NullTime" case "boolean", "bool": if notNull { return "bool" } + if emitPointersForNull { + return "*bool" + } return "sql.NullBool" case "json": From 7331e37a044867d1561c74052777b7caaecb25a1 Mon Sep 17 00:00:00 2001 From: yoRyuuuuu Date: Wed, 12 Mar 2025 00:01:06 +0900 Subject: [PATCH 2/6] feat: add end-to-end tests for emitting pointers for nullable types in MySQL code generation --- .../mysql/go/db.go | 31 +++++++++++++++++++ .../mysql/go/models.go | 23 ++++++++++++++ .../mysql/go/query.sql.go | 21 +++++++++++++ .../mysql/sql/query.sql | 2 ++ .../mysql/sql/types.sql | 17 ++++++++++ .../mysql/sqlc.json | 13 ++++++++ 6 files changed, 107 insertions(+) create mode 100644 internal/endtoend/testdata/emit_pointers_for_null_types/mysql/go/db.go create mode 100644 internal/endtoend/testdata/emit_pointers_for_null_types/mysql/go/models.go create mode 100644 internal/endtoend/testdata/emit_pointers_for_null_types/mysql/go/query.sql.go create mode 100644 internal/endtoend/testdata/emit_pointers_for_null_types/mysql/sql/query.sql create mode 100644 internal/endtoend/testdata/emit_pointers_for_null_types/mysql/sql/types.sql create mode 100644 internal/endtoend/testdata/emit_pointers_for_null_types/mysql/sqlc.json diff --git a/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/go/db.go b/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/go/db.go new file mode 100644 index 0000000000..187de0bd03 --- /dev/null +++ b/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/go/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.28.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/mysql/go/models.go b/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/go/models.go new file mode 100644 index 0000000000..580c1fddd8 --- /dev/null +++ b/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/go/models.go @@ -0,0 +1,23 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.28.0 + +package datatype + +type DtInteger struct { + A *int8 + B *int16 + C *int32 + D *int32 + E *int32 + F *int64 +} + +type DtUnsignedInteger struct { + A *uint8 + B *uint16 + C *uint32 + D *uint32 + E *uint32 + F *uint64 +} diff --git a/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/go/query.sql.go b/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/go/query.sql.go new file mode 100644 index 0000000000..1190d84031 --- /dev/null +++ b/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/go/query.sql.go @@ -0,0 +1,21 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.28.0 +// source: query.sql + +package datatype + +import ( + "context" +) + +const test = `-- name: Test :one +SELECT 1 +` + +func (q *Queries) Test(ctx context.Context) (int32, error) { + row := q.db.QueryRowContext(ctx, test) + var column_1 int32 + err := row.Scan(&column_1) + return column_1, err +} diff --git a/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/sql/query.sql b/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/sql/query.sql new file mode 100644 index 0000000000..9da604b57e --- /dev/null +++ b/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/sql/query.sql @@ -0,0 +1,2 @@ +-- name: Test :one +SELECT 1; diff --git a/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/sql/types.sql b/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/sql/types.sql new file mode 100644 index 0000000000..6b1ba9d0b9 --- /dev/null +++ b/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/sql/types.sql @@ -0,0 +1,17 @@ +CREATE TABLE dt_integer ( + a tinyint, + b smallint, + c int, + d integer, + e mediumint, + f bigint +); + +CREATE TABLE dt_unsigned_integer ( + a tinyint unsigned, + b smallint unsigned, + c int unsigned, + d integer unsigned, + e mediumint unsigned, + f bigint unsigned +); diff --git a/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/sqlc.json b/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/sqlc.json new file mode 100644 index 0000000000..498b789ec2 --- /dev/null +++ b/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/sqlc.json @@ -0,0 +1,13 @@ +{ + "version": "1", + "packages": [ + { + "path": "go", + "engine": "mysql", + "name": "datatype", + "schema": "sql/", + "queries": "sql/", + "emit_pointers_for_null_types": true + } + ] +} From d2a3c13c0417f181217402eedf1135fd013fa700 Mon Sep 17 00:00:00 2001 From: yoRyuuuuu Date: Sat, 15 Mar 2025 00:11:18 +0900 Subject: [PATCH 3/6] test: add test cases for NOT NULL columns --- .../mysql/go/models.go | 18 ++++++++++++++++++ .../mysql/sql/types.sql | 19 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/go/models.go b/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/go/models.go index 580c1fddd8..b276e6a5cb 100644 --- a/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/go/models.go +++ b/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/go/models.go @@ -13,6 +13,15 @@ type DtInteger struct { F *int64 } +type DtIntegerNotNull struct { + A int8 + B int16 + C int32 + D int32 + E int32 + F int64 +} + type DtUnsignedInteger struct { A *uint8 B *uint16 @@ -21,3 +30,12 @@ type DtUnsignedInteger struct { E *uint32 F *uint64 } + +type DtUnsignedIntegerNotNull struct { + A uint8 + B uint16 + C uint32 + D uint32 + E uint32 + F uint64 +} diff --git a/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/sql/types.sql b/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/sql/types.sql index 6b1ba9d0b9..0d0582e9e1 100644 --- a/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/sql/types.sql +++ b/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/sql/types.sql @@ -7,6 +7,15 @@ CREATE TABLE dt_integer ( f bigint ); +CREATE TABLE dt_integer_not_null ( + a tinyint not null, + b smallint not null, + c int not null, + d integer not null, + e mediumint not null, + f bigint not null +); + CREATE TABLE dt_unsigned_integer ( a tinyint unsigned, b smallint unsigned, @@ -15,3 +24,13 @@ CREATE TABLE dt_unsigned_integer ( e mediumint unsigned, f bigint unsigned ); + + +CREATE TABLE dt_unsigned_integer_not_null ( + a tinyint unsigned not null, + b smallint unsigned not null, + c int unsigned not null, + d integer unsigned not null, + e mediumint unsigned not null, + f bigint unsigned not null +); From e17374d1a270d462c70a3a3c5cea8530253f1cde Mon Sep 17 00:00:00 2001 From: yoRyuuuuu Date: Sat, 15 Mar 2025 00:16:40 +0900 Subject: [PATCH 4/6] rename types.sql to integer.sql --- .../mysql/sql/{types.sql => integer.sql} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename internal/endtoend/testdata/emit_pointers_for_null_types/mysql/sql/{types.sql => integer.sql} (100%) diff --git a/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/sql/types.sql b/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/sql/integer.sql similarity index 100% rename from internal/endtoend/testdata/emit_pointers_for_null_types/mysql/sql/types.sql rename to internal/endtoend/testdata/emit_pointers_for_null_types/mysql/sql/integer.sql From 29183a23e7fbc926a64c920ea7d8c1252f6ac285 Mon Sep 17 00:00:00 2001 From: yoRyuuuuu Date: Sat, 15 Mar 2025 00:22:34 +0900 Subject: [PATCH 5/6] test: add test cases for boolean columns --- .../emit_pointers_for_null_types/mysql/go/models.go | 12 ++++++++++++ .../mysql/sql/boolean.sql | 11 +++++++++++ 2 files changed, 23 insertions(+) create mode 100644 internal/endtoend/testdata/emit_pointers_for_null_types/mysql/sql/boolean.sql diff --git a/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/go/models.go b/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/go/models.go index b276e6a5cb..69180b043e 100644 --- a/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/go/models.go +++ b/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/go/models.go @@ -4,6 +4,18 @@ package datatype +type DtBoolean struct { + A *bool + B *bool + C *bool +} + +type DtBooleanNotNull struct { + A bool + B bool + C bool +} + type DtInteger struct { A *int8 B *int16 diff --git a/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/sql/boolean.sql b/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/sql/boolean.sql new file mode 100644 index 0000000000..8e7b1a65fa --- /dev/null +++ b/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/sql/boolean.sql @@ -0,0 +1,11 @@ +CREATE table dt_boolean ( + a boolean, + b bool, + c tinyint(1) +); + +CREATE table dt_boolean_not_null ( + a boolean not null, + b bool not null, + c tinyint(1) not null +); From e9562586486f49b7b707f38b5a6b5646f9c48278 Mon Sep 17 00:00:00 2001 From: yoRyuuuuu Date: Sat, 15 Mar 2025 00:27:59 +0900 Subject: [PATCH 6/6] test: add test cases for real number columns --- .../mysql/go/models.go | 20 +++++++++++++++++++ .../mysql/sql/real_number.sql | 19 ++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 internal/endtoend/testdata/emit_pointers_for_null_types/mysql/sql/real_number.sql diff --git a/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/go/models.go b/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/go/models.go index 69180b043e..3ec1d77af5 100644 --- a/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/go/models.go +++ b/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/go/models.go @@ -34,6 +34,26 @@ type DtIntegerNotNull struct { F int64 } +type DtRealNumber struct { + A *float64 + B *float64 + C *float64 + D *float64 + E *string + F *string + G *string +} + +type DtRealNumberNotNull struct { + A float64 + B float64 + C float64 + D float64 + E string + F string + G string +} + type DtUnsignedInteger struct { A *uint8 B *uint16 diff --git a/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/sql/real_number.sql b/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/sql/real_number.sql new file mode 100644 index 0000000000..351d51194d --- /dev/null +++ b/internal/endtoend/testdata/emit_pointers_for_null_types/mysql/sql/real_number.sql @@ -0,0 +1,19 @@ +CREATE TABLE dt_real_number ( + a double, + b double precision, + c real, + d float, + e decimal, + f dec, + g fixed +); + +CREATE TABLE dt_real_number_not_null ( + a double not null, + b double precision not null, + c real not null, + d float not null, + e decimal not null, + f dec not null, + g fixed not null +);