// 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 {
}
}
+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) {