Skip to content

Commit c496858

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 c496858

File tree

3 files changed

+156
-1
lines changed

3 files changed

+156
-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.go

+112
Original file line numberDiff line numberDiff line change
@@ -1654,6 +1654,11 @@ func (db *DB) prepareDC(ctx context.Context, dc *driverConn, release func(error)
16541654

16551655
// ExecContext executes a query without returning any rows.
16561656
// The args are for any placeholder parameters in the query.
1657+
//
1658+
// If any argument implements the driver.Valuer interface and
1659+
// returns an error from its Value method, ExecContext wraps these errors
1660+
// using fmt.Errorf and %w verb. This allows callers to use errors.Is
1661+
// to check for specific error values.
16571662
func (db *DB) ExecContext(ctx context.Context, query string, args ...any) (Result, error) {
16581663
var res Result
16591664
var err error
@@ -1669,6 +1674,11 @@ func (db *DB) ExecContext(ctx context.Context, query string, args ...any) (Resul
16691674
// Exec executes a query without returning any rows.
16701675
// The args are for any placeholder parameters in the query.
16711676
//
1677+
// If any argument implements the driver.Valuer interface and
1678+
// returns an error from its Value method, Exec wraps these errors
1679+
// using fmt.Errorf and %w verb. This allows callers to use errors.Is
1680+
// to check for specific error values.
1681+
//
16721682
// Exec uses [context.Background] internally; to specify the context, use
16731683
// [DB.ExecContext].
16741684
func (db *DB) Exec(query string, args ...any) (Result, error) {
@@ -1724,6 +1734,11 @@ func (db *DB) execDC(ctx context.Context, dc *driverConn, release func(error), q
17241734

17251735
// QueryContext executes a query that returns rows, typically a SELECT.
17261736
// The args are for any placeholder parameters in the query.
1737+
//
1738+
// If any argument implements the driver.Valuer interface and
1739+
// returns an error from its Value method, QueryContext wraps these errors
1740+
// using fmt.Errorf and %w verb. This allows callers to use errors.Is
1741+
// to check for specific error values.
17271742
func (db *DB) QueryContext(ctx context.Context, query string, args ...any) (*Rows, error) {
17281743
var rows *Rows
17291744
var err error
@@ -1739,6 +1754,11 @@ func (db *DB) QueryContext(ctx context.Context, query string, args ...any) (*Row
17391754
// Query executes a query that returns rows, typically a SELECT.
17401755
// The args are for any placeholder parameters in the query.
17411756
//
1757+
// If any argument implements the driver.Valuer interface and
1758+
// returns an error from its Value method, Query wraps these errors
1759+
// using fmt.Errorf and %w verb. This allows callers to use errors.Is
1760+
// to check for specific error values.
1761+
//
17421762
// Query uses [context.Background] internally; to specify the context, use
17431763
// [DB.QueryContext].
17441764
func (db *DB) Query(query string, args ...any) (*Rows, error) {
@@ -1828,6 +1848,12 @@ func (db *DB) queryDC(ctx, txctx context.Context, dc *driverConn, releaseConn fu
18281848
// If the query selects no rows, the [*Row.Scan] will return [ErrNoRows].
18291849
// Otherwise, [*Row.Scan] scans the first selected row and discards
18301850
// the rest.
1851+
//
1852+
// if any argument implements the driver.Valuer interface and
1853+
// returns an error from its Value method, this error will be captured and
1854+
// wrapped using fmt.Errorf and the %w verb when it is processed during the call to Scan.
1855+
// This approach allows callers to use errors.Is in the subsequent call to [*Row.Scan]
1856+
// to check for specific error values, ensuring precise error handling.
18311857
func (db *DB) QueryRowContext(ctx context.Context, query string, args ...any) *Row {
18321858
rows, err := db.QueryContext(ctx, query, args...)
18331859
return &Row{rows: rows, err: err}
@@ -1840,6 +1866,12 @@ func (db *DB) QueryRowContext(ctx context.Context, query string, args ...any) *R
18401866
// Otherwise, [*Row.Scan] scans the first selected row and discards
18411867
// the rest.
18421868
//
1869+
// if any argument implements the driver.Valuer interface and
1870+
// returns an error from its Value method, this error will be captured and
1871+
// wrapped using fmt.Errorf and the %w verb when it is processed during the call to Scan.
1872+
// This approach allows callers to use errors.Is in the subsequent call to [*Row.Scan]
1873+
// to check for specific error values, ensuring precise error handling.
1874+
//
18431875
// QueryRow uses [context.Background] internally; to specify the context, use
18441876
// [DB.QueryRowContext].
18451877
func (db *DB) QueryRow(query string, args ...any) *Row {
@@ -2009,6 +2041,11 @@ func (c *Conn) PingContext(ctx context.Context) error {
20092041

20102042
// ExecContext executes a query without returning any rows.
20112043
// The args are for any placeholder parameters in the query.
2044+
//
2045+
// If any argument implements the driver.Valuer interface and
2046+
// returns an error from its Value method, ExecContext wraps these errors
2047+
// using fmt.Errorf and %w verb. This allows callers to use errors.Is
2048+
// to check for specific error values.
20122049
func (c *Conn) ExecContext(ctx context.Context, query string, args ...any) (Result, error) {
20132050
dc, release, err := c.grabConn(ctx)
20142051
if err != nil {
@@ -2019,6 +2056,11 @@ func (c *Conn) ExecContext(ctx context.Context, query string, args ...any) (Resu
20192056

20202057
// QueryContext executes a query that returns rows, typically a SELECT.
20212058
// The args are for any placeholder parameters in the query.
2059+
//
2060+
// If any argument implements the driver.Valuer interface and
2061+
// returns an error from its Value method, QueryContext wraps these errors
2062+
// using fmt.Errorf and %w verb. This allows callers to use errors.Is
2063+
// to check for specific error values.
20222064
func (c *Conn) QueryContext(ctx context.Context, query string, args ...any) (*Rows, error) {
20232065
dc, release, err := c.grabConn(ctx)
20242066
if err != nil {
@@ -2033,6 +2075,12 @@ func (c *Conn) QueryContext(ctx context.Context, query string, args ...any) (*Ro
20332075
// If the query selects no rows, the [*Row.Scan] will return [ErrNoRows].
20342076
// Otherwise, the [*Row.Scan] scans the first selected row and discards
20352077
// the rest.
2078+
//
2079+
// if any argument implements the driver.Valuer interface and
2080+
// returns an error from its Value method, this error will be captured and
2081+
// wrapped using fmt.Errorf and the %w verb when it is processed during the call to Scan.
2082+
// This approach allows callers to use errors.Is in the subsequent call to [*Row.Scan]
2083+
// to check for specific error values, ensuring precise error handling.
20362084
func (c *Conn) QueryRowContext(ctx context.Context, query string, args ...any) *Row {
20372085
rows, err := c.QueryContext(ctx, query, args...)
20382086
return &Row{rows: rows, err: err}
@@ -2498,6 +2546,11 @@ func (tx *Tx) Stmt(stmt *Stmt) *Stmt {
24982546

24992547
// ExecContext executes a query that doesn't return rows.
25002548
// For example: an INSERT and UPDATE.
2549+
//
2550+
// If any argument implements the driver.Valuer interface and
2551+
// returns an error from its Value method, ExecContext wraps these errors
2552+
// using fmt.Errorf and %w verb. This allows callers to use errors.Is
2553+
// to check for specific error values.
25012554
func (tx *Tx) ExecContext(ctx context.Context, query string, args ...any) (Result, error) {
25022555
dc, release, err := tx.grabConn(ctx)
25032556
if err != nil {
@@ -2509,13 +2562,23 @@ func (tx *Tx) ExecContext(ctx context.Context, query string, args ...any) (Resul
25092562
// Exec executes a query that doesn't return rows.
25102563
// For example: an INSERT and UPDATE.
25112564
//
2565+
// If any argument implements the driver.Valuer interface and
2566+
// returns an error from its Value method, Exec wraps these errors
2567+
// using fmt.Errorf and %w verb. This allows callers to use errors.Is
2568+
// to check for specific error values.
2569+
//
25122570
// Exec uses [context.Background] internally; to specify the context, use
25132571
// [Tx.ExecContext].
25142572
func (tx *Tx) Exec(query string, args ...any) (Result, error) {
25152573
return tx.ExecContext(context.Background(), query, args...)
25162574
}
25172575

25182576
// QueryContext executes a query that returns rows, typically a SELECT.
2577+
//
2578+
// If any argument implements the driver.Valuer interface and
2579+
// returns an error from its Value method, QueryContext wraps these errors
2580+
// using fmt.Errorf and %w verb. This allows callers to use errors.Is
2581+
// to check for specific error values.
25192582
func (tx *Tx) QueryContext(ctx context.Context, query string, args ...any) (*Rows, error) {
25202583
dc, release, err := tx.grabConn(ctx)
25212584
if err != nil {
@@ -2529,6 +2592,11 @@ func (tx *Tx) QueryContext(ctx context.Context, query string, args ...any) (*Row
25292592
//
25302593
// Query uses [context.Background] internally; to specify the context, use
25312594
// [Tx.QueryContext].
2595+
//
2596+
// If any argument implements the driver.Valuer interface and
2597+
// returns an error from its Value method, Query wraps these errors
2598+
// using fmt.Errorf and %w verb. This allows callers to use errors.Is
2599+
// to check for specific error values.
25322600
func (tx *Tx) Query(query string, args ...any) (*Rows, error) {
25332601
return tx.QueryContext(context.Background(), query, args...)
25342602
}
@@ -2539,6 +2607,12 @@ func (tx *Tx) Query(query string, args ...any) (*Rows, error) {
25392607
// If the query selects no rows, the [*Row.Scan] will return [ErrNoRows].
25402608
// Otherwise, the [*Row.Scan] scans the first selected row and discards
25412609
// the rest.
2610+
//
2611+
// if any argument implements the driver.Valuer interface and
2612+
// returns an error from its Value method, this error will be captured and
2613+
// wrapped using fmt.Errorf and the %w verb when it is processed during the call to Scan.
2614+
// This approach allows callers to use errors.Is in the subsequent call to [*Row.Scan]
2615+
// to check for specific error values, ensuring precise error handling.
25422616
func (tx *Tx) QueryRowContext(ctx context.Context, query string, args ...any) *Row {
25432617
rows, err := tx.QueryContext(ctx, query, args...)
25442618
return &Row{rows: rows, err: err}
@@ -2551,6 +2625,12 @@ func (tx *Tx) QueryRowContext(ctx context.Context, query string, args ...any) *R
25512625
// Otherwise, the [*Row.Scan] scans the first selected row and discards
25522626
// the rest.
25532627
//
2628+
// if any argument implements the driver.Valuer interface and
2629+
// returns an error from its Value method, this error will be captured and
2630+
// wrapped using fmt.Errorf and the %w verb when it is processed during the call to Scan.
2631+
// This approach allows callers to use errors.Is in the subsequent call to [*Row.Scan]
2632+
// to check for specific error values, ensuring precise error handling.
2633+
//
25542634
// QueryRow uses [context.Background] internally; to specify the context, use
25552635
// [Tx.QueryRowContext].
25562636
func (tx *Tx) QueryRow(query string, args ...any) *Row {
@@ -2630,6 +2710,11 @@ type Stmt struct {
26302710

26312711
// ExecContext executes a prepared statement with the given arguments and
26322712
// returns a [Result] summarizing the effect of the statement.
2713+
//
2714+
// If any argument implements the driver.Valuer interface and
2715+
// returns an error from its Value method, ExecContext wraps these errors
2716+
// using fmt.Errorf and %w verb. This allows callers to use errors.Is
2717+
// to check for specific error values.
26332718
func (s *Stmt) ExecContext(ctx context.Context, args ...any) (Result, error) {
26342719
s.closemu.RLock()
26352720
defer s.closemu.RUnlock()
@@ -2652,6 +2737,11 @@ func (s *Stmt) ExecContext(ctx context.Context, args ...any) (Result, error) {
26522737
// Exec executes a prepared statement with the given arguments and
26532738
// returns a [Result] summarizing the effect of the statement.
26542739
//
2740+
// If any argument implements the driver.Valuer interface and
2741+
// returns an error from its Value method, Exec wraps these errors
2742+
// using fmt.Errorf and %w verb. This allows callers to use errors.Is
2743+
// to check for specific error values.
2744+
//
26552745
// Exec uses [context.Background] internally; to specify the context, use
26562746
// [Stmt.ExecContext].
26572747
func (s *Stmt) Exec(args ...any) (Result, error) {
@@ -2770,6 +2860,11 @@ func (s *Stmt) prepareOnConnLocked(ctx context.Context, dc *driverConn) (*driver
27702860

27712861
// QueryContext executes a prepared query statement with the given arguments
27722862
// and returns the query results as a [*Rows].
2863+
//
2864+
// If any argument implements the driver.Valuer interface and
2865+
// returns an error from its Value method, QueryContext wraps these errors
2866+
// using fmt.Errorf and %w verb. This allows callers to use errors.Is
2867+
// to check for specific error values.
27732868
func (s *Stmt) QueryContext(ctx context.Context, args ...any) (*Rows, error) {
27742869
s.closemu.RLock()
27752870
defer s.closemu.RUnlock()
@@ -2820,6 +2915,11 @@ func (s *Stmt) QueryContext(ctx context.Context, args ...any) (*Rows, error) {
28202915
// Query executes a prepared query statement with the given arguments
28212916
// and returns the query results as a *Rows.
28222917
//
2918+
// If any argument implements the driver.Valuer interface and
2919+
// returns an error from its Value method, Query wraps these errors
2920+
// using fmt.Errorf and %w verb. This allows callers to use errors.Is
2921+
// to check for specific error values.
2922+
//
28232923
// Query uses [context.Background] internally; to specify the context, use
28242924
// [Stmt.QueryContext].
28252925
func (s *Stmt) Query(args ...any) (*Rows, error) {
@@ -2842,6 +2942,12 @@ func rowsiFromStatement(ctx context.Context, ci driver.Conn, ds *driverStmt, arg
28422942
// If the query selects no rows, the [*Row.Scan] will return [ErrNoRows].
28432943
// Otherwise, the [*Row.Scan] scans the first selected row and discards
28442944
// the rest.
2945+
//
2946+
// if any argument implements the driver.Valuer interface and
2947+
// returns an error from its Value method, this error will be captured and
2948+
// wrapped using fmt.Errorf and the %w verb when it is processed during the call to Scan.
2949+
// This approach allows callers to use errors.Is in the subsequent call to [*Row.Scan]
2950+
// to check for specific error values, ensuring precise error handling.
28452951
func (s *Stmt) QueryRowContext(ctx context.Context, args ...any) *Row {
28462952
rows, err := s.QueryContext(ctx, args...)
28472953
if err != nil {
@@ -2857,6 +2963,12 @@ func (s *Stmt) QueryRowContext(ctx context.Context, args ...any) *Row {
28572963
// Otherwise, the [*Row.Scan] scans the first selected row and discards
28582964
// the rest.
28592965
//
2966+
// if any argument implements the driver.Valuer interface and
2967+
// returns an error from its Value method, this error will be captured and
2968+
// wrapped using fmt.Errorf and the %w verb when it is processed during the call to Scan.
2969+
// This approach allows callers to use errors.Is in the subsequent call to [*Row.Scan]
2970+
// to check for specific error values, ensuring precise error handling.
2971+
//
28602972
// Example usage:
28612973
//
28622974
// var name string

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)