diff --git a/docs/reference/config.md b/docs/reference/config.md index c88d41d963..4eb9ccf4e5 100644 --- a/docs/reference/config.md +++ b/docs/reference/config.md @@ -42,6 +42,13 @@ Each mapping in the `sql` collection has the following keys: - Directory of SQL migrations or path to single SQL file; or a list of paths. - `queries`: - Directory of SQL queries or path to single SQL file; or a list of paths. +- `query_comments`: + - If enabled, prepend generated SQL text with a structured block comment using query metadata. + Built-in generators include the comment in generated query strings. Supported formats are + `sqlcommenter` and `marginalia`. Supported tags are `name`, `cmd`, and `filename`. + Defaults to `format: sqlcommenter` and `tags: ["name"]`. These comments make generated + queries easier to trace in database logs, APM tools, database monitoring products, and + distributed tracing systems. - `codegen`: - A collection of mappings to configure code generators. See [codegen](#codegen) for the supported keys. - `gen`: @@ -116,6 +123,45 @@ sql: out: postgresql ``` +### query_comments + +The `query_comments` mapping supports the following keys: + +- `enabled`: + - If true, prepend generated SQL query text with a structured comment derived from query metadata. + Defaults to `false`. +- `format`: + - Either `sqlcommenter` or `marginalia`. Defaults to `sqlcommenter`. +- `tags`: + - Query metadata tags to include in the comment. Supported values are `name`, `cmd`, and + `filename`. Defaults to `["name"]`. + +```yaml +version: '2' +sql: +- schema: schema.sql + queries: query.sql + engine: postgresql + query_comments: + enabled: true + format: sqlcommenter + tags: + - name + - cmd + - filename + gen: + go: + package: authors + out: postgresql +``` + +Query comments are useful when you need to connect an expensive query sample, slow query, +or database log entry back to the generated query that produced it. Tools and ecosystems +that understand or generate these SQL comment formats include Datadog Database Monitoring, +OpenTelemetry sqlcommenter libraries, Prisma ORM sql comments, and Rails applications using +Marginalia. Other observability products that consume OpenTelemetry data or preserve SQL +comments in query logs can also use this metadata for correlation. + ### analyzer The `analyzer` mapping supports the following keys: diff --git a/internal/cmd/generate.go b/internal/cmd/generate.go index 6d2a0297b6..61e3702554 100644 --- a/internal/cmd/generate.go +++ b/internal/cmd/generate.go @@ -339,6 +339,7 @@ func codegen(ctx context.Context, combo config.CombinedSettings, sql OutputPair, case sql.Gen.Go != nil: out = combo.Go.Out + applyQueryComments(req, combo.Package.QueryComments) handler = ext.HandleFunc(golang.Generate) opts, err := json.Marshal(sql.Gen.Go) if err != nil { @@ -356,6 +357,7 @@ func codegen(ctx context.Context, combo config.CombinedSettings, sql OutputPair, case sql.Gen.JSON != nil: out = combo.JSON.Out + applyQueryComments(req, combo.Package.QueryComments) handler = ext.HandleFunc(genjson.Generate) opts, err := json.Marshal(sql.Gen.JSON) if err != nil { diff --git a/internal/cmd/query_comments.go b/internal/cmd/query_comments.go new file mode 100644 index 0000000000..0cab7bc7c9 --- /dev/null +++ b/internal/cmd/query_comments.go @@ -0,0 +1,77 @@ +package cmd + +import ( + "strings" + + "github.com/sqlc-dev/sqlc/internal/config" + "github.com/sqlc-dev/sqlc/internal/plugin" +) + +const ( + queryCommentFormatMarginalia = "marginalia" + queryCommentFormatSQLCommenter = "sqlcommenter" +) + +func applyQueryComments(req *plugin.GenerateRequest, opts config.QueryComments) { + if !opts.Enabled { + return + } + for _, query := range req.Queries { + if query.Text == "" { + continue + } + comment := queryComment(query, opts) + if comment == "" { + continue + } + query.Text = comment + " " + query.Text + } +} + +func queryComment(query *plugin.Query, opts config.QueryComments) string { + tags := opts.Tags + if len(tags) == 0 { + tags = []string{"name"} + } + + parts := make([]string, 0, len(tags)) + for _, tag := range tags { + value := queryCommentValue(query, tag) + if value == "" { + continue + } + key := "sqlc_" + tag + if opts.Format == queryCommentFormatMarginalia { + parts = append(parts, key+":"+escapeQueryCommentValue(value)) + } else { + parts = append(parts, key+"='"+escapeQueryCommentValue(value)+"'") + } + } + if len(parts) == 0 { + return "" + } + return "/*" + strings.Join(parts, ",") + "*/" +} + +func queryCommentValue(query *plugin.Query, tag string) string { + switch tag { + case "name": + return query.Name + case "cmd": + return query.Cmd + case "filename": + return query.Filename + default: + return "" + } +} + +func escapeQueryCommentValue(value string) string { + value = strings.ReplaceAll(value, "*/", "* /") + value = strings.ReplaceAll(value, "\n", " ") + value = strings.ReplaceAll(value, "\r", " ") + value = strings.ReplaceAll(value, "'", "%27") + value = strings.ReplaceAll(value, ",", "%2C") + value = strings.ReplaceAll(value, ":", "%3A") + return value +} diff --git a/internal/cmd/query_comments_test.go b/internal/cmd/query_comments_test.go new file mode 100644 index 0000000000..809f4ecb88 --- /dev/null +++ b/internal/cmd/query_comments_test.go @@ -0,0 +1,62 @@ +package cmd + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + + "github.com/sqlc-dev/sqlc/internal/config" + "github.com/sqlc-dev/sqlc/internal/plugin" +) + +func TestApplyQueryComments(t *testing.T) { + req := &plugin.GenerateRequest{ + Queries: []*plugin.Query{ + { + Name: "GetAuthor", + Cmd: ":one", + Filename: "query.sql", + Text: "SELECT * FROM authors WHERE id = $1", + }, + }, + } + + applyQueryComments(req, config.QueryComments{ + Enabled: true, + Tags: []string{"name", "cmd", "filename"}, + }) + + want := "/*sqlc_name='GetAuthor',sqlc_cmd='%3Aone',sqlc_filename='query.sql'*/ SELECT * FROM authors WHERE id = $1" + if diff := cmp.Diff(want, req.Queries[0].Text); diff != "" { + t.Errorf("query text differed (-want +got):\n%s", diff) + } +} + +func TestApplyQueryCommentsMarginalia(t *testing.T) { + req := &plugin.GenerateRequest{ + Queries: []*plugin.Query{ + { + Name: "GetAuthor", + Text: "SELECT * FROM authors WHERE id = $1", + }, + }, + } + + applyQueryComments(req, config.QueryComments{ + Enabled: true, + Format: "marginalia", + }) + + want := "/*sqlc_name:GetAuthor*/ SELECT * FROM authors WHERE id = $1" + if diff := cmp.Diff(want, req.Queries[0].Text); diff != "" { + t.Errorf("query text differed (-want +got):\n%s", diff) + } +} + +func TestEscapeQueryCommentValue(t *testing.T) { + got := escapeQueryCommentValue("a'b,c:d\n*/") + want := "a%27b%2Cc%3Ad * /" + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("escaped value differed (-want +got):\n%s", diff) + } +} diff --git a/internal/codegen/golang/query.go b/internal/codegen/golang/query.go index 27c596c24e..4a3a2eb9df 100644 --- a/internal/codegen/golang/query.go +++ b/internal/codegen/golang/query.go @@ -273,6 +273,7 @@ type Query struct { MethodName string FieldName string ConstantName string + SQLComment string SQL string SourceName string Ret QueryValue diff --git a/internal/codegen/golang/result.go b/internal/codegen/golang/result.go index 5bfa7f795e..b9d68c13c6 100644 --- a/internal/codegen/golang/result.go +++ b/internal/codegen/golang/result.go @@ -217,13 +217,16 @@ func buildQueries(req *plugin.GenerateRequest, options *opts.Options, enums []En } } + sqlComment, sql := splitSQLComment(query.Text) + gq := Query{ Cmd: query.Cmd, ConstantName: constantName, FieldName: sdk.LowerTitle(query.Name) + "Stmt", MethodName: query.Name, SourceName: query.Filename, - SQL: query.Text, + SQLComment: sqlComment, + SQL: sql, Comments: comments, Table: query.InsertIntoTable, } @@ -354,6 +357,17 @@ func buildQueries(req *plugin.GenerateRequest, options *opts.Options, enums []En return qs, nil } +func splitSQLComment(sql string) (string, string) { + if !strings.HasPrefix(sql, "/*sqlc_") { + return "", sql + } + idx := strings.Index(sql, "*/") + if idx == -1 { + return "", sql + } + return sql[:idx+2], strings.TrimLeft(sql[idx+2:], " \t\r\n") +} + var cmdReturnsData = map[string]struct{}{ metadata.CmdBatchMany: {}, metadata.CmdBatchOne: {}, diff --git a/internal/codegen/golang/result_test.go b/internal/codegen/golang/result_test.go index 0c58525ec3..cbee77b818 100644 --- a/internal/codegen/golang/result_test.go +++ b/internal/codegen/golang/result_test.go @@ -3,6 +3,8 @@ package golang import ( "testing" + "github.com/google/go-cmp/cmp" + "github.com/sqlc-dev/sqlc/internal/metadata" "github.com/sqlc-dev/sqlc/internal/plugin" ) @@ -76,3 +78,23 @@ func TestPutOutColumns_AlwaysTrueWhenQueryHasColumns(t *testing.T) { t.Error("should be true when we have columns") } } + +func TestSplitSQLComment(t *testing.T) { + comment, sql := splitSQLComment("/*sqlc_name='GetAuthor'*/ SELECT 1") + if diff := cmp.Diff("/*sqlc_name='GetAuthor'*/", comment); diff != "" { + t.Errorf("comment differed (-want +got):\n%s", diff) + } + if diff := cmp.Diff("SELECT 1", sql); diff != "" { + t.Errorf("sql differed (-want +got):\n%s", diff) + } +} + +func TestSplitSQLCommentIgnoresOtherComments(t *testing.T) { + comment, sql := splitSQLComment("/*application='api'*/ SELECT 1") + if comment != "" { + t.Errorf("expected empty comment, got %q", comment) + } + if diff := cmp.Diff("/*application='api'*/ SELECT 1", sql); diff != "" { + t.Errorf("sql differed (-want +got):\n%s", diff) + } +} diff --git a/internal/codegen/golang/templates/pgx/batchCode.tmpl b/internal/codegen/golang/templates/pgx/batchCode.tmpl index 35bd701bd3..78347891af 100644 --- a/internal/codegen/golang/templates/pgx/batchCode.tmpl +++ b/internal/codegen/golang/templates/pgx/batchCode.tmpl @@ -6,7 +6,8 @@ var ( {{range .GoQueries}} {{if eq (hasPrefix .Cmd ":batch") true }} -const {{.ConstantName}} = {{$.Q}}-- name: {{.MethodName}} {{.Cmd}} +const {{.ConstantName}} = {{$.Q}}{{if .SQLComment}}{{.SQLComment}} +{{end}}-- name: {{.MethodName}} {{.Cmd}} {{escape .SQL}} {{$.Q}} diff --git a/internal/codegen/golang/templates/pgx/queryCode.tmpl b/internal/codegen/golang/templates/pgx/queryCode.tmpl index 59a88c880a..06b568a0a7 100644 --- a/internal/codegen/golang/templates/pgx/queryCode.tmpl +++ b/internal/codegen/golang/templates/pgx/queryCode.tmpl @@ -2,7 +2,8 @@ {{range .GoQueries}} {{if $.OutputQuery .SourceName}} {{if and (ne .Cmd ":copyfrom") (ne (hasPrefix .Cmd ":batch") true)}} -const {{.ConstantName}} = {{$.Q}}-- name: {{.MethodName}} {{.Cmd}} +const {{.ConstantName}} = {{$.Q}}{{if .SQLComment}}{{.SQLComment}} +{{end}}-- name: {{.MethodName}} {{.Cmd}} {{escape .SQL}} {{$.Q}} {{end}} diff --git a/internal/codegen/golang/templates/stdlib/queryCode.tmpl b/internal/codegen/golang/templates/stdlib/queryCode.tmpl index 1e7f4e22a4..7a68f1c1ba 100644 --- a/internal/codegen/golang/templates/stdlib/queryCode.tmpl +++ b/internal/codegen/golang/templates/stdlib/queryCode.tmpl @@ -1,7 +1,8 @@ {{define "queryCodeStd"}} {{range .GoQueries}} {{if $.OutputQuery .SourceName}} -const {{.ConstantName}} = {{$.Q}}-- name: {{.MethodName}} {{.Cmd}} +const {{.ConstantName}} = {{$.Q}}{{if .SQLComment}}{{.SQLComment}} +{{end}}-- name: {{.MethodName}} {{.Cmd}} {{escape .SQL}} {{$.Q}} diff --git a/internal/config/config.go b/internal/config/config.go index d3e610ef05..3d02f0be2f 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -109,24 +109,31 @@ type Overrides struct { } type SQL struct { - Name string `json:"name" yaml:"name"` - Engine Engine `json:"engine,omitempty" yaml:"engine"` - Schema Paths `json:"schema" yaml:"schema"` - Queries Paths `json:"queries" yaml:"queries"` - Database *Database `json:"database" yaml:"database"` - StrictFunctionChecks bool `json:"strict_function_checks" yaml:"strict_function_checks"` - StrictOrderBy *bool `json:"strict_order_by" yaml:"strict_order_by"` - Gen SQLGen `json:"gen" yaml:"gen"` - Codegen []Codegen `json:"codegen" yaml:"codegen"` - Rules []string `json:"rules" yaml:"rules"` - Analyzer Analyzer `json:"analyzer" yaml:"analyzer"` + Name string `json:"name" yaml:"name"` + Engine Engine `json:"engine,omitempty" yaml:"engine"` + Schema Paths `json:"schema" yaml:"schema"` + Queries Paths `json:"queries" yaml:"queries"` + QueryComments QueryComments `json:"query_comments" yaml:"query_comments"` + Database *Database `json:"database" yaml:"database"` + StrictFunctionChecks bool `json:"strict_function_checks" yaml:"strict_function_checks"` + StrictOrderBy *bool `json:"strict_order_by" yaml:"strict_order_by"` + Gen SQLGen `json:"gen" yaml:"gen"` + Codegen []Codegen `json:"codegen" yaml:"codegen"` + Rules []string `json:"rules" yaml:"rules"` + Analyzer Analyzer `json:"analyzer" yaml:"analyzer"` +} + +type QueryComments struct { + Enabled bool `json:"enabled" yaml:"enabled"` + Format string `json:"format,omitempty" yaml:"format"` + Tags []string `json:"tags,omitempty" yaml:"tags"` } // AnalyzerDatabase represents the database analyzer setting. // It can be a boolean (true/false) or the string "only" for database-only mode. type AnalyzerDatabase struct { - value *bool // nil means not set, true/false for boolean values - isOnly bool // true when set to "only" + value *bool // nil means not set, true/false for boolean values + isOnly bool // true when set to "only" } // IsEnabled returns true if the database analyzer should be used. @@ -228,6 +235,9 @@ var ErrPluginNoType = errors.New("plugin: field `process` or `wasm` required") var ErrPluginBothTypes = errors.New("plugin: `process` and `wasm` cannot both be defined") var ErrPluginProcessNoCmd = errors.New("plugin: missing process command") +var ErrInvalidQueryCommentFormat = errors.New("invalid query_comments format") +var ErrInvalidQueryCommentTag = errors.New("invalid query_comments tag") + var ErrInvalidDatabase = errors.New("database must be managed or have a non-empty URI") var ErrManagedDatabaseNoProject = errors.New(`managed databases require a cloud project diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 57211d674c..41639f5ec8 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -89,3 +89,85 @@ func TestInvalidConfig(t *testing.T) { t.Errorf("expected err; got nil") } } + +func TestQueryCommentsConfig(t *testing.T) { + const raw = `{ + "version": "2", + "sql": [ + { + "schema": "schema.sql", + "queries": "query.sql", + "engine": "postgresql", + "query_comments": { + "enabled": true, + "format": "marginalia", + "tags": ["name", "cmd", "filename"] + }, + "gen": { + "go": { + "out": "db" + } + } + } + ] +}` + conf, err := ParseConfig(strings.NewReader(raw)) + if err != nil { + t.Fatal(err) + } + got := conf.SQL[0].QueryComments + want := QueryComments{ + Enabled: true, + Format: "marginalia", + Tags: []string{"name", "cmd", "filename"}, + } + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("query comments differed (-want +got):\n%s", diff) + } +} + +func TestInvalidQueryCommentsConfig(t *testing.T) { + for _, test := range []struct { + name string + body string + err string + }{ + { + name: "invalid format", + body: `"query_comments": {"enabled": true, "format": "unknown"}`, + err: "invalid query_comments format: unknown", + }, + { + name: "invalid tag", + body: `"query_comments": {"enabled": true, "tags": ["name", "database"]}`, + err: "invalid query_comments tag: database", + }, + } { + tt := test + t.Run(tt.name, func(t *testing.T) { + raw := `{ + "version": "2", + "sql": [ + { + "schema": "schema.sql", + "queries": "query.sql", + "engine": "postgresql", + ` + tt.body + `, + "gen": { + "go": { + "out": "db" + } + } + } + ] +}` + _, err := ParseConfig(strings.NewReader(raw)) + if err == nil { + t.Fatalf("expected err; got nil") + } + if diff := cmp.Diff(tt.err, err.Error()); diff != "" { + t.Errorf("error differed (-want +got):\n%s", diff) + } + }) + } +} diff --git a/internal/config/v_two.go b/internal/config/v_two.go index 0fe22ffa2c..3816468ff4 100644 --- a/internal/config/v_two.go +++ b/internal/config/v_two.go @@ -59,6 +59,9 @@ func v2ParseConfig(rd io.Reader) (Config, error) { if conf.SQL[j].Engine == "" { return conf, ErrMissingEngine } + if err := conf.SQL[j].QueryComments.Validate(); err != nil { + return conf, err + } if conf.SQL[j].Gen.Go != nil { if conf.SQL[j].Gen.Go.Out == "" { return conf, ErrNoPackagePath @@ -107,3 +110,22 @@ func (c *Config) validateGlobalOverrides() error { } return nil } + +func (q QueryComments) Validate() error { + if !q.Enabled { + return nil + } + switch q.Format { + case "", "sqlcommenter", "marginalia": + default: + return fmt.Errorf("%w: %s", ErrInvalidQueryCommentFormat, q.Format) + } + for _, tag := range q.Tags { + switch tag { + case "name", "cmd", "filename": + default: + return fmt.Errorf("%w: %s", ErrInvalidQueryCommentTag, tag) + } + } + return nil +} diff --git a/internal/endtoend/testdata/codegen_json/gen/codegen.json b/internal/endtoend/testdata/codegen_json/gen/codegen.json index 4fb849e730..6e25144415 100644 --- a/internal/endtoend/testdata/codegen_json/gen/codegen.json +++ b/internal/endtoend/testdata/codegen_json/gen/codegen.json @@ -64963,7 +64963,7 @@ }, "queries": [ { - "text": "SELECT id, name, bio FROM authors\nWHERE id = $1 LIMIT 1", + "text": "/*sqlc_name:GetAuthor*/ SELECT id, name, bio FROM authors\nWHERE id = $1 LIMIT 1", "name": "GetAuthor", "cmd": ":one", "columns": [ @@ -65082,7 +65082,7 @@ "insert_into_table": null }, { - "text": "SELECT id, name, bio FROM authors\nORDER BY name", + "text": "/*sqlc_name:ListAuthors*/ SELECT id, name, bio FROM authors\nORDER BY name", "name": "ListAuthors", "cmd": ":many", "columns": [ @@ -65171,7 +65171,7 @@ "insert_into_table": null }, { - "text": "INSERT INTO authors (\n name, bio\n) VALUES (\n $1, $2\n)\nRETURNING id, name, bio", + "text": "/*sqlc_name:CreateAuthor*/ INSERT INTO authors (\n name, bio\n) VALUES (\n $1, $2\n)\nRETURNING id, name, bio", "name": "CreateAuthor", "cmd": ":one", "columns": [ @@ -65323,7 +65323,7 @@ } }, { - "text": "DELETE FROM authors\nWHERE id = $1", + "text": "/*sqlc_name:DeleteAuthor*/ DELETE FROM authors\nWHERE id = $1", "name": "DeleteAuthor", "cmd": ":exec", "columns": [], diff --git a/internal/endtoend/testdata/codegen_json/sqlc.json b/internal/endtoend/testdata/codegen_json/sqlc.json index cbbf97440c..278b11d4eb 100644 --- a/internal/endtoend/testdata/codegen_json/sqlc.json +++ b/internal/endtoend/testdata/codegen_json/sqlc.json @@ -5,6 +5,10 @@ "schema": "postgresql/schema.sql", "queries": "postgresql/query.sql", "engine": "postgresql", + "query_comments": { + "enabled": true, + "format": "marginalia" + }, "gen": { "json": { "out": "gen", diff --git a/internal/endtoend/testdata/query_comments/pgxv4/db.go b/internal/endtoend/testdata/query_comments/pgxv4/db.go new file mode 100644 index 0000000000..9da6d9a49f --- /dev/null +++ b/internal/endtoend/testdata/query_comments/pgxv4/db.go @@ -0,0 +1,32 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 + +package querytest + +import ( + "context" + + "github.com/jackc/pgconn" + "github.com/jackc/pgx/v4" +) + +type DBTX interface { + Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) + Query(context.Context, string, ...interface{}) (pgx.Rows, error) + QueryRow(context.Context, string, ...interface{}) pgx.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx pgx.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/query_comments/pgxv4/models.go b/internal/endtoend/testdata/query_comments/pgxv4/models.go new file mode 100644 index 0000000000..b334d0651a --- /dev/null +++ b/internal/endtoend/testdata/query_comments/pgxv4/models.go @@ -0,0 +1,10 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 + +package querytest + +type Author struct { + ID int64 + Name string +} diff --git a/internal/endtoend/testdata/query_comments/pgxv4/query.sql.go b/internal/endtoend/testdata/query_comments/pgxv4/query.sql.go new file mode 100644 index 0000000000..3c3cec6dba --- /dev/null +++ b/internal/endtoend/testdata/query_comments/pgxv4/query.sql.go @@ -0,0 +1,47 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 +// source: query.sql + +package querytest + +import ( + "context" +) + +const getAuthor = `/*sqlc_name='GetAuthor',sqlc_cmd='%3Aone',sqlc_filename='query.sql'*/ +-- name: GetAuthor :one +SELECT id, name FROM authors WHERE id = $1 +` + +func (q *Queries) GetAuthor(ctx context.Context, id int64) (Author, error) { + row := q.db.QueryRow(ctx, getAuthor, id) + var i Author + err := row.Scan(&i.ID, &i.Name) + return i, err +} + +const listAuthors = `/*sqlc_name='ListAuthors',sqlc_cmd='%3Amany',sqlc_filename='query.sql'*/ +-- name: ListAuthors :many +SELECT id, name FROM authors ORDER BY name +` + +func (q *Queries) ListAuthors(ctx context.Context) ([]Author, error) { + rows, err := q.db.Query(ctx, listAuthors) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Author + for rows.Next() { + var i Author + if err := rows.Scan(&i.ID, &i.Name); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/endtoend/testdata/query_comments/pgxv5/db.go b/internal/endtoend/testdata/query_comments/pgxv5/db.go new file mode 100644 index 0000000000..0057c62319 --- /dev/null +++ b/internal/endtoend/testdata/query_comments/pgxv5/db.go @@ -0,0 +1,32 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 + +package querytest + +import ( + "context" + + "github.com/jackc/pgx/v5" + "github.com/jackc/pgx/v5/pgconn" +) + +type DBTX interface { + Exec(context.Context, string, ...interface{}) (pgconn.CommandTag, error) + Query(context.Context, string, ...interface{}) (pgx.Rows, error) + QueryRow(context.Context, string, ...interface{}) pgx.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx pgx.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/query_comments/pgxv5/models.go b/internal/endtoend/testdata/query_comments/pgxv5/models.go new file mode 100644 index 0000000000..b334d0651a --- /dev/null +++ b/internal/endtoend/testdata/query_comments/pgxv5/models.go @@ -0,0 +1,10 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 + +package querytest + +type Author struct { + ID int64 + Name string +} diff --git a/internal/endtoend/testdata/query_comments/pgxv5/query.sql.go b/internal/endtoend/testdata/query_comments/pgxv5/query.sql.go new file mode 100644 index 0000000000..f888864f9f --- /dev/null +++ b/internal/endtoend/testdata/query_comments/pgxv5/query.sql.go @@ -0,0 +1,47 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 +// source: query.sql + +package querytest + +import ( + "context" +) + +const getAuthor = `/*sqlc_name:GetAuthor,sqlc_cmd:%3Aone,sqlc_filename:query.sql*/ +-- name: GetAuthor :one +SELECT id, name FROM authors WHERE id = $1 +` + +func (q *Queries) GetAuthor(ctx context.Context, id int64) (Author, error) { + row := q.db.QueryRow(ctx, getAuthor, id) + var i Author + err := row.Scan(&i.ID, &i.Name) + return i, err +} + +const listAuthors = `/*sqlc_name:ListAuthors,sqlc_cmd:%3Amany,sqlc_filename:query.sql*/ +-- name: ListAuthors :many +SELECT id, name FROM authors ORDER BY name +` + +func (q *Queries) ListAuthors(ctx context.Context) ([]Author, error) { + rows, err := q.db.Query(ctx, listAuthors) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Author + for rows.Next() { + var i Author + if err := rows.Scan(&i.ID, &i.Name); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/internal/endtoend/testdata/query_comments/query.sql b/internal/endtoend/testdata/query_comments/query.sql new file mode 100644 index 0000000000..427d5b3772 --- /dev/null +++ b/internal/endtoend/testdata/query_comments/query.sql @@ -0,0 +1,5 @@ +-- name: GetAuthor :one +SELECT id, name FROM authors WHERE id = $1; + +-- name: ListAuthors :many +SELECT id, name FROM authors ORDER BY name; diff --git a/internal/endtoend/testdata/query_comments/schema.sql b/internal/endtoend/testdata/query_comments/schema.sql new file mode 100644 index 0000000000..cea3bb1092 --- /dev/null +++ b/internal/endtoend/testdata/query_comments/schema.sql @@ -0,0 +1,4 @@ +CREATE TABLE authors ( + id BIGSERIAL PRIMARY KEY, + name TEXT NOT NULL +); diff --git a/internal/endtoend/testdata/query_comments/sqlc.json b/internal/endtoend/testdata/query_comments/sqlc.json new file mode 100644 index 0000000000..4e01e182e2 --- /dev/null +++ b/internal/endtoend/testdata/query_comments/sqlc.json @@ -0,0 +1,55 @@ +{ + "version": "2", + "sql": [ + { + "schema": "schema.sql", + "queries": "query.sql", + "engine": "postgresql", + "query_comments": { + "enabled": true + }, + "gen": { + "go": { + "out": "stdlib", + "package": "querytest", + "sql_package": "database/sql", + "emit_sql_as_comment": true + } + } + }, + { + "schema": "schema.sql", + "queries": "query.sql", + "engine": "postgresql", + "query_comments": { + "enabled": true, + "format": "sqlcommenter", + "tags": ["name", "cmd", "filename"] + }, + "gen": { + "go": { + "out": "pgxv4", + "package": "querytest", + "sql_package": "pgx/v4" + } + } + }, + { + "schema": "schema.sql", + "queries": "query.sql", + "engine": "postgresql", + "query_comments": { + "enabled": true, + "format": "marginalia", + "tags": ["name", "cmd", "filename"] + }, + "gen": { + "go": { + "out": "pgxv5", + "package": "querytest", + "sql_package": "pgx/v5" + } + } + } + ] +} diff --git a/internal/endtoend/testdata/query_comments/stdlib/db.go b/internal/endtoend/testdata/query_comments/stdlib/db.go new file mode 100644 index 0000000000..80dd6ab1f6 --- /dev/null +++ b/internal/endtoend/testdata/query_comments/stdlib/db.go @@ -0,0 +1,31 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 + +package querytest + +import ( + "context" + "database/sql" +) + +type DBTX interface { + ExecContext(context.Context, string, ...interface{}) (sql.Result, error) + PrepareContext(context.Context, string) (*sql.Stmt, error) + QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error) + QueryRowContext(context.Context, string, ...interface{}) *sql.Row +} + +func New(db DBTX) *Queries { + return &Queries{db: db} +} + +type Queries struct { + db DBTX +} + +func (q *Queries) WithTx(tx *sql.Tx) *Queries { + return &Queries{ + db: tx, + } +} diff --git a/internal/endtoend/testdata/query_comments/stdlib/models.go b/internal/endtoend/testdata/query_comments/stdlib/models.go new file mode 100644 index 0000000000..b334d0651a --- /dev/null +++ b/internal/endtoend/testdata/query_comments/stdlib/models.go @@ -0,0 +1,10 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 + +package querytest + +type Author struct { + ID int64 + Name string +} diff --git a/internal/endtoend/testdata/query_comments/stdlib/query.sql.go b/internal/endtoend/testdata/query_comments/stdlib/query.sql.go new file mode 100644 index 0000000000..161e337a8f --- /dev/null +++ b/internal/endtoend/testdata/query_comments/stdlib/query.sql.go @@ -0,0 +1,56 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.31.1 +// source: query.sql + +package querytest + +import ( + "context" +) + +const getAuthor = `/*sqlc_name='GetAuthor'*/ +-- name: GetAuthor :one +SELECT id, name FROM authors WHERE id = $1 +` + +// GetAuthor +// +// /*sqlc_name='GetAuthor'*/ SELECT id, name FROM authors WHERE id = $1 +func (q *Queries) GetAuthor(ctx context.Context, id int64) (Author, error) { + row := q.db.QueryRowContext(ctx, getAuthor, id) + var i Author + err := row.Scan(&i.ID, &i.Name) + return i, err +} + +const listAuthors = `/*sqlc_name='ListAuthors'*/ +-- name: ListAuthors :many +SELECT id, name FROM authors ORDER BY name +` + +// ListAuthors +// +// /*sqlc_name='ListAuthors'*/ SELECT id, name FROM authors ORDER BY name +func (q *Queries) ListAuthors(ctx context.Context) ([]Author, error) { + rows, err := q.db.QueryContext(ctx, listAuthors) + if err != nil { + return nil, err + } + defer rows.Close() + var items []Author + for rows.Next() { + var i Author + if err := rows.Scan(&i.ID, &i.Name); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Close(); err != nil { + return nil, err + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +}