[chore]: Bump github.com/jackc/pgx/v5 from 5.4.2 to 5.4.3 (#2112)

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
dependabot[bot] 2023-08-16 16:10:13 +01:00 committed by GitHub
parent 5a4ceebcbd
commit e70629e856
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 191 additions and 190 deletions

2
go.mod
View file

@ -33,7 +33,7 @@ require (
github.com/gorilla/websocket v1.5.0 github.com/gorilla/websocket v1.5.0
github.com/h2non/filetype v1.1.3 github.com/h2non/filetype v1.1.3
github.com/jackc/pgconn v1.14.1 github.com/jackc/pgconn v1.14.1
github.com/jackc/pgx/v5 v5.4.2 github.com/jackc/pgx/v5 v5.4.3
github.com/microcosm-cc/bluemonday v1.0.25 github.com/microcosm-cc/bluemonday v1.0.25
github.com/miekg/dns v1.1.55 github.com/miekg/dns v1.1.55
github.com/minio/minio-go/v7 v7.0.61 github.com/minio/minio-go/v7 v7.0.61

4
go.sum
View file

@ -391,8 +391,8 @@ github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrU
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
github.com/jackc/pgx/v5 v5.4.2 h1:u1gmGDwbdRUZiwisBm/Ky2M14uQyUP65bG8+20nnyrg= github.com/jackc/pgx/v5 v5.4.3 h1:cxFyXhxlvAifxnkKKdlxv8XqUf59tDlYjnV5YYfsJJY=
github.com/jackc/pgx/v5 v5.4.2/go.mod h1:q6iHT8uDNXWiFNOlRqJzBTaSH3+2xCXkokxHZC5qWFY= github.com/jackc/pgx/v5 v5.4.3/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 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 v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=

View file

@ -1,3 +1,15 @@
# 5.4.3 (August 5, 2023)
* Fix: QCharArrayOID was defined with the wrong OID (Christoph Engelbert)
* Fix: connect_timeout for sslmode=allow|prefer (smaher-edb)
* Fix: pgxpool: background health check cannot overflow pool
* Fix: Check for nil in defer when sending batch (recover properly from panic)
* Fix: json scan of non-string pointer to pointer
* Fix: zeronull.Timestamptz should use pgtype.Timestamptz
* Fix: NewConnsCount was not correctly counting connections created by Acquire directly. (James Hartig)
* RowTo(AddrOf)StructByPos ignores fields with "-" db tag
* Optimization: improve text format numeric parsing (horpto)
# 5.4.2 (July 11, 2023) # 5.4.2 (July 11, 2023)
* Fix: RowScanner errors are fatal to Rows * Fix: RowScanner errors are fatal to Rows

View file

@ -1,5 +1,5 @@
[![Go Reference](https://pkg.go.dev/badge/github.com/jackc/pgx/v5.svg)](https://pkg.go.dev/github.com/jackc/pgx/v5) [![Go Reference](https://pkg.go.dev/badge/github.com/jackc/pgx/v5.svg)](https://pkg.go.dev/github.com/jackc/pgx/v5)
![Build Status](https://github.com/jackc/pgx/actions/workflows/ci.yml/badge.svg) [![Build Status](https://github.com/jackc/pgx/actions/workflows/ci.yml/badge.svg)](https://github.com/jackc/pgx/actions/workflows/ci.yml)
# pgx - PostgreSQL Driver and Toolkit # pgx - PostgreSQL Driver and Toolkit

View file

@ -194,7 +194,7 @@ func ParseConfigWithOptions(connString string, options ParseConfigOptions) (*Con
return connConfig, nil return connConfig, nil
} }
// ParseConfig creates a ConnConfig from a connection string. ParseConfig handles all options that pgconn.ParseConfig // ParseConfig creates a ConnConfig from a connection string. ParseConfig handles all options that [pgconn.ParseConfig]
// does. In addition, it accepts the following options: // does. In addition, it accepts the following options:
// //
// - default_query_exec_mode. // - default_query_exec_mode.
@ -507,7 +507,7 @@ func (c *Conn) execSimpleProtocol(ctx context.Context, sql string, arguments []a
mrr := c.pgConn.Exec(ctx, sql) mrr := c.pgConn.Exec(ctx, sql)
for mrr.NextResult() { for mrr.NextResult() {
commandTag, err = mrr.ResultReader().Close() commandTag, _ = mrr.ResultReader().Close()
} }
err = mrr.Close() err = mrr.Close()
return commandTag, err return commandTag, err
@ -1064,7 +1064,7 @@ func (c *Conn) sendBatchQueryExecModeDescribeExec(ctx context.Context, b *Batch)
func (c *Conn) sendBatchExtendedWithDescription(ctx context.Context, b *Batch, distinctNewQueries []*pgconn.StatementDescription, sdCache stmtcache.Cache) (pbr *pipelineBatchResults) { func (c *Conn) sendBatchExtendedWithDescription(ctx context.Context, b *Batch, distinctNewQueries []*pgconn.StatementDescription, sdCache stmtcache.Cache) (pbr *pipelineBatchResults) {
pipeline := c.pgConn.StartPipeline(context.Background()) pipeline := c.pgConn.StartPipeline(context.Background())
defer func() { defer func() {
if pbr.err != nil { if pbr != nil && pbr.err != nil {
pipeline.Close() pipeline.Close()
} }
}() }()

View file

@ -7,17 +7,17 @@
Establishing a Connection Establishing a Connection
The primary way of establishing a connection is with `pgx.Connect`. The primary way of establishing a connection is with [pgx.Connect]:
conn, err := pgx.Connect(context.Background(), os.Getenv("DATABASE_URL")) conn, err := pgx.Connect(context.Background(), os.Getenv("DATABASE_URL"))
The database connection string can be in URL or DSN format. Both PostgreSQL settings and pgx settings can be specified The database connection string can be in URL or DSN format. Both PostgreSQL settings and pgx settings can be specified
here. In addition, a config struct can be created by `ParseConfig` and modified before establishing the connection with here. In addition, a config struct can be created by [ParseConfig] and modified before establishing the connection with
`ConnectConfig` to configure settings such as tracing that cannot be configured with a connection string. [ConnectConfig] to configure settings such as tracing that cannot be configured with a connection string.
Connection Pool Connection Pool
`*pgx.Conn` represents a single connection to the database and is not concurrency safe. Use package [*pgx.Conn] represents a single connection to the database and is not concurrency safe. Use package
github.com/jackc/pgx/v5/pgxpool for a concurrency safe connection pool. github.com/jackc/pgx/v5/pgxpool for a concurrency safe connection pool.
Query Interface Query Interface

View file

@ -26,7 +26,7 @@
type ValidateConnectFunc func(ctx context.Context, pgconn *PgConn) error type ValidateConnectFunc func(ctx context.Context, pgconn *PgConn) error
type GetSSLPasswordFunc func(ctx context.Context) string type GetSSLPasswordFunc func(ctx context.Context) string
// Config is the settings used to establish a connection to a PostgreSQL server. It must be created by ParseConfig. A // Config is the settings used to establish a connection to a PostgreSQL server. It must be created by [ParseConfig]. A
// manually initialized Config will cause ConnectConfig to panic. // manually initialized Config will cause ConnectConfig to panic.
type Config struct { type Config struct {
Host string // host (e.g. localhost) or absolute path to unix domain socket directory (e.g. /private/tmp) Host string // host (e.g. localhost) or absolute path to unix domain socket directory (e.g. /private/tmp)

View file

@ -97,7 +97,7 @@ type PgConn struct {
} }
// Connect establishes a connection to a PostgreSQL server using the environment and connString (in URL or DSN format) // Connect establishes a connection to a PostgreSQL server using the environment and connString (in URL or DSN format)
// to provide configuration. See documentation for ParseConfig for details. ctx can be used to cancel a connect attempt. // to provide configuration. See documentation for [ParseConfig] for details. ctx can be used to cancel a connect attempt.
func Connect(ctx context.Context, connString string) (*PgConn, error) { func Connect(ctx context.Context, connString string) (*PgConn, error) {
config, err := ParseConfig(connString) config, err := ParseConfig(connString)
if err != nil { if err != nil {
@ -108,7 +108,7 @@ func Connect(ctx context.Context, connString string) (*PgConn, error) {
} }
// Connect establishes a connection to a PostgreSQL server using the environment and connString (in URL or DSN format) // Connect establishes a connection to a PostgreSQL server using the environment and connString (in URL or DSN format)
// and ParseConfigOptions to provide additional configuration. See documentation for ParseConfig for details. ctx can be // and ParseConfigOptions to provide additional configuration. See documentation for [ParseConfig] for details. ctx can be
// used to cancel a connect attempt. // used to cancel a connect attempt.
func ConnectWithOptions(ctx context.Context, connString string, parseConfigOptions ParseConfigOptions) (*PgConn, error) { func ConnectWithOptions(ctx context.Context, connString string, parseConfigOptions ParseConfigOptions) (*PgConn, error) {
config, err := ParseConfigWithOptions(connString, parseConfigOptions) config, err := ParseConfigWithOptions(connString, parseConfigOptions)
@ -120,7 +120,7 @@ func ConnectWithOptions(ctx context.Context, connString string, parseConfigOptio
} }
// Connect establishes a connection to a PostgreSQL server using config. config must have been constructed with // Connect establishes a connection to a PostgreSQL server using config. config must have been constructed with
// ParseConfig. ctx can be used to cancel a connect attempt. // [ParseConfig]. ctx can be used to cancel a connect attempt.
// //
// If config.Fallbacks are present they will sequentially be tried in case of error establishing network connection. An // If config.Fallbacks are present they will sequentially be tried in case of error establishing network connection. An
// authentication error will terminate the chain of attempts (like libpq: // authentication error will terminate the chain of attempts (like libpq:
@ -154,12 +154,15 @@ func ConnectConfig(octx context.Context, config *Config) (pgConn *PgConn, err er
foundBestServer := false foundBestServer := false
var fallbackConfig *FallbackConfig var fallbackConfig *FallbackConfig
for _, fc := range fallbackConfigs { for i, fc := range fallbackConfigs {
// ConnectTimeout restricts the whole connection process. // ConnectTimeout restricts the whole connection process.
if config.ConnectTimeout != 0 { if config.ConnectTimeout != 0 {
// create new context first time or when previous host was different
if i == 0 || (fallbackConfigs[i].Host != fallbackConfigs[i-1].Host) {
var cancel context.CancelFunc var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(octx, config.ConnectTimeout) ctx, cancel = context.WithTimeout(octx, config.ConnectTimeout)
defer cancel() defer cancel()
}
} else { } else {
ctx = octx ctx = octx
} }

View file

@ -6,15 +6,18 @@
"io" "io"
"strconv" "strconv"
"strings" "strings"
"sync"
"time" "time"
) )
// tracer traces the messages send to and from a Backend or Frontend. The format it produces roughly mimics the // tracer traces the messages send to and from a Backend or Frontend. The format it produces roughly mimics the
// format produced by the libpq C function PQtrace. // format produced by the libpq C function PQtrace.
type tracer struct { type tracer struct {
TracerOptions
mux sync.Mutex
w io.Writer w io.Writer
buf *bytes.Buffer buf *bytes.Buffer
TracerOptions
} }
// TracerOptions controls tracing behavior. It is roughly equivalent to the libpq function PQsetTraceFlags. // TracerOptions controls tracing behavior. It is roughly equivalent to the libpq function PQsetTraceFlags.
@ -119,63 +122,54 @@ func (t *tracer) traceMessage(sender byte, encodedLen int32, msg Message) {
case *Terminate: case *Terminate:
t.traceTerminate(sender, encodedLen, msg) t.traceTerminate(sender, encodedLen, msg)
default: default:
t.beginTrace(sender, encodedLen, "Unknown") t.writeTrace(sender, encodedLen, "Unknown", nil)
t.finishTrace()
} }
} }
func (t *tracer) traceAuthenticationCleartextPassword(sender byte, encodedLen int32, msg *AuthenticationCleartextPassword) { func (t *tracer) traceAuthenticationCleartextPassword(sender byte, encodedLen int32, msg *AuthenticationCleartextPassword) {
t.beginTrace(sender, encodedLen, "AuthenticationCleartextPassword") t.writeTrace(sender, encodedLen, "AuthenticationCleartextPassword", nil)
t.finishTrace()
} }
func (t *tracer) traceAuthenticationGSS(sender byte, encodedLen int32, msg *AuthenticationGSS) { func (t *tracer) traceAuthenticationGSS(sender byte, encodedLen int32, msg *AuthenticationGSS) {
t.beginTrace(sender, encodedLen, "AuthenticationGSS") t.writeTrace(sender, encodedLen, "AuthenticationGSS", nil)
t.finishTrace()
} }
func (t *tracer) traceAuthenticationGSSContinue(sender byte, encodedLen int32, msg *AuthenticationGSSContinue) { func (t *tracer) traceAuthenticationGSSContinue(sender byte, encodedLen int32, msg *AuthenticationGSSContinue) {
t.beginTrace(sender, encodedLen, "AuthenticationGSSContinue") t.writeTrace(sender, encodedLen, "AuthenticationGSSContinue", nil)
t.finishTrace()
} }
func (t *tracer) traceAuthenticationMD5Password(sender byte, encodedLen int32, msg *AuthenticationMD5Password) { func (t *tracer) traceAuthenticationMD5Password(sender byte, encodedLen int32, msg *AuthenticationMD5Password) {
t.beginTrace(sender, encodedLen, "AuthenticationMD5Password") t.writeTrace(sender, encodedLen, "AuthenticationMD5Password", nil)
t.finishTrace()
} }
func (t *tracer) traceAuthenticationOk(sender byte, encodedLen int32, msg *AuthenticationOk) { func (t *tracer) traceAuthenticationOk(sender byte, encodedLen int32, msg *AuthenticationOk) {
t.beginTrace(sender, encodedLen, "AuthenticationOk") t.writeTrace(sender, encodedLen, "AuthenticationOk", nil)
t.finishTrace()
} }
func (t *tracer) traceAuthenticationSASL(sender byte, encodedLen int32, msg *AuthenticationSASL) { func (t *tracer) traceAuthenticationSASL(sender byte, encodedLen int32, msg *AuthenticationSASL) {
t.beginTrace(sender, encodedLen, "AuthenticationSASL") t.writeTrace(sender, encodedLen, "AuthenticationSASL", nil)
t.finishTrace()
} }
func (t *tracer) traceAuthenticationSASLContinue(sender byte, encodedLen int32, msg *AuthenticationSASLContinue) { func (t *tracer) traceAuthenticationSASLContinue(sender byte, encodedLen int32, msg *AuthenticationSASLContinue) {
t.beginTrace(sender, encodedLen, "AuthenticationSASLContinue") t.writeTrace(sender, encodedLen, "AuthenticationSASLContinue", nil)
t.finishTrace()
} }
func (t *tracer) traceAuthenticationSASLFinal(sender byte, encodedLen int32, msg *AuthenticationSASLFinal) { func (t *tracer) traceAuthenticationSASLFinal(sender byte, encodedLen int32, msg *AuthenticationSASLFinal) {
t.beginTrace(sender, encodedLen, "AuthenticationSASLFinal") t.writeTrace(sender, encodedLen, "AuthenticationSASLFinal", nil)
t.finishTrace()
} }
func (t *tracer) traceBackendKeyData(sender byte, encodedLen int32, msg *BackendKeyData) { func (t *tracer) traceBackendKeyData(sender byte, encodedLen int32, msg *BackendKeyData) {
t.beginTrace(sender, encodedLen, "BackendKeyData") t.writeTrace(sender, encodedLen, "BackendKeyData", func() {
if t.RegressMode { if t.RegressMode {
t.buf.WriteString("\t NNNN NNNN") t.buf.WriteString("\t NNNN NNNN")
} else { } else {
fmt.Fprintf(t.buf, "\t %d %d", msg.ProcessID, msg.SecretKey) fmt.Fprintf(t.buf, "\t %d %d", msg.ProcessID, msg.SecretKey)
} }
t.finishTrace() })
} }
func (t *tracer) traceBind(sender byte, encodedLen int32, msg *Bind) { func (t *tracer) traceBind(sender byte, encodedLen int32, msg *Bind) {
t.beginTrace(sender, encodedLen, "Bind") t.writeTrace(sender, encodedLen, "Bind", func() {
fmt.Fprintf(t.buf, "\t %s %s %d", traceDoubleQuotedString([]byte(msg.DestinationPortal)), traceDoubleQuotedString([]byte(msg.PreparedStatement)), len(msg.ParameterFormatCodes)) fmt.Fprintf(t.buf, "\t %s %s %d", traceDoubleQuotedString([]byte(msg.DestinationPortal)), traceDoubleQuotedString([]byte(msg.PreparedStatement)), len(msg.ParameterFormatCodes))
for _, fc := range msg.ParameterFormatCodes { for _, fc := range msg.ParameterFormatCodes {
fmt.Fprintf(t.buf, " %d", fc) fmt.Fprintf(t.buf, " %d", fc)
@ -188,68 +182,59 @@ func (t *tracer) traceBind(sender byte, encodedLen int32, msg *Bind) {
for _, fc := range msg.ResultFormatCodes { for _, fc := range msg.ResultFormatCodes {
fmt.Fprintf(t.buf, " %d", fc) fmt.Fprintf(t.buf, " %d", fc)
} }
t.finishTrace() })
} }
func (t *tracer) traceBindComplete(sender byte, encodedLen int32, msg *BindComplete) { func (t *tracer) traceBindComplete(sender byte, encodedLen int32, msg *BindComplete) {
t.beginTrace(sender, encodedLen, "BindComplete") t.writeTrace(sender, encodedLen, "BindComplete", nil)
t.finishTrace()
} }
func (t *tracer) traceCancelRequest(sender byte, encodedLen int32, msg *CancelRequest) { func (t *tracer) traceCancelRequest(sender byte, encodedLen int32, msg *CancelRequest) {
t.beginTrace(sender, encodedLen, "CancelRequest") t.writeTrace(sender, encodedLen, "CancelRequest", nil)
t.finishTrace()
} }
func (t *tracer) traceClose(sender byte, encodedLen int32, msg *Close) { func (t *tracer) traceClose(sender byte, encodedLen int32, msg *Close) {
t.beginTrace(sender, encodedLen, "Close") t.writeTrace(sender, encodedLen, "Close", nil)
t.finishTrace()
} }
func (t *tracer) traceCloseComplete(sender byte, encodedLen int32, msg *CloseComplete) { func (t *tracer) traceCloseComplete(sender byte, encodedLen int32, msg *CloseComplete) {
t.beginTrace(sender, encodedLen, "CloseComplete") t.writeTrace(sender, encodedLen, "CloseComplete", nil)
t.finishTrace()
} }
func (t *tracer) traceCommandComplete(sender byte, encodedLen int32, msg *CommandComplete) { func (t *tracer) traceCommandComplete(sender byte, encodedLen int32, msg *CommandComplete) {
t.beginTrace(sender, encodedLen, "CommandComplete") t.writeTrace(sender, encodedLen, "CommandComplete", func() {
fmt.Fprintf(t.buf, "\t %s", traceDoubleQuotedString(msg.CommandTag)) fmt.Fprintf(t.buf, "\t %s", traceDoubleQuotedString(msg.CommandTag))
t.finishTrace() })
} }
func (t *tracer) traceCopyBothResponse(sender byte, encodedLen int32, msg *CopyBothResponse) { func (t *tracer) traceCopyBothResponse(sender byte, encodedLen int32, msg *CopyBothResponse) {
t.beginTrace(sender, encodedLen, "CopyBothResponse") t.writeTrace(sender, encodedLen, "CopyBothResponse", nil)
t.finishTrace()
} }
func (t *tracer) traceCopyData(sender byte, encodedLen int32, msg *CopyData) { func (t *tracer) traceCopyData(sender byte, encodedLen int32, msg *CopyData) {
t.beginTrace(sender, encodedLen, "CopyData") t.writeTrace(sender, encodedLen, "CopyData", nil)
t.finishTrace()
} }
func (t *tracer) traceCopyDone(sender byte, encodedLen int32, msg *CopyDone) { func (t *tracer) traceCopyDone(sender byte, encodedLen int32, msg *CopyDone) {
t.beginTrace(sender, encodedLen, "CopyDone") t.writeTrace(sender, encodedLen, "CopyDone", nil)
t.finishTrace()
} }
func (t *tracer) traceCopyFail(sender byte, encodedLen int32, msg *CopyFail) { func (t *tracer) traceCopyFail(sender byte, encodedLen int32, msg *CopyFail) {
t.beginTrace(sender, encodedLen, "CopyFail") t.writeTrace(sender, encodedLen, "CopyFail", func() {
fmt.Fprintf(t.buf, "\t %s", traceDoubleQuotedString([]byte(msg.Message))) fmt.Fprintf(t.buf, "\t %s", traceDoubleQuotedString([]byte(msg.Message)))
t.finishTrace() })
} }
func (t *tracer) traceCopyInResponse(sender byte, encodedLen int32, msg *CopyInResponse) { func (t *tracer) traceCopyInResponse(sender byte, encodedLen int32, msg *CopyInResponse) {
t.beginTrace(sender, encodedLen, "CopyInResponse") t.writeTrace(sender, encodedLen, "CopyInResponse", nil)
t.finishTrace()
} }
func (t *tracer) traceCopyOutResponse(sender byte, encodedLen int32, msg *CopyOutResponse) { func (t *tracer) traceCopyOutResponse(sender byte, encodedLen int32, msg *CopyOutResponse) {
t.beginTrace(sender, encodedLen, "CopyOutResponse") t.writeTrace(sender, encodedLen, "CopyOutResponse", nil)
t.finishTrace()
} }
func (t *tracer) traceDataRow(sender byte, encodedLen int32, msg *DataRow) { func (t *tracer) traceDataRow(sender byte, encodedLen int32, msg *DataRow) {
t.beginTrace(sender, encodedLen, "DataRow") t.writeTrace(sender, encodedLen, "DataRow", func() {
fmt.Fprintf(t.buf, "\t %d", len(msg.Values)) fmt.Fprintf(t.buf, "\t %d", len(msg.Values))
for _, v := range msg.Values { for _, v := range msg.Values {
if v == nil { if v == nil {
@ -258,139 +243,134 @@ func (t *tracer) traceDataRow(sender byte, encodedLen int32, msg *DataRow) {
fmt.Fprintf(t.buf, " %d %s", len(v), traceSingleQuotedString(v)) fmt.Fprintf(t.buf, " %d %s", len(v), traceSingleQuotedString(v))
} }
} }
t.finishTrace() })
} }
func (t *tracer) traceDescribe(sender byte, encodedLen int32, msg *Describe) { func (t *tracer) traceDescribe(sender byte, encodedLen int32, msg *Describe) {
t.beginTrace(sender, encodedLen, "Describe") t.writeTrace(sender, encodedLen, "Describe", func() {
fmt.Fprintf(t.buf, "\t %c %s", msg.ObjectType, traceDoubleQuotedString([]byte(msg.Name))) fmt.Fprintf(t.buf, "\t %c %s", msg.ObjectType, traceDoubleQuotedString([]byte(msg.Name)))
t.finishTrace() })
} }
func (t *tracer) traceEmptyQueryResponse(sender byte, encodedLen int32, msg *EmptyQueryResponse) { func (t *tracer) traceEmptyQueryResponse(sender byte, encodedLen int32, msg *EmptyQueryResponse) {
t.beginTrace(sender, encodedLen, "EmptyQueryResponse") t.writeTrace(sender, encodedLen, "EmptyQueryResponse", nil)
t.finishTrace()
} }
func (t *tracer) traceErrorResponse(sender byte, encodedLen int32, msg *ErrorResponse) { func (t *tracer) traceErrorResponse(sender byte, encodedLen int32, msg *ErrorResponse) {
t.beginTrace(sender, encodedLen, "ErrorResponse") t.writeTrace(sender, encodedLen, "ErrorResponse", nil)
t.finishTrace()
} }
func (t *tracer) TraceQueryute(sender byte, encodedLen int32, msg *Execute) { func (t *tracer) TraceQueryute(sender byte, encodedLen int32, msg *Execute) {
t.beginTrace(sender, encodedLen, "Execute") t.writeTrace(sender, encodedLen, "Execute", func() {
fmt.Fprintf(t.buf, "\t %s %d", traceDoubleQuotedString([]byte(msg.Portal)), msg.MaxRows) fmt.Fprintf(t.buf, "\t %s %d", traceDoubleQuotedString([]byte(msg.Portal)), msg.MaxRows)
t.finishTrace() })
} }
func (t *tracer) traceFlush(sender byte, encodedLen int32, msg *Flush) { func (t *tracer) traceFlush(sender byte, encodedLen int32, msg *Flush) {
t.beginTrace(sender, encodedLen, "Flush") t.writeTrace(sender, encodedLen, "Flush", nil)
t.finishTrace()
} }
func (t *tracer) traceFunctionCall(sender byte, encodedLen int32, msg *FunctionCall) { func (t *tracer) traceFunctionCall(sender byte, encodedLen int32, msg *FunctionCall) {
t.beginTrace(sender, encodedLen, "FunctionCall") t.writeTrace(sender, encodedLen, "FunctionCall", nil)
t.finishTrace()
} }
func (t *tracer) traceFunctionCallResponse(sender byte, encodedLen int32, msg *FunctionCallResponse) { func (t *tracer) traceFunctionCallResponse(sender byte, encodedLen int32, msg *FunctionCallResponse) {
t.beginTrace(sender, encodedLen, "FunctionCallResponse") t.writeTrace(sender, encodedLen, "FunctionCallResponse", nil)
t.finishTrace()
} }
func (t *tracer) traceGSSEncRequest(sender byte, encodedLen int32, msg *GSSEncRequest) { func (t *tracer) traceGSSEncRequest(sender byte, encodedLen int32, msg *GSSEncRequest) {
t.beginTrace(sender, encodedLen, "GSSEncRequest") t.writeTrace(sender, encodedLen, "GSSEncRequest", nil)
t.finishTrace()
} }
func (t *tracer) traceNoData(sender byte, encodedLen int32, msg *NoData) { func (t *tracer) traceNoData(sender byte, encodedLen int32, msg *NoData) {
t.beginTrace(sender, encodedLen, "NoData") t.writeTrace(sender, encodedLen, "NoData", nil)
t.finishTrace()
} }
func (t *tracer) traceNoticeResponse(sender byte, encodedLen int32, msg *NoticeResponse) { func (t *tracer) traceNoticeResponse(sender byte, encodedLen int32, msg *NoticeResponse) {
t.beginTrace(sender, encodedLen, "NoticeResponse") t.writeTrace(sender, encodedLen, "NoticeResponse", nil)
t.finishTrace()
} }
func (t *tracer) traceNotificationResponse(sender byte, encodedLen int32, msg *NotificationResponse) { func (t *tracer) traceNotificationResponse(sender byte, encodedLen int32, msg *NotificationResponse) {
t.beginTrace(sender, encodedLen, "NotificationResponse") t.writeTrace(sender, encodedLen, "NotificationResponse", func() {
fmt.Fprintf(t.buf, "\t %d %s %s", msg.PID, traceDoubleQuotedString([]byte(msg.Channel)), traceDoubleQuotedString([]byte(msg.Payload))) fmt.Fprintf(t.buf, "\t %d %s %s", msg.PID, traceDoubleQuotedString([]byte(msg.Channel)), traceDoubleQuotedString([]byte(msg.Payload)))
t.finishTrace() })
} }
func (t *tracer) traceParameterDescription(sender byte, encodedLen int32, msg *ParameterDescription) { func (t *tracer) traceParameterDescription(sender byte, encodedLen int32, msg *ParameterDescription) {
t.beginTrace(sender, encodedLen, "ParameterDescription") t.writeTrace(sender, encodedLen, "ParameterDescription", nil)
t.finishTrace()
} }
func (t *tracer) traceParameterStatus(sender byte, encodedLen int32, msg *ParameterStatus) { func (t *tracer) traceParameterStatus(sender byte, encodedLen int32, msg *ParameterStatus) {
t.beginTrace(sender, encodedLen, "ParameterStatus") t.writeTrace(sender, encodedLen, "ParameterStatus", func() {
fmt.Fprintf(t.buf, "\t %s %s", traceDoubleQuotedString([]byte(msg.Name)), traceDoubleQuotedString([]byte(msg.Value))) fmt.Fprintf(t.buf, "\t %s %s", traceDoubleQuotedString([]byte(msg.Name)), traceDoubleQuotedString([]byte(msg.Value)))
t.finishTrace() })
} }
func (t *tracer) traceParse(sender byte, encodedLen int32, msg *Parse) { func (t *tracer) traceParse(sender byte, encodedLen int32, msg *Parse) {
t.beginTrace(sender, encodedLen, "Parse") t.writeTrace(sender, encodedLen, "Parse", func() {
fmt.Fprintf(t.buf, "\t %s %s %d", traceDoubleQuotedString([]byte(msg.Name)), traceDoubleQuotedString([]byte(msg.Query)), len(msg.ParameterOIDs)) fmt.Fprintf(t.buf, "\t %s %s %d", traceDoubleQuotedString([]byte(msg.Name)), traceDoubleQuotedString([]byte(msg.Query)), len(msg.ParameterOIDs))
for _, oid := range msg.ParameterOIDs { for _, oid := range msg.ParameterOIDs {
fmt.Fprintf(t.buf, " %d", oid) fmt.Fprintf(t.buf, " %d", oid)
} }
t.finishTrace() })
} }
func (t *tracer) traceParseComplete(sender byte, encodedLen int32, msg *ParseComplete) { func (t *tracer) traceParseComplete(sender byte, encodedLen int32, msg *ParseComplete) {
t.beginTrace(sender, encodedLen, "ParseComplete") t.writeTrace(sender, encodedLen, "ParseComplete", nil)
t.finishTrace()
} }
func (t *tracer) tracePortalSuspended(sender byte, encodedLen int32, msg *PortalSuspended) { func (t *tracer) tracePortalSuspended(sender byte, encodedLen int32, msg *PortalSuspended) {
t.beginTrace(sender, encodedLen, "PortalSuspended") t.writeTrace(sender, encodedLen, "PortalSuspended", nil)
t.finishTrace()
} }
func (t *tracer) traceQuery(sender byte, encodedLen int32, msg *Query) { func (t *tracer) traceQuery(sender byte, encodedLen int32, msg *Query) {
t.beginTrace(sender, encodedLen, "Query") t.writeTrace(sender, encodedLen, "Query", func() {
fmt.Fprintf(t.buf, "\t %s", traceDoubleQuotedString([]byte(msg.String))) fmt.Fprintf(t.buf, "\t %s", traceDoubleQuotedString([]byte(msg.String)))
t.finishTrace() })
} }
func (t *tracer) traceReadyForQuery(sender byte, encodedLen int32, msg *ReadyForQuery) { func (t *tracer) traceReadyForQuery(sender byte, encodedLen int32, msg *ReadyForQuery) {
t.beginTrace(sender, encodedLen, "ReadyForQuery") t.writeTrace(sender, encodedLen, "ReadyForQuery", func() {
fmt.Fprintf(t.buf, "\t %c", msg.TxStatus) fmt.Fprintf(t.buf, "\t %c", msg.TxStatus)
t.finishTrace() })
} }
func (t *tracer) traceRowDescription(sender byte, encodedLen int32, msg *RowDescription) { func (t *tracer) traceRowDescription(sender byte, encodedLen int32, msg *RowDescription) {
t.beginTrace(sender, encodedLen, "RowDescription") t.writeTrace(sender, encodedLen, "RowDescription", func() {
fmt.Fprintf(t.buf, "\t %d", len(msg.Fields)) fmt.Fprintf(t.buf, "\t %d", len(msg.Fields))
for _, fd := range msg.Fields { for _, fd := range msg.Fields {
fmt.Fprintf(t.buf, ` %s %d %d %d %d %d %d`, traceDoubleQuotedString(fd.Name), fd.TableOID, fd.TableAttributeNumber, fd.DataTypeOID, fd.DataTypeSize, fd.TypeModifier, fd.Format) fmt.Fprintf(t.buf, ` %s %d %d %d %d %d %d`, traceDoubleQuotedString(fd.Name), fd.TableOID, fd.TableAttributeNumber, fd.DataTypeOID, fd.DataTypeSize, fd.TypeModifier, fd.Format)
} }
t.finishTrace() })
} }
func (t *tracer) traceSSLRequest(sender byte, encodedLen int32, msg *SSLRequest) { func (t *tracer) traceSSLRequest(sender byte, encodedLen int32, msg *SSLRequest) {
t.beginTrace(sender, encodedLen, "SSLRequest") t.writeTrace(sender, encodedLen, "SSLRequest", nil)
t.finishTrace()
} }
func (t *tracer) traceStartupMessage(sender byte, encodedLen int32, msg *StartupMessage) { func (t *tracer) traceStartupMessage(sender byte, encodedLen int32, msg *StartupMessage) {
t.beginTrace(sender, encodedLen, "StartupMessage") t.writeTrace(sender, encodedLen, "StartupMessage", nil)
t.finishTrace()
} }
func (t *tracer) traceSync(sender byte, encodedLen int32, msg *Sync) { func (t *tracer) traceSync(sender byte, encodedLen int32, msg *Sync) {
t.beginTrace(sender, encodedLen, "Sync") t.writeTrace(sender, encodedLen, "Sync", nil)
t.finishTrace()
} }
func (t *tracer) traceTerminate(sender byte, encodedLen int32, msg *Terminate) { func (t *tracer) traceTerminate(sender byte, encodedLen int32, msg *Terminate) {
t.beginTrace(sender, encodedLen, "Terminate") t.writeTrace(sender, encodedLen, "Terminate", nil)
t.finishTrace()
} }
func (t *tracer) beginTrace(sender byte, encodedLen int32, msgType string) { func (t *tracer) writeTrace(sender byte, encodedLen int32, msgType string, writeDetails func()) {
t.mux.Lock()
defer t.mux.Unlock()
defer func() {
if t.buf.Cap() > 1024 {
t.buf = &bytes.Buffer{}
} else {
t.buf.Reset()
}
}()
if !t.SuppressTimestamps { if !t.SuppressTimestamps {
now := time.Now() now := time.Now()
t.buf.WriteString(now.Format("2006-01-02 15:04:05.000000")) t.buf.WriteString(now.Format("2006-01-02 15:04:05.000000"))
@ -402,17 +382,13 @@ func (t *tracer) beginTrace(sender byte, encodedLen int32, msgType string) {
t.buf.WriteString(msgType) t.buf.WriteString(msgType)
t.buf.WriteByte('\t') t.buf.WriteByte('\t')
t.buf.WriteString(strconv.FormatInt(int64(encodedLen), 10)) t.buf.WriteString(strconv.FormatInt(int64(encodedLen), 10))
}
func (t *tracer) finishTrace() { if writeDetails != nil {
writeDetails()
}
t.buf.WriteByte('\n') t.buf.WriteByte('\n')
t.buf.WriteTo(t.w) t.buf.WriteTo(t.w)
if t.buf.Cap() > 1024 {
t.buf = &bytes.Buffer{}
} else {
t.buf.Reset()
}
} }
// traceDoubleQuotedString returns t.buf as a double-quoted string without any escaping. It is roughly equivalent to // traceDoubleQuotedString returns t.buf as a double-quoted string without any escaping. It is roughly equivalent to

View file

@ -92,6 +92,23 @@ func (JSONCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan
switch target.(type) { switch target.(type) {
case *string: case *string:
return scanPlanAnyToString{} return scanPlanAnyToString{}
case **string:
// This is to fix **string scanning. It seems wrong to special case **string, but it's not clear what a better
// solution would be.
//
// https://github.com/jackc/pgx/issues/1470 -- **string
// https://github.com/jackc/pgx/issues/1691 -- ** anything else
if wrapperPlan, nextDst, ok := TryPointerPointerScanPlan(target); ok {
if nextPlan := m.planScan(oid, format, nextDst); nextPlan != nil {
if _, failed := nextPlan.(*scanPlanFail); !failed {
wrapperPlan.SetNext(nextPlan)
return wrapperPlan
}
}
}
case *[]byte: case *[]byte:
return scanPlanJSONToByteSlice{} return scanPlanJSONToByteSlice{}
case BytesScanner: case BytesScanner:
@ -104,19 +121,6 @@ func (JSONCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan
return &scanPlanSQLScanner{formatCode: format} 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{} return scanPlanJSONToJSONUnmarshal{}
} }

View file

@ -144,20 +144,20 @@ func (n *Numeric) toBigInt() (*big.Int, error) {
} }
func parseNumericString(str string) (n *big.Int, exp int32, err error) { func parseNumericString(str string) (n *big.Int, exp int32, err error) {
parts := strings.SplitN(str, ".", 2) idx := strings.IndexByte(str, '.')
digits := strings.Join(parts, "")
if len(parts) > 1 { if idx == -1 {
exp = int32(-len(parts[1])) for len(str) > 1 && str[len(str)-1] == '0' && str[len(str)-2] != '-' {
} else { str = str[:len(str)-1]
for len(digits) > 1 && digits[len(digits)-1] == '0' && digits[len(digits)-2] != '-' {
digits = digits[:len(digits)-1]
exp++ exp++
} }
} else {
exp = int32(-(len(str) - idx - 1))
str = str[:idx] + str[idx+1:]
} }
accum := &big.Int{} accum := &big.Int{}
if _, ok := accum.SetString(digits, 10); !ok { if _, ok := accum.SetString(str, 10); !ok {
return nil, 0, fmt.Errorf("%s is not a number", str) return nil, 0, fmt.Errorf("%s is not a number", str)
} }

View file

@ -44,7 +44,7 @@
MacaddrOID = 829 MacaddrOID = 829
InetOID = 869 InetOID = 869
BoolArrayOID = 1000 BoolArrayOID = 1000
QCharArrayOID = 1003 QCharArrayOID = 1002
NameArrayOID = 1003 NameArrayOID = 1003
Int2ArrayOID = 1005 Int2ArrayOID = 1005
Int4ArrayOID = 1007 Int4ArrayOID = 1007

View file

@ -306,7 +306,7 @@ func (rows *baseRows) Values() ([]any, error) {
copy(newBuf, buf) copy(newBuf, buf)
values = append(values, newBuf) values = append(values, newBuf)
default: default:
rows.fatal(errors.New("Unknown format code")) rows.fatal(errors.New("unknown format code"))
} }
} }
@ -496,7 +496,8 @@ func (rs *mapRowScanner) ScanRow(rows Rows) error {
} }
// RowToStructByPos returns a T scanned from row. T must be a struct. T must have the same number a public fields as row // RowToStructByPos returns a T scanned from row. T must be a struct. T must have the same number a public fields as row
// has fields. The row and T fields will by matched by position. // has fields. The row and T fields will by matched by position. If the "db" struct tag is "-" then the field will be
// ignored.
func RowToStructByPos[T any](row CollectableRow) (T, error) { func RowToStructByPos[T any](row CollectableRow) (T, error) {
var value T var value T
err := row.Scan(&positionalStructRowScanner{ptrToStruct: &value}) err := row.Scan(&positionalStructRowScanner{ptrToStruct: &value})
@ -504,7 +505,8 @@ func RowToStructByPos[T any](row CollectableRow) (T, error) {
} }
// RowToAddrOfStructByPos returns the address of a T scanned from row. T must be a struct. T must have the same number a // RowToAddrOfStructByPos returns the address of a T scanned from row. T must be a struct. T must have the same number a
// public fields as row has fields. The row and T fields will by matched by position. // public fields as row has fields. The row and T fields will by matched by position. If the "db" struct tag is "-" then
// the field will be ignored.
func RowToAddrOfStructByPos[T any](row CollectableRow) (*T, error) { func RowToAddrOfStructByPos[T any](row CollectableRow) (*T, error) {
var value T var value T
err := row.Scan(&positionalStructRowScanner{ptrToStruct: &value}) err := row.Scan(&positionalStructRowScanner{ptrToStruct: &value})
@ -545,6 +547,11 @@ func (rs *positionalStructRowScanner) appendScanTargets(dstElemValue reflect.Val
if sf.Anonymous && sf.Type.Kind() == reflect.Struct { if sf.Anonymous && sf.Type.Kind() == reflect.Struct {
scanTargets = rs.appendScanTargets(dstElemValue.Field(i), scanTargets) scanTargets = rs.appendScanTargets(dstElemValue.Field(i), scanTargets)
} else if sf.PkgPath == "" { } else if sf.PkgPath == "" {
dbTag, _ := sf.Tag.Lookup(structTagKey)
if dbTag == "-" {
// Field is ignored, skip it.
continue
}
scanTargets = append(scanTargets, dstElemValue.Field(i).Addr().Interface()) scanTargets = append(scanTargets, dstElemValue.Field(i).Addr().Interface())
} }
} }

View file

@ -152,7 +152,6 @@ type Tx interface {
// called on the dbTx. // called on the dbTx.
type dbTx struct { type dbTx struct {
conn *Conn conn *Conn
err error
savepointNum int64 savepointNum int64
closed bool closed bool
} }

2
vendor/modules.txt vendored
View file

@ -338,7 +338,7 @@ github.com/jackc/pgproto3/v2
# github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a # github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a
## explicit; go 1.14 ## explicit; go 1.14
github.com/jackc/pgservicefile github.com/jackc/pgservicefile
# github.com/jackc/pgx/v5 v5.4.2 # github.com/jackc/pgx/v5 v5.4.3
## explicit; go 1.19 ## explicit; go 1.19
github.com/jackc/pgx/v5 github.com/jackc/pgx/v5
github.com/jackc/pgx/v5/internal/anynil github.com/jackc/pgx/v5/internal/anynil