From 69b44c34872573f205d41570a49fb1a145562210 Mon Sep 17 00:00:00 2001 From: Jason Turim Date: Thu, 9 Jan 2025 11:45:12 -0500 Subject: [PATCH] Include the field name in error messages when scanning structs --- pgtype/json_test.go | 4 ++-- pgtype/jsonb_test.go | 4 ++-- pgtype/xml_test.go | 2 +- query_test.go | 2 +- rows.go | 11 ++++++++--- values_test.go | 4 +++- 6 files changed, 17 insertions(+), 10 deletions(-) diff --git a/pgtype/json_test.go b/pgtype/json_test.go index 6277fc8bb..8bb6ff77b 100644 --- a/pgtype/json_test.go +++ b/pgtype/json_test.go @@ -191,11 +191,11 @@ func TestJSONCodecUnmarshalSQLNull(t *testing.T) { // A string cannot scan a NULL. str := "foobar" err = conn.QueryRow(ctx, "select null::json").Scan(&str) - require.EqualError(t, err, "can't scan into dest[0]: cannot scan NULL into *string") + require.EqualError(t, err, "can't scan into dest[0] (col: json): cannot scan NULL into *string") // A non-string cannot scan a NULL. err = conn.QueryRow(ctx, "select null::json").Scan(&n) - require.EqualError(t, err, "can't scan into dest[0]: cannot scan NULL into *int") + require.EqualError(t, err, "can't scan into dest[0] (col: json): cannot scan NULL into *int") }) } diff --git a/pgtype/jsonb_test.go b/pgtype/jsonb_test.go index f34b3ad51..031cd3768 100644 --- a/pgtype/jsonb_test.go +++ b/pgtype/jsonb_test.go @@ -66,11 +66,11 @@ func TestJSONBCodecUnmarshalSQLNull(t *testing.T) { // A string cannot scan a NULL. str := "foobar" err = conn.QueryRow(ctx, "select null::jsonb").Scan(&str) - require.EqualError(t, err, "can't scan into dest[0]: cannot scan NULL into *string") + require.EqualError(t, err, "can't scan into dest[0] (col: jsonb): cannot scan NULL into *string") // A non-string cannot scan a NULL. err = conn.QueryRow(ctx, "select null::jsonb").Scan(&n) - require.EqualError(t, err, "can't scan into dest[0]: cannot scan NULL into *int") + require.EqualError(t, err, "can't scan into dest[0] (col: jsonb): cannot scan NULL into *int") }) } diff --git a/pgtype/xml_test.go b/pgtype/xml_test.go index 0f755e96f..de7de4d20 100644 --- a/pgtype/xml_test.go +++ b/pgtype/xml_test.go @@ -79,7 +79,7 @@ func TestXMLCodecUnmarshalSQLNull(t *testing.T) { // A string cannot scan a NULL. str := "foobar" err = conn.QueryRow(ctx, "select null::xml").Scan(&str) - assert.EqualError(t, err, "can't scan into dest[0]: cannot scan NULL into *string") + assert.EqualError(t, err, "can't scan into dest[0] (col: xml): cannot scan NULL into *string") }) } diff --git a/query_test.go b/query_test.go index a6a26ad77..2c8410248 100644 --- a/query_test.go +++ b/query_test.go @@ -420,7 +420,7 @@ func TestConnQueryReadWrongTypeError(t *testing.T) { t.Fatal("Expected Rows to have an error after an improper read but it didn't") } - if rows.Err().Error() != "can't scan into dest[0]: cannot scan int4 (OID 23) in binary format into *time.Time" { + if rows.Err().Error() != "can't scan into dest[0] (col: n): cannot scan int4 (OID 23) in binary format into *time.Time" { t.Fatalf("Expected different Rows.Err(): %v", rows.Err()) } diff --git a/rows.go b/rows.go index f23625d4c..268559ce6 100644 --- a/rows.go +++ b/rows.go @@ -272,7 +272,7 @@ func (rows *baseRows) Scan(dest ...any) error { err := rows.scanPlans[i].Scan(values[i], dst) if err != nil { - err = ScanArgError{ColumnIndex: i, Err: err} + err = ScanArgError{ColumnIndex: i, FieldName: fieldDescriptions[i].Name, Err: err} rows.fatal(err) return err } @@ -334,11 +334,16 @@ func (rows *baseRows) Conn() *Conn { type ScanArgError struct { ColumnIndex int + FieldName string Err error } func (e ScanArgError) Error() string { - return fmt.Sprintf("can't scan into dest[%d]: %v", e.ColumnIndex, e.Err) + if e.FieldName == "?column?" { // Don't include the fieldname if it's unknown + return fmt.Sprintf("can't scan into dest[%d]: %v", e.ColumnIndex, e.Err) + } + + return fmt.Sprintf("can't scan into dest[%d] (col: %s): %v", e.ColumnIndex, e.FieldName, e.Err) } func (e ScanArgError) Unwrap() error { @@ -366,7 +371,7 @@ func ScanRow(typeMap *pgtype.Map, fieldDescriptions []pgconn.FieldDescription, v err := typeMap.Scan(fieldDescriptions[i].DataTypeOID, fieldDescriptions[i].Format, values[i], d) if err != nil { - return ScanArgError{ColumnIndex: i, Err: err} + return ScanArgError{ColumnIndex: i, FieldName: fieldDescriptions[i].Name, Err: err} } } diff --git a/values_test.go b/values_test.go index 132b79a2a..4a5df8c65 100644 --- a/values_test.go +++ b/values_test.go @@ -3,6 +3,7 @@ package pgx_test import ( "bytes" "context" + "fmt" "net" "os" "reflect" @@ -215,7 +216,8 @@ func testJSONInt16ArrayFailureDueToOverflow(t *testing.T, conn *pgx.Conn, typena input := []int{1, 2, 234432} var output []int16 err := conn.QueryRow(context.Background(), "select $1::"+typename, input).Scan(&output) - if err == nil || err.Error() != "can't scan into dest[0]: json: cannot unmarshal number 234432 into Go value of type int16" { + expectedMessage := fmt.Sprintf("can't scan into dest[0] (col: %s): json: cannot unmarshal number 234432 into Go value of type int16", typename) + if err == nil || err.Error() != expectedMessage { t.Errorf("%s: Expected *json.UnmarshalTypeError, but got %v", typename, err) } }