Skip to content

Commit 69ccc94

Browse files
Untested golang codegen for pgx v5
1 parent f9d1b90 commit 69ccc94

File tree

8 files changed

+193
-36
lines changed

8 files changed

+193
-36
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package golang
2+
3+
type CompositeType struct {
4+
SQLName string
5+
Name string
6+
Comment string
7+
Fields []Field
8+
}

internal/codegen/golang/gen.go

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,14 @@ import (
1717
)
1818

1919
type tmplCtx struct {
20-
Q string
21-
Package string
22-
SQLDriver opts.SQLDriver
23-
Enums []Enum
24-
Structs []Struct
25-
GoQueries []Query
26-
SqlcVersion string
20+
Q string
21+
Package string
22+
SQLDriver opts.SQLDriver
23+
Enums []Enum
24+
Structs []Struct
25+
CompositeTypes []CompositeType
26+
GoQueries []Query
27+
SqlcVersion string
2728

2829
// TODO: Race conditions
2930
SourceName string
@@ -109,12 +110,17 @@ func Generate(ctx context.Context, req *plugin.GenerateRequest) (*plugin.Generat
109110
if err != nil {
110111
return nil, err
111112
}
113+
driver := parseDriver(options.SqlPackage)
112114

113115
if err := opts.ValidateOpts(options); err != nil {
114116
return nil, err
115117
}
116118

117119
enums := buildEnums(req, options)
120+
compositeTypes := []CompositeType{}
121+
if driver.IsPGXV5() {
122+
compositeTypes = buildCompositeTypes(req, options)
123+
}
118124
structs := buildStructs(req, options)
119125
queries, err := buildQueries(req, options, structs)
120126
if err != nil {
@@ -125,46 +131,49 @@ func Generate(ctx context.Context, req *plugin.GenerateRequest) (*plugin.Generat
125131
enums, structs = filterUnusedStructs(enums, structs, queries)
126132
}
127133

128-
if err := validate(options, enums, structs, queries); err != nil {
134+
if err := validate(options, enums, compositeTypes, structs, queries); err != nil {
129135
return nil, err
130136
}
131137

132-
return generate(req, options, enums, structs, queries)
138+
return generate(req, options, enums, compositeTypes, structs, queries)
133139
}
134140

135-
func validate(options *opts.Options, enums []Enum, structs []Struct, queries []Query) error {
136-
enumNames := make(map[string]struct{})
141+
func validate(options *opts.Options, enums []Enum, compositeTypes []CompositeType, structs []Struct, queries []Query) error {
142+
usedNames := make(map[string]string)
137143
for _, enum := range enums {
138-
enumNames[enum.Name] = struct{}{}
139-
enumNames["Null"+enum.Name] = struct{}{}
144+
usedNames[enum.Name] = "enum"
145+
usedNames["Null"+enum.Name] = "enum"
140146
}
141-
structNames := make(map[string]struct{})
142147
for _, struckt := range structs {
143-
if _, ok := enumNames[struckt.Name]; ok {
144-
return fmt.Errorf("struct name conflicts with enum name: %s", struckt.Name)
148+
if usedType, ok := usedNames[struckt.Name]; ok {
149+
return fmt.Errorf("struct name conflicts with %s name: %s", usedType, struckt.Name)
150+
}
151+
usedNames[struckt.Name] = "struct"
152+
}
153+
for _, ct := range compositeTypes {
154+
if usedType, ok := usedNames[ct.Name]; ok {
155+
return fmt.Errorf("composite type name conflicts with %s name: %s", usedType, ct.Name)
145156
}
146-
structNames[struckt.Name] = struct{}{}
157+
usedNames[ct.Name] = "composite type"
147158
}
148159
if !options.EmitExportedQueries {
149160
return nil
150161
}
151162
for _, query := range queries {
152-
if _, ok := enumNames[query.ConstantName]; ok {
153-
return fmt.Errorf("query constant name conflicts with enum name: %s", query.ConstantName)
154-
}
155-
if _, ok := structNames[query.ConstantName]; ok {
156-
return fmt.Errorf("query constant name conflicts with struct name: %s", query.ConstantName)
163+
if usedType, ok := usedNames[query.ConstantName]; ok {
164+
return fmt.Errorf("query constant name conflicts with %s name: %s", usedType, query.ConstantName)
157165
}
158166
}
159167
return nil
160168
}
161169

162-
func generate(req *plugin.GenerateRequest, options *opts.Options, enums []Enum, structs []Struct, queries []Query) (*plugin.GenerateResponse, error) {
170+
func generate(req *plugin.GenerateRequest, options *opts.Options, enums []Enum, compositeTypes []CompositeType, structs []Struct, queries []Query) (*plugin.GenerateResponse, error) {
163171
i := &importer{
164-
Options: options,
165-
Queries: queries,
166-
Enums: enums,
167-
Structs: structs,
172+
Options: options,
173+
Queries: queries,
174+
Enums: enums,
175+
CompositeTypes: compositeTypes,
176+
Structs: structs,
168177
}
169178

170179
tctx := tmplCtx{
@@ -183,6 +192,7 @@ func generate(req *plugin.GenerateRequest, options *opts.Options, enums []Enum,
183192
Q: "`",
184193
Package: options.Package,
185194
Enums: enums,
195+
CompositeTypes: compositeTypes,
186196
Structs: structs,
187197
SqlcVersion: req.SqlcVersion,
188198
BuildTags: options.BuildTags,

internal/codegen/golang/imports.go

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,11 @@ func mergeImports(imps ...fileImports) [][]ImportSpec {
5858
}
5959

6060
type importer struct {
61-
Options *opts.Options
62-
Queries []Query
63-
Enums []Enum
64-
Structs []Struct
61+
Options *opts.Options
62+
Queries []Query
63+
Enums []Enum
64+
CompositeTypes []CompositeType
65+
Structs []Struct
6566
}
6667

6768
func (i *importer) usesType(typ string) bool {
@@ -72,6 +73,13 @@ func (i *importer) usesType(typ string) bool {
7273
}
7374
}
7475
}
76+
for _, ct := range i.CompositeTypes {
77+
for _, f := range ct.Fields {
78+
if hasPrefixIgnoringSliceAndPointerPrefix(f.Type, typ) {
79+
return true
80+
}
81+
}
82+
}
7583
return false
7684
}
7785

@@ -132,6 +140,9 @@ func (i *importer) dbImports() fileImports {
132140
case opts.SQLDriverPGXV5:
133141
pkg = append(pkg, ImportSpec{Path: "github.com/jackc/pgx/v5/pgconn"})
134142
pkg = append(pkg, ImportSpec{Path: "github.com/jackc/pgx/v5"})
143+
if len(i.CompositeTypes) > 0 {
144+
pkg = append(pkg, ImportSpec{Path: "github.com/jackc/pgx/v5/pgtype"})
145+
}
135146
default:
136147
std = append(std, ImportSpec{Path: "database/sql"})
137148
if i.Options.EmitPreparedQueries {

internal/codegen/golang/opts/enum.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ func (d SQLDriver) IsPGX() bool {
4848
return d == SQLDriverPGXV4 || d == SQLDriverPGXV5
4949
}
5050

51+
func (d SQLDriver) IsPGXV5() bool {
52+
return d == SQLDriverPGXV5
53+
}
54+
5155
func (d SQLDriver) IsGoSQLDriverMySQL() bool {
5256
return d == SQLDriverGoSQLDriverMySQL
5357
}

internal/codegen/golang/postgresql_type.go

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -587,13 +587,26 @@ func postgresType(req *plugin.GenerateRequest, options *opts.Options, col *plugi
587587

588588
for _, ct := range schema.CompositeTypes {
589589
if rel.Name == ct.Name && rel.Schema == schema.Name {
590-
if notNull {
591-
return "string"
590+
if !driver.IsPGXV5() {
591+
if notNull {
592+
return "string"
593+
}
594+
if emitPointersForNull {
595+
return "*string"
596+
}
597+
return "sql.NullString"
592598
}
593-
if emitPointersForNull {
594-
return "*string"
599+
600+
var ctName string
601+
if schema.Name == req.Catalog.DefaultSchema {
602+
ctName = StructName(ct.Name, options)
603+
} else {
604+
ctName = StructName(schema.Name+"_"+ct.Name, options)
605+
}
606+
if notNull {
607+
return ctName
595608
}
596-
return "sql.NullString"
609+
return "*" + ctName
597610
}
598611
}
599612
}

internal/codegen/golang/result.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,82 @@ func buildEnums(req *plugin.GenerateRequest, options *opts.Options) []Enum {
6060
return enums
6161
}
6262

63+
func topoSort(graph map[string]map[string]bool, visited map[string]bool, node string, sorted *[]string) {
64+
visited[node] = true
65+
for child := range graph[node] {
66+
if visited[child] {
67+
continue
68+
}
69+
topoSort(graph, visited, child, sorted)
70+
}
71+
*sorted = append(*sorted, node)
72+
}
73+
74+
// Orders types in order such that pgtype.Map can successfully register them
75+
func sortedCompositeTypes(compositeTypes map[string]CompositeType) []CompositeType {
76+
// Map of composite type names to a set of every child composite type name
77+
graph := make(map[string]map[string]bool)
78+
for typeName, ct := range compositeTypes {
79+
graph[typeName] = make(map[string]bool)
80+
for _, field := range ct.Fields {
81+
fieldType := trimSliceAndPointerPrefix(field.Type)
82+
if _, ok := compositeTypes[fieldType]; ok {
83+
graph[typeName][fieldType] = true
84+
}
85+
}
86+
}
87+
88+
visited := make(map[string]bool)
89+
sorted := []string{}
90+
for typeName := range compositeTypes {
91+
if visited[typeName] {
92+
continue
93+
}
94+
topoSort(graph, visited, typeName, &sorted)
95+
}
96+
97+
compositeTypeArr := []CompositeType{}
98+
for _, typeName := range sorted {
99+
compositeTypeArr = append(compositeTypeArr, compositeTypes[typeName])
100+
}
101+
return compositeTypeArr
102+
}
103+
104+
func buildCompositeTypes(req *plugin.GenerateRequest, options *opts.Options) []CompositeType {
105+
compositeTypes := make(map[string]CompositeType)
106+
for _, schema := range req.Catalog.Schemas {
107+
if schema.Name == "pg_catalog" || schema.Name == "information_schema" {
108+
continue
109+
}
110+
for _, ct := range schema.CompositeTypes {
111+
var typeName string
112+
var sqlName string
113+
if schema.Name == req.Catalog.DefaultSchema {
114+
typeName = ct.Name
115+
sqlName = ct.Name
116+
} else {
117+
typeName = schema.Name + "_" + ct.Name
118+
sqlName = schema.Name + "." + ct.Name
119+
}
120+
typeName = StructName(typeName, options)
121+
compositeType := CompositeType{
122+
SQLName: sqlName,
123+
Name: typeName,
124+
Comment: ct.Comment,
125+
}
126+
for _, column := range ct.Columns {
127+
compositeType.Fields = append(compositeType.Fields, Field{
128+
Name: StructName(column.Name, options),
129+
Type: goType(req, options, column),
130+
Comment: column.Comment,
131+
})
132+
}
133+
compositeTypes[typeName] = compositeType
134+
}
135+
}
136+
return sortedCompositeTypes(compositeTypes)
137+
}
138+
63139
func buildStructs(req *plugin.GenerateRequest, options *opts.Options) []Struct {
64140
var structs []Struct
65141
for _, schema := range req.Catalog.Schemas {

internal/codegen/golang/templates/pgx/dbCode.tmpl

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ type DBTX interface {
1010
{{- if .UsesBatch }}
1111
SendBatch(context.Context, *pgx.Batch) pgx.BatchResults
1212
{{- end }}
13+
{{- if gt (len .CompositeTypes) 0 }}
14+
LoadTypes(ctx context.Context, typeNames []string) ([]*pgtype.Type, error)
15+
TypeMap() *pgtype.Map
16+
{{- end }}
1317
}
1418

1519
{{ if .EmitMethodsWithDBArgument}}
@@ -27,6 +31,26 @@ type Queries struct {
2731
{{end}}
2832
}
2933

34+
{{if gt (len .CompositeTypes) 0}}
35+
{{if not .EmitMethodsWithDBArgument}}
36+
func (q *Queries) RegisterTypes(ctx context.Context) error {
37+
db := q.db
38+
{{else}}
39+
func (q *Queries) RegisterTypes(ctx context.Context, db *Queries) error {
40+
{{end}}
41+
typeNames := []string{
42+
{{- range .CompositeTypes}}
43+
"{{.SQLName}}",
44+
{{- end}}
45+
}
46+
dataTypes, err := db.LoadTypes(ctx, typeNames)
47+
if err != nil {
48+
return err
49+
}
50+
db.TypeMap().RegisterTypes(dataTypes)
51+
return nil
52+
}
53+
{{end}}
3054
{{if not .EmitMethodsWithDBArgument}}
3155
func (q *Queries) WithTx(tx pgx.Tx) *Queries {
3256
return &Queries{

internal/codegen/golang/templates/template.tmpl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,17 @@ func All{{ .Name }}Values() []{{ .Name }} {
150150
{{ end }}
151151
{{end}}
152152

153+
{{range .CompositeTypes}}
154+
{{if .Comment}}{{comment .Comment}}{{end}}
155+
type {{.Name}} struct { {{- range .Fields}}
156+
{{- if .Comment}}
157+
{{comment .Comment}}{{else}}
158+
{{- end}}
159+
{{.Name}} {{.Type}} {{if .Tag}}{{$.Q}}{{.Tag}}{{$.Q}}{{end}}
160+
{{- end}}
161+
}
162+
{{end}}
163+
153164
{{range .Structs}}
154165
{{if .Comment}}{{comment .Comment}}{{end}}
155166
type {{.Name}} struct { {{- range .Fields}}

0 commit comments

Comments
 (0)