Skip to content

Commit d8311c8

Browse files
aimuzgopherbot
authored andcommitted
database/sql: wrap errors with %w in driverArgsConnLocked
Use fmt.Errorf %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. Change-Id: Id9f80e265735d0849ee7abba63e58e4c26e658ad GitHub-Last-Rev: 0df367e GitHub-Pull-Request: #64728 Reviewed-on: https://go-review.googlesource.com/c/go/+/550116 Reviewed-by: Ian Lance Taylor <[email protected]> Reviewed-by: Carlos Amedee <[email protected]> Reviewed-by: Mauri de Souza Meneguzzo <[email protected]> Auto-Submit: Ian Lance Taylor <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 856355a commit d8311c8

File tree

3 files changed

+48
-1
lines changed

3 files changed

+48
-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/driver/types.go

+4
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ type ValueConverter interface {
3434

3535
// Valuer is the interface providing the Value method.
3636
//
37+
// Errors returned by the [Value] method are wrapped by the database/sql package.
38+
// This allows callers to use [errors.Is] for precise error handling after operations
39+
// like [database/sql.Query], [database/sql.Exec], or [database/sql.QueryRow].
40+
//
3741
// Types implementing Valuer interface are able to convert
3842
// themselves to a driver [Value].
3943
type Valuer interface {

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)