Skip to content

Commit d373474

Browse files
committed
Generate read-only Queries
1 parent c870a71 commit d373474

File tree

20 files changed

+924
-7
lines changed

20 files changed

+924
-7
lines changed

internal/codegen/golang/gen.go

Lines changed: 37 additions & 7 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+
GoQueries []Query
26+
GoReadQueries []Query
27+
SqlcVersion string
2728

2829
// TODO: Race conditions
2930
SourceName string
@@ -37,6 +38,7 @@ type tmplCtx struct {
3738
EmitMethodsWithDBArgument bool
3839
EmitEnumValidMethod bool
3940
EmitAllEnumValues bool
41+
EmitReadOnlyPrepared bool
4042
UsesCopyFrom bool
4143
UsesBatch bool
4244
OmitSqlcVersion bool
@@ -177,6 +179,7 @@ func generate(req *plugin.GenerateRequest, options *opts.Options, enums []Enum,
177179
EmitMethodsWithDBArgument: options.EmitMethodsWithDbArgument,
178180
EmitEnumValidMethod: options.EmitEnumValidMethod,
179181
EmitAllEnumValues: options.EmitAllEnumValues,
182+
EmitReadOnlyPrepared: options.EmitReadOnlyPrepared,
180183
UsesCopyFrom: usesCopyFrom(queries),
181184
UsesBatch: usesBatch(queries),
182185
SQLDriver: parseDriver(options.SqlPackage),
@@ -240,6 +243,7 @@ func generate(req *plugin.GenerateRequest, options *opts.Options, enums []Enum,
240243
w := bufio.NewWriter(&b)
241244
tctx.SourceName = name
242245
tctx.GoQueries = replacedQueries
246+
tctx.GoReadQueries = readOnly(replacedQueries)
243247
err := tmpl.ExecuteTemplate(w, templateName, &tctx)
244248
w.Flush()
245249
if err != nil {
@@ -278,6 +282,7 @@ func generate(req *plugin.GenerateRequest, options *opts.Options, enums []Enum,
278282
if options.OutputCopyfromFileName != "" {
279283
copyfromFileName = options.OutputCopyfromFileName
280284
}
285+
readQueriesFileName := "read.go"
281286

282287
batchFileName := "batch.go"
283288
if options.OutputBatchFileName != "" {
@@ -305,6 +310,11 @@ func generate(req *plugin.GenerateRequest, options *opts.Options, enums []Enum,
305310
return nil, err
306311
}
307312
}
313+
if tctx.EmitReadOnlyPrepared {
314+
if err := execute(readQueriesFileName, "readQueriesFile"); err != nil {
315+
return nil, err
316+
}
317+
}
308318

309319
files := map[string]struct{}{}
310320
for _, gq := range queries {
@@ -405,3 +415,23 @@ func filterUnusedStructs(enums []Enum, structs []Struct, queries []Query) ([]Enu
405415

406416
return keepEnums, keepStructs
407417
}
418+
419+
func readOnly(queries []Query) []Query {
420+
var rq []Query
421+
for _, q := range queries {
422+
if !q.hasRetType() {
423+
continue
424+
}
425+
426+
qsql := strings.ToUpper(q.SQL)
427+
if !strings.Contains(qsql, "SELECT") {
428+
continue
429+
}
430+
if strings.Contains(qsql, "UPDATE") || strings.Contains(qsql, "INSERT") {
431+
continue
432+
}
433+
434+
rq = append(rq, q)
435+
}
436+
return rq
437+
}

internal/codegen/golang/imports.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ func (i *importer) Imports(filename string) [][]ImportSpec {
101101
if i.Options.OutputBatchFileName != "" {
102102
batchFileName = i.Options.OutputBatchFileName
103103
}
104+
readQueriesFileName := "read.go"
104105

105106
switch filename {
106107
case dbFileName:
@@ -113,6 +114,8 @@ func (i *importer) Imports(filename string) [][]ImportSpec {
113114
return mergeImports(i.copyfromImports())
114115
case batchFileName:
115116
return mergeImports(i.batchImports())
117+
case readQueriesFileName:
118+
return mergeImports(i.dbImports())
116119
default:
117120
return mergeImports(i.queryImports(filename))
118121
}

internal/codegen/golang/opts/options.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ type Options struct {
2525
EmitEnumValidMethod bool `json:"emit_enum_valid_method,omitempty" yaml:"emit_enum_valid_method"`
2626
EmitAllEnumValues bool `json:"emit_all_enum_values,omitempty" yaml:"emit_all_enum_values"`
2727
EmitSqlAsComment bool `json:"emit_sql_as_comment,omitempty" yaml:"emit_sql_as_comment"`
28+
EmitReadOnlyPrepared bool `json:"emit_read_only_prepared,omitempty" yaml:"emit_read_only_prepared"`
2829
JsonTagsCaseStyle string `json:"json_tags_case_style,omitempty" yaml:"json_tags_case_style"`
2930
Package string `json:"package" yaml:"package"`
3031
Out string `json:"out" yaml:"out"`
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
{{define "readQueriesFileStd"}}
2+
3+
{{if and .EmitPreparedQueries .EmitReadOnlyPrepared}}
4+
func PrepareReadOnly(ctx context.Context, db DBTX) (*ReadQueries, error) {
5+
q := ReadQueries{db: db}
6+
var err error
7+
{{- if eq (len .GoReadQueries) 0 }}
8+
_ = err
9+
{{- end }}
10+
{{- range .GoReadQueries }}
11+
if q.{{.FieldName}}, err = db.PrepareContext(ctx, {{.ConstantName}}); err != nil {
12+
return nil, fmt.Errorf("error preparing query {{.MethodName}}: %w", err)
13+
}
14+
{{- end}}
15+
return &q, nil
16+
}
17+
18+
type ReadQueries struct {
19+
{{- if not .EmitMethodsWithDBArgument}}
20+
db DBTX
21+
{{- end}}
22+
23+
{{- if .EmitPreparedQueries}}
24+
{{- range .GoReadQueries}}
25+
{{.FieldName}} *sql.Stmt
26+
{{- end}}
27+
{{- end}}
28+
}
29+
30+
func (q *ReadQueries) Close() error {
31+
var err error
32+
{{- range .GoReadQueries }}
33+
if q.{{.FieldName}} != nil {
34+
if cerr := q.{{.FieldName}}.Close(); cerr != nil {
35+
err = fmt.Errorf("error closing {{.FieldName}}: %w", cerr)
36+
}
37+
}
38+
{{- end}}
39+
return err
40+
}
41+
42+
func (q *ReadQueries) query(ctx context.Context, stmt *sql.Stmt, query string, args ...interface{}) (*sql.Rows, error) {
43+
switch {
44+
case stmt != nil:
45+
return stmt.QueryContext(ctx, args...)
46+
default:
47+
return q.db.QueryContext(ctx, query, args...)
48+
}
49+
}
50+
51+
func (q *ReadQueries) queryRow(ctx context.Context, stmt *sql.Stmt, query string, args ...interface{}) (*sql.Row) {
52+
switch {
53+
case stmt != nil:
54+
return stmt.QueryRowContext(ctx, args...)
55+
default:
56+
return q.db.QueryRowContext(ctx, query, args...)
57+
}
58+
}
59+
60+
{{range .GoReadQueries}}
61+
62+
{{if eq .Cmd ":one"}}
63+
{{range .Comments}}//{{.}}
64+
{{end -}}
65+
func (q *ReadQueries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}}) ({{.Ret.DefineType}}, error) {
66+
{{- template "queryCodeStdExec" . }}
67+
{{- if or (ne .Arg.Pair .Ret.Pair) (ne .Arg.DefineType .Ret.DefineType) }}
68+
var {{.Ret.Name}} {{.Ret.Type}}
69+
{{- end}}
70+
err := row.Scan({{.Ret.Scan}})
71+
return {{.Ret.ReturnName}}, err
72+
}
73+
{{end}}
74+
75+
{{if eq .Cmd ":many"}}
76+
{{range .Comments}}//{{.}}
77+
{{end -}}
78+
func (q *ReadQueries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}}) ([]{{.Ret.DefineType}}, error) {
79+
{{- template "queryCodeStdExec" . }}
80+
if err != nil {
81+
return nil, err
82+
}
83+
defer rows.Close()
84+
{{- if $.EmitEmptySlices}}
85+
items := []{{.Ret.DefineType}}{}
86+
{{else}}
87+
var items []{{.Ret.DefineType}}
88+
{{end -}}
89+
for rows.Next() {
90+
var {{.Ret.Name}} {{.Ret.Type}}
91+
if err := rows.Scan({{.Ret.Scan}}); err != nil {
92+
return nil, err
93+
}
94+
items = append(items, {{.Ret.ReturnName}})
95+
}
96+
if err := rows.Close(); err != nil {
97+
return nil, err
98+
}
99+
if err := rows.Err(); err != nil {
100+
return nil, err
101+
}
102+
return items, nil
103+
}
104+
{{end}}
105+
106+
{{end}}
107+
{{end}}
108+
{{end}}

internal/codegen/golang/templates/template.tmpl

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,4 +251,31 @@ import (
251251
{{if .SQLDriver.IsPGX }}
252252
{{- template "batchCodePgx" .}}
253253
{{end}}
254+
255+
{{template "readQueriesFile" . }}
256+
{{end}}
257+
258+
{{define "readQueriesFile"}}
259+
{{if not .SQLDriver.IsPGX }}
260+
{{if .BuildTags}}
261+
//go:build {{.BuildTags}}
262+
263+
{{end}}// Code generated by sqlc. DO NOT EDIT.
264+
{{if not .OmitSqlcVersion}}// versions:
265+
// sqlc {{.SqlcVersion}}
266+
267+
package {{.Package}}
268+
269+
{{ if hasImports .SourceName }}
270+
import (
271+
{{range imports .SourceName}}
272+
{{range .}}{{.}}
273+
{{end}}
274+
{{end}}
275+
)
276+
{{end}}
277+
278+
{{- template "readQueriesFileStd" .}}
279+
{{end}}
280+
{{end}}
254281
{{end}}

internal/config/v_one.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ type v1PackageSettings struct {
4242
EmitEnumValidMethod bool `json:"emit_enum_valid_method,omitempty" yaml:"emit_enum_valid_method"`
4343
EmitAllEnumValues bool `json:"emit_all_enum_values,omitempty" yaml:"emit_all_enum_values"`
4444
EmitSqlAsComment bool `json:"emit_sql_as_comment,omitempty" yaml:"emit_sql_as_comment"`
45+
EmitReadOnlyPrepared bool `json:"emit_read_only_prepared,omitempty" yaml:"emit_read_only_prepared"`
4546
JSONTagsCaseStyle string `json:"json_tags_case_style,omitempty" yaml:"json_tags_case_style"`
4647
SQLPackage string `json:"sql_package" yaml:"sql_package"`
4748
SQLDriver string `json:"sql_driver" yaml:"sql_driver"`
@@ -152,6 +153,7 @@ func (c *V1GenerateSettings) Translate() Config {
152153
EmitEnumValidMethod: pkg.EmitEnumValidMethod,
153154
EmitAllEnumValues: pkg.EmitAllEnumValues,
154155
EmitSqlAsComment: pkg.EmitSqlAsComment,
156+
EmitReadOnlyPrepared: pkg.EmitReadOnlyPrepared,
155157
Package: pkg.Name,
156158
Out: pkg.Path,
157159
SqlPackage: pkg.SQLPackage,

0 commit comments

Comments
 (0)