diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7595757ddd..b15a5ea75b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,7 +12,7 @@ jobs: - uses: actions/checkout@v6 - uses: actions/setup-go@v6 with: - go-version: '1.25.0' + go-version: '1.26.0' - name: install ./... run: go build ./... env: diff --git a/.github/workflows/ci-kotlin.yml b/.github/workflows/ci-kotlin.yml index b011cb997f..a324917ed7 100644 --- a/.github/workflows/ci-kotlin.yml +++ b/.github/workflows/ci-kotlin.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v6 - uses: actions/setup-go@v6 with: - go-version: '1.24.1' + go-version: '1.26.0' - name: install ./... run: go install ./... - uses: actions/checkout@v6 diff --git a/.github/workflows/ci-python.yml b/.github/workflows/ci-python.yml index 940a5008b0..a59bd402c3 100644 --- a/.github/workflows/ci-python.yml +++ b/.github/workflows/ci-python.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v6 - uses: actions/setup-go@v6 with: - go-version: '1.24.1' + go-version: '1.26.0' - name: install ./... run: go install ./... - uses: actions/checkout@v6 diff --git a/.github/workflows/ci-typescript.yml b/.github/workflows/ci-typescript.yml index d08c7ba8f0..7ec747a91f 100644 --- a/.github/workflows/ci-typescript.yml +++ b/.github/workflows/ci-typescript.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v6 - uses: actions/setup-go@v6 with: - go-version: '1.24.1' + go-version: '1.26.0' - name: install ./... run: go install ./... - uses: actions/checkout@v6 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5959992750..e2324c05f7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: - uses: actions/checkout@v6 - uses: actions/setup-go@v6 with: - go-version: '1.25.0' + go-version: '1.26.0' - run: go build ./... env: CGO_ENABLED: "0" @@ -28,7 +28,7 @@ jobs: - uses: actions/checkout@v6 - uses: actions/setup-go@v6 with: - go-version: '1.25.0' + go-version: '1.26.0' - name: install gotestsum run: go install gotest.tools/gotestsum@latest diff --git a/.gitignore b/.gitignore index 39961ebb02..c030d23e2a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/sqlc /.idea/ __pycache__ .DS_Store diff --git a/CLAUDE.md b/CLAUDE.md index 43abb0d491..e9d0da109f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,7 +6,7 @@ This document provides essential information for working with the sqlc codebase, ### Prerequisites -- **Go 1.25.0+** - Required for building and testing +- **Go 1.26.0+** - Required for building and testing - **Docker & Docker Compose** - Required for integration tests with databases (local development) - **Git** - For version control @@ -14,9 +14,13 @@ This document provides essential information for working with the sqlc codebase, When running in the Claude Code remote environment (or any environment without Docker), you can install PostgreSQL and MySQL natively. The test framework automatically detects and uses native database installations. +### Network Proxy (Pre-configured) + +The Claude Code remote environment routes outbound traffic through an HTTP proxy via the `HTTP_PROXY` and `HTTPS_PROXY` environment variables. **Go module operations (`go mod tidy`, `go mod download`, `go get`, etc.) work automatically** because Go's toolchain respects these variables. No extra configuration is needed for the Go module proxy (`proxy.golang.org`) or checksum database (`sum.golang.org`). + ### Step 1: Configure apt Proxy (Required in Remote Environment) -The Claude Code remote environment requires an HTTP proxy for apt. Configure it: +The apt package manager needs its own proxy configuration since it does not read `HTTP_PROXY`: ```bash bash -c 'echo "Acquire::http::Proxy \"$http_proxy\";"' | sudo tee /etc/apt/apt.conf.d/99proxy @@ -306,9 +310,13 @@ POSTGRESQL_SERVER_URI="postgres://postgres:mysecretpassword@localhost:5432/postg ## Common Issues & Solutions -### Network Connectivity Issues +### Network Connectivity / Go Module Proxy Issues + +In the Claude Code remote environment, Go module fetching works automatically via the `HTTP_PROXY`/`HTTPS_PROXY` environment variables. If you see errors about `storage.googleapis.com` or `proxy.golang.org`: -If you see errors about `storage.googleapis.com`, the Go proxy may be unreachable. Tests may still pass for packages that don't require network dependencies. +1. **Verify proxy vars are set:** `echo $HTTP_PROXY` (should be non-empty in the remote environment) +2. **Test connectivity:** `go mod download 2>&1 | head` to check for errors +3. **If proxy is not set** (e.g., local development without Docker), Go will try to reach `proxy.golang.org` directly, which requires internet access ### Test Timeouts diff --git a/Dockerfile b/Dockerfile index 05a93abf7e..1c78fb107e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # STEP 1: Build sqlc -FROM golang:1.25.5 AS builder +FROM golang:1.26.0 AS builder COPY . /workspace WORKDIR /workspace diff --git a/go.mod b/go.mod index f3c92bfd78..f27750e6fb 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/sqlc-dev/sqlc -go 1.24.7 +go 1.26.0 require ( github.com/antlr4-go/antlr/v4 v4.13.1 diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go index 80a167353e..b7c82f58da 100644 --- a/internal/cmd/cmd.go +++ b/internal/cmd/cmd.go @@ -100,7 +100,7 @@ var initCmd = &cobra.Command{ if err != nil { return err } - var yamlConfig interface{} + var yamlConfig any if useV1 { yamlConfig = config.V1GenerateSettings{Version: "1"} } else { diff --git a/internal/cmd/createdb.go b/internal/cmd/createdb.go index 02b3031352..09536816d3 100644 --- a/internal/cmd/createdb.go +++ b/internal/cmd/createdb.go @@ -47,7 +47,6 @@ func CreateDB(ctx context.Context, dir, filename, querySetName string, o *Option var queryset *config.SQL var count int for _, sql := range conf.SQL { - sql := sql if querySetName != "" && sql.Name != querySetName { continue } diff --git a/internal/codegen/golang/field.go b/internal/codegen/golang/field.go index 2a63b6d342..90ea1a7529 100644 --- a/internal/codegen/golang/field.go +++ b/internal/codegen/golang/field.go @@ -97,23 +97,23 @@ func toPascalCase(s string) string { } func toCamelInitCase(name string, initUpper bool) string { - out := "" + var out strings.Builder for i, p := range strings.Split(name, "_") { if !initUpper && i == 0 { - out += p + out.WriteString(p) continue } if p == "id" { - out += "ID" + out.WriteString("ID") } else { - out += strings.Title(p) + out.WriteString(strings.Title(p)) } } - return out + return out.String() } func toJsonCamelCase(name string, idUppercase bool) string { - out := "" + var out strings.Builder idStr := "Id" if idUppercase { @@ -122,16 +122,16 @@ func toJsonCamelCase(name string, idUppercase bool) string { for i, p := range strings.Split(name, "_") { if i == 0 { - out += p + out.WriteString(p) continue } if p == "id" { - out += idStr + out.WriteString(idStr) } else { - out += strings.Title(p) + out.WriteString(strings.Title(p)) } } - return out + return out.String() } func toLowerCase(str string) string { diff --git a/internal/codegen/golang/gen.go b/internal/codegen/golang/gen.go index 7df56a0a41..9c29b9da9e 100644 --- a/internal/codegen/golang/gen.go +++ b/internal/codegen/golang/gen.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "go/format" + "slices" "strings" "text/template" @@ -344,10 +345,8 @@ func usesCopyFrom(queries []Query) bool { func usesBatch(queries []Query) bool { for _, q := range queries { - for _, cmd := range []string{metadata.CmdBatchExec, metadata.CmdBatchMany, metadata.CmdBatchOne} { - if q.Cmd == cmd { - return true - } + if slices.Contains([]string{metadata.CmdBatchExec, metadata.CmdBatchMany, metadata.CmdBatchOne}, q.Cmd) { + return true } } return false diff --git a/internal/codegen/golang/go_type.go b/internal/codegen/golang/go_type.go index c4aac84dd6..21914736d0 100644 --- a/internal/codegen/golang/go_type.go +++ b/internal/codegen/golang/go_type.go @@ -1,6 +1,7 @@ package golang import ( + "maps" "strings" "github.com/sqlc-dev/sqlc/internal/codegen/golang/opts" @@ -15,9 +16,7 @@ func addExtraGoStructTags(tags map[string]string, req *plugin.GenerateRequest, o continue } if override.MatchesColumn(col) { - for k, v := range oride.GoType.StructTags { - tags[k] = v - } + maps.Copy(tags, oride.GoType.StructTags) continue } if !override.Matches(col.Table, req.Catalog.DefaultSchema) { @@ -33,9 +32,7 @@ func addExtraGoStructTags(tags map[string]string, req *plugin.GenerateRequest, o continue } // Add the extra tags. - for k, v := range oride.GoType.StructTags { - tags[k] = v - } + maps.Copy(tags, oride.GoType.StructTags) } } diff --git a/internal/codegen/golang/opts/go_type.go b/internal/codegen/golang/opts/go_type.go index 5dcb3e8af0..723594cffb 100644 --- a/internal/codegen/golang/opts/go_type.go +++ b/internal/codegen/golang/opts/go_type.go @@ -51,7 +51,7 @@ func (o *GoType) UnmarshalJSON(data []byte) error { return nil } -func (o *GoType) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (o *GoType) UnmarshalYAML(unmarshal func(any) error) error { var spec string if err := unmarshal(&spec); err == nil { *o = GoType{Spec: spec} diff --git a/internal/codegen/golang/result.go b/internal/codegen/golang/result.go index 0820488f9d..fa48e27e93 100644 --- a/internal/codegen/golang/result.go +++ b/internal/codegen/golang/result.go @@ -168,17 +168,17 @@ func paramName(p *plugin.Parameter) string { } func argName(name string) string { - out := "" + var out strings.Builder for i, p := range strings.Split(name, "_") { if i == 0 { - out += strings.ToLower(p) + out.WriteString(strings.ToLower(p)) } else if p == "id" { - out += "ID" + out.WriteString("ID") } else { - out += strings.Title(p) + out.WriteString(strings.Title(p)) } } - return out + return out.String() } func buildQueries(req *plugin.GenerateRequest, options *opts.Options, structs []Struct) ([]Query, error) { diff --git a/internal/codegen/golang/struct.go b/internal/codegen/golang/struct.go index ed9311800e..f52eb4947d 100644 --- a/internal/codegen/golang/struct.go +++ b/internal/codegen/golang/struct.go @@ -31,7 +31,7 @@ func StructName(name string, options *opts.Options) string { return rune('_') }, name) - for _, p := range strings.Split(name, "_") { + for p := range strings.SplitSeq(name, "_") { if _, found := options.InitialismsMap[p]; found { out += strings.ToUpper(p) } else { diff --git a/internal/compiler/output_columns.go b/internal/compiler/output_columns.go index dbd486359a..6cc2567ebe 100644 --- a/internal/compiler/output_columns.go +++ b/internal/compiler/output_columns.go @@ -517,7 +517,6 @@ func (c *Compiler) sourceTables(qc *QueryCatalog, node ast.Node) ([]*Table, erro var tables []*Table for _, item := range list.Items { - item := item switch n := item.(type) { case *ast.RangeFunction: diff --git a/internal/config/config.go b/internal/config/config.go index d3e610ef05..ff7faedcaa 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -36,7 +36,7 @@ func (p *Paths) UnmarshalJSON(data []byte) error { return nil } -func (p *Paths) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (p *Paths) UnmarshalYAML(unmarshal func(any) error) error { out := []string{} if sliceErr := unmarshal(&out); sliceErr != nil { var ele string @@ -61,7 +61,7 @@ type Config struct { Cloud Cloud `json:"cloud" yaml:"cloud"` Servers []Server `json:"servers" yaml:"servers"` SQL []SQL `json:"sql" yaml:"sql"` - Overrides Overrides `json:"overrides,omitempty" yaml:"overrides"` + Overrides Overrides `json:"overrides" yaml:"overrides"` Plugins []Plugin `json:"plugins" yaml:"plugins"` Rules []Rule `json:"rules" yaml:"rules"` Options map[string]yaml.Node `json:"options" yaml:"options"` @@ -125,8 +125,8 @@ type SQL struct { // 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. @@ -166,7 +166,7 @@ func (a *AnalyzerDatabase) UnmarshalJSON(data []byte) error { return errors.New("analyzer.database must be true, false, or \"only\"") } -func (a *AnalyzerDatabase) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (a *AnalyzerDatabase) UnmarshalYAML(unmarshal func(any) error) error { // Try to unmarshal as boolean first var b bool if err := unmarshal(&b); err == nil { diff --git a/internal/config/convert/convert.go b/internal/config/convert/convert.go index 2bd0550886..a1fd2881ce 100644 --- a/internal/config/convert/convert.go +++ b/internal/config/convert/convert.go @@ -8,11 +8,11 @@ import ( "gopkg.in/yaml.v3" ) -func gen(n *yaml.Node) (interface{}, error) { +func gen(n *yaml.Node) (any, error) { switch n.Kind { case yaml.MappingNode: - nn := map[string]interface{}{} + nn := map[string]any{} for i, _ := range n.Content { if i%2 == 0 { k := n.Content[i] @@ -26,7 +26,7 @@ func gen(n *yaml.Node) (interface{}, error) { return nn, nil case yaml.SequenceNode: - nn := []interface{}{} + nn := []any{} for i, _ := range n.Content { v, err := gen(n.Content[i]) if err != nil { diff --git a/internal/dbmanager/client.go b/internal/dbmanager/client.go index 18aec947cb..2c49433aef 100644 --- a/internal/dbmanager/client.go +++ b/internal/dbmanager/client.go @@ -90,7 +90,7 @@ func (m *ManagedClient) CreateDatabase(ctx context.Context, req *CreateDatabaseR uri.Path = "/" + name key := uri.String() - _, err, _ = flight.Do(key, func() (interface{}, error) { + _, err, _ = flight.Do(key, func() (any, error) { // TODO: Use a parameterized query row := pool.QueryRow(ctx, fmt.Sprintf(`SELECT datname FROM pg_database WHERE datname = '%s'`, name)) diff --git a/internal/debug/dump.go b/internal/debug/dump.go index 6921ecb67f..9756328556 100644 --- a/internal/debug/dump.go +++ b/internal/debug/dump.go @@ -20,7 +20,7 @@ func init() { } } -func Dump(n ...interface{}) { +func Dump(n ...any) { if Active { spew.Dump(n) } diff --git a/internal/endtoend/ddl_test.go b/internal/endtoend/ddl_test.go index bed9333743..36b0aaa131 100644 --- a/internal/endtoend/ddl_test.go +++ b/internal/endtoend/ddl_test.go @@ -12,7 +12,6 @@ import ( func TestValidSchema(t *testing.T) { for _, replay := range FindTests(t, "testdata", "base") { - replay := replay // https://golang.org/doc/faq#closures_and_goroutines if replay.Exec != nil { if replay.Exec.Meta.InvalidSchema { @@ -32,7 +31,6 @@ func TestValidSchema(t *testing.T) { } for j, pkg := range conf.SQL { - j, pkg := j, pkg switch pkg.Engine { case config.EnginePostgreSQL: // pass diff --git a/internal/endtoend/endtoend_test.go b/internal/endtoend/endtoend_test.go index 7634918446..648bb03255 100644 --- a/internal/endtoend/endtoend_test.go +++ b/internal/endtoend/endtoend_test.go @@ -221,8 +221,6 @@ func TestReplay(t *testing.T) { } for name, testctx := range contexts { - name := name - testctx := testctx if !testctx.Enabled() { continue @@ -354,7 +352,6 @@ func cmpDirectory(t *testing.T, dir string, actual map[string]string) { if !cmp.Equal(expected, actual, opts...) { t.Errorf("%s contents differ", dir) for name, contents := range expected { - name := name if actual[name] == "" { t.Errorf("%s is empty", name) return diff --git a/internal/endtoend/fmt_test.go b/internal/endtoend/fmt_test.go index eac3fa0390..886ab419e8 100644 --- a/internal/endtoend/fmt_test.go +++ b/internal/endtoend/fmt_test.go @@ -31,7 +31,6 @@ type sqlFormatter interface { func TestFormat(t *testing.T) { t.Parallel() for _, tc := range FindTests(t, "testdata", "base") { - tc := tc t.Run(tc.Name, func(t *testing.T) { // Parse the config file to determine the engine configPath := filepath.Join(tc.Path, tc.ConfigName) @@ -145,7 +144,6 @@ func TestFormat(t *testing.T) { } for i, stmt := range stmts { - stmt := stmt t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { // Extract the original query text using statement location and length start := stmt.Raw.StmtLocation diff --git a/internal/engine/postgresql/catalog.go b/internal/engine/postgresql/catalog.go index 3c262122a2..e11fe21222 100644 --- a/internal/engine/postgresql/catalog.go +++ b/internal/engine/postgresql/catalog.go @@ -4,8 +4,10 @@ import "github.com/sqlc-dev/sqlc/internal/sql/catalog" // toPointer converts an int to a pointer without a temporary // variable at the call-site, and is used by the generated schemas +// +//go:fix inline func toPointer(x int) *int { - return &x + return new(x) } func NewCatalog() *catalog.Catalog { diff --git a/internal/engine/sqlite/analyzer/analyze.go b/internal/engine/sqlite/analyzer/analyze.go index 3af9f99a30..c9e6a167be 100644 --- a/internal/engine/sqlite/analyzer/analyze.go +++ b/internal/engine/sqlite/analyzer/analyze.go @@ -87,7 +87,7 @@ func (a *Analyzer) Analyze(ctx context.Context, n ast.Node, query string, migrat // Get column information colCount := stmt.ColumnCount() - for i := 0; i < colCount; i++ { + for i := range colCount { name := stmt.ColumnName(i) declType := stmt.ColumnDeclType(i) tableName := stmt.ColumnTableName(i) @@ -249,7 +249,7 @@ func (a *Analyzer) GetColumnNames(ctx context.Context, query string) ([]string, colCount := stmt.ColumnCount() columns := make([]string, colCount) - for i := 0; i < colCount; i++ { + for i := range colCount { columns[i] = stmt.ColumnName(i) } diff --git a/internal/engine/sqlite/parse.go b/internal/engine/sqlite/parse.go index 13425b156e..5de4c3a69e 100644 --- a/internal/engine/sqlite/parse.go +++ b/internal/engine/sqlite/parse.go @@ -17,7 +17,7 @@ type errorListener struct { err string } -func (el *errorListener) SyntaxError(recognizer antlr.Recognizer, offendingSymbol interface{}, line, column int, msg string, e antlr.RecognitionException) { +func (el *errorListener) SyntaxError(recognizer antlr.Recognizer, offendingSymbol any, line, column int, msg string, e antlr.RecognitionException) { el.err = msg } diff --git a/internal/ext/wasm/wasm.go b/internal/ext/wasm/wasm.go index 58384a9c95..7653676493 100644 --- a/internal/ext/wasm/wasm.go +++ b/internal/ext/wasm/wasm.go @@ -59,7 +59,7 @@ func (r *Runner) loadAndCompile(ctx context.Context) (*runtimeAndCode, error) { if err != nil { return nil, err } - value, err, _ := flight.Do(expected, func() (interface{}, error) { + value, err, _ := flight.Do(expected, func() (any, error) { return r.loadAndCompileWASM(ctx, cacheDir, expected) }) if err != nil { diff --git a/internal/metadata/meta.go b/internal/metadata/meta.go index 8f63624d2c..76ee992a7a 100644 --- a/internal/metadata/meta.go +++ b/internal/metadata/meta.go @@ -59,7 +59,7 @@ func validateQueryName(name string) error { } func ParseQueryNameAndType(t string, commentStyle CommentSyntax) (string, string, error) { - for _, line := range strings.Split(t, "\n") { + for line := range strings.SplitSeq(t, "\n") { var prefix string if strings.HasPrefix(line, "--") { if !commentStyle.Dash { diff --git a/internal/opts/debug.go b/internal/opts/debug.go index b92cbd4ae8..96f2fb06a7 100644 --- a/internal/opts/debug.go +++ b/internal/opts/debug.go @@ -39,7 +39,7 @@ func DebugFromString(val string) Debug { if val == "" { return d } - for _, pair := range strings.Split(val, ",") { + for pair := range strings.SplitSeq(val, ",") { pair = strings.TrimSpace(pair) switch { case pair == "dumpast=1": diff --git a/internal/opts/experiment.go b/internal/opts/experiment.go index 45a1c11e05..e37c35db60 100644 --- a/internal/opts/experiment.go +++ b/internal/opts/experiment.go @@ -47,7 +47,7 @@ func ExperimentFromString(val string) Experiment { return e } - for _, name := range strings.Split(val, ",") { + for name := range strings.SplitSeq(val, ",") { name = strings.TrimSpace(name) if name == "" { continue diff --git a/internal/pattern/match.go b/internal/pattern/match.go index 1cf8afb1e4..c72663d463 100644 --- a/internal/pattern/match.go +++ b/internal/pattern/match.go @@ -3,6 +3,7 @@ package pattern import ( "fmt" "regexp" + "strings" "sync" ) @@ -42,16 +43,16 @@ func MatchCompile(pattern string) (*Match, error) { } func matchCompile(pattern string) (match *Match, err error) { - regex := "" + var regex strings.Builder escaped := false arr := []byte(pattern) - for i := 0; i < len(arr); i++ { + for i := range arr { if escaped { escaped = false switch arr[i] { case '*', '?', '\\': - regex += "\\" + string(arr[i]) + regex.WriteString("\\" + string(arr[i])) default: return nil, fmt.Errorf("Invalid escaped character '%c'", arr[i]) } @@ -60,13 +61,13 @@ func matchCompile(pattern string) (match *Match, err error) { case '\\': escaped = true case '*': - regex += ".*" + regex.WriteString(".*") case '?': - regex += "." + regex.WriteString(".") case '.', '(', ')', '+', '|', '^', '$', '[', ']', '{', '}': - regex += "\\" + string(arr[i]) + regex.WriteString("\\" + string(arr[i])) default: - regex += string(arr[i]) + regex.WriteString(string(arr[i])) } } } @@ -77,7 +78,7 @@ func matchCompile(pattern string) (match *Match, err error) { var r *regexp.Regexp - if r, err = regexp.Compile("^" + regex + "$"); err != nil { + if r, err = regexp.Compile("^" + regex.String() + "$"); err != nil { return nil, err } diff --git a/internal/rpc/interceptor.go b/internal/rpc/interceptor.go index ac0490bd1a..6d8dc798b1 100644 --- a/internal/rpc/interceptor.go +++ b/internal/rpc/interceptor.go @@ -8,7 +8,7 @@ import ( "google.golang.org/grpc/status" ) -func UnaryInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { +func UnaryInterceptor(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error { err := invoker(ctx, method, req, reply, cc, opts...) switch status.Convert(err).Code() { diff --git a/internal/source/code.go b/internal/source/code.go index 8b88a24136..e7ef0f5880 100644 --- a/internal/source/code.go +++ b/internal/source/code.go @@ -110,8 +110,8 @@ func StripComments(sql string) (string, []string, error) { if strings.HasPrefix(t, "# name:") { continue } - if strings.HasPrefix(t, "--") { - comments = append(comments, strings.TrimPrefix(t, "--")) + if after, ok := strings.CutPrefix(t, "--"); ok { + comments = append(comments, after) continue } if strings.HasPrefix(t, "/*") && strings.HasSuffix(t, "*/") { @@ -120,8 +120,8 @@ func StripComments(sql string) (string, []string, error) { comments = append(comments, t) continue } - if strings.HasPrefix(t, "#") { - comments = append(comments, strings.TrimPrefix(t, "#")) + if after, ok := strings.CutPrefix(t, "#"); ok { + comments = append(comments, after) continue } lines = append(lines, t) diff --git a/internal/sql/ast/param_exec_data.go b/internal/sql/ast/param_exec_data.go index 83e9b04f9a..0d8c3db9bf 100644 --- a/internal/sql/ast/param_exec_data.go +++ b/internal/sql/ast/param_exec_data.go @@ -1,7 +1,7 @@ package ast type ParamExecData struct { - ExecPlan interface{} + ExecPlan any Value Datum Isnull bool } diff --git a/internal/sql/ast/param_list_info_data.go b/internal/sql/ast/param_list_info_data.go index 1275124244..a3fa0796e2 100644 --- a/internal/sql/ast/param_list_info_data.go +++ b/internal/sql/ast/param_list_info_data.go @@ -1,8 +1,8 @@ package ast type ParamListInfoData struct { - ParamFetchArg interface{} - ParserSetupArg interface{} + ParamFetchArg any + ParserSetupArg any NumParams int ParamMask []uint32 } diff --git a/internal/sql/astutils/rewrite.go b/internal/sql/astutils/rewrite.go index fc7996b5f5..25b274a16c 100644 --- a/internal/sql/astutils/rewrite.go +++ b/internal/sql/astutils/rewrite.go @@ -120,7 +120,7 @@ type application struct { func (a *application) apply(parent ast.Node, name string, iter *iterator, n ast.Node) { // convert typed nil into untyped nil - if v := reflect.ValueOf(n); v.Kind() == reflect.Ptr && v.IsNil() { + if v := reflect.ValueOf(n); v.Kind() == reflect.Pointer && v.IsNil() { n = nil } diff --git a/internal/sqltest/docker/enabled.go b/internal/sqltest/docker/enabled.go index e17c0201b2..3196d0ea97 100644 --- a/internal/sqltest/docker/enabled.go +++ b/internal/sqltest/docker/enabled.go @@ -3,6 +3,7 @@ package docker import ( "fmt" "os/exec" + "strings" "golang.org/x/sync/singleflight" ) @@ -15,3 +16,44 @@ func Installed() error { } return nil } + +// ensureContainer makes sure a Docker container with the given name is running. +// It handles three cases: +// 1. Container doesn't exist: create it with docker run +// 2. Container exists but stopped: start it with docker start +// 3. Container exists and running: do nothing +// +// It also handles the race condition where a parallel test process creates the +// container between our inspect and run calls. +func ensureContainer(name string, runArgs ...string) error { + // Check if container exists and whether it's running + output, err := exec.Command("docker", "container", "inspect", + "-f", "{{.State.Running}}", name).CombinedOutput() + + if err == nil { + // Container exists — check if it's running + if strings.TrimSpace(string(output)) != "true" { + // Container exists but is stopped, start it + if startOut, startErr := exec.Command("docker", "start", name).CombinedOutput(); startErr != nil { + return fmt.Errorf("docker start %s: %s: %w", name, string(startOut), startErr) + } + } + return nil + } + + // Container doesn't exist, create it + args := append([]string{"run", "--name", name}, runArgs...) + createOut, createErr := exec.Command("docker", args...).CombinedOutput() + + if createErr != nil { + // Handle race: another process may have created the container between + // our inspect and run calls + conflictMsg := fmt.Sprintf(`Conflict. The container name "/%s" is already in use`, name) + if strings.Contains(string(createOut), conflictMsg) { + return nil + } + return fmt.Errorf("docker run %s: %s: %w", name, string(createOut), createErr) + } + + return nil +} diff --git a/internal/sqltest/docker/mysql.go b/internal/sqltest/docker/mysql.go index 39a1af6160..fecef886e9 100644 --- a/internal/sqltest/docker/mysql.go +++ b/internal/sqltest/docker/mysql.go @@ -5,7 +5,6 @@ import ( "database/sql" "fmt" "os/exec" - "strings" "time" _ "github.com/go-sql-driver/mysql" @@ -20,7 +19,7 @@ func StartMySQLServer(c context.Context) (string, error) { if mysqlHost != "" { return mysqlHost, nil } - value, err, _ := flight.Do("mysql", func() (interface{}, error) { + value, err, _ := flight.Do("mysql", func() (any, error) { host, err := startMySQLServer(c) if err != nil { return "", err @@ -46,30 +45,14 @@ func startMySQLServer(c context.Context) (string, error) { } } - var exists bool - { - cmd := exec.Command("docker", "container", "inspect", "sqlc_sqltest_docker_mysql") - // This means we've already started the container - exists = cmd.Run() == nil - } - - if !exists { - cmd := exec.Command("docker", "run", - "--name", "sqlc_sqltest_docker_mysql", - "-e", "MYSQL_ROOT_PASSWORD=mysecretpassword", - "-e", "MYSQL_DATABASE=dinotest", - "-p", "3306:3306", - "-d", - "mysql:9", - ) - - output, err := cmd.CombinedOutput() - fmt.Println(string(output)) - - msg := `Conflict. The container name "/sqlc_sqltest_docker_mysql" is already in use by container` - if !strings.Contains(string(output), msg) && err != nil { - return "", err - } + if err := ensureContainer("sqlc_sqltest_docker_mysql", + "-e", "MYSQL_ROOT_PASSWORD=mysecretpassword", + "-e", "MYSQL_DATABASE=dinotest", + "-p", "3306:3306", + "-d", + "mysql:9", + ); err != nil { + return "", err } ctx, cancel := context.WithTimeout(c, 10*time.Second) diff --git a/internal/sqltest/docker/postgres.go b/internal/sqltest/docker/postgres.go index 1b2d842c70..15ec860871 100644 --- a/internal/sqltest/docker/postgres.go +++ b/internal/sqltest/docker/postgres.go @@ -5,7 +5,6 @@ import ( "fmt" "log/slog" "os/exec" - "strings" "time" "github.com/jackc/pgx/v5" @@ -20,7 +19,7 @@ func StartPostgreSQLServer(c context.Context) (string, error) { if postgresHost != "" { return postgresHost, nil } - value, err, _ := flight.Do("postgresql", func() (interface{}, error) { + value, err, _ := flight.Do("postgresql", func() (any, error) { host, err := startPostgreSQLServer(c) if err != nil { return "", err @@ -48,31 +47,15 @@ func startPostgreSQLServer(c context.Context) (string, error) { uri := "postgres://postgres:mysecretpassword@localhost:5432/postgres?sslmode=disable" - var exists bool - { - cmd := exec.Command("docker", "container", "inspect", "sqlc_sqltest_docker_postgres") - // This means we've already started the container - exists = cmd.Run() == nil - } - - if !exists { - cmd := exec.Command("docker", "run", - "--name", "sqlc_sqltest_docker_postgres", - "-e", "POSTGRES_PASSWORD=mysecretpassword", - "-e", "POSTGRES_USER=postgres", - "-p", "5432:5432", - "-d", - "postgres:16", - "-c", "max_connections=200", - ) - - output, err := cmd.CombinedOutput() - fmt.Println(string(output)) - - msg := `Conflict. The container name "/sqlc_sqltest_docker_postgres" is already in use by container` - if !strings.Contains(string(output), msg) && err != nil { - return "", err - } + if err := ensureContainer("sqlc_sqltest_docker_postgres", + "-e", "POSTGRES_PASSWORD=mysecretpassword", + "-e", "POSTGRES_USER=postgres", + "-p", "5432:5432", + "-d", + "postgres:16", + "-c", "max_connections=200", + ); err != nil { + return "", err } ctx, cancel := context.WithTimeout(c, 5*time.Second) diff --git a/internal/sqltest/local/postgres.go b/internal/sqltest/local/postgres.go index 243a7133ab..7b5a848dc7 100644 --- a/internal/sqltest/local/postgres.go +++ b/internal/sqltest/local/postgres.go @@ -91,7 +91,7 @@ func postgreSQL(t *testing.T, migrations []string, rw bool) string { key := uri.String() - _, err, _ = flight.Do(key, func() (interface{}, error) { + _, err, _ = flight.Do(key, func() (any, error) { row := postgresPool.QueryRow(ctx, fmt.Sprintf(`SELECT datname FROM pg_database WHERE datname = '%s'`, name)) diff --git a/internal/sqltest/native/mysql.go b/internal/sqltest/native/mysql.go index 69482bace6..924e23b13b 100644 --- a/internal/sqltest/native/mysql.go +++ b/internal/sqltest/native/mysql.go @@ -23,7 +23,7 @@ func StartMySQLServer(ctx context.Context) (string, error) { if mysqlURI != "" { return mysqlURI, nil } - value, err, _ := mysqlFlight.Do("mysql", func() (interface{}, error) { + value, err, _ := mysqlFlight.Do("mysql", func() (any, error) { uri, err := startMySQLServer(ctx) if err != nil { return "", err diff --git a/internal/sqltest/native/postgres.go b/internal/sqltest/native/postgres.go index f805a40a1c..91b7f56afe 100644 --- a/internal/sqltest/native/postgres.go +++ b/internal/sqltest/native/postgres.go @@ -23,7 +23,7 @@ func StartPostgreSQLServer(ctx context.Context) (string, error) { if postgresURI != "" { return postgresURI, nil } - value, err, _ := postgresFlight.Do("postgresql", func() (interface{}, error) { + value, err, _ := postgresFlight.Do("postgresql", func() (any, error) { uri, err := startPostgreSQLServer(ctx) if err != nil { return "", err diff --git a/internal/tools/sqlc-pg-gen/main.go b/internal/tools/sqlc-pg-gen/main.go index d70dcb9595..0b1a99d309 100644 --- a/internal/tools/sqlc-pg-gen/main.go +++ b/internal/tools/sqlc-pg-gen/main.go @@ -304,7 +304,7 @@ func run(ctx context.Context) error { name := strings.Replace(extension, "-", "_", -1) var funcName string - for _, part := range strings.Split(name, "_") { + for part := range strings.SplitSeq(name, "_") { funcName += strings.Title(part) } diff --git a/internal/x/expander/expander_test.go b/internal/x/expander/expander_test.go index 84de74cdf3..195eeb9ea8 100644 --- a/internal/x/expander/expander_test.go +++ b/internal/x/expander/expander_test.go @@ -101,7 +101,7 @@ func (g *SQLiteColumnGetter) GetColumnNames(ctx context.Context, query string) ( // Get column names from the prepared statement count := stmt.ColumnCount() columns := make([]string, count) - for i := 0; i < count; i++ { + for i := range count { columns[i] = stmt.ColumnName(i) } diff --git a/scripts/test-json-process-plugin/main.go b/scripts/test-json-process-plugin/main.go index 6bcc7a25d0..471e0206ef 100644 --- a/scripts/test-json-process-plugin/main.go +++ b/scripts/test-json-process-plugin/main.go @@ -17,7 +17,7 @@ type File struct { } func main() { - in := make(map[string]interface{}) + in := make(map[string]any) decoder := json.NewDecoder(os.Stdin) err := decoder.Decode(&in) if err != nil { @@ -26,9 +26,9 @@ func main() { } buf := bytes.NewBuffer(nil) - queries := in["queries"].([]interface{}) + queries := in["queries"].([]any) for _, q := range queries { - text := q.(map[string]interface{})["text"].(string) + text := q.(map[string]any)["text"].(string) buf.WriteString(text) buf.WriteString("\n") }