]> Cypherpunks repositories - gostls13.git/commitdiff
database/sql: wrap errors with %w in driverArgsConnLocked
authoraimuz <mr.imuz@gmail.com>
Wed, 21 Feb 2024 02:15:40 +0000 (02:15 +0000)
committerGopher Robot <gobot@golang.org>
Tue, 27 Feb 2024 16:30:20 +0000 (16:30 +0000)
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: 0df367e0fb5b213513d4e0ab7f5a87984798f96d
GitHub-Pull-Request: golang/go#64728
Reviewed-on: https://go-review.googlesource.com/c/go/+/550116
Reviewed-by: Ian Lance Taylor <iant@google.com>
Reviewed-by: Carlos Amedee <carlos@golang.org>
Reviewed-by: Mauri de Souza Meneguzzo <mauri870@gmail.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>

src/database/sql/convert.go
src/database/sql/driver/types.go
src/database/sql/sql_test.go

index cca5d15e07005f5ddbf58cfe76436f4155e3d73d..b291c1557c7ab616a800564ecdc8e5ec92160402 100644 (file)
@@ -192,7 +192,7 @@ func driverArgsConnLocked(ci driver.Conn, ds *driverStmt, args []any) ([]driver.
                        }
                        goto nextCheck
                default:
-                       return nil, fmt.Errorf("sql: converting argument %s type: %v", describeNamedValue(nv), err)
+                       return nil, fmt.Errorf("sql: converting argument %s type: %w", describeNamedValue(nv), err)
                }
        }
 
index 0380572ab19791a4ad9c2215ccb32d4b3cb2a7d8..a322f85277c6d45abefa25c69cb54bcdaa651c44 100644 (file)
@@ -34,6 +34,10 @@ type ValueConverter interface {
 
 // Valuer is the interface providing the Value method.
 //
+// Errors returned by the [Value] method are wrapped by the database/sql package.
+// This allows callers to use [errors.Is] for precise error handling after operations
+// like [database/sql.Query], [database/sql.Exec], or [database/sql.QueryRow].
+//
 // Types implementing Valuer interface are able to convert
 // themselves to a driver [Value].
 type Valuer interface {
index c38a348ab4f88fee53bdd5cce2973522250e6f71..eb4e22fcd8d974900a2504413817598dd2226aa3 100644 (file)
@@ -4398,6 +4398,49 @@ func TestRowsScanProperlyWrapsErrors(t *testing.T) {
        }
 }
 
+type alwaysErrValuer struct{}
+
+// errEmpty is returned when an empty value is found
+var errEmpty = errors.New("empty value")
+
+func (v alwaysErrValuer) Value() (driver.Value, error) {
+       return nil, errEmpty
+}
+
+// Issue 64707: Ensure that Stmt.Exec and Stmt.Query properly wraps underlying errors.
+func TestDriverArgsWrapsErrors(t *testing.T) {
+       db := newTestDB(t, "people")
+       defer closeDB(t, db)
+
+       t.Run("exec", func(t *testing.T) {
+               _, err := db.Exec("INSERT|keys|dec1=?", alwaysErrValuer{})
+               if err == nil {
+                       t.Fatal("expecting back an error")
+               }
+               if !errors.Is(err, errEmpty) {
+                       t.Fatalf("errors.Is mismatch\n%v\nWant: %v", err, errEmpty)
+               }
+               // Ensure that error substring matching still correctly works.
+               if !strings.Contains(err.Error(), errEmpty.Error()) {
+                       t.Fatalf("Error %v does not contain %v", err, errEmpty)
+               }
+       })
+
+       t.Run("query", func(t *testing.T) {
+               _, err := db.Query("INSERT|keys|dec1=?", alwaysErrValuer{})
+               if err == nil {
+                       t.Fatal("expecting back an error")
+               }
+               if !errors.Is(err, errEmpty) {
+                       t.Fatalf("errors.Is mismatch\n%v\nWant: %v", err, errEmpty)
+               }
+               // Ensure that error substring matching still correctly works.
+               if !strings.Contains(err.Error(), errEmpty.Error()) {
+                       t.Fatalf("Error %v does not contain %v", err, errEmpty)
+               }
+       })
+}
+
 func TestContextCancelDuringRawBytesScan(t *testing.T) {
        for _, mode := range []string{"nocancel", "top", "bottom", "go"} {
                t.Run(mode, func(t *testing.T) {