diff --git a/.golangci.yml b/.golangci.yml index d0903ab3f..1750ff5dc 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -6,6 +6,7 @@ linters: enable: - govet - ineffassign + - unconvert # See: https://golangci-lint.run/usage/formatters/ formatters: diff --git a/CLAUDE.md b/CLAUDE.md index e3ed1a2ec..71a8fc164 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -69,5 +69,5 @@ Supporting packages: - **Context-based** — all blocking operations take `context.Context`. - **Tracer interfaces** — observability via `QueryTracer`, `BatchTracer`, `CopyFromTracer`, `PrepareTracer` on `ConnConfig.Tracer`. - **Formatting** — always run `goimports -w .` after making changes to ensure code is properly formatted. CI checks formatting via `gofmt -l -s -w . && git diff --exit-code`. `gofumpt` with extra rules is also enforced via `golangci-lint`. -- **Linters** — `govet` and `ineffassign` only (configured in `.golangci.yml`). +- **Linters** — `govet`, `ineffassign`, and `unconvert` only (configured in `.golangci.yml`). - **CI matrix** — tests run against Go 1.25/1.26 × PostgreSQL 14-18 + CockroachDB, on Linux and Windows. Race detector enabled on Linux only. diff --git a/conn.go b/conn.go index 4f27a5df2..879f6c164 100644 --- a/conn.go +++ b/conn.go @@ -833,7 +833,7 @@ optionLoop: if resultFormatsByOID != nil { resultFormats = make([]int16, len(sd.Fields)) for i := range resultFormats { - resultFormats[i] = resultFormatsByOID[uint32(sd.Fields[i].DataTypeOID)] + resultFormats[i] = resultFormatsByOID[sd.Fields[i].DataTypeOID] } } diff --git a/named_args.go b/named_args.go index c88991ee4..bc0345359 100644 --- a/named_args.go +++ b/named_args.go @@ -163,7 +163,7 @@ func namedArgState(l *sqlLexer) stateFn { if _, found := l.nameToOrdinal[na]; !found { l.nameToOrdinal[na] = len(l.nameToOrdinal) + 1 } - l.parts = append(l.parts, namedArg(na)) + l.parts = append(l.parts, na) l.start = l.pos return rawState } diff --git a/pgconn/pgconn.go b/pgconn/pgconn.go index d6587cef8..7547c480e 100644 --- a/pgconn/pgconn.go +++ b/pgconn/pgconn.go @@ -475,7 +475,7 @@ func connectOne(ctx context.Context, config *Config, connectConfig *connectOneCo case *pgproto3.ParameterStatus, *pgproto3.NoticeResponse: // handled by ReceiveMessage case *pgproto3.NegotiateProtocolVersion: - serverVersion := pgproto3.ProtocolVersion30&0xFFFF0000 | uint32(msg.NewestMinorProtocol) + serverVersion := pgproto3.ProtocolVersion30&0xFFFF0000 | msg.NewestMinorProtocol if serverVersion < minProtocolVersion { pgConn.conn.Close() return nil, newPerDialConnectError("server protocol version too low", nil) @@ -1008,22 +1008,22 @@ func ErrorResponseToPgError(msg *pgproto3.ErrorResponse) *PgError { return &PgError{ Severity: msg.Severity, SeverityUnlocalized: msg.SeverityUnlocalized, - Code: string(msg.Code), - Message: string(msg.Message), - Detail: string(msg.Detail), + Code: msg.Code, + Message: msg.Message, + Detail: msg.Detail, Hint: msg.Hint, Position: msg.Position, InternalPosition: msg.InternalPosition, - InternalQuery: string(msg.InternalQuery), - Where: string(msg.Where), - SchemaName: string(msg.SchemaName), - TableName: string(msg.TableName), - ColumnName: string(msg.ColumnName), - DataTypeName: string(msg.DataTypeName), + InternalQuery: msg.InternalQuery, + Where: msg.Where, + SchemaName: msg.SchemaName, + TableName: msg.TableName, + ColumnName: msg.ColumnName, + DataTypeName: msg.DataTypeName, ConstraintName: msg.ConstraintName, - File: string(msg.File), + File: msg.File, Line: msg.Line, - Routine: string(msg.Routine), + Routine: msg.Routine, } } diff --git a/pgconn/pgconn_test.go b/pgconn/pgconn_test.go index 92d2d370e..8bd52b46c 100644 --- a/pgconn/pgconn_test.go +++ b/pgconn/pgconn_test.go @@ -3209,7 +3209,7 @@ func TestPipelinePrepare(t *testing.T) { sd, ok := results.(*pgconn.StatementDescription) require.Truef(t, ok, "expected StatementDescription, got: %#v", results) require.Len(t, sd.Fields, 1) - require.Equal(t, "a", string(sd.Fields[0].Name)) + require.Equal(t, "a", sd.Fields[0].Name) require.Equal(t, []uint32{pgtype.Int8OID}, sd.ParamOIDs) results, err = pipeline.GetResults() @@ -3217,7 +3217,7 @@ func TestPipelinePrepare(t *testing.T) { sd, ok = results.(*pgconn.StatementDescription) require.Truef(t, ok, "expected StatementDescription, got: %#v", results) require.Len(t, sd.Fields, 1) - require.Equal(t, "b", string(sd.Fields[0].Name)) + require.Equal(t, "b", sd.Fields[0].Name) require.Equal(t, []uint32{pgtype.TextOID}, sd.ParamOIDs) results, err = pipeline.GetResults() @@ -3225,7 +3225,7 @@ func TestPipelinePrepare(t *testing.T) { sd, ok = results.(*pgconn.StatementDescription) require.Truef(t, ok, "expected StatementDescription, got: %#v", results) require.Len(t, sd.Fields, 1) - require.Equal(t, "c", string(sd.Fields[0].Name)) + require.Equal(t, "c", sd.Fields[0].Name) require.Equal(t, []uint32{}, sd.ParamOIDs) results, err = pipeline.GetResults() @@ -3279,7 +3279,7 @@ func TestPipelinePrepareError(t *testing.T) { sd, ok := results.(*pgconn.StatementDescription) require.Truef(t, ok, "expected StatementDescription, got: %#v", results) require.Len(t, sd.Fields, 1) - require.Equal(t, "a", string(sd.Fields[0].Name)) + require.Equal(t, "a", sd.Fields[0].Name) require.Equal(t, []uint32{pgtype.Int8OID}, sd.ParamOIDs) results, err = pipeline.GetResults() @@ -3323,7 +3323,7 @@ func TestPipelinePrepareAndDeallocate(t *testing.T) { sd, ok := results.(*pgconn.StatementDescription) require.Truef(t, ok, "expected StatementDescription, got: %#v", results) require.Len(t, sd.Fields, 1) - require.Equal(t, "a", string(sd.Fields[0].Name)) + require.Equal(t, "a", sd.Fields[0].Name) require.Equal(t, []uint32{pgtype.Int8OID}, sd.ParamOIDs) results, err = pipeline.GetResults() @@ -3460,7 +3460,7 @@ func TestPipelinePrepareQuery(t *testing.T) { sd, ok := results.(*pgconn.StatementDescription) require.Truef(t, ok, "expected StatementDescription, got: %#v", results) require.Len(t, sd.Fields, 1) - require.Equal(t, "msg", string(sd.Fields[0].Name)) + require.Equal(t, "msg", sd.Fields[0].Name) require.Equal(t, []uint32{pgtype.TextOID}, sd.ParamOIDs) results, err = pipeline.GetResults() @@ -3627,7 +3627,7 @@ func TestPipelineFlushForSingleRequests(t *testing.T) { sd, ok := results.(*pgconn.StatementDescription) require.Truef(t, ok, "expected StatementDescription, got: %#v", results) require.Len(t, sd.Fields, 1) - require.Equal(t, "msg", string(sd.Fields[0].Name)) + require.Equal(t, "msg", sd.Fields[0].Name) require.Equal(t, []uint32{pgtype.TextOID}, sd.ParamOIDs) results, err = pipeline.GetResults() @@ -3724,7 +3724,7 @@ func TestPipelineFlushForRequestSeries(t *testing.T) { sd, ok := results.(*pgconn.StatementDescription) require.Truef(t, ok, "expected StatementDescription, got: %#v", results) require.Len(t, sd.Fields, 1) - require.Equal(t, "num", string(sd.Fields[0].Name)) + require.Equal(t, "num", sd.Fields[0].Name) require.Equal(t, []uint32{pgtype.Int8OID}, sd.ParamOIDs) results, err = pipeline.GetResults() diff --git a/pgproto3/close.go b/pgproto3/close.go index 0b50f27cb..0e7e04952 100644 --- a/pgproto3/close.go +++ b/pgproto3/close.go @@ -75,7 +75,7 @@ func (dst *Close) UnmarshalJSON(data []byte) error { return errors.New("invalid length for Close.ObjectType") } - dst.ObjectType = byte(msg.ObjectType[0]) + dst.ObjectType = msg.ObjectType[0] dst.Name = msg.Name return nil } diff --git a/pgproto3/describe.go b/pgproto3/describe.go index 89feff215..0c396f1ba 100644 --- a/pgproto3/describe.go +++ b/pgproto3/describe.go @@ -74,7 +74,7 @@ func (dst *Describe) UnmarshalJSON(data []byte) error { return errors.New("invalid length for Describe.ObjectType") } - dst.ObjectType = byte(msg.ObjectType[0]) + dst.ObjectType = msg.ObjectType[0] dst.Name = msg.Name return nil } diff --git a/pgtype/float4.go b/pgtype/float4.go index 241a25add..a43553f67 100644 --- a/pgtype/float4.go +++ b/pgtype/float4.go @@ -50,7 +50,7 @@ func (f *Float4) Scan(src any) error { *f = Float4{Float32: float32(src), Valid: true} return nil case string: - n, err := strconv.ParseFloat(string(src), 32) + n, err := strconv.ParseFloat(src, 32) if err != nil { return err } diff --git a/pgtype/float8.go b/pgtype/float8.go index 54d6781ec..6234231d7 100644 --- a/pgtype/float8.go +++ b/pgtype/float8.go @@ -58,7 +58,7 @@ func (f *Float8) Scan(src any) error { *f = Float8{Float64: src, Valid: true} return nil case string: - n, err := strconv.ParseFloat(string(src), 64) + n, err := strconv.ParseFloat(src, 64) if err != nil { return err } diff --git a/pgtype/numeric.go b/pgtype/numeric.go index c9022abce..0bd55e9c3 100644 --- a/pgtype/numeric.go +++ b/pgtype/numeric.go @@ -132,7 +132,7 @@ func (n *Numeric) ScanScientific(src string) error { return scanPlanTextAnyToNumericScanner{}.Scan([]byte(src), n) } - if bigF, ok := new(big.Float).SetString(string(src)); ok { + if bigF, ok := new(big.Float).SetString(src); ok { smallF, _ := bigF.Float64() src = strconv.FormatFloat(smallF, 'f', -1, 64) } diff --git a/pgtype/tsvector.go b/pgtype/tsvector.go index b357948a0..4a2f2e2b1 100644 --- a/pgtype/tsvector.go +++ b/pgtype/tsvector.go @@ -174,7 +174,7 @@ func (encodePlanTSVectorCodecBinary) Encode(value any, buf []byte) ([]byte, erro // Each position is a uint16: weight (2 bits) | position (14 bits) for _, pos := range entry.Positions { - packed := tsvectorWeightToBinary(pos.Weight)<<14 | uint16(pos.Position)&0x3FFF + packed := tsvectorWeightToBinary(pos.Weight)<<14 | pos.Position&0x3FFF buf = pgio.AppendUint16(buf, packed) } } diff --git a/pgtype/uint64.go b/pgtype/uint64.go index 68fd16613..fc407bdb6 100644 --- a/pgtype/uint64.go +++ b/pgtype/uint64.go @@ -158,7 +158,7 @@ type encodePlanUint64CodecTextUint64 struct{} func (encodePlanUint64CodecTextUint64) Encode(value any, buf []byte) (newBuf []byte, err error) { v := value.(uint64) - return append(buf, strconv.FormatUint(uint64(v), 10)...), nil + return append(buf, strconv.FormatUint(v, 10)...), nil } type encodePlanUint64CodecTextUint64Valuer struct{} @@ -298,7 +298,7 @@ func (scanPlanBinaryUint64ToTextScanner) Scan(src []byte, dst any) error { return fmt.Errorf("invalid length for uint64: %v", len(src)) } - n := uint64(binary.BigEndian.Uint64(src)) + n := binary.BigEndian.Uint64(src) return s.ScanText(Text{String: strconv.FormatUint(n, 10), Valid: true}) } diff --git a/rows.go b/rows.go index 2c5d24245..4e5cf95d0 100644 --- a/rows.go +++ b/rows.go @@ -554,7 +554,7 @@ func (rs *mapRowScanner) ScanRow(rows Rows) error { *rs = make(mapRowScanner, len(values)) for i := range values { - (*rs)[string(rows.FieldDescriptions()[i].Name)] = values[i] + (*rs)[rows.FieldDescriptions()[i].Name] = values[i] } return nil diff --git a/stdlib/sql.go b/stdlib/sql.go index 23113286a..576d3c60f 100644 --- a/stdlib/sql.go +++ b/stdlib/sql.go @@ -641,7 +641,7 @@ func (r *Rows) Columns() []string { fields := r.rows.FieldDescriptions() r.columnNames = make([]string, len(fields)) for i, fd := range fields { - r.columnNames[i] = string(fd.Name) + r.columnNames[i] = fd.Name } } diff --git a/tracer_test.go b/tracer_test.go index 8920313ff..207999cc4 100644 --- a/tracer_test.go +++ b/tracer_test.go @@ -119,13 +119,13 @@ func TestTraceExec(t *testing.T) { require.Equal(t, `select $1::text`, data.SQL) require.Len(t, data.Args, 1) require.Equal(t, `testing`, data.Args[0]) - return context.WithValue(ctx, ctxKey(ctxKey("fromTraceQueryStart")), "foo") + return context.WithValue(ctx, ctxKey("fromTraceQueryStart"), "foo") } traceQueryEndCalled := false tracer.traceQueryEnd = func(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryEndData) { traceQueryEndCalled = true - require.Equal(t, "foo", ctx.Value(ctxKey(ctxKey("fromTraceQueryStart")))) + require.Equal(t, "foo", ctx.Value(ctxKey("fromTraceQueryStart"))) require.Equal(t, `SELECT 1`, data.CommandTag.String()) require.NoError(t, data.Err) }