diff --git a/docs/reference/config.md b/docs/reference/config.md index b9bc36210f..c6502271c5 100644 --- a/docs/reference/config.md +++ b/docs/reference/config.md @@ -84,7 +84,7 @@ The `gen` mapping supports the following keys: - `out`: - Output directory for generated code. - `sql_package`: - - Either `pgx/v4` or `database/sql`. Defaults to `database/sql`. + - Either `pgx/v4`, `pgx/v5` or `database/sql`. Defaults to `database/sql`. - `emit_db_tags`: - If true, add DB tags to generated structs. Defaults to `false`. - `emit_prepared_queries`: @@ -363,7 +363,7 @@ Each mapping in the `packages` collection has the following keys: - `engine`: - Either `postgresql` or `mysql`. Defaults to `postgresql`. - `sql_package`: - - Either `pgx/v4` or `database/sql`. Defaults to `database/sql`. + - Either `pgx/v4`, `pgx/v5` or `database/sql`. Defaults to `database/sql`. - `emit_db_tags`: - If true, add DB tags to generated structs. Defaults to `false`. - `emit_prepared_queries`: diff --git a/docs/reference/datatypes.md b/docs/reference/datatypes.md index b035bd6865..971f7a3fc3 100644 --- a/docs/reference/datatypes.md +++ b/docs/reference/datatypes.md @@ -3,8 +3,7 @@ ## Arrays PostgreSQL [arrays](https://www.postgresql.org/docs/current/arrays.html) are -materialized as Go slices. Currently, only one-dimensional arrays are -supported. +materialized as Go slices. Currently, the `pgx/v5` sql package only supports multidimensional arrays. ```sql CREATE TABLE places ( @@ -26,6 +25,7 @@ type Place struct { All PostgreSQL time and date types are returned as `time.Time` structs. For null time or date values, the `NullTime` type from `database/sql` is used. +The `pgx/v5` sql package uses the appropriate pgx types. ```sql CREATE TABLE authors ( @@ -86,7 +86,7 @@ type Store struct { ## Null For structs, null values are represented using the appropriate type from the -`database/sql` package. +`database/sql` or `pgx` package. ```sql CREATE TABLE authors ( @@ -132,3 +132,48 @@ type Author struct { ID uuid.UUID } ``` + +## JSON + +By default, sqlc will generate the `[]byte`, `pgtype.JSON` or `json.RawMessage` for JSON column type. +But if you use the `pgx/v5` sql package then you can specify a some struct instead of default type. +The `pgx` implementation will marshall/unmarshall the struct automatically. + +```go +package dto + +type BookData struct { + Genres []string `json:"genres"` + Title string `json:"title"` + Published bool `json:"published"` +} +``` + +```sql +CREATE TABLE books ( + data jsonb +); +``` + +```json +{ + "overrides": [ + { + "column": "books.data", + "go_type": "*example.com/db/dto.BookData" + } + ] +} +``` + +```go +package db + +import ( + "example.com/db/dto" +) + +type Book struct { + Data *dto.BookData +} +``` \ No newline at end of file diff --git a/docs/reference/query-annotations.md b/docs/reference/query-annotations.md index fe9fa791f5..0045f49fa7 100644 --- a/docs/reference/query-annotations.md +++ b/docs/reference/query-annotations.md @@ -115,7 +115,7 @@ func (q *Queries) GetAuthor(ctx context.Context, id int64) (Author, error) { ## `:batchexec` -__NOTE: This command only works with PostgreSQL using the `pgx` driver and outputting Go code.__ +__NOTE: This command only works with PostgreSQL using the `pgx/v4` and `pgx/v5` drivers and outputting Go code.__ The generated method will return a batch object. The batch object will have the following methods: @@ -147,7 +147,7 @@ func (b *DeleteBookBatchResults) Close() error { ## `:batchmany` -__NOTE: This command only works with PostgreSQL using the `pgx` driver and outputting Go code.__ +__NOTE: This command only works with PostgreSQL using the `pgx/v4` and `pgx/v5` drivers and outputting Go code.__ The generated method will return a batch object. The batch object will have the following methods: @@ -183,7 +183,7 @@ func (b *BooksByTitleYearBatchResults) Close() error { ## `:batchone` -__NOTE: This command only works with PostgreSQL using the `pgx` driver and outputting Go code.__ +__NOTE: This command only works with PostgreSQL using the `pgx/v4` and `pgx/v5` drivers and outputting Go code.__ The generated method will return a batch object. The batch object will have the following methods: diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go index f78b47a077..276fc10fb9 100644 --- a/internal/cmd/cmd.go +++ b/internal/cmd/cmd.go @@ -11,8 +11,9 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" - yaml "gopkg.in/yaml.v3" + "gopkg.in/yaml.v3" + "github.com/kyleconroy/sqlc/internal/codegen/golang" "github.com/kyleconroy/sqlc/internal/config" "github.com/kyleconroy/sqlc/internal/debug" "github.com/kyleconroy/sqlc/internal/info" @@ -112,6 +113,16 @@ func ParseEnv(c *cobra.Command) Env { } } +func (e *Env) Validate(cfg *config.Config) error { + for _, sql := range cfg.SQL { + if sql.Gen.Go != nil && sql.Gen.Go.SQLPackage == golang.SQLPackagePGXV5 && !e.ExperimentalFeatures { + return fmt.Errorf("'pgx/v5' golang sql package requires enabled '--experimental' flag") + } + } + + return nil +} + func getConfigPath(stderr io.Writer, f *pflag.Flag) (string, string) { if f != nil && f.Changed { file := f.Value.String() diff --git a/internal/cmd/generate.go b/internal/cmd/generate.go index e5a1a07e9a..602974b9cc 100644 --- a/internal/cmd/generate.go +++ b/internal/cmd/generate.go @@ -129,6 +129,11 @@ func Generate(ctx context.Context, e Env, dir, filename string, stderr io.Writer return nil, err } + if err := e.Validate(conf); err != nil { + fmt.Fprintf(stderr, "error validating %s: %s\n", base, err) + return nil, err + } + output := map[string]string{} errored := false @@ -194,7 +199,7 @@ func Generate(ctx context.Context, e Env, dir, filename string, stderr io.Writer trace.Logf(ctx, "", "name=%s dir=%s plugin=%s", name, dir, lang) } - result, failed := parse(ctx, e, name, dir, sql.SQL, combo, parseOpts, stderr) + result, failed := parse(ctx, name, dir, sql.SQL, combo, parseOpts, stderr) if failed { if packageRegion != nil { packageRegion.End() @@ -233,7 +238,7 @@ func Generate(ctx context.Context, e Env, dir, filename string, stderr io.Writer return output, nil } -func parse(ctx context.Context, e Env, name, dir string, sql config.SQL, combo config.CombinedSettings, parserOpts opts.Parser, stderr io.Writer) (*compiler.Result, bool) { +func parse(ctx context.Context, name, dir string, sql config.SQL, combo config.CombinedSettings, parserOpts opts.Parser, stderr io.Writer) (*compiler.Result, bool) { if debug.Traced { defer trace.StartRegion(ctx, "parse").End() } diff --git a/internal/codegen/golang/driver.go b/internal/codegen/golang/driver.go index b9efe77abf..f09f89b535 100644 --- a/internal/codegen/golang/driver.go +++ b/internal/codegen/golang/driver.go @@ -1,20 +1,41 @@ package golang -import ( - "github.com/kyleconroy/sqlc/internal/plugin" -) - type SQLDriver int +const ( + SQLPackagePGXV4 string = "pgx/v4" + SQLPackagePGXV5 string = "pgx/v5" + SQLPackageStandard string = "database/sql" +) + const ( SQLDriverPGXV4 SQLDriver = iota + SQLDriverPGXV5 SQLDriverLibPQ ) -func parseDriver(settings *plugin.Settings) SQLDriver { - if settings.Go.SqlPackage == "pgx/v4" { +func parseDriver(sqlPackage string) SQLDriver { + switch sqlPackage { + case SQLPackagePGXV4: return SQLDriverPGXV4 - } else { + case SQLPackagePGXV5: + return SQLDriverPGXV5 + default: return SQLDriverLibPQ } } + +func (d SQLDriver) IsPGX() bool { + return d == SQLDriverPGXV4 || d == SQLDriverPGXV5 +} + +func (d SQLDriver) Package() string { + switch d { + case SQLDriverPGXV4: + return SQLPackagePGXV4 + case SQLDriverPGXV5: + return SQLPackagePGXV5 + default: + return SQLPackageStandard + } +} diff --git a/internal/codegen/golang/gen.go b/internal/codegen/golang/gen.go index 0b896e7c41..e463849f03 100644 --- a/internal/codegen/golang/gen.go +++ b/internal/codegen/golang/gen.go @@ -18,7 +18,7 @@ import ( type tmplCtx struct { Q string Package string - SQLPackage SQLPackage + SQLDriver SQLDriver Enums []Enum Structs []Struct GoQueries []Query @@ -91,7 +91,7 @@ func generate(req *plugin.CodeGenRequest, enums []Enum, structs []Struct, querie EmitAllEnumValues: golang.EmitAllEnumValues, UsesCopyFrom: usesCopyFrom(queries), UsesBatch: usesBatch(queries), - SQLPackage: SQLPackageFromString(golang.SqlPackage), + SQLDriver: parseDriver(golang.SqlPackage), Q: "`", Package: golang.Package, GoQueries: queries, @@ -100,11 +100,11 @@ func generate(req *plugin.CodeGenRequest, enums []Enum, structs []Struct, querie SqlcVersion: req.SqlcVersion, } - if tctx.UsesCopyFrom && tctx.SQLPackage != SQLPackagePGX { + if tctx.UsesCopyFrom && !tctx.SQLDriver.IsPGX() { return nil, errors.New(":copyfrom is only supported by pgx") } - if tctx.UsesBatch && tctx.SQLPackage != SQLPackagePGX { + if tctx.UsesBatch && !tctx.SQLDriver.IsPGX() { return nil, errors.New(":batch* commands are only supported by pgx") } diff --git a/internal/codegen/golang/go_type.go b/internal/codegen/golang/go_type.go index 77415e69af..d740047cef 100644 --- a/internal/codegen/golang/go_type.go +++ b/internal/codegen/golang/go_type.go @@ -38,6 +38,9 @@ func goType(req *plugin.CodeGenRequest, col *plugin.Column) string { } typ := goInnerType(req, col) if col.IsArray { + if parseDriver(req.Settings.Go.SqlPackage) == SQLDriverPGXV5 { + return "pgtype.Array[" + typ + "]" + } return "[]" + typ } return typ diff --git a/internal/codegen/golang/imports.go b/internal/codegen/golang/imports.go index 9937e38359..b95779091b 100644 --- a/internal/codegen/golang/imports.go +++ b/internal/codegen/golang/imports.go @@ -102,7 +102,7 @@ func (i *importer) Imports(filename string) [][]ImportSpec { case copyfromFileName: return mergeImports(i.copyfromImports()) case batchFileName: - return mergeImports(i.batchImports(filename)) + return mergeImports(i.batchImports()) default: return mergeImports(i.queryImports(filename)) } @@ -114,11 +114,14 @@ func (i *importer) dbImports() fileImports { {Path: "context"}, } - sqlpkg := SQLPackageFromString(i.Settings.Go.SqlPackage) + sqlpkg := parseDriver(i.Settings.Go.SqlPackage) switch sqlpkg { - case SQLPackagePGX: + case SQLDriverPGXV4: pkg = append(pkg, ImportSpec{Path: "github.com/jackc/pgconn"}) pkg = append(pkg, ImportSpec{Path: "github.com/jackc/pgx/v4"}) + case SQLDriverPGXV5: + pkg = append(pkg, ImportSpec{Path: "github.com/jackc/pgx/v5/pgconn"}) + pkg = append(pkg, ImportSpec{Path: "github.com/jackc/pgx/v5"}) default: std = append(std, ImportSpec{Path: "database/sql"}) if i.Settings.Go.EmitPreparedQueries { @@ -136,22 +139,8 @@ var stdlibTypes = map[string]string{ "time.Time": "time", "net.IP": "net", "net.HardwareAddr": "net", -} - -var pgtypeTypes = map[string]struct{}{ - "pgtype.CIDR": {}, - "pgtype.Daterange": {}, - "pgtype.Inet": {}, - "pgtype.Int4range": {}, - "pgtype.Int8range": {}, - "pgtype.JSON": {}, - "pgtype.JSONB": {}, - "pgtype.Hstore": {}, - "pgtype.Macaddr": {}, - "pgtype.Numeric": {}, - "pgtype.Numrange": {}, - "pgtype.Tsrange": {}, - "pgtype.Tstzrange": {}, + "netip.Addr": "net/netip", + "netip.Prefix": "net/netip", } var pqtypeTypes = map[string]struct{}{ @@ -169,12 +158,14 @@ func buildImports(settings *plugin.Settings, queries []Query, uses func(string) std["database/sql"] = struct{}{} } - sqlpkg := SQLPackageFromString(settings.Go.SqlPackage) + sqlpkg := parseDriver(settings.Go.SqlPackage) for _, q := range queries { if q.Cmd == metadata.CmdExecResult { switch sqlpkg { - case SQLPackagePGX: + case SQLDriverPGXV4: pkg[ImportSpec{Path: "github.com/jackc/pgconn"}] = struct{}{} + case SQLDriverPGXV5: + pkg[ImportSpec{Path: "github.com/jackc/pgx/v5/pgconn"}] = struct{}{} default: std["database/sql"] = struct{}{} } @@ -187,8 +178,10 @@ func buildImports(settings *plugin.Settings, queries []Query, uses func(string) } } - for typeName, _ := range pgtypeTypes { - if uses(typeName) { + if uses("pgtype.") { + if sqlpkg == SQLDriverPGXV5 { + pkg[ImportSpec{Path: "github.com/jackc/pgx/v5/pgtype"}] = struct{}{} + } else { pkg[ImportSpec{Path: "github.com/jackc/pgtype"}] = struct{}{} } } @@ -196,6 +189,7 @@ func buildImports(settings *plugin.Settings, queries []Query, uses func(string) for typeName, _ := range pqtypeTypes { if uses(typeName) { pkg[ImportSpec{Path: "github.com/tabbed/pqtype"}] = struct{}{} + break } } @@ -373,8 +367,8 @@ func (i *importer) queryImports(filename string) fileImports { std["context"] = struct{}{} } - sqlpkg := SQLPackageFromString(i.Settings.Go.SqlPackage) - if sliceScan() && sqlpkg != SQLPackagePGX { + sqlpkg := parseDriver(i.Settings.Go.SqlPackage) + if sliceScan() && !sqlpkg.IsPGX() { pkg[ImportSpec{Path: "github.com/lib/pq"}] = struct{}{} } @@ -409,7 +403,7 @@ func (i *importer) copyfromImports() fileImports { return sortedImports(std, pkg) } -func (i *importer) batchImports(filename string) fileImports { +func (i *importer) batchImports() fileImports { batchQueries := make([]Query, 0, len(i.Queries)) for _, q := range i.Queries { if usesBatch([]Query{q}) { @@ -452,7 +446,13 @@ func (i *importer) batchImports(filename string) fileImports { std["context"] = struct{}{} std["errors"] = struct{}{} - pkg[ImportSpec{Path: "github.com/jackc/pgx/v4"}] = struct{}{} + sqlpkg := parseDriver(i.Settings.Go.SqlPackage) + switch sqlpkg { + case SQLDriverPGXV4: + pkg[ImportSpec{Path: "github.com/jackc/pgx/v4"}] = struct{}{} + case SQLDriverPGXV5: + pkg[ImportSpec{Path: "github.com/jackc/pgx/v5"}] = struct{}{} + } return sortedImports(std, pkg) } diff --git a/internal/codegen/golang/postgresql_type.go b/internal/codegen/golang/postgresql_type.go index 38ac70f961..9172ba0460 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.CodeGenRequest, col *plugin.Column) string { columnType := sdk.DataType(col.Type) notNull := col.NotNull || col.IsArray - driver := parseDriver(req.Settings) + driver := parseDriver(req.Settings.Go.SqlPackage) emitPointersForNull := driver == SQLDriverPGXV4 && req.Settings.Go.EmitPointersForNullTypes switch columnType { @@ -47,6 +47,9 @@ func postgresType(req *plugin.CodeGenRequest, col *plugin.Column) string { if emitPointersForNull { return "*int32" } + if driver == SQLDriverPGXV5 { + return "pgtype.Int4" + } return "sql.NullInt32" case "bigserial", "serial8", "pg_catalog.serial8": @@ -56,6 +59,9 @@ func postgresType(req *plugin.CodeGenRequest, col *plugin.Column) string { if emitPointersForNull { return "*int64" } + if driver == SQLDriverPGXV5 { + return "pgtype.Int8" + } return "sql.NullInt64" case "smallserial", "serial2", "pg_catalog.serial2": @@ -65,6 +71,9 @@ func postgresType(req *plugin.CodeGenRequest, col *plugin.Column) string { if emitPointersForNull { return "*int16" } + if driver == SQLDriverPGXV5 { + return "pgtype.Int2" + } return "sql.NullInt16" case "integer", "int", "int4", "pg_catalog.int4": @@ -74,6 +83,9 @@ func postgresType(req *plugin.CodeGenRequest, col *plugin.Column) string { if emitPointersForNull { return "*int32" } + if driver == SQLDriverPGXV5 { + return "pgtype.Int4" + } return "sql.NullInt32" case "bigint", "int8", "pg_catalog.int8": @@ -83,6 +95,9 @@ func postgresType(req *plugin.CodeGenRequest, col *plugin.Column) string { if emitPointersForNull { return "*int64" } + if driver == SQLDriverPGXV5 { + return "pgtype.Int8" + } return "sql.NullInt64" case "smallint", "int2", "pg_catalog.int2": @@ -92,6 +107,9 @@ func postgresType(req *plugin.CodeGenRequest, col *plugin.Column) string { if emitPointersForNull { return "*int16" } + if driver == SQLDriverPGXV5 { + return "pgtype.Int2" + } return "sql.NullInt16" case "float", "double precision", "float8", "pg_catalog.float8": @@ -101,6 +119,9 @@ func postgresType(req *plugin.CodeGenRequest, col *plugin.Column) string { if emitPointersForNull { return "*float64" } + if driver == SQLDriverPGXV5 { + return "pgtype.Float8" + } return "sql.NullFloat64" case "real", "float4", "pg_catalog.float4": @@ -110,10 +131,13 @@ func postgresType(req *plugin.CodeGenRequest, col *plugin.Column) string { if emitPointersForNull { return "*float32" } + if driver == SQLDriverPGXV5 { + return "pgtype.Float4" + } return "sql.NullFloat64" // TODO: Change to sql.NullFloat32 after updating the go.mod file case "numeric", "pg_catalog.numeric", "money": - if driver == SQLDriverPGXV4 { + if driver.IsPGX() { return "pgtype.Numeric" } // Since the Go standard library does not have a decimal type, lib/pq @@ -135,10 +159,15 @@ func postgresType(req *plugin.CodeGenRequest, col *plugin.Column) string { if emitPointersForNull { return "*bool" } + if driver == SQLDriverPGXV5 { + return "pgtype.Bool" + } return "sql.NullBool" case "json": switch driver { + case SQLDriverPGXV5: + return "[]byte" case SQLDriverPGXV4: return "pgtype.JSON" case SQLDriverLibPQ: @@ -153,6 +182,8 @@ func postgresType(req *plugin.CodeGenRequest, col *plugin.Column) string { case "jsonb": switch driver { + case SQLDriverPGXV5: + return "[]byte" case SQLDriverPGXV4: return "pgtype.JSONB" case SQLDriverLibPQ: @@ -169,6 +200,9 @@ func postgresType(req *plugin.CodeGenRequest, col *plugin.Column) string { return "[]byte" case "date": + if driver == SQLDriverPGXV5 { + return "pgtype.Date" + } if notNull { return "time.Time" } @@ -177,7 +211,10 @@ func postgresType(req *plugin.CodeGenRequest, col *plugin.Column) string { } return "sql.NullTime" - case "pg_catalog.time", "pg_catalog.timetz": + case "pg_catalog.time": + if driver == SQLDriverPGXV5 { + return "pgtype.Time" + } if notNull { return "time.Time" } @@ -186,7 +223,31 @@ func postgresType(req *plugin.CodeGenRequest, col *plugin.Column) string { } return "sql.NullTime" - case "pg_catalog.timestamp", "pg_catalog.timestamptz", "timestamptz": + case "pg_catalog.timetz": + if notNull { + return "time.Time" + } + if emitPointersForNull { + return "*time.Time" + } + return "sql.NullTime" + + case "pg_catalog.timestamp": + if driver == SQLDriverPGXV5 { + return "pgtype.Timestamp" + } + if notNull { + return "time.Time" + } + if emitPointersForNull { + return "*time.Time" + } + return "sql.NullTime" + + case "pg_catalog.timestamptz", "timestamptz": + if driver == SQLDriverPGXV5 { + return "pgtype.Timestamptz" + } if notNull { return "time.Time" } @@ -202,9 +263,15 @@ func postgresType(req *plugin.CodeGenRequest, col *plugin.Column) string { if emitPointersForNull { return "*string" } + if driver == SQLDriverPGXV5 { + return "pgtype.Text" + } return "sql.NullString" case "uuid": + if driver == SQLDriverPGXV5 { + return "pgtype.UUID" + } if notNull { return "uuid.UUID" } @@ -215,6 +282,11 @@ func postgresType(req *plugin.CodeGenRequest, col *plugin.Column) string { case "inet": switch driver { + case SQLDriverPGXV5: + if notNull { + return "netip.Addr" + } + return "*netip.Addr" case SQLDriverPGXV4: return "pgtype.Inet" case SQLDriverLibPQ: @@ -225,6 +297,11 @@ func postgresType(req *plugin.CodeGenRequest, col *plugin.Column) string { case "cidr": switch driver { + case SQLDriverPGXV5: + if notNull { + return "netip.Prefix" + } + return "*netip.Prefix" case SQLDriverPGXV4: return "pgtype.CIDR" case SQLDriverLibPQ: @@ -235,6 +312,8 @@ func postgresType(req *plugin.CodeGenRequest, col *plugin.Column) string { case "macaddr", "macaddr8": switch driver { + case SQLDriverPGXV5: + return "net.HardwareAddr" case SQLDriverPGXV4: return "pgtype.Macaddr" case SQLDriverLibPQ: @@ -255,9 +334,16 @@ func postgresType(req *plugin.CodeGenRequest, col *plugin.Column) string { if emitPointersForNull { return "*string" } + if driver == SQLDriverPGXV5 { + return "pgtype.Text" + } return "sql.NullString" case "interval", "pg_catalog.interval": + if driver == SQLDriverPGXV5 { + return "pgtype.Interval" + } + if notNull { return "int64" } @@ -267,47 +353,169 @@ func postgresType(req *plugin.CodeGenRequest, col *plugin.Column) string { return "sql.NullInt64" case "daterange": - if driver == SQLDriverPGXV4 { + switch driver { + case SQLDriverPGXV4: return "pgtype.Daterange" + case SQLDriverPGXV5: + return "pgtype.Range[pgtype.Date]" + default: + return "interface{}" + } + + case "datemultirange": + switch driver { + case SQLDriverPGXV5: + return "pgtype.Multirange[pgtype.Range[pgtype.Date]]" + default: + return "interface{}" } - return "interface{}" case "tsrange": - if driver == SQLDriverPGXV4 { + switch driver { + case SQLDriverPGXV4: return "pgtype.Tsrange" + case SQLDriverPGXV5: + return "pgtype.Range[pgtype.Timestamp]" + default: + return "interface{}" + } + + case "tsmultirange": + switch driver { + case SQLDriverPGXV5: + return "pgtype.Multirange[pgtype.Range[pgtype.Timestamp]]" + default: + return "interface{}" } - return "interface{}" case "tstzrange": - if driver == SQLDriverPGXV4 { + switch driver { + case SQLDriverPGXV4: return "pgtype.Tstzrange" + case SQLDriverPGXV5: + return "pgtype.Range[pgtype.Timestamptz]" + default: + return "interface{}" + } + + case "tstzmultirange": + switch driver { + case SQLDriverPGXV5: + return "pgtype.Multirange[pgtype.Range[pgtype.Timestamptz]]" + default: + return "interface{}" } - return "interface{}" case "numrange": - if driver == SQLDriverPGXV4 { + switch driver { + case SQLDriverPGXV4: return "pgtype.Numrange" + case SQLDriverPGXV5: + return "pgtype.Range[pgtype.Numeric]" + default: + return "interface{}" + } + + case "nummultirange": + switch driver { + case SQLDriverPGXV5: + return "pgtype.Multirange[pgtype.Range[pgtype.Numeric]]" + default: + return "interface{}" } - return "interface{}" case "int4range": - if driver == SQLDriverPGXV4 { + switch driver { + case SQLDriverPGXV4: return "pgtype.Int4range" + case SQLDriverPGXV5: + return "pgtype.Range[pgtype.Int4]" + default: + return "interface{}" + } + + case "int4multirange": + switch driver { + case SQLDriverPGXV5: + return "pgtype.Multirange[pgtype.Range[pgtype.Int4]]" + default: + return "interface{}" } - return "interface{}" case "int8range": - if driver == SQLDriverPGXV4 { + switch driver { + case SQLDriverPGXV4: return "pgtype.Int8range" + case SQLDriverPGXV5: + return "pgtype.Range[pgtype.Int8]" + default: + return "interface{}" + } + + case "int8multirange": + switch driver { + case SQLDriverPGXV5: + return "pgtype.Multirange[pgtype.Range[pgtype.Int8]]" + default: + return "interface{}" } - return "interface{}" case "hstore": - if driver == SQLDriverPGXV4 { + if driver.IsPGX() { return "pgtype.Hstore" } return "interface{}" + case "bit", "varbit", "pg_catalog.bit", "pg_catalog.varbit": + if driver.IsPGX() { + return "pgtype.Bits" + } + + case "box": + if driver.IsPGX() { + return "pgtype.Box" + } + + case "cid", "oid": + if driver.IsPGX() { + return "pgtype.Uint32" + } + + case "tid": + if driver.IsPGX() { + return "pgtype.TID" + } + + case "circle": + if driver.IsPGX() { + return "pgtype.Circle" + } + + case "line": + if driver.IsPGX() { + return "pgtype.Line" + } + + case "lseg": + if driver.IsPGX() { + return "pgtype.Lseg" + } + + case "path": + if driver.IsPGX() { + return "pgtype.Path" + } + + case "point": + if driver.IsPGX() { + return "pgtype.Point" + } + + case "polygon": + if driver.IsPGX() { + return "pgtype.Polygon" + } + case "void": // A void value can only be scanned into an empty interface. return "interface{}" @@ -358,9 +566,10 @@ func postgresType(req *plugin.CodeGenRequest, col *plugin.Column) string { } } } - if debug.Active { - log.Printf("unknown PostgreSQL type: %s\n", columnType) - } - return "interface{}" } + + if debug.Active { + log.Printf("unknown PostgreSQL type: %s\n", columnType) + } + return "interface{}" } diff --git a/internal/codegen/golang/query.go b/internal/codegen/golang/query.go index 430dfb3b8f..1168bf6d7a 100644 --- a/internal/codegen/golang/query.go +++ b/internal/codegen/golang/query.go @@ -14,7 +14,7 @@ type QueryValue struct { Name string Struct *Struct Typ string - SQLPackage SQLPackage + SQLDriver SQLDriver } func (v QueryValue) EmitStruct() bool { @@ -93,14 +93,14 @@ func (v QueryValue) Params() string { } var out []string if v.Struct == nil { - if strings.HasPrefix(v.Typ, "[]") && v.Typ != "[]byte" && v.SQLPackage != SQLPackagePGX { + if strings.HasPrefix(v.Typ, "[]") && v.Typ != "[]byte" && !v.SQLDriver.IsPGX() { out = append(out, "pq.Array("+v.Name+")") } else { out = append(out, v.Name) } } else { for _, f := range v.Struct.Fields { - if strings.HasPrefix(f.Type, "[]") && f.Type != "[]byte" && v.SQLPackage != SQLPackagePGX { + if strings.HasPrefix(f.Type, "[]") && f.Type != "[]byte" && !v.SQLDriver.IsPGX() { out = append(out, "pq.Array("+v.Name+"."+f.Name+")") } else { out = append(out, v.Name+"."+f.Name) @@ -128,14 +128,14 @@ func (v QueryValue) ColumnNames() string { func (v QueryValue) Scan() string { var out []string if v.Struct == nil { - if strings.HasPrefix(v.Typ, "[]") && v.Typ != "[]byte" && v.SQLPackage != SQLPackagePGX { + if strings.HasPrefix(v.Typ, "[]") && v.Typ != "[]byte" && !v.SQLDriver.IsPGX() { out = append(out, "pq.Array(&"+v.Name+")") } else { out = append(out, "&"+v.Name) } } else { for _, f := range v.Struct.Fields { - if strings.HasPrefix(f.Type, "[]") && f.Type != "[]byte" && v.SQLPackage != SQLPackagePGX { + if strings.HasPrefix(f.Type, "[]") && f.Type != "[]byte" && !v.SQLDriver.IsPGX() { out = append(out, "pq.Array(&"+v.Name+"."+f.Name+")") } else { out = append(out, "&"+v.Name+"."+f.Name) diff --git a/internal/codegen/golang/result.go b/internal/codegen/golang/result.go index 1ab01b4de0..640132ca92 100644 --- a/internal/codegen/golang/result.go +++ b/internal/codegen/golang/result.go @@ -160,14 +160,14 @@ func buildQueries(req *plugin.CodeGenRequest, structs []Struct) ([]Query, error) Comments: query.Comments, Table: query.InsertIntoTable, } - sqlpkg := SQLPackageFromString(req.Settings.Go.SqlPackage) + sqlpkg := parseDriver(req.Settings.Go.SqlPackage) if len(query.Params) == 1 { p := query.Params[0] gq.Arg = QueryValue{ - Name: paramName(p), - Typ: goType(req, p.Column), - SQLPackage: sqlpkg, + Name: paramName(p), + Typ: goType(req, p.Column), + SQLDriver: sqlpkg, } } else if len(query.Params) > 1 { var cols []goColumn @@ -185,7 +185,7 @@ func buildQueries(req *plugin.CodeGenRequest, structs []Struct) ([]Query, error) Emit: true, Name: "arg", Struct: s, - SQLPackage: sqlpkg, + SQLDriver: sqlpkg, EmitPointer: req.Settings.Go.EmitParamsStructPointers, } } @@ -197,9 +197,9 @@ func buildQueries(req *plugin.CodeGenRequest, structs []Struct) ([]Query, error) name = strings.Replace(name, "$", "_", -1) } gq.Ret = QueryValue{ - Name: name, - Typ: goType(req, c), - SQLPackage: sqlpkg, + Name: name, + Typ: goType(req, c), + SQLDriver: sqlpkg, } } else if putOutColumns(query) { var gs *Struct @@ -244,7 +244,7 @@ func buildQueries(req *plugin.CodeGenRequest, structs []Struct) ([]Query, error) Emit: emit, Name: "i", Struct: gs, - SQLPackage: sqlpkg, + SQLDriver: sqlpkg, EmitPointer: req.Settings.Go.EmitResultStructPointers, } } diff --git a/internal/codegen/golang/sql_package.go b/internal/codegen/golang/sql_package.go deleted file mode 100644 index 18827879c8..0000000000 --- a/internal/codegen/golang/sql_package.go +++ /dev/null @@ -1,17 +0,0 @@ -package golang - -type SQLPackage string - -const ( - SQLPackagePGX SQLPackage = "pgx/v4" - SQLPackageStandard SQLPackage = "database/sql" -) - -func SQLPackageFromString(s string) SQLPackage { - switch s { - case string(SQLPackagePGX): - return SQLPackagePGX - default: - return SQLPackageStandard - } -} diff --git a/internal/codegen/golang/templates/template.tmpl b/internal/codegen/golang/templates/template.tmpl index 5dadf8e289..2ea0573036 100644 --- a/internal/codegen/golang/templates/template.tmpl +++ b/internal/codegen/golang/templates/template.tmpl @@ -16,7 +16,7 @@ import ( {{define "dbCode"}} -{{if eq .SQLPackage "pgx/v4"}} +{{if .SQLDriver.IsPGX }} {{- template "dbCodeTemplatePgx" .}} {{else}} {{- template "dbCodeTemplateStd" .}} @@ -41,7 +41,7 @@ import ( {{end}} {{define "interfaceCode"}} - {{if eq .SQLPackage "pgx/v4"}} + {{if .SQLDriver.IsPGX }} {{- template "interfaceCodePgx" .}} {{else}} {{- template "interfaceCodeStd" .}} @@ -159,7 +159,7 @@ import ( {{end}} {{define "queryCode"}} -{{if eq .SQLPackage "pgx/v4"}} +{{if .SQLDriver.IsPGX }} {{- template "queryCodePgx" .}} {{else}} {{- template "queryCodeStd" .}} @@ -184,7 +184,7 @@ import ( {{end}} {{define "copyfromCode"}} -{{if eq .SQLPackage "pgx/v4"}} +{{if .SQLDriver.IsPGX }} {{- template "copyfromCodePgx" .}} {{end}} {{end}} @@ -206,7 +206,7 @@ import ( {{end}} {{define "batchCode"}} -{{if eq .SQLPackage "pgx/v4"}} +{{if .SQLDriver.IsPGX }} {{- template "batchCodePgx" .}} {{end}} {{end}} diff --git a/internal/config/config.go b/internal/config/config.go index 5e8f14292d..0dfd57bcc9 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -7,7 +7,7 @@ import ( "fmt" "io" - yaml "gopkg.in/yaml.v3" + "gopkg.in/yaml.v3" ) type versionSetting struct { @@ -119,7 +119,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"` + EmitPointersForNullTypes bool `json:"emit_pointers_for_null_types" yaml:"emit_pointers_for_null_types"` 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"` JSONTagsCaseStyle string `json:"json_tags_case_style,omitempty" yaml:"json_tags_case_style"` diff --git a/internal/endtoend/testdata/exec_result/go_postgresql_pgx/go/db.go b/internal/endtoend/testdata/exec_result/go_postgresql_pgx/v4/go/db.go similarity index 100% rename from internal/endtoend/testdata/exec_result/go_postgresql_pgx/go/db.go rename to internal/endtoend/testdata/exec_result/go_postgresql_pgx/v4/go/db.go diff --git a/internal/endtoend/testdata/exec_result/go_postgresql_pgx/go/models.go b/internal/endtoend/testdata/exec_result/go_postgresql_pgx/v4/go/models.go similarity index 100% rename from internal/endtoend/testdata/exec_result/go_postgresql_pgx/go/models.go rename to internal/endtoend/testdata/exec_result/go_postgresql_pgx/v4/go/models.go diff --git a/internal/endtoend/testdata/exec_result/go_postgresql_pgx/go/querier.go b/internal/endtoend/testdata/exec_result/go_postgresql_pgx/v4/go/querier.go similarity index 100% rename from internal/endtoend/testdata/exec_result/go_postgresql_pgx/go/querier.go rename to internal/endtoend/testdata/exec_result/go_postgresql_pgx/v4/go/querier.go diff --git a/internal/endtoend/testdata/exec_result/go_postgresql_pgx/go/query.sql.go b/internal/endtoend/testdata/exec_result/go_postgresql_pgx/v4/go/query.sql.go similarity index 100% rename from internal/endtoend/testdata/exec_result/go_postgresql_pgx/go/query.sql.go rename to internal/endtoend/testdata/exec_result/go_postgresql_pgx/v4/go/query.sql.go diff --git a/internal/endtoend/testdata/exec_result/go_postgresql_pgx/query.sql b/internal/endtoend/testdata/exec_result/go_postgresql_pgx/v4/query.sql similarity index 100% rename from internal/endtoend/testdata/exec_result/go_postgresql_pgx/query.sql rename to internal/endtoend/testdata/exec_result/go_postgresql_pgx/v4/query.sql diff --git a/internal/endtoend/testdata/exec_result/go_postgresql_pgx/sqlc.json b/internal/endtoend/testdata/exec_result/go_postgresql_pgx/v4/sqlc.json similarity index 100% rename from internal/endtoend/testdata/exec_result/go_postgresql_pgx/sqlc.json rename to internal/endtoend/testdata/exec_result/go_postgresql_pgx/v4/sqlc.json diff --git a/internal/endtoend/testdata/exec_rows/go_postgresql_pgx/go/db.go b/internal/endtoend/testdata/exec_rows/go_postgresql_pgx/v4/go/db.go similarity index 100% rename from internal/endtoend/testdata/exec_rows/go_postgresql_pgx/go/db.go rename to internal/endtoend/testdata/exec_rows/go_postgresql_pgx/v4/go/db.go diff --git a/internal/endtoend/testdata/exec_rows/go_postgresql_pgx/go/models.go b/internal/endtoend/testdata/exec_rows/go_postgresql_pgx/v4/go/models.go similarity index 100% rename from internal/endtoend/testdata/exec_rows/go_postgresql_pgx/go/models.go rename to internal/endtoend/testdata/exec_rows/go_postgresql_pgx/v4/go/models.go diff --git a/internal/endtoend/testdata/exec_rows/go_postgresql_pgx/go/querier.go b/internal/endtoend/testdata/exec_rows/go_postgresql_pgx/v4/go/querier.go similarity index 100% rename from internal/endtoend/testdata/exec_rows/go_postgresql_pgx/go/querier.go rename to internal/endtoend/testdata/exec_rows/go_postgresql_pgx/v4/go/querier.go diff --git a/internal/endtoend/testdata/exec_rows/go_postgresql_pgx/go/query.sql.go b/internal/endtoend/testdata/exec_rows/go_postgresql_pgx/v4/go/query.sql.go similarity index 100% rename from internal/endtoend/testdata/exec_rows/go_postgresql_pgx/go/query.sql.go rename to internal/endtoend/testdata/exec_rows/go_postgresql_pgx/v4/go/query.sql.go diff --git a/internal/endtoend/testdata/exec_rows/go_postgresql_pgx/query.sql b/internal/endtoend/testdata/exec_rows/go_postgresql_pgx/v4/query.sql similarity index 100% rename from internal/endtoend/testdata/exec_rows/go_postgresql_pgx/query.sql rename to internal/endtoend/testdata/exec_rows/go_postgresql_pgx/v4/query.sql diff --git a/internal/endtoend/testdata/exec_rows/go_postgresql_pgx/sqlc.json b/internal/endtoend/testdata/exec_rows/go_postgresql_pgx/v4/sqlc.json similarity index 100% rename from internal/endtoend/testdata/exec_rows/go_postgresql_pgx/sqlc.json rename to internal/endtoend/testdata/exec_rows/go_postgresql_pgx/v4/sqlc.json diff --git a/internal/endtoend/testdata/pg_timezone_names/go_pgx/db.go b/internal/endtoend/testdata/pg_timezone_names/go_pgx/v4/db.go similarity index 100% rename from internal/endtoend/testdata/pg_timezone_names/go_pgx/db.go rename to internal/endtoend/testdata/pg_timezone_names/go_pgx/v4/db.go diff --git a/internal/endtoend/testdata/pg_timezone_names/go_pgx/models.go b/internal/endtoend/testdata/pg_timezone_names/go_pgx/v4/models.go similarity index 100% rename from internal/endtoend/testdata/pg_timezone_names/go_pgx/models.go rename to internal/endtoend/testdata/pg_timezone_names/go_pgx/v4/models.go diff --git a/internal/endtoend/testdata/pg_timezone_names/go_pgx/query.sql.go b/internal/endtoend/testdata/pg_timezone_names/go_pgx/v4/query.sql.go similarity index 100% rename from internal/endtoend/testdata/pg_timezone_names/go_pgx/query.sql.go rename to internal/endtoend/testdata/pg_timezone_names/go_pgx/v4/query.sql.go diff --git a/internal/endtoend/testdata/pg_timezone_names/sqlc.json b/internal/endtoend/testdata/pg_timezone_names/sqlc.json index 28435fd460..b24b3aaf06 100644 --- a/internal/endtoend/testdata/pg_timezone_names/sqlc.json +++ b/internal/endtoend/testdata/pg_timezone_names/sqlc.json @@ -9,7 +9,7 @@ "go": { "sql_package": "pgx/v4", "package": "querytest", - "out": "go_pgx" + "out": "go_pgx/v4" } } },