diff --git a/go.mod b/go.mod index c6ba1293..49a3c27a 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index 42036d35..880fffe6 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/vendor/github.com/jackc/pgx/v5/.gitignore b/vendor/github.com/jackc/pgx/v5/.gitignore index 348e014f..a2ebbe9c 100644 --- a/vendor/github.com/jackc/pgx/v5/.gitignore +++ b/vendor/github.com/jackc/pgx/v5/.gitignore @@ -23,3 +23,5 @@ _testmain.go .envrc /.testdb + +.DS_Store diff --git a/vendor/github.com/jackc/pgx/v5/CHANGELOG.md b/vendor/github.com/jackc/pgx/v5/CHANGELOG.md index 422c8074..6c749a87 100644 --- a/vendor/github.com/jackc/pgx/v5/CHANGELOG.md +++ b/vendor/github.com/jackc/pgx/v5/CHANGELOG.md @@ -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) diff --git a/vendor/github.com/jackc/pgx/v5/README.md b/vendor/github.com/jackc/pgx/v5/README.md index c21a182a..5c9032ff 100644 --- a/vendor/github.com/jackc/pgx/v5/README.md +++ b/vendor/github.com/jackc/pgx/v5/README.md @@ -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. diff --git a/vendor/github.com/jackc/pgx/v5/conn.go b/vendor/github.com/jackc/pgx/v5/conn.go index 4c8b59da..92b6f3e4 100644 --- a/vendor/github.com/jackc/pgx/v5/conn.go +++ b/vendor/github.com/jackc/pgx/v5/conn.go @@ -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, ) diff --git a/vendor/github.com/jackc/pgx/v5/copy_from.go b/vendor/github.com/jackc/pgx/v5/copy_from.go index 41acafdc..a2c227fd 100644 --- a/vendor/github.com/jackc/pgx/v5/copy_from.go +++ b/vendor/github.com/jackc/pgx/v5/copy_from.go @@ -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) diff --git a/vendor/github.com/jackc/pgx/v5/doc.go b/vendor/github.com/jackc/pgx/v5/doc.go index fb271874..0db8cbb1 100644 --- a/vendor/github.com/jackc/pgx/v5/doc.go +++ b/vendor/github.com/jackc/pgx/v5/doc.go @@ -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 diff --git a/vendor/github.com/jackc/pgx/v5/extended_query_builder.go b/vendor/github.com/jackc/pgx/v5/extended_query_builder.go index b0c0e02b..0bbdfbb5 100644 --- a/vendor/github.com/jackc/pgx/v5/extended_query_builder.go +++ b/vendor/github.com/jackc/pgx/v5/extended_query_builder.go @@ -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 { diff --git a/vendor/github.com/jackc/pgx/v5/internal/iobufpool/iobufpool.go b/vendor/github.com/jackc/pgx/v5/internal/iobufpool/iobufpool.go index 9e55c435..89e0c227 100644 --- a/vendor/github.com/jackc/pgx/v5/internal/iobufpool/iobufpool.go +++ b/vendor/github.com/jackc/pgx/v5/internal/iobufpool/iobufpool.go @@ -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 } diff --git a/vendor/github.com/jackc/pgx/v5/internal/nbconn/bufferqueue.go b/vendor/github.com/jackc/pgx/v5/internal/nbconn/bufferqueue.go index 138a9aa5..4bf25481 100644 --- a/vendor/github.com/jackc/pgx/v5/internal/nbconn/bufferqueue.go +++ b/vendor/github.com/jackc/pgx/v5/internal/nbconn/bufferqueue.go @@ -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 } diff --git a/vendor/github.com/jackc/pgx/v5/internal/nbconn/nbconn.go b/vendor/github.com/jackc/pgx/v5/internal/nbconn/nbconn.go index 99688518..61b610a4 100644 --- a/vendor/github.com/jackc/pgx/v5/internal/nbconn/nbconn.go +++ b/vendor/github.com/jackc/pgx/v5/internal/nbconn/nbconn.go @@ -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) diff --git a/vendor/github.com/jackc/pgx/v5/internal/nbconn/nbconn_fake_non_block.go b/vendor/github.com/jackc/pgx/v5/internal/nbconn/nbconn_fake_non_block.go index cf05df1c..4915c621 100644 --- a/vendor/github.com/jackc/pgx/v5/internal/nbconn/nbconn_fake_non_block.go +++ b/vendor/github.com/jackc/pgx/v5/internal/nbconn/nbconn_fake_non_block.go @@ -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) } diff --git a/vendor/github.com/jackc/pgx/v5/internal/nbconn/nbconn_real_non_block.go b/vendor/github.com/jackc/pgx/v5/internal/nbconn/nbconn_real_non_block.go index ee48d129..e93372f2 100644 --- a/vendor/github.com/jackc/pgx/v5/internal/nbconn/nbconn_real_non_block.go +++ b/vendor/github.com/jackc/pgx/v5/internal/nbconn/nbconn_real_non_block.go @@ -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 { diff --git a/vendor/github.com/jackc/pgx/v5/pgconn/config.go b/vendor/github.com/jackc/pgx/v5/pgconn/config.go index 6282f41b..24bf837c 100644 --- a/vendor/github.com/jackc/pgx/v5/pgconn/config.go +++ b/vendor/github.com/jackc/pgx/v5/pgconn/config.go @@ -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) } diff --git a/vendor/github.com/jackc/pgx/v5/pgconn/pgconn.go b/vendor/github.com/jackc/pgx/v5/pgconn/pgconn.go index 59fa35c6..8656ea51 100644 --- a/vendor/github.com/jackc/pgx/v5/pgconn/pgconn.go +++ b/vendor/github.com/jackc/pgx/v5/pgconn/pgconn.go @@ -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 diff --git a/vendor/github.com/jackc/pgx/v5/pgproto3/backend.go b/vendor/github.com/jackc/pgx/v5/pgproto3/backend.go index 09aeb7c8..6db77e4a 100644 --- a/vendor/github.com/jackc/pgx/v5/pgproto3/backend.go +++ b/vendor/github.com/jackc/pgx/v5/pgproto3/backend.go @@ -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. diff --git a/vendor/github.com/jackc/pgx/v5/pgproto3/chunkreader.go b/vendor/github.com/jackc/pgx/v5/pgproto3/chunkreader.go index 3c35d0b1..fc0fa61e 100644 --- a/vendor/github.com/jackc/pgx/v5/pgproto3/chunkreader.go +++ b/vendor/github.com/jackc/pgx/v5/pgproto3/chunkreader.go @@ -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 } diff --git a/vendor/github.com/jackc/pgx/v5/pgtype/array_codec.go b/vendor/github.com/jackc/pgx/v5/pgtype/array_codec.go index dae12039..c1863b32 100644 --- a/vendor/github.com/jackc/pgx/v5/pgtype/array_codec.go +++ b/vendor/github.com/jackc/pgx/v5/pgtype/array_codec.go @@ -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) } diff --git a/vendor/github.com/jackc/pgx/v5/pgtype/builtin_wrappers.go b/vendor/github.com/jackc/pgx/v5/pgtype/builtin_wrappers.go index e34dd578..8bf367c1 100644 --- a/vendor/github.com/jackc/pgx/v5/pgtype/builtin_wrappers.go +++ b/vendor/github.com/jackc/pgx/v5/pgtype/builtin_wrappers.go @@ -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() +} diff --git a/vendor/github.com/jackc/pgx/v5/pgtype/bytea.go b/vendor/github.com/jackc/pgx/v5/pgtype/bytea.go index 2e067672..a247705e 100644 --- a/vendor/github.com/jackc/pgx/v5/pgtype/bytea.go +++ b/vendor/github.com/jackc/pgx/v5/pgtype/bytea.go @@ -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) { diff --git a/vendor/github.com/jackc/pgx/v5/pgtype/doc.go b/vendor/github.com/jackc/pgx/v5/pgtype/doc.go index 834aa0b6..6612c896 100644 --- a/vendor/github.com/jackc/pgx/v5/pgtype/doc.go +++ b/vendor/github.com/jackc/pgx/v5/pgtype/doc.go @@ -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 diff --git a/vendor/github.com/jackc/pgx/v5/pgtype/int.go b/vendor/github.com/jackc/pgx/v5/pgtype/int.go index 1cda0ba3..90a20a26 100644 --- a/vendor/github.com/jackc/pgx/v5/pgtype/int.go +++ b/vendor/github.com/jackc/pgx/v5/pgtype/int.go @@ -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) diff --git a/vendor/github.com/jackc/pgx/v5/pgtype/int.go.erb b/vendor/github.com/jackc/pgx/v5/pgtype/int.go.erb index 572408e1..e0c8b7a3 100644 --- a/vendor/github.com/jackc/pgx/v5/pgtype/int.go.erb +++ b/vendor/github.com/jackc/pgx/v5/pgtype/int.go.erb @@ -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) diff --git a/vendor/github.com/jackc/pgx/v5/pgtype/json.go b/vendor/github.com/jackc/pgx/v5/pgtype/json.go index d0d98fc9..69861bf8 100644 --- a/vendor/github.com/jackc/pgx/v5/pgtype/json.go +++ b/vendor/github.com/jackc/pgx/v5/pgtype/json.go @@ -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{} diff --git a/vendor/github.com/jackc/pgx/v5/pgtype/numeric.go b/vendor/github.com/jackc/pgx/v5/pgtype/numeric.go index a5f4ed3a..376c03fe 100644 --- a/vendor/github.com/jackc/pgx/v5/pgtype/numeric.go +++ b/vendor/github.com/jackc/pgx/v5/pgtype/numeric.go @@ -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) diff --git a/vendor/github.com/jackc/pgx/v5/pgtype/pgtype.go b/vendor/github.com/jackc/pgx/v5/pgtype/pgtype.go index 2aa96e68..7b300709 100644 --- a/vendor/github.com/jackc/pgx/v5/pgtype/pgtype.go +++ b/vendor/github.com/jackc/pgx/v5/pgtype/pgtype.go @@ -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 { diff --git a/vendor/github.com/jackc/pgx/v5/pgtype/tid.go b/vendor/github.com/jackc/pgx/v5/pgtype/tid.go index cb4a9ec4..5839e874 100644 --- a/vendor/github.com/jackc/pgx/v5/pgtype/tid.go +++ b/vendor/github.com/jackc/pgx/v5/pgtype/tid.go @@ -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. // diff --git a/vendor/github.com/jackc/pgx/v5/stdlib/sql.go b/vendor/github.com/jackc/pgx/v5/stdlib/sql.go index fc0b0239..ddb15ff6 100644 --- a/vendor/github.com/jackc/pgx/v5/stdlib/sql.go +++ b/vendor/github.com/jackc/pgx/v5/stdlib/sql.go @@ -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, diff --git a/vendor/modules.txt b/vendor/modules.txt index 8620f6f3..14f57e0f 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -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