Skip to content

Commit 3f21a21

Browse files
committed
database/sql: wrap errors with %w in driverArgsConnLocked
Use fmt.Errorf's %w verb to wrap errors in driverArgsConnLocked, which allows for easier unwrapping and checking of error types. Add tests in sql_test.go to ensure that Stmt.Exec and Stmt.Query correctly wrap underlying Valuer errors, adhering to the new change. Fixes #64707.
1 parent d73b432 commit 3f21a21

File tree

2 files changed

+44
-1
lines changed

2 files changed

+44
-1
lines changed

src/database/sql/convert.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ func driverArgsConnLocked(ci driver.Conn, ds *driverStmt, args []any) ([]driver.
192192
}
193193
goto nextCheck
194194
default:
195-
return nil, fmt.Errorf("sql: converting argument %s type: %v", describeNamedValue(nv), err)
195+
return nil, fmt.Errorf("sql: converting argument %s type: %w", describeNamedValue(nv), err)
196196
}
197197
}
198198

src/database/sql/sql_test.go

+43
Original file line numberDiff line numberDiff line change
@@ -4398,6 +4398,49 @@ func TestRowsScanProperlyWrapsErrors(t *testing.T) {
43984398
}
43994399
}
44004400

4401+
type alwaysErrValuer struct{}
4402+
4403+
// errEmpty is returned when an empty value is found
4404+
var errEmpty = errors.New("empty value")
4405+
4406+
func (v alwaysErrValuer) Value() (driver.Value, error) {
4407+
return nil, errEmpty
4408+
}
4409+
4410+
// Issue 64707: Ensure that Stmt.Exec and Stmt.Query properly wraps underlying errors.
4411+
func TestDriverArgsWrapsErrors(t *testing.T) {
4412+
db := newTestDB(t, "people")
4413+
defer closeDB(t, db)
4414+
4415+
t.Run("exec", func(t *testing.T) {
4416+
_, err := db.Exec("INSERT|keys|dec1=?", alwaysErrValuer{})
4417+
if err == nil {
4418+
t.Fatal("expecting back an error")
4419+
}
4420+
if !errors.Is(err, errEmpty) {
4421+
t.Fatalf("errors.Is mismatch\n%v\nWant: %v", err, errEmpty)
4422+
}
4423+
// Ensure that error substring matching still correctly works.
4424+
if !strings.Contains(err.Error(), errEmpty.Error()) {
4425+
t.Fatalf("Error %v does not contain %v", err, errEmpty)
4426+
}
4427+
})
4428+
4429+
t.Run("query", func(t *testing.T) {
4430+
_, err := db.Query("INSERT|keys|dec1=?", alwaysErrValuer{})
4431+
if err == nil {
4432+
t.Fatal("expecting back an error")
4433+
}
4434+
if !errors.Is(err, errEmpty) {
4435+
t.Fatalf("errors.Is mismatch\n%v\nWant: %v", err, errEmpty)
4436+
}
4437+
// Ensure that error substring matching still correctly works.
4438+
if !strings.Contains(err.Error(), errEmpty.Error()) {
4439+
t.Fatalf("Error %v does not contain %v", err, errEmpty)
4440+
}
4441+
})
4442+
}
4443+
44014444
func TestContextCancelDuringRawBytesScan(t *testing.T) {
44024445
for _, mode := range []string{"nocancel", "top", "bottom", "go"} {
44034446
t.Run(mode, func(t *testing.T) {

0 commit comments

Comments
 (0)