- update vendor

master
李光春 1 year ago
parent 60ce1d8dd1
commit bdd705368c

@ -49,7 +49,7 @@ require (
github.com/google/go-querystring v1.1.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/pgx/v5 v5.2.0 // indirect
github.com/jackc/pgx/v5 v5.3.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect

@ -234,8 +234,9 @@ github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6
github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg=
github.com/jackc/pgx/v4 v4.11.0/go.mod h1:i62xJgdrtVDsnL3U8ekyrQXEwGNTRoG7/8r+CIdYfcc=
github.com/jackc/pgx/v4 v4.12.0/go.mod h1:fE547h6VulLPA3kySjfnSG/e2D861g/50JlVUa/ub60=
github.com/jackc/pgx/v5 v5.2.0 h1:NdPpngX0Y6z6XDFKqmFQaE+bCtkqzvQIOt1wvBlAqs8=
github.com/jackc/pgx/v5 v5.2.0/go.mod h1:Ptn7zmohNsWEsdxRawMzk3gaKma2obW+NWTnKa0S4nk=
github.com/jackc/pgx/v5 v5.3.0 h1:/NQi8KHMpKWHInxXesC8yD4DhkXPrVhmnwYkjp9AmBA=
github.com/jackc/pgx/v5 v5.3.0/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=

@ -23,3 +23,5 @@ _testmain.go
.envrc
/.testdb
.DS_Store

@ -1,3 +1,27 @@
# 5.3.0 (February 11, 2023)
* Fix: json values work with sql.Scanner
* Fixed / improved error messages (Mark Chambers and Yevgeny Pats)
* Fix: support scan into single dimensional arrays
* Fix: MaxConnLifetimeJitter setting actually jitter (Ben Weintraub)
* Fix: driver.Value representation of bytea should be []byte not string
* Fix: better handling of unregistered OIDs
* CopyFrom can use query cache to avoid extra round trip to get OIDs (Alejandro Do Nascimento Mora)
* Fix: encode to json ignoring driver.Valuer
* Support sql.Scanner on renamed base type
* Fix: pgtype.Numeric text encoding of negative numbers (Mark Chambers)
* Fix: connect with multiple hostnames when one can't be resolved
* Upgrade puddle to remove dependency on uber/atomic and fix alignment issue on 32-bit platform
* Fix: scanning json column into **string
* Multiple reductions in memory allocations
* Fake non-blocking read adapts its max wait time
* Improve CopyFrom performance and reduce memory usage
* Fix: encode []any to array
* Fix: LoadType for composite with dropped attributes (Felix Röhrich)
* Support v4 and v5 stdlib in same program
* Fix: text format array decoding with string of "NULL"
* Prefer binary format for arrays
# 5.2.0 (December 5, 2022)
* `tracelog.TraceLog` implements the pgx.PrepareTracer interface. (Vitalii Solodilov)

@ -88,7 +88,7 @@ See CONTRIBUTING.md for setup instructions.
## 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.18 and higher and PostgreSQL 11 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.19 and higher and PostgreSQL 11 and higher. pgx also is tested against the latest version of [CockroachDB](https://www.cockroachlabs.com/product/).
## Version Policy
@ -142,3 +142,12 @@ Library for scanning data from a database into Go structs and more.
### [https://github.com/otan/gopgkrb5](https://github.com/otan/gopgkrb5)
Adds GSSAPI / Kerberos authentication support.
### [github.com/wcamarao/pmx](https://github.com/wcamarao/pmx)
Explicit data mapping and scanning library for Go structs and slices.
### [github.com/stephenafamo/scan](https://github.com/stephenafamo/scan)
Type safe and flexible package for scanning database data into Go types.
Supports, structs, maps, slices and custom mapping functions.

@ -197,17 +197,17 @@ func ParseConfigWithOptions(connString string, options ParseConfigOptions) (*Con
// ParseConfig creates a ConnConfig from a connection string. ParseConfig handles all options that pgconn.ParseConfig
// does. In addition, it accepts the following options:
//
// default_query_exec_mode
// Possible values: "cache_statement", "cache_describe", "describe_exec", "exec", and "simple_protocol". See
// QueryExecMode constant documentation for the meaning of these values. Default: "cache_statement".
// - default_query_exec_mode.
// Possible values: "cache_statement", "cache_describe", "describe_exec", "exec", and "simple_protocol". See
// QueryExecMode constant documentation for the meaning of these values. Default: "cache_statement".
//
// statement_cache_capacity
// The maximum size of the statement cache used when executing a query with "cache_statement" query exec mode.
// Default: 512.
// - statement_cache_capacity.
// The maximum size of the statement cache used when executing a query with "cache_statement" query exec mode.
// Default: 512.
//
// description_cache_capacity
// The maximum size of the description cache used when executing a query with "cache_describe" query exec mode.
// Default: 512.
// - description_cache_capacity.
// The maximum size of the description cache used when executing a query with "cache_describe" query exec mode.
// Default: 512.
func ParseConfig(connString string) (*ConnConfig, error) {
return ParseConfigWithOptions(connString, ParseConfigOptions{})
}
@ -721,43 +721,10 @@ optionLoop:
sd, explicitPreparedStatement := c.preparedStatements[sql]
if sd != nil || mode == QueryExecModeCacheStatement || mode == QueryExecModeCacheDescribe || mode == QueryExecModeDescribeExec {
if sd == nil {
switch mode {
case QueryExecModeCacheStatement:
if c.statementCache == nil {
err = errDisabledStatementCache
rows.fatal(err)
return rows, err
}
sd = c.statementCache.Get(sql)
if sd == nil {
sd, err = c.Prepare(ctx, stmtcache.NextStatementName(), sql)
if err != nil {
rows.fatal(err)
return rows, err
}
c.statementCache.Put(sd)
}
case QueryExecModeCacheDescribe:
if c.descriptionCache == nil {
err = errDisabledDescriptionCache
rows.fatal(err)
return rows, err
}
sd = c.descriptionCache.Get(sql)
if sd == nil {
sd, err = c.Prepare(ctx, "", sql)
if err != nil {
rows.fatal(err)
return rows, err
}
c.descriptionCache.Put(sd)
}
case QueryExecModeDescribeExec:
sd, err = c.Prepare(ctx, "", sql)
if err != nil {
rows.fatal(err)
return rows, err
}
sd, err = c.getStatementDescription(ctx, mode, sql)
if err != nil {
rows.fatal(err)
return rows, err
}
}
@ -827,6 +794,48 @@ optionLoop:
return rows, rows.err
}
// getStatementDescription returns the statement description of the sql query
// according to the given mode.
//
// If the mode is one that doesn't require to know the param and result OIDs
// then nil is returned without error.
func (c *Conn) getStatementDescription(
ctx context.Context,
mode QueryExecMode,
sql string,
) (sd *pgconn.StatementDescription, err error) {
switch mode {
case QueryExecModeCacheStatement:
if c.statementCache == nil {
return nil, errDisabledStatementCache
}
sd = c.statementCache.Get(sql)
if sd == nil {
sd, err = c.Prepare(ctx, stmtcache.NextStatementName(), sql)
if err != nil {
return nil, err
}
c.statementCache.Put(sd)
}
case QueryExecModeCacheDescribe:
if c.descriptionCache == nil {
return nil, errDisabledDescriptionCache
}
sd = c.descriptionCache.Get(sql)
if sd == nil {
sd, err = c.Prepare(ctx, "", sql)
if err != nil {
return nil, err
}
c.descriptionCache.Put(sd)
}
case QueryExecModeDescribeExec:
return c.Prepare(ctx, "", sql)
}
return sd, err
}
// QueryRow is a convenience wrapper over Query. Any error that occurs while
// querying is deferred until calling Scan on the returned Row. That Row will
// error with ErrNoRows if no rows are returned.
@ -1106,6 +1115,8 @@ func (c *Conn) sendBatchExtendedWithDescription(ctx context.Context, b *Batch, d
for _, bi := range b.queuedQueries {
err := c.eqb.Build(c.typeMap, bi.sd, bi.arguments)
if err != nil {
// we wrap the error so we the user can understand which query failed inside the batch
err = fmt.Errorf("error building query %s: %w", bi.query, err)
return &pipelineBatchResults{ctx: ctx, conn: c, err: err}
}
@ -1271,7 +1282,7 @@ func (c *Conn) getCompositeFields(ctx context.Context, oid uint32) ([]pgtype.Com
var fieldOID uint32
rows, _ := c.Query(ctx, `select attname, atttypid
from pg_attribute
where attrelid=$1
where attrelid=$1 and not attisdropped
order by attnum`,
typrelid,
)

@ -85,6 +85,7 @@ type copyFrom struct {
columnNames []string
rowSrc CopyFromSource
readerErrChan chan error
mode QueryExecMode
}
func (ct *copyFrom) run(ctx context.Context) (int64, error) {
@ -105,9 +106,29 @@ func (ct *copyFrom) run(ctx context.Context) (int64, error) {
}
quotedColumnNames := cbuf.String()
sd, err := ct.conn.Prepare(ctx, "", fmt.Sprintf("select %s from %s", quotedColumnNames, quotedTableName))
if err != nil {
return 0, err
var sd *pgconn.StatementDescription
switch ct.mode {
case QueryExecModeExec, QueryExecModeSimpleProtocol:
// These modes don't support the binary format. Before the inclusion of the
// QueryExecModes, Conn.Prepare was called on every COPY operation to get
// the OIDs. These prepared statements were not cached.
//
// Since that's the same behavior provided by QueryExecModeDescribeExec,
// we'll default to that mode.
ct.mode = QueryExecModeDescribeExec
fallthrough
case QueryExecModeCacheStatement, QueryExecModeCacheDescribe, QueryExecModeDescribeExec:
var err error
sd, err = ct.conn.getStatementDescription(
ctx,
ct.mode,
fmt.Sprintf("select %s from %s", quotedColumnNames, quotedTableName),
)
if err != nil {
return 0, fmt.Errorf("statement description failed: %w", err)
}
default:
return 0, fmt.Errorf("unknown QueryExecMode: %v", ct.mode)
}
r, w := io.Pipe()
@ -167,8 +188,13 @@ func (ct *copyFrom) run(ctx context.Context) (int64, error) {
}
func (ct *copyFrom) buildCopyBuf(buf []byte, sd *pgconn.StatementDescription) (bool, []byte, error) {
const sendBufSize = 65536 - 5 // The packet has a 5-byte header
lastBufLen := 0
largestRowLen := 0
for ct.rowSrc.Next() {
lastBufLen = len(buf)
values, err := ct.rowSrc.Values()
if err != nil {
return false, nil, err
@ -185,7 +211,15 @@ func (ct *copyFrom) buildCopyBuf(buf []byte, sd *pgconn.StatementDescription) (b
}
}
if len(buf) > 65536 {
rowLen := len(buf) - lastBufLen
if rowLen > largestRowLen {
largestRowLen = rowLen
}
// Try not to overflow size of the buffer PgConn.CopyFrom will be reading into. If that happens then the nature of
// io.Pipe means that the next Read will be short. This can lead to pathological send sizes such as 65531, 13, 65531
// 13, 65531, 13, 65531, 13.
if len(buf) > sendBufSize-largestRowLen {
return true, buf, nil
}
}
@ -208,6 +242,7 @@ func (c *Conn) CopyFrom(ctx context.Context, tableName Identifier, columnNames [
columnNames: columnNames,
rowSrc: rowSrc,
readerErrChan: make(chan error),
mode: c.config.DefaultQueryExecMode,
}
return ct.run(ctx)

@ -69,8 +69,9 @@ Use Exec to execute a query that does not return a result set.
PostgreSQL Data Types
The package pgtype provides extensive and customizable support for converting Go values to and from PostgreSQL values
including array and composite types. See that package's documentation for details.
pgx uses the pgtype package to converting Go values to and from PostgreSQL values. It supports many PostgreSQL types
directly and is customizable and extendable. User defined data types such as enums, domains, and composite types may
require type registration. See that package's documentation for details.
Transactions

@ -1,6 +1,7 @@
package pgx
import (
"database/sql/driver"
"fmt"
"github.com/jackc/pgx/v5/internal/anynil"
@ -181,6 +182,19 @@ func (eqb *ExtendedQueryBuilder) appendParamsForQueryExecModeExec(m *pgtype.Map,
}
}
}
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 {

@ -1,4 +1,7 @@
// Package iobufpool implements a global segregated-fit pool of buffers for IO.
//
// It uses *[]byte instead of []byte to avoid the sync.Pool allocation with Put. Unfortunately, using a pointer to avoid
// an allocation is purposely not documented. https://github.com/golang/go/issues/16323
package iobufpool
import "sync"
@ -10,17 +13,27 @@ var pools [18]*sync.Pool
func init() {
for i := range pools {
bufLen := 1 << (minPoolExpOf2 + i)
pools[i] = &sync.Pool{New: func() any { return make([]byte, bufLen) }}
pools[i] = &sync.Pool{
New: func() any {
buf := make([]byte, bufLen)
return &buf
},
}
}
}
// Get gets a []byte of len size with cap <= size*2.
func Get(size int) []byte {
func Get(size int) *[]byte {
i := getPoolIdx(size)
if i >= len(pools) {
return make([]byte, size)
buf := make([]byte, size)
return &buf
}
return pools[i].Get().([]byte)[:size]
ptrBuf := (pools[i].Get().(*[]byte))
*ptrBuf = (*ptrBuf)[:size]
return ptrBuf
}
func getPoolIdx(size int) int {
@ -36,8 +49,8 @@ func getPoolIdx(size int) int {
}
// Put returns buf to the pool.
func Put(buf []byte) {
i := putPoolIdx(cap(buf))
func Put(buf *[]byte) {
i := putPoolIdx(cap(*buf))
if i < 0 {
return
}

@ -8,11 +8,11 @@ const minBufferQueueLen = 8
type bufferQueue struct {
lock sync.Mutex
queue [][]byte
queue []*[]byte
r, w int
}
func (bq *bufferQueue) pushBack(buf []byte) {
func (bq *bufferQueue) pushBack(buf *[]byte) {
bq.lock.Lock()
defer bq.lock.Unlock()
@ -23,7 +23,7 @@ func (bq *bufferQueue) pushBack(buf []byte) {
bq.w++
}
func (bq *bufferQueue) pushFront(buf []byte) {
func (bq *bufferQueue) pushFront(buf *[]byte) {
bq.lock.Lock()
defer bq.lock.Unlock()
@ -35,7 +35,7 @@ func (bq *bufferQueue) pushFront(buf []byte) {
bq.w++
}
func (bq *bufferQueue) popFront() []byte {
func (bq *bufferQueue) popFront() *[]byte {
bq.lock.Lock()
defer bq.lock.Unlock()
@ -51,7 +51,7 @@ func (bq *bufferQueue) popFront() []byte {
bq.r = 0
bq.w = 0
if len(bq.queue) > minBufferQueueLen {
bq.queue = make([][]byte, minBufferQueueLen)
bq.queue = make([]*[]byte, minBufferQueueLen)
}
}
@ -64,7 +64,7 @@ func (bq *bufferQueue) growQueue() {
desiredLen = minBufferQueueLen
}
newQueue := make([][]byte, desiredLen)
newQueue := make([]*[]byte, desiredLen)
copy(newQueue, bq.queue)
bq.queue = newQueue
}

@ -26,7 +26,9 @@ import (
var errClosed = errors.New("closed")
var ErrWouldBlock = new(wouldBlockError)
const fakeNonblockingWaitDuration = 100 * time.Millisecond
const fakeNonblockingWriteWaitDuration = 100 * time.Millisecond
const minNonblockingReadWaitDuration = time.Microsecond
const maxNonblockingReadWaitDuration = 100 * time.Millisecond
// NonBlockingDeadline is a magic value that when passed to Set[Read]Deadline places the connection in non-blocking read
// mode.
@ -54,7 +56,7 @@ type Conn interface {
// Flush flushes any buffered writes.
Flush() error
// BufferReadUntilBlock reads and buffers any sucessfully read bytes until the read would block.
// BufferReadUntilBlock reads and buffers any successfully read bytes until the read would block.
BufferReadUntilBlock() error
}
@ -73,14 +75,24 @@ type NetConn struct {
readFlushLock sync.Mutex
// non-blocking writes with syscall.RawConn are done with a callback function. By using these fields instead of the
// callback functions closure to pass the buf argument and receive the n and err results we avoid some allocations.
nonblockWriteBuf []byte
nonblockWriteErr error
nonblockWriteN int
readDeadlineLock sync.Mutex
readDeadline time.Time
readNonblocking bool
// callback functions closure to pass the buf argument and receive the n and err results we avoid some allocations.
nonblockWriteFunc func(fd uintptr) (done bool)
nonblockWriteBuf []byte
nonblockWriteErr error
nonblockWriteN int
// non-blocking reads with syscall.RawConn are done with a callback function. By using these fields instead of the
// callback functions closure to pass the buf argument and receive the n and err results we avoid some allocations.
nonblockReadFunc func(fd uintptr) (done bool)
nonblockReadBuf []byte
nonblockReadErr error
nonblockReadN int
readDeadlineLock sync.Mutex
readDeadline time.Time
readNonblocking bool
fakeNonBlockingShortReadCount int
fakeNonblockingReadWaitDuration time.Duration
writeDeadlineLock sync.Mutex
writeDeadline time.Time
@ -88,7 +100,8 @@ type NetConn struct {
func NewNetConn(conn net.Conn, fakeNonBlockingIO bool) *NetConn {
nc := &NetConn{
conn: conn,
conn: conn,
fakeNonblockingReadWaitDuration: maxNonblockingReadWaitDuration,
}
if !fakeNonBlockingIO {
@ -121,9 +134,9 @@ func (c *NetConn) Read(b []byte) (n int, err error) {
if buf == nil {
break
}
copiedN := copy(b[n:], buf)
if copiedN < len(buf) {
buf = buf[copiedN:]
copiedN := copy(b[n:], *buf)
if copiedN < len(*buf) {
*buf = (*buf)[copiedN:]
c.readQueue.pushFront(buf)
} else {
iobufpool.Put(buf)
@ -160,7 +173,7 @@ func (c *NetConn) Write(b []byte) (n int, err error) {
}
buf := iobufpool.Get(len(b))
copy(buf, b)
copy(*buf, b)
c.writeQueue.pushBack(buf)
return len(b), nil
}
@ -278,14 +291,14 @@ func (c *NetConn) flush() error {
}()
for buf := c.writeQueue.popFront(); buf != nil; buf = c.writeQueue.popFront() {
remainingBuf := buf
remainingBuf := *buf
for len(remainingBuf) > 0 {
n, err := c.nonblockingWrite(remainingBuf)
remainingBuf = remainingBuf[n:]
if err != nil {
if !errors.Is(err, ErrWouldBlock) {
buf = buf[:len(remainingBuf)]
copy(buf, remainingBuf)
*buf = (*buf)[:len(remainingBuf)]
copy(*buf, remainingBuf)
c.writeQueue.pushFront(buf)
return err
}
@ -313,10 +326,12 @@ func (c *NetConn) flush() error {
func (c *NetConn) BufferReadUntilBlock() error {
for {
buf := iobufpool.Get(8 * 1024)
n, err := c.nonblockingRead(buf)
n, err := c.nonblockingRead(*buf)
if n > 0 {
buf = buf[:n]
*buf = (*buf)[:n]
c.readQueue.pushBack(buf)
} else if n == 0 {
iobufpool.Put(buf)
}
if err != nil {
@ -369,7 +384,7 @@ func (c *NetConn) fakeNonblockingWrite(b []byte) (n int, err error) {
c.writeDeadlineLock.Lock()
defer c.writeDeadlineLock.Unlock()
deadline := time.Now().Add(fakeNonblockingWaitDuration)
deadline := time.Now().Add(fakeNonblockingWriteWaitDuration)
if c.writeDeadline.IsZero() || deadline.Before(c.writeDeadline) {
err = c.conn.SetWriteDeadline(deadline)
if err != nil {
@ -402,13 +417,40 @@ func (c *NetConn) fakeNonblockingRead(b []byte) (n int, err error) {
c.readDeadlineLock.Lock()
defer c.readDeadlineLock.Unlock()
deadline := time.Now().Add(fakeNonblockingWaitDuration)
// The first 5 reads only read 1 byte at a time. This should give us 4 chances to read when we are sure the bytes are
// already in Go or the OS's receive buffer.
if c.fakeNonBlockingShortReadCount < 5 && len(b) > 0 {
b = b[:1]
}
startTime := time.Now()
deadline := startTime.Add(c.fakeNonblockingReadWaitDuration)
if c.readDeadline.IsZero() || deadline.Before(c.readDeadline) {
err = c.conn.SetReadDeadline(deadline)
if err != nil {
return 0, err
}
defer func() {
// If the read was successful and the wait duration is not already the minimum
if err == nil && c.fakeNonblockingReadWaitDuration > minNonblockingReadWaitDuration {
endTime := time.Now()
if n > 0 && c.fakeNonBlockingShortReadCount < 5 {
c.fakeNonBlockingShortReadCount++
}
// The wait duration should be 2x the fastest read that has occurred. This should give reasonable assurance that
// a Read deadline will not block a read before it has a chance to read data already in Go or the OS's receive
// buffer.
proposedWait := endTime.Sub(startTime) * 2
if proposedWait < minNonblockingReadWaitDuration {
proposedWait = minNonblockingReadWaitDuration
}
if proposedWait < c.fakeNonblockingReadWaitDuration {
c.fakeNonblockingReadWaitDuration = proposedWait
}
}
// Ignoring error resetting deadline as there is nothing that can reasonably be done if it fails.
c.conn.SetReadDeadline(c.readDeadline)

@ -1,9 +1,7 @@
//go:build !(aix || android || darwin || dragonfly || freebsd || hurd || illumos || ios || linux || netbsd || openbsd || solaris)
//go:build !unix
package nbconn
// Not using unix build tag for support on Go 1.18.
func (c *NetConn) realNonblockingWrite(b []byte) (n int, err error) {
return c.fakeNonblockingWrite(b)
}

@ -1,9 +1,7 @@
//go:build aix || android || darwin || dragonfly || freebsd || hurd || illumos || ios || linux || netbsd || openbsd || solaris
//go:build unix
package nbconn
// Not using unix build tag for support on Go 1.18.
import (
"errors"
"io"
@ -12,14 +10,19 @@ import (
// realNonblockingWrite does a non-blocking write. readFlushLock must already be held.
func (c *NetConn) realNonblockingWrite(b []byte) (n int, err error) {
if c.nonblockWriteFunc == nil {
c.nonblockWriteFunc = func(fd uintptr) (done bool) {
c.nonblockWriteN, c.nonblockWriteErr = syscall.Write(int(fd), c.nonblockWriteBuf)
return true
}
}
c.nonblockWriteBuf = b
c.nonblockWriteN = 0
c.nonblockWriteErr = nil
err = c.rawConn.Write(func(fd uintptr) (done bool) {
c.nonblockWriteN, c.nonblockWriteErr = syscall.Write(int(fd), c.nonblockWriteBuf)
return true
})
err = c.rawConn.Write(c.nonblockWriteFunc)
n = c.nonblockWriteN
c.nonblockWriteBuf = nil // ensure that no reference to b is kept.
if err == nil && c.nonblockWriteErr != nil {
if errors.Is(c.nonblockWriteErr, syscall.EWOULDBLOCK) {
err = ErrWouldBlock
@ -40,16 +43,24 @@ func (c *NetConn) realNonblockingWrite(b []byte) (n int, err error) {
}
func (c *NetConn) realNonblockingRead(b []byte) (n int, err error) {
var funcErr error
err = c.rawConn.Read(func(fd uintptr) (done bool) {
n, funcErr = syscall.Read(int(fd), b)
return true
})
if err == nil && funcErr != nil {
if errors.Is(funcErr, syscall.EWOULDBLOCK) {
if c.nonblockReadFunc == nil {
c.nonblockReadFunc = func(fd uintptr) (done bool) {
c.nonblockReadN, c.nonblockReadErr = syscall.Read(int(fd), c.nonblockReadBuf)
return true
}
}
c.nonblockReadBuf = b
c.nonblockReadN = 0
c.nonblockReadErr = nil
err = c.rawConn.Read(c.nonblockReadFunc)
n = c.nonblockReadN
c.nonblockReadBuf = nil // ensure that no reference to b is kept.
if err == nil && c.nonblockReadErr != nil {
if errors.Is(c.nonblockReadErr, syscall.EWOULDBLOCK) {
err = ErrWouldBlock
} else {
err = funcErr
err = c.nonblockReadErr
}
}
if err != nil {

@ -8,7 +8,6 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"math"
"net"
"net/url"
@ -211,9 +210,9 @@ func NetworkAddress(host string, port uint16) (network, address string) {
//
// In addition, ParseConfig accepts the following options:
//
// servicefile
// libpq only reads servicefile from the PGSERVICEFILE environment variable. ParseConfig accepts servicefile as a
// part of the connection string.
// - servicefile.
// libpq only reads servicefile from the PGSERVICEFILE environment variable. ParseConfig accepts servicefile as a
// part of the connection string.
func ParseConfig(connString string) (*Config, error) {
var parseConfigOptions ParseConfigOptions
return ParseConfigWithOptions(connString, parseConfigOptions)
@ -687,7 +686,7 @@ func configTLS(settings map[string]string, thisHost string, parseConfigOptions P
caCertPool := x509.NewCertPool()
caPath := sslrootcert
caCert, err := ioutil.ReadFile(caPath)
caCert, err := os.ReadFile(caPath)
if err != nil {
return nil, fmt.Errorf("unable to read CA file: %w", err)
}
@ -705,7 +704,7 @@ func configTLS(settings map[string]string, thisHost string, parseConfigOptions P
}
if sslcert != "" && sslkey != "" {
buf, err := ioutil.ReadFile(sslkey)
buf, err := os.ReadFile(sslkey)
if err != nil {
return nil, fmt.Errorf("unable to read sslkey: %w", err)
}
@ -744,7 +743,7 @@ func configTLS(settings map[string]string, thisHost string, parseConfigOptions P
} else {
pemKey = pem.EncodeToMemory(block)
}
certfile, err := ioutil.ReadFile(sslcert)
certfile, err := os.ReadFile(sslcert)
if err != nil {
return nil, fmt.Errorf("unable to read cert: %w", err)
}

@ -203,6 +203,8 @@ func ConnectConfig(octx context.Context, config *Config) (pgConn *PgConn, err er
func expandWithIPs(ctx context.Context, lookupFn LookupFunc, fallbacks []*FallbackConfig) ([]*FallbackConfig, error) {
var configs []*FallbackConfig
var lookupErrors []error
for _, fb := range fallbacks {
// skip resolve for unix sockets
if isAbsolutePath(fb.Host) {
@ -217,7 +219,8 @@ func expandWithIPs(ctx context.Context, lookupFn LookupFunc, fallbacks []*Fallba
ips, err := lookupFn(ctx, fb.Host)
if err != nil {
return nil, err
lookupErrors = append(lookupErrors, err)
continue
}
for _, ip := range ips {
@ -242,6 +245,12 @@ func expandWithIPs(ctx context.Context, lookupFn LookupFunc, fallbacks []*Fallba
}
}
// See https://github.com/jackc/pgx/issues/1464. When Go 1.20 can be used in pgx consider using errors.Join so all
// errors are reported.
if len(configs) == 0 && len(lookupErrors) > 0 {
return nil, lookupErrors[0]
}
return configs, nil
}
@ -1166,20 +1175,20 @@ func (pgConn *PgConn) CopyFrom(ctx context.Context, r io.Reader, sql string) (Co
buf := iobufpool.Get(65536)
defer iobufpool.Put(buf)
buf[0] = 'd'
(*buf)[0] = 'd'
var readErr, pgErr error
for pgErr == nil {
// Read chunk from r.
var n int
n, readErr = r.Read(buf[5:cap(buf)])
n, readErr = r.Read((*buf)[5:cap(*buf)])
// Send chunk to PostgreSQL.
if n > 0 {
buf = buf[0 : n+5]
pgio.SetInt32(buf[1:], int32(n+4))
*buf = (*buf)[0 : n+5]
pgio.SetInt32((*buf)[1:], int32(n+4))
writeErr := pgConn.frontend.SendUnbufferedEncodedCopyData(buf)
writeErr := pgConn.frontend.SendUnbufferedEncodedCopyData(*buf)
if writeErr != nil {
pgConn.asyncClose()
return CommandTag{}, err

@ -196,7 +196,7 @@ func (b *Backend) Receive() (FrontendMessage, error) {
case AuthTypeCleartextPassword, AuthTypeMD5Password:
fallthrough
default:
// to maintain backwards compatability
// to maintain backwards compatibility
msg = &PasswordMessage{}
}
case 'Q':
@ -233,11 +233,11 @@ func (b *Backend) Receive() (FrontendMessage, error) {
// contextual identification of FrontendMessages. For example, in the
// PG message flow documentation for PasswordMessage:
//
// Byte1('p')
// Byte1('p')
//
// Identifies the message as a password response. Note that this is also used for
// GSSAPI, SSPI and SASL response messages. The exact message type can be deduced from
// the context.
// Identifies the message as a password response. Note that this is also used for
// GSSAPI, SSPI and SASL response messages. The exact message type can be deduced from
// the context.
//
// Since the Frontend does not know about the state of a backend, it is important
// to call SetAuthType() after an authentication request is received by the Frontend.

@ -14,7 +14,7 @@ import (
type chunkReader struct {
r io.Reader
buf []byte
buf *[]byte
rp, wp int // buf read position and write position
minBufSize int
@ -45,7 +45,7 @@ func newChunkReader(r io.Reader, minBufSize int) *chunkReader {
func (r *chunkReader) Next(n int) (buf []byte, err error) {
// Reset the buffer if it is empty
if r.rp == r.wp {
if len(r.buf) != r.minBufSize {
if len(*r.buf) != r.minBufSize {
iobufpool.Put(r.buf)
r.buf = iobufpool.Get(r.minBufSize)
}
@ -55,15 +55,15 @@ func (r *chunkReader) Next(n int) (buf []byte, err error) {
// n bytes already in buf
if (r.wp - r.rp) >= n {
buf = r.buf[r.rp : r.rp+n : r.rp+n]
buf = (*r.buf)[r.rp : r.rp+n : r.rp+n]
r.rp += n
return buf, err
}
// buf is smaller than requested number of bytes
if len(r.buf) < n {
if len(*r.buf) < n {
bigBuf := iobufpool.Get(n)
r.wp = copy(bigBuf, r.buf[r.rp:r.wp])
r.wp = copy((*bigBuf), (*r.buf)[r.rp:r.wp])
r.rp = 0
iobufpool.Put(r.buf)
r.buf = bigBuf
@ -71,20 +71,20 @@ func (r *chunkReader) Next(n int) (buf []byte, err error) {
// buf is large enough, but need to shift filled area to start to make enough contiguous space
minReadCount := n - (r.wp - r.rp)
if (len(r.buf) - r.wp) < minReadCount {
r.wp = copy(r.buf, r.buf[r.rp:r.wp])
if (len(*r.buf) - r.wp) < minReadCount {
r.wp = copy((*r.buf), (*r.buf)[r.rp:r.wp])
r.rp = 0
}
// Read at least the required number of bytes from the underlying io.Reader
readBytesCount, err := io.ReadAtLeast(r.r, r.buf[r.wp:], minReadCount)
readBytesCount, err := io.ReadAtLeast(r.r, (*r.buf)[r.wp:], minReadCount)
r.wp += readBytesCount
// fmt.Println("read", n)
if err != nil {
return nil, err
}
buf = r.buf[r.rp : r.rp+n : r.rp+n]
buf = (*r.buf)[r.rp : r.rp+n : r.rp+n]
r.rp += n
return buf, nil
}

@ -47,7 +47,16 @@ func (c *ArrayCodec) FormatSupported(format int16) bool {
}
func (c *ArrayCodec) PreferredFormat() int16 {
return c.ElementType.Codec.PreferredFormat()
// The binary format should always be preferred for arrays if it is supported. Usually, this will happen automatically
// because most types that support binary prefer it. However, text, json, and jsonb support binary but prefer the text
// format. This is because it is simpler for jsonb and PostgreSQL can be significantly faster using the text format
// for text-like data types than binary. However, arrays appear to always be faster in binary.
//
// https://www.postgresql.org/message-id/CAMovtNoHFod2jMAKQjjxv209PCTJx5Kc66anwWvX0mEiaXwgmA%40mail.gmail.com
if c.ElementType.Codec.FormatSupported(BinaryFormatCode) {
return BinaryFormatCode
}
return TextFormatCode
}
func (c *ArrayCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {
@ -60,7 +69,9 @@ func (c *ArrayCodec) PlanEncode(m *Map, oid uint32, format int16, value any) Enc
elementEncodePlan := m.PlanEncode(c.ElementType.OID, format, elementType)
if elementEncodePlan == nil {
return nil
if reflect.TypeOf(elementType) != nil {
return nil
}
}
switch format {
@ -301,7 +312,7 @@ func (c *ArrayCodec) decodeText(m *Map, arrayOID uint32, src []byte, array Array
for i, s := range uta.Elements {
elem := array.ScanIndex(i)
var elemSrc []byte
if s != "NULL" {
if s != "NULL" || uta.Quoted[i] {
elemSrc = []byte(s)
}

@ -910,3 +910,43 @@ func (a *anyMultiDimSliceArray) ScanIndexType() any {
}
return reflect.New(lowestSliceType.Elem()).Interface()
}
type anyArrayArrayReflect struct {
array reflect.Value
}
func (a anyArrayArrayReflect) Dimensions() []ArrayDimension {
return []ArrayDimension{{Length: int32(a.array.Len()), LowerBound: 1}}
}
func (a anyArrayArrayReflect) Index(i int) any {
return a.array.Index(i).Interface()
}
func (a anyArrayArrayReflect) IndexType() any {
return reflect.New(a.array.Type().Elem()).Elem().Interface()
}
func (a *anyArrayArrayReflect) SetDimensions(dimensions []ArrayDimension) error {
if dimensions == nil {
return fmt.Errorf("anyArrayArrayReflect: cannot scan NULL into %v", a.array.Type().String())
}
if len(dimensions) != 1 {
return fmt.Errorf("anyArrayArrayReflect: cannot scan multi-dimensional array into %v", a.array.Type().String())
}
if int(dimensions[0].Length) != a.array.Len() {
return fmt.Errorf("anyArrayArrayReflect: cannot scan array with length %v into %v", dimensions[0].Length, a.array.Type().String())
}
return nil
}
func (a *anyArrayArrayReflect) ScanIndex(i int) any {
return a.array.Index(i).Addr().Interface()
}
func (a *anyArrayArrayReflect) ScanIndexType() any {
return reflect.New(a.array.Type().Elem()).Interface()
}

@ -238,7 +238,7 @@ func decodeHexBytea(src []byte) ([]byte, error) {
}
func (c ByteaCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {
return codecDecodeToTextFormat(c, m, oid, format, src)
return c.DecodeValue(m, oid, format, src)
}
func (c ByteaCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {

@ -57,27 +57,7 @@ JSON Support
pgtype automatically marshals and unmarshals data from json and jsonb PostgreSQL types.
Array Support
ArrayCodec implements support for arrays. If pgtype supports type T then it can easily support []T by registering an
ArrayCodec for the appropriate PostgreSQL OID. In addition, Array[T] type can support multi-dimensional arrays.
Composite Support
CompositeCodec implements support for PostgreSQL composite types. Go structs can be scanned into if the public fields of
the struct are in the exact order and type of the PostgreSQL type or by implementing CompositeIndexScanner and
CompositeIndexGetter.
Enum Support
PostgreSQL enums can usually be treated as text. However, EnumCodec implements support for interning strings which can
reduce memory usage.
Array, Composite, and Enum Type Registration
Array, composite, and enum types can be easily registered from a pgx.Conn with the LoadType method.
Extending Existing Type Support
Extending Existing PostgreSQL Type Support
Generally, all Codecs will support interfaces that can be implemented to enable scanning and encoding. For example,
PointCodec can use any Go type that implements the PointScanner and PointValuer interfaces. So rather than use
@ -90,11 +70,58 @@ pgx support such as github.com/shopspring/decimal. These types can be registered
logic. See https://github.com/jackc/pgx-shopspring-decimal and https://github.com/jackc/pgx-gofrs-uuid for a example
integrations.
Entirely New Type Support
New PostgreSQL Type Support
pgtype uses the PostgreSQL OID to determine how to encode or decode a value. pgtype supports array, composite, domain,
and enum types. However, any type created in PostgreSQL with CREATE TYPE will receive a new OID. This means that the OID
of each new PostgreSQL type must be registered for pgtype to handle values of that type with the correct Codec.
The pgx.Conn LoadType method can return a *Type for array, composite, domain, and enum types by inspecting the database
metadata. This *Type can then be registered with Map.RegisterType.
For example, the following function could be called after a connection is established:
func RegisterDataTypes(ctx context.Context, conn *pgx.Conn) error {
dataTypeNames := []string{
"foo",
"_foo",
"bar",
"_bar",
}
for _, typeName := range dataTypeNames {
dataType, err := conn.LoadType(ctx, typeName)
if err != nil {
return err
}
conn.TypeMap().RegisterType(dataType)
}
return nil
}
A type cannot be registered unless all types it depends on are already registered. e.g. An array type cannot be
registered until its element type is registered.
ArrayCodec implements support for arrays. If pgtype supports type T then it can easily support []T by registering an
ArrayCodec for the appropriate PostgreSQL OID. In addition, Array[T] type can support multi-dimensional arrays.
CompositeCodec implements support for PostgreSQL composite types. Go structs can be scanned into if the public fields of
the struct are in the exact order and type of the PostgreSQL type or by implementing CompositeIndexScanner and
CompositeIndexGetter.
Domain types are treated as their underlying type if the underlying type and the domain type are registered.
PostgreSQL enums can usually be treated as text. However, EnumCodec implements support for interning strings which can
reduce memory usage.
While pgtype will often still work with unregistered types it is highly recommended that all types be registered due to
an improvement in performance and the elimination of certain edge cases.
If the PostgreSQL type is not already supported then an OID / Codec mapping can be registered with Map.RegisterType.
There is no difference between a Codec defined and registered by the application and a Codec built in to pgtype. See any
of the Codecs in pgtype for Codec examples and for examples of type registration.
If an entirely new PostgreSQL type (e.g. PostGIS types) is used then the application or a library can create a new
Codec. Then the OID / Codec mapping can be registered with Map.RegisterType. There is no difference between a Codec
defined and registered by the application and a Codec built in to pgtype. See any of the Codecs in pgtype for Codec
examples and for examples of type registration.
Encoding Unknown Types

@ -33,7 +33,7 @@ func (dst *Int2) ScanInt64(n Int8) error {
}
if n.Int64 < math.MinInt16 {
return fmt.Errorf("%d is greater than maximum value for Int2", n.Int64)
return fmt.Errorf("%d is less than minimum value for Int2", n.Int64)
}
if n.Int64 > math.MaxInt16 {
return fmt.Errorf("%d is greater than maximum value for Int2", n.Int64)
@ -593,7 +593,7 @@ func (dst *Int4) ScanInt64(n Int8) error {
}
if n.Int64 < math.MinInt32 {
return fmt.Errorf("%d is greater than maximum value for Int4", n.Int64)
return fmt.Errorf("%d is less than minimum value for Int4", n.Int64)
}
if n.Int64 > math.MaxInt32 {
return fmt.Errorf("%d is greater than maximum value for Int4", n.Int64)
@ -1164,7 +1164,7 @@ func (dst *Int8) ScanInt64(n Int8) error {
}
if n.Int64 < math.MinInt64 {
return fmt.Errorf("%d is greater than maximum value for Int8", n.Int64)
return fmt.Errorf("%d is less than minimum value for Int8", n.Int64)
}
if n.Int64 > math.MaxInt64 {
return fmt.Errorf("%d is greater than maximum value for Int8", n.Int64)

@ -3,6 +3,7 @@ package pgtype
import (
"database/sql/driver"
"encoding/binary"
"encoding/json"
"fmt"
"math"
"strconv"
@ -34,7 +35,7 @@ func (dst *Int<%= pg_byte_size %>) ScanInt64(n Int8) error {
}
if n.Int64 < math.MinInt<%= pg_bit_size %> {
return fmt.Errorf("%d is greater than maximum value for Int<%= pg_byte_size %>", n.Int64)
return fmt.Errorf("%d is less than minimum value for Int<%= pg_byte_size %>", n.Int64)
}
if n.Int64 > math.MaxInt<%= pg_bit_size %> {
return fmt.Errorf("%d is greater than maximum value for Int<%= pg_byte_size %>", n.Int64)

@ -1,6 +1,7 @@
package pgtype
import (
"database/sql"
"database/sql/driver"
"encoding/json"
"fmt"
@ -23,6 +24,12 @@ func (c JSONCodec) PlanEncode(m *Map, oid uint32, format int16, value any) Encod
return encodePlanJSONCodecEitherFormatString{}
case []byte:
return encodePlanJSONCodecEitherFormatByteSlice{}
// Cannot rely on driver.Valuer being handled later because anything can be marshalled.
//
// https://github.com/jackc/pgx/issues/1430
case driver.Valuer:
return &encodePlanDriverValuer{m: m, oid: oid, formatCode: format}
}
// Because anything can be marshalled the normal wrapping in Map.PlanScan doesn't get a chance to run. So try the
@ -82,10 +89,28 @@ func (JSONCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan
return scanPlanJSONToByteSlice{}
case BytesScanner:
return scanPlanBinaryBytesToBytesScanner{}
default:
return scanPlanJSONToJSONUnmarshal{}
// Cannot rely on sql.Scanner being handled later because scanPlanJSONToJSONUnmarshal will take precedence.
//
// https://github.com/jackc/pgx/issues/1418
case sql.Scanner:
return &scanPlanSQLScanner{formatCode: format}
}
// This is to fix **string scanning. It seems wrong to special case sql.Scanner and pointer to pointer, but it's not
// clear what a better solution would be.
//
// https://github.com/jackc/pgx/issues/1470
if wrapperPlan, nextDst, ok := TryPointerPointerScanPlan(target); ok {
if nextPlan := m.planScan(oid, format, nextDst); nextPlan != nil {
if _, failed := nextPlan.(*scanPlanFail); !failed {
wrapperPlan.SetNext(nextPlan)
return wrapperPlan
}
}
}
return scanPlanJSONToJSONUnmarshal{}
}
type scanPlanAnyToString struct{}

@ -240,10 +240,29 @@ func (n Numeric) MarshalJSON() ([]byte, error) {
return n.numberTextBytes(), nil
}
func (n *Numeric) UnmarshalJSON(src []byte) error {
if bytes.Compare(src, []byte(`null`)) == 0 {
*n = Numeric{}
return nil
}
if bytes.Compare(src, []byte(`"NaN"`)) == 0 {
*n = Numeric{NaN: true, Valid: true}
return nil
}
return scanPlanTextAnyToNumericScanner{}.Scan(src, n)
}
// numberString returns a string of the number. undefined if NaN, infinite, or NULL
func (n Numeric) numberTextBytes() []byte {
intStr := n.Int.String()
buf := &bytes.Buffer{}
if len(intStr) > 0 && intStr[:1] == "-" {
intStr = intStr[1:]
buf.WriteByte('-')
}
exp := int(n.Exp)
if exp > 0 {
buf.WriteString(intStr)

@ -192,7 +192,8 @@ type Map struct {
reflectTypeToType map[reflect.Type]*Type
memoizedScanPlans map[uint32]map[reflect.Type][2]ScanPlan
memoizedScanPlans map[uint32]map[reflect.Type][2]ScanPlan
memoizedEncodePlans map[uint32]map[reflect.Type][2]EncodePlan
// TryWrapEncodePlanFuncs is a slice of functions that will wrap a value that cannot be encoded by the Codec. Every
// time a wrapper is found the PlanEncode method will be recursively called with the new value. This allows several layers of wrappers
@ -214,7 +215,8 @@ func NewMap() *Map {
reflectTypeToName: make(map[reflect.Type]string),
oidToFormatCode: make(map[uint32]int16),
memoizedScanPlans: make(map[uint32]map[reflect.Type][2]ScanPlan),
memoizedScanPlans: make(map[uint32]map[reflect.Type][2]ScanPlan),
memoizedEncodePlans: make(map[uint32]map[reflect.Type][2]EncodePlan),
TryWrapEncodePlanFuncs: []TryWrapEncodePlanFunc{
TryWrapDerefPointerEncodePlan,
@ -223,6 +225,7 @@ func NewMap() *Map {
TryWrapStructEncodePlan,
TryWrapSliceEncodePlan,
TryWrapMultiDimSliceEncodePlan,
TryWrapArrayEncodePlan,
},
TryWrapScanPlanFuncs: []TryWrapScanPlanFunc{
@ -232,6 +235,7 @@ func NewMap() *Map {
TryWrapStructScanPlan,
TryWrapPtrSliceScanPlan,
TryWrapPtrMultiDimSliceScanPlan,
TryWrapPtrArrayScanPlan,
},
}
@ -420,6 +424,9 @@ func (m *Map) RegisterType(t *Type) {
for k := range m.memoizedScanPlans {
delete(m.memoizedScanPlans, k)
}
for k := range m.memoizedEncodePlans {
delete(m.memoizedEncodePlans, k)
}
}
// RegisterDefaultPgType registers a mapping of a Go type to a PostgreSQL type name. Typically the data type to be
@ -433,6 +440,9 @@ func (m *Map) RegisterDefaultPgType(value any, name string) {
for k := range m.memoizedScanPlans {
delete(m.memoizedScanPlans, k)
}
for k := range m.memoizedEncodePlans {
delete(m.memoizedEncodePlans, k)
}
}
func (m *Map) TypeForOID(oid uint32) (*Type, bool) {
@ -1018,7 +1028,7 @@ func TryWrapStructScanPlan(target any) (plan WrappedScanPlanNextSetter, nextValu
var targetElemValue reflect.Value
if targetValue.IsNil() {
targetElemValue = reflect.New(targetValue.Type().Elem())
targetElemValue = reflect.Zero(targetValue.Type().Elem())
} else {
targetElemValue = targetValue.Elem()
}
@ -1139,6 +1149,31 @@ func (plan *wrapPtrMultiDimSliceScanPlan) Scan(src []byte, target any) error {
return plan.next.Scan(src, &anyMultiDimSliceArray{slice: reflect.ValueOf(target).Elem()})
}
// TryWrapPtrArrayScanPlan tries to wrap a pointer to a single dimension array.
func TryWrapPtrArrayScanPlan(target any) (plan WrappedScanPlanNextSetter, nextValue any, ok bool) {
targetValue := reflect.ValueOf(target)
if targetValue.Kind() != reflect.Ptr {
return nil, nil, false
}
targetElemValue := targetValue.Elem()
if targetElemValue.Kind() == reflect.Array {
return &wrapPtrArrayReflectScanPlan{}, &anyArrayArrayReflect{array: targetElemValue}, true
}
return nil, nil, false
}
type wrapPtrArrayReflectScanPlan struct {
next ScanPlan
}
func (plan *wrapPtrArrayReflectScanPlan) SetNext(next ScanPlan) { plan.next = next }
func (plan *wrapPtrArrayReflectScanPlan) Scan(src []byte, target any) error {
return plan.next.Scan(src, &anyArrayArrayReflect{array: reflect.ValueOf(target).Elem()})
}
// PlanScan prepares a plan to scan a value into target.
func (m *Map) PlanScan(oid uint32, formatCode int16, target any) ScanPlan {
oidMemo := m.memoizedScanPlans[oid]
@ -1200,6 +1235,16 @@ func (m *Map) planScan(oid uint32, formatCode int16, target any) ScanPlan {
}
}
// This needs to happen before trying m.TryWrapScanPlanFuncs. Otherwise, a sql.Scanner would not get called if it was
// defined on a type that could be unwrapped such as `type myString string`.
//
// https://github.com/jackc/pgtype/issues/197
if dt == nil {
if _, ok := target.(sql.Scanner); ok {
return &scanPlanSQLScanner{formatCode: formatCode}
}
}
for _, f := range m.TryWrapScanPlanFuncs {
if wrapperPlan, nextDst, ok := f(target); ok {
if nextPlan := m.planScan(oid, formatCode, nextDst); nextPlan != nil {
@ -1221,10 +1266,6 @@ func (m *Map) planScan(oid uint32, formatCode int16, target any) ScanPlan {
}
}
if _, ok := target.(sql.Scanner); ok {
return &scanPlanSQLScanner{formatCode: formatCode}
}
return &scanPlanFail{m: m, oid: oid, formatCode: formatCode}
}
@ -1289,6 +1330,24 @@ 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 {
oidMemo := m.memoizedEncodePlans[oid]
if oidMemo == nil {
oidMemo = make(map[reflect.Type][2]EncodePlan)
m.memoizedEncodePlans[oid] = oidMemo
}
targetReflectType := reflect.TypeOf(value)
typeMemo := oidMemo[targetReflectType]
plan := typeMemo[format]
if plan == nil {
plan = m.planEncode(oid, format, value)
typeMemo[format] = plan
oidMemo[targetReflectType] = typeMemo
}
return plan
}
func (m *Map) planEncode(oid uint32, format int16, value any) EncodePlan {
if format == TextFormatCode {
switch value.(type) {
case string:
@ -1299,16 +1358,16 @@ func (m *Map) PlanEncode(oid uint32, format int16, value any) EncodePlan {
}
var dt *Type
if oid == 0 {
if dataType, ok := m.TypeForOID(oid); ok {
dt = dataType
} else {
// If no type for the OID was found, then either it is unknowable (e.g. the simple protocol) or it is an
// unregistered type. In either case try to find the type and OID that matches the value (e.g. a []byte would be
// registered to PostgreSQL bytea).
if dataType, ok := m.TypeForValue(value); ok {
dt = dataType
oid = dt.OID // Preserve assumed OID in case we are recursively called below.
}
} else {
if dataType, ok := m.TypeForOID(oid); ok {
dt = dataType
}
}
if dt != nil {
@ -1941,6 +2000,35 @@ func (plan *wrapMultiDimSliceEncodePlan) Encode(value any, buf []byte) (newBuf [
return plan.next.Encode(&w, buf)
}
func TryWrapArrayEncodePlan(value any) (plan WrappedEncodePlanNextSetter, nextValue any, ok bool) {
if _, ok := value.(driver.Valuer); ok {
return nil, nil, false
}
if valueType := reflect.TypeOf(value); valueType != nil && valueType.Kind() == reflect.Array {
w := anyArrayArrayReflect{
array: reflect.ValueOf(value),
}
return &wrapArrayEncodeReflectPlan{}, w, true
}
return nil, nil, false
}
type wrapArrayEncodeReflectPlan struct {
next EncodePlan
}
func (plan *wrapArrayEncodeReflectPlan) SetNext(next EncodePlan) { plan.next = next }
func (plan *wrapArrayEncodeReflectPlan) Encode(value any, buf []byte) (newBuf []byte, err error) {
w := anyArrayArrayReflect{
array: reflect.ValueOf(value),
}
return plan.next.Encode(w, buf)
}
func newEncodeError(value any, m *Map, oid uint32, formatCode int16, err error) error {
var format string
switch formatCode {

@ -22,7 +22,7 @@ type TIDValuer interface {
//
// When one does
//
// select ctid, * from some_table;
// select ctid, * from some_table;
//
// it is the data type of the ctid hidden system column.
//

@ -2,58 +2,58 @@
//
// A database/sql connection can be established through sql.Open.
//
// db, err := sql.Open("pgx", "postgres://pgx_md5:secret@localhost:5432/pgx_test?sslmode=disable")
// if err != nil {
// return err
// }
// db, err := sql.Open("pgx", "postgres://pgx_md5:secret@localhost:5432/pgx_test?sslmode=disable")
// if err != nil {
// return err
// }
//
// Or from a DSN string.
//
// db, err := sql.Open("pgx", "user=postgres password=secret host=localhost port=5432 database=pgx_test sslmode=disable")
// if err != nil {
// return err
// }
// db, err := sql.Open("pgx", "user=postgres password=secret host=localhost port=5432 database=pgx_test sslmode=disable")
// if err != nil {
// return err
// }
//
// Or a pgx.ConnConfig can be used to set configuration not accessible via connection string. In this case the
// pgx.ConnConfig must first be registered with the driver. This registration returns a connection string which is used
// with sql.Open.
//
// connConfig, _ := pgx.ParseConfig(os.Getenv("DATABASE_URL"))
// connConfig.Logger = myLogger
// connStr := stdlib.RegisterConnConfig(connConfig)
// db, _ := sql.Open("pgx", connStr)
// connConfig, _ := pgx.ParseConfig(os.Getenv("DATABASE_URL"))
// connConfig.Logger = myLogger
// connStr := stdlib.RegisterConnConfig(connConfig)
// db, _ := sql.Open("pgx", connStr)
//
// pgx uses standard PostgreSQL positional parameters in queries. e.g. $1, $2. It does not support named parameters.
//
// db.QueryRow("select * from users where id=$1", userID)
// db.QueryRow("select * from users where id=$1", userID)
//
// (*sql.Conn) Raw() can be used to get a *pgx.Conn from the standard database/sql.DB connection pool. This allows
// operations that use pgx specific functionality.
//
// // Given db is a *sql.DB
// conn, err := db.Conn(context.Background())
// if err != nil {
// // handle error from acquiring connection from DB pool
// }
// // Given db is a *sql.DB
// conn, err := db.Conn(context.Background())
// if err != nil {
// // handle error from acquiring connection from DB pool
// }
//
// err = conn.Raw(func(driverConn any) error {
// conn := driverConn.(*stdlib.Conn).Conn() // conn is a *pgx.Conn
// // Do pgx specific stuff with conn
// conn.CopyFrom(...)
// return nil
// })
// if err != nil {
// // handle error that occurred while using *pgx.Conn
// }
// err = conn.Raw(func(driverConn any) error {
// conn := driverConn.(*stdlib.Conn).Conn() // conn is a *pgx.Conn
// // Do pgx specific stuff with conn
// conn.CopyFrom(...)
// return nil
// })
// if err != nil {
// // handle error that occurred while using *pgx.Conn
// }
//
// PostgreSQL Specific Data Types
// # PostgreSQL Specific Data Types
//
// The pgtype package provides support for PostgreSQL specific types. *pgtype.Map.SQLScanner is an adapter that makes
// these types usable as a sql.Scanner.
//
// m := pgtype.NewMap()
// var a []int64
// err := db.QueryRow("select '{1,2,3}'::bigint[]").Scan(m.SQLScanner(&a))
// m := pgtype.NewMap()
// var a []int64
// err := db.QueryRow("select '{1,2,3}'::bigint[]").Scan(m.SQLScanner(&a))
package stdlib
import (
@ -66,6 +66,7 @@ import (
"math"
"math/rand"
"reflect"
"sort"
"strconv"
"strings"
"sync"
@ -85,7 +86,13 @@ func init() {
pgxDriver = &Driver{
configs: make(map[string]*pgx.ConnConfig),
}
sql.Register("pgx", pgxDriver)
drivers := sql.Drivers()
// if pgx driver was already registered by different pgx major version then we skip registration under the default name.
if i := sort.SearchStrings(sql.Drivers(), "pgx"); len(drivers) >= i || drivers[i] != "pgx" {
sql.Register("pgx", pgxDriver)
}
sql.Register("pgx/v5", pgxDriver)
databaseSQLResultFormats = pgx.QueryResultFormatsByOID{
pgtype.BoolOID: 1,
@ -140,7 +147,7 @@ func RandomizeHostOrderFunc(ctx context.Context, connConfig *pgx.ConnConfig) err
return nil
}
newFallbacks := append([]*pgconn.FallbackConfig{&pgconn.FallbackConfig{
newFallbacks := append([]*pgconn.FallbackConfig{{
Host: connConfig.Host,
Port: connConfig.Port,
TLSConfig: connConfig.TLSConfig,

@ -84,8 +84,8 @@ github.com/jackc/pgpassfile
# github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a
## explicit; go 1.14
github.com/jackc/pgservicefile
# github.com/jackc/pgx/v5 v5.2.0
## explicit; go 1.18
# github.com/jackc/pgx/v5 v5.3.0
## explicit; go 1.19
github.com/jackc/pgx/v5
github.com/jackc/pgx/v5/internal/anynil
github.com/jackc/pgx/v5/internal/iobufpool

Loading…
Cancel
Save