From 15acd622964c8a3b424068c149818838632fb86a Mon Sep 17 00:00:00 2001 From: nerocorp935 Date: Fri, 4 Sep 2026 10:01:25 +0000 Subject: [PATCH] fix(golang): correctly map nullable PostgreSQL UUID[] columns to nullable slice representation --- go.mod | 3 + internal/codegen/golang/go_type.go | 33 ----------- internal/codegen/golang/struct.go | 22 -------- internal/codegen/golang/types.go | 80 +++++++++++++++++++++++++++ internal/codegen/golang/types_test.go | 68 +++++++++++++++++++++++ 5 files changed, 151 insertions(+), 55 deletions(-) create mode 100644 go.mod delete mode 100644 internal/codegen/golang/go_type.go delete mode 100644 internal/codegen/golang/struct.go create mode 100644 internal/codegen/golang/types.go create mode 100644 internal/codegen/golang/types_test.go diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..3bd1da4 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/sqlc-dev/sqlc + +go 1.22.2 diff --git a/internal/codegen/golang/go_type.go b/internal/codegen/golang/go_type.go deleted file mode 100644 index a5d4b6b..0000000 --- a/internal/codegen/golang/go_type.go +++ /dev/null @@ -1,33 +0,0 @@ -package golang - -import ( - "fmt" - "path/filepath" - "strings" - - "github.com/sqlc-dev/sqlc/internal/codegen/sdk" - "github.com/sqlc-dev/sqlc/internal/plugin" -) - -func (g *Generator) goType(req *plugin.CodeGenRequest, col *plugin.Column, settings Settings) string { - typ := g.dbType(req, col, settings) - - if col.IsArray { - typ = "[]" + typ - } - - if !col.NotNull { - if col.IsArray { - if settings.SQLPackage == SQLPackageStandard { - typ = "*" + typ - } - } else if settings.GoTypePointer { - typ = "*" + typ - } else { - // Handle non-array nullable types - // ... - } - } - - return typ -} \ No newline at end of file diff --git a/internal/codegen/golang/struct.go b/internal/codegen/golang/struct.go deleted file mode 100644 index de637cd..0000000 --- a/internal/codegen/golang/struct.go +++ /dev/null @@ -1,22 +0,0 @@ -package golang - -import ( - "github.com/sqlc-dev/sqlc/internal/plugin" -) - -func (g *Generator) goType(col *plugin.Column) string { - // Existing logic... - // Add logic to check if column is nullable and is an array type - if col.NotNull == false && isArrayType(col.Type) { - return "*[]" + baseType(col.Type) - } - return g.defaultGoType(col) -} - -func isArrayType(t string) bool { - return t == "uuid[]" // Simplified for demonstration -} - -func baseType(t string) string { - return "uuid.UUID" -} \ No newline at end of file diff --git a/internal/codegen/golang/types.go b/internal/codegen/golang/types.go new file mode 100644 index 0000000..385ce91 --- /dev/null +++ b/internal/codegen/golang/types.go @@ -0,0 +1,80 @@ +package golang + +// SQLPackage identifies the database driver/package target. +type SQLPackage string + +const ( + SQLPackageStandard SQLPackage = "database/sql" + SQLPackagePGXV4 SQLPackage = "pgx/v4" + SQLPackagePGXV5 SQLPackage = "pgx/v5" +) + +// Settings represents golang generator configuration. +type Settings struct { + SQLPackage SQLPackage + GoTypePointer bool +} + +// Column represents a database column for code generation. +type Column struct { + Name string + Type string + NotNull bool + IsArray bool +} + +// Generator manages Go code generation. +type Generator struct{} + +func (g *Generator) dbType(col *Column) string { + switch col.Type { + case "uuid", "uuid[]": + return "uuid.UUID" + case "text", "varchar": + return "string" + case "integer", "int": + return "int32" + case "bigint": + return "int64" + case "boolean", "bool": + return "bool" + default: + return "interface{}" + } +} + +// GoType maps a database column and its nullability/array status to the appropriate Go type representation. +func (g *Generator) GoType(col *Column, settings Settings) string { + base := g.dbType(col) + if col.IsArray { + sliceTyp := "[]" + base + if !col.NotNull { + // For standard database/sql and drivers needing pointer indirection on nullable slices + if settings.SQLPackage == SQLPackageStandard || settings.GoTypePointer { + return "*" + sliceTyp + } + return sliceTyp + } + return sliceTyp + } + + if !col.NotNull { + if settings.GoTypePointer { + return "*" + base + } + switch base { + case "string": + return "sql.NullString" + case "int32": + return "sql.NullInt32" + case "int64": + return "sql.NullInt64" + case "bool": + return "sql.NullBool" + default: + return "*" + base + } + } + + return base +} diff --git a/internal/codegen/golang/types_test.go b/internal/codegen/golang/types_test.go new file mode 100644 index 0000000..049d6a0 --- /dev/null +++ b/internal/codegen/golang/types_test.go @@ -0,0 +1,68 @@ +package golang + +import "testing" + +func TestGoType_NullableUUIDArray(t *testing.T) { + g := &Generator{} + + tests := []struct { + name string + col *Column + settings Settings + expected string + }{ + { + name: "non-nullable uuid array (lib/pq)", + col: &Column{ + Name: "associated_ids", + Type: "uuid[]", + NotNull: true, + IsArray: true, + }, + settings: Settings{SQLPackage: SQLPackageStandard}, + expected: "[]uuid.UUID", + }, + { + name: "nullable uuid array (lib/pq standard driver)", + col: &Column{ + Name: "associated_ids", + Type: "uuid[]", + NotNull: false, + IsArray: true, + }, + settings: Settings{SQLPackage: SQLPackageStandard}, + expected: "*[]uuid.UUID", + }, + { + name: "nullable uuid array with pointer setting enabled", + col: &Column{ + Name: "associated_ids", + Type: "uuid[]", + NotNull: false, + IsArray: true, + }, + settings: Settings{SQLPackage: SQLPackagePGXV5, GoTypePointer: true}, + expected: "*[]uuid.UUID", + }, + { + name: "nullable uuid array with native pgx/v5 driver", + col: &Column{ + Name: "associated_ids", + Type: "uuid[]", + NotNull: false, + IsArray: true, + }, + settings: Settings{SQLPackage: SQLPackagePGXV5, GoTypePointer: false}, + expected: "[]uuid.UUID", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actual := g.GoType(tt.col, tt.settings) + if actual != tt.expected { + t.Errorf("GoType() = %v, expected %v", actual, tt.expected) + } + }) + } +}