From c9ccd33692c529e97dd05c1b010b296a3dc96cae Mon Sep 17 00:00:00 2001 From: Nicholas Santi Date: Mon, 22 Jul 2024 17:14:04 +0100 Subject: [PATCH 1/8] TODO: Fix queryCode.tmpl to accept setting --- Makefile | 3 + internal/codegen/golang/opts/options.go | 1 + internal/codegen/golang/query.go | 3 +- internal/codegen/golang/result.go | 9 ++- internal/codegen/golang/schema.go | 62 +++++++++++++++++++ internal/codegen/golang/schema_test.go | 47 ++++++++++++++ .../golang/templates/stdlib/queryCode.tmpl | 28 ++++++--- .../codegen/golang/templates/template.tmpl | 4 ++ 8 files changed, 146 insertions(+), 11 deletions(-) create mode 100644 internal/codegen/golang/schema.go create mode 100644 internal/codegen/golang/schema_test.go diff --git a/Makefile b/Makefile index e9c2fbb899..2e4e47b6d5 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,9 @@ build: go build ./... +build-local: + go build -o ../sqlc ./cmd/sqlc + install: go install ./... diff --git a/internal/codegen/golang/opts/options.go b/internal/codegen/golang/opts/options.go index 30a6c2246c..263b256cf2 100644 --- a/internal/codegen/golang/opts/options.go +++ b/internal/codegen/golang/opts/options.go @@ -25,6 +25,7 @@ type Options struct { 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"` EmitSqlAsComment bool `json:"emit_sql_as_comment,omitempty" yaml:"emit_sql_as_comment"` + EmitSchemaName bool `json:"emit_schema_name,omitempty" yaml:"emit_schema_name"` JsonTagsCaseStyle string `json:"json_tags_case_style,omitempty" yaml:"json_tags_case_style"` Package string `json:"package" yaml:"package"` Out string `json:"out" yaml:"out"` diff --git a/internal/codegen/golang/query.go b/internal/codegen/golang/query.go index 3b4fb2fa1a..76c7ff9e69 100644 --- a/internal/codegen/golang/query.go +++ b/internal/codegen/golang/query.go @@ -266,7 +266,8 @@ type Query struct { Ret QueryValue Arg QueryValue // Used for :copyfrom - Table *plugin.Identifier + Table *plugin.Identifier + EmitSchema bool } func (q Query) hasRetType() bool { diff --git a/internal/codegen/golang/result.go b/internal/codegen/golang/result.go index 515d0a654f..2aed272e66 100644 --- a/internal/codegen/golang/result.go +++ b/internal/codegen/golang/result.go @@ -141,9 +141,7 @@ func newGoEmbed(embed *plugin.Identifier, structs []Struct, defaultSchema string } fields := make([]Field, len(s.Fields)) - for i, f := range s.Fields { - fields[i] = f - } + copy(fields, s.Fields) return &goEmbed{ modelType: s.Name, @@ -216,6 +214,10 @@ func buildQueries(req *plugin.GenerateRequest, options *opts.Options, structs [] } } + if options.EmitSchemaName { + query.Text = ApplySchema(query.Text) + } + gq := Query{ Cmd: query.Cmd, ConstantName: constantName, @@ -225,6 +227,7 @@ func buildQueries(req *plugin.GenerateRequest, options *opts.Options, structs [] SQL: query.Text, Comments: comments, Table: query.InsertIntoTable, + EmitSchema: options.EmitSchemaName, } sqlpkg := parseDriver(options.SqlPackage) diff --git a/internal/codegen/golang/schema.go b/internal/codegen/golang/schema.go new file mode 100644 index 0000000000..cf3a1ce735 --- /dev/null +++ b/internal/codegen/golang/schema.go @@ -0,0 +1,62 @@ +package golang + +import ( + "fmt" + "strings" +) + +func ApplySchema(query string) string { + tables := make(map[string]bool) + ctes := make(map[string]bool) + + words := strings.Fields(query) + + // Getting all the table names and CTEs + withinCTE := false + for i, word := range words { + upperWord := strings.ToUpper(word) + + if upperWord == "WITH" { + withinCTE = true + continue + } else if withinCTE { + ctes[words[i]] = true + withinCTE = false + continue + } + + if isSQLKeyword(upperWord) { + tables[nextNonKeyword(words, i)] = true + } + } + + // Removing from tables the CTEs + for cte := range ctes { + delete(tables, cte) + } + + // Replacing the table names with the placeholder + for table := range tables { + query = strings.ReplaceAll(query, " "+table, fmt.Sprintf(" `%%s`.%s", table)) + } + + return query +} + +// Helper function to check if a word is a relevant SQL keyword +func isSQLKeyword(word string) bool { + switch word { + case "FROM", "JOIN", "LEFT JOIN", "RIGHT JOIN", "FULL JOIN", "INNER JOIN", "CROSS JOIN", "UPDATE", "DELETE FROM", "INSERT INTO": + return true + } + return false +} + +func nextNonKeyword(words []string, currentIndex int) string { + for i := currentIndex + 1; i < len(words); i++ { + if !isSQLKeyword(words[i]) && words[i] != "AS" && words[i] != "(" { + return words[i] + } + } + return "" +} diff --git a/internal/codegen/golang/schema_test.go b/internal/codegen/golang/schema_test.go new file mode 100644 index 0000000000..70928776d7 --- /dev/null +++ b/internal/codegen/golang/schema_test.go @@ -0,0 +1,47 @@ +package golang_test + +import ( + "testing" + + "github.com/sqlc-dev/sqlc/internal/codegen/golang" +) + +func TestApplySchema(t *testing.T) { + testCases := []struct { + name string + inputQuery string + expectedQuery string + }{ + { + name: "Simple Query with Single Table", + inputQuery: "SELECT * FROM users", + expectedQuery: "SELECT * FROM `%s`.users", + }, + { + name: "Query with Multiple Tables", + inputQuery: "SELECT * FROM users JOIN orders ON users.id = orders.user_id", + expectedQuery: "SELECT * FROM `%s`.users JOIN `%s`.orders ON `%s`.users.id = `%s`.orders.user_id", + }, + { + name: "Query with CTE", + inputQuery: "WITH user_orders AS (SELECT * FROM users JOIN orders ON users.id = orders.user_id) SELECT * FROM user_orders", + expectedQuery: "WITH user_orders AS (SELECT * FROM `%s`.users JOIN `%s`.orders ON `%s`.users.id = `%s`.orders.user_id) SELECT * FROM user_orders", + }, + { + name: "Query with CTE and Aliases", + inputQuery: "WITH user_orders AS (SELECT * FROM users u JOIN orders o ON u.id = o.user_id) SELECT * FROM user_orders uo", + expectedQuery: "WITH user_orders AS (SELECT * FROM `%s`.users u JOIN `%s`.orders o ON u.id = o.user_id) SELECT * FROM user_orders uo", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + result := golang.ApplySchema(tc.inputQuery) + if result != tc.expectedQuery { + t.Errorf("Expected:\n%s\nGot:\n%s", tc.expectedQuery, result) + } + }) + } +} + +// "SELECT * FROM `%s`.users JOIN `%s`.orders ON `%s`.users.id `%s`.= `%s`.orders.user_id" diff --git a/internal/codegen/golang/templates/stdlib/queryCode.tmpl b/internal/codegen/golang/templates/stdlib/queryCode.tmpl index cf56000ec6..9d1ff189c0 100644 --- a/internal/codegen/golang/templates/stdlib/queryCode.tmpl +++ b/internal/codegen/golang/templates/stdlib/queryCode.tmpl @@ -22,7 +22,7 @@ type {{.Ret.Type}} struct { {{- range .Ret.Struct.Fields}} {{if eq .Cmd ":one"}} {{range .Comments}}//{{.}} {{end -}} -func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}}) ({{.Ret.DefineType}}, error) { +func (q *Queries) {{.MethodName}}(ctx context.Context, schema string, {{ dbarg }} {{.Arg.Pair}}) error { {{- template "queryCodeStdExec" . }} {{- if or (ne .Arg.Pair .Ret.Pair) (ne .Arg.DefineType .Ret.DefineType) }} var {{.Ret.Name}} {{.Ret.Type}} @@ -35,7 +35,7 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}} {{if eq .Cmd ":many"}} {{range .Comments}}//{{.}} {{end -}} -func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}}) ([]{{.Ret.DefineType}}, error) { +func (q *Queries) {{.MethodName}}(ctx context.Context, schema string, {{ dbarg }} {{.Arg.Pair}}) ([]{{.Ret.DefineType}}, error) { {{- template "queryCodeStdExec" . }} if err != nil { return nil, err @@ -66,7 +66,7 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}} {{if eq .Cmd ":exec"}} {{range .Comments}}//{{.}} {{end -}} -func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}}) error { +func (q *Queries) {{.MethodName}}(ctx context.Context, schema string, {{ dbarg }} {{.Arg.Pair}}) error { {{- template "queryCodeStdExec" . }} return err } @@ -75,7 +75,7 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}} {{if eq .Cmd ":execrows"}} {{range .Comments}}//{{.}} {{end -}} -func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}}) (int64, error) { +func (q *Queries) {{.MethodName}}(ctx context.Context, schema string, {{ dbarg }} {{.Arg.Pair}}) (int64, error) { {{- template "queryCodeStdExec" . }} if err != nil { return 0, err @@ -87,7 +87,7 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}} {{if eq .Cmd ":execlastid"}} {{range .Comments}}//{{.}} {{end -}} -func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}}) (int64, error) { +func (q *Queries) {{.MethodName}}(ctx context.Context, schema string, {{ dbarg }} {{.Arg.Pair}}) (int64, error) { {{- template "queryCodeStdExec" . }} if err != nil { return 0, err @@ -99,9 +99,15 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}} {{if eq .Cmd ":execresult"}} {{range .Comments}}//{{.}} {{end -}} +{{- if .EmitSchema }} +func (q *Queries) {{.MethodName}}(ctx context.Context, schema string, {{ dbarg }} {{.Arg.Pair}}) (sql.Result, error) { + {{- template "queryCodeStdExec" . -}} +} +{{- else }} func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}}) (sql.Result, error) { {{- template "queryCodeStdExec" . }} } +{{- end }} {{end}} {{end}} @@ -148,8 +154,16 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}} {{ queryRetval . }} {{ queryMethod . }}(ctx, query, queryParams...) {{- end -}} {{- else if emitPreparedQueries }} - {{- queryRetval . }} {{ queryMethod . }}(ctx, q.{{.FieldName}}, {{.ConstantName}}, {{.Arg.Params}}) + {{- if .EmitSchema }} + {{- queryRetval . }} {{ queryMethod . }}(ctx, q.{{.FieldName}}, strings.ReplaceAll({{.ConstantName}}, "%s", schema), {{.Arg.Params}}) + {{- else }} + {{- queryRetval . }} {{ queryMethod . }}(ctx, q.{{.FieldName}}, {{.ConstantName}}, {{.Arg.Params}}) + {{- end }} {{- else}} - {{- queryRetval . }} {{ queryMethod . }}(ctx, {{.ConstantName}}, {{.Arg.Params}}) + {{- if .EmitSchema }} + {{- queryRetval . }} {{ queryMethod . }}(ctx, q.{{.FieldName}}, strings.ReplaceAll({{.ConstantName}}, "%s", schema), {{.Arg.Params}}) + {{- else }} + {{- queryRetval . }} {{ queryMethod . }}(ctx, {{.ConstantName}}, {{.Arg.Params}}) + {{- end }} {{- end -}} {{end}} diff --git a/internal/codegen/golang/templates/template.tmpl b/internal/codegen/golang/templates/template.tmpl index afd50c01ac..4be94b9b04 100644 --- a/internal/codegen/golang/templates/template.tmpl +++ b/internal/codegen/golang/templates/template.tmpl @@ -179,6 +179,8 @@ import ( {{range .}}{{.}} {{end}} {{end}} + + "fmt" ) {{end}} @@ -210,6 +212,8 @@ import ( {{range .}}{{.}} {{end}} {{end}} + + "fmt" ) {{end}} From c0b191424a1b5c79a311a0a9f7af3616d09e6160 Mon Sep 17 00:00:00 2001 From: Nicholas Santi Date: Tue, 23 Jul 2024 09:29:30 +0100 Subject: [PATCH 2/8] Working schema emission --- internal/codegen/golang/gen.go | 7 +++ internal/codegen/golang/query.go | 3 +- internal/codegen/golang/result.go | 1 - .../golang/templates/stdlib/queryCode.tmpl | 62 +++++++++++++------ .../codegen/golang/templates/template.tmpl | 4 +- 5 files changed, 53 insertions(+), 24 deletions(-) diff --git a/internal/codegen/golang/gen.go b/internal/codegen/golang/gen.go index 5b7977f500..2d6d3bf738 100644 --- a/internal/codegen/golang/gen.go +++ b/internal/codegen/golang/gen.go @@ -41,6 +41,7 @@ type tmplCtx struct { UsesBatch bool OmitSqlcVersion bool BuildTags string + EmitSchemaName bool } func (t *tmplCtx) OutputQuery(sourceName string) bool { @@ -60,6 +61,10 @@ func (t *tmplCtx) codegenEmitPreparedQueries() bool { return t.EmitPreparedQueries } +func (t *tmplCtx) codegenEmitSchemaName() bool { + return t.EmitSchemaName +} + func (t *tmplCtx) codegenQueryMethod(q Query) string { db := "q.db" if t.EmitMethodsWithDBArgument { @@ -187,6 +192,7 @@ func generate(req *plugin.GenerateRequest, options *opts.Options, enums []Enum, SqlcVersion: req.SqlcVersion, BuildTags: options.BuildTags, OmitSqlcVersion: options.OmitSqlcVersion, + EmitSchemaName: options.EmitSchemaName, } if tctx.UsesCopyFrom && !tctx.SQLDriver.IsPGX() && options.SqlDriver != opts.SQLDriverGoSQLDriverMySQL { @@ -218,6 +224,7 @@ func generate(req *plugin.GenerateRequest, options *opts.Options, enums []Enum, "emitPreparedQueries": tctx.codegenEmitPreparedQueries, "queryMethod": tctx.codegenQueryMethod, "queryRetval": tctx.codegenQueryRetval, + "emitSchemaName": tctx.codegenEmitSchemaName, } tmpl := template.Must( diff --git a/internal/codegen/golang/query.go b/internal/codegen/golang/query.go index 76c7ff9e69..3b4fb2fa1a 100644 --- a/internal/codegen/golang/query.go +++ b/internal/codegen/golang/query.go @@ -266,8 +266,7 @@ type Query struct { Ret QueryValue Arg QueryValue // Used for :copyfrom - Table *plugin.Identifier - EmitSchema bool + Table *plugin.Identifier } func (q Query) hasRetType() bool { diff --git a/internal/codegen/golang/result.go b/internal/codegen/golang/result.go index 2aed272e66..d6388f383c 100644 --- a/internal/codegen/golang/result.go +++ b/internal/codegen/golang/result.go @@ -227,7 +227,6 @@ func buildQueries(req *plugin.GenerateRequest, options *opts.Options, structs [] SQL: query.Text, Comments: comments, Table: query.InsertIntoTable, - EmitSchema: options.EmitSchemaName, } sqlpkg := parseDriver(options.SqlPackage) diff --git a/internal/codegen/golang/templates/stdlib/queryCode.tmpl b/internal/codegen/golang/templates/stdlib/queryCode.tmpl index 9d1ff189c0..d712cbf918 100644 --- a/internal/codegen/golang/templates/stdlib/queryCode.tmpl +++ b/internal/codegen/golang/templates/stdlib/queryCode.tmpl @@ -22,12 +22,20 @@ type {{.Ret.Type}} struct { {{- range .Ret.Struct.Fields}} {{if eq .Cmd ":one"}} {{range .Comments}}//{{.}} {{end -}} -func (q *Queries) {{.MethodName}}(ctx context.Context, schema string, {{ dbarg }} {{.Arg.Pair}}) error { +{{- if emitSchemaName }} +func (q *Queries) {{.MethodName}}(ctx context.Context, schema string, {{ dbarg }} {{.Arg.Pair}}) ({{.Ret.DefineType}}, error) { +{{- else }} +func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}}) error { +{{- end }} {{- template "queryCodeStdExec" . }} {{- if or (ne .Arg.Pair .Ret.Pair) (ne .Arg.DefineType .Ret.DefineType) }} var {{.Ret.Name}} {{.Ret.Type}} {{- end}} - err := row.Scan({{.Ret.Scan}}) + {{- if emitSchemaName }} + err := row.Scan(strings.ReplaceAll({{.Ret.Scan}}, "%s", schema)) + {{- else }} + err := row.Scan({{.Ret.Scan}}) + {{- end }} return {{.Ret.ReturnName}}, err } {{end}} @@ -35,7 +43,11 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, schema string, {{ dbarg } {{if eq .Cmd ":many"}} {{range .Comments}}//{{.}} {{end -}} -func (q *Queries) {{.MethodName}}(ctx context.Context, schema string, {{ dbarg }} {{.Arg.Pair}}) ([]{{.Ret.DefineType}}, error) { +{{- if emitSchemaName }} +func (q *Queries) {{.MethodName}}(ctx context.Context, schema string, {{ dbarg }} {{.Arg.Pair}}) ([]{{.Ret.DefineType}}, error) { +{{- else }} +func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}}) ([]{{.Ret.DefineType}}, error) { +{{- end }} {{- template "queryCodeStdExec" . }} if err != nil { return nil, err @@ -66,7 +78,11 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, schema string, {{ dbarg } {{if eq .Cmd ":exec"}} {{range .Comments}}//{{.}} {{end -}} +{{- if emitSchemaName }} func (q *Queries) {{.MethodName}}(ctx context.Context, schema string, {{ dbarg }} {{.Arg.Pair}}) error { +{{- else }} +func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}}) error { +{{- end }} {{- template "queryCodeStdExec" . }} return err } @@ -75,7 +91,11 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, schema string, {{ dbarg } {{if eq .Cmd ":execrows"}} {{range .Comments}}//{{.}} {{end -}} +{{- if emitSchemaName }} func (q *Queries) {{.MethodName}}(ctx context.Context, schema string, {{ dbarg }} {{.Arg.Pair}}) (int64, error) { +{{- else }} +func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}}) (int64, error) { +{{- end }} {{- template "queryCodeStdExec" . }} if err != nil { return 0, err @@ -87,7 +107,11 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, schema string, {{ dbarg } {{if eq .Cmd ":execlastid"}} {{range .Comments}}//{{.}} {{end -}} +{{- if emitSchemaName }} func (q *Queries) {{.MethodName}}(ctx context.Context, schema string, {{ dbarg }} {{.Arg.Pair}}) (int64, error) { +{{- else }} +func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}}) (int64, error) { +{{- end }} {{- template "queryCodeStdExec" . }} if err != nil { return 0, err @@ -99,15 +123,13 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, schema string, {{ dbarg } {{if eq .Cmd ":execresult"}} {{range .Comments}}//{{.}} {{end -}} -{{- if .EmitSchema }} +{{- if emitSchemaName }} func (q *Queries) {{.MethodName}}(ctx context.Context, schema string, {{ dbarg }} {{.Arg.Pair}}) (sql.Result, error) { - {{- template "queryCodeStdExec" . -}} -} -{{- else }} +{{else }} func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}}) (sql.Result, error) { +{{- end }} {{- template "queryCodeStdExec" . }} } -{{- end }} {{end}} {{end}} @@ -149,21 +171,21 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}} } {{- end }} {{- if emitPreparedQueries }} - {{ queryRetval . }} {{ queryMethod . }}(ctx, nil, query, queryParams...) + {{- if emitSchemaName }} + {{ queryRetval . }} {{ queryMethod . }}(ctx, nil, strings.ReplaceAll(query, "%s", schema), queryParams...) + {{- else }} + {{ queryRetval . }} {{ queryMethod . }}(ctx, nil, query, queryParams...) + {{- end }} {{- else}} - {{ queryRetval . }} {{ queryMethod . }}(ctx, query, queryParams...) + {{- if emitSchemaName }} + {{ queryRetval . }} {{ queryMethod . }}(ctx, strings.ReplaceAll(query, "%s", schema), queryParams...) + {{- else }} + {{ queryRetval . }} {{ queryMethod . }}(ctx, query, queryParams...) + {{- end -}} {{- end -}} {{- else if emitPreparedQueries }} - {{- if .EmitSchema }} - {{- queryRetval . }} {{ queryMethod . }}(ctx, q.{{.FieldName}}, strings.ReplaceAll({{.ConstantName}}, "%s", schema), {{.Arg.Params}}) - {{- else }} - {{- queryRetval . }} {{ queryMethod . }}(ctx, q.{{.FieldName}}, {{.ConstantName}}, {{.Arg.Params}}) - {{- end }} + {{- queryRetval . }} {{ queryMethod . }}(ctx, q.{{.FieldName}}, {{.ConstantName}}, {{.Arg.Params}}) {{- else}} - {{- if .EmitSchema }} - {{- queryRetval . }} {{ queryMethod . }}(ctx, q.{{.FieldName}}, strings.ReplaceAll({{.ConstantName}}, "%s", schema), {{.Arg.Params}}) - {{- else }} - {{- queryRetval . }} {{ queryMethod . }}(ctx, {{.ConstantName}}, {{.Arg.Params}}) - {{- end }} + {{- queryRetval . }} {{ queryMethod . }}(ctx, {{.ConstantName}}, {{.Arg.Params}}) {{- end -}} {{end}} diff --git a/internal/codegen/golang/templates/template.tmpl b/internal/codegen/golang/templates/template.tmpl index 4be94b9b04..665e1f9cda 100644 --- a/internal/codegen/golang/templates/template.tmpl +++ b/internal/codegen/golang/templates/template.tmpl @@ -180,7 +180,9 @@ import ( {{end}} {{end}} - "fmt" + {{- if emitSchemaName }} + "strings" + {{- end }} ) {{end}} From 1c903402c8b58f2a3193e3262273e5637beadab5 Mon Sep 17 00:00:00 2001 From: Nicholas Santi Date: Tue, 23 Jul 2024 10:13:33 +0100 Subject: [PATCH 3/8] Fix replaceall --- internal/codegen/golang/templates/stdlib/queryCode.tmpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/codegen/golang/templates/stdlib/queryCode.tmpl b/internal/codegen/golang/templates/stdlib/queryCode.tmpl index d712cbf918..9a4ea576d5 100644 --- a/internal/codegen/golang/templates/stdlib/queryCode.tmpl +++ b/internal/codegen/golang/templates/stdlib/queryCode.tmpl @@ -165,9 +165,9 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}} for _, v := range {{.Arg.Name}} { queryParams = append(queryParams, v) } - query = strings.Replace(query, "/*SLICE:{{.Arg.Column.Name}}*/?", strings.Repeat(",?", len({{.Arg.Name}}))[1:], 1) + query = strings.ReplaceAll(query, "/*SLICE:{{.Arg.Column.Name}}*/?", strings.Repeat(",?", len({{.Arg.Name}}))[1:]) } else { - query = strings.Replace(query, "/*SLICE:{{.Arg.Column.Name}}*/?", "NULL", 1) + query = strings.ReplaceAll(query, "/*SLICE:{{.Arg.Column.Name}}*/?", "NULL") } {{- end }} {{- if emitPreparedQueries }} From 4a03413681329bb72592a1c9ec3227e58ee9be1b Mon Sep 17 00:00:00 2001 From: Nicholas Santi Date: Tue, 23 Jul 2024 10:31:45 +0100 Subject: [PATCH 4/8] Fix schema in query context --- internal/codegen/golang/templates/stdlib/queryCode.tmpl | 6 +++++- internal/codegen/golang/templates/template.tmpl | 3 --- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/internal/codegen/golang/templates/stdlib/queryCode.tmpl b/internal/codegen/golang/templates/stdlib/queryCode.tmpl index 9a4ea576d5..8c622d0f27 100644 --- a/internal/codegen/golang/templates/stdlib/queryCode.tmpl +++ b/internal/codegen/golang/templates/stdlib/queryCode.tmpl @@ -138,7 +138,7 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}} {{define "queryCodeStdExec"}} {{- if .Arg.HasSqlcSlices }} - query := {{.ConstantName}} + query := strings.ReplaceAll({{.ConstantName}}, "%s", schema) var queryParams []interface{} {{- if .Arg.Struct }} {{- $arg := .Arg }} @@ -186,6 +186,10 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}} {{- else if emitPreparedQueries }} {{- queryRetval . }} {{ queryMethod . }}(ctx, q.{{.FieldName}}, {{.ConstantName}}, {{.Arg.Params}}) {{- else}} + {{- if emitSchemaName }} + {{- queryRetval . }} {{ queryMethod . }}(ctx, strings.ReplaceAll({{.ConstantName}}, "%s", schema), {{.Arg.Params}}) + {{- else }} {{- queryRetval . }} {{ queryMethod . }}(ctx, {{.ConstantName}}, {{.Arg.Params}}) + {{- end -}} {{- end -}} {{end}} diff --git a/internal/codegen/golang/templates/template.tmpl b/internal/codegen/golang/templates/template.tmpl index 665e1f9cda..d4dfcdcbef 100644 --- a/internal/codegen/golang/templates/template.tmpl +++ b/internal/codegen/golang/templates/template.tmpl @@ -180,9 +180,6 @@ import ( {{end}} {{end}} - {{- if emitSchemaName }} - "strings" - {{- end }} ) {{end}} From 5c0beb72527d75fce01b796905a58e0a60c6df32 Mon Sep 17 00:00:00 2001 From: Nicholas Santi Date: Fri, 16 Aug 2024 13:39:47 +0100 Subject: [PATCH 5/8] Added queryparams for many --- .vscode/settings.json | 5 +- Makefile | 2 +- .../golang/templates/stdlib/queryCode.tmpl | 98 ++++++++++++++++++- .../codegen/golang/templates/template.tmpl | 19 ++++ 4 files changed, 118 insertions(+), 6 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index d5d00188a7..cbb1b7459c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,6 @@ { - "files.insertFinalNewline": true + "files.insertFinalNewline": true, + "files.associations": { + "*.tmpl": "html" + } } diff --git a/Makefile b/Makefile index 2e4e47b6d5..92c5bd7942 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ build: go build ./... build-local: - go build -o ../sqlc ./cmd/sqlc + go build -o ../sqlc-tilby ./cmd/sqlc install: go install ./... diff --git a/internal/codegen/golang/templates/stdlib/queryCode.tmpl b/internal/codegen/golang/templates/stdlib/queryCode.tmpl index 8c622d0f27..5e8f9f7e13 100644 --- a/internal/codegen/golang/templates/stdlib/queryCode.tmpl +++ b/internal/codegen/golang/templates/stdlib/queryCode.tmpl @@ -44,7 +44,21 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}} {{range .Comments}}//{{.}} {{end -}} {{- if emitSchemaName }} -func (q *Queries) {{.MethodName}}(ctx context.Context, schema string, {{ dbarg }} {{.Arg.Pair}}) ([]{{.Ret.DefineType}}, error) { + +type {{.MethodName}}Filter struct { + FieldName string + Value string +} + +type {{.MethodName}}FilterParams struct { + ExactParams []{{.MethodName}}Filter + InParams []{{.MethodName}}Filter + LikeParams []{{.MethodName}}Filter + SinceParams []{{.MethodName}}Filter + MaxParams []{{.MethodName}}Filter +} + +func (q *Queries) {{.MethodName}}(ctx context.Context, schema string, filterParams {{.MethodName}}FilterParams, {{ dbarg }} {{.Arg.Pair}}) ([]{{.Ret.DefineType}}, error) { {{- else }} func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}}) ([]{{.Ret.DefineType}}, error) { {{- end }} @@ -140,6 +154,7 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}} {{- if .Arg.HasSqlcSlices }} query := strings.ReplaceAll({{.ConstantName}}, "%s", schema) var queryParams []interface{} + {{- if .Arg.Struct }} {{- $arg := .Arg }} {{- range .Arg.Struct.Fields }} @@ -170,15 +185,18 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}} query = strings.ReplaceAll(query, "/*SLICE:{{.Arg.Column.Name}}*/?", "NULL") } {{- end }} + {{- if emitPreparedQueries }} {{- if emitSchemaName }} - {{ queryRetval . }} {{ queryMethod . }}(ctx, nil, strings.ReplaceAll(query, "%s", schema), queryParams...) + replacedQuery := strings.ReplaceAll(query, "%s", schema) + {{ queryRetval . }} {{ queryMethod . }}(ctx, nil, replacedQuery, queryParams...) {{- else }} {{ queryRetval . }} {{ queryMethod . }}(ctx, nil, query, queryParams...) {{- end }} {{- else}} {{- if emitSchemaName }} - {{ queryRetval . }} {{ queryMethod . }}(ctx, strings.ReplaceAll(query, "%s", schema), queryParams...) + replacedQuery := strings.ReplaceAll(query, "%s", schema) + {{ queryRetval . }} {{ queryMethod . }}(ctx, replacedQuery, queryParams...) {{- else }} {{ queryRetval . }} {{ queryMethod . }}(ctx, query, queryParams...) {{- end -}} @@ -187,7 +205,79 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}} {{- queryRetval . }} {{ queryMethod . }}(ctx, q.{{.FieldName}}, {{.ConstantName}}, {{.Arg.Params}}) {{- else}} {{- if emitSchemaName }} - {{- queryRetval . }} {{ queryMethod . }}(ctx, strings.ReplaceAll({{.ConstantName}}, "%s", schema), {{.Arg.Params}}) + query := strings.ReplaceAll({{.ConstantName}}, "%s", schema); + + {{- /** + If the query has a filter, we need to add the filter to the query + and the query params. + */}} + + isFirstFilter := true; + var queryParams []interface{}; + + for _, filter := range filterParams.ExactParams { + if isFirstFilter { + query += " WHERE " + isFirstFilter = false + } else { + query += " AND " + } + + query += filter.FieldName + " = ?" + queryParams = append(queryParams, filter.Value) + }; + + for _, filter := range filterParams.InParams { + if isFirstFilter { + query += " WHERE " + isFirstFilter = false + } else { + query += " AND " + } + + query += filter.FieldName + " IN (?)" + queryParams = append(queryParams, filter.Value) + }; + + for _, filter := range filterParams.LikeParams { + if isFirstFilter { + query += " WHERE " + isFirstFilter = false + } else { + query += " AND " + } + + query += filter.FieldName + " LIKE ?" + queryParams = append(queryParams, filter.Value) + }; + + for _, filter := range filterParams.SinceParams { + if isFirstFilter { + query += " WHERE " + isFirstFilter = false + } else { + query += " AND " + } + + query += filter.FieldName + " > ?" + queryParams = append(queryParams, filter.Value) + }; + + for _, filter := range filterParams.MaxParams { + if isFirstFilter { + query += " WHERE " + isFirstFilter = false + } else { + query += " AND " + } + + query += filter.FieldName + " < ?" + queryParams = append(queryParams, filter.Value) + }; + + + {{- queryRetval . }} {{ queryMethod . }}(ctx, query, {{.Arg.Params}}) + {{- else }} {{- queryRetval . }} {{ queryMethod . }}(ctx, {{.ConstantName}}, {{.Arg.Params}}) {{- end -}} diff --git a/internal/codegen/golang/templates/template.tmpl b/internal/codegen/golang/templates/template.tmpl index d4dfcdcbef..1712e5ecc8 100644 --- a/internal/codegen/golang/templates/template.tmpl +++ b/internal/codegen/golang/templates/template.tmpl @@ -11,6 +11,9 @@ package {{.Package}} {{ if hasImports .SourceName }} import ( + {{ if .EmitSchemaName }} + {{end}} + {{range imports .SourceName}} {{range .}}{{.}} {{end}} @@ -44,6 +47,9 @@ package {{.Package}} {{ if hasImports .SourceName }} import ( + {{ if .EmitSchemaName }} + {{end}} + {{range imports .SourceName}} {{range .}}{{.}} {{end}} @@ -75,6 +81,9 @@ package {{.Package}} {{ if hasImports .SourceName }} import ( + {{ if .EmitSchemaName }} + {{end}} + {{range imports .SourceName}} {{range .}}{{.}} {{end}} @@ -175,6 +184,10 @@ package {{.Package}} {{ if hasImports .SourceName }} import ( + {{ if .EmitSchemaName }} + "strings" + {{end}} + {{range imports .SourceName}} {{range .}}{{.}} {{end}} @@ -207,6 +220,9 @@ package {{.Package}} {{ if hasImports .SourceName }} import ( + {{ if .EmitSchemaName }} + {{end}} + {{range imports .SourceName}} {{range .}}{{.}} {{end}} @@ -240,6 +256,9 @@ package {{.Package}} {{ if hasImports .SourceName }} import ( + {{ if .EmitSchemaName }} + {{end}} + {{range imports .SourceName}} {{range .}}{{.}} {{end}} From 7f4977513f315a299304359964f4a90e1deaab57 Mon Sep 17 00:00:00 2001 From: Nicholas Santi Date: Mon, 19 Aug 2024 08:50:07 +0100 Subject: [PATCH 6/8] Working filters --- .../golang/templates/stdlib/queryCode.tmpl | 37 ++++++++++++++++++- .../codegen/golang/templates/template.tmpl | 3 ++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/internal/codegen/golang/templates/stdlib/queryCode.tmpl b/internal/codegen/golang/templates/stdlib/queryCode.tmpl index 5e8f9f7e13..39e6f1d1d7 100644 --- a/internal/codegen/golang/templates/stdlib/queryCode.tmpl +++ b/internal/codegen/golang/templates/stdlib/queryCode.tmpl @@ -41,6 +41,8 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}} {{end}} {{if eq .Cmd ":many"}} +var columnRegex = regexp.MustCompile(`SELECT\s+([\w,\s]+)\s+FROM`); + {{range .Comments}}//{{.}} {{end -}} {{- if emitSchemaName }} @@ -58,7 +60,7 @@ type {{.MethodName}}FilterParams struct { MaxParams []{{.MethodName}}Filter } -func (q *Queries) {{.MethodName}}(ctx context.Context, schema string, filterParams {{.MethodName}}FilterParams, {{ dbarg }} {{.Arg.Pair}}) ([]{{.Ret.DefineType}}, error) { +func (q *Queries) {{.MethodName}}(ctx context.Context, schema string, filterParams {{.MethodName}}FilterParams, {{ dbarg }} {{.Arg.Pair}}) ([]{{.Ret.DefineType}}, error) {; {{- else }} func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}}) ([]{{.Ret.DefineType}}, error) { {{- end }} @@ -153,6 +155,10 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}} {{define "queryCodeStdExec"}} {{- if .Arg.HasSqlcSlices }} query := strings.ReplaceAll({{.ConstantName}}, "%s", schema) + + // Extract the columns from the query; + columns := columnRegex.FindString(query); + var queryParams []interface{} {{- if .Arg.Struct }} @@ -207,6 +213,9 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}} {{- if emitSchemaName }} query := strings.ReplaceAll({{.ConstantName}}, "%s", schema); + // Extract the columns from the query; + columns := columnRegex.FindString(query); + {{- /** If the query has a filter, we need to add the filter to the query and the query params. @@ -216,6 +225,11 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}} var queryParams []interface{}; for _, filter := range filterParams.ExactParams { + // Is the filter in the columns? + if !strings.Contains(columns, filter.FieldName) { + continue + } + if isFirstFilter { query += " WHERE " isFirstFilter = false @@ -228,6 +242,11 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}} }; for _, filter := range filterParams.InParams { + // Is the filter in the columns? + if !strings.Contains(columns, filter.FieldName) { + continue + } + if isFirstFilter { query += " WHERE " isFirstFilter = false @@ -240,6 +259,11 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}} }; for _, filter := range filterParams.LikeParams { + // Is the filter in the columns? + if !strings.Contains(columns, filter.FieldName) { + continue + } + if isFirstFilter { query += " WHERE " isFirstFilter = false @@ -252,6 +276,11 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}} }; for _, filter := range filterParams.SinceParams { + // Is the filter in the columns? + if !strings.Contains(columns, filter.FieldName) { + continue + } + if isFirstFilter { query += " WHERE " isFirstFilter = false @@ -264,6 +293,11 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}} }; for _, filter := range filterParams.MaxParams { + // Is the filter in the columns? + if !strings.Contains(columns, filter.FieldName) { + continue + } + if isFirstFilter { query += " WHERE " isFirstFilter = false @@ -274,7 +308,6 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}} query += filter.FieldName + " < ?" queryParams = append(queryParams, filter.Value) }; - {{- queryRetval . }} {{ queryMethod . }}(ctx, query, {{.Arg.Params}}) diff --git a/internal/codegen/golang/templates/template.tmpl b/internal/codegen/golang/templates/template.tmpl index 1712e5ecc8..5ce5f31a6b 100644 --- a/internal/codegen/golang/templates/template.tmpl +++ b/internal/codegen/golang/templates/template.tmpl @@ -186,6 +186,7 @@ package {{.Package}} import ( {{ if .EmitSchemaName }} "strings" + "regexp" {{end}} {{range imports .SourceName}} @@ -221,6 +222,8 @@ package {{.Package}} {{ if hasImports .SourceName }} import ( {{ if .EmitSchemaName }} + "strings" + "regexp" {{end}} {{range imports .SourceName}} From fdd7b3ed16b68da8209748eef6e0b68e11ce7543 Mon Sep 17 00:00:00 2001 From: Nicholas Santi Date: Tue, 20 Aug 2024 09:53:29 +0100 Subject: [PATCH 7/8] TODO: add flag for custom params --- internal/codegen/golang/schema.go | 2 +- .../golang/templates/stdlib/queryCode.tmpl | 31 +++++++++++++++---- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/internal/codegen/golang/schema.go b/internal/codegen/golang/schema.go index cf3a1ce735..d69adaa781 100644 --- a/internal/codegen/golang/schema.go +++ b/internal/codegen/golang/schema.go @@ -46,7 +46,7 @@ func ApplySchema(query string) string { // Helper function to check if a word is a relevant SQL keyword func isSQLKeyword(word string) bool { switch word { - case "FROM", "JOIN", "LEFT JOIN", "RIGHT JOIN", "FULL JOIN", "INNER JOIN", "CROSS JOIN", "UPDATE", "DELETE FROM", "INSERT INTO": + case "FROM", "JOIN", "UPDATE", "INTO": return true } return false diff --git a/internal/codegen/golang/templates/stdlib/queryCode.tmpl b/internal/codegen/golang/templates/stdlib/queryCode.tmpl index 39e6f1d1d7..82bd7e017b 100644 --- a/internal/codegen/golang/templates/stdlib/queryCode.tmpl +++ b/internal/codegen/golang/templates/stdlib/queryCode.tmpl @@ -31,8 +31,8 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}} {{- if or (ne .Arg.Pair .Ret.Pair) (ne .Arg.DefineType .Ret.DefineType) }} var {{.Ret.Name}} {{.Ret.Type}} {{- end}} - {{- if emitSchemaName }} - err := row.Scan(strings.ReplaceAll({{.Ret.Scan}}, "%s", schema)) + {{- if emitSchemaName }} + err := row.Scan({{.Ret.Scan}}) {{- else }} err := row.Scan({{.Ret.Scan}}) {{- end }} @@ -58,6 +58,8 @@ type {{.MethodName}}FilterParams struct { LikeParams []{{.MethodName}}Filter SinceParams []{{.MethodName}}Filter MaxParams []{{.MethodName}}Filter + SortParam string + SortOrder string } func (q *Queries) {{.MethodName}}(ctx context.Context, schema string, filterParams {{.MethodName}}FilterParams, {{ dbarg }} {{.Arg.Pair}}) ([]{{.Ret.DefineType}}, error) {; @@ -213,14 +215,16 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}} {{- if emitSchemaName }} query := strings.ReplaceAll({{.ConstantName}}, "%s", schema); - // Extract the columns from the query; - columns := columnRegex.FindString(query); - {{- /** If the query has a filter, we need to add the filter to the query and the query params. */}} + {{- if eq .Cmd ":many"}} + + // Extract the columns from the query; + columns := columnRegex.FindString(query); + isFirstFilter := true; var queryParams []interface{}; @@ -309,10 +313,25 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}} queryParams = append(queryParams, filter.Value) }; - {{- queryRetval . }} {{ queryMethod . }}(ctx, query, {{.Arg.Params}}) + if filterParams.SortParam != "" { + query += " ORDER BY " + filterParams.SortParam + " " + filterParams.SortOrder + } + + // If there is not the ; at the end, add it + if !strings.HasSuffix(query, ";") { + query += ";" + }; + + {{- queryRetval . }} {{ queryMethod . }}(ctx, query, queryParams...) + + {{- end }} {{- else }} {{- queryRetval . }} {{ queryMethod . }}(ctx, {{.ConstantName}}, {{.Arg.Params}}) {{- end -}} + + {{- if ne .Cmd ":many"}} + {{- queryRetval . }} {{ queryMethod . }}(ctx, query, {{.Arg.Params}}) + {{- end -}} {{- end -}} {{end}} From 011aa0568f7bca1b62a629a0187ab6d655df6231 Mon Sep 17 00:00:00 2001 From: Nicholas Santi Date: Tue, 20 Aug 2024 10:11:46 +0100 Subject: [PATCH 8/8] Added pagination --- internal/codegen/golang/templates/stdlib/queryCode.tmpl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/internal/codegen/golang/templates/stdlib/queryCode.tmpl b/internal/codegen/golang/templates/stdlib/queryCode.tmpl index 82bd7e017b..66efae4f3d 100644 --- a/internal/codegen/golang/templates/stdlib/queryCode.tmpl +++ b/internal/codegen/golang/templates/stdlib/queryCode.tmpl @@ -60,6 +60,9 @@ type {{.MethodName}}FilterParams struct { MaxParams []{{.MethodName}}Filter SortParam string SortOrder string + Pagination bool + PerPage int + PageNumber int } func (q *Queries) {{.MethodName}}(ctx context.Context, schema string, filterParams {{.MethodName}}FilterParams, {{ dbarg }} {{.Arg.Pair}}) ([]{{.Ret.DefineType}}, error) {; @@ -317,6 +320,10 @@ func (q *Queries) {{.MethodName}}(ctx context.Context, {{ dbarg }} {{.Arg.Pair}} query += " ORDER BY " + filterParams.SortParam + " " + filterParams.SortOrder } + if filterParams.Pagination { + query += " LIMIT " + fmt.Sprint(filterParams.PerPage) + " OFFSET " + fmt.Sprint(filterParams.PageNumber * filterParams.PerPage) + } + // If there is not the ; at the end, add it if !strings.HasSuffix(query, ";") { query += ";"