Skip to content

Commit 3623ec8

Browse files
authored
[PART 2] pgx v5 support (#1823) (#1874)
1 parent 4837b07 commit 3623ec8

File tree

31 files changed

+386
-109
lines changed

31 files changed

+386
-109
lines changed

docs/reference/config.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ The `gen` mapping supports the following keys:
8484
- `out`:
8585
- Output directory for generated code.
8686
- `sql_package`:
87-
- Either `pgx/v4` or `database/sql`. Defaults to `database/sql`.
87+
- Either `pgx/v4`, `pgx/v5` or `database/sql`. Defaults to `database/sql`.
8888
- `emit_db_tags`:
8989
- If true, add DB tags to generated structs. Defaults to `false`.
9090
- `emit_prepared_queries`:
@@ -363,7 +363,7 @@ Each mapping in the `packages` collection has the following keys:
363363
- `engine`:
364364
- Either `postgresql` or `mysql`. Defaults to `postgresql`.
365365
- `sql_package`:
366-
- Either `pgx/v4` or `database/sql`. Defaults to `database/sql`.
366+
- Either `pgx/v4`, `pgx/v5` or `database/sql`. Defaults to `database/sql`.
367367
- `emit_db_tags`:
368368
- If true, add DB tags to generated structs. Defaults to `false`.
369369
- `emit_prepared_queries`:

docs/reference/datatypes.md

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
## Arrays
44

55
PostgreSQL [arrays](https://www.postgresql.org/docs/current/arrays.html) are
6-
materialized as Go slices. Currently, only one-dimensional arrays are
7-
supported.
6+
materialized as Go slices. Currently, the `pgx/v5` sql package only supports multidimensional arrays.
87

98
```sql
109
CREATE TABLE places (
@@ -26,6 +25,7 @@ type Place struct {
2625

2726
All PostgreSQL time and date types are returned as `time.Time` structs. For
2827
null time or date values, the `NullTime` type from `database/sql` is used.
28+
The `pgx/v5` sql package uses the appropriate pgx types.
2929

3030
```sql
3131
CREATE TABLE authors (
@@ -86,7 +86,7 @@ type Store struct {
8686
## Null
8787

8888
For structs, null values are represented using the appropriate type from the
89-
`database/sql` package.
89+
`database/sql` or `pgx` package.
9090

9191
```sql
9292
CREATE TABLE authors (
@@ -132,3 +132,48 @@ type Author struct {
132132
ID uuid.UUID
133133
}
134134
```
135+
136+
## JSON
137+
138+
By default, sqlc will generate the `[]byte`, `pgtype.JSON` or `json.RawMessage` for JSON column type.
139+
But if you use the `pgx/v5` sql package then you can specify a some struct instead of default type.
140+
The `pgx` implementation will marshall/unmarshall the struct automatically.
141+
142+
```go
143+
package dto
144+
145+
type BookData struct {
146+
Genres []string `json:"genres"`
147+
Title string `json:"title"`
148+
Published bool `json:"published"`
149+
}
150+
```
151+
152+
```sql
153+
CREATE TABLE books (
154+
data jsonb
155+
);
156+
```
157+
158+
```json
159+
{
160+
"overrides": [
161+
{
162+
"column": "books.data",
163+
"go_type": "*example.com/db/dto.BookData"
164+
}
165+
]
166+
}
167+
```
168+
169+
```go
170+
package db
171+
172+
import (
173+
"example.com/db/dto"
174+
)
175+
176+
type Book struct {
177+
Data *dto.BookData
178+
}
179+
```

docs/reference/query-annotations.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ func (q *Queries) GetAuthor(ctx context.Context, id int64) (Author, error) {
115115

116116
## `:batchexec`
117117

118-
__NOTE: This command only works with PostgreSQL using the `pgx` driver and outputting Go code.__
118+
__NOTE: This command only works with PostgreSQL using the `pgx/v4` and `pgx/v5` drivers and outputting Go code.__
119119

120120
The generated method will return a batch object. The batch object will have
121121
the following methods:
@@ -147,7 +147,7 @@ func (b *DeleteBookBatchResults) Close() error {
147147

148148
## `:batchmany`
149149

150-
__NOTE: This command only works with PostgreSQL using the `pgx` driver and outputting Go code.__
150+
__NOTE: This command only works with PostgreSQL using the `pgx/v4` and `pgx/v5` drivers and outputting Go code.__
151151

152152
The generated method will return a batch object. The batch object will have
153153
the following methods:
@@ -183,7 +183,7 @@ func (b *BooksByTitleYearBatchResults) Close() error {
183183

184184
## `:batchone`
185185

186-
__NOTE: This command only works with PostgreSQL using the `pgx` driver and outputting Go code.__
186+
__NOTE: This command only works with PostgreSQL using the `pgx/v4` and `pgx/v5` drivers and outputting Go code.__
187187

188188
The generated method will return a batch object. The batch object will have
189189
the following methods:

internal/cmd/cmd.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ import (
1111

1212
"github.com/spf13/cobra"
1313
"github.com/spf13/pflag"
14-
yaml "gopkg.in/yaml.v3"
14+
"gopkg.in/yaml.v3"
1515

16+
"github.com/kyleconroy/sqlc/internal/codegen/golang"
1617
"github.com/kyleconroy/sqlc/internal/config"
1718
"github.com/kyleconroy/sqlc/internal/debug"
1819
"github.com/kyleconroy/sqlc/internal/info"
@@ -112,6 +113,16 @@ func ParseEnv(c *cobra.Command) Env {
112113
}
113114
}
114115

116+
func (e *Env) Validate(cfg *config.Config) error {
117+
for _, sql := range cfg.SQL {
118+
if sql.Gen.Go != nil && sql.Gen.Go.SQLPackage == golang.SQLPackagePGXV5 && !e.ExperimentalFeatures {
119+
return fmt.Errorf("'pgx/v5' golang sql package requires enabled '--experimental' flag")
120+
}
121+
}
122+
123+
return nil
124+
}
125+
115126
func getConfigPath(stderr io.Writer, f *pflag.Flag) (string, string) {
116127
if f != nil && f.Changed {
117128
file := f.Value.String()

internal/cmd/generate.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,11 @@ func Generate(ctx context.Context, e Env, dir, filename string, stderr io.Writer
129129
return nil, err
130130
}
131131

132+
if err := e.Validate(conf); err != nil {
133+
fmt.Fprintf(stderr, "error validating %s: %s\n", base, err)
134+
return nil, err
135+
}
136+
132137
output := map[string]string{}
133138
errored := false
134139

@@ -194,7 +199,7 @@ func Generate(ctx context.Context, e Env, dir, filename string, stderr io.Writer
194199
trace.Logf(ctx, "", "name=%s dir=%s plugin=%s", name, dir, lang)
195200
}
196201

197-
result, failed := parse(ctx, e, name, dir, sql.SQL, combo, parseOpts, stderr)
202+
result, failed := parse(ctx, name, dir, sql.SQL, combo, parseOpts, stderr)
198203
if failed {
199204
if packageRegion != nil {
200205
packageRegion.End()
@@ -233,7 +238,7 @@ func Generate(ctx context.Context, e Env, dir, filename string, stderr io.Writer
233238
return output, nil
234239
}
235240

236-
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) {
241+
func parse(ctx context.Context, name, dir string, sql config.SQL, combo config.CombinedSettings, parserOpts opts.Parser, stderr io.Writer) (*compiler.Result, bool) {
237242
if debug.Traced {
238243
defer trace.StartRegion(ctx, "parse").End()
239244
}

internal/codegen/golang/driver.go

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,41 @@
11
package golang
22

3-
import (
4-
"github.com/kyleconroy/sqlc/internal/plugin"
5-
)
6-
73
type SQLDriver int
84

5+
const (
6+
SQLPackagePGXV4 string = "pgx/v4"
7+
SQLPackagePGXV5 string = "pgx/v5"
8+
SQLPackageStandard string = "database/sql"
9+
)
10+
911
const (
1012
SQLDriverPGXV4 SQLDriver = iota
13+
SQLDriverPGXV5
1114
SQLDriverLibPQ
1215
)
1316

14-
func parseDriver(settings *plugin.Settings) SQLDriver {
15-
if settings.Go.SqlPackage == "pgx/v4" {
17+
func parseDriver(sqlPackage string) SQLDriver {
18+
switch sqlPackage {
19+
case SQLPackagePGXV4:
1620
return SQLDriverPGXV4
17-
} else {
21+
case SQLPackagePGXV5:
22+
return SQLDriverPGXV5
23+
default:
1824
return SQLDriverLibPQ
1925
}
2026
}
27+
28+
func (d SQLDriver) IsPGX() bool {
29+
return d == SQLDriverPGXV4 || d == SQLDriverPGXV5
30+
}
31+
32+
func (d SQLDriver) Package() string {
33+
switch d {
34+
case SQLDriverPGXV4:
35+
return SQLPackagePGXV4
36+
case SQLDriverPGXV5:
37+
return SQLPackagePGXV5
38+
default:
39+
return SQLPackageStandard
40+
}
41+
}

internal/codegen/golang/gen.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import (
1818
type tmplCtx struct {
1919
Q string
2020
Package string
21-
SQLPackage SQLPackage
21+
SQLDriver SQLDriver
2222
Enums []Enum
2323
Structs []Struct
2424
GoQueries []Query
@@ -91,7 +91,7 @@ func generate(req *plugin.CodeGenRequest, enums []Enum, structs []Struct, querie
9191
EmitAllEnumValues: golang.EmitAllEnumValues,
9292
UsesCopyFrom: usesCopyFrom(queries),
9393
UsesBatch: usesBatch(queries),
94-
SQLPackage: SQLPackageFromString(golang.SqlPackage),
94+
SQLDriver: parseDriver(golang.SqlPackage),
9595
Q: "`",
9696
Package: golang.Package,
9797
GoQueries: queries,
@@ -100,11 +100,11 @@ func generate(req *plugin.CodeGenRequest, enums []Enum, structs []Struct, querie
100100
SqlcVersion: req.SqlcVersion,
101101
}
102102

103-
if tctx.UsesCopyFrom && tctx.SQLPackage != SQLPackagePGX {
103+
if tctx.UsesCopyFrom && !tctx.SQLDriver.IsPGX() {
104104
return nil, errors.New(":copyfrom is only supported by pgx")
105105
}
106106

107-
if tctx.UsesBatch && tctx.SQLPackage != SQLPackagePGX {
107+
if tctx.UsesBatch && !tctx.SQLDriver.IsPGX() {
108108
return nil, errors.New(":batch* commands are only supported by pgx")
109109
}
110110

internal/codegen/golang/go_type.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ func goType(req *plugin.CodeGenRequest, col *plugin.Column) string {
3838
}
3939
typ := goInnerType(req, col)
4040
if col.IsArray {
41+
if parseDriver(req.Settings.Go.SqlPackage) == SQLDriverPGXV5 {
42+
return "pgtype.Array[" + typ + "]"
43+
}
4144
return "[]" + typ
4245
}
4346
return typ

internal/codegen/golang/imports.go

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ func (i *importer) Imports(filename string) [][]ImportSpec {
102102
case copyfromFileName:
103103
return mergeImports(i.copyfromImports())
104104
case batchFileName:
105-
return mergeImports(i.batchImports(filename))
105+
return mergeImports(i.batchImports())
106106
default:
107107
return mergeImports(i.queryImports(filename))
108108
}
@@ -114,11 +114,14 @@ func (i *importer) dbImports() fileImports {
114114
{Path: "context"},
115115
}
116116

117-
sqlpkg := SQLPackageFromString(i.Settings.Go.SqlPackage)
117+
sqlpkg := parseDriver(i.Settings.Go.SqlPackage)
118118
switch sqlpkg {
119-
case SQLPackagePGX:
119+
case SQLDriverPGXV4:
120120
pkg = append(pkg, ImportSpec{Path: "github.com/jackc/pgconn"})
121121
pkg = append(pkg, ImportSpec{Path: "github.com/jackc/pgx/v4"})
122+
case SQLDriverPGXV5:
123+
pkg = append(pkg, ImportSpec{Path: "github.com/jackc/pgx/v5/pgconn"})
124+
pkg = append(pkg, ImportSpec{Path: "github.com/jackc/pgx/v5"})
122125
default:
123126
std = append(std, ImportSpec{Path: "database/sql"})
124127
if i.Settings.Go.EmitPreparedQueries {
@@ -136,22 +139,8 @@ var stdlibTypes = map[string]string{
136139
"time.Time": "time",
137140
"net.IP": "net",
138141
"net.HardwareAddr": "net",
139-
}
140-
141-
var pgtypeTypes = map[string]struct{}{
142-
"pgtype.CIDR": {},
143-
"pgtype.Daterange": {},
144-
"pgtype.Inet": {},
145-
"pgtype.Int4range": {},
146-
"pgtype.Int8range": {},
147-
"pgtype.JSON": {},
148-
"pgtype.JSONB": {},
149-
"pgtype.Hstore": {},
150-
"pgtype.Macaddr": {},
151-
"pgtype.Numeric": {},
152-
"pgtype.Numrange": {},
153-
"pgtype.Tsrange": {},
154-
"pgtype.Tstzrange": {},
142+
"netip.Addr": "net/netip",
143+
"netip.Prefix": "net/netip",
155144
}
156145

157146
var pqtypeTypes = map[string]struct{}{
@@ -169,12 +158,14 @@ func buildImports(settings *plugin.Settings, queries []Query, uses func(string)
169158
std["database/sql"] = struct{}{}
170159
}
171160

172-
sqlpkg := SQLPackageFromString(settings.Go.SqlPackage)
161+
sqlpkg := parseDriver(settings.Go.SqlPackage)
173162
for _, q := range queries {
174163
if q.Cmd == metadata.CmdExecResult {
175164
switch sqlpkg {
176-
case SQLPackagePGX:
165+
case SQLDriverPGXV4:
177166
pkg[ImportSpec{Path: "github.com/jackc/pgconn"}] = struct{}{}
167+
case SQLDriverPGXV5:
168+
pkg[ImportSpec{Path: "github.com/jackc/pgx/v5/pgconn"}] = struct{}{}
178169
default:
179170
std["database/sql"] = struct{}{}
180171
}
@@ -187,15 +178,18 @@ func buildImports(settings *plugin.Settings, queries []Query, uses func(string)
187178
}
188179
}
189180

190-
for typeName, _ := range pgtypeTypes {
191-
if uses(typeName) {
181+
if uses("pgtype.") {
182+
if sqlpkg == SQLDriverPGXV5 {
183+
pkg[ImportSpec{Path: "github.com/jackc/pgx/v5/pgtype"}] = struct{}{}
184+
} else {
192185
pkg[ImportSpec{Path: "github.com/jackc/pgtype"}] = struct{}{}
193186
}
194187
}
195188

196189
for typeName, _ := range pqtypeTypes {
197190
if uses(typeName) {
198191
pkg[ImportSpec{Path: "github.com/tabbed/pqtype"}] = struct{}{}
192+
break
199193
}
200194
}
201195

@@ -373,8 +367,8 @@ func (i *importer) queryImports(filename string) fileImports {
373367
std["context"] = struct{}{}
374368
}
375369

376-
sqlpkg := SQLPackageFromString(i.Settings.Go.SqlPackage)
377-
if sliceScan() && sqlpkg != SQLPackagePGX {
370+
sqlpkg := parseDriver(i.Settings.Go.SqlPackage)
371+
if sliceScan() && !sqlpkg.IsPGX() {
378372
pkg[ImportSpec{Path: "github.com/lib/pq"}] = struct{}{}
379373
}
380374

@@ -409,7 +403,7 @@ func (i *importer) copyfromImports() fileImports {
409403
return sortedImports(std, pkg)
410404
}
411405

412-
func (i *importer) batchImports(filename string) fileImports {
406+
func (i *importer) batchImports() fileImports {
413407
batchQueries := make([]Query, 0, len(i.Queries))
414408
for _, q := range i.Queries {
415409
if usesBatch([]Query{q}) {
@@ -452,7 +446,13 @@ func (i *importer) batchImports(filename string) fileImports {
452446

453447
std["context"] = struct{}{}
454448
std["errors"] = struct{}{}
455-
pkg[ImportSpec{Path: "github.com/jackc/pgx/v4"}] = struct{}{}
449+
sqlpkg := parseDriver(i.Settings.Go.SqlPackage)
450+
switch sqlpkg {
451+
case SQLDriverPGXV4:
452+
pkg[ImportSpec{Path: "github.com/jackc/pgx/v4"}] = struct{}{}
453+
case SQLDriverPGXV5:
454+
pkg[ImportSpec{Path: "github.com/jackc/pgx/v5"}] = struct{}{}
455+
}
456456

457457
return sortedImports(std, pkg)
458458
}

0 commit comments

Comments
 (0)