Repository: jackc/pgx
Pull request #2028 (2024-05-25 12:49:10)

Enable composite tests on cockroachdb


Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index cd59c495..47ed2448 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -13,10 +13,10 @@ jobs:
 
     strategy:
       matrix:
-        go-version: ["1.22", "1.23"]
-        pg-version: [13, 14, 15, 16, 17, cockroachdb]
+        go-version: ["1.21", "1.22"]
+        pg-version: [12, 13, 14, 15, 16, cockroachdb]
         include:
-          - pg-version: 13
+          - pg-version: 12
             pgx-test-database: "host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test"
             pgx-test-unix-socket-conn-string: "host=/var/run/postgresql dbname=pgx_test"
             pgx-test-tcp-conn-string: "host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test"
@@ -26,7 +26,7 @@ jobs:
             pgx-test-tls-conn-string: "host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test"
             pgx-ssl-password: certpw
             pgx-test-tls-client-conn-string: "host=localhost user=pgx_sslcert sslmode=verify-full sslrootcert=/tmp/ca.pem sslcert=/tmp/pgx_sslcert.crt sslkey=/tmp/pgx_sslcert.key dbname=pgx_test"
-          - pg-version: 14
+          - pg-version: 13
             pgx-test-database: "host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test"
             pgx-test-unix-socket-conn-string: "host=/var/run/postgresql dbname=pgx_test"
             pgx-test-tcp-conn-string: "host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test"
@@ -36,7 +36,7 @@ jobs:
             pgx-test-tls-conn-string: "host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test"
             pgx-ssl-password: certpw
             pgx-test-tls-client-conn-string: "host=localhost user=pgx_sslcert sslmode=verify-full sslrootcert=/tmp/ca.pem sslcert=/tmp/pgx_sslcert.crt sslkey=/tmp/pgx_sslcert.key dbname=pgx_test"
-          - pg-version: 15
+          - pg-version: 14
             pgx-test-database: "host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test"
             pgx-test-unix-socket-conn-string: "host=/var/run/postgresql dbname=pgx_test"
             pgx-test-tcp-conn-string: "host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test"
@@ -46,7 +46,7 @@ jobs:
             pgx-test-tls-conn-string: "host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test"
             pgx-ssl-password: certpw
             pgx-test-tls-client-conn-string: "host=localhost user=pgx_sslcert sslmode=verify-full sslrootcert=/tmp/ca.pem sslcert=/tmp/pgx_sslcert.crt sslkey=/tmp/pgx_sslcert.key dbname=pgx_test"
-          - pg-version: 16
+          - pg-version: 15
             pgx-test-database: "host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test"
             pgx-test-unix-socket-conn-string: "host=/var/run/postgresql dbname=pgx_test"
             pgx-test-tcp-conn-string: "host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test"
@@ -56,7 +56,7 @@ jobs:
             pgx-test-tls-conn-string: "host=localhost user=pgx_ssl password=secret sslmode=verify-full sslrootcert=/tmp/ca.pem dbname=pgx_test"
             pgx-ssl-password: certpw
             pgx-test-tls-client-conn-string: "host=localhost user=pgx_sslcert sslmode=verify-full sslrootcert=/tmp/ca.pem sslcert=/tmp/pgx_sslcert.crt sslkey=/tmp/pgx_sslcert.key dbname=pgx_test"
-          - pg-version: 17
+          - pg-version: 16
             pgx-test-database: "host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test"
             pgx-test-unix-socket-conn-string: "host=/var/run/postgresql dbname=pgx_test"
             pgx-test-tcp-conn-string: "host=127.0.0.1 user=pgx_md5 password=secret dbname=pgx_test"
@@ -125,7 +125,7 @@ jobs:
     runs-on: windows-latest
     strategy:
       matrix:
-        go-version: ["1.22", "1.23"]
+        go-version: ["1.21", "1.22"]
 
     steps:
       - name: Setup PostgreSQL
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6470088b..5f780fdc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,56 +1,3 @@
-# 5.7.2 (December 21, 2024)
-
-* Fix prepared statement already exists on batch prepare failure
-* Add commit query to tx options (Lucas Hild)
-* Fix pgtype.Timestamp json unmarshal (Shean de Montigny-Desautels)
-* Add message body size limits in frontend and backend (zene)
-* Add xid8 type
-* Ensure planning encodes and scans cannot infinitely recurse
-* Implement pgtype.UUID.String() (Konstantin Grachev)
-* Switch from ExecParams to Exec in ValidateConnectTargetSessionAttrs functions (Alexander Rumyantsev)
-* Update golang.org/x/crypto
-
-# 5.7.1 (September 10, 2024)
-
-* Fix data race in tracelog.TraceLog
-* Update puddle to v2.2.2. This removes the import of nanotime via linkname.
-* Update golang.org/x/crypto and golang.org/x/text
-
-# 5.7.0 (September 7, 2024)
-
-* Add support for sslrootcert=system (Yann Soubeyrand)
-* Add LoadTypes to load multiple types in a single SQL query (Nick Farrell)
-* Add XMLCodec supports encoding + scanning XML column type like json (nickcruess-soda)
-* Add MultiTrace (Stepan Rabotkin)
-* Add TraceLogConfig with customizable TimeKey (stringintech)
-* pgx.ErrNoRows wraps sql.ErrNoRows to aid in database/sql compatibility with native pgx functions (merlin)
-* Support scanning binary formatted uint32 into string / TextScanner (jennifersp)
-* Fix interval encoding to allow 0s and avoid extra spaces (Carlos Pérez-Aradros Herce)
-* Update pgservicefile - fixes panic when parsing invalid file
-* Better error message when reading past end of batch
-* Don't print url when url.Parse returns an error (Kevin Biju)
-* Fix snake case name normalization collision in RowToStructByName with db tag (nolandseigler)
-* Fix: Scan and encode types with underlying types of arrays
-
-# 5.6.0 (May 25, 2024)
-
-* Add StrictNamedArgs (Tomas Zahradnicek)
-* Add support for macaddr8 type (Carlos Pérez-Aradros Herce)
-* Add SeverityUnlocalized field to PgError / Notice
-* Performance optimization of RowToStructByPos/Name (Zach Olstein)
-* Allow customizing context canceled behavior for pgconn
-* Add ScanLocation to pgtype.Timestamp[tz]Codec
-* Add custom data to pgconn.PgConn
-* Fix ResultReader.Read() to handle nil values
-* Do not encode interval microseconds when they are 0 (Carlos Pérez-Aradros Herce)
-* pgconn.SafeToRetry checks for wrapped errors (tjasko)
-* Failed connection attempts include all errors
-* Optimize LargeObject.Read (Mitar)
-* Add tracing for connection acquire and release from pool (ngavinsir)
-* Fix encode driver.Valuer not called when nil
-* Add support for custom JSON marshal and unmarshal (Mitar)
-* Use Go default keepalive for TCP connections (Hans-Joachim Kliemeck)
-
 # 5.5.5 (March 9, 2024)
 
 Use spaces instead of parentheses for SQL sanitization.
diff --git a/README.md b/README.md
index d84434d8..49f2c3d7 100644
--- a/README.md
+++ b/README.md
@@ -84,7 +84,7 @@ It is also possible to use the `database/sql` interface and convert a connection
 
 ## Testing
 
-See [CONTRIBUTING.md](./CONTRIBUTING.md) for setup instructions.
+See CONTRIBUTING.md for setup instructions.
 
 ## Architecture
 
@@ -92,7 +92,7 @@ See the presentation at Golang Estonia, [PGX Top to Bottom](https://www.youtube.
 
 ## Supported Go and PostgreSQL Versions
 
-pgx supports the same versions of Go and PostgreSQL that are supported by their respective teams. For [Go](https://golang.org/doc/devel/release.html#policy) that is the two most recent major releases and for [PostgreSQL](https://www.postgresql.org/support/versioning/) the major releases in the last 5 years. This means pgx supports Go 1.22 and higher and PostgreSQL 13 and higher. pgx also is tested against the latest version of [CockroachDB](https://www.cockroachlabs.com/product/).
+pgx supports the same versions of Go and PostgreSQL that are supported by their respective teams. For [Go](https://golang.org/doc/devel/release.html#policy) that is the two most recent major releases and for [PostgreSQL](https://www.postgresql.org/support/versioning/) the major releases in the last 5 years. This means pgx supports Go 1.20 and higher and PostgreSQL 12 and higher. pgx also is tested against the latest version of [CockroachDB](https://www.cockroachlabs.com/product/).
 
 ## Version Policy
 
@@ -126,7 +126,7 @@ pgerrcode contains constants for the PostgreSQL error codes.
 
 ## Adapters for 3rd Party Tracers
 
-* [github.com/jackhopner/pgx-xray-tracer](https://github.com/jackhopner/pgx-xray-tracer)
+* [https://github.com/jackhopner/pgx-xray-tracer](https://github.com/jackhopner/pgx-xray-tracer)
 
 ## Adapters for 3rd Party Loggers
 
@@ -156,7 +156,7 @@ Library for scanning data from a database into Go structs and more.
 A carefully designed SQL client for making using SQL easier,
 more productive, and less error-prone on Golang.
 
-### [github.com/otan/gopgkrb5](https://github.com/otan/gopgkrb5)
+### [https://github.com/otan/gopgkrb5](https://github.com/otan/gopgkrb5)
 
 Adds GSSAPI / Kerberos authentication support.
 
@@ -169,10 +169,6 @@ Explicit data mapping and scanning library for Go structs and slices.
 Type safe and flexible package for scanning database data into Go types.
 Supports, structs, maps, slices and custom mapping functions.
 
-### [github.com/z0ne-dev/mgx](https://github.com/z0ne-dev/mgx)
+### [https://github.com/z0ne-dev/mgx](https://github.com/z0ne-dev/mgx)
 
 Code first migration library for native pgx (no database/sql abstraction).
-
-### [github.com/amirsalarsafaei/sqlc-pgx-monitoring](https://github.com/amirsalarsafaei/sqlc-pgx-monitoring)
-
-A database monitoring/metrics library for pgx and sqlc. Trace, log and monitor your sqlc query performance using OpenTelemetry.
diff --git a/Rakefile b/Rakefile
index 3e3aa503..d957573e 100644
--- a/Rakefile
+++ b/Rakefile
@@ -2,7 +2,7 @@ require "erb"
 
 rule '.go' => '.go.erb' do |task|
   erb = ERB.new(File.read(task.source))
-  File.write(task.name, "// Code generated from #{task.source}. DO NOT EDIT.\n\n" + erb.result(binding))
+  File.write(task.name, "// Do not edit. Generated from #{task.source}\n" + erb.result(binding))
   sh "goimports", "-w", task.name
 end
 
diff --git a/batch.go b/batch.go
index c3c2834f..3540f57f 100644
--- a/batch.go
+++ b/batch.go
@@ -60,13 +60,9 @@ type Batch struct {
 	QueuedQueries []*QueuedQuery
 }
 
-// Queue queues a query to batch b. query can be an SQL query or the name of a prepared statement. The only pgx option
-// argument that is supported is QueryRewriter. Queries are executed using the connection's DefaultQueryExecMode.
-//
-// While query can contain multiple statements if the connection's DefaultQueryExecMode is QueryModeSimple, this should
-// be avoided. QueuedQuery.Fn must not be set as it will only be called for the first query. That is, QueuedQuery.Query,
-// QueuedQuery.QueryRow, and QueuedQuery.Exec must not be called. In addition, any error messages or tracing that
-// include the current query may reference the wrong query.
+// Queue queues a query to batch b. query can be an SQL query or the name of a prepared statement.
+// The only pgx option argument that is supported is QueryRewriter. Queries are executed using the
+// connection's DefaultQueryExecMode.
 func (b *Batch) Queue(query string, arguments ...any) *QueuedQuery {
 	qq := &QueuedQuery{
 		SQL:       query,
@@ -132,7 +128,7 @@ func (br *batchResults) Exec() (pgconn.CommandTag, error) {
 	if !br.mrr.NextResult() {
 		err := br.mrr.Close()
 		if err == nil {
-			err = errors.New("no more results in batch")
+			err = errors.New("no result")
 		}
 		if br.conn.batchTracer != nil {
 			br.conn.batchTracer.TraceBatchQuery(br.ctx, br.conn, TraceBatchQueryData{
@@ -184,7 +180,7 @@ func (br *batchResults) Query() (Rows, error) {
 	if !br.mrr.NextResult() {
 		rows.err = br.mrr.Close()
 		if rows.err == nil {
-			rows.err = errors.New("no more results in batch")
+			rows.err = errors.New("no result")
 		}
 		rows.closed = true
 
@@ -291,10 +287,7 @@ func (br *pipelineBatchResults) Exec() (pgconn.CommandTag, error) {
 		return pgconn.CommandTag{}, br.err
 	}
 
-	query, arguments, err := br.nextQueryAndArgs()
-	if err != nil {
-		return pgconn.CommandTag{}, err
-	}
+	query, arguments, _ := br.nextQueryAndArgs()
 
 	results, err := br.pipeline.GetResults()
 	if err != nil {
@@ -337,9 +330,9 @@ func (br *pipelineBatchResults) Query() (Rows, error) {
 		return &baseRows{err: br.err, closed: true}, br.err
 	}
 
-	query, arguments, err := br.nextQueryAndArgs()
-	if err != nil {
-		return &baseRows{err: err, closed: true}, err
+	query, arguments, ok := br.nextQueryAndArgs()
+	if !ok {
+		query = "batch query"
 	}
 
 	rows := br.conn.getRows(br.ctx, query, arguments)
@@ -428,16 +421,13 @@ func (br *pipelineBatchResults) earlyError() error {
 	return br.err
 }
 
-func (br *pipelineBatchResults) nextQueryAndArgs() (query string, args []any, err error) {
-	if br.b == nil {
-		return "", nil, errors.New("no reference to batch")
-	}
-
-	if br.qqIdx >= len(br.b.QueuedQueries) {
-		return "", nil, errors.New("no more results in batch")
+func (br *pipelineBatchResults) nextQueryAndArgs() (query string, args []any, ok bool) {
+	if br.b != nil && br.qqIdx < len(br.b.QueuedQueries) {
+		bi := br.b.QueuedQueries[br.qqIdx]
+		query = bi.SQL
+		args = bi.Arguments
+		ok = true
+		br.qqIdx++
 	}
-
-	bi := br.b.QueuedQueries[br.qqIdx]
-	br.qqIdx++
-	return bi.SQL, bi.Arguments, nil
+	return
 }
diff --git a/conn.go b/conn.go
index 93e2e718..a9cb3163 100644
--- a/conn.go
+++ b/conn.go
@@ -3,7 +3,6 @@ package pgx
 import (
 	"context"
 	"crypto/sha256"
-	"database/sql"
 	"encoding/hex"
 	"errors"
 	"fmt"
@@ -11,6 +10,7 @@ import (
 	"strings"
 	"time"
 
+	"github.com/jackc/pgx/v5/internal/anynil"
 	"github.com/jackc/pgx/v5/internal/sanitize"
 	"github.com/jackc/pgx/v5/internal/stmtcache"
 	"github.com/jackc/pgx/v5/pgconn"
@@ -103,31 +103,13 @@ func (ident Identifier) Sanitize() string {
 
 var (
 	// ErrNoRows occurs when rows are expected but none are returned.
-	ErrNoRows = newProxyErr(sql.ErrNoRows, "no rows in result set")
+	ErrNoRows = errors.New("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")
-)
+var errDisabledStatementCache = fmt.Errorf("cannot use QueryExecModeCacheStatement with disabled statement cache")
+var errDisabledDescriptionCache = fmt.Errorf("cannot use QueryExecModeCacheDescribe with disabled description cache")
 
 // Connect establishes a connection with a PostgreSQL server with a connection string. See
 // pgconn.Connect for details.
@@ -420,7 +402,7 @@ func (c *Conn) IsClosed() bool {
 	return c.pgConn.IsClosed()
 }
 
-func (c *Conn) die() {
+func (c *Conn) die(err error) {
 	if c.IsClosed() {
 		return
 	}
@@ -588,6 +570,14 @@ func (c *Conn) execPrepared(ctx context.Context, sd *pgconn.StatementDescription
 	return result.CommandTag, result.Err
 }
 
+type unknownArgumentTypeQueryExecModeExecError struct {
+	arg any
+}
+
+func (e *unknownArgumentTypeQueryExecModeExecError) Error() string {
+	return fmt.Sprintf("cannot use unregistered type %T as query argument in QueryExecModeExec", e.arg)
+}
+
 func (c *Conn) execSQLParams(ctx context.Context, sql string, args []any) (pgconn.CommandTag, error) {
 	err := c.eqb.Build(c.typeMap, nil, args)
 	if err != nil {
@@ -642,33 +632,21 @@ const (
 	// registered with pgtype.Map.RegisterDefaultPgType. Queries will be rejected that have arguments that are
 	// unregistered or ambiguous. e.g. A map[string]string may have the PostgreSQL type json or hstore. Modes that know
 	// the PostgreSQL type can use a map[string]string directly as an argument. This mode cannot.
-	//
-	// On rare occasions user defined types may behave differently when encoded in the text format instead of the binary
-	// format. For example, this could happen if a "type RomanNumeral int32" implements fmt.Stringer to format integers as
-	// Roman numerals (e.g. 7 is VII). The binary format would properly encode the integer 7 as the binary value for 7.
-	// But the text format would encode the integer 7 as the string "VII". As QueryExecModeExec uses the text format, it
-	// is possible that changing query mode from another mode to QueryExecModeExec could change the behavior of the query.
-	// This should not occur with types pgx supports directly and can be avoided by registering the types with
-	// pgtype.Map.RegisterDefaultPgType and implementing the appropriate type interfaces. In the cas of RomanNumeral, it
-	// should implement pgtype.Int64Valuer.
 	QueryExecModeExec
 
-	// Use the simple protocol. Assume the PostgreSQL query parameter types based on the Go type of the arguments. This is
-	// especially significant for []byte values. []byte values are encoded as PostgreSQL bytea. string must be used
-	// instead for text type values including json and jsonb. Type mappings can be registered with
+	// Use the simple protocol. Assume the PostgreSQL query parameter types based on the Go type of the arguments.
+	// Queries are executed in a single round trip. Type mappings can be registered with
 	// pgtype.Map.RegisterDefaultPgType. Queries will be rejected that have arguments that are unregistered or ambiguous.
-	// e.g. A map[string]string may have the PostgreSQL type json or hstore. Modes that know the PostgreSQL type can use a
-	// map[string]string directly as an argument. This mode cannot. Queries are executed in a single round trip.
+	// e.g. A map[string]string may have the PostgreSQL type json or hstore. Modes that know the PostgreSQL type can use
+	// a map[string]string directly as an argument. This mode cannot.
 	//
-	// QueryExecModeSimpleProtocol should have the user application visible behavior as QueryExecModeExec. This includes
-	// the warning regarding differences in text format and binary format encoding with user defined types. There may be
-	// other minor exceptions such as behavior when multiple result returning queries are erroneously sent in a single
-	// string.
+	// QueryExecModeSimpleProtocol should have the user application visible behavior as QueryExecModeExec with minor
+	// exceptions such as behavior when multiple result returning queries are erroneously sent in a single string.
 	//
 	// QueryExecModeSimpleProtocol uses client side parameter interpolation. All values are quoted and escaped. Prefer
-	// QueryExecModeExec over QueryExecModeSimpleProtocol whenever possible. In general QueryExecModeSimpleProtocol should
-	// only be used if connecting to a proxy server, connection pool server, or non-PostgreSQL server that does not
-	// support the extended protocol.
+	// QueryExecModeExec over QueryExecModeSimpleProtocol whenever possible. In general QueryExecModeSimpleProtocol
+	// should only be used if connecting to a proxy server, connection pool server, or non-PostgreSQL server that does
+	// not support the extended protocol.
 	QueryExecModeSimpleProtocol
 )
 
@@ -777,6 +755,7 @@ optionLoop:
 	}
 
 	c.eqb.reset()
+	anynil.NormalizeSlice(args)
 	rows := c.getRows(ctx, sql, args)
 
 	var err error
@@ -866,6 +845,7 @@ func (c *Conn) getStatementDescription(
 	mode QueryExecMode,
 	sql string,
 ) (sd *pgconn.StatementDescription, err error) {
+
 	switch mode {
 	case QueryExecModeCacheStatement:
 		if c.statementCache == nil {
@@ -908,9 +888,6 @@ func (c *Conn) QueryRow(ctx context.Context, sql string, args ...any) Row {
 // SendBatch sends all queued queries to the server at once. All queries are run in an implicit transaction unless
 // explicit transaction control statements are executed. The returned BatchResults must be closed before the connection
 // is used again.
-//
-// Depending on the QueryExecMode, all queries may be prepared before any are executed. This means that creating a table
-// and using it in a subsequent query in the same batch can fail.
 func (c *Conn) SendBatch(ctx context.Context, b *Batch) (br BatchResults) {
 	if c.batchTracer != nil {
 		ctx = c.batchTracer.TraceBatchStart(ctx, c, TraceBatchStartData{Batch: b})
@@ -1133,64 +1110,47 @@ func (c *Conn) sendBatchExtendedWithDescription(ctx context.Context, b *Batch, d
 
 	// Prepare any needed queries
 	if len(distinctNewQueries) > 0 {
-		err := func() (err error) {
-			for _, sd := range distinctNewQueries {
-				pipeline.SendPrepare(sd.Name, sd.SQL, nil)
-			}
-
-			// Store all statements we are preparing into the cache. It's fine if it overflows because HandleInvalidated will
-			// clean them up later.
-			if sdCache != nil {
-				for _, sd := range distinctNewQueries {
-					sdCache.Put(sd)
-				}
-			}
-
-			// If something goes wrong preparing the statements, we need to invalidate the cache entries we just added.
-			defer func() {
-				if err != nil && sdCache != nil {
-					for _, sd := range distinctNewQueries {
-						sdCache.Invalidate(sd.SQL)
-					}
-				}
-			}()
-
-			err = pipeline.Sync()
-			if err != nil {
-				return err
-			}
-
-			for _, sd := range distinctNewQueries {
-				results, err := pipeline.GetResults()
-				if err != nil {
-					return err
-				}
-
-				resultSD, ok := results.(*pgconn.StatementDescription)
-				if !ok {
-					return fmt.Errorf("expected statement description, got %T", results)
-				}
+		for _, sd := range distinctNewQueries {
+			pipeline.SendPrepare(sd.Name, sd.SQL, nil)
+		}
 
-				// Fill in the previously empty / pending statement descriptions.
-				sd.ParamOIDs = resultSD.ParamOIDs
-				sd.Fields = resultSD.Fields
-			}
+		err := pipeline.Sync()
+		if err != nil {
+			return &pipelineBatchResults{ctx: ctx, conn: c, err: err, closed: true}
+		}
 
+		for _, sd := range distinctNewQueries {
 			results, err := pipeline.GetResults()
 			if err != nil {
-				return err
+				return &pipelineBatchResults{ctx: ctx, conn: c, err: err, closed: true}
 			}
 
-			_, ok := results.(*pgconn.PipelineSync)
+			resultSD, ok := results.(*pgconn.StatementDescription)
 			if !ok {
-				return fmt.Errorf("expected sync, got %T", results)
+				return &pipelineBatchResults{ctx: ctx, conn: c, err: fmt.Errorf("expected statement description, got %T", results), closed: true}
 			}
 
-			return nil
-		}()
+			// Fill in the previously empty / pending statement descriptions.
+			sd.ParamOIDs = resultSD.ParamOIDs
+			sd.Fields = resultSD.Fields
+		}
+
+		results, err := pipeline.GetResults()
 		if err != nil {
 			return &pipelineBatchResults{ctx: ctx, conn: c, err: err, closed: true}
 		}
+
+		_, ok := results.(*pgconn.PipelineSync)
+		if !ok {
+			return &pipelineBatchResults{ctx: ctx, conn: c, err: fmt.Errorf("expected sync, got %T", results), closed: true}
+		}
+	}
+
+	// Put all statements into the cache. It's fine if it overflows because HandleInvalidated will clean them up later.
+	if sdCache != nil {
+		for _, sd := range distinctNewQueries {
+			sdCache.Put(sd)
+		}
 	}
 
 	// Queue the queries.
diff --git a/derived_types.go b/derived_types.go
deleted file mode 100644
index 72c0a242..00000000
--- a/derived_types.go
+++ /dev/null
@@ -1,256 +0,0 @@
-package pgx
-
-import (
-	"context"
-	"fmt"
-	"regexp"
-	"strconv"
-	"strings"
-
-	"github.com/jackc/pgx/v5/pgtype"
-)
-
-/*
-buildLoadDerivedTypesSQL generates the correct query for retrieving type information.
-
-	pgVersion: the major version of the PostgreSQL server
-	typeNames: the names of the types to load. If nil, load all types.
-*/
-func buildLoadDerivedTypesSQL(pgVersion int64, typeNames []string) string {
-	supportsMultirange := (pgVersion >= 14)
-	var typeNamesClause string
-
-	if typeNames == nil {
-		// This should not occur; this will not return any types
-		typeNamesClause = "= ''"
-	} else {
-		typeNamesClause = "= ANY($1)"
-	}
-	parts := make([]string, 0, 10)
-
-	// Each of the type names provided might be found in pg_class or pg_type.
-	// Additionally, it may or may not include a schema portion.
-	parts = append(parts, `
-WITH RECURSIVE
--- find the OIDs in pg_class which match one of the provided type names
-selected_classes(oid,reltype) AS (
-    -- this query uses the namespace search path, so will match type names without a schema prefix
-    SELECT pg_class.oid, pg_class.reltype
-    FROM pg_catalog.pg_class
-        LEFT JOIN pg_catalog.pg_namespace n ON n.oid = pg_class.relnamespace
-    WHERE pg_catalog.pg_table_is_visible(pg_class.oid)
-      AND relname `, typeNamesClause, `
-UNION ALL
-    -- this query will only match type names which include the schema prefix
-    SELECT pg_class.oid, pg_class.reltype
-    FROM pg_class
-    INNER JOIN pg_namespace ON (pg_class.relnamespace = pg_namespace.oid)
-    WHERE nspname || '.' || relname `, typeNamesClause, `
-),
-selected_types(oid) AS (
-    -- collect the OIDs from pg_types which correspond to the selected classes
-    SELECT reltype AS oid
-    FROM selected_classes
-UNION ALL
-    -- as well as any other type names which match our criteria
-    SELECT pg_type.oid
-    FROM pg_type
-    LEFT OUTER JOIN pg_namespace ON (pg_type.typnamespace = pg_namespace.oid)
-    WHERE typname `, typeNamesClause, `
-        OR nspname || '.' || typname `, typeNamesClause, `
-),
--- this builds a parent/child mapping of objects, allowing us to know
--- all the child (ie: dependent) types that a parent (type) requires
--- As can be seen, there are 3 ways this can occur (the last of which
--- is due to being a composite class, where the composite fields are children)
-pc(parent, child) AS (
-    SELECT parent.oid, parent.typelem
-    FROM pg_type parent
-    WHERE parent.typtype = 'b' AND parent.typelem != 0
-UNION ALL
-    SELECT parent.oid, parent.typbasetype
-    FROM pg_type parent
-    WHERE parent.typtypmod = -1 AND parent.typbasetype != 0
-UNION ALL
-    SELECT pg_type.oid, atttypid
-    FROM pg_attribute
-    INNER JOIN pg_class ON (pg_class.oid = pg_attribute.attrelid)
-    INNER JOIN pg_type ON (pg_type.oid = pg_class.reltype)
-    WHERE NOT attisdropped
-      AND attnum > 0
-),
--- Now construct a recursive query which includes a 'depth' element.
--- This is used to ensure that the "youngest" children are registered before
--- their parents.
-relationships(parent, child, depth) AS (
-    SELECT DISTINCT 0::OID, selected_types.oid, 0
-    FROM selected_types
-UNION ALL
-    SELECT pg_type.oid AS parent, pg_attribute.atttypid AS child, 1
-    FROM selected_classes c
-    inner join pg_type ON (c.reltype = pg_type.oid)
-    inner join pg_attribute on (c.oid = pg_attribute.attrelid)
-UNION ALL
-    SELECT pc.parent, pc.child, relationships.depth + 1
-    FROM pc
-    INNER JOIN relationships ON (pc.parent = relationships.child)
-),
--- composite fields need to be encapsulated as a couple of arrays to provide the required information for registration
-composite AS (
-    SELECT pg_type.oid, ARRAY_AGG(attname ORDER BY attnum) AS attnames, ARRAY_AGG(atttypid ORDER BY ATTNUM) AS atttypids
-    FROM pg_attribute
-    INNER JOIN pg_class ON (pg_class.oid = pg_attribute.attrelid)
-    INNER JOIN pg_type ON (pg_type.oid = pg_class.reltype)
-    WHERE NOT attisdropped
-      AND attnum > 0
-    GROUP BY pg_type.oid
-)
--- Bring together this information, showing all the information which might possibly be required
--- to complete the registration, applying filters to only show the items which relate to the selected
--- types/classes.
-SELECT typname,
-       pg_namespace.nspname,
-       typtype,
-       typbasetype,
-       typelem,
-       pg_type.oid,`)
-	if supportsMultirange {
-		parts = append(parts, `
-       COALESCE(multirange.rngtypid, 0) AS rngtypid,`)
-	} else {
-		parts = append(parts, `
-       0 AS rngtypid,`)
-	}
-	parts = append(parts, `
-       COALESCE(pg_range.rngsubtype, 0) AS rngsubtype,
-       attnames, atttypids
-    FROM relationships
-    INNER JOIN pg_type ON (pg_type.oid = relationships.child)
-    LEFT OUTER JOIN pg_range ON (pg_type.oid = pg_range.rngtypid)`)
-	if supportsMultirange {
-		parts = append(parts, `
-    LEFT OUTER JOIN pg_range multirange ON (pg_type.oid = multirange.rngmultitypid)`)
-	}
-
-	parts = append(parts, `
-    LEFT OUTER JOIN composite USING (oid)
-    LEFT OUTER JOIN pg_namespace ON (pg_type.typnamespace = pg_namespace.oid)
-    WHERE NOT (typtype = 'b' AND typelem = 0)`)
-	parts = append(parts, `
-    GROUP BY typname, pg_namespace.nspname, typtype, typbasetype, typelem, pg_type.oid, pg_range.rngsubtype,`)
-	if supportsMultirange {
-		parts = append(parts, `
-        multirange.rngtypid,`)
-	}
-	parts = append(parts, `
-        attnames, atttypids
-    ORDER BY MAX(depth) desc, typname;`)
-	return strings.Join(parts, "")
-}
-
-type derivedTypeInfo struct {
-	Oid, Typbasetype, Typelem, Rngsubtype, Rngtypid uint32
-	TypeName, Typtype, NspName                      string
-	Attnames                                        []string
-	Atttypids                                       []uint32
-}
-
-// LoadTypes performs a single (complex) query, returning all the required
-// information to register the named types, as well as any other types directly
-// or indirectly required to complete the registration.
-// The result of this call can be passed into RegisterTypes to complete the process.
-func (c *Conn) LoadTypes(ctx context.Context, typeNames []string) ([]*pgtype.Type, error) {
-	m := c.TypeMap()
-	if len(typeNames) == 0 {
-		return nil, fmt.Errorf("No type names were supplied.")
-	}
-
-	// Disregard server version errors. This will result in
-	// the SQL not support recent structures such as multirange
-	serverVersion, _ := serverVersion(c)
-	sql := buildLoadDerivedTypesSQL(serverVersion, typeNames)
-	rows, err := c.Query(ctx, sql, QueryExecModeSimpleProtocol, typeNames)
-	if err != nil {
-		return nil, fmt.Errorf("While generating load types query: %w", err)
-	}
-	defer rows.Close()
-	result := make([]*pgtype.Type, 0, 100)
-	for rows.Next() {
-		ti := derivedTypeInfo{}
-		err = rows.Scan(&ti.TypeName, &ti.NspName, &ti.Typtype, &ti.Typbasetype, &ti.Typelem, &ti.Oid, &ti.Rngtypid, &ti.Rngsubtype, &ti.Attnames, &ti.Atttypids)
-		if err != nil {
-			return nil, fmt.Errorf("While scanning type information: %w", err)
-		}
-		var type_ *pgtype.Type
-		switch ti.Typtype {
-		case "b": // array
-			dt, ok := m.TypeForOID(ti.Typelem)
-			if !ok {
-				return nil, fmt.Errorf("Array element OID %v not registered while loading pgtype %q", ti.Typelem, ti.TypeName)
-			}
-			type_ = &pgtype.Type{Name: ti.TypeName, OID: ti.Oid, Codec: &pgtype.ArrayCodec{ElementType: dt}}
-		case "c": // composite
-			var fields []pgtype.CompositeCodecField
-			for i, fieldName := range ti.Attnames {
-				dt, ok := m.TypeForOID(ti.Atttypids[i])
-				if !ok {
-					return nil, fmt.Errorf("Unknown field for composite type %q:  field %q (OID %v) is not already registered.", ti.TypeName, fieldName, ti.Atttypids[i])
-				}
-				fields = append(fields, pgtype.CompositeCodecField{Name: fieldName, Type: dt})
-			}
-
-			type_ = &pgtype.Type{Name: ti.TypeName, OID: ti.Oid, Codec: &pgtype.CompositeCodec{Fields: fields}}
-		case "d": // domain
-			dt, ok := m.TypeForOID(ti.Typbasetype)
-			if !ok {
-				return nil, fmt.Errorf("Domain base type OID %v was not already registered, needed for %q", ti.Typbasetype, ti.TypeName)
-			}
-
-			type_ = &pgtype.Type{Name: ti.TypeName, OID: ti.Oid, Codec: dt.Codec}
-		case "e": // enum
-			type_ = &pgtype.Type{Name: ti.TypeName, OID: ti.Oid, Codec: &pgtype.EnumCodec{}}
-		case "r": // range
-			dt, ok := m.TypeForOID(ti.Rngsubtype)
-			if !ok {
-				return nil, fmt.Errorf("Range element OID %v was not already registered, needed for %q", ti.Rngsubtype, ti.TypeName)
-			}
-
-			type_ = &pgtype.Type{Name: ti.TypeName, OID: ti.Oid, Codec: &pgtype.RangeCodec{ElementType: dt}}
-		case "m": // multirange
-			dt, ok := m.TypeForOID(ti.Rngtypid)
-			if !ok {
-				return nil, fmt.Errorf("Multirange element OID %v was not already registered, needed for %q", ti.Rngtypid, ti.TypeName)
-			}
-
-			type_ = &pgtype.Type{Name: ti.TypeName, OID: ti.Oid, Codec: &pgtype.MultirangeCodec{ElementType: dt}}
-		default:
-			return nil, fmt.Errorf("Unknown typtype %q was found while registering %q", ti.Typtype, ti.TypeName)
-		}
-
-		// the type_ is imposible to be null
-		m.RegisterType(type_)
-		if ti.NspName != "" {
-			nspType := &pgtype.Type{Name: ti.NspName + "." + type_.Name, OID: type_.OID, Codec: type_.Codec}
-			m.RegisterType(nspType)
-			result = append(result, nspType)
-		}
-		result = append(result, type_)
-	}
-	return result, nil
-}
-
-// serverVersion returns the postgresql server version.
-func serverVersion(c *Conn) (int64, error) {
-	serverVersionStr := c.PgConn().ParameterStatus("server_version")
-	serverVersionStr = regexp.MustCompile(`^[0-9]+`).FindString(serverVersionStr)
-	// if not PostgreSQL do nothing
-	if serverVersionStr == "" {
-		return 0, fmt.Errorf("Cannot identify server version in %q", serverVersionStr)
-	}
-
-	version, err := strconv.ParseInt(serverVersionStr, 10, 64)
-	if err != nil {
-		return 0, fmt.Errorf("postgres version parsing failed: %w", err)
-	}
-	return version, nil
-}
diff --git a/doc.go b/doc.go
index 0e91d64e..bc0391dd 100644
--- a/doc.go
+++ b/doc.go
@@ -175,7 +175,7 @@ notification is received or the context is canceled.
 
 Tracing and Logging
 
-pgx supports tracing by setting ConnConfig.Tracer. To combine several tracers you can use the multitracer.Tracer.
+pgx supports tracing by setting ConnConfig.Tracer.
 
 In addition, the tracelog package provides the TraceLog type which lets a traditional logger act as a Tracer.
 
diff --git a/extended_query_builder.go b/extended_query_builder.go
index 526b0e95..9c9de5b2 100644
--- a/extended_query_builder.go
+++ b/extended_query_builder.go
@@ -1,8 +1,10 @@
 package pgx
 
 import (
+	"database/sql/driver"
 	"fmt"
 
+	"github.com/jackc/pgx/v5/internal/anynil"
 	"github.com/jackc/pgx/v5/pgconn"
 	"github.com/jackc/pgx/v5/pgtype"
 )
@@ -21,15 +23,10 @@ type ExtendedQueryBuilder struct {
 func (eqb *ExtendedQueryBuilder) Build(m *pgtype.Map, sd *pgconn.StatementDescription, args []any) error {
 	eqb.reset()
 
+	anynil.NormalizeSlice(args)
+
 	if sd == nil {
-		for i := range args {
-			err := eqb.appendParam(m, 0, pgtype.TextFormatCode, args[i])
-			if err != nil {
-				err = fmt.Errorf("failed to encode args[%d]: %w", i, err)
-				return err
-			}
-		}
-		return nil
+		return eqb.appendParamsForQueryExecModeExec(m, args)
 	}
 
 	if len(sd.ParamOIDs) != len(args) {
@@ -116,6 +113,10 @@ func (eqb *ExtendedQueryBuilder) reset() {
 }
 
 func (eqb *ExtendedQueryBuilder) encodeExtendedParamValue(m *pgtype.Map, oid uint32, formatCode int16, arg any) ([]byte, error) {
+	if anynil.Is(arg) {
+		return nil, nil
+	}
+
 	if eqb.paramValueBytes == nil {
 		eqb.paramValueBytes = make([]byte, 0, 128)
 	}
@@ -144,3 +145,74 @@ func (eqb *ExtendedQueryBuilder) chooseParameterFormatCode(m *pgtype.Map, oid ui
 
 	return m.FormatCodeForOID(oid)
 }
+
+// appendParamsForQueryExecModeExec appends the args to eqb.
+//
+// Parameters must be encoded in the text format because of differences in type conversion between timestamps and
+// dates. In QueryExecModeExec we don't know what the actual PostgreSQL type is. To determine the type we use the
+// Go type to OID type mapping registered by RegisterDefaultPgType. However, the Go time.Time represents both
+// PostgreSQL timestamp[tz] and date. To use the binary format we would need to also specify what the PostgreSQL
+// type OID is. But that would mean telling PostgreSQL that we have sent a timestamp[tz] when what is needed is a date.
+// This means that the value is converted from text to timestamp[tz] to date. This means it does a time zone conversion
+// before converting it to date. This means that dates can be shifted by one day. In text format without that double
+// type conversion it takes the date directly and ignores time zone (i.e. it works).
+//
+// Given that the whole point of QueryExecModeExec is to operate without having to know the PostgreSQL types there is
+// no way to safely use binary or to specify the parameter OIDs.
+func (eqb *ExtendedQueryBuilder) appendParamsForQueryExecModeExec(m *pgtype.Map, args []any) error {
+	for _, arg := range args {
+		if arg == nil {
+			err := eqb.appendParam(m, 0, TextFormatCode, arg)
+			if err != nil {
+				return err
+			}
+		} else {
+			dt, ok := m.TypeForValue(arg)
+			if !ok {
+				var tv pgtype.TextValuer
+				if tv, ok = arg.(pgtype.TextValuer); ok {
+					t, err := tv.TextValue()
+					if err != nil {
+						return err
+					}
+
+					dt, ok = m.TypeForOID(pgtype.TextOID)
+					if ok {
+						arg = t
+					}
+				}
+			}
+			if !ok {
+				var dv driver.Valuer
+				if dv, ok = arg.(driver.Valuer); ok {
+					v, err := dv.Value()
+					if err != nil {
+						return err
+					}
+					dt, ok = m.TypeForValue(v)
+					if ok {
+						arg = v
+					}
+				}
+			}
+			if !ok {
+				var str fmt.Stringer
+				if str, ok = arg.(fmt.Stringer); ok {
+					dt, ok = m.TypeForOID(pgtype.TextOID)
+					if ok {
+						arg = str.String()
+					}
+				}
+			}
+			if !ok {
+				return &unknownArgumentTypeQueryExecModeExecError{arg: arg}
+			}
+			err := eqb.appendParam(m, dt.OID, TextFormatCode, arg)
+			if err != nil {
+				return err
+			}
+		}
+	}
+
+	return nil
+}
diff --git a/go.mod b/go.mod
index 0378b800..c8430a41 100644
--- a/go.mod
+++ b/go.mod
@@ -1,21 +1,21 @@
 module github.com/jackc/pgx/v5
 
-go 1.21
+go 1.19
 
 require (
 	github.com/jackc/pgpassfile v1.0.0
-	github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761
-	github.com/jackc/puddle/v2 v2.2.2
+	github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a
+	github.com/jackc/puddle/v2 v2.2.1
 	github.com/stretchr/testify v1.8.1
-	golang.org/x/crypto v0.31.0
-	golang.org/x/sync v0.10.0
-	golang.org/x/text v0.21.0
+	golang.org/x/crypto v0.17.0
+	golang.org/x/text v0.14.0
 )
 
 require (
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/kr/pretty v0.3.0 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
+	golang.org/x/sync v0.1.0 // indirect
 	gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 )
diff --git a/go.sum b/go.sum
index b81b2196..1b39c053 100644
--- a/go.sum
+++ b/go.sum
@@ -4,10 +4,10 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
 github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
-github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
-github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
-github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
-github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
+github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
+github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
+github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
+github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
 github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
@@ -29,12 +29,12 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
 github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
-golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
-golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
-golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
-golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
-golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
+golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
+golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
+golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
+golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
diff --git a/internal/anynil/anynil.go b/internal/anynil/anynil.go
new file mode 100644
index 00000000..9a48c1a8
--- /dev/null
+++ b/internal/anynil/anynil.go
@@ -0,0 +1,36 @@
+package anynil
+
+import "reflect"
+
+// Is returns true if value is any type of nil. e.g. nil or []byte(nil).
+func Is(value any) bool {
+	if value == nil {
+		return true
+	}
+
+	refVal := reflect.ValueOf(value)
+	switch refVal.Kind() {
+	case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Interface, reflect.Slice:
+		return refVal.IsNil()
+	default:
+		return false
+	}
+}
+
+// Normalize converts typed nils (e.g. []byte(nil)) into untyped nil. Other values are returned unmodified.
+func Normalize(v any) any {
+	if Is(v) {
+		return nil
+	}
+	return v
+}
+
+// NormalizeSlice converts all typed nils (e.g. []byte(nil)) in s into untyped nils. Other values are unmodified. s is
+// mutated in place.
+func NormalizeSlice(s []any) {
+	for i := range s {
+		if Is(s[i]) {
+			s[i] = nil
+		}
+	}
+}
diff --git a/internal/sanitize/benchmmark.sh b/internal/sanitize/benchmmark.sh
deleted file mode 100644
index ec0f7b03..00000000
--- a/internal/sanitize/benchmmark.sh
+++ /dev/null
@@ -1,60 +0,0 @@
-#!/usr/bin/env bash
-
-current_branch=$(git rev-parse --abbrev-ref HEAD)
-if [ "$current_branch" == "HEAD" ]; then
-    current_branch=$(git rev-parse HEAD)
-fi
-
-restore_branch() {
-    echo "Restoring original branch/commit: $current_branch"
-    git checkout "$current_branch"
-}
-trap restore_branch EXIT
-
-# Check if there are uncommitted changes
-if ! git diff --quiet || ! git diff --cached --quiet; then
-    echo "There are uncommitted changes. Please commit or stash them before running this script."
-    exit 1
-fi
-
-# Ensure that at least one commit argument is passed
-if [ "$#" -lt 1 ]; then
-    echo "Usage: $0 <commit1> <commit2> ... <commitN>"
-    exit 1
-fi
-
-commits=("$@")
-benchmarks_dir=benchmarks
-
-if ! mkdir -p "${benchmarks_dir}"; then
-    echo "Unable to create dir for benchmarks data"
-    exit 1
-fi
-
-# Benchmark results
-bench_files=()
-
-# Run benchmark for each listed commit
-for i in "${!commits[@]}"; do
-    commit="${commits[i]}"
-    git checkout "$commit" || {
-        echo "Failed to checkout $commit"
-        exit 1
-    }
-
-    # Sanitized commmit message
-    commit_message=$(git log -1 --pretty=format:"%s" | tr -c '[:alnum:]-_' '_')
-
-    # Benchmark data will go there
-    bench_file="${benchmarks_dir}/${i}_${commit_message}.bench"
-
-    if ! go test -bench=. -count=10 >"$bench_file"; then
-        echo "Benchmarking failed for commit $commit"
-        exit 1
-    fi
-
-    bench_files+=("$bench_file")
-done
-
-# go install golang.org/x/perf/cmd/benchstat[@latest]
-benchstat "${bench_files[@]}"
diff --git a/internal/sanitize/sanitize.go b/internal/sanitize/sanitize.go
index b516817c..df58c448 100644
--- a/internal/sanitize/sanitize.go
+++ b/internal/sanitize/sanitize.go
@@ -4,10 +4,8 @@ import (
 	"bytes"
 	"encoding/hex"
 	"fmt"
-	"slices"
 	"strconv"
 	"strings"
-	"sync"
 	"time"
 	"unicode/utf8"
 )
@@ -26,33 +24,18 @@ type Query struct {
 // https://github.com/jackc/pgx/issues/1380
 const replacementcharacterwidth = 3
 
-const maxBufSize = 16384 // 16 Ki
-
-var bufPool = &pool[*bytes.Buffer]{
-	new: func() *bytes.Buffer {
-		return &bytes.Buffer{}
-	},
-	reset: func(b *bytes.Buffer) bool {
-		n := b.Len()
-		b.Reset()
-		return n < maxBufSize
-	},
-}
-
-var null = []byte("null")
-
 func (q *Query) Sanitize(args ...any) (string, error) {
 	argUse := make([]bool, len(args))
-	buf := bufPool.get()
-	defer bufPool.put(buf)
+	buf := &bytes.Buffer{}
 
 	for _, part := range q.Parts {
+		var str string
 		switch part := part.(type) {
 		case string:
-			buf.WriteString(part)
+			str = part
 		case int:
 			argIdx := part - 1
-			var p []byte
+
 			if argIdx < 0 {
 				return "", fmt.Errorf("first sql argument must be > 0")
 			}
@@ -60,41 +43,34 @@ func (q *Query) Sanitize(args ...any) (string, error) {
 			if argIdx >= len(args) {
 				return "", fmt.Errorf("insufficient arguments")
 			}
-
-			// Prevent SQL injection via Line Comment Creation
-			// https://github.com/jackc/pgx/security/advisories/GHSA-m7wr-2xf7-cm9p
-			buf.WriteByte(' ')
-
 			arg := args[argIdx]
 			switch arg := arg.(type) {
 			case nil:
-				p = null
+				str = "null"
 			case int64:
-				p = strconv.AppendInt(buf.AvailableBuffer(), arg, 10)
+				str = strconv.FormatInt(arg, 10)
 			case float64:
-				p = strconv.AppendFloat(buf.AvailableBuffer(), arg, 'f', -1, 64)
+				str = strconv.FormatFloat(arg, 'f', -1, 64)
 			case bool:
-				p = strconv.AppendBool(buf.AvailableBuffer(), arg)
+				str = strconv.FormatBool(arg)
 			case []byte:
-				p = QuoteBytes(buf.AvailableBuffer(), arg)
+				str = QuoteBytes(arg)
 			case string:
-				p = QuoteString(buf.AvailableBuffer(), arg)
+				str = QuoteString(arg)
 			case time.Time:
-				p = arg.Truncate(time.Microsecond).
-					AppendFormat(buf.AvailableBuffer(), "'2006-01-02 15:04:05.999999999Z07:00:00'")
+				str = arg.Truncate(time.Microsecond).Format("'2006-01-02 15:04:05.999999999Z07:00:00'")
 			default:
 				return "", fmt.Errorf("invalid arg type: %T", arg)
 			}
 			argUse[argIdx] = true
 
-			buf.Write(p)
-
 			// Prevent SQL injection via Line Comment Creation
 			// https://github.com/jackc/pgx/security/advisories/GHSA-m7wr-2xf7-cm9p
-			buf.WriteByte(' ')
+			str = " " + str + " "
 		default:
 			return "", fmt.Errorf("invalid Part type: %T", part)
 		}
+		buf.WriteString(str)
 	}
 
 	for i, used := range argUse {
@@ -106,99 +82,26 @@ func (q *Query) Sanitize(args ...any) (string, error) {
 }
 
 func NewQuery(sql string) (*Query, error) {
-	query := &Query{}
-	query.init(sql)
-
-	return query, nil
-}
-
-var sqlLexerPool = &pool[*sqlLexer]{
-	new: func() *sqlLexer {
-		return &sqlLexer{}
-	},
-	reset: func(sl *sqlLexer) bool {
-		*sl = sqlLexer{}
-		return true
-	},
-}
-
-func (q *Query) init(sql string) {
-	parts := q.Parts[:0]
-	if parts == nil {
-		// dirty, but fast heuristic to preallocate for ~90% usecases
-		n := strings.Count(sql, "$") + strings.Count(sql, "--") + 1
-		parts = make([]Part, 0, n)
+	l := &sqlLexer{
+		src:     sql,
+		stateFn: rawState,
 	}
 
-	l := sqlLexerPool.get()
-	defer sqlLexerPool.put(l)
-
-	l.src = sql
-	l.stateFn = rawState
-	l.parts = parts
-
 	for l.stateFn != nil {
 		l.stateFn = l.stateFn(l)
 	}
 
-	q.Parts = l.parts
-}
-
-func QuoteString(dst []byte, str string) []byte {
-	const quote = '\''
-
-	// Preallocate space for the worst case scenario
-	dst = slices.Grow(dst, len(str)*2+2)
-
-	// Add opening quote
-	dst = append(dst, quote)
-
-	// Iterate through the string without allocating
-	for i := 0; i < len(str); i++ {
-		if str[i] == quote {
-			dst = append(dst, quote, quote)
-		} else {
-			dst = append(dst, str[i])
-		}
-	}
+	query := &Query{Parts: l.parts}
 
-	// Add closing quote
-	dst = append(dst, quote)
-
-	return dst
+	return query, nil
 }
 
-func QuoteBytes(dst, buf []byte) []byte {
-	if len(buf) == 0 {
-		return append(dst, `'\x'`...)
-	}
-
-	// Calculate required length
-	requiredLen := 3 + hex.EncodedLen(len(buf)) + 1
-
-	// Ensure dst has enough capacity
-	if cap(dst)-len(dst) < requiredLen {
-		newDst := make([]byte, len(dst), len(dst)+requiredLen)
-		copy(newDst, dst)
-		dst = newDst
-	}
-
-	// Record original length and extend slice
-	origLen := len(dst)
-	dst = dst[:origLen+requiredLen]
-
-	// Add prefix
-	dst[origLen] = '\''
-	dst[origLen+1] = '\\'
-	dst[origLen+2] = 'x'
-
-	// Encode bytes directly into dst
-	hex.Encode(dst[origLen+3:len(dst)-1], buf)
-
-	// Add suffix
-	dst[len(dst)-1] = '\''
+func QuoteString(str string) string {
+	return "'" + strings.ReplaceAll(str, "'", "''") + "'"
+}
 
-	return dst
+func QuoteBytes(buf []byte) string {
+	return `'\x` + hex.EncodeToString(buf) + "'"
 }
 
 type sqlLexer struct {
@@ -416,45 +319,13 @@ func multilineCommentState(l *sqlLexer) stateFn {
 	}
 }
 
-var queryPool = &pool[*Query]{
-	new: func() *Query {
-		return &Query{}
-	},
-	reset: func(q *Query) bool {
-		n := len(q.Parts)
-		q.Parts = q.Parts[:0]
-		return n < 64 // drop too large queries
-	},
-}
-
 // SanitizeSQL replaces placeholder values with args. It quotes and escapes args
 // as necessary. This function is only safe when standard_conforming_strings is
 // on.
 func SanitizeSQL(sql string, args ...any) (string, error) {
-	query := queryPool.get()
-	query.init(sql)
-	defer queryPool.put(query)
-
-	return query.Sanitize(args...)
-}
-
-type pool[E any] struct {
-	p     sync.Pool
-	new   func() E
-	reset func(E) bool
-}
-
-func (pool *pool[E]) get() E {
-	v, ok := pool.p.Get().(E)
-	if !ok {
-		v = pool.new()
-	}
-
-	return v
-}
-
-func (p *pool[E]) put(v E) {
-	if p.reset(v) {
-		p.p.Put(v)
+	query, err := NewQuery(sql)
+	if err != nil {
+		return "", err
 	}
+	return query.Sanitize(args...)
 }
diff --git a/multitracer/tracer.go b/multitracer/tracer.go
deleted file mode 100644
index acff1739..00000000
--- a/multitracer/tracer.go
+++ /dev/null
@@ -1,152 +0,0 @@
-// Package multitracer provides a Tracer that can combine several tracers into one.
-package multitracer
-
-import (
-	"context"
-
-	"github.com/jackc/pgx/v5"
-	"github.com/jackc/pgx/v5/pgxpool"
-)
-
-// Tracer can combine several tracers into one.
-// You can use New to automatically split tracers by interface.
-type Tracer struct {
-	QueryTracers       []pgx.QueryTracer
-	BatchTracers       []pgx.BatchTracer
-	CopyFromTracers    []pgx.CopyFromTracer
-	PrepareTracers     []pgx.PrepareTracer
-	ConnectTracers     []pgx.ConnectTracer
-	PoolAcquireTracers []pgxpool.AcquireTracer
-	PoolReleaseTracers []pgxpool.ReleaseTracer
-}
-
-// New returns new Tracer from tracers with automatically split tracers by interface.
-func New(tracers ...pgx.QueryTracer) *Tracer {
-	var t Tracer
-
-	for _, tracer := range tracers {
-		t.QueryTracers = append(t.QueryTracers, tracer)
-
-		if batchTracer, ok := tracer.(pgx.BatchTracer); ok {
-			t.BatchTracers = append(t.BatchTracers, batchTracer)
-		}
-
-		if copyFromTracer, ok := tracer.(pgx.CopyFromTracer); ok {
-			t.CopyFromTracers = append(t.CopyFromTracers, copyFromTracer)
-		}
-
-		if prepareTracer, ok := tracer.(pgx.PrepareTracer); ok {
-			t.PrepareTracers = append(t.PrepareTracers, prepareTracer)
-		}
-
-		if connectTracer, ok := tracer.(pgx.ConnectTracer); ok {
-			t.ConnectTracers = append(t.ConnectTracers, connectTracer)
-		}
-
-		if poolAcquireTracer, ok := tracer.(pgxpool.AcquireTracer); ok {
-			t.PoolAcquireTracers = append(t.PoolAcquireTracers, poolAcquireTracer)
-		}
-
-		if poolReleaseTracer, ok := tracer.(pgxpool.ReleaseTracer); ok {
-			t.PoolReleaseTracers = append(t.PoolReleaseTracers, poolReleaseTracer)
-		}
-	}
-
-	return &t
-}
-
-func (t *Tracer) TraceQueryStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryStartData) context.Context {
-	for _, tracer := range t.QueryTracers {
-		ctx = tracer.TraceQueryStart(ctx, conn, data)
-	}
-
-	return ctx
-}
-
-func (t *Tracer) TraceQueryEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryEndData) {
-	for _, tracer := range t.QueryTracers {
-		tracer.TraceQueryEnd(ctx, conn, data)
-	}
-}
-
-func (t *Tracer) TraceBatchStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchStartData) context.Context {
-	for _, tracer := range t.BatchTracers {
-		ctx = tracer.TraceBatchStart(ctx, conn, data)
-	}
-
-	return ctx
-}
-
-func (t *Tracer) TraceBatchQuery(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchQueryData) {
-	for _, tracer := range t.BatchTracers {
-		tracer.TraceBatchQuery(ctx, conn, data)
-	}
-}
-
-func (t *Tracer) TraceBatchEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchEndData) {
-	for _, tracer := range t.BatchTracers {
-		tracer.TraceBatchEnd(ctx, conn, data)
-	}
-}
-
-func (t *Tracer) TraceCopyFromStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceCopyFromStartData) context.Context {
-	for _, tracer := range t.CopyFromTracers {
-		ctx = tracer.TraceCopyFromStart(ctx, conn, data)
-	}
-
-	return ctx
-}
-
-func (t *Tracer) TraceCopyFromEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceCopyFromEndData) {
-	for _, tracer := range t.CopyFromTracers {
-		tracer.TraceCopyFromEnd(ctx, conn, data)
-	}
-}
-
-func (t *Tracer) TracePrepareStart(ctx context.Context, conn *pgx.Conn, data pgx.TracePrepareStartData) context.Context {
-	for _, tracer := range t.PrepareTracers {
-		ctx = tracer.TracePrepareStart(ctx, conn, data)
-	}
-
-	return ctx
-}
-
-func (t *Tracer) TracePrepareEnd(ctx context.Context, conn *pgx.Conn, data pgx.TracePrepareEndData) {
-	for _, tracer := range t.PrepareTracers {
-		tracer.TracePrepareEnd(ctx, conn, data)
-	}
-}
-
-func (t *Tracer) TraceConnectStart(ctx context.Context, data pgx.TraceConnectStartData) context.Context {
-	for _, tracer := range t.ConnectTracers {
-		ctx = tracer.TraceConnectStart(ctx, data)
-	}
-
-	return ctx
-}
-
-func (t *Tracer) TraceConnectEnd(ctx context.Context, data pgx.TraceConnectEndData) {
-	for _, tracer := range t.ConnectTracers {
-		tracer.TraceConnectEnd(ctx, data)
-	}
-}
-
-func (t *Tracer) TraceAcquireStart(ctx context.Context, pool *pgxpool.Pool, data pgxpool.TraceAcquireStartData) context.Context {
-	for _, tracer := range t.PoolAcquireTracers {
-		ctx = tracer.TraceAcquireStart(ctx, pool, data)
-	}
-
-	return ctx
-}
-
-func (t *Tracer) TraceAcquireEnd(ctx context.Context, pool *pgxpool.Pool, data pgxpool.TraceAcquireEndData) {
-	for _, tracer := range t.PoolAcquireTracers {
-		tracer.TraceAcquireEnd(ctx, pool, data)
-	}
-}
-
-func (t *Tracer) TraceRelease(pool *pgxpool.Pool, data pgxpool.TraceReleaseData) {
-	for _, tracer := range t.PoolReleaseTracers {
-		tracer.TraceRelease(pool, data)
-	}
-}
diff --git a/pgconn/config.go b/pgconn/config.go
index 46b39f14..598917f5 100644
--- a/pgconn/config.go
+++ b/pgconn/config.go
@@ -467,17 +467,14 @@ func parseEnvSettings() map[string]string {
 func parseURLSettings(connString string) (map[string]string, error) {
 	settings := make(map[string]string)
 
-	parsedURL, err := url.Parse(connString)
+	url, err := url.Parse(connString)
 	if err != nil {
-		if urlErr := new(url.Error); errors.As(err, &urlErr) {
-			return nil, urlErr.Err
-		}
 		return nil, err
 	}
 
-	if parsedURL.User != nil {
-		settings["user"] = parsedURL.User.Username()
-		if password, present := parsedURL.User.Password(); present {
+	if url.User != nil {
+		settings["user"] = url.User.Username()
+		if password, present := url.User.Password(); present {
 			settings["password"] = password
 		}
 	}
@@ -485,7 +482,7 @@ func parseURLSettings(connString string) (map[string]string, error) {
 	// Handle multiple host:port's in url.Host by splitting them into host,host,host and port,port,port.
 	var hosts []string
 	var ports []string
-	for _, host := range strings.Split(parsedURL.Host, ",") {
+	for _, host := range strings.Split(url.Host, ",") {
 		if host == "" {
 			continue
 		}
@@ -511,7 +508,7 @@ func parseURLSettings(connString string) (map[string]string, error) {
 		settings["port"] = strings.Join(ports, ",")
 	}
 
-	database := strings.TrimLeft(parsedURL.Path, "/")
+	database := strings.TrimLeft(url.Path, "/")
 	if database != "" {
 		settings["database"] = database
 	}
@@ -520,7 +517,7 @@ func parseURLSettings(connString string) (map[string]string, error) {
 		"dbname": "database",
 	}
 
-	for k, v := range parsedURL.Query() {
+	for k, v := range url.Query() {
 		if k2, present := nameMap[k]; present {
 			k = k2
 		}
@@ -657,36 +654,6 @@ func configTLS(settings map[string]string, thisHost string, parseConfigOptions P
 
 	tlsConfig := &tls.Config{}
 
-	if sslrootcert != "" {
-		var caCertPool *x509.CertPool
-
-		if sslrootcert == "system" {
-			var err error
-
-			caCertPool, err = x509.SystemCertPool()
-			if err != nil {
-				return nil, fmt.Errorf("unable to load system certificate pool: %w", err)
-			}
-
-			sslmode = "verify-full"
-		} else {
-			caCertPool = x509.NewCertPool()
-
-			caPath := sslrootcert
-			caCert, err := os.ReadFile(caPath)
-			if err != nil {
-				return nil, fmt.Errorf("unable to read CA file: %w", err)
-			}
-
-			if !caCertPool.AppendCertsFromPEM(caCert) {
-				return nil, errors.New("unable to add CA to cert pool")
-			}
-		}
-
-		tlsConfig.RootCAs = caCertPool
-		tlsConfig.ClientCAs = caCertPool
-	}
-
 	switch sslmode {
 	case "disable":
 		return []*tls.Config{nil}, nil
@@ -744,6 +711,23 @@ func configTLS(settings map[string]string, thisHost string, parseConfigOptions P
 		return nil, errors.New("sslmode is invalid")
 	}
 
+	if sslrootcert != "" {
+		caCertPool := x509.NewCertPool()
+
+		caPath := sslrootcert
+		caCert, err := os.ReadFile(caPath)
+		if err != nil {
+			return nil, fmt.Errorf("unable to read CA file: %w", err)
+		}
+
+		if !caCertPool.AppendCertsFromPEM(caCert) {
+			return nil, errors.New("unable to add CA to cert pool")
+		}
+
+		tlsConfig.RootCAs = caCertPool
+		tlsConfig.ClientCAs = caCertPool
+	}
+
 	if (sslcert != "" && sslkey == "") || (sslcert == "" && sslkey != "") {
 		return nil, errors.New(`both "sslcert" and "sslkey" are required`)
 	}
@@ -861,12 +845,12 @@ func makeConnectTimeoutDialFunc(timeout time.Duration) DialFunc {
 // ValidateConnectTargetSessionAttrsReadWrite is a ValidateConnectFunc that implements libpq compatible
 // target_session_attrs=read-write.
 func ValidateConnectTargetSessionAttrsReadWrite(ctx context.Context, pgConn *PgConn) error {
-	result, err := pgConn.Exec(ctx, "show transaction_read_only").ReadAll()
-	if err != nil {
-		return err
+	result := pgConn.ExecParams(ctx, "show transaction_read_only", nil, nil, nil, nil).Read()
+	if result.Err != nil {
+		return result.Err
 	}
 
-	if string(result[0].Rows[0][0]) == "on" {
+	if string(result.Rows[0][0]) == "on" {
 		return errors.New("read only connection")
 	}
 
@@ -876,12 +860,12 @@ func ValidateConnectTargetSessionAttrsReadWrite(ctx context.Context, pgConn *PgC
 // ValidateConnectTargetSessionAttrsReadOnly is a ValidateConnectFunc that implements libpq compatible
 // target_session_attrs=read-only.
 func ValidateConnectTargetSessionAttrsReadOnly(ctx context.Context, pgConn *PgConn) error {
-	result, err := pgConn.Exec(ctx, "show transaction_read_only").ReadAll()
-	if err != nil {
-		return err
+	result := pgConn.ExecParams(ctx, "show transaction_read_only", nil, nil, nil, nil).Read()
+	if result.Err != nil {
+		return result.Err
 	}
 
-	if string(result[0].Rows[0][0]) != "on" {
+	if string(result.Rows[0][0]) != "on" {
 		return errors.New("connection is not read only")
 	}
 
@@ -891,12 +875,12 @@ func ValidateConnectTargetSessionAttrsReadOnly(ctx context.Context, pgConn *PgCo
 // ValidateConnectTargetSessionAttrsStandby is a ValidateConnectFunc that implements libpq compatible
 // target_session_attrs=standby.
 func ValidateConnectTargetSessionAttrsStandby(ctx context.Context, pgConn *PgConn) error {
-	result, err := pgConn.Exec(ctx, "select pg_is_in_recovery()").ReadAll()
-	if err != nil {
-		return err
+	result := pgConn.ExecParams(ctx, "select pg_is_in_recovery()", nil, nil, nil, nil).Read()
+	if result.Err != nil {
+		return result.Err
 	}
 
-	if string(result[0].Rows[0][0]) != "t" {
+	if string(result.Rows[0][0]) != "t" {
 		return errors.New("server is not in hot standby mode")
 	}
 
@@ -906,12 +890,12 @@ func ValidateConnectTargetSessionAttrsStandby(ctx context.Context, pgConn *PgCon
 // ValidateConnectTargetSessionAttrsPrimary is a ValidateConnectFunc that implements libpq compatible
 // target_session_attrs=primary.
 func ValidateConnectTargetSessionAttrsPrimary(ctx context.Context, pgConn *PgConn) error {
-	result, err := pgConn.Exec(ctx, "select pg_is_in_recovery()").ReadAll()
-	if err != nil {
-		return err
+	result := pgConn.ExecParams(ctx, "select pg_is_in_recovery()", nil, nil, nil, nil).Read()
+	if result.Err != nil {
+		return result.Err
 	}
 
-	if string(result[0].Rows[0][0]) == "t" {
+	if string(result.Rows[0][0]) == "t" {
 		return errors.New("server is in standby mode")
 	}
 
@@ -921,12 +905,12 @@ func ValidateConnectTargetSessionAttrsPrimary(ctx context.Context, pgConn *PgCon
 // ValidateConnectTargetSessionAttrsPreferStandby is a ValidateConnectFunc that implements libpq compatible
 // target_session_attrs=prefer-standby.
 func ValidateConnectTargetSessionAttrsPreferStandby(ctx context.Context, pgConn *PgConn) error {
-	result, err := pgConn.Exec(ctx, "select pg_is_in_recovery()").ReadAll()
-	if err != nil {
-		return err
+	result := pgConn.ExecParams(ctx, "select pg_is_in_recovery()", nil, nil, nil, nil).Read()
+	if result.Err != nil {
+		return result.Err
 	}
 
-	if string(result[0].Rows[0][0]) != "t" {
+	if string(result.Rows[0][0]) != "t" {
 		return &NotPreferredError{err: errors.New("server is not in hot standby mode")}
 	}
 
diff --git a/pgconn/pgconn.go b/pgconn/pgconn.go
index 59b89cf7..7efb522a 100644
--- a/pgconn/pgconn.go
+++ b/pgconn/pgconn.go
@@ -1,7 +1,6 @@
 package pgconn
 
 import (
-	"container/list"
 	"context"
 	"crypto/md5"
 	"crypto/tls"
@@ -1409,8 +1408,9 @@ func (pgConn *PgConn) CopyFrom(ctx context.Context, r io.Reader, sql string) (Co
 
 // MultiResultReader is a reader for a command that could return multiple results such as Exec or ExecBatch.
 type MultiResultReader struct {
-	pgConn *PgConn
-	ctx    context.Context
+	pgConn   *PgConn
+	ctx      context.Context
+	pipeline *Pipeline
 
 	rr *ResultReader
 
@@ -1443,8 +1443,12 @@ func (mrr *MultiResultReader) receiveMessage() (pgproto3.BackendMessage, error)
 	switch msg := msg.(type) {
 	case *pgproto3.ReadyForQuery:
 		mrr.closed = true
-		mrr.pgConn.contextWatcher.Unwatch()
-		mrr.pgConn.unlock()
+		if mrr.pipeline != nil {
+			mrr.pipeline.expectedReadyForQueryCount--
+		} else {
+			mrr.pgConn.contextWatcher.Unwatch()
+			mrr.pgConn.unlock()
+		}
 	case *pgproto3.ErrorResponse:
 		mrr.err = ErrorResponseToPgError(msg)
 	}
@@ -1668,11 +1672,7 @@ func (rr *ResultReader) receiveMessage() (msg pgproto3.BackendMessage, err error
 	case *pgproto3.EmptyQueryResponse:
 		rr.concludeCommand(CommandTag{}, nil)
 	case *pgproto3.ErrorResponse:
-		pgErr := ErrorResponseToPgError(msg)
-		if rr.pipeline != nil {
-			rr.pipeline.state.HandleError(pgErr)
-		}
-		rr.concludeCommand(CommandTag{}, pgErr)
+		rr.concludeCommand(CommandTag{}, ErrorResponseToPgError(msg))
 	}
 
 	return msg, nil
@@ -1999,7 +1999,9 @@ type Pipeline struct {
 	conn *PgConn
 	ctx  context.Context
 
-	state  pipelineState
+	expectedReadyForQueryCount int
+	pendingSync                bool
+
 	err    error
 	closed bool
 }
@@ -2010,122 +2012,6 @@ type PipelineSync struct{}
 // CloseComplete is returned by GetResults when a CloseComplete message is received.
 type CloseComplete struct{}
 
-type pipelineRequestType int
-
-const (
-	pipelineNil pipelineRequestType = iota
-	pipelinePrepare
-	pipelineQueryParams
-	pipelineQueryPrepared
-	pipelineDeallocate
-	pipelineSyncRequest
-	pipelineFlushRequest
-)
-
-type pipelineRequestEvent struct {
-	RequestType       pipelineRequestType
-	WasSentToServer   bool
-	BeforeFlushOrSync bool
-}
-
-type pipelineState struct {
-	requestEventQueue          list.List
-	lastRequestType            pipelineRequestType
-	pgErr                      *PgError
-	expectedReadyForQueryCount int
-}
-
-func (s *pipelineState) Init() {
-	s.requestEventQueue.Init()
-	s.lastRequestType = pipelineNil
-}
-
-func (s *pipelineState) RegisterSendingToServer() {
-	for elem := s.requestEventQueue.Back(); elem != nil; elem = elem.Prev() {
-		val := elem.Value.(pipelineRequestEvent)
-		if val.WasSentToServer {
-			return
-		}
-		val.WasSentToServer = true
-		elem.Value = val
-	}
-}
-
-func (s *pipelineState) registerFlushingBufferOnServer() {
-	for elem := s.requestEventQueue.Back(); elem != nil; elem = elem.Prev() {
-		val := elem.Value.(pipelineRequestEvent)
-		if val.BeforeFlushOrSync {
-			return
-		}
-		val.BeforeFlushOrSync = true
-		elem.Value = val
-	}
-}
-
-func (s *pipelineState) PushBackRequestType(req pipelineRequestType) {
-	if req == pipelineNil {
-		return
-	}
-
-	if req != pipelineFlushRequest {
-		s.requestEventQueue.PushBack(pipelineRequestEvent{RequestType: req})
-	}
-	if req == pipelineFlushRequest || req == pipelineSyncRequest {
-		s.registerFlushingBufferOnServer()
-	}
-	s.lastRequestType = req
-
-	if req == pipelineSyncRequest {
-		s.expectedReadyForQueryCount++
-	}
-}
-
-func (s *pipelineState) ExtractFrontRequestType() pipelineRequestType {
-	for {
-		elem := s.requestEventQueue.Front()
-		if elem == nil {
-			return pipelineNil
-		}
-		val := elem.Value.(pipelineRequestEvent)
-		if !(val.WasSentToServer && val.BeforeFlushOrSync) {
-			return pipelineNil
-		}
-
-		s.requestEventQueue.Remove(elem)
-		if val.RequestType == pipelineSyncRequest {
-			s.pgErr = nil
-		}
-		if s.pgErr == nil {
-			return val.RequestType
-		}
-	}
-}
-
-func (s *pipelineState) HandleError(err *PgError) {
-	s.pgErr = err
-}
-
-func (s *pipelineState) HandleReadyForQuery() {
-	s.expectedReadyForQueryCount--
-}
-
-func (s *pipelineState) PendingSync() bool {
-	var notPendingSync bool
-
-	if elem := s.requestEventQueue.Back(); elem != nil {
-		val := elem.Value.(pipelineRequestEvent)
-		notPendingSync = (val.RequestType == pipelineSyncRequest) && val.WasSentToServer
-	} else {
-		notPendingSync = (s.lastRequestType == pipelineSyncRequest) || (s.lastRequestType == pipelineNil)
-	}
-
-	return !notPendingSync
-}
-
-func (s *pipelineState) ExpectedReadyForQuery() int {
-	return s.expectedReadyForQueryCount
-}
-
 // StartPipeline switches the connection to pipeline mode and returns a *Pipeline. In pipeline mode requests can be sent
 // to the server without waiting for a response. Close must be called on the returned *Pipeline to return the connection
 // to normal mode. While in pipeline mode, no methods that communicate with the server may be called except
@@ -2134,21 +2020,16 @@ func (s *pipelineState) ExpectedReadyForQuery() int {
 // Prefer ExecBatch when only sending one group of queries at once.
 func (pgConn *PgConn) StartPipeline(ctx context.Context) *Pipeline {
 	if err := pgConn.lock(); err != nil {
-		pipeline := &Pipeline{
+		return &Pipeline{
 			closed: true,
 			err:    err,
 		}
-		pipeline.state.Init()
-
-		return pipeline
 	}
 
 	pgConn.pipeline = Pipeline{
 		conn: pgConn,
 		ctx:  ctx,
 	}
-	pgConn.pipeline.state.Init()
-
 	pipeline := &pgConn.pipeline
 
 	if ctx != context.Background() {
@@ -2171,10 +2052,10 @@ func (p *Pipeline) SendPrepare(name, sql string, paramOIDs []uint32) {
 	if p.closed {
 		return
 	}
+	p.pendingSync = true
 
 	p.conn.frontend.SendParse(&pgproto3.Parse{Name: name, Query: sql, ParameterOIDs: paramOIDs})
 	p.conn.frontend.SendDescribe(&pgproto3.Describe{ObjectType: 'S', Name: name})
-	p.state.PushBackRequestType(pipelinePrepare)
 }
 
 // SendDeallocate deallocates a prepared statement.
@@ -2182,9 +2063,9 @@ func (p *Pipeline) SendDeallocate(name string) {
 	if p.closed {
 		return
 	}
+	p.pendingSync = true
 
 	p.conn.frontend.SendClose(&pgproto3.Close{ObjectType: 'S', Name: name})
-	p.state.PushBackRequestType(pipelineDeallocate)
 }
 
 // SendQueryParams is the pipeline version of *PgConn.QueryParams.
@@ -2192,12 +2073,12 @@ func (p *Pipeline) SendQueryParams(sql string, paramValues [][]byte, paramOIDs [
 	if p.closed {
 		return
 	}
+	p.pendingSync = true
 
 	p.conn.frontend.SendParse(&pgproto3.Parse{Query: sql, ParameterOIDs: paramOIDs})
 	p.conn.frontend.SendBind(&pgproto3.Bind{ParameterFormatCodes: paramFormats, Parameters: paramValues, ResultFormatCodes: resultFormats})
 	p.conn.frontend.SendDescribe(&pgproto3.Describe{ObjectType: 'P'})
 	p.conn.frontend.SendExecute(&pgproto3.Execute{})
-	p.state.PushBackRequestType(pipelineQueryParams)
 }
 
 // SendQueryPrepared is the pipeline version of *PgConn.QueryPrepared.
@@ -2205,42 +2086,11 @@ func (p *Pipeline) SendQueryPrepared(stmtName string, paramValues [][]byte, para
 	if p.closed {
 		return
 	}
+	p.pendingSync = true
 
 	p.conn.frontend.SendBind(&pgproto3.Bind{PreparedStatement: stmtName, ParameterFormatCodes: paramFormats, Parameters: paramValues, ResultFormatCodes: resultFormats})
 	p.conn.frontend.SendDescribe(&pgproto3.Describe{ObjectType: 'P'})
 	p.conn.frontend.SendExecute(&pgproto3.Execute{})
-	p.state.PushBackRequestType(pipelineQueryPrepared)
-}
-
-// SendFlushRequest sends a request for the server to flush its output buffer.
-//
-// The server flushes its output buffer automatically as a result of Sync being called,
-// or on any request when not in pipeline mode; this function is useful to cause the server
-// to flush its output buffer in pipeline mode without establishing a synchronization point.
-// Note that the request is not itself flushed to the server automatically; use Flush if
-// necessary. This copies the behavior of libpq PQsendFlushRequest.
-func (p *Pipeline) SendFlushRequest() {
-	if p.closed {
-		return
-	}
-
-	p.conn.frontend.Send(&pgproto3.Flush{})
-	p.state.PushBackRequestType(pipelineFlushRequest)
-}
-
-// SendPipelineSync marks a synchronization point in a pipeline by sending a sync message
-// without flushing the send buffer. This serves as the delimiter of an implicit
-// transaction and an error recovery point.
-//
-// Note that the request is not itself flushed to the server automatically; use Flush if
-// necessary. This copies the behavior of libpq PQsendPipelineSync.
-func (p *Pipeline) SendPipelineSync() {
-	if p.closed {
-		return
-	}
-
-	p.conn.frontend.SendSync(&pgproto3.Sync{})
-	p.state.PushBackRequestType(pipelineSyncRequest)
 }
 
 // Flush flushes the queued requests without establishing a synchronization point.
@@ -2265,14 +2115,28 @@ func (p *Pipeline) Flush() error {
 		return err
 	}
 
-	p.state.RegisterSendingToServer()
 	return nil
 }
 
 // Sync establishes a synchronization point and flushes the queued requests.
 func (p *Pipeline) Sync() error {
-	p.SendPipelineSync()
-	return p.Flush()
+	if p.closed {
+		if p.err != nil {
+			return p.err
+		}
+		return errors.New("pipeline closed")
+	}
+
+	p.conn.frontend.SendSync(&pgproto3.Sync{})
+	err := p.Flush()
+	if err != nil {
+		return err
+	}
+
+	p.pendingSync = false
+	p.expectedReadyForQueryCount++
+
+	return nil
 }
 
 // GetResults gets the next results. If results are present, results may be a *ResultReader, *StatementDescription, or
@@ -2286,7 +2150,7 @@ func (p *Pipeline) GetResults() (results any, err error) {
 		return nil, errors.New("pipeline closed")
 	}
 
-	if p.state.ExtractFrontRequestType() == pipelineNil {
+	if p.expectedReadyForQueryCount == 0 {
 		return nil, nil
 	}
 
@@ -2331,13 +2195,13 @@ func (p *Pipeline) getResults() (results any, err error) {
 		case *pgproto3.CloseComplete:
 			return &CloseComplete{}, nil
 		case *pgproto3.ReadyForQuery:
-			p.state.HandleReadyForQuery()
+			p.expectedReadyForQueryCount--
 			return &PipelineSync{}, nil
 		case *pgproto3.ErrorResponse:
 			pgErr := ErrorResponseToPgError(msg)
-			p.state.HandleError(pgErr)
 			return nil, pgErr
 		}
+
 	}
 }
 
@@ -2367,7 +2231,6 @@ func (p *Pipeline) getResultsPrepare() (*StatementDescription, error) {
 		// These should never happen here. But don't take chances that could lead to a deadlock.
 		case *pgproto3.ErrorResponse:
 			pgErr := ErrorResponseToPgError(msg)
-			p.state.HandleError(pgErr)
 			return nil, pgErr
 		case *pgproto3.CommandComplete:
 			p.conn.asyncClose()
@@ -2387,7 +2250,7 @@ func (p *Pipeline) Close() error {
 
 	p.closed = true
 
-	if p.state.PendingSync() {
+	if p.pendingSync {
 		p.conn.asyncClose()
 		p.err = errors.New("pipeline has unsynced requests")
 		p.conn.contextWatcher.Unwatch()
@@ -2396,7 +2259,7 @@ func (p *Pipeline) Close() error {
 		return p.err
 	}
 
-	for p.state.ExpectedReadyForQuery() > 0 {
+	for p.expectedReadyForQueryCount > 0 {
 		_, err := p.getResults()
 		if err != nil {
 			p.err = err
diff --git a/pgproto3/backend.go b/pgproto3/backend.go
index 28cff049..d146c338 100644
--- a/pgproto3/backend.go
+++ b/pgproto3/backend.go
@@ -175,13 +175,7 @@ func (b *Backend) Receive() (FrontendMessage, error) {
 		}
 
 		b.msgType = header[0]
-
-		msgLength := int(binary.BigEndian.Uint32(header[1:]))
-		if msgLength < 4 {
-			return nil, fmt.Errorf("invalid message length: %d", msgLength)
-		}
-
-		b.bodyLen = msgLength - 4
+		b.bodyLen = int(binary.BigEndian.Uint32(header[1:])) - 4
 		if b.maxBodyLen > 0 && b.bodyLen > b.maxBodyLen {
 			return nil, &ExceededMaxBodyLenErr{b.maxBodyLen, b.bodyLen}
 		}
@@ -288,10 +282,9 @@ func (b *Backend) SetAuthType(authType uint32) error {
 	return nil
 }
 
-// SetMaxBodyLen sets the maximum length of a message body in octets.
-// If a message body exceeds this length, Receive will return an error.
-// This is useful for protecting against malicious clients that send
-// large messages with the intent of causing memory exhaustion.
+// SetMaxBodyLen sets the maximum length of a message body in octets. If a message body exceeds this length, Receive will return
+// an error. This is useful for protecting against malicious clients that send large messages with the intent of
+// causing memory exhaustion.
 // The default value is 0.
 // If maxBodyLen is 0, then no maximum is enforced.
 func (b *Backend) SetMaxBodyLen(maxBodyLen int) {
diff --git a/pgproto3/frontend.go b/pgproto3/frontend.go
index 056e547c..b41abbe1 100644
--- a/pgproto3/frontend.go
+++ b/pgproto3/frontend.go
@@ -54,7 +54,6 @@ type Frontend struct {
 	portalSuspended                 PortalSuspended
 
 	bodyLen    int
-	maxBodyLen int // maxBodyLen is the maximum length of a message body in octets. If a message body exceeds this length, Receive will return an error.
 	msgType    byte
 	partialMsg bool
 	authType   uint32
@@ -318,9 +317,6 @@ func (f *Frontend) Receive() (BackendMessage, error) {
 		}
 
 		f.bodyLen = msgLength - 4
-		if f.maxBodyLen > 0 && f.bodyLen > f.maxBodyLen {
-			return nil, &ExceededMaxBodyLenErr{f.maxBodyLen, f.bodyLen}
-		}
 		f.partialMsg = true
 	}
 
@@ -456,13 +452,3 @@ func (f *Frontend) GetAuthType() uint32 {
 func (f *Frontend) ReadBufferLen() int {
 	return f.cr.wp - f.cr.rp
 }
-
-// SetMaxBodyLen sets the maximum length of a message body in octets.
-// If a message body exceeds this length, Receive will return an error.
-// This is useful for protecting against a corrupted server that sends
-// messages with incorrect length, which can cause memory exhaustion.
-// The default value is 0.
-// If maxBodyLen is 0, then no maximum is enforced.
-func (f *Frontend) SetMaxBodyLen(maxBodyLen int) {
-	f.maxBodyLen = maxBodyLen
-}
diff --git a/pgproto3/password_message.go b/pgproto3/password_message.go
index 67b78515..d820d327 100644
--- a/pgproto3/password_message.go
+++ b/pgproto3/password_message.go
@@ -12,7 +12,7 @@ type PasswordMessage struct {
 // Frontend identifies this message as sendable by a PostgreSQL frontend.
 func (*PasswordMessage) Frontend() {}
 
-// InitialResponse identifies this message as an authentication response.
+// Frontend identifies this message as an authentication response.
 func (*PasswordMessage) InitialResponse() {}
 
 // Decode decodes src into dst. src must contain the complete message with the exception of the initial 1 byte message
diff --git a/pgtype/array_codec.go b/pgtype/array_codec.go
index bf5f6989..c1863b32 100644
--- a/pgtype/array_codec.go
+++ b/pgtype/array_codec.go
@@ -6,6 +6,7 @@ import (
 	"fmt"
 	"reflect"
 
+	"github.com/jackc/pgx/v5/internal/anynil"
 	"github.com/jackc/pgx/v5/internal/pgio"
 )
 
@@ -229,7 +230,7 @@ func (c *ArrayCodec) PlanScan(m *Map, oid uint32, format int16, target any) Scan
 
 	// target / arrayScanner might be a pointer to a nil. If it is create one so we can call ScanIndexType to plan the
 	// scan of the elements.
-	if isNil, _ := isNilDriverValuer(target); isNil {
+	if anynil.Is(target) {
 		arrayScanner = reflect.New(reflect.TypeOf(target).Elem()).Interface().(ArraySetter)
 	}
 
diff --git a/pgtype/doc.go b/pgtype/doc.go
index 7687ea8f..ec9270ac 100644
--- a/pgtype/doc.go
+++ b/pgtype/doc.go
@@ -53,9 +53,6 @@ similar fashion to database/sql. The second is to use a pointer to a pointer.
         return err
     }
 
-When using nullable pgtype types as parameters for queries, one has to remember
-to explicitly set their Valid field to true, otherwise the parameter's value will be NULL.
-
 JSON Support
 
 pgtype automatically marshals and unmarshals data from json and jsonb PostgreSQL types.
@@ -142,16 +139,6 @@ Compatibility with database/sql
 pgtype also includes support for custom types implementing the database/sql.Scanner and database/sql/driver.Valuer
 interfaces.
 
-Encoding Typed Nils
-
-pgtype encodes untyped and typed nils (e.g. nil and []byte(nil)) to the SQL NULL value without going through the Codec
-system. This means that Codecs and other encoding logic do not have to handle nil or *T(nil).
-
-However, database/sql compatibility requires Value to be called on T(nil) when T implements driver.Valuer. Therefore,
-driver.Valuer values are only considered NULL when *T(nil) where driver.Valuer is implemented on T not on *T. See
-https://github.com/golang/go/issues/8415 and
-https://github.com/golang/go/commit/0ce1d79a6a771f7449ec493b993ed2a720917870.
-
 Child Records
 
 pgtype's support for arrays and composite records can be used to load records and their children in a single query.  See
diff --git a/pgtype/int.go b/pgtype/int.go
index 7a2f8cb2..90a20a26 100644
--- a/pgtype/int.go
+++ b/pgtype/int.go
@@ -1,5 +1,4 @@
-// Code generated from pgtype/int.go.erb. DO NOT EDIT.
-
+// Do not edit. Generated from pgtype/int.go.erb
 package pgtype
 
 import (
diff --git a/pgtype/interval.go b/pgtype/interval.go
index 4b511629..06703d4d 100644
--- a/pgtype/interval.go
+++ b/pgtype/interval.go
@@ -132,25 +132,29 @@ func (encodePlanIntervalCodecText) Encode(value any, buf []byte) (newBuf []byte,
 
 	if interval.Days != 0 {
 		buf = append(buf, strconv.FormatInt(int64(interval.Days), 10)...)
-		buf = append(buf, " day "...)
+		buf = append(buf, " day"...)
 	}
 
-	absMicroseconds := interval.Microseconds
-	if absMicroseconds < 0 {
-		absMicroseconds = -absMicroseconds
-		buf = append(buf, '-')
-	}
+	if interval.Microseconds != 0 {
+		buf = append(buf, " "...)
+
+		absMicroseconds := interval.Microseconds
+		if absMicroseconds < 0 {
+			absMicroseconds = -absMicroseconds
+			buf = append(buf, '-')
+		}
 
-	hours := absMicroseconds / microsecondsPerHour
-	minutes := (absMicroseconds % microsecondsPerHour) / microsecondsPerMinute
-	seconds := (absMicroseconds % microsecondsPerMinute) / microsecondsPerSecond
+		hours := absMicroseconds / microsecondsPerHour
+		minutes := (absMicroseconds % microsecondsPerHour) / microsecondsPerMinute
+		seconds := (absMicroseconds % microsecondsPerMinute) / microsecondsPerSecond
 
-	timeStr := fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds)
-	buf = append(buf, timeStr...)
+		timeStr := fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds)
+		buf = append(buf, timeStr...)
 
-	microseconds := absMicroseconds % microsecondsPerSecond
-	if microseconds != 0 {
-		buf = append(buf, fmt.Sprintf(".%06d", microseconds)...)
+		microseconds := absMicroseconds % microsecondsPerSecond
+		if microseconds != 0 {
+			buf = append(buf, fmt.Sprintf(".%06d", microseconds)...)
+		}
 	}
 
 	return buf, nil
diff --git a/pgtype/json.go b/pgtype/json.go
index 76cec51b..e71dcb9b 100644
--- a/pgtype/json.go
+++ b/pgtype/json.go
@@ -37,7 +37,7 @@ func (c *JSONCodec) PlanEncode(m *Map, oid uint32, format int16, value any) Enco
 	//
 	// https://github.com/jackc/pgx/issues/1430
 	//
-	// Check for driver.Valuer must come before json.Marshaler so that it is guaranteed to be used
+	// Check for driver.Valuer must come before json.Marshaler so that it is guaranteed to beused
 	// when both are implemented https://github.com/jackc/pgx/issues/1805
 	case driver.Valuer:
 		return &encodePlanDriverValuer{m: m, oid: oid, formatCode: format}
@@ -130,7 +130,7 @@ func (c *JSONCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanP
 		// https://github.com/jackc/pgx/issues/1691 -- ** anything else
 
 		if wrapperPlan, nextDst, ok := TryPointerPointerScanPlan(target); ok {
-			if nextPlan := m.planScan(oid, format, nextDst, 0); nextPlan != nil {
+			if nextPlan := m.planScan(oid, format, nextDst); nextPlan != nil {
 				if _, failed := nextPlan.(*scanPlanFail); !failed {
 					wrapperPlan.SetNext(nextPlan)
 					return wrapperPlan
@@ -143,12 +143,10 @@ func (c *JSONCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanP
 	case BytesScanner:
 		return scanPlanBinaryBytesToBytesScanner{}
 
-	}
-
 	// Cannot rely on sql.Scanner being handled later because scanPlanJSONToJSONUnmarshal will take precedence.
 	//
 	// https://github.com/jackc/pgx/issues/1418
-	if isSQLScanner(target) {
+	case sql.Scanner:
 		return &scanPlanSQLScanner{formatCode: format}
 	}
 
@@ -157,24 +155,6 @@ func (c *JSONCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanP
 	}
 }
 
-// we need to check if the target is a pointer to a sql.Scanner (or any of the pointer ref tree implements a sql.Scanner).
-//
-// https://github.com/jackc/pgx/issues/2146
-func isSQLScanner(v any) bool {
-	if _, is := v.(sql.Scanner); is {
-		return true
-	}
-
-	val := reflect.ValueOf(v)
-	for val.Kind() == reflect.Ptr {
-		if _, ok := val.Interface().(sql.Scanner); ok {
-			return true
-		}
-		val = val.Elem()
-	}
-	return false
-}
-
 type scanPlanAnyToString struct{}
 
 func (scanPlanAnyToString) Scan(src []byte, dst any) error {
@@ -197,6 +177,13 @@ func (scanPlanJSONToByteSlice) Scan(src []byte, dst any) error {
 	return nil
 }
 
+type scanPlanJSONToBytesScanner struct{}
+
+func (scanPlanJSONToBytesScanner) Scan(src []byte, dst any) error {
+	scanner := (dst).(BytesScanner)
+	return scanner.ScanBytes(src)
+}
+
 type scanPlanJSONToJSONUnmarshal struct {
 	unmarshal func(data []byte, v any) error
 }
@@ -216,12 +203,7 @@ func (s *scanPlanJSONToJSONUnmarshal) Scan(src []byte, dst any) error {
 		return fmt.Errorf("cannot scan NULL into %T", dst)
 	}
 
-	v := reflect.ValueOf(dst)
-	if v.Kind() != reflect.Pointer || v.IsNil() {
-		return fmt.Errorf("cannot scan into non-pointer or nil destinations %T", dst)
-	}
-
-	elem := v.Elem()
+	elem := reflect.ValueOf(dst).Elem()
 	elem.Set(reflect.Zero(elem.Type()))
 
 	return s.unmarshal(src, dst)
diff --git a/pgtype/pgtype.go b/pgtype/pgtype.go
index 20645d69..2be11e82 100644
--- a/pgtype/pgtype.go
+++ b/pgtype/pgtype.go
@@ -26,10 +26,7 @@ const (
 	XIDOID                 = 28
 	CIDOID                 = 29
 	JSONOID                = 114
-	XMLOID                 = 142
-	XMLArrayOID            = 143
 	JSONArrayOID           = 199
-	XID8ArrayOID           = 271
 	PointOID               = 600
 	LsegOID                = 601
 	PathOID                = 602
@@ -118,7 +115,6 @@ const (
 	TstzmultirangeOID      = 4534
 	DatemultirangeOID      = 4535
 	Int8multirangeOID      = 4536
-	XID8OID                = 5069
 	Int4multirangeArrayOID = 6150
 	NummultirangeArrayOID  = 6151
 	TsmultirangeArrayOID   = 6152
@@ -218,15 +214,6 @@ type Map struct {
 	TryWrapScanPlanFuncs []TryWrapScanPlanFunc
 }
 
-// Copy returns a new Map containing the same registered types.
-func (m *Map) Copy() *Map {
-	newMap := NewMap()
-	for _, type_ := range m.oidToType {
-		newMap.RegisterType(type_)
-	}
-	return newMap
-}
-
 func NewMap() *Map {
 	defaultMapInitOnce.Do(initDefaultMap)
 
@@ -261,13 +248,6 @@ func NewMap() *Map {
 	}
 }
 
-// RegisterTypes registers multiple data types in the sequence they are provided.
-func (m *Map) RegisterTypes(types []*Type) {
-	for _, t := range types {
-		m.RegisterType(t)
-	}
-}
-
 // RegisterType registers a data type with the Map. t must not be mutated after it is registered.
 func (m *Map) RegisterType(t *Type) {
 	m.oidToType[t.OID] = t
@@ -396,12 +376,7 @@ type scanPlanSQLScanner struct {
 }
 
 func (plan *scanPlanSQLScanner) Scan(src []byte, dst any) error {
-	scanner := getSQLScanner(dst)
-
-	if scanner == nil {
-		return fmt.Errorf("cannot scan into %T", dst)
-	}
-
+	scanner := dst.(sql.Scanner)
 	if src == nil {
 		// This is necessary because interface value []byte:nil does not equal nil:nil for the binary format path and the
 		// text format path would be converted to empty string.
@@ -413,25 +388,6 @@ func (plan *scanPlanSQLScanner) Scan(src []byte, dst any) error {
 	}
 }
 
-// we don't know if the target is a sql.Scanner or a pointer on a sql.Scanner, so we need to check recursively
-func getSQLScanner(target any) sql.Scanner {
-	if sc, is := target.(sql.Scanner); is {
-		return sc
-	}
-
-	val := reflect.ValueOf(target)
-	for val.Kind() == reflect.Ptr {
-		if _, ok := val.Interface().(sql.Scanner); ok {
-			if val.IsNil() {
-				val.Set(reflect.New(val.Type().Elem()))
-			}
-			return val.Interface().(sql.Scanner)
-		}
-		val = val.Elem()
-	}
-	return nil
-}
-
 type scanPlanString struct{}
 
 func (scanPlanString) Scan(src []byte, dst any) error {
@@ -475,14 +431,14 @@ func (plan *scanPlanFail) Scan(src []byte, dst any) error {
 		// As a horrible hack try all types to find anything that can scan into dst.
 		for oid := range plan.m.oidToType {
 			// using planScan instead of Scan or PlanScan to avoid polluting the planned scan cache.
-			plan := plan.m.planScan(oid, plan.formatCode, dst, 0)
+			plan := plan.m.planScan(oid, plan.formatCode, dst)
 			if _, ok := plan.(*scanPlanFail); !ok {
 				return plan.Scan(src, dst)
 			}
 		}
 		for oid := range defaultMap.oidToType {
 			if _, ok := plan.m.oidToType[oid]; !ok {
-				plan := plan.m.planScan(oid, plan.formatCode, dst, 0)
+				plan := plan.m.planScan(oid, plan.formatCode, dst)
 				if _, ok := plan.(*scanPlanFail); !ok {
 					return plan.Scan(src, dst)
 				}
@@ -599,24 +555,17 @@ func TryFindUnderlyingTypeScanPlan(dst any) (plan WrappedScanPlanNextSetter, nex
 			elemValue = dstValue.Elem()
 		}
 		nextDstType := elemKindToPointerTypes[elemValue.Kind()]
-		if nextDstType == nil {
-			if elemValue.Kind() == reflect.Slice {
-				if elemValue.Type().Elem().Kind() == reflect.Uint8 {
-					var v *[]byte
-					nextDstType = reflect.TypeOf(v)
-				}
-			}
-
-			// Get underlying type of any array.
-			// https://github.com/jackc/pgx/issues/2107
-			if elemValue.Kind() == reflect.Array {
-				nextDstType = reflect.PointerTo(reflect.ArrayOf(elemValue.Len(), elemValue.Type().Elem()))
+		if nextDstType == nil && elemValue.Kind() == reflect.Slice {
+			if elemValue.Type().Elem().Kind() == reflect.Uint8 {
+				var v *[]byte
+				nextDstType = reflect.TypeOf(v)
 			}
 		}
 
 		if nextDstType != nil && dstValue.Type() != nextDstType && dstValue.CanConvert(nextDstType) {
 			return &underlyingTypeScanPlan{dstType: dstValue.Type(), nextDstType: nextDstType}, dstValue.Convert(nextDstType).Interface(), true
 		}
+
 	}
 
 	return nil, nil, false
@@ -1090,14 +1039,6 @@ func (plan *wrapPtrArrayReflectScanPlan) Scan(src []byte, target any) error {
 
 // PlanScan prepares a plan to scan a value into target.
 func (m *Map) PlanScan(oid uint32, formatCode int16, target any) ScanPlan {
-	return m.planScanDepth(oid, formatCode, target, 0)
-}
-
-func (m *Map) planScanDepth(oid uint32, formatCode int16, target any, depth int) ScanPlan {
-	if depth > 8 {
-		return &scanPlanFail{m: m, oid: oid, formatCode: formatCode}
-	}
-
 	oidMemo := m.memoizedScanPlans[oid]
 	if oidMemo == nil {
 		oidMemo = make(map[reflect.Type][2]ScanPlan)
@@ -1107,7 +1048,7 @@ func (m *Map) planScanDepth(oid uint32, formatCode int16, target any, depth int)
 	typeMemo := oidMemo[targetReflectType]
 	plan := typeMemo[formatCode]
 	if plan == nil {
-		plan = m.planScan(oid, formatCode, target, depth)
+		plan = m.planScan(oid, formatCode, target)
 		typeMemo[formatCode] = plan
 		oidMemo[targetReflectType] = typeMemo
 	}
@@ -1115,7 +1056,7 @@ func (m *Map) planScanDepth(oid uint32, formatCode int16, target any, depth int)
 	return plan
 }
 
-func (m *Map) planScan(oid uint32, formatCode int16, target any, depth int) ScanPlan {
+func (m *Map) planScan(oid uint32, formatCode int16, target any) ScanPlan {
 	if target == nil {
 		return &scanPlanFail{m: m, oid: oid, formatCode: formatCode}
 	}
@@ -1175,7 +1116,7 @@ func (m *Map) planScan(oid uint32, formatCode int16, target any, depth int) Scan
 
 	for _, f := range m.TryWrapScanPlanFuncs {
 		if wrapperPlan, nextDst, ok := f(target); ok {
-			if nextPlan := m.planScanDepth(oid, formatCode, nextDst, depth+1); nextPlan != nil {
+			if nextPlan := m.planScan(oid, formatCode, nextDst); nextPlan != nil {
 				if _, failed := nextPlan.(*scanPlanFail); !failed {
 					wrapperPlan.SetNext(nextPlan)
 					return wrapperPlan
@@ -1235,15 +1176,6 @@ func codecDecodeToTextFormat(codec Codec, m *Map, oid uint32, format int16, src
 // PlanEncode returns an Encode plan for encoding value into PostgreSQL format for oid and format. If no plan can be
 // found then nil is returned.
 func (m *Map) PlanEncode(oid uint32, format int16, value any) EncodePlan {
-	return m.planEncodeDepth(oid, format, value, 0)
-}
-
-func (m *Map) planEncodeDepth(oid uint32, format int16, value any, depth int) EncodePlan {
-	// Guard against infinite recursion.
-	if depth > 8 {
-		return nil
-	}
-
 	oidMemo := m.memoizedEncodePlans[oid]
 	if oidMemo == nil {
 		oidMemo = make(map[reflect.Type][2]EncodePlan)
@@ -1253,7 +1185,7 @@ func (m *Map) planEncodeDepth(oid uint32, format int16, value any, depth int) En
 	typeMemo := oidMemo[targetReflectType]
 	plan := typeMemo[format]
 	if plan == nil {
-		plan = m.planEncode(oid, format, value, depth)
+		plan = m.planEncode(oid, format, value)
 		typeMemo[format] = plan
 		oidMemo[targetReflectType] = typeMemo
 	}
@@ -1261,7 +1193,7 @@ func (m *Map) planEncodeDepth(oid uint32, format int16, value any, depth int) En
 	return plan
 }
 
-func (m *Map) planEncode(oid uint32, format int16, value any, depth int) EncodePlan {
+func (m *Map) planEncode(oid uint32, format int16, value any) EncodePlan {
 	if format == TextFormatCode {
 		switch value.(type) {
 		case string:
@@ -1292,7 +1224,7 @@ func (m *Map) planEncode(oid uint32, format int16, value any, depth int) EncodeP
 
 	for _, f := range m.TryWrapEncodePlanFuncs {
 		if wrapperPlan, nextValue, ok := f(value); ok {
-			if nextPlan := m.planEncodeDepth(oid, format, nextValue, depth+1); nextPlan != nil {
+			if nextPlan := m.PlanEncode(oid, format, nextValue); nextPlan != nil {
 				wrapperPlan.SetNext(nextPlan)
 				return wrapperPlan
 			}
@@ -1473,15 +1405,6 @@ func TryWrapFindUnderlyingTypeEncodePlan(value any) (plan WrappedEncodePlanNextS
 		return &underlyingTypeEncodePlan{nextValueType: byteSliceType}, refValue.Convert(byteSliceType).Interface(), true
 	}
 
-	// Get underlying type of any array.
-	// https://github.com/jackc/pgx/issues/2107
-	if refValue.Kind() == reflect.Array {
-		underlyingArrayType := reflect.ArrayOf(refValue.Len(), refValue.Type().Elem())
-		if refValue.Type() != underlyingArrayType {
-			return &underlyingTypeEncodePlan{nextValueType: underlyingArrayType}, refValue.Convert(underlyingArrayType).Interface(), true
-		}
-	}
-
 	return nil, nil, false
 }
 
@@ -1989,17 +1912,8 @@ func newEncodeError(value any, m *Map, oid uint32, formatCode int16, err error)
 // (nil, nil). The caller of Encode is responsible for writing the correct NULL value or the length of the data
 // written.
 func (m *Map) Encode(oid uint32, formatCode int16, value any, buf []byte) (newBuf []byte, err error) {
-	if isNil, callNilDriverValuer := isNilDriverValuer(value); isNil {
-		if callNilDriverValuer {
-			newBuf, err = (&encodePlanDriverValuer{m: m, oid: oid, formatCode: formatCode}).Encode(value, buf)
-			if err != nil {
-				return nil, newEncodeError(value, m, oid, formatCode, err)
-			}
-
-			return newBuf, nil
-		} else {
-			return nil, nil
-		}
+	if value == nil {
+		return nil, nil
 	}
 
 	plan := m.PlanEncode(oid, formatCode, value)
@@ -2054,55 +1968,3 @@ func (w *sqlScannerWrapper) Scan(src any) error {
 
 	return w.m.Scan(t.OID, TextFormatCode, bufSrc, w.v)
 }
-
-// canBeNil returns true if value can be nil.
-func canBeNil(value any) bool {
-	refVal := reflect.ValueOf(value)
-	kind := refVal.Kind()
-	switch kind {
-	case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Interface, reflect.Slice:
-		return true
-	default:
-		return false
-	}
-}
-
-// valuerReflectType is a reflect.Type for driver.Valuer. It has confusing syntax because reflect.TypeOf returns nil
-// when it's argument is a nil interface value. So we use a pointer to the interface and call Elem to get the actual
-// type. Yuck.
-//
-// This can be simplified in Go 1.22 with reflect.TypeFor.
-//
-// var valuerReflectType = reflect.TypeFor[driver.Valuer]()
-var valuerReflectType = reflect.TypeOf((*driver.Valuer)(nil)).Elem()
-
-// isNilDriverValuer returns true if value is any type of nil unless it implements driver.Valuer. *T is not considered to implement
-// driver.Valuer if it is only implemented by T.
-func isNilDriverValuer(value any) (isNil bool, callNilDriverValuer bool) {
-	if value == nil {
-		return true, false
-	}
-
-	refVal := reflect.ValueOf(value)
-	kind := refVal.Kind()
-	switch kind {
-	case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Interface, reflect.Slice:
-		if !refVal.IsNil() {
-			return false, false
-		}
-
-		if _, ok := value.(driver.Valuer); ok {
-			if kind == reflect.Ptr {
-				// The type assertion will succeed if driver.Valuer is implemented on T or *T. Check if it is implemented on *T
-				// by checking if it is not implemented on *T.
-				return true, !refVal.Type().Elem().Implements(valuerReflectType)
-			} else {
-				return true, true
-			}
-		}
-
-		return true, false
-	default:
-		return false, false
-	}
-}
diff --git a/pgtype/pgtype_default.go b/pgtype/pgtype_default.go
index 8cb512fa..9525f37c 100644
--- a/pgtype/pgtype_default.go
+++ b/pgtype/pgtype_default.go
@@ -2,7 +2,6 @@ package pgtype
 
 import (
 	"encoding/json"
-	"encoding/xml"
 	"net"
 	"net/netip"
 	"reflect"
@@ -90,26 +89,6 @@ func initDefaultMap() {
 	defaultMap.RegisterType(&Type{Name: "varbit", OID: VarbitOID, Codec: BitsCodec{}})
 	defaultMap.RegisterType(&Type{Name: "varchar", OID: VarcharOID, Codec: TextCodec{}})
 	defaultMap.RegisterType(&Type{Name: "xid", OID: XIDOID, Codec: Uint32Codec{}})
-	defaultMap.RegisterType(&Type{Name: "xid8", OID: XID8OID, Codec: Uint64Codec{}})
-	defaultMap.RegisterType(&Type{Name: "xml", OID: XMLOID, Codec: &XMLCodec{
-		Marshal: xml.Marshal,
-		// xml.Unmarshal does not support unmarshalling into *any. However, XMLCodec.DecodeValue calls Unmarshal with a
-		// *any. Wrap xml.Marshal with a function that copies the data into a new byte slice in this case. Not implementing
-		// directly in XMLCodec.DecodeValue to allow for the unlikely possibility that someone uses an alternative XML
-		// unmarshaler that does support unmarshalling into *any.
-		//
-		// https://github.com/jackc/pgx/issues/2227
-		// https://github.com/jackc/pgx/pull/2228
-		Unmarshal: func(data []byte, v any) error {
-			if v, ok := v.(*any); ok {
-				dstBuf := make([]byte, len(data))
-				copy(dstBuf, data)
-				*v = dstBuf
-				return nil
-			}
-			return xml.Unmarshal(data, v)
-		},
-	}})
 
 	// Range types
 	defaultMap.RegisterType(&Type{Name: "daterange", OID: DaterangeOID, Codec: &RangeCodec{ElementType: defaultMap.oidToType[DateOID]}})
@@ -174,8 +153,6 @@ func initDefaultMap() {
 	defaultMap.RegisterType(&Type{Name: "_varbit", OID: VarbitArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[VarbitOID]}})
 	defaultMap.RegisterType(&Type{Name: "_varchar", OID: VarcharArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[VarcharOID]}})
 	defaultMap.RegisterType(&Type{Name: "_xid", OID: XIDArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[XIDOID]}})
-	defaultMap.RegisterType(&Type{Name: "_xid8", OID: XID8ArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[XID8OID]}})
-	defaultMap.RegisterType(&Type{Name: "_xml", OID: XMLArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[XMLOID]}})
 
 	// Integer types that directly map to a PostgreSQL type
 	registerDefaultPgTypeVariants[int16](defaultMap, "int2")
diff --git a/pgtype/time.go b/pgtype/time.go
index f8fd9489..61a3abdf 100644
--- a/pgtype/time.go
+++ b/pgtype/time.go
@@ -19,11 +19,9 @@ type TimeValuer interface {
 
 // Time represents the PostgreSQL time type. The PostgreSQL time is a time of day without time zone.
 //
-// Time is represented as the number of microseconds since midnight in the same way that PostgreSQL does. Other time and
-// date types in pgtype can use time.Time as the underlying representation. However, pgtype.Time type cannot due to
-// needing to handle 24:00:00. time.Time converts that to 00:00:00 on the following day.
-//
-// The time with time zone type is not supported. Use of time with time zone is discouraged by the PostgreSQL documentation.
+// Time is represented as the number of microseconds since midnight in the same way that PostgreSQL does. Other time
+// and date types in pgtype can use time.Time as the underlying representation. However, pgtype.Time type cannot due
+// to needing to handle 24:00:00. time.Time converts that to 00:00:00 on the following day.
 type Time struct {
 	Microseconds int64 // Number of microseconds since midnight
 	Valid        bool
diff --git a/pgtype/timestamp.go b/pgtype/timestamp.go
index c31f2ac5..677a2c6e 100644
--- a/pgtype/timestamp.go
+++ b/pgtype/timestamp.go
@@ -12,7 +12,6 @@ import (
 )
 
 const pgTimestampFormat = "2006-01-02 15:04:05.999999999"
-const jsonISO8601 = "2006-01-02T15:04:05.999999999"
 
 type TimestampScanner interface {
 	ScanTimestamp(v Timestamp) error
@@ -77,7 +76,7 @@ func (ts Timestamp) MarshalJSON() ([]byte, error) {
 
 	switch ts.InfinityModifier {
 	case Finite:
-		s = ts.Time.Format(jsonISO8601)
+		s = ts.Time.Format(time.RFC3339Nano)
 	case Infinity:
 		s = "infinity"
 	case NegativeInfinity:
@@ -105,23 +104,15 @@ func (ts *Timestamp) UnmarshalJSON(b []byte) error {
 	case "-infinity":
 		*ts = Timestamp{Valid: true, InfinityModifier: -Infinity}
 	default:
-		// Parse time with or without timezonr
-		tss := *s
-		//		PostgreSQL uses ISO 8601 without timezone for to_json function and casting from a string to timestampt
-		tim, err := time.Parse(time.RFC3339Nano, tss)
-		if err == nil {
-			*ts = Timestamp{Time: tim, Valid: true}
-			return nil
-		}
-		tim, err = time.ParseInLocation(jsonISO8601, tss, time.UTC)
-		if err == nil {
-			*ts = Timestamp{Time: tim, Valid: true}
-			return nil
+		// PostgreSQL uses ISO 8601 for to_json function and casting from a string to timestamptz
+		tim, err := time.Parse(time.RFC3339Nano, *s)
+		if err != nil {
+			return err
 		}
-		ts.Valid = false
-		return fmt.Errorf("cannot unmarshal %s to timestamp with layout %s or %s (%w)",
-			*s, time.RFC3339Nano, jsonISO8601, err)
+
+		*ts = Timestamp{Time: tim, Valid: true}
 	}
+
 	return nil
 }
 
@@ -234,6 +225,7 @@ func discardTimeZone(t time.Time) time.Time {
 }
 
 func (c *TimestampCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
+
 	switch format {
 	case BinaryFormatCode:
 		switch target.(type) {
diff --git a/pgtype/uint32.go b/pgtype/uint32.go
index f2b2fa6d..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,26 +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{})
-	}
-
-	if len(src) != 4 {
-		return fmt.Errorf("invalid length for uint32: %v", len(src))
-	}
-
-	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 {
diff --git a/pgtype/uint64.go b/pgtype/uint64.go
deleted file mode 100644
index dd2130eb..00000000
--- a/pgtype/uint64.go
+++ /dev/null
@@ -1,322 +0,0 @@
-package pgtype
-
-import (
-	"database/sql/driver"
-	"encoding/binary"
-	"fmt"
-	"math"
-	"strconv"
-
-	"github.com/jackc/pgx/v5/internal/pgio"
-)
-
-type Uint64Scanner interface {
-	ScanUint64(v Uint64) error
-}
-
-type Uint64Valuer interface {
-	Uint64Value() (Uint64, error)
-}
-
-// Uint64 is the core type that is used to represent PostgreSQL types such as XID8.
-type Uint64 struct {
-	Uint64 uint64
-	Valid  bool
-}
-
-func (n *Uint64) ScanUint64(v Uint64) error {
-	*n = v
-	return nil
-}
-
-func (n Uint64) Uint64Value() (Uint64, error) {
-	return n, nil
-}
-
-// Scan implements the database/sql Scanner interface.
-func (dst *Uint64) Scan(src any) error {
-	if src == nil {
-		*dst = Uint64{}
-		return nil
-	}
-
-	var n uint64
-
-	switch src := src.(type) {
-	case int64:
-		if src < 0 {
-			return fmt.Errorf("%d is less than the minimum value for Uint64", src)
-		}
-		n = uint64(src)
-	case string:
-		un, err := strconv.ParseUint(src, 10, 64)
-		if err != nil {
-			return err
-		}
-		n = un
-	default:
-		return fmt.Errorf("cannot scan %T", src)
-	}
-
-	*dst = Uint64{Uint64: n, Valid: true}
-
-	return nil
-}
-
-// Value implements the database/sql/driver Valuer interface.
-func (src Uint64) Value() (driver.Value, error) {
-	if !src.Valid {
-		return nil, nil
-	}
-
-	// If the value is greater than the maximum value for int64, return it as a string instead of losing data or returning
-	// an error.
-	if src.Uint64 > math.MaxInt64 {
-		return strconv.FormatUint(src.Uint64, 10), nil
-	}
-
-	return int64(src.Uint64), nil
-}
-
-type Uint64Codec struct{}
-
-func (Uint64Codec) FormatSupported(format int16) bool {
-	return format == TextFormatCode || format == BinaryFormatCode
-}
-
-func (Uint64Codec) PreferredFormat() int16 {
-	return BinaryFormatCode
-}
-
-func (Uint64Codec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {
-	switch format {
-	case BinaryFormatCode:
-		switch value.(type) {
-		case uint64:
-			return encodePlanUint64CodecBinaryUint64{}
-		case Uint64Valuer:
-			return encodePlanUint64CodecBinaryUint64Valuer{}
-		case Int64Valuer:
-			return encodePlanUint64CodecBinaryInt64Valuer{}
-		}
-	case TextFormatCode:
-		switch value.(type) {
-		case uint64:
-			return encodePlanUint64CodecTextUint64{}
-		case Int64Valuer:
-			return encodePlanUint64CodecTextInt64Valuer{}
-		}
-	}
-
-	return nil
-}
-
-type encodePlanUint64CodecBinaryUint64 struct{}
-
-func (encodePlanUint64CodecBinaryUint64) Encode(value any, buf []byte) (newBuf []byte, err error) {
-	v := value.(uint64)
-	return pgio.AppendUint64(buf, v), nil
-}
-
-type encodePlanUint64CodecBinaryUint64Valuer struct{}
-
-func (encodePlanUint64CodecBinaryUint64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {
-	v, err := value.(Uint64Valuer).Uint64Value()
-	if err != nil {
-		return nil, err
-	}
-
-	if !v.Valid {
-		return nil, nil
-	}
-
-	return pgio.AppendUint64(buf, v.Uint64), nil
-}
-
-type encodePlanUint64CodecBinaryInt64Valuer struct{}
-
-func (encodePlanUint64CodecBinaryInt64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {
-	v, err := value.(Int64Valuer).Int64Value()
-	if err != nil {
-		return nil, err
-	}
-
-	if !v.Valid {
-		return nil, nil
-	}
-
-	if v.Int64 < 0 {
-		return nil, fmt.Errorf("%d is less than minimum value for uint64", v.Int64)
-	}
-
-	return pgio.AppendUint64(buf, uint64(v.Int64)), nil
-}
-
-type encodePlanUint64CodecTextUint64 struct{}
-
-func (encodePlanUint64CodecTextUint64) Encode(value any, buf []byte) (newBuf []byte, err error) {
-	v := value.(uint64)
-	return append(buf, strconv.FormatUint(uint64(v), 10)...), nil
-}
-
-type encodePlanUint64CodecTextUint64Valuer struct{}
-
-func (encodePlanUint64CodecTextUint64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {
-	v, err := value.(Uint64Valuer).Uint64Value()
-	if err != nil {
-		return nil, err
-	}
-
-	if !v.Valid {
-		return nil, nil
-	}
-
-	return append(buf, strconv.FormatUint(v.Uint64, 10)...), nil
-}
-
-type encodePlanUint64CodecTextInt64Valuer struct{}
-
-func (encodePlanUint64CodecTextInt64Valuer) Encode(value any, buf []byte) (newBuf []byte, err error) {
-	v, err := value.(Int64Valuer).Int64Value()
-	if err != nil {
-		return nil, err
-	}
-
-	if !v.Valid {
-		return nil, nil
-	}
-
-	if v.Int64 < 0 {
-		return nil, fmt.Errorf("%d is less than minimum value for uint64", v.Int64)
-	}
-
-	return append(buf, strconv.FormatInt(v.Int64, 10)...), nil
-}
-
-func (Uint64Codec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
-
-	switch format {
-	case BinaryFormatCode:
-		switch target.(type) {
-		case *uint64:
-			return scanPlanBinaryUint64ToUint64{}
-		case Uint64Scanner:
-			return scanPlanBinaryUint64ToUint64Scanner{}
-		case TextScanner:
-			return scanPlanBinaryUint64ToTextScanner{}
-		}
-	case TextFormatCode:
-		switch target.(type) {
-		case *uint64:
-			return scanPlanTextAnyToUint64{}
-		case Uint64Scanner:
-			return scanPlanTextAnyToUint64Scanner{}
-		}
-	}
-
-	return nil
-}
-
-func (c Uint64Codec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {
-	if src == nil {
-		return nil, nil
-	}
-
-	var n uint64
-	err := codecScan(c, m, oid, format, src, &n)
-	if err != nil {
-		return nil, err
-	}
-	return int64(n), nil
-}
-
-func (c Uint64Codec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {
-	if src == nil {
-		return nil, nil
-	}
-
-	var n uint64
-	err := codecScan(c, m, oid, format, src, &n)
-	if err != nil {
-		return nil, err
-	}
-	return n, nil
-}
-
-type scanPlanBinaryUint64ToUint64 struct{}
-
-func (scanPlanBinaryUint64ToUint64) Scan(src []byte, dst any) error {
-	if src == nil {
-		return fmt.Errorf("cannot scan NULL into %T", dst)
-	}
-
-	if len(src) != 8 {
-		return fmt.Errorf("invalid length for uint64: %v", len(src))
-	}
-
-	p := (dst).(*uint64)
-	*p = binary.BigEndian.Uint64(src)
-
-	return nil
-}
-
-type scanPlanBinaryUint64ToUint64Scanner struct{}
-
-func (scanPlanBinaryUint64ToUint64Scanner) Scan(src []byte, dst any) error {
-	s, ok := (dst).(Uint64Scanner)
-	if !ok {
-		return ErrScanTargetTypeChanged
-	}
-
-	if src == nil {
-		return s.ScanUint64(Uint64{})
-	}
-
-	if len(src) != 8 {
-		return fmt.Errorf("invalid length for uint64: %v", len(src))
-	}
-
-	n := binary.BigEndian.Uint64(src)
-
-	return s.ScanUint64(Uint64{Uint64: n, Valid: true})
-}
-
-type scanPlanBinaryUint64ToTextScanner struct{}
-
-func (scanPlanBinaryUint64ToTextScanner) Scan(src []byte, dst any) error {
-	s, ok := (dst).(TextScanner)
-	if !ok {
-		return ErrScanTargetTypeChanged
-	}
-
-	if src == nil {
-		return s.ScanText(Text{})
-	}
-
-	if len(src) != 8 {
-		return fmt.Errorf("invalid length for uint64: %v", len(src))
-	}
-
-	n := uint64(binary.BigEndian.Uint64(src))
-	return s.ScanText(Text{String: strconv.FormatUint(n, 10), Valid: true})
-}
-
-type scanPlanTextAnyToUint64Scanner struct{}
-
-func (scanPlanTextAnyToUint64Scanner) Scan(src []byte, dst any) error {
-	s, ok := (dst).(Uint64Scanner)
-	if !ok {
-		return ErrScanTargetTypeChanged
-	}
-
-	if src == nil {
-		return s.ScanUint64(Uint64{})
-	}
-
-	n, err := strconv.ParseUint(string(src), 10, 64)
-	if err != nil {
-		return err
-	}
-
-	return s.ScanUint64(Uint64{Uint64: n, Valid: true})
-}
diff --git a/pgtype/uuid.go b/pgtype/uuid.go
index 0628f193..d57c0f2f 100644
--- a/pgtype/uuid.go
+++ b/pgtype/uuid.go
@@ -96,14 +96,6 @@ func (src UUID) Value() (driver.Value, error) {
 	return encodeUUID(src.Bytes), nil
 }
 
-func (src UUID) String() string {
-	if !src.Valid {
-		return ""
-	}
-
-	return encodeUUID(src.Bytes)
-}
-
 func (src UUID) MarshalJSON() ([]byte, error) {
 	if !src.Valid {
 		return []byte("null"), nil
diff --git a/pgtype/xml.go b/pgtype/xml.go
deleted file mode 100644
index 79e3698a..00000000
--- a/pgtype/xml.go
+++ /dev/null
@@ -1,198 +0,0 @@
-package pgtype
-
-import (
-	"database/sql"
-	"database/sql/driver"
-	"encoding/xml"
-	"fmt"
-	"reflect"
-)
-
-type XMLCodec struct {
-	Marshal   func(v any) ([]byte, error)
-	Unmarshal func(data []byte, v any) error
-}
-
-func (*XMLCodec) FormatSupported(format int16) bool {
-	return format == TextFormatCode || format == BinaryFormatCode
-}
-
-func (*XMLCodec) PreferredFormat() int16 {
-	return TextFormatCode
-}
-
-func (c *XMLCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {
-	switch value.(type) {
-	case string:
-		return encodePlanXMLCodecEitherFormatString{}
-	case []byte:
-		return encodePlanXMLCodecEitherFormatByteSlice{}
-
-	// Cannot rely on driver.Valuer being handled later because anything can be marshalled.
-	//
-	// https://github.com/jackc/pgx/issues/1430
-	//
-	// Check for driver.Valuer must come before xml.Marshaler so that it is guaranteed to be used
-	// when both are implemented https://github.com/jackc/pgx/issues/1805
-	case driver.Valuer:
-		return &encodePlanDriverValuer{m: m, oid: oid, formatCode: format}
-
-	// Must come before trying wrap encode plans because a pointer to a struct may be unwrapped to a struct that can be
-	// marshalled.
-	//
-	// https://github.com/jackc/pgx/issues/1681
-	case xml.Marshaler:
-		return &encodePlanXMLCodecEitherFormatMarshal{
-			marshal: c.Marshal,
-		}
-	}
-
-	// Because anything can be marshalled the normal wrapping in Map.PlanScan doesn't get a chance to run. So try the
-	// appropriate wrappers here.
-	for _, f := range []TryWrapEncodePlanFunc{
-		TryWrapDerefPointerEncodePlan,
-		TryWrapFindUnderlyingTypeEncodePlan,
-	} {
-		if wrapperPlan, nextValue, ok := f(value); ok {
-			if nextPlan := c.PlanEncode(m, oid, format, nextValue); nextPlan != nil {
-				wrapperPlan.SetNext(nextPlan)
-				return wrapperPlan
-			}
-		}
-	}
-
-	return &encodePlanXMLCodecEitherFormatMarshal{
-		marshal: c.Marshal,
-	}
-}
-
-type encodePlanXMLCodecEitherFormatString struct{}
-
-func (encodePlanXMLCodecEitherFormatString) Encode(value any, buf []byte) (newBuf []byte, err error) {
-	xmlString := value.(string)
-	buf = append(buf, xmlString...)
-	return buf, nil
-}
-
-type encodePlanXMLCodecEitherFormatByteSlice struct{}
-
-func (encodePlanXMLCodecEitherFormatByteSlice) Encode(value any, buf []byte) (newBuf []byte, err error) {
-	xmlBytes := value.([]byte)
-	if xmlBytes == nil {
-		return nil, nil
-	}
-
-	buf = append(buf, xmlBytes...)
-	return buf, nil
-}
-
-type encodePlanXMLCodecEitherFormatMarshal struct {
-	marshal func(v any) ([]byte, error)
-}
-
-func (e *encodePlanXMLCodecEitherFormatMarshal) Encode(value any, buf []byte) (newBuf []byte, err error) {
-	xmlBytes, err := e.marshal(value)
-	if err != nil {
-		return nil, err
-	}
-
-	buf = append(buf, xmlBytes...)
-	return buf, nil
-}
-
-func (c *XMLCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
-	switch target.(type) {
-	case *string:
-		return scanPlanAnyToString{}
-
-	case **string:
-		// This is to fix **string scanning. It seems wrong to special case **string, but it's not clear what a better
-		// solution would be.
-		//
-		// https://github.com/jackc/pgx/issues/1470 -- **string
-		// https://github.com/jackc/pgx/issues/1691 -- ** anything else
-
-		if wrapperPlan, nextDst, ok := TryPointerPointerScanPlan(target); ok {
-			if nextPlan := m.planScan(oid, format, nextDst, 0); nextPlan != nil {
-				if _, failed := nextPlan.(*scanPlanFail); !failed {
-					wrapperPlan.SetNext(nextPlan)
-					return wrapperPlan
-				}
-			}
-		}
-
-	case *[]byte:
-		return scanPlanXMLToByteSlice{}
-	case BytesScanner:
-		return scanPlanBinaryBytesToBytesScanner{}
-
-	// Cannot rely on sql.Scanner being handled later because scanPlanXMLToXMLUnmarshal will take precedence.
-	//
-	// https://github.com/jackc/pgx/issues/1418
-	case sql.Scanner:
-		return &scanPlanSQLScanner{formatCode: format}
-	}
-
-	return &scanPlanXMLToXMLUnmarshal{
-		unmarshal: c.Unmarshal,
-	}
-}
-
-type scanPlanXMLToByteSlice struct{}
-
-func (scanPlanXMLToByteSlice) Scan(src []byte, dst any) error {
-	dstBuf := dst.(*[]byte)
-	if src == nil {
-		*dstBuf = nil
-		return nil
-	}
-
-	*dstBuf = make([]byte, len(src))
-	copy(*dstBuf, src)
-	return nil
-}
-
-type scanPlanXMLToXMLUnmarshal struct {
-	unmarshal func(data []byte, v any) error
-}
-
-func (s *scanPlanXMLToXMLUnmarshal) Scan(src []byte, dst any) error {
-	if src == nil {
-		dstValue := reflect.ValueOf(dst)
-		if dstValue.Kind() == reflect.Ptr {
-			el := dstValue.Elem()
-			switch el.Kind() {
-			case reflect.Ptr, reflect.Slice, reflect.Map, reflect.Interface, reflect.Struct:
-				el.Set(reflect.Zero(el.Type()))
-				return nil
-			}
-		}
-
-		return fmt.Errorf("cannot scan NULL into %T", dst)
-	}
-
-	elem := reflect.ValueOf(dst).Elem()
-	elem.Set(reflect.Zero(elem.Type()))
-
-	return s.unmarshal(src, dst)
-}
-
-func (c *XMLCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {
-	if src == nil {
-		return nil, nil
-	}
-
-	dstBuf := make([]byte, len(src))
-	copy(dstBuf, src)
-	return dstBuf, nil
-}
-
-func (c *XMLCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {
-	if src == nil {
-		return nil, nil
-	}
-
-	var dst any
-	err := c.Unmarshal(src, &dst)
-	return dst, err
-}
diff --git a/pgtype/zeronull/int.go b/pgtype/zeronull/int.go
index 7fa0210e..4fec8a1a 100644
--- a/pgtype/zeronull/int.go
+++ b/pgtype/zeronull/int.go
@@ -1,5 +1,4 @@
-// Code generated from pgtype/zeronull/int.go.erb. DO NOT EDIT.
-
+// Do not edit. Generated from pgtype/zeronull/int.go.erb
 package zeronull
 
 import (
diff --git a/pgxpool/pool.go b/pgxpool/pool.go
index 270b7617..fdcba724 100644
--- a/pgxpool/pool.go
+++ b/pgxpool/pool.go
@@ -281,20 +281,20 @@ func NewWithConfig(ctx context.Context, config *Config) (*Pool, error) {
 // ParseConfig builds a Config from connString. It parses connString with the same behavior as [pgx.ParseConfig] with the
 // addition of the following variables:
 //
-//   - pool_max_conns: integer greater than 0 (default 4)
-//   - pool_min_conns: integer 0 or greater (default 0)
-//   - pool_max_conn_lifetime: duration string (default 1 hour)
-//   - pool_max_conn_idle_time: duration string (default 30 minutes)
-//   - pool_health_check_period: duration string (default 1 minute)
-//   - pool_max_conn_lifetime_jitter: duration string (default 0)
+//   - pool_max_conns: integer greater than 0
+//   - pool_min_conns: integer 0 or greater
+//   - pool_max_conn_lifetime: duration string
+//   - pool_max_conn_idle_time: duration string
+//   - pool_health_check_period: duration string
+//   - pool_max_conn_lifetime_jitter: duration string
 //
 // See Config for definitions of these arguments.
 //
 //	# Example Keyword/Value
-//	user=jack password=secret host=pg.example.com port=5432 dbname=mydb sslmode=verify-ca pool_max_conns=10 pool_max_conn_lifetime=1h30m
+//	user=jack password=secret host=pg.example.com port=5432 dbname=mydb sslmode=verify-ca pool_max_conns=10
 //
 //	# Example URL
-//	postgres://jack:secret@pg.example.com:5432/mydb?sslmode=verify-ca&pool_max_conns=10&pool_max_conn_lifetime=1h30m
+//	postgres://jack:secret@pg.example.com:5432/mydb?sslmode=verify-ca&pool_max_conns=10
 func ParseConfig(connString string) (*Config, error) {
 	connConfig, err := pgx.ParseConfig(connString)
 	if err != nil {
diff --git a/pgxpool/stat.go b/pgxpool/stat.go
index e02b6ac3..cfa0c4c5 100644
--- a/pgxpool/stat.go
+++ b/pgxpool/stat.go
@@ -82,10 +82,3 @@ func (s *Stat) MaxLifetimeDestroyCount() int64 {
 func (s *Stat) MaxIdleDestroyCount() int64 {
 	return s.idleDestroyCount
 }
-
-// EmptyAcquireWaitTime returns the cumulative time waited for successful acquires
-// from the pool for a resource to be released or constructed because the pool was
-// empty.
-func (s *Stat) EmptyAcquireWaitTime() time.Duration {
-	return s.s.EmptyAcquireWaitTime()
-}
diff --git a/pgxpool/tx.go b/pgxpool/tx.go
index b49e7f4d..74df8593 100644
--- a/pgxpool/tx.go
+++ b/pgxpool/tx.go
@@ -18,10 +18,9 @@ func (tx *Tx) Begin(ctx context.Context) (pgx.Tx, error) {
 	return tx.t.Begin(ctx)
 }
 
-// Commit commits the transaction and returns the associated connection back to the Pool. Commit will return an error
-// where errors.Is(ErrTxClosed) is true if the Tx is already closed, but is otherwise safe to call multiple times. If
-// the commit fails with a rollback status (e.g. the transaction was already in a broken state) then ErrTxCommitRollback
-// will be returned.
+// Commit commits the transaction and returns the associated connection back to the Pool. Commit will return ErrTxClosed
+// if the Tx is already closed, but is otherwise safe to call multiple times. If the commit fails with a rollback status
+// (e.g. the transaction was already in a broken state) then ErrTxCommitRollback will be returned.
 func (tx *Tx) Commit(ctx context.Context) error {
 	err := tx.t.Commit(ctx)
 	if tx.c != nil {
@@ -31,9 +30,9 @@ func (tx *Tx) Commit(ctx context.Context) error {
 	return err
 }
 
-// Rollback rolls back the transaction and returns the associated connection back to the Pool. Rollback will return
-// where an error where errors.Is(ErrTxClosed) is true if the Tx is already closed, but is otherwise safe to call
-// multiple times. Hence, defer tx.Rollback() is safe even if tx.Commit() will be called first in a non-error condition.
+// Rollback rolls back the transaction and returns the associated connection back to the Pool. Rollback will return ErrTxClosed
+// if the Tx is already closed, but is otherwise safe to call multiple times. Hence, defer tx.Rollback() is safe even if
+// tx.Commit() will be called first in a non-error condition.
 func (tx *Tx) Rollback(ctx context.Context) error {
 	err := tx.t.Rollback(ctx)
 	if tx.c != nil {
diff --git a/rows.go b/rows.go
index 268559ce..d4f7a901 100644
--- a/rows.go
+++ b/rows.go
@@ -272,7 +272,7 @@ func (rows *baseRows) Scan(dest ...any) error {
 
 		err := rows.scanPlans[i].Scan(values[i], dst)
 		if err != nil {
-			err = ScanArgError{ColumnIndex: i, FieldName: fieldDescriptions[i].Name, Err: err}
+			err = ScanArgError{ColumnIndex: i, Err: err}
 			rows.fatal(err)
 			return err
 		}
@@ -334,16 +334,11 @@ func (rows *baseRows) Conn() *Conn {
 
 type ScanArgError struct {
 	ColumnIndex int
-	FieldName   string
 	Err         error
 }
 
 func (e ScanArgError) Error() string {
-	if e.FieldName == "?column?" { // Don't include the fieldname if it's unknown
-		return fmt.Sprintf("can't scan into dest[%d]: %v", e.ColumnIndex, e.Err)
-	}
-
-	return fmt.Sprintf("can't scan into dest[%d] (col: %s): %v", e.ColumnIndex, e.FieldName, e.Err)
+	return fmt.Sprintf("can't scan into dest[%d]: %v", e.ColumnIndex, e.Err)
 }
 
 func (e ScanArgError) Unwrap() error {
@@ -371,7 +366,7 @@ func ScanRow(typeMap *pgtype.Map, fieldDescriptions []pgconn.FieldDescription, v
 
 		err := typeMap.Scan(fieldDescriptions[i].DataTypeOID, fieldDescriptions[i].Format, values[i], d)
 		if err != nil {
-			return ScanArgError{ColumnIndex: i, FieldName: fieldDescriptions[i].Name, Err: err}
+			return ScanArgError{ColumnIndex: i, Err: err}
 		}
 	}
 
@@ -802,7 +797,7 @@ func computeNamedStructFields(
 			if !dbTagPresent {
 				colName = sf.Name
 			}
-			fpos := fieldPosByName(fldDescs, colName, !dbTagPresent)
+			fpos := fieldPosByName(fldDescs, colName)
 			if fpos == -1 {
 				if missingField == "" {
 					missingField = colName
@@ -821,21 +816,16 @@ func computeNamedStructFields(
 
 const structTagKey = "db"
 
-func fieldPosByName(fldDescs []pgconn.FieldDescription, field string, normalize bool) (i int) {
+func fieldPosByName(fldDescs []pgconn.FieldDescription, field string) (i int) {
 	i = -1
+	for i, desc := range fldDescs {
 
-	if normalize {
+		// Snake case support.
 		field = strings.ReplaceAll(field, "_", "")
-	}
-	for i, desc := range fldDescs {
-		if normalize {
-			if strings.EqualFold(strings.ReplaceAll(desc.Name, "_", ""), field) {
-				return i
-			}
-		} else {
-			if desc.Name == field {
-				return i
-			}
+		descName := strings.ReplaceAll(desc.Name, "_", "")
+
+		if strings.EqualFold(descName, field) {
+			return i
 		}
 	}
 	return
diff --git a/stdlib/sql.go b/stdlib/sql.go
index c1d00ab4..29cd3fbb 100644
--- a/stdlib/sql.go
+++ b/stdlib/sql.go
@@ -75,7 +75,6 @@ import (
 	"math"
 	"math/rand"
 	"reflect"
-	"slices"
 	"strconv"
 	"strings"
 	"sync"
@@ -99,7 +98,7 @@ func init() {
 
 	// if pgx driver was already registered by different pgx major version then we
 	// skip registration under the default name.
-	if !slices.Contains(sql.Drivers(), "pgx") {
+	if !contains(sql.Drivers(), "pgx") {
 		sql.Register("pgx", pgxDriver)
 	}
 	sql.Register("pgx/v5", pgxDriver)
@@ -121,6 +120,17 @@ func init() {
 	}
 }
 
+// TODO replace by slices.Contains when experimental package will be merged to stdlib
+// https://pkg.go.dev/golang.org/x/exp/slices#Contains
+func contains(list []string, y string) bool {
+	for _, x := range list {
+		if x == y {
+			return true
+		}
+	}
+	return false
+}
+
 // OptionOpenDB options for configuring the driver when opening a new db pool.
 type OptionOpenDB func(*connector)
 
@@ -795,16 +805,6 @@ func (r *Rows) Next(dest []driver.Value) error {
 					}
 					return d.Value()
 				}
-			case pgtype.XMLOID:
-				var d []byte
-				scanPlan := m.PlanScan(dataTypeOID, format, &d)
-				r.valueFuncs[i] = func(src []byte) (driver.Value, error) {
-					err := scanPlan.Scan(src, &d)
-					if err != nil {
-						return nil, err
-					}
-					return d, nil
-				}
 			default:
 				var d string
 				scanPlan := m.PlanScan(dataTypeOID, format, &d)
diff --git a/tracelog/tracelog.go b/tracelog/tracelog.go
index 61015354..78b15fbe 100644
--- a/tracelog/tracelog.go
+++ b/tracelog/tracelog.go
@@ -6,7 +6,6 @@ import (
 	"encoding/hex"
 	"errors"
 	"fmt"
-	"sync"
 	"time"
 	"unicode/utf8"
 
@@ -118,37 +117,11 @@ func logQueryArgs(args []any) []any {
 	return logArgs
 }
 
-// TraceLogConfig holds the configuration for key names
-type TraceLogConfig struct {
-	TimeKey string
-}
-
-// DefaultTraceLogConfig returns the default configuration for TraceLog
-func DefaultTraceLogConfig() *TraceLogConfig {
-	return &TraceLogConfig{
-		TimeKey: "time",
-	}
-}
-
-// TraceLog implements pgx.QueryTracer, pgx.BatchTracer, pgx.ConnectTracer, and pgx.CopyFromTracer. Logger and LogLevel
-// are required. Config will be automatically initialized on first use if nil.
+// TraceLog implements pgx.QueryTracer, pgx.BatchTracer, pgx.ConnectTracer, and pgx.CopyFromTracer. All fields are
+// required.
 type TraceLog struct {
 	Logger   Logger
 	LogLevel LogLevel
-
-	Config           *TraceLogConfig
-	ensureConfigOnce sync.Once
-}
-
-// ensureConfig initializes the Config field with default values if it is nil.
-func (tl *TraceLog) ensureConfig() {
-	tl.ensureConfigOnce.Do(
-		func() {
-			if tl.Config == nil {
-				tl.Config = DefaultTraceLogConfig()
-			}
-		},
-	)
 }
 
 type ctxKey int
@@ -177,7 +150,6 @@ func (tl *TraceLog) TraceQueryStart(ctx context.Context, conn *pgx.Conn, data pg
 }
 
 func (tl *TraceLog) TraceQueryEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryEndData) {
-	tl.ensureConfig()
 	queryData := ctx.Value(tracelogQueryCtxKey).(*traceQueryData)
 
 	endTime := time.Now()
@@ -185,13 +157,13 @@ func (tl *TraceLog) TraceQueryEnd(ctx context.Context, conn *pgx.Conn, data pgx.
 
 	if data.Err != nil {
 		if tl.shouldLog(LogLevelError) {
-			tl.log(ctx, conn, LogLevelError, "Query", map[string]any{"sql": queryData.sql, "args": logQueryArgs(queryData.args), "err": data.Err, tl.Config.TimeKey: interval})
+			tl.log(ctx, conn, LogLevelError, "Query", map[string]any{"sql": queryData.sql, "args": logQueryArgs(queryData.args), "err": data.Err, "time": interval})
 		}
 		return
 	}
 
 	if tl.shouldLog(LogLevelInfo) {
-		tl.log(ctx, conn, LogLevelInfo, "Query", map[string]any{"sql": queryData.sql, "args": logQueryArgs(queryData.args), tl.Config.TimeKey: interval, "commandTag": data.CommandTag.String()})
+		tl.log(ctx, conn, LogLevelInfo, "Query", map[string]any{"sql": queryData.sql, "args": logQueryArgs(queryData.args), "time": interval, "commandTag": data.CommandTag.String()})
 	}
 }
 
@@ -219,7 +191,6 @@ func (tl *TraceLog) TraceBatchQuery(ctx context.Context, conn *pgx.Conn, data pg
 }
 
 func (tl *TraceLog) TraceBatchEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchEndData) {
-	tl.ensureConfig()
 	queryData := ctx.Value(tracelogBatchCtxKey).(*traceBatchData)
 
 	endTime := time.Now()
@@ -227,13 +198,13 @@ func (tl *TraceLog) TraceBatchEnd(ctx context.Context, conn *pgx.Conn, data pgx.
 
 	if data.Err != nil {
 		if tl.shouldLog(LogLevelError) {
-			tl.log(ctx, conn, LogLevelError, "BatchClose", map[string]any{"err": data.Err, tl.Config.TimeKey: interval})
+			tl.log(ctx, conn, LogLevelError, "BatchClose", map[string]any{"err": data.Err, "time": interval})
 		}
 		return
 	}
 
 	if tl.shouldLog(LogLevelInfo) {
-		tl.log(ctx, conn, LogLevelInfo, "BatchClose", map[string]any{tl.Config.TimeKey: interval})
+		tl.log(ctx, conn, LogLevelInfo, "BatchClose", map[string]any{"time": interval})
 	}
 }
 
@@ -252,7 +223,6 @@ func (tl *TraceLog) TraceCopyFromStart(ctx context.Context, conn *pgx.Conn, data
 }
 
 func (tl *TraceLog) TraceCopyFromEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceCopyFromEndData) {
-	tl.ensureConfig()
 	copyFromData := ctx.Value(tracelogCopyFromCtxKey).(*traceCopyFromData)
 
 	endTime := time.Now()
@@ -260,13 +230,13 @@ func (tl *TraceLog) TraceCopyFromEnd(ctx context.Context, conn *pgx.Conn, data p
 
 	if data.Err != nil {
 		if tl.shouldLog(LogLevelError) {
-			tl.log(ctx, conn, LogLevelError, "CopyFrom", map[string]any{"tableName": copyFromData.TableName, "columnNames": copyFromData.ColumnNames, "err": data.Err, tl.Config.TimeKey: interval})
+			tl.log(ctx, conn, LogLevelError, "CopyFrom", map[string]any{"tableName": copyFromData.TableName, "columnNames": copyFromData.ColumnNames, "err": data.Err, "time": interval})
 		}
 		return
 	}
 
 	if tl.shouldLog(LogLevelInfo) {
-		tl.log(ctx, conn, LogLevelInfo, "CopyFrom", map[string]any{"tableName": copyFromData.TableName, "columnNames": copyFromData.ColumnNames, "err": data.Err, tl.Config.TimeKey: interval, "rowCount": data.CommandTag.RowsAffected()})
+		tl.log(ctx, conn, LogLevelInfo, "CopyFrom", map[string]any{"tableName": copyFromData.TableName, "columnNames": copyFromData.ColumnNames, "err": data.Err, "time": interval, "rowCount": data.CommandTag.RowsAffected()})
 	}
 }
 
@@ -283,7 +253,6 @@ func (tl *TraceLog) TraceConnectStart(ctx context.Context, data pgx.TraceConnect
 }
 
 func (tl *TraceLog) TraceConnectEnd(ctx context.Context, data pgx.TraceConnectEndData) {
-	tl.ensureConfig()
 	connectData := ctx.Value(tracelogConnectCtxKey).(*traceConnectData)
 
 	endTime := time.Now()
@@ -292,11 +261,11 @@ func (tl *TraceLog) TraceConnectEnd(ctx context.Context, data pgx.TraceConnectEn
 	if data.Err != nil {
 		if tl.shouldLog(LogLevelError) {
 			tl.Logger.Log(ctx, LogLevelError, "Connect", map[string]any{
-				"host":            connectData.connConfig.Host,
-				"port":            connectData.connConfig.Port,
-				"database":        connectData.connConfig.Database,
-				tl.Config.TimeKey: interval,
-				"err":             data.Err,
+				"host":     connectData.connConfig.Host,
+				"port":     connectData.connConfig.Port,
+				"database": connectData.connConfig.Database,
+				"time":     interval,
+				"err":      data.Err,
 			})
 		}
 		return
@@ -305,10 +274,10 @@ func (tl *TraceLog) TraceConnectEnd(ctx context.Context, data pgx.TraceConnectEn
 	if data.Conn != nil {
 		if tl.shouldLog(LogLevelInfo) {
 			tl.log(ctx, data.Conn, LogLevelInfo, "Connect", map[string]any{
-				"host":            connectData.connConfig.Host,
-				"port":            connectData.connConfig.Port,
-				"database":        connectData.connConfig.Database,
-				tl.Config.TimeKey: interval,
+				"host":     connectData.connConfig.Host,
+				"port":     connectData.connConfig.Port,
+				"database": connectData.connConfig.Database,
+				"time":     interval,
 			})
 		}
 	}
@@ -329,7 +298,6 @@ func (tl *TraceLog) TracePrepareStart(ctx context.Context, _ *pgx.Conn, data pgx
 }
 
 func (tl *TraceLog) TracePrepareEnd(ctx context.Context, conn *pgx.Conn, data pgx.TracePrepareEndData) {
-	tl.ensureConfig()
 	prepareData := ctx.Value(tracelogPrepareCtxKey).(*tracePrepareData)
 
 	endTime := time.Now()
@@ -337,13 +305,13 @@ func (tl *TraceLog) TracePrepareEnd(ctx context.Context, conn *pgx.Conn, data pg
 
 	if data.Err != nil {
 		if tl.shouldLog(LogLevelError) {
-			tl.log(ctx, conn, LogLevelError, "Prepare", map[string]any{"name": prepareData.name, "sql": prepareData.sql, "err": data.Err, tl.Config.TimeKey: interval})
+			tl.log(ctx, conn, LogLevelError, "Prepare", map[string]any{"name": prepareData.name, "sql": prepareData.sql, "err": data.Err, "time": interval})
 		}
 		return
 	}
 
 	if tl.shouldLog(LogLevelInfo) {
-		tl.log(ctx, conn, LogLevelInfo, "Prepare", map[string]any{"name": prepareData.name, "sql": prepareData.sql, tl.Config.TimeKey: interval, "alreadyPrepared": data.AlreadyPrepared})
+		tl.log(ctx, conn, LogLevelInfo, "Prepare", map[string]any{"name": prepareData.name, "sql": prepareData.sql, "time": interval, "alreadyPrepared": data.AlreadyPrepared})
 	}
 }
 
diff --git a/tx.go b/tx.go
index 571e5e00..8feeb512 100644
--- a/tx.go
+++ b/tx.go
@@ -3,6 +3,7 @@ package pgx
 import (
 	"context"
 	"errors"
+	"fmt"
 	"strconv"
 	"strings"
 
@@ -47,8 +48,6 @@ type TxOptions struct {
 	// BeginQuery is the SQL query that will be executed to begin the transaction. This allows using non-standard syntax
 	// such as BEGIN PRIORITY HIGH with CockroachDB. If set this will override the other settings.
 	BeginQuery string
-	// CommitQuery is the SQL query that will be executed to commit the transaction.
-	CommitQuery string
 }
 
 var emptyTxOptions TxOptions
@@ -102,14 +101,11 @@ func (c *Conn) BeginTx(ctx context.Context, txOptions TxOptions) (Tx, error) {
 	if err != nil {
 		// begin should never fail unless there is an underlying connection issue or
 		// a context timeout. In either case, the connection is possibly broken.
-		c.die()
+		c.die(errors.New("failed to begin transaction"))
 		return nil, err
 	}
 
-	return &dbTx{
-		conn:        c,
-		commitQuery: txOptions.CommitQuery,
-	}, nil
+	return &dbTx{conn: c}, nil
 }
 
 // Tx represents a database transaction.
@@ -158,7 +154,6 @@ type dbTx struct {
 	conn         *Conn
 	savepointNum int64
 	closed       bool
-	commitQuery  string
 }
 
 // Begin starts a pseudo nested transaction implemented with a savepoint.
@@ -182,12 +177,7 @@ func (tx *dbTx) Commit(ctx context.Context) error {
 		return ErrTxClosed
 	}
 
-	commandSQL := "commit"
-	if tx.commitQuery != "" {
-		commandSQL = tx.commitQuery
-	}
-
-	commandTag, err := tx.conn.Exec(ctx, commandSQL)
+	commandTag, err := tx.conn.Exec(ctx, "commit")
 	tx.closed = true
 	if err != nil {
 		if tx.conn.PgConn().TxStatus() != 'I' {
@@ -215,7 +205,7 @@ func (tx *dbTx) Rollback(ctx context.Context) error {
 	tx.closed = true
 	if err != nil {
 		// A rollback failure leaves the connection in an undefined state
-		tx.conn.die()
+		tx.conn.die(fmt.Errorf("rollback failed: %w", err))
 		return err
 	}
 
diff --git a/values.go b/values.go
index 6e2ff300..cab717d0 100644
--- a/values.go
+++ b/values.go
@@ -3,6 +3,7 @@ package pgx
 import (
 	"errors"
 
+	"github.com/jackc/pgx/v5/internal/anynil"
 	"github.com/jackc/pgx/v5/internal/pgio"
 	"github.com/jackc/pgx/v5/pgtype"
 )
@@ -14,6 +15,10 @@ const (
 )
 
 func convertSimpleArgument(m *pgtype.Map, arg any) (any, error) {
+	if anynil.Is(arg) {
+		return nil, nil
+	}
+
 	buf, err := m.Encode(0, TextFormatCode, arg, []byte{})
 	if err != nil {
 		return nil, err
@@ -25,6 +30,10 @@ func convertSimpleArgument(m *pgtype.Map, arg any) (any, error) {
 }
 
 func encodeCopyValue(m *pgtype.Map, buf []byte, oid uint32, arg any) ([]byte, error) {
+	if anynil.Is(arg) {
+		return pgio.AppendInt32(buf, -1), nil
+	}
+
 	sp := len(buf)
 	buf = pgio.AppendInt32(buf, -1)
 	argBuf, err := m.Encode(oid, BinaryFormatCode, arg, buf)


Test patch:
diff --git a/batch_test.go b/batch_test.go
index b1bc25de..9ff2417f 100644
--- a/batch_test.go
+++ b/batch_test.go
@@ -290,45 +290,6 @@ func TestConnSendBatchMany(t *testing.T) {
 	})
 }
 
-// https://github.com/jackc/pgx/issues/1801#issuecomment-2203784178
-func TestConnSendBatchReadResultsWhenNothingQueued(t *testing.T) {
-	t.Parallel()
-
-	ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
-	defer cancel()
-
-	pgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
-		batch := &pgx.Batch{}
-		br := conn.SendBatch(ctx, batch)
-		commandTag, err := br.Exec()
-		require.Equal(t, "", commandTag.String())
-		require.EqualError(t, err, "no more results in batch")
-		err = br.Close()
-		require.NoError(t, err)
-	})
-}
-
-func TestConnSendBatchReadMoreResultsThanQueriesSent(t *testing.T) {
-	t.Parallel()
-
-	ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
-	defer cancel()
-
-	pgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
-		batch := &pgx.Batch{}
-		batch.Queue("select 1")
-		br := conn.SendBatch(ctx, batch)
-		commandTag, err := br.Exec()
-		require.Equal(t, "SELECT 1", commandTag.String())
-		require.NoError(t, err)
-		commandTag, err = br.Exec()
-		require.Equal(t, "", commandTag.String())
-		require.EqualError(t, err, "no more results in batch")
-		err = br.Close()
-		require.NoError(t, err)
-	})
-}
-
 func TestConnSendBatchWithPreparedStatement(t *testing.T) {
 	t.Parallel()
 
@@ -1008,36 +969,6 @@ func TestSendBatchSimpleProtocol(t *testing.T) {
 	assert.False(t, rows.Next())
 }
 
-// https://github.com/jackc/pgx/issues/1847#issuecomment-2347858887
-func TestConnSendBatchErrorDoesNotLeaveOrphanedPreparedStatement(t *testing.T) {
-	t.Parallel()
-
-	ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
-	defer cancel()
-
-	pgxtest.RunWithQueryExecModes(ctx, t, defaultConnTestRunner, nil, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
-		pgxtest.SkipCockroachDB(t, conn, "Server serial type is incompatible with test")
-
-		mustExec(t, conn, `create temporary table foo(col1 text primary key);`)
-
-		batch := &pgx.Batch{}
-		batch.Queue("select col1 from foo")
-		batch.Queue("select col1 from baz")
-		err := conn.SendBatch(ctx, batch).Close()
-		require.EqualError(t, err, `ERROR: relation "baz" does not exist (SQLSTATE 42P01)`)
-
-		mustExec(t, conn, `create temporary table baz(col1 text primary key);`)
-
-		// Since table baz now exists, the batch should succeed.
-
-		batch = &pgx.Batch{}
-		batch.Queue("select col1 from foo")
-		batch.Queue("select col1 from baz")
-		err = conn.SendBatch(ctx, batch).Close()
-		require.NoError(t, err)
-	})
-}
-
 func ExampleConn_SendBatch() {
 	ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
 	defer cancel()
diff --git a/ci/setup_test.bash b/ci/setup_test.bash
index 66ba07d4..d591c512 100755
--- a/ci/setup_test.bash
+++ b/ci/setup_test.bash
@@ -42,8 +42,8 @@ fi
 
 if [[ "${PGVERSION-}" =~ ^cockroach ]]
 then
-  wget -qO- https://binaries.cockroachdb.com/cockroach-v23.1.3.linux-amd64.tgz | tar xvz
-  sudo mv cockroach-v23.1.3.linux-amd64/cockroach /usr/local/bin/
+  wget -qO- https://binaries.cockroachdb.com/cockroach-v24.3.3.linux-amd64.tgz | tar xvz
+  sudo mv cockroach-v24.3.3.linux-amd64/cockroach /usr/local/bin/
   cockroach start-single-node --insecure --background --listen-addr=localhost
   cockroach sql --insecure -e 'create database pgx_test'
 fi
diff --git a/conn_test.go b/conn_test.go
index d51f829b..df8c9186 100644
--- a/conn_test.go
+++ b/conn_test.go
@@ -3,7 +3,6 @@ package pgx_test
 import (
 	"bytes"
 	"context"
-	"database/sql"
 	"os"
 	"strings"
 	"sync"
@@ -1409,12 +1408,3 @@ 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")
-}
diff --git a/derived_types_test.go b/derived_types_test.go
deleted file mode 100644
index 6fb6e1d3..00000000
--- a/derived_types_test.go
+++ /dev/null
@@ -1,40 +0,0 @@
-package pgx_test
-
-import (
-	"context"
-	"testing"
-
-	"github.com/jackc/pgx/v5"
-	"github.com/stretchr/testify/require"
-)
-
-func TestCompositeCodecTranscodeWithLoadTypes(t *testing.T) {
-	skipCockroachDB(t, "Server does not support composite types (see https://github.com/cockroachdb/cockroach/issues/27792)")
-
-	defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
-		_, err := conn.Exec(ctx, `
-drop type if exists dtype_test;
-drop domain if exists anotheruint64;
-
-create domain anotheruint64 as numeric(20,0);
-create type dtype_test as (
-  a text,
-  b int4,
-  c anotheruint64,
-  d anotheruint64[]
-);`)
-		require.NoError(t, err)
-		defer conn.Exec(ctx, "drop type dtype_test")
-		defer conn.Exec(ctx, "drop domain anotheruint64")
-
-		types, err := conn.LoadTypes(ctx, []string{"dtype_test"})
-		require.NoError(t, err)
-		require.Len(t, types, 6)
-		require.Equal(t, types[0].Name, "public.anotheruint64")
-		require.Equal(t, types[1].Name, "anotheruint64")
-		require.Equal(t, types[2].Name, "public._anotheruint64")
-		require.Equal(t, types[3].Name, "_anotheruint64")
-		require.Equal(t, types[4].Name, "public.dtype_test")
-		require.Equal(t, types[5].Name, "dtype_test")
-	})
-}
diff --git a/internal/sanitize/sanitize_bench_test.go b/internal/sanitize/sanitize_bench_test.go
deleted file mode 100644
index baa742b1..00000000
--- a/internal/sanitize/sanitize_bench_test.go
+++ /dev/null
@@ -1,62 +0,0 @@
-// sanitize_benchmark_test.go
-package sanitize_test
-
-import (
-	"testing"
-	"time"
-
-	"github.com/jackc/pgx/v5/internal/sanitize"
-)
-
-var benchmarkSanitizeResult string
-
-const benchmarkQuery = "" +
-	`SELECT * 
-   FROM "water_containers" 
-   WHERE NOT "id" = $1			-- int64
-   AND "tags" NOT IN $2         -- nil
-   AND "volume" > $3            -- float64
-   AND "transportable" = $4     -- bool
-   AND position($5 IN "sign")   -- bytes
-   AND "label" LIKE $6          -- string
-   AND "created_at" > $7;       -- time.Time`
-
-var benchmarkArgs = []any{
-	int64(12345),
-	nil,
-	float64(500),
-	true,
-	[]byte("8BADF00D"),
-	"kombucha's han'dy awokowa",
-	time.Date(2015, 10, 1, 0, 0, 0, 0, time.UTC),
-}
-
-func BenchmarkSanitize(b *testing.B) {
-	query, err := sanitize.NewQuery(benchmarkQuery)
-	if err != nil {
-		b.Fatalf("failed to create query: %v", err)
-	}
-
-	b.ResetTimer()
-	b.ReportAllocs()
-
-	for i := 0; i < b.N; i++ {
-		benchmarkSanitizeResult, err = query.Sanitize(benchmarkArgs...)
-		if err != nil {
-			b.Fatalf("failed to sanitize query: %v", err)
-		}
-	}
-}
-
-var benchmarkNewSQLResult string
-
-func BenchmarkSanitizeSQL(b *testing.B) {
-	b.ReportAllocs()
-	var err error
-	for i := 0; i < b.N; i++ {
-		benchmarkNewSQLResult, err = sanitize.SanitizeSQL(benchmarkQuery, benchmarkArgs...)
-		if err != nil {
-			b.Fatalf("failed to sanitize SQL: %v", err)
-		}
-	}
-}
diff --git a/internal/sanitize/sanitize_fuzz_test.go b/internal/sanitize/sanitize_fuzz_test.go
deleted file mode 100644
index 2f0c4122..00000000
--- a/internal/sanitize/sanitize_fuzz_test.go
+++ /dev/null
@@ -1,55 +0,0 @@
-package sanitize_test
-
-import (
-	"strings"
-	"testing"
-
-	"github.com/jackc/pgx/v5/internal/sanitize"
-)
-
-func FuzzQuoteString(f *testing.F) {
-	const prefix = "prefix"
-	f.Add("new\nline")
-	f.Add("sample text")
-	f.Add("sample q'u'o't'e's")
-	f.Add("select 'quoted $42', $1")
-
-	f.Fuzz(func(t *testing.T, input string) {
-		got := string(sanitize.QuoteString([]byte(prefix), input))
-		want := oldQuoteString(input)
-
-		quoted, ok := strings.CutPrefix(got, prefix)
-		if !ok {
-			t.Fatalf("result has no prefix")
-		}
-
-		if want != quoted {
-			t.Errorf("got  %q", got)
-			t.Fatalf("want %q", want)
-		}
-	})
-}
-
-func FuzzQuoteBytes(f *testing.F) {
-	const prefix = "prefix"
-	f.Add([]byte(nil))
-	f.Add([]byte("\n"))
-	f.Add([]byte("sample text"))
-	f.Add([]byte("sample q'u'o't'e's"))
-	f.Add([]byte("select 'quoted $42', $1"))
-
-	f.Fuzz(func(t *testing.T, input []byte) {
-		got := string(sanitize.QuoteBytes([]byte(prefix), input))
-		want := oldQuoteBytes(input)
-
-		quoted, ok := strings.CutPrefix(got, prefix)
-		if !ok {
-			t.Fatalf("result has no prefix")
-		}
-
-		if want != quoted {
-			t.Errorf("got  %q", got)
-			t.Fatalf("want %q", want)
-		}
-	})
-}
diff --git a/internal/sanitize/sanitize_test.go b/internal/sanitize/sanitize_test.go
index 92675153..1deff3fb 100644
--- a/internal/sanitize/sanitize_test.go
+++ b/internal/sanitize/sanitize_test.go
@@ -1,8 +1,6 @@
 package sanitize_test
 
 import (
-	"encoding/hex"
-	"strings"
 	"testing"
 	"time"
 
@@ -229,55 +227,3 @@ func TestQuerySanitize(t *testing.T) {
 		}
 	}
 }
-
-func TestQuoteString(t *testing.T) {
-	tc := func(name, input string) {
-		t.Run(name, func(t *testing.T) {
-			t.Parallel()
-
-			got := string(sanitize.QuoteString(nil, input))
-			want := oldQuoteString(input)
-
-			if got != want {
-				t.Errorf("got:  %s", got)
-				t.Fatalf("want: %s", want)
-			}
-		})
-	}
-
-	tc("empty", "")
-	tc("text", "abcd")
-	tc("with quotes", `one's hat is always a cat`)
-}
-
-// This function was used before optimizations.
-// You should keep for testing purposes - we want to ensure there are no breaking changes.
-func oldQuoteString(str string) string {
-	return "'" + strings.ReplaceAll(str, "'", "''") + "'"
-}
-
-func TestQuoteBytes(t *testing.T) {
-	tc := func(name string, input []byte) {
-		t.Run(name, func(t *testing.T) {
-			t.Parallel()
-
-			got := string(sanitize.QuoteBytes(nil, input))
-			want := oldQuoteBytes(input)
-
-			if got != want {
-				t.Errorf("got:  %s", got)
-				t.Fatalf("want: %s", want)
-			}
-		})
-	}
-
-	tc("nil", nil)
-	tc("empty", []byte{})
-	tc("text", []byte("abcd"))
-}
-
-// This function was used before optimizations.
-// You should keep for testing purposes - we want to ensure there are no breaking changes.
-func oldQuoteBytes(buf []byte) string {
-	return `'\x` + hex.EncodeToString(buf) + "'"
-}
diff --git a/multitracer/tracer_test.go b/multitracer/tracer_test.go
deleted file mode 100644
index aa5ccd08..00000000
--- a/multitracer/tracer_test.go
+++ /dev/null
@@ -1,115 +0,0 @@
-package multitracer_test
-
-import (
-	"context"
-	"testing"
-
-	"github.com/jackc/pgx/v5"
-	"github.com/jackc/pgx/v5/multitracer"
-	"github.com/jackc/pgx/v5/pgxpool"
-	"github.com/stretchr/testify/require"
-)
-
-type testFullTracer struct{}
-
-func (tt *testFullTracer) TraceQueryStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryStartData) context.Context {
-	return ctx
-}
-
-func (tt *testFullTracer) TraceQueryEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryEndData) {
-}
-
-func (tt *testFullTracer) TraceBatchStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchStartData) context.Context {
-	return ctx
-}
-
-func (tt *testFullTracer) TraceBatchQuery(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchQueryData) {
-}
-
-func (tt *testFullTracer) TraceBatchEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceBatchEndData) {
-}
-
-func (tt *testFullTracer) TraceCopyFromStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceCopyFromStartData) context.Context {
-	return ctx
-}
-
-func (tt *testFullTracer) TraceCopyFromEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceCopyFromEndData) {
-}
-
-func (tt *testFullTracer) TracePrepareStart(ctx context.Context, conn *pgx.Conn, data pgx.TracePrepareStartData) context.Context {
-	return ctx
-}
-
-func (tt *testFullTracer) TracePrepareEnd(ctx context.Context, conn *pgx.Conn, data pgx.TracePrepareEndData) {
-}
-
-func (tt *testFullTracer) TraceConnectStart(ctx context.Context, data pgx.TraceConnectStartData) context.Context {
-	return ctx
-}
-
-func (tt *testFullTracer) TraceConnectEnd(ctx context.Context, data pgx.TraceConnectEndData) {
-}
-
-func (tt *testFullTracer) TraceAcquireStart(ctx context.Context, pool *pgxpool.Pool, data pgxpool.TraceAcquireStartData) context.Context {
-	return ctx
-}
-
-func (tt *testFullTracer) TraceAcquireEnd(ctx context.Context, pool *pgxpool.Pool, data pgxpool.TraceAcquireEndData) {
-}
-
-func (tt *testFullTracer) TraceRelease(pool *pgxpool.Pool, data pgxpool.TraceReleaseData) {
-}
-
-type testCopyTracer struct{}
-
-func (tt *testCopyTracer) TraceQueryStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryStartData) context.Context {
-	return ctx
-}
-
-func (tt *testCopyTracer) TraceQueryEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceQueryEndData) {
-}
-
-func (tt *testCopyTracer) TraceCopyFromStart(ctx context.Context, conn *pgx.Conn, data pgx.TraceCopyFromStartData) context.Context {
-	return ctx
-}
-
-func (tt *testCopyTracer) TraceCopyFromEnd(ctx context.Context, conn *pgx.Conn, data pgx.TraceCopyFromEndData) {
-}
-
-func TestNew(t *testing.T) {
-	t.Parallel()
-
-	fullTracer := &testFullTracer{}
-	copyTracer := &testCopyTracer{}
-
-	mt := multitracer.New(fullTracer, copyTracer)
-	require.Equal(
-		t,
-		&multitracer.Tracer{
-			QueryTracers: []pgx.QueryTracer{
-				fullTracer,
-				copyTracer,
-			},
-			BatchTracers: []pgx.BatchTracer{
-				fullTracer,
-			},
-			CopyFromTracers: []pgx.CopyFromTracer{
-				fullTracer,
-				copyTracer,
-			},
-			PrepareTracers: []pgx.PrepareTracer{
-				fullTracer,
-			},
-			ConnectTracers: []pgx.ConnectTracer{
-				fullTracer,
-			},
-			PoolAcquireTracers: []pgxpool.AcquireTracer{
-				fullTracer,
-			},
-			PoolReleaseTracers: []pgxpool.ReleaseTracer{
-				fullTracer,
-			},
-		},
-		mt,
-	)
-}
diff --git a/pgconn/ctxwatch/context_watcher_test.go b/pgconn/ctxwatch/context_watcher_test.go
index a18e7339..302aabe3 100644
--- a/pgconn/ctxwatch/context_watcher_test.go
+++ b/pgconn/ctxwatch/context_watcher_test.go
@@ -49,7 +49,7 @@ func TestContextWatcherContextCancelled(t *testing.T) {
 	require.True(t, cleanupCalled, "Cleanup func was not called")
 }
 
-func TestContextWatcherUnwatchedBeforeContextCancelled(t *testing.T) {
+func TestContextWatcherUnwatchdBeforeContextCancelled(t *testing.T) {
 	cw := ctxwatch.NewContextWatcher(&testHandler{
 		handleCancel: func(context.Context) {
 			t.Error("cancel func should not have been called")
diff --git a/pgconn/pgconn_test.go b/pgconn/pgconn_test.go
index 2b582e24..a53061d1 100644
--- a/pgconn/pgconn_test.go
+++ b/pgconn/pgconn_test.go
@@ -2103,9 +2103,8 @@ func TestConnCopyFromGzipReader(t *testing.T) {
 	)`).ReadAll()
 	require.NoError(t, err)
 
-	f, err := os.CreateTemp(t.TempDir(), "*")
+	f, err := os.CreateTemp("", "*")
 	require.NoError(t, err)
-	defer f.Close()
 
 	gw := gzip.NewWriter(f)
 
@@ -2138,6 +2137,12 @@ func TestConnCopyFromGzipReader(t *testing.T) {
 	err = gr.Close()
 	require.NoError(t, err)
 
+	err = f.Close()
+	require.NoError(t, err)
+
+	err = os.Remove(f.Name())
+	require.NoError(t, err)
+
 	result := pgConn.ExecParams(ctx, "select * from foo", nil, nil, nil, nil).Read()
 	require.NoError(t, result.Err)
 
@@ -3105,344 +3110,6 @@ func TestPipelineQueryErrorBetweenSyncs(t *testing.T) {
 	ensureConnValid(t, pgConn)
 }
 
-func TestPipelineFlushForSingleRequests(t *testing.T) {
-	t.Parallel()
-
-	ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
-	defer cancel()
-
-	pgConn, err := pgconn.Connect(ctx, os.Getenv("PGX_TEST_DATABASE"))
-	require.NoError(t, err)
-	defer closeConn(t, pgConn)
-
-	pipeline := pgConn.StartPipeline(ctx)
-
-	pipeline.SendPrepare("ps", "select $1::text as msg", nil)
-	pipeline.SendFlushRequest()
-	err = pipeline.Flush()
-	require.NoError(t, err)
-
-	results, err := pipeline.GetResults()
-	require.NoError(t, err)
-	sd, ok := results.(*pgconn.StatementDescription)
-	require.Truef(t, ok, "expected StatementDescription, got: %#v", results)
-	require.Len(t, sd.Fields, 1)
-	require.Equal(t, "msg", string(sd.Fields[0].Name))
-	require.Equal(t, []uint32{pgtype.TextOID}, sd.ParamOIDs)
-
-	results, err = pipeline.GetResults()
-	require.NoError(t, err)
-	require.Nil(t, results)
-
-	pipeline.SendQueryPrepared(`ps`, [][]byte{[]byte("hello")}, nil, nil)
-	pipeline.SendFlushRequest()
-	err = pipeline.Flush()
-	require.NoError(t, err)
-
-	results, err = pipeline.GetResults()
-	require.NoError(t, err)
-	rr, ok := results.(*pgconn.ResultReader)
-	require.Truef(t, ok, "expected ResultReader, got: %#v", results)
-	readResult := rr.Read()
-	require.NoError(t, readResult.Err)
-	require.Len(t, readResult.Rows, 1)
-	require.Len(t, readResult.Rows[0], 1)
-	require.Equal(t, "hello", string(readResult.Rows[0][0]))
-
-	results, err = pipeline.GetResults()
-	require.NoError(t, err)
-	require.Nil(t, results)
-
-	pipeline.SendDeallocate("ps")
-	pipeline.SendFlushRequest()
-	err = pipeline.Flush()
-	require.NoError(t, err)
-
-	results, err = pipeline.GetResults()
-	require.NoError(t, err)
-	_, ok = results.(*pgconn.CloseComplete)
-	require.Truef(t, ok, "expected CloseComplete, got: %#v", results)
-
-	results, err = pipeline.GetResults()
-	require.NoError(t, err)
-	require.Nil(t, results)
-
-	pipeline.SendQueryParams(`select 1`, nil, nil, nil, nil)
-	pipeline.SendFlushRequest()
-	err = pipeline.Flush()
-	require.NoError(t, err)
-
-	results, err = pipeline.GetResults()
-	require.NoError(t, err)
-	rr, ok = results.(*pgconn.ResultReader)
-	require.Truef(t, ok, "expected ResultReader, got: %#v", results)
-	readResult = rr.Read()
-	require.NoError(t, readResult.Err)
-	require.Len(t, readResult.Rows, 1)
-	require.Len(t, readResult.Rows[0], 1)
-	require.Equal(t, "1", string(readResult.Rows[0][0]))
-
-	results, err = pipeline.GetResults()
-	require.NoError(t, err)
-	require.Nil(t, results)
-
-	err = pipeline.Sync()
-	require.NoError(t, err)
-
-	results, err = pipeline.GetResults()
-	require.NoError(t, err)
-	_, ok = results.(*pgconn.PipelineSync)
-	require.Truef(t, ok, "expected PipelineSync, got: %#v", results)
-
-	results, err = pipeline.GetResults()
-	require.NoError(t, err)
-	require.Nil(t, results)
-
-	err = pipeline.Close()
-	require.NoError(t, err)
-
-	ensureConnValid(t, pgConn)
-}
-
-func TestPipelineFlushForRequestSeries(t *testing.T) {
-	t.Parallel()
-
-	ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
-	defer cancel()
-
-	pgConn, err := pgconn.Connect(ctx, os.Getenv("PGX_TEST_DATABASE"))
-	require.NoError(t, err)
-	defer closeConn(t, pgConn)
-
-	pipeline := pgConn.StartPipeline(ctx)
-	pipeline.SendPrepare("ps", "select $1::bigint as num", nil)
-	err = pipeline.Sync()
-	require.NoError(t, err)
-
-	results, err := pipeline.GetResults()
-	require.NoError(t, err)
-	sd, ok := results.(*pgconn.StatementDescription)
-	require.Truef(t, ok, "expected StatementDescription, got: %#v", results)
-	require.Len(t, sd.Fields, 1)
-	require.Equal(t, "num", string(sd.Fields[0].Name))
-	require.Equal(t, []uint32{pgtype.Int8OID}, sd.ParamOIDs)
-
-	results, err = pipeline.GetResults()
-	require.NoError(t, err)
-	_, ok = results.(*pgconn.PipelineSync)
-	require.Truef(t, ok, "expected PipelineSync, got: %#v", results)
-
-	pipeline.SendQueryPrepared(`ps`, [][]byte{[]byte("1")}, nil, nil)
-	pipeline.SendQueryPrepared(`ps`, [][]byte{[]byte("2")}, nil, nil)
-	pipeline.SendFlushRequest()
-	err = pipeline.Flush()
-	require.NoError(t, err)
-
-	results, err = pipeline.GetResults()
-	require.NoError(t, err)
-	rr, ok := results.(*pgconn.ResultReader)
-	require.Truef(t, ok, "expected ResultReader, got: %#v", results)
-	readResult := rr.Read()
-	require.NoError(t, readResult.Err)
-	require.Len(t, readResult.Rows, 1)
-	require.Len(t, readResult.Rows[0], 1)
-	require.Equal(t, "1", string(readResult.Rows[0][0]))
-
-	results, err = pipeline.GetResults()
-	require.NoError(t, err)
-	rr, ok = results.(*pgconn.ResultReader)
-	require.Truef(t, ok, "expected ResultReader, got: %#v", results)
-	readResult = rr.Read()
-	require.NoError(t, readResult.Err)
-	require.Len(t, readResult.Rows, 1)
-	require.Len(t, readResult.Rows[0], 1)
-	require.Equal(t, "2", string(readResult.Rows[0][0]))
-
-	results, err = pipeline.GetResults()
-	require.NoError(t, err)
-	require.Nil(t, results)
-
-	pipeline.SendQueryPrepared(`ps`, [][]byte{[]byte("3")}, nil, nil)
-	err = pipeline.Flush()
-	require.NoError(t, err)
-
-	results, err = pipeline.GetResults()
-	require.NoError(t, err)
-	require.Nil(t, results)
-
-	pipeline.SendQueryPrepared(`ps`, [][]byte{[]byte("4")}, nil, nil)
-	pipeline.SendFlushRequest()
-	err = pipeline.Flush()
-	require.NoError(t, err)
-
-	results, err = pipeline.GetResults()
-	require.NoError(t, err)
-	rr, ok = results.(*pgconn.ResultReader)
-	require.Truef(t, ok, "expected ResultReader, got: %#v", results)
-	readResult = rr.Read()
-	require.NoError(t, readResult.Err)
-	require.Len(t, readResult.Rows, 1)
-	require.Len(t, readResult.Rows[0], 1)
-	require.Equal(t, "3", string(readResult.Rows[0][0]))
-
-	results, err = pipeline.GetResults()
-	require.NoError(t, err)
-	rr, ok = results.(*pgconn.ResultReader)
-	require.Truef(t, ok, "expected ResultReader, got: %#v", results)
-	readResult = rr.Read()
-	require.NoError(t, readResult.Err)
-	require.Len(t, readResult.Rows, 1)
-	require.Len(t, readResult.Rows[0], 1)
-	require.Equal(t, "4", string(readResult.Rows[0][0]))
-
-	results, err = pipeline.GetResults()
-	require.NoError(t, err)
-	require.Nil(t, results)
-
-	pipeline.SendQueryPrepared(`ps`, [][]byte{[]byte("5")}, nil, nil)
-	pipeline.SendFlushRequest()
-
-	results, err = pipeline.GetResults()
-	require.NoError(t, err)
-	require.Nil(t, results)
-
-	pipeline.SendQueryPrepared(`ps`, [][]byte{[]byte("6")}, nil, nil)
-	pipeline.SendFlushRequest()
-	err = pipeline.Flush()
-	require.NoError(t, err)
-
-	results, err = pipeline.GetResults()
-	require.NoError(t, err)
-	rr, ok = results.(*pgconn.ResultReader)
-	require.Truef(t, ok, "expected ResultReader, got: %#v", results)
-	readResult = rr.Read()
-	require.NoError(t, readResult.Err)
-	require.Len(t, readResult.Rows, 1)
-	require.Len(t, readResult.Rows[0], 1)
-	require.Equal(t, "5", string(readResult.Rows[0][0]))
-
-	results, err = pipeline.GetResults()
-	require.NoError(t, err)
-	rr, ok = results.(*pgconn.ResultReader)
-	require.Truef(t, ok, "expected ResultReader, got: %#v", results)
-	readResult = rr.Read()
-	require.NoError(t, readResult.Err)
-	require.Len(t, readResult.Rows, 1)
-	require.Len(t, readResult.Rows[0], 1)
-	require.Equal(t, "6", string(readResult.Rows[0][0]))
-
-	results, err = pipeline.GetResults()
-	require.NoError(t, err)
-	require.Nil(t, results)
-
-	err = pipeline.Sync()
-	require.NoError(t, err)
-
-	results, err = pipeline.GetResults()
-	require.NoError(t, err)
-	_, ok = results.(*pgconn.PipelineSync)
-	require.Truef(t, ok, "expected PipelineSync, got: %#v", results)
-
-	results, err = pipeline.GetResults()
-	require.NoError(t, err)
-	require.Nil(t, results)
-
-	err = pipeline.Close()
-	require.NoError(t, err)
-
-	ensureConnValid(t, pgConn)
-}
-
-func TestPipelineFlushWithError(t *testing.T) {
-	t.Parallel()
-
-	ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
-	defer cancel()
-
-	pgConn, err := pgconn.Connect(ctx, os.Getenv("PGX_TEST_DATABASE"))
-	require.NoError(t, err)
-	defer closeConn(t, pgConn)
-
-	pipeline := pgConn.StartPipeline(ctx)
-	pipeline.SendQueryParams(`select 1`, nil, nil, nil, nil)
-	pipeline.SendQueryParams(`select 1/(3-n) from generate_series(1,10) n`, nil, nil, nil, nil)
-	pipeline.SendQueryParams(`select 2`, nil, nil, nil, nil)
-	pipeline.SendFlushRequest()
-	err = pipeline.Flush()
-	require.NoError(t, err)
-
-	results, err := pipeline.GetResults()
-	require.NoError(t, err)
-	rr, ok := results.(*pgconn.ResultReader)
-	require.Truef(t, ok, "expected ResultReader, got: %#v", results)
-	readResult := rr.Read()
-	require.NoError(t, readResult.Err)
-	require.Len(t, readResult.Rows, 1)
-	require.Len(t, readResult.Rows[0], 1)
-	require.Equal(t, "1", string(readResult.Rows[0][0]))
-
-	results, err = pipeline.GetResults()
-	require.NoError(t, err)
-	rr, ok = results.(*pgconn.ResultReader)
-	require.Truef(t, ok, "expected ResultReader, got: %#v", results)
-	readResult = rr.Read()
-	var pgErr *pgconn.PgError
-	require.ErrorAs(t, readResult.Err, &pgErr)
-	require.Equal(t, "22012", pgErr.Code)
-
-	results, err = pipeline.GetResults()
-	require.NoError(t, err)
-	require.Nil(t, results)
-
-	pipeline.SendQueryParams(`select 3`, nil, nil, nil, nil)
-	pipeline.SendFlushRequest()
-	err = pipeline.Flush()
-	require.NoError(t, err)
-
-	results, err = pipeline.GetResults()
-	require.NoError(t, err)
-	require.Nil(t, results)
-
-	pipeline.SendQueryParams(`select 4`, nil, nil, nil, nil)
-	pipeline.SendPipelineSync()
-	pipeline.SendQueryParams(`select 5`, nil, nil, nil, nil)
-	pipeline.SendFlushRequest()
-	err = pipeline.Flush()
-	require.NoError(t, err)
-
-	results, err = pipeline.GetResults()
-	require.NoError(t, err)
-	_, ok = results.(*pgconn.PipelineSync)
-	require.Truef(t, ok, "expected PipelineSync, got: %#v", results)
-
-	results, err = pipeline.GetResults()
-	require.NoError(t, err)
-	rr, ok = results.(*pgconn.ResultReader)
-	require.Truef(t, ok, "expected ResultReader, got: %#v", results)
-	readResult = rr.Read()
-	require.NoError(t, readResult.Err)
-	require.Len(t, readResult.Rows, 1)
-	require.Len(t, readResult.Rows[0], 1)
-	require.Equal(t, "5", string(readResult.Rows[0][0]))
-
-	results, err = pipeline.GetResults()
-	require.NoError(t, err)
-	require.Nil(t, results)
-
-	err = pipeline.Sync()
-	require.NoError(t, err)
-
-	results, err = pipeline.GetResults()
-	require.NoError(t, err)
-	_, ok = results.(*pgconn.PipelineSync)
-	require.Truef(t, ok, "expected PipelineSync, got: %#v", results)
-
-	err = pipeline.Close()
-	require.NoError(t, err)
-
-	ensureConnValid(t, pgConn)
-}
-
 func TestPipelineCloseReadsUnreadResults(t *testing.T) {
 	t.Parallel()
 
diff --git a/pgproto3/frontend_test.go b/pgproto3/frontend_test.go
index c2872661..e02457d6 100644
--- a/pgproto3/frontend_test.go
+++ b/pgproto3/frontend_test.go
@@ -115,21 +115,3 @@ func TestErrorResponse(t *testing.T) {
 	require.NoError(t, err)
 	assert.Equal(t, want, got)
 }
-
-func TestFrontendReceiveExceededMaxBodyLen(t *testing.T) {
-	t.Parallel()
-
-	client := &interruptReader{}
-	client.push([]byte{'D', 0, 0, 10, 10})
-
-	frontend := pgproto3.NewFrontend(client, nil)
-
-	// Set max body len to 5
-	frontend.SetMaxBodyLen(5)
-
-	// Receive regular msg
-	msg, err := frontend.Receive()
-	assert.Nil(t, msg)
-	var invalidBodyLenErr *pgproto3.ExceededMaxBodyLenErr
-	assert.ErrorAs(t, err, &invalidBodyLenErr)
-}
diff --git a/pgtype/composite_test.go b/pgtype/composite_test.go
index a049b448..1eedc89a 100644
--- a/pgtype/composite_test.go
+++ b/pgtype/composite_test.go
@@ -11,8 +11,6 @@ import (
 )
 
 func TestCompositeCodecTranscode(t *testing.T) {
-	skipCockroachDB(t, "Server does not support composite types (see https://github.com/cockroachdb/cockroach/issues/27792)")
-
 	defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
 
 		_, err := conn.Exec(ctx, `drop type if exists ct_test;
@@ -91,8 +89,6 @@ func (p *point3d) ScanIndex(i int) any {
 }
 
 func TestCompositeCodecTranscodeStruct(t *testing.T) {
-	skipCockroachDB(t, "Server does not support composite types (see https://github.com/cockroachdb/cockroach/issues/27792)")
-
 	defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
 
 		_, err := conn.Exec(ctx, `drop type if exists point3d;
@@ -128,8 +124,6 @@ create type point3d as (
 }
 
 func TestCompositeCodecTranscodeStructWrapper(t *testing.T) {
-	skipCockroachDB(t, "Server does not support composite types (see https://github.com/cockroachdb/cockroach/issues/27792)")
-
 	defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
 
 		_, err := conn.Exec(ctx, `drop type if exists point3d;
@@ -169,8 +163,6 @@ create type point3d as (
 }
 
 func TestCompositeCodecDecodeValue(t *testing.T) {
-	skipCockroachDB(t, "Server does not support composite types (see https://github.com/cockroachdb/cockroach/issues/27792)")
-
 	defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
 
 		_, err := conn.Exec(ctx, `drop type if exists point3d;
@@ -214,7 +206,7 @@ create type point3d as (
 //
 // https://github.com/jackc/pgx/issues/1576
 func TestCompositeCodecTranscodeStructWrapperForTable(t *testing.T) {
-	skipCockroachDB(t, "Server does not support composite types (see https://github.com/cockroachdb/cockroach/issues/27792)")
+	skipCockroachDB(t, "Server does not support composite types from table definitions")
 
 	defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
 
diff --git a/pgtype/derived_types_test.go b/pgtype/derived_types_test.go
deleted file mode 100644
index 05d109bd..00000000
--- a/pgtype/derived_types_test.go
+++ /dev/null
@@ -1,61 +0,0 @@
-package pgtype_test
-
-import (
-	"context"
-	"testing"
-
-	pgx "github.com/jackc/pgx/v5"
-	"github.com/jackc/pgx/v5/pgtype"
-	"github.com/stretchr/testify/require"
-)
-
-func TestDerivedTypes(t *testing.T) {
-	skipCockroachDB(t, "Server does not support composite types (see https://github.com/cockroachdb/cockroach/issues/27792)")
-
-	defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
-		_, err := conn.Exec(ctx, `
-drop type if exists dt_test;
-drop domain if exists dt_uint64;
-
-create domain dt_uint64 as numeric(20,0);
-create type dt_test as (
-	a text,
-    b dt_uint64,
-    c dt_uint64[]
-);`)
-		require.NoError(t, err)
-		defer conn.Exec(ctx, "drop domain dt_uint64")
-		defer conn.Exec(ctx, "drop type dt_test")
-
-		dtypes, err := conn.LoadTypes(ctx, []string{"dt_test"})
-		require.Len(t, dtypes, 6)
-		require.Equal(t, dtypes[0].Name, "public.dt_uint64")
-		require.Equal(t, dtypes[1].Name, "dt_uint64")
-		require.Equal(t, dtypes[2].Name, "public._dt_uint64")
-		require.Equal(t, dtypes[3].Name, "_dt_uint64")
-		require.Equal(t, dtypes[4].Name, "public.dt_test")
-		require.Equal(t, dtypes[5].Name, "dt_test")
-		require.NoError(t, err)
-		conn.TypeMap().RegisterTypes(dtypes)
-
-		formats := []struct {
-			name string
-			code int16
-		}{
-			{name: "TextFormat", code: pgx.TextFormatCode},
-			{name: "BinaryFormat", code: pgx.BinaryFormatCode},
-		}
-
-		for _, format := range formats {
-			var a string
-			var b uint64
-			var c *[]uint64
-
-			row := conn.QueryRow(ctx, "select $1::dt_test", pgx.QueryResultFormats{format.code}, pgtype.CompositeFields{"hi", uint64(42), []uint64{10, 20, 30}})
-			err := row.Scan(pgtype.CompositeFields{&a, &b, &c})
-			require.NoError(t, err)
-			require.EqualValuesf(t, "hi", a, "%v", format.name)
-			require.EqualValuesf(t, 42, b, "%v", format.name)
-		}
-	})
-}
diff --git a/pgtype/int_test.go b/pgtype/int_test.go
index 8c498769..73294b3c 100644
--- a/pgtype/int_test.go
+++ b/pgtype/int_test.go
@@ -1,5 +1,4 @@
-// Code generated from pgtype/int_test.go.erb. DO NOT EDIT.
-
+// Do not edit. Generated from pgtype/int_test.go.erb
 package pgtype_test
 
 import (
diff --git a/pgtype/integration_benchmark_test.go b/pgtype/integration_benchmark_test.go
index 88516a9f..41e5f750 100644
--- a/pgtype/integration_benchmark_test.go
+++ b/pgtype/integration_benchmark_test.go
@@ -1,5 +1,3 @@
-// Code generated from pgtype/integration_benchmark_test.go.erb. DO NOT EDIT.
-
 package pgtype_test
 
 import (
diff --git a/pgtype/integration_benchmark_test.go.erb b/pgtype/integration_benchmark_test.go.erb
index 6f401153..0175700a 100644
--- a/pgtype/integration_benchmark_test.go.erb
+++ b/pgtype/integration_benchmark_test.go.erb
@@ -25,7 +25,7 @@ func BenchmarkQuery<%= format_name %>FormatDecode_PG_<%= pg_type %>_to_Go_<%= go
       rows, _ := conn.Query(
         ctx,
         `select <% columns.times do |col_idx| %><% if col_idx != 0 %>, <% end %>n::<%= pg_type %> + <%= col_idx%><% end %> from generate_series(1, <%= rows %>) n`,
-        pgx.QueryResultFormats{<%= format_code %>},
+        []any{pgx.QueryResultFormats{<%= format_code %>}},
       )
       _, err := pgx.ForEachRow(rows, []any{<% columns.times do |col_idx| %><% if col_idx != 0 %>, <% end %>&v[<%= col_idx%>]<% end %>},  func() error { return nil })
       if err != nil {
@@ -49,7 +49,7 @@ func BenchmarkQuery<%= format_name %>FormatDecode_PG_Int4Array_With_Go_Int4Array
       rows, _ := conn.Query(
         ctx,
         `select array_agg(n) from generate_series(1, <%= array_size %>) n`,
-        pgx.QueryResultFormats{<%= format_code %>},
+        []any{pgx.QueryResultFormats{<%= format_code %>}},
       )
       _, err := pgx.ForEachRow(rows, []any{&v},  func() error { return nil })
       if err != nil {
diff --git a/pgtype/interval_test.go b/pgtype/interval_test.go
index c06c3b2d..754c44e3 100644
--- a/pgtype/interval_test.go
+++ b/pgtype/interval_test.go
@@ -7,7 +7,6 @@ import (
 
 	"github.com/jackc/pgx/v5/pgtype"
 	"github.com/jackc/pgx/v5/pgxtest"
-	"github.com/stretchr/testify/assert"
 )
 
 func TestIntervalCodec(t *testing.T) {
@@ -137,22 +136,3 @@ func TestIntervalCodec(t *testing.T) {
 		{nil, new(pgtype.Interval), isExpectedEq(pgtype.Interval{})},
 	})
 }
-
-func TestIntervalTextEncode(t *testing.T) {
-	m := pgtype.NewMap()
-
-	successfulTests := []struct {
-		source pgtype.Interval
-		result string
-	}{
-		{source: pgtype.Interval{Months: 2, Days: 1, Microseconds: 0, Valid: true}, result: "2 mon 1 day 00:00:00"},
-		{source: pgtype.Interval{Months: 0, Days: 0, Microseconds: 0, Valid: true}, result: "00:00:00"},
-		{source: pgtype.Interval{Months: 0, Days: 0, Microseconds: 6 * 60 * 1000000, Valid: true}, result: "00:06:00"},
-		{source: pgtype.Interval{Months: 0, Days: 1, Microseconds: 6*60*1000000 + 30, Valid: true}, result: "1 day 00:06:00.000030"},
-	}
-	for i, tt := range successfulTests {
-		buf, err := m.Encode(pgtype.DateOID, pgtype.TextFormatCode, tt.source, nil)
-		assert.NoErrorf(t, err, "%d", i)
-		assert.Equalf(t, tt.result, string(buf), "%d", i)
-	}
-}
diff --git a/pgtype/json_test.go b/pgtype/json_test.go
index 78bdc3a9..3a8ddae5 100644
--- a/pgtype/json_test.go
+++ b/pgtype/json_test.go
@@ -6,7 +6,6 @@ import (
 	"database/sql/driver"
 	"encoding/json"
 	"errors"
-	"fmt"
 	"reflect"
 	"testing"
 
@@ -49,7 +48,6 @@ func TestJSONCodec(t *testing.T) {
 		Age  int    `json:"age"`
 	}
 
-	var str string
 	pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "json", []pgxtest.ValueRoundTripTest{
 		{nil, new(*jsonStruct), isExpectedEq((*jsonStruct)(nil))},
 		{map[string]any(nil), new(*string), isExpectedEq((*string)(nil))},
@@ -65,11 +63,6 @@ func TestJSONCodec(t *testing.T) {
 
 		// Test driver.Valuer is used before json.Marshaler (https://github.com/jackc/pgx/issues/1805)
 		{Issue1805(7), new(Issue1805), isExpectedEq(Issue1805(7))},
-		// Test driver.Scanner is used before json.Unmarshaler (https://github.com/jackc/pgx/issues/2146)
-		{Issue2146(7), new(*Issue2146), isPtrExpectedEq(Issue2146(7))},
-
-		// Test driver.Scanner without pointer receiver (https://github.com/jackc/pgx/issues/2204)
-		{NonPointerJSONScanner{V: stringPtr("{}")}, NonPointerJSONScanner{V: &str}, func(a any) bool { return str == "{}" }},
 	})
 
 	pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, pgxtest.KnownOIDQueryExecModes, "json", []pgxtest.ValueRoundTripTest{
@@ -116,52 +109,6 @@ func (i Issue1805) MarshalJSON() ([]byte, error) {
 	return nil, errors.New("MarshalJSON called")
 }
 
-type Issue2146 int
-
-func (i *Issue2146) Scan(src any) error {
-	var source []byte
-	switch src.(type) {
-	case string:
-		source = []byte(src.(string))
-	case []byte:
-		source = src.([]byte)
-	default:
-		return errors.New("unknown source type")
-	}
-	var newI int
-	if err := json.Unmarshal(source, &newI); err != nil {
-		return err
-	}
-	*i = Issue2146(newI + 1)
-	return nil
-}
-
-func (i Issue2146) Value() (driver.Value, error) {
-	b, err := json.Marshal(int(i - 1))
-	return string(b), err
-}
-
-type NonPointerJSONScanner struct {
-	V *string
-}
-
-func (i NonPointerJSONScanner) Scan(src any) error {
-	switch c := src.(type) {
-	case string:
-		*i.V = c
-	case []byte:
-		*i.V = string(c)
-	default:
-		return errors.New("unknown source type")
-	}
-
-	return nil
-}
-
-func (i NonPointerJSONScanner) Value() (driver.Value, error) {
-	return i.V, nil
-}
-
 // https://github.com/jackc/pgx/issues/1273#issuecomment-1221414648
 func TestJSONCodecUnmarshalSQLNull(t *testing.T) {
 	defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
@@ -192,15 +139,11 @@ func TestJSONCodecUnmarshalSQLNull(t *testing.T) {
 		// A string cannot scan a NULL.
 		str := "foobar"
 		err = conn.QueryRow(ctx, "select null::json").Scan(&str)
-		fieldName := "json"
-		if conn.PgConn().ParameterStatus("crdb_version") != "" {
-			fieldName = "jsonb" // Seems like CockroachDB treats json as jsonb.
-		}
-		require.EqualError(t, err, fmt.Sprintf("can't scan into dest[0] (col: %s): cannot scan NULL into *string", fieldName))
+		require.EqualError(t, err, "can't scan into dest[0]: cannot scan NULL into *string")
 
 		// A non-string cannot scan a NULL.
 		err = conn.QueryRow(ctx, "select null::json").Scan(&n)
-		require.EqualError(t, err, fmt.Sprintf("can't scan into dest[0] (col: %s): cannot scan NULL into *int", fieldName))
+		require.EqualError(t, err, "can't scan into dest[0]: cannot scan NULL into *int")
 	})
 }
 
@@ -297,8 +240,7 @@ func TestJSONCodecCustomMarshal(t *testing.T) {
 				Unmarshal: func(data []byte, v any) error {
 					return json.Unmarshal([]byte(`{"custom":"value"}`), v)
 				},
-			},
-		})
+			}})
 	}
 
 	pgxtest.RunValueRoundTripTests(context.Background(), t, connTestRunner, pgxtest.KnownOIDQueryExecModes, "json", []pgxtest.ValueRoundTripTest{
@@ -309,20 +251,3 @@ func TestJSONCodecCustomMarshal(t *testing.T) {
 		}},
 	})
 }
-
-func TestJSONCodecScanToNonPointerValues(t *testing.T) {
-	defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
-		n := 44
-		err := conn.QueryRow(ctx, "select '42'::jsonb").Scan(n)
-		require.Error(t, err)
-
-		var i *int
-		err = conn.QueryRow(ctx, "select '42'::jsonb").Scan(i)
-		require.Error(t, err)
-
-		m := 0
-		err = conn.QueryRow(ctx, "select '42'::jsonb").Scan(&m)
-		require.NoError(t, err)
-		require.Equal(t, 42, m)
-	})
-}
diff --git a/pgtype/jsonb_test.go b/pgtype/jsonb_test.go
index 031cd376..f34b3ad5 100644
--- a/pgtype/jsonb_test.go
+++ b/pgtype/jsonb_test.go
@@ -66,11 +66,11 @@ func TestJSONBCodecUnmarshalSQLNull(t *testing.T) {
 		// A string cannot scan a NULL.
 		str := "foobar"
 		err = conn.QueryRow(ctx, "select null::jsonb").Scan(&str)
-		require.EqualError(t, err, "can't scan into dest[0] (col: jsonb): cannot scan NULL into *string")
+		require.EqualError(t, err, "can't scan into dest[0]: cannot scan NULL into *string")
 
 		// A non-string cannot scan a NULL.
 		err = conn.QueryRow(ctx, "select null::jsonb").Scan(&n)
-		require.EqualError(t, err, "can't scan into dest[0] (col: jsonb): cannot scan NULL into *int")
+		require.EqualError(t, err, "can't scan into dest[0]: cannot scan NULL into *int")
 	})
 }
 
diff --git a/pgtype/pgtype_test.go b/pgtype/pgtype_test.go
index eb136940..e8613aee 100644
--- a/pgtype/pgtype_test.go
+++ b/pgtype/pgtype_test.go
@@ -9,7 +9,6 @@ import (
 	"fmt"
 	"net"
 	"os"
-	"reflect"
 	"regexp"
 	"strconv"
 	"testing"
@@ -632,10 +631,3 @@ func isExpectedEq(a any) func(any) bool {
 		return a == v
 	}
 }
-
-func isPtrExpectedEq(a any) func(any) bool {
-	return func(v any) bool {
-		val := reflect.ValueOf(v)
-		return a == val.Elem().Interface()
-	}
-}
diff --git a/pgtype/timestamp_test.go b/pgtype/timestamp_test.go
index bd8c7132..31b3ad82 100644
--- a/pgtype/timestamp_test.go
+++ b/pgtype/timestamp_test.go
@@ -2,14 +2,12 @@ package pgtype_test
 
 import (
 	"context"
-	"encoding/json"
 	"testing"
 	"time"
 
 	pgx "github.com/jackc/pgx/v5"
 	"github.com/jackc/pgx/v5/pgtype"
 	"github.com/jackc/pgx/v5/pgxtest"
-	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 )
 
@@ -102,24 +100,13 @@ func TestTimestampCodecDecodeTextInvalid(t *testing.T) {
 }
 
 func TestTimestampMarshalJSON(t *testing.T) {
-
-	tsStruct := struct {
-		TS pgtype.Timestamp `json:"ts"`
-	}{}
-
-	tm := time.Date(2012, 3, 29, 10, 5, 45, 0, time.UTC)
-	tsString := "\"" + tm.Format("2006-01-02T15:04:05") + "\"" //  `"2012-03-29T10:05:45"`
-	var pgt pgtype.Timestamp
-	_ = pgt.Scan(tm)
-
 	successfulTests := []struct {
 		source pgtype.Timestamp
 		result string
 	}{
 		{source: pgtype.Timestamp{}, result: "null"},
-		{source: pgtype.Timestamp{Time: tm, Valid: true}, result: tsString},
-		{source: pgt, result: tsString},
-		{source: pgtype.Timestamp{Time: tm.Add(time.Second * 555 / 1000), Valid: true}, result: `"2012-03-29T10:05:45.555"`},
+		{source: pgtype.Timestamp{Time: time.Date(2012, 3, 29, 10, 5, 45, 0, time.UTC), Valid: true}, result: "\"2012-03-29T10:05:45Z\""},
+		{source: pgtype.Timestamp{Time: time.Date(2012, 3, 29, 10, 5, 45, 555*1000*1000, time.UTC), Valid: true}, result: "\"2012-03-29T10:05:45.555Z\""},
 		{source: pgtype.Timestamp{InfinityModifier: pgtype.Infinity, Valid: true}, result: "\"infinity\""},
 		{source: pgtype.Timestamp{InfinityModifier: pgtype.NegativeInfinity, Valid: true}, result: "\"-infinity\""},
 	}
@@ -129,40 +116,20 @@ func TestTimestampMarshalJSON(t *testing.T) {
 			t.Errorf("%d: %v", i, err)
 		}
 
-		if !assert.Equal(t, tt.result, string(r)) {
+		if string(r) != tt.result {
 			t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.source, tt.result, string(r))
 		}
-		tsStruct.TS = tt.source
-		b, err := json.Marshal(tsStruct)
-		assert.NoErrorf(t, err, "failed to marshal %v %s", tt.source, err)
-		t2 := tsStruct
-		t2.TS = pgtype.Timestamp{} // Clear out the value so that we can compare after unmarshalling
-		err = json.Unmarshal(b, &t2)
-		assert.NoErrorf(t, err, "failed to unmarshal %v with %s", tt.source, err)
-		assert.True(t, tsStruct.TS.Time.Unix() == t2.TS.Time.Unix())
 	}
 }
 
-func TestTimestampUnmarshalJSONErrors(t *testing.T) {
-	tsStruct := struct {
-		TS pgtype.Timestamp `json:"ts"`
-	}{}
-	goodJson1 := []byte(`{"ts":"2012-03-29T10:05:45"}`)
-	assert.NoError(t, json.Unmarshal(goodJson1, &tsStruct))
-	goodJson2 := []byte(`{"ts":"2012-03-29T10:05:45Z"}`)
-	assert.NoError(t, json.Unmarshal(goodJson2, &tsStruct))
-	badJson := []byte(`{"ts":"2012-03-29"}`)
-	assert.Error(t, json.Unmarshal(badJson, &tsStruct))
-}
-
 func TestTimestampUnmarshalJSON(t *testing.T) {
 	successfulTests := []struct {
 		source string
 		result pgtype.Timestamp
 	}{
 		{source: "null", result: pgtype.Timestamp{}},
-		{source: "\"2012-03-29T10:05:45\"", result: pgtype.Timestamp{Time: time.Date(2012, 3, 29, 10, 5, 45, 0, time.UTC), Valid: true}},
-		{source: "\"2012-03-29T10:05:45.555\"", result: pgtype.Timestamp{Time: time.Date(2012, 3, 29, 10, 5, 45, 555*1000*1000, time.UTC), Valid: true}},
+		{source: "\"2012-03-29T10:05:45Z\"", result: pgtype.Timestamp{Time: time.Date(2012, 3, 29, 10, 5, 45, 0, time.UTC), Valid: true}},
+		{source: "\"2012-03-29T10:05:45.555Z\"", result: pgtype.Timestamp{Time: time.Date(2012, 3, 29, 10, 5, 45, 555*1000*1000, time.UTC), Valid: true}},
 		{source: "\"infinity\"", result: pgtype.Timestamp{InfinityModifier: pgtype.Infinity, Valid: true}},
 		{source: "\"-infinity\"", result: pgtype.Timestamp{InfinityModifier: pgtype.NegativeInfinity, Valid: true}},
 	}
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")},
 	})
 }
diff --git a/pgtype/uint64_test.go b/pgtype/uint64_test.go
deleted file mode 100644
index 33c2622d..00000000
--- a/pgtype/uint64_test.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package pgtype_test
-
-import (
-	"context"
-	"testing"
-
-	"github.com/jackc/pgx/v5/pgtype"
-	"github.com/jackc/pgx/v5/pgxtest"
-)
-
-func TestUint64Codec(t *testing.T) {
-	skipCockroachDB(t, "Server does not support xid8 (https://github.com/cockroachdb/cockroach/issues/36815)")
-	skipPostgreSQLVersionLessThan(t, 13)
-
-	pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, pgxtest.KnownOIDQueryExecModes, "xid8", []pgxtest.ValueRoundTripTest{
-		{
-			pgtype.Uint64{Uint64: 1 << 36, Valid: true},
-			new(pgtype.Uint64),
-			isExpectedEq(pgtype.Uint64{Uint64: 1 << 36, Valid: true}),
-		},
-		{pgtype.Uint64{}, new(pgtype.Uint64), isExpectedEq(pgtype.Uint64{})},
-		{nil, new(pgtype.Uint64), isExpectedEq(pgtype.Uint64{})},
-		{
-			uint64(1 << 36),
-			new(uint64),
-			isExpectedEq(uint64(1 << 36)),
-		},
-		{"1147", new(string), isExpectedEq("1147")},
-	})
-}
diff --git a/pgtype/uuid_test.go b/pgtype/uuid_test.go
index 255bd92f..2dc258b1 100644
--- a/pgtype/uuid_test.go
+++ b/pgtype/uuid_test.go
@@ -10,8 +10,6 @@ import (
 	"github.com/stretchr/testify/require"
 )
 
-type renamedUUIDByteArray [16]byte
-
 func TestUUIDCodec(t *testing.T) {
 	pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "uuid", []pgxtest.ValueRoundTripTest{
 		{
@@ -45,16 +43,6 @@ func TestUUIDCodec(t *testing.T) {
 			new(pgtype.UUID),
 			isExpectedEq(pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}),
 		},
-		{
-			renamedUUIDByteArray{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
-			new(pgtype.UUID),
-			isExpectedEq(pgtype.UUID{Bytes: [16]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, Valid: true}),
-		},
-		{
-			[]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
-			new(renamedUUIDByteArray),
-			isExpectedEq(renamedUUIDByteArray{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}),
-		},
 		{
 			[]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
 			new(pgtype.UUID),
@@ -63,38 +51,6 @@ func TestUUIDCodec(t *testing.T) {
 	})
 }
 
-func TestUUID_String(t *testing.T) {
-	tests := []struct {
-		name string
-		src  pgtype.UUID
-		want string
-	}{
-		{
-			name: "first",
-			src: pgtype.UUID{
-				Bytes: [16]byte{29, 72, 90, 122, 109, 24, 69, 153, 140, 108, 52, 66, 86, 22, 136, 122},
-				Valid: true,
-			},
-			want: "1d485a7a-6d18-4599-8c6c-34425616887a",
-		},
-		{
-			name: "third",
-			src: pgtype.UUID{
-				Bytes: [16]byte{},
-			},
-			want: "",
-		},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			got := tt.src.String()
-			if !reflect.DeepEqual(got, tt.want) {
-				t.Errorf("MarshalJSON() got = %v, want %v", got, tt.want)
-			}
-		})
-	}
-}
-
 func TestUUID_MarshalJSON(t *testing.T) {
 	tests := []struct {
 		name string
diff --git a/pgtype/xml_test.go b/pgtype/xml_test.go
deleted file mode 100644
index 2c0b899a..00000000
--- a/pgtype/xml_test.go
+++ /dev/null
@@ -1,128 +0,0 @@
-package pgtype_test
-
-import (
-	"context"
-	"database/sql"
-	"encoding/xml"
-	"testing"
-
-	pgx "github.com/jackc/pgx/v5"
-	"github.com/jackc/pgx/v5/pgxtest"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-)
-
-type xmlStruct struct {
-	XMLName xml.Name `xml:"person"`
-	Name    string   `xml:"name"`
-	Age     int      `xml:"age,attr"`
-}
-
-func TestXMLCodec(t *testing.T) {
-	skipCockroachDB(t, "CockroachDB does not support XML.")
-	pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, nil, "xml", []pgxtest.ValueRoundTripTest{
-		{nil, new(*xmlStruct), isExpectedEq((*xmlStruct)(nil))},
-		{map[string]any(nil), new(*string), isExpectedEq((*string)(nil))},
-		{map[string]any(nil), new([]byte), isExpectedEqBytes([]byte(nil))},
-		{[]byte(nil), new([]byte), isExpectedEqBytes([]byte(nil))},
-		{nil, new([]byte), isExpectedEqBytes([]byte(nil))},
-
-		// Test sql.Scanner.
-		{"", new(sql.NullString), isExpectedEq(sql.NullString{String: "", Valid: true})},
-
-		// Test driver.Valuer.
-		{sql.NullString{String: "", Valid: true}, new(sql.NullString), isExpectedEq(sql.NullString{String: "", Valid: true})},
-	})
-
-	pgxtest.RunValueRoundTripTests(context.Background(), t, defaultConnTestRunner, pgxtest.KnownOIDQueryExecModes, "xml", []pgxtest.ValueRoundTripTest{
-		{[]byte(`<?xml version="1.0"?><Root></Root>`), new([]byte), isExpectedEqBytes([]byte(`<Root></Root>`))},
-		{[]byte(`<?xml version="1.0"?>`), new([]byte), isExpectedEqBytes([]byte(``))},
-		{[]byte(`<?xml version="1.0"?>`), new(string), isExpectedEq(``)},
-		{[]byte(`<Root></Root>`), new([]byte), isExpectedEqBytes([]byte(`<Root></Root>`))},
-		{[]byte(`<Root></Root>`), new(string), isExpectedEq(`<Root></Root>`)},
-		{[]byte(""), new([]byte), isExpectedEqBytes([]byte(""))},
-		{xmlStruct{Name: "Adam", Age: 10}, new(xmlStruct), isExpectedEq(xmlStruct{XMLName: xml.Name{Local: "person"}, Name: "Adam", Age: 10})},
-		{xmlStruct{XMLName: xml.Name{Local: "person"}, Name: "Adam", Age: 10}, new(xmlStruct), isExpectedEq(xmlStruct{XMLName: xml.Name{Local: "person"}, Name: "Adam", Age: 10})},
-		{[]byte(`<person age="10"><name>Adam</name></person>`), new(xmlStruct), isExpectedEq(xmlStruct{XMLName: xml.Name{Local: "person"}, Name: "Adam", Age: 10})},
-	})
-}
-
-// https://github.com/jackc/pgx/issues/1273#issuecomment-1221414648
-func TestXMLCodecUnmarshalSQLNull(t *testing.T) {
-	skipCockroachDB(t, "CockroachDB does not support XML.")
-	defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
-		// Byte arrays are nilified
-		slice := []byte{10, 4}
-		err := conn.QueryRow(ctx, "select null::xml").Scan(&slice)
-		assert.NoError(t, err)
-		assert.Nil(t, slice)
-
-		// Non-pointer structs are zeroed
-		m := xmlStruct{Name: "Adam"}
-		err = conn.QueryRow(ctx, "select null::xml").Scan(&m)
-		assert.NoError(t, err)
-		assert.Empty(t, m)
-
-		// Pointers to structs are nilified
-		pm := &xmlStruct{Name: "Adam"}
-		err = conn.QueryRow(ctx, "select null::xml").Scan(&pm)
-		assert.NoError(t, err)
-		assert.Nil(t, pm)
-
-		// Pointer to pointer are nilified
-		n := ""
-		p := &n
-		err = conn.QueryRow(ctx, "select null::xml").Scan(&p)
-		assert.NoError(t, err)
-		assert.Nil(t, p)
-
-		// A string cannot scan a NULL.
-		str := "foobar"
-		err = conn.QueryRow(ctx, "select null::xml").Scan(&str)
-		assert.EqualError(t, err, "can't scan into dest[0] (col: xml): cannot scan NULL into *string")
-	})
-}
-
-func TestXMLCodecPointerToPointerToString(t *testing.T) {
-	skipCockroachDB(t, "CockroachDB does not support XML.")
-	defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
-		var s *string
-		err := conn.QueryRow(ctx, "select ''::xml").Scan(&s)
-		require.NoError(t, err)
-		require.NotNil(t, s)
-		require.Equal(t, "", *s)
-
-		err = conn.QueryRow(ctx, "select null::xml").Scan(&s)
-		require.NoError(t, err)
-		require.Nil(t, s)
-	})
-}
-
-func TestXMLCodecDecodeValue(t *testing.T) {
-	skipCockroachDB(t, "CockroachDB does not support XML.")
-	defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, _ testing.TB, conn *pgx.Conn) {
-		for _, tt := range []struct {
-			sql      string
-			expected any
-		}{
-			{
-				sql:      `select '<foo>bar</foo>'::xml`,
-				expected: []byte("<foo>bar</foo>"),
-			},
-		} {
-			t.Run(tt.sql, func(t *testing.T) {
-				rows, err := conn.Query(ctx, tt.sql)
-				require.NoError(t, err)
-
-				for rows.Next() {
-					values, err := rows.Values()
-					require.NoError(t, err)
-					require.Len(t, values, 1)
-					require.Equal(t, tt.expected, values[0])
-				}
-
-				require.NoError(t, rows.Err())
-			})
-		}
-	})
-}
diff --git a/pgtype/zeronull/int_test.go b/pgtype/zeronull/int_test.go
index 7e32064a..7204cc88 100644
--- a/pgtype/zeronull/int_test.go
+++ b/pgtype/zeronull/int_test.go
@@ -1,5 +1,4 @@
-// Code generated from pgtype/zeronull/int_test.go.erb. DO NOT EDIT.
-
+// Do not edit. Generated from pgtype/zeronull/int_test.go.erb
 package zeronull_test
 
 import (
diff --git a/pgx_test.go b/pgx_test.go
deleted file mode 100644
index 51b4bbc4..00000000
--- a/pgx_test.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package pgx_test
-
-import (
-	"context"
-	"os"
-	"testing"
-
-	"github.com/jackc/pgx/v5"
-	_ "github.com/jackc/pgx/v5/stdlib"
-)
-
-func skipCockroachDB(t testing.TB, msg string) {
-	conn, err := pgx.Connect(context.Background(), os.Getenv("PGX_TEST_DATABASE"))
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer conn.Close(context.Background())
-
-	if conn.PgConn().ParameterStatus("crdb_version") != "" {
-		t.Skip(msg)
-	}
-}
diff --git a/query_test.go b/query_test.go
index 2c841024..df044cde 100644
--- a/query_test.go
+++ b/query_test.go
@@ -4,8 +4,6 @@ import (
 	"bytes"
 	"context"
 	"database/sql"
-	"database/sql/driver"
-	"encoding/json"
 	"errors"
 	"fmt"
 	"os"
@@ -420,7 +418,7 @@ func TestConnQueryReadWrongTypeError(t *testing.T) {
 		t.Fatal("Expected Rows to have an error after an improper read but it didn't")
 	}
 
-	if rows.Err().Error() != "can't scan into dest[0] (col: n): cannot scan int4 (OID 23) in binary format into *time.Time" {
+	if rows.Err().Error() != "can't scan into dest[0]: cannot scan int4 (OID 23) in binary format into *time.Time" {
 		t.Fatalf("Expected different Rows.Err(): %v", rows.Err())
 	}
 
@@ -1173,161 +1171,6 @@ func TestConnQueryDatabaseSQLDriverValuerWithAutoGeneratedPointerReceiver(t *tes
 	ensureConnValid(t, conn)
 }
 
-type nilPointerAsEmptyJSONObject struct {
-	ID   string
-	Name string
-}
-
-func (v *nilPointerAsEmptyJSONObject) Value() (driver.Value, error) {
-	if v == nil {
-		return "{}", nil
-	}
-
-	return json.Marshal(v)
-}
-
-// https://github.com/jackc/pgx/issues/1566
-func TestConnQueryDatabaseSQLDriverValuerCalledOnNilPointerImplementers(t *testing.T) {
-	t.Parallel()
-
-	conn := mustConnectString(t, os.Getenv("PGX_TEST_DATABASE"))
-	defer closeConn(t, conn)
-
-	mustExec(t, conn, "create temporary table t(v json not null)")
-
-	var v *nilPointerAsEmptyJSONObject
-	commandTag, err := conn.Exec(context.Background(), `insert into t(v) values($1)`, v)
-	require.NoError(t, err)
-	require.Equal(t, "INSERT 0 1", commandTag.String())
-
-	var s string
-	err = conn.QueryRow(context.Background(), "select v from t").Scan(&s)
-	require.NoError(t, err)
-	require.Equal(t, "{}", s)
-
-	_, err = conn.Exec(context.Background(), `delete from t`)
-	require.NoError(t, err)
-
-	v = &nilPointerAsEmptyJSONObject{ID: "1", Name: "foo"}
-	commandTag, err = conn.Exec(context.Background(), `insert into t(v) values($1)`, v)
-	require.NoError(t, err)
-	require.Equal(t, "INSERT 0 1", commandTag.String())
-
-	var v2 *nilPointerAsEmptyJSONObject
-	err = conn.QueryRow(context.Background(), "select v from t").Scan(&v2)
-	require.NoError(t, err)
-	require.Equal(t, v, v2)
-
-	ensureConnValid(t, conn)
-}
-
-type nilSliceAsEmptySlice []byte
-
-func (j nilSliceAsEmptySlice) Value() (driver.Value, error) {
-	if len(j) == 0 {
-		return []byte("[]"), nil
-	}
-
-	return []byte(j), nil
-}
-
-func (j *nilSliceAsEmptySlice) UnmarshalJSON(data []byte) error {
-	*j = bytes.Clone(data)
-	return nil
-}
-
-// https://github.com/jackc/pgx/issues/1860
-func TestConnQueryDatabaseSQLDriverValuerCalledOnNilSliceImplementers(t *testing.T) {
-	t.Parallel()
-
-	conn := mustConnectString(t, os.Getenv("PGX_TEST_DATABASE"))
-	defer closeConn(t, conn)
-
-	mustExec(t, conn, "create temporary table t(v json not null)")
-
-	var v nilSliceAsEmptySlice
-	commandTag, err := conn.Exec(context.Background(), `insert into t(v) values($1)`, v)
-	require.NoError(t, err)
-	require.Equal(t, "INSERT 0 1", commandTag.String())
-
-	var s string
-	err = conn.QueryRow(context.Background(), "select v from t").Scan(&s)
-	require.NoError(t, err)
-	require.Equal(t, "[]", s)
-
-	_, err = conn.Exec(context.Background(), `delete from t`)
-	require.NoError(t, err)
-
-	v = nilSliceAsEmptySlice(`{"name": "foo"}`)
-	commandTag, err = conn.Exec(context.Background(), `insert into t(v) values($1)`, v)
-	require.NoError(t, err)
-	require.Equal(t, "INSERT 0 1", commandTag.String())
-
-	var v2 nilSliceAsEmptySlice
-	err = conn.QueryRow(context.Background(), "select v from t").Scan(&v2)
-	require.NoError(t, err)
-	require.Equal(t, v, v2)
-
-	ensureConnValid(t, conn)
-}
-
-type nilMapAsEmptyObject map[string]any
-
-func (j nilMapAsEmptyObject) Value() (driver.Value, error) {
-	if j == nil {
-		return []byte("{}"), nil
-	}
-
-	return json.Marshal(j)
-}
-
-func (j *nilMapAsEmptyObject) UnmarshalJSON(data []byte) error {
-	var m map[string]any
-	err := json.Unmarshal(data, &m)
-	if err != nil {
-		return err
-	}
-
-	*j = m
-
-	return nil
-}
-
-// https://github.com/jackc/pgx/pull/2019#discussion_r1605806751
-func TestConnQueryDatabaseSQLDriverValuerCalledOnNilMapImplementers(t *testing.T) {
-	t.Parallel()
-
-	conn := mustConnectString(t, os.Getenv("PGX_TEST_DATABASE"))
-	defer closeConn(t, conn)
-
-	mustExec(t, conn, "create temporary table t(v json not null)")
-
-	var v nilMapAsEmptyObject
-	commandTag, err := conn.Exec(context.Background(), `insert into t(v) values($1)`, v)
-	require.NoError(t, err)
-	require.Equal(t, "INSERT 0 1", commandTag.String())
-
-	var s string
-	err = conn.QueryRow(context.Background(), "select v from t").Scan(&s)
-	require.NoError(t, err)
-	require.Equal(t, "{}", s)
-
-	_, err = conn.Exec(context.Background(), `delete from t`)
-	require.NoError(t, err)
-
-	v = nilMapAsEmptyObject{"name": "foo"}
-	commandTag, err = conn.Exec(context.Background(), `insert into t(v) values($1)`, v)
-	require.NoError(t, err)
-	require.Equal(t, "INSERT 0 1", commandTag.String())
-
-	var v2 nilMapAsEmptyObject
-	err = conn.QueryRow(context.Background(), "select v from t").Scan(&v2)
-	require.NoError(t, err)
-	require.Equal(t, v, v2)
-
-	ensureConnValid(t, conn)
-}
-
 func TestConnQueryDatabaseSQLDriverScannerWithBinaryPgTypeThatAcceptsSameType(t *testing.T) {
 	t.Parallel()
 
diff --git a/rows_test.go b/rows_test.go
index 4cda957f..bb9d5015 100644
--- a/rows_test.go
+++ b/rows_test.go
@@ -667,41 +667,6 @@ func TestRowToStructByName(t *testing.T) {
 	})
 }
 
-func TestRowToStructByNameDbTags(t *testing.T) {
-	type person struct {
-		Last             string `db:"last_name"`
-		First            string `db:"first_name"`
-		Age              int32  `db:"age"`
-		AccountID        string `db:"account_id"`
-		AnotherAccountID string `db:"account__id"`
-	}
-
-	defaultConnTestRunner.RunTest(context.Background(), t, func(ctx context.Context, t testing.TB, conn *pgx.Conn) {
-		rows, _ := conn.Query(ctx, `select 'John' as first_name, 'Smith' as last_name, n as age, 'd5e49d3f' as account_id, '5e49d321' as account__id from generate_series(0, 9) n`)
-		slice, err := pgx.CollectRows(rows, pgx.RowToStructByName[person])
-		assert.NoError(t, err)
-
-		assert.Len(t, slice, 10)
-		for i := range slice {
-			assert.Equal(t, "Smith", slice[i].Last)
-			assert.Equal(t, "John", slice[i].First)
-			assert.EqualValues(t, i, slice[i].Age)
-			assert.Equal(t, "d5e49d3f", slice[i].AccountID)
-			assert.Equal(t, "5e49d321", slice[i].AnotherAccountID)
-		}
-
-		// check missing fields in a returned row
-		rows, _ = conn.Query(ctx, `select 'Smith' as last_name, n as age from generate_series(0, 9) n`)
-		_, err = pgx.CollectRows(rows, pgx.RowToStructByName[person])
-		assert.ErrorContains(t, err, "cannot find field first_name in returned row")
-
-		// check missing field in a destination struct
-		rows, _ = conn.Query(ctx, `select 'John' as first_name, 'Smith' as last_name, n as age, 'd5e49d3f' as account_id, '5e49d321' as account__id, null as ignore from generate_series(0, 9) n`)
-		_, err = pgx.CollectRows(rows, pgx.RowToAddrOfStructByName[person])
-		assert.ErrorContains(t, err, "struct doesn't have corresponding row field ignore")
-	})
-}
-
 func TestRowToStructByNameEmbeddedStruct(t *testing.T) {
 	type Name struct {
 		Last  string `db:"last_name"`
diff --git a/testsetup/generate_certs.go b/testsetup/generate_certs.go
index f285549a..945c6c5e 100644
--- a/testsetup/generate_certs.go
+++ b/testsetup/generate_certs.go
@@ -106,12 +106,12 @@ func main() {
 		panic(err)
 	}
 
-	err = writeEncryptedPrivateKey("pgx_sslcert.key", clientCertPrivKey, "certpw")
+	writeEncryptedPrivateKey("pgx_sslcert.key", clientCertPrivKey, "certpw")
 	if err != nil {
 		panic(err)
 	}
 
-	err = writeCertificate("pgx_sslcert.crt", clientBytes)
+	writeCertificate("pgx_sslcert.crt", clientBytes)
 	if err != nil {
 		panic(err)
 	}
diff --git a/tracelog/tracelog_test.go b/tracelog/tracelog_test.go
index f1959b32..6812a97b 100644
--- a/tracelog/tracelog_test.go
+++ b/tracelog/tracelog_test.go
@@ -6,17 +6,14 @@ import (
 	"log"
 	"os"
 	"strings"
-	"sync"
 	"testing"
 	"time"
 
 	"github.com/jackc/pgx/v5"
-	"github.com/jackc/pgx/v5/pgxpool"
 	"github.com/jackc/pgx/v5/pgxtest"
 	"github.com/jackc/pgx/v5/tracelog"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
-	"golang.org/x/sync/errgroup"
 )
 
 var defaultConnTestRunner pgxtest.ConnTestRunner
@@ -38,29 +35,18 @@ type testLog struct {
 
 type testLogger struct {
 	logs []testLog
-
-	mux sync.Mutex
 }
 
 func (l *testLogger) Log(ctx context.Context, level tracelog.LogLevel, msg string, data map[string]any) {
-	l.mux.Lock()
-	defer l.mux.Unlock()
-
 	data["ctxdata"] = ctx.Value("ctxdata")
 	l.logs = append(l.logs, testLog{lvl: level, msg: msg, data: data})
 }
 
 func (l *testLogger) Clear() {
-	l.mux.Lock()
-	defer l.mux.Unlock()
-
 	l.logs = l.logs[0:0]
 }
 
 func (l *testLogger) FilterByMsg(msg string) (res []testLog) {
-	l.mux.Lock()
-	defer l.mux.Unlock()
-
 	for _, log := range l.logs {
 		if log.msg == msg {
 			res = append(res, log)
@@ -471,42 +457,3 @@ func TestLogPrepare(t *testing.T) {
 		require.Equal(t, err, logger.logs[0].data["err"])
 	})
 }
-
-// https://github.com/jackc/pgx/pull/2120
-func TestConcurrentUsage(t *testing.T) {
-	t.Parallel()
-
-	ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
-	defer cancel()
-
-	logger := &testLogger{}
-	tracer := &tracelog.TraceLog{
-		Logger:   logger,
-		LogLevel: tracelog.LogLevelTrace,
-	}
-
-	config, err := pgxpool.ParseConfig(os.Getenv("PGX_TEST_DATABASE"))
-	require.NoError(t, err)
-	config.ConnConfig.Tracer = tracer
-
-	for i := 0; i < 50; i++ {
-		func() {
-			pool, err := pgxpool.NewWithConfig(ctx, config)
-			require.NoError(t, err)
-
-			defer pool.Close()
-
-			eg := errgroup.Group{}
-
-			for i := 0; i < 5; i++ {
-				eg.Go(func() error {
-					_, err := pool.Exec(ctx, `select 1`)
-					return err
-				})
-			}
-
-			err = eg.Wait()
-			require.NoError(t, err)
-		}()
-	}
-}
diff --git a/values_test.go b/values_test.go
index 01550c9c..132b79a2 100644
--- a/values_test.go
+++ b/values_test.go
@@ -3,7 +3,6 @@ package pgx_test
 import (
 	"bytes"
 	"context"
-	"fmt"
 	"net"
 	"os"
 	"reflect"
@@ -216,12 +215,7 @@ func testJSONInt16ArrayFailureDueToOverflow(t *testing.T, conn *pgx.Conn, typena
 	input := []int{1, 2, 234432}
 	var output []int16
 	err := conn.QueryRow(context.Background(), "select $1::"+typename, input).Scan(&output)
-	fieldName := typename
-	if conn.PgConn().ParameterStatus("crdb_version") != "" && typename == "json" {
-		fieldName = "jsonb" // Seems like CockroachDB treats json as jsonb.
-	}
-	expectedMessage := fmt.Sprintf("can't scan into dest[0] (col: %s): json: cannot unmarshal number 234432 into Go value of type int16", fieldName)
-	if err == nil || err.Error() != expectedMessage {
+	if err == nil || err.Error() != "can't scan into dest[0]: json: cannot unmarshal number 234432 into Go value of type int16" {
 		t.Errorf("%s: Expected *json.UnmarshalTypeError, but got %v", typename, err)
 	}
 }