Repository: jackc/pgx
Pull request #2115 (2024-08-26 11:09:34)

Use sql.ErrNoRows as value for pgx.ErrNoRows
Remaking pgx.ErrNoRows as wrapper over sql.ErrNoRows

**Why not just `var ErrNoRows = sql.ErrNoRows`?** 
I want to preserve the original error message in case any tools (log analyzers, for example) depend on the exact string value

Corresponding issue
https://github.com/jackc/pgx/issues/1931


Patch:
diff --git a/conn.go b/conn.go
index 979095af..187b3dd5 100644
--- a/conn.go
+++ b/conn.go
@@ -3,6 +3,7 @@ package pgx
 import (
 	"context"
 	"crypto/sha256"
+	"database/sql"
 	"encoding/hex"
 	"errors"
 	"fmt"
@@ -102,11 +103,27 @@ func (ident Identifier) Sanitize() string {
 
 var (
 	// ErrNoRows occurs when rows are expected but none are returned.
-	ErrNoRows = errors.New("no rows in result set")
+	ErrNoRows = newProxyErr(sql.ErrNoRows, "no rows in result set")
 	// ErrTooManyRows occurs when more rows than expected are returned.
 	ErrTooManyRows = errors.New("too many rows in result set")
 )
 
+func newProxyErr(background error, msg string) error {
+	return &proxyError{
+		msg:        msg,
+		background: background,
+	}
+}
+
+type proxyError struct {
+	msg        string
+	background error
+}
+
+func (err *proxyError) Error() string { return err.msg }
+
+func (err *proxyError) Unwrap() error { return err.background }
+
 var (
 	errDisabledStatementCache   = fmt.Errorf("cannot use QueryExecModeCacheStatement with disabled statement cache")
 	errDisabledDescriptionCache = fmt.Errorf("cannot use QueryExecModeCacheDescribe with disabled description cache")
diff --git a/pgtype/uint32.go b/pgtype/uint32.go
index c0134109..098c516c 100644
--- a/pgtype/uint32.go
+++ b/pgtype/uint32.go
@@ -205,8 +205,6 @@ func (Uint32Codec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPl
 			return scanPlanBinaryUint32ToUint32{}
 		case Uint32Scanner:
 			return scanPlanBinaryUint32ToUint32Scanner{}
-		case TextScanner:
-			return scanPlanBinaryUint32ToTextScanner{}
 		}
 	case TextFormatCode:
 		switch target.(type) {
@@ -284,22 +282,6 @@ func (scanPlanBinaryUint32ToUint32Scanner) Scan(src []byte, dst any) error {
 	return s.ScanUint32(Uint32{Uint32: n, Valid: true})
 }
 
-type scanPlanBinaryUint32ToTextScanner struct{}
-
-func (scanPlanBinaryUint32ToTextScanner) Scan(src []byte, dst any) error {
-	s, ok := (dst).(TextScanner)
-	if !ok {
-		return ErrScanTargetTypeChanged
-	}
-
-	if src == nil {
-		return s.ScanText(Text{})
-	}
-
-	n := uint64(binary.BigEndian.Uint32(src))
-	return s.ScanText(Text{String: strconv.FormatUint(n, 10), Valid: true})
-}
-
 type scanPlanTextAnyToUint32Scanner struct{}
 
 func (scanPlanTextAnyToUint32Scanner) Scan(src []byte, dst any) error {


Test patch:
diff --git a/conn_test.go b/conn_test.go
index df8c9186..200ecc1a 100644
--- a/conn_test.go
+++ b/conn_test.go
@@ -3,6 +3,7 @@ package pgx_test
 import (
 	"bytes"
 	"context"
+	"database/sql"
 	"os"
 	"strings"
 	"sync"
@@ -1408,3 +1409,13 @@ func TestConnDeallocateInvalidatedCachedStatementsInTransactionWithBatch(t *test
 
 	ensureConnValid(t, conn)
 }
+
+func TestErrNoRows(t *testing.T) {
+	t.Parallel()
+
+	// ensure we preserve old error message
+	require.Equal(t, "no rows in result set", pgx.ErrNoRows.Error())
+
+	require.ErrorIs(t, pgx.ErrNoRows, sql.ErrNoRows, "pgx.ErrNowRows must match sql.ErrNoRows")
+	require.ErrorIs(t, pgx.ErrNoRows, pgx.ErrNoRows, "sql.ErrNowRows must match pgx.ErrNoRows")
+}
diff --git a/pgtype/uint32_test.go b/pgtype/uint32_test.go
index efa4e273..842de643 100644
--- a/pgtype/uint32_test.go
+++ b/pgtype/uint32_test.go
@@ -17,6 +17,5 @@ func TestUint32Codec(t *testing.T) {
 		},
 		{pgtype.Uint32{}, new(pgtype.Uint32), isExpectedEq(pgtype.Uint32{})},
 		{nil, new(pgtype.Uint32), isExpectedEq(pgtype.Uint32{})},
-		{"1147", new(string), isExpectedEq("1147")},
 	})
 }