用户登录和接口鉴权
This commit is contained in:
9
vendor/github.com/jackc/pgservicefile/.travis.yml
generated
vendored
9
vendor/github.com/jackc/pgservicefile/.travis.yml
generated
vendored
@@ -1,9 +0,0 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.x
|
||||
- tip
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: tip
|
||||
5
vendor/github.com/jackc/pgservicefile/README.md
generated
vendored
5
vendor/github.com/jackc/pgservicefile/README.md
generated
vendored
@@ -1,5 +1,6 @@
|
||||
[](https://godoc.org/github.com/jackc/pgservicefile)
|
||||
[](https://travis-ci.org/jackc/pgservicefile)
|
||||
[](https://pkg.go.dev/github.com/jackc/pgservicefile)
|
||||
[](https://github.com/jackc/pgservicefile/actions/workflows/ci.yml)
|
||||
|
||||
|
||||
# pgservicefile
|
||||
|
||||
|
||||
4
vendor/github.com/jackc/pgservicefile/pgservicefile.go
generated
vendored
4
vendor/github.com/jackc/pgservicefile/pgservicefile.go
generated
vendored
@@ -57,7 +57,7 @@ func ParseServicefile(r io.Reader) (*Servicefile, error) {
|
||||
} else if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
|
||||
service = &Service{Name: line[1 : len(line)-1], Settings: make(map[string]string)}
|
||||
servicefile.Services = append(servicefile.Services, service)
|
||||
} else {
|
||||
} else if service != nil {
|
||||
parts := strings.SplitN(line, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
return nil, fmt.Errorf("unable to parse line %d", lineNum)
|
||||
@@ -67,6 +67,8 @@ func ParseServicefile(r io.Reader) (*Servicefile, error) {
|
||||
value := strings.TrimSpace(parts[1])
|
||||
|
||||
service.Settings[key] = value
|
||||
} else {
|
||||
return nil, fmt.Errorf("line %d is not in a section", lineNum)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
111
vendor/github.com/jackc/pgx/v5/CHANGELOG.md
generated
vendored
111
vendor/github.com/jackc/pgx/v5/CHANGELOG.md
generated
vendored
@@ -1,3 +1,114 @@
|
||||
# 5.7.1 (September 10, 2024)
|
||||
|
||||
* Fix data race in tracelog.TraceLog
|
||||
* Update puddle to v2.2.2. This removes the import of nanotime via linkname.
|
||||
* Update golang.org/x/crypto and golang.org/x/text
|
||||
|
||||
# 5.7.0 (September 7, 2024)
|
||||
|
||||
* Add support for sslrootcert=system (Yann Soubeyrand)
|
||||
* Add LoadTypes to load multiple types in a single SQL query (Nick Farrell)
|
||||
* Add XMLCodec supports encoding + scanning XML column type like json (nickcruess-soda)
|
||||
* Add MultiTrace (Stepan Rabotkin)
|
||||
* Add TraceLogConfig with customizable TimeKey (stringintech)
|
||||
* pgx.ErrNoRows wraps sql.ErrNoRows to aid in database/sql compatibility with native pgx functions (merlin)
|
||||
* Support scanning binary formatted uint32 into string / TextScanner (jennifersp)
|
||||
* Fix interval encoding to allow 0s and avoid extra spaces (Carlos Pérez-Aradros Herce)
|
||||
* Update pgservicefile - fixes panic when parsing invalid file
|
||||
* Better error message when reading past end of batch
|
||||
* Don't print url when url.Parse returns an error (Kevin Biju)
|
||||
* Fix snake case name normalization collision in RowToStructByName with db tag (nolandseigler)
|
||||
* Fix: Scan and encode types with underlying types of arrays
|
||||
|
||||
# 5.6.0 (May 25, 2024)
|
||||
|
||||
* Add StrictNamedArgs (Tomas Zahradnicek)
|
||||
* Add support for macaddr8 type (Carlos Pérez-Aradros Herce)
|
||||
* Add SeverityUnlocalized field to PgError / Notice
|
||||
* Performance optimization of RowToStructByPos/Name (Zach Olstein)
|
||||
* Allow customizing context canceled behavior for pgconn
|
||||
* Add ScanLocation to pgtype.Timestamp[tz]Codec
|
||||
* Add custom data to pgconn.PgConn
|
||||
* Fix ResultReader.Read() to handle nil values
|
||||
* Do not encode interval microseconds when they are 0 (Carlos Pérez-Aradros Herce)
|
||||
* pgconn.SafeToRetry checks for wrapped errors (tjasko)
|
||||
* Failed connection attempts include all errors
|
||||
* Optimize LargeObject.Read (Mitar)
|
||||
* Add tracing for connection acquire and release from pool (ngavinsir)
|
||||
* Fix encode driver.Valuer not called when nil
|
||||
* Add support for custom JSON marshal and unmarshal (Mitar)
|
||||
* Use Go default keepalive for TCP connections (Hans-Joachim Kliemeck)
|
||||
|
||||
# 5.5.5 (March 9, 2024)
|
||||
|
||||
Use spaces instead of parentheses for SQL sanitization.
|
||||
|
||||
This still solves the problem of negative numbers creating a line comment, but this avoids breaking edge cases such as
|
||||
`set foo to $1` where the substitution is taking place in a location where an arbitrary expression is not allowed.
|
||||
|
||||
# 5.5.4 (March 4, 2024)
|
||||
|
||||
Fix CVE-2024-27304
|
||||
|
||||
SQL injection can occur if an attacker can cause a single query or bind message to exceed 4 GB in size. An integer
|
||||
overflow in the calculated message size can cause the one large message to be sent as multiple messages under the
|
||||
attacker's control.
|
||||
|
||||
Thanks to Paul Gerste for reporting this issue.
|
||||
|
||||
* Fix behavior of CollectRows to return empty slice if Rows are empty (Felix)
|
||||
* Fix simple protocol encoding of json.RawMessage
|
||||
* Fix *Pipeline.getResults should close pipeline on error
|
||||
* Fix panic in TryFindUnderlyingTypeScanPlan (David Kurman)
|
||||
* Fix deallocation of invalidated cached statements in a transaction
|
||||
* Handle invalid sslkey file
|
||||
* Fix scan float4 into sql.Scanner
|
||||
* Fix pgtype.Bits not making copy of data from read buffer. This would cause the data to be corrupted by future reads.
|
||||
|
||||
# 5.5.3 (February 3, 2024)
|
||||
|
||||
* Fix: prepared statement already exists
|
||||
* Improve CopyFrom auto-conversion of text-ish values
|
||||
* Add ltree type support (Florent Viel)
|
||||
* Make some properties of Batch and QueuedQuery public (Pavlo Golub)
|
||||
* Add AppendRows function (Edoardo Spadolini)
|
||||
* Optimize convert UUID [16]byte to string (Kirill Malikov)
|
||||
* Fix: LargeObject Read and Write of more than ~1GB at a time (Mitar)
|
||||
|
||||
# 5.5.2 (January 13, 2024)
|
||||
|
||||
* Allow NamedArgs to start with underscore
|
||||
* pgproto3: Maximum message body length support (jeremy.spriet)
|
||||
* Upgrade golang.org/x/crypto to v0.17.0
|
||||
* Add snake_case support to RowToStructByName (Tikhon Fedulov)
|
||||
* Fix: update description cache after exec prepare (James Hartig)
|
||||
* Fix: pipeline checks if it is closed (James Hartig and Ryan Fowler)
|
||||
* Fix: normalize timeout / context errors during TLS startup (Samuel Stauffer)
|
||||
* Add OnPgError for easier centralized error handling (James Hartig)
|
||||
|
||||
# 5.5.1 (December 9, 2023)
|
||||
|
||||
* Add CopyFromFunc helper function. (robford)
|
||||
* Add PgConn.Deallocate method that uses PostgreSQL protocol Close message.
|
||||
* pgx uses new PgConn.Deallocate method. This allows deallocating statements to work in a failed transaction. This fixes a case where the prepared statement map could become invalid.
|
||||
* Fix: Prefer driver.Valuer over json.Marshaler for json fields. (Jacopo)
|
||||
* Fix: simple protocol SQL sanitizer previously panicked if an invalid $0 placeholder was used. This now returns an error instead. (maksymnevajdev)
|
||||
* Add pgtype.Numeric.ScanScientific (Eshton Robateau)
|
||||
|
||||
# 5.5.0 (November 4, 2023)
|
||||
|
||||
* Add CollectExactlyOneRow. (Julien GOTTELAND)
|
||||
* Add OpenDBFromPool to create *database/sql.DB from *pgxpool.Pool. (Lev Zakharov)
|
||||
* Prepare can automatically choose statement name based on sql. This makes it easier to explicitly manage prepared statements.
|
||||
* Statement cache now uses deterministic, stable statement names.
|
||||
* database/sql prepared statement names are deterministically generated.
|
||||
* Fix: SendBatch wasn't respecting context cancellation.
|
||||
* Fix: Timeout error from pipeline is now normalized.
|
||||
* Fix: database/sql encoding json.RawMessage to []byte.
|
||||
* CancelRequest: Wait for the cancel request to be acknowledged by the server. This should improve PgBouncer compatibility. (Anton Levakin)
|
||||
* stdlib: Use Ping instead of CheckConn in ResetSession
|
||||
* Add json.Marshaler and json.Unmarshaler for Float4, Float8 (Kirill Mironov)
|
||||
|
||||
# 5.4.3 (August 5, 2023)
|
||||
|
||||
* Fix: QCharArrayOID was defined with the wrong OID (Christoph Engelbert)
|
||||
|
||||
19
vendor/github.com/jackc/pgx/v5/CONTRIBUTING.md
generated
vendored
19
vendor/github.com/jackc/pgx/v5/CONTRIBUTING.md
generated
vendored
@@ -29,6 +29,7 @@ Create and setup a test database:
|
||||
export PGDATABASE=pgx_test
|
||||
createdb
|
||||
psql -c 'create extension hstore;'
|
||||
psql -c 'create extension ltree;'
|
||||
psql -c 'create domain uint64 as numeric(20,0);'
|
||||
```
|
||||
|
||||
@@ -79,20 +80,11 @@ echo "listen_addresses = '127.0.0.1'" >> .testdb/$POSTGRESQL_DATA_DIR/postgresql
|
||||
echo "port = $PGPORT" >> .testdb/$POSTGRESQL_DATA_DIR/postgresql.conf
|
||||
cat testsetup/postgresql_ssl.conf >> .testdb/$POSTGRESQL_DATA_DIR/postgresql.conf
|
||||
cp testsetup/pg_hba.conf .testdb/$POSTGRESQL_DATA_DIR/pg_hba.conf
|
||||
cp testsetup/ca.cnf .testdb
|
||||
cp testsetup/localhost.cnf .testdb
|
||||
cp testsetup/pgx_sslcert.cnf .testdb
|
||||
|
||||
cd .testdb
|
||||
|
||||
# Generate a CA public / private key pair.
|
||||
openssl genrsa -out ca.key 4096
|
||||
openssl req -x509 -config ca.cnf -new -nodes -key ca.key -sha256 -days 365 -subj '/O=pgx-test-root' -out ca.pem
|
||||
|
||||
# Generate the certificate for localhost (the server).
|
||||
openssl genrsa -out localhost.key 2048
|
||||
openssl req -new -config localhost.cnf -key localhost.key -out localhost.csr
|
||||
openssl x509 -req -in localhost.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out localhost.crt -days 364 -sha256 -extfile localhost.cnf -extensions v3_req
|
||||
# Generate CA, server, and encrypted client certificates.
|
||||
go run ../testsetup/generate_certs.go
|
||||
|
||||
# Copy certificates to server directory and set permissions.
|
||||
cp ca.pem $POSTGRESQL_DATA_DIR/root.crt
|
||||
@@ -100,11 +92,6 @@ cp localhost.key $POSTGRESQL_DATA_DIR/server.key
|
||||
chmod 600 $POSTGRESQL_DATA_DIR/server.key
|
||||
cp localhost.crt $POSTGRESQL_DATA_DIR/server.crt
|
||||
|
||||
# Generate the certificate for client authentication.
|
||||
openssl genrsa -des3 -out pgx_sslcert.key -passout pass:certpw 2048
|
||||
openssl req -new -config pgx_sslcert.cnf -key pgx_sslcert.key -passin pass:certpw -out pgx_sslcert.csr
|
||||
openssl x509 -req -in pgx_sslcert.csr -CA ca.pem -CAkey ca.key -CAcreateserial -out pgx_sslcert.crt -days 363 -sha256 -extfile pgx_sslcert.cnf -extensions v3_req
|
||||
|
||||
cd ..
|
||||
```
|
||||
|
||||
|
||||
7
vendor/github.com/jackc/pgx/v5/README.md
generated
vendored
7
vendor/github.com/jackc/pgx/v5/README.md
generated
vendored
@@ -86,9 +86,13 @@ It is also possible to use the `database/sql` interface and convert a connection
|
||||
|
||||
See CONTRIBUTING.md for setup instructions.
|
||||
|
||||
## Architecture
|
||||
|
||||
See the presentation at Golang Estonia, [PGX Top to Bottom](https://www.youtube.com/watch?v=sXMSWhcHCf8) for a description of pgx architecture.
|
||||
|
||||
## 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.19 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.21 and higher and PostgreSQL 12 and higher. pgx also is tested against the latest version of [CockroachDB](https://www.cockroachlabs.com/product/).
|
||||
|
||||
## Version Policy
|
||||
|
||||
@@ -116,6 +120,7 @@ pgerrcode contains constants for the PostgreSQL error codes.
|
||||
|
||||
* [github.com/jackc/pgx-gofrs-uuid](https://github.com/jackc/pgx-gofrs-uuid)
|
||||
* [github.com/jackc/pgx-shopspring-decimal](https://github.com/jackc/pgx-shopspring-decimal)
|
||||
* [github.com/twpayne/pgx-geos](https://github.com/twpayne/pgx-geos) ([PostGIS](https://postgis.net/) and [GEOS](https://libgeos.org/) via [go-geos](https://github.com/twpayne/go-geos))
|
||||
* [github.com/vgarvardt/pgx-google-uuid](https://github.com/vgarvardt/pgx-google-uuid)
|
||||
|
||||
|
||||
|
||||
84
vendor/github.com/jackc/pgx/v5/batch.go
generated
vendored
84
vendor/github.com/jackc/pgx/v5/batch.go
generated
vendored
@@ -10,9 +10,9 @@ import (
|
||||
|
||||
// QueuedQuery is a query that has been queued for execution via a Batch.
|
||||
type QueuedQuery struct {
|
||||
query string
|
||||
arguments []any
|
||||
fn batchItemFunc
|
||||
SQL string
|
||||
Arguments []any
|
||||
Fn batchItemFunc
|
||||
sd *pgconn.StatementDescription
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ type batchItemFunc func(br BatchResults) error
|
||||
|
||||
// Query sets fn to be called when the response to qq is received.
|
||||
func (qq *QueuedQuery) Query(fn func(rows Rows) error) {
|
||||
qq.fn = func(br BatchResults) error {
|
||||
qq.Fn = func(br BatchResults) error {
|
||||
rows, _ := br.Query()
|
||||
defer rows.Close()
|
||||
|
||||
@@ -36,7 +36,7 @@ func (qq *QueuedQuery) Query(fn func(rows Rows) error) {
|
||||
|
||||
// Query sets fn to be called when the response to qq is received.
|
||||
func (qq *QueuedQuery) QueryRow(fn func(row Row) error) {
|
||||
qq.fn = func(br BatchResults) error {
|
||||
qq.Fn = func(br BatchResults) error {
|
||||
row := br.QueryRow()
|
||||
return fn(row)
|
||||
}
|
||||
@@ -44,7 +44,7 @@ func (qq *QueuedQuery) QueryRow(fn func(row Row) error) {
|
||||
|
||||
// Exec sets fn to be called when the response to qq is received.
|
||||
func (qq *QueuedQuery) Exec(fn func(ct pgconn.CommandTag) error) {
|
||||
qq.fn = func(br BatchResults) error {
|
||||
qq.Fn = func(br BatchResults) error {
|
||||
ct, err := br.Exec()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -57,22 +57,28 @@ func (qq *QueuedQuery) Exec(fn func(ct pgconn.CommandTag) error) {
|
||||
// Batch queries are a way of bundling multiple queries together to avoid
|
||||
// unnecessary network round trips. A Batch must only be sent once.
|
||||
type Batch struct {
|
||||
queuedQueries []*QueuedQuery
|
||||
QueuedQueries []*QueuedQuery
|
||||
}
|
||||
|
||||
// Queue queues a query to batch b. query can be an SQL query or the name of a prepared statement.
|
||||
// Queue queues a query to batch b. query can be an SQL query or the name of a prepared statement. The only pgx option
|
||||
// argument that is supported is QueryRewriter. Queries are executed using the connection's DefaultQueryExecMode.
|
||||
//
|
||||
// While query can contain multiple statements if the connection's DefaultQueryExecMode is QueryModeSimple, this should
|
||||
// be avoided. QueuedQuery.Fn must not be set as it will only be called for the first query. That is, QueuedQuery.Query,
|
||||
// QueuedQuery.QueryRow, and QueuedQuery.Exec must not be called. In addition, any error messages or tracing that
|
||||
// include the current query may reference the wrong query.
|
||||
func (b *Batch) Queue(query string, arguments ...any) *QueuedQuery {
|
||||
qq := &QueuedQuery{
|
||||
query: query,
|
||||
arguments: arguments,
|
||||
SQL: query,
|
||||
Arguments: arguments,
|
||||
}
|
||||
b.queuedQueries = append(b.queuedQueries, qq)
|
||||
b.QueuedQueries = append(b.QueuedQueries, qq)
|
||||
return qq
|
||||
}
|
||||
|
||||
// Len returns number of queries that have been queued so far.
|
||||
func (b *Batch) Len() int {
|
||||
return len(b.queuedQueries)
|
||||
return len(b.QueuedQueries)
|
||||
}
|
||||
|
||||
type BatchResults interface {
|
||||
@@ -126,7 +132,7 @@ func (br *batchResults) Exec() (pgconn.CommandTag, error) {
|
||||
if !br.mrr.NextResult() {
|
||||
err := br.mrr.Close()
|
||||
if err == nil {
|
||||
err = errors.New("no result")
|
||||
err = errors.New("no more results in batch")
|
||||
}
|
||||
if br.conn.batchTracer != nil {
|
||||
br.conn.batchTracer.TraceBatchQuery(br.ctx, br.conn, TraceBatchQueryData{
|
||||
@@ -178,7 +184,7 @@ func (br *batchResults) Query() (Rows, error) {
|
||||
if !br.mrr.NextResult() {
|
||||
rows.err = br.mrr.Close()
|
||||
if rows.err == nil {
|
||||
rows.err = errors.New("no result")
|
||||
rows.err = errors.New("no more results in batch")
|
||||
}
|
||||
rows.closed = true
|
||||
|
||||
@@ -225,9 +231,9 @@ func (br *batchResults) Close() error {
|
||||
}
|
||||
|
||||
// Read and run fn for all remaining items
|
||||
for br.err == nil && !br.closed && br.b != nil && br.qqIdx < len(br.b.queuedQueries) {
|
||||
if br.b.queuedQueries[br.qqIdx].fn != nil {
|
||||
err := br.b.queuedQueries[br.qqIdx].fn(br)
|
||||
for br.err == nil && !br.closed && br.b != nil && br.qqIdx < len(br.b.QueuedQueries) {
|
||||
if br.b.QueuedQueries[br.qqIdx].Fn != nil {
|
||||
err := br.b.QueuedQueries[br.qqIdx].Fn(br)
|
||||
if err != nil {
|
||||
br.err = err
|
||||
}
|
||||
@@ -251,10 +257,10 @@ func (br *batchResults) earlyError() error {
|
||||
}
|
||||
|
||||
func (br *batchResults) nextQueryAndArgs() (query string, args []any, ok bool) {
|
||||
if br.b != nil && br.qqIdx < len(br.b.queuedQueries) {
|
||||
bi := br.b.queuedQueries[br.qqIdx]
|
||||
query = bi.query
|
||||
args = bi.arguments
|
||||
if br.b != nil && br.qqIdx < len(br.b.QueuedQueries) {
|
||||
bi := br.b.QueuedQueries[br.qqIdx]
|
||||
query = bi.SQL
|
||||
args = bi.Arguments
|
||||
ok = true
|
||||
br.qqIdx++
|
||||
}
|
||||
@@ -285,7 +291,10 @@ func (br *pipelineBatchResults) Exec() (pgconn.CommandTag, error) {
|
||||
return pgconn.CommandTag{}, br.err
|
||||
}
|
||||
|
||||
query, arguments, _ := br.nextQueryAndArgs()
|
||||
query, arguments, err := br.nextQueryAndArgs()
|
||||
if err != nil {
|
||||
return pgconn.CommandTag{}, err
|
||||
}
|
||||
|
||||
results, err := br.pipeline.GetResults()
|
||||
if err != nil {
|
||||
@@ -328,9 +337,9 @@ func (br *pipelineBatchResults) Query() (Rows, error) {
|
||||
return &baseRows{err: br.err, closed: true}, br.err
|
||||
}
|
||||
|
||||
query, arguments, ok := br.nextQueryAndArgs()
|
||||
if !ok {
|
||||
query = "batch query"
|
||||
query, arguments, err := br.nextQueryAndArgs()
|
||||
if err != nil {
|
||||
return &baseRows{err: err, closed: true}, err
|
||||
}
|
||||
|
||||
rows := br.conn.getRows(br.ctx, query, arguments)
|
||||
@@ -394,9 +403,9 @@ func (br *pipelineBatchResults) Close() error {
|
||||
}
|
||||
|
||||
// Read and run fn for all remaining items
|
||||
for br.err == nil && !br.closed && br.b != nil && br.qqIdx < len(br.b.queuedQueries) {
|
||||
if br.b.queuedQueries[br.qqIdx].fn != nil {
|
||||
err := br.b.queuedQueries[br.qqIdx].fn(br)
|
||||
for br.err == nil && !br.closed && br.b != nil && br.qqIdx < len(br.b.QueuedQueries) {
|
||||
if br.b.QueuedQueries[br.qqIdx].Fn != nil {
|
||||
err := br.b.QueuedQueries[br.qqIdx].Fn(br)
|
||||
if err != nil {
|
||||
br.err = err
|
||||
}
|
||||
@@ -419,13 +428,16 @@ func (br *pipelineBatchResults) earlyError() error {
|
||||
return br.err
|
||||
}
|
||||
|
||||
func (br *pipelineBatchResults) nextQueryAndArgs() (query string, args []any, ok bool) {
|
||||
if br.b != nil && br.qqIdx < len(br.b.queuedQueries) {
|
||||
bi := br.b.queuedQueries[br.qqIdx]
|
||||
query = bi.query
|
||||
args = bi.arguments
|
||||
ok = true
|
||||
br.qqIdx++
|
||||
func (br *pipelineBatchResults) nextQueryAndArgs() (query string, args []any, err error) {
|
||||
if br.b == nil {
|
||||
return "", nil, errors.New("no reference to batch")
|
||||
}
|
||||
return
|
||||
|
||||
if br.qqIdx >= len(br.b.QueuedQueries) {
|
||||
return "", nil, errors.New("no more results in batch")
|
||||
}
|
||||
|
||||
bi := br.b.QueuedQueries[br.qqIdx]
|
||||
br.qqIdx++
|
||||
return bi.SQL, bi.Arguments, nil
|
||||
}
|
||||
|
||||
207
vendor/github.com/jackc/pgx/v5/conn.go
generated
vendored
207
vendor/github.com/jackc/pgx/v5/conn.go
generated
vendored
@@ -2,13 +2,15 @@ package pgx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jackc/pgx/v5/internal/anynil"
|
||||
"github.com/jackc/pgx/v5/internal/sanitize"
|
||||
"github.com/jackc/pgx/v5/internal/stmtcache"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
@@ -35,7 +37,7 @@ type ConnConfig struct {
|
||||
|
||||
// DefaultQueryExecMode controls the default mode for executing queries. By default pgx uses the extended protocol
|
||||
// and automatically prepares and caches prepared statements. However, this may be incompatible with proxies such as
|
||||
// PGBouncer. In this case it may be preferrable to use QueryExecModeExec or QueryExecModeSimpleProtocol. The same
|
||||
// PGBouncer. In this case it may be preferable to use QueryExecModeExec or QueryExecModeSimpleProtocol. The same
|
||||
// functionality can be controlled on a per query basis by passing a QueryExecMode as the first query argument.
|
||||
DefaultQueryExecMode QueryExecMode
|
||||
|
||||
@@ -99,11 +101,33 @@ func (ident Identifier) Sanitize() string {
|
||||
return strings.Join(parts, ".")
|
||||
}
|
||||
|
||||
// ErrNoRows occurs when rows are expected but none are returned.
|
||||
var ErrNoRows = errors.New("no rows in result set")
|
||||
var (
|
||||
// ErrNoRows occurs when rows are expected but none are returned.
|
||||
ErrNoRows = newProxyErr(sql.ErrNoRows, "no rows in result set")
|
||||
// ErrTooManyRows occurs when more rows than expected are returned.
|
||||
ErrTooManyRows = errors.New("too many rows in result set")
|
||||
)
|
||||
|
||||
var errDisabledStatementCache = fmt.Errorf("cannot use QueryExecModeCacheStatement with disabled statement cache")
|
||||
var errDisabledDescriptionCache = fmt.Errorf("cannot use QueryExecModeCacheDescribe with disabled description cache")
|
||||
func newProxyErr(background error, msg string) error {
|
||||
return &proxyError{
|
||||
msg: msg,
|
||||
background: background,
|
||||
}
|
||||
}
|
||||
|
||||
type proxyError struct {
|
||||
msg string
|
||||
background error
|
||||
}
|
||||
|
||||
func (err *proxyError) Error() string { return err.msg }
|
||||
|
||||
func (err *proxyError) Unwrap() error { return err.background }
|
||||
|
||||
var (
|
||||
errDisabledStatementCache = fmt.Errorf("cannot use QueryExecModeCacheStatement with disabled statement cache")
|
||||
errDisabledDescriptionCache = fmt.Errorf("cannot use QueryExecModeCacheDescribe with disabled description cache")
|
||||
)
|
||||
|
||||
// Connect establishes a connection with a PostgreSQL server with a connection string. See
|
||||
// pgconn.Connect for details.
|
||||
@@ -269,7 +293,7 @@ func connect(ctx context.Context, config *ConnConfig) (c *Conn, err error) {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Close closes a connection. It is safe to call Close on a already closed
|
||||
// Close closes a connection. It is safe to call Close on an already closed
|
||||
// connection.
|
||||
func (c *Conn) Close(ctx context.Context) error {
|
||||
if c.IsClosed() {
|
||||
@@ -280,12 +304,15 @@ func (c *Conn) Close(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Prepare creates a prepared statement with name and sql. sql can contain placeholders
|
||||
// for bound parameters. These placeholders are referenced positional as $1, $2, etc.
|
||||
// Prepare creates a prepared statement with name and sql. sql can contain placeholders for bound parameters. These
|
||||
// placeholders are referenced positionally as $1, $2, etc. name can be used instead of sql with Query, QueryRow, and
|
||||
// Exec to execute the statement. It can also be used with Batch.Queue.
|
||||
//
|
||||
// Prepare is idempotent; i.e. it is safe to call Prepare multiple times with the same
|
||||
// name and sql arguments. This allows a code path to Prepare and Query/Exec without
|
||||
// concern for if the statement has already been prepared.
|
||||
// The underlying PostgreSQL identifier for the prepared statement will be name if name != sql or a digest of sql if
|
||||
// name == sql.
|
||||
//
|
||||
// Prepare is idempotent; i.e. it is safe to call Prepare multiple times with the same name and sql arguments. This
|
||||
// allows a code path to Prepare and Query/Exec without concern for if the statement has already been prepared.
|
||||
func (c *Conn) Prepare(ctx context.Context, name, sql string) (sd *pgconn.StatementDescription, err error) {
|
||||
if c.prepareTracer != nil {
|
||||
ctx = c.prepareTracer.TracePrepareStart(ctx, c, TracePrepareStartData{Name: name, SQL: sql})
|
||||
@@ -307,23 +334,48 @@ func (c *Conn) Prepare(ctx context.Context, name, sql string) (sd *pgconn.Statem
|
||||
}()
|
||||
}
|
||||
|
||||
sd, err = c.pgConn.Prepare(ctx, name, sql, nil)
|
||||
var psName, psKey string
|
||||
if name == sql {
|
||||
digest := sha256.Sum256([]byte(sql))
|
||||
psName = "stmt_" + hex.EncodeToString(digest[0:24])
|
||||
psKey = sql
|
||||
} else {
|
||||
psName = name
|
||||
psKey = name
|
||||
}
|
||||
|
||||
sd, err = c.pgConn.Prepare(ctx, psName, sql, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if name != "" {
|
||||
c.preparedStatements[name] = sd
|
||||
if psKey != "" {
|
||||
c.preparedStatements[psKey] = sd
|
||||
}
|
||||
|
||||
return sd, nil
|
||||
}
|
||||
|
||||
// Deallocate released a prepared statement
|
||||
// Deallocate releases a prepared statement. Calling Deallocate on a non-existent prepared statement will succeed.
|
||||
func (c *Conn) Deallocate(ctx context.Context, name string) error {
|
||||
delete(c.preparedStatements, name)
|
||||
_, err := c.pgConn.Exec(ctx, "deallocate "+quoteIdentifier(name)).ReadAll()
|
||||
return err
|
||||
var psName string
|
||||
sd := c.preparedStatements[name]
|
||||
if sd != nil {
|
||||
psName = sd.Name
|
||||
} else {
|
||||
psName = name
|
||||
}
|
||||
|
||||
err := c.pgConn.Deallocate(ctx, psName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if sd != nil {
|
||||
delete(c.preparedStatements, name)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeallocateAll releases all previously prepared statements from the server and client, where it also resets the statement and description cache.
|
||||
@@ -441,7 +493,7 @@ optionLoop:
|
||||
if queryRewriter != nil {
|
||||
sql, arguments, err = queryRewriter.RewriteQuery(ctx, c, sql, arguments)
|
||||
if err != nil {
|
||||
return pgconn.CommandTag{}, fmt.Errorf("rewrite query failed: %v", err)
|
||||
return pgconn.CommandTag{}, fmt.Errorf("rewrite query failed: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -461,7 +513,7 @@ optionLoop:
|
||||
}
|
||||
sd := c.statementCache.Get(sql)
|
||||
if sd == nil {
|
||||
sd, err = c.Prepare(ctx, stmtcache.NextStatementName(), sql)
|
||||
sd, err = c.Prepare(ctx, stmtcache.StatementName(sql), sql)
|
||||
if err != nil {
|
||||
return pgconn.CommandTag{}, err
|
||||
}
|
||||
@@ -479,6 +531,7 @@ optionLoop:
|
||||
if err != nil {
|
||||
return pgconn.CommandTag{}, err
|
||||
}
|
||||
c.descriptionCache.Put(sd)
|
||||
}
|
||||
|
||||
return c.execParams(ctx, sd, arguments)
|
||||
@@ -573,32 +626,35 @@ type QueryExecMode int32
|
||||
const (
|
||||
_ QueryExecMode = iota
|
||||
|
||||
// Automatically prepare and cache statements. This uses the extended protocol. Queries are executed in a single
|
||||
// round trip after the statement is cached. This is the default.
|
||||
// Automatically prepare and cache statements. This uses the extended protocol. Queries are executed in a single round
|
||||
// trip after the statement is cached. This is the default. If the database schema is modified or the search_path is
|
||||
// changed after a statement is cached then the first execution of a previously cached query may fail. e.g. If the
|
||||
// number of columns returned by a "SELECT *" changes or the type of a column is changed.
|
||||
QueryExecModeCacheStatement
|
||||
|
||||
// Cache statement descriptions (i.e. argument and result types) and assume they do not change. This uses the
|
||||
// extended protocol. Queries are executed in a single round trip after the description is cached. If the database
|
||||
// schema is modified or the search_path is changed this may result in undetected result decoding errors.
|
||||
// Cache statement descriptions (i.e. argument and result types) and assume they do not change. This uses the extended
|
||||
// protocol. Queries are executed in a single round trip after the description is cached. If the database schema is
|
||||
// modified or the search_path is changed after a statement is cached then the first execution of a previously cached
|
||||
// query may fail. e.g. If the number of columns returned by a "SELECT *" changes or the type of a column is changed.
|
||||
QueryExecModeCacheDescribe
|
||||
|
||||
// Get the statement description on every execution. This uses the extended protocol. Queries require two round trips
|
||||
// to execute. It does not use named prepared statements. But it does use the unnamed prepared statement to get the
|
||||
// statement description on the first round trip and then uses it to execute the query on the second round trip. This
|
||||
// may cause problems with connection poolers that switch the underlying connection between round trips. It is safe
|
||||
// even when the the database schema is modified concurrently.
|
||||
// even when the database schema is modified concurrently.
|
||||
QueryExecModeDescribeExec
|
||||
|
||||
// Assume the PostgreSQL query parameter types based on the Go type of the arguments. This uses the extended protocol
|
||||
// with text formatted parameters and results. Queries are executed in a single round trip. Type mappings can be
|
||||
// registered with pgtype.Map.RegisterDefaultPgType. Queries will be rejected that have arguments that are
|
||||
// unregistered or ambigious. e.g. A map[string]string may have the PostgreSQL type json or hstore. Modes that know
|
||||
// unregistered or ambiguous. e.g. A map[string]string may have the PostgreSQL type json or hstore. Modes that know
|
||||
// the PostgreSQL type can use a map[string]string directly as an argument. This mode cannot.
|
||||
QueryExecModeExec
|
||||
|
||||
// Use the simple protocol. Assume the PostgreSQL query parameter types based on the Go type of the arguments.
|
||||
// Queries are executed in a single round trip. Type mappings can be registered with
|
||||
// pgtype.Map.RegisterDefaultPgType. Queries will be rejected that have arguments that are unregistered or ambigious.
|
||||
// pgtype.Map.RegisterDefaultPgType. Queries will be rejected that have arguments that are unregistered or ambiguous.
|
||||
// e.g. A map[string]string may have the PostgreSQL type json or hstore. Modes that know the PostgreSQL type can use
|
||||
// a map[string]string directly as an argument. This mode cannot.
|
||||
//
|
||||
@@ -705,7 +761,7 @@ optionLoop:
|
||||
sql, args, err = queryRewriter.RewriteQuery(ctx, c, sql, args)
|
||||
if err != nil {
|
||||
rows := c.getRows(ctx, originalSQL, originalArgs)
|
||||
err = fmt.Errorf("rewrite query failed: %v", err)
|
||||
err = fmt.Errorf("rewrite query failed: %w", err)
|
||||
rows.fatal(err)
|
||||
return rows, err
|
||||
}
|
||||
@@ -717,7 +773,6 @@ optionLoop:
|
||||
}
|
||||
|
||||
c.eqb.reset()
|
||||
anynil.NormalizeSlice(args)
|
||||
rows := c.getRows(ctx, sql, args)
|
||||
|
||||
var err error
|
||||
@@ -807,7 +862,6 @@ func (c *Conn) getStatementDescription(
|
||||
mode QueryExecMode,
|
||||
sql string,
|
||||
) (sd *pgconn.StatementDescription, err error) {
|
||||
|
||||
switch mode {
|
||||
case QueryExecModeCacheStatement:
|
||||
if c.statementCache == nil {
|
||||
@@ -815,7 +869,7 @@ func (c *Conn) getStatementDescription(
|
||||
}
|
||||
sd = c.statementCache.Get(sql)
|
||||
if sd == nil {
|
||||
sd, err = c.Prepare(ctx, stmtcache.NextStatementName(), sql)
|
||||
sd, err = c.Prepare(ctx, stmtcache.StatementName(sql), sql)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -865,15 +919,14 @@ func (c *Conn) SendBatch(ctx context.Context, b *Batch) (br BatchResults) {
|
||||
return &batchResults{ctx: ctx, conn: c, err: err}
|
||||
}
|
||||
|
||||
mode := c.config.DefaultQueryExecMode
|
||||
|
||||
for _, bi := range b.queuedQueries {
|
||||
for _, bi := range b.QueuedQueries {
|
||||
var queryRewriter QueryRewriter
|
||||
sql := bi.query
|
||||
arguments := bi.arguments
|
||||
sql := bi.SQL
|
||||
arguments := bi.Arguments
|
||||
|
||||
optionLoop:
|
||||
for len(arguments) > 0 {
|
||||
// Update Batch.Queue function comment when additional options are implemented
|
||||
switch arg := arguments[0].(type) {
|
||||
case QueryRewriter:
|
||||
queryRewriter = arg
|
||||
@@ -887,21 +940,23 @@ func (c *Conn) SendBatch(ctx context.Context, b *Batch) (br BatchResults) {
|
||||
var err error
|
||||
sql, arguments, err = queryRewriter.RewriteQuery(ctx, c, sql, arguments)
|
||||
if err != nil {
|
||||
return &batchResults{ctx: ctx, conn: c, err: fmt.Errorf("rewrite query failed: %v", err)}
|
||||
return &batchResults{ctx: ctx, conn: c, err: fmt.Errorf("rewrite query failed: %w", err)}
|
||||
}
|
||||
}
|
||||
|
||||
bi.query = sql
|
||||
bi.arguments = arguments
|
||||
bi.SQL = sql
|
||||
bi.Arguments = arguments
|
||||
}
|
||||
|
||||
// TODO: changing mode per batch? Update Batch.Queue function comment when implemented
|
||||
mode := c.config.DefaultQueryExecMode
|
||||
if mode == QueryExecModeSimpleProtocol {
|
||||
return c.sendBatchQueryExecModeSimpleProtocol(ctx, b)
|
||||
}
|
||||
|
||||
// All other modes use extended protocol and thus can use prepared statements.
|
||||
for _, bi := range b.queuedQueries {
|
||||
if sd, ok := c.preparedStatements[bi.query]; ok {
|
||||
for _, bi := range b.QueuedQueries {
|
||||
if sd, ok := c.preparedStatements[bi.SQL]; ok {
|
||||
bi.sd = sd
|
||||
}
|
||||
}
|
||||
@@ -922,11 +977,11 @@ func (c *Conn) SendBatch(ctx context.Context, b *Batch) (br BatchResults) {
|
||||
|
||||
func (c *Conn) sendBatchQueryExecModeSimpleProtocol(ctx context.Context, b *Batch) *batchResults {
|
||||
var sb strings.Builder
|
||||
for i, bi := range b.queuedQueries {
|
||||
for i, bi := range b.QueuedQueries {
|
||||
if i > 0 {
|
||||
sb.WriteByte(';')
|
||||
}
|
||||
sql, err := c.sanitizeForSimpleQuery(bi.query, bi.arguments...)
|
||||
sql, err := c.sanitizeForSimpleQuery(bi.SQL, bi.Arguments...)
|
||||
if err != nil {
|
||||
return &batchResults{ctx: ctx, conn: c, err: err}
|
||||
}
|
||||
@@ -945,21 +1000,21 @@ func (c *Conn) sendBatchQueryExecModeSimpleProtocol(ctx context.Context, b *Batc
|
||||
func (c *Conn) sendBatchQueryExecModeExec(ctx context.Context, b *Batch) *batchResults {
|
||||
batch := &pgconn.Batch{}
|
||||
|
||||
for _, bi := range b.queuedQueries {
|
||||
for _, bi := range b.QueuedQueries {
|
||||
sd := bi.sd
|
||||
if sd != nil {
|
||||
err := c.eqb.Build(c.typeMap, sd, bi.arguments)
|
||||
err := c.eqb.Build(c.typeMap, sd, bi.Arguments)
|
||||
if err != nil {
|
||||
return &batchResults{ctx: ctx, conn: c, err: err}
|
||||
}
|
||||
|
||||
batch.ExecPrepared(sd.Name, c.eqb.ParamValues, c.eqb.ParamFormats, c.eqb.ResultFormats)
|
||||
} else {
|
||||
err := c.eqb.Build(c.typeMap, nil, bi.arguments)
|
||||
err := c.eqb.Build(c.typeMap, nil, bi.Arguments)
|
||||
if err != nil {
|
||||
return &batchResults{ctx: ctx, conn: c, err: err}
|
||||
}
|
||||
batch.ExecParams(bi.query, c.eqb.ParamValues, nil, c.eqb.ParamFormats, c.eqb.ResultFormats)
|
||||
batch.ExecParams(bi.SQL, c.eqb.ParamValues, nil, c.eqb.ParamFormats, c.eqb.ResultFormats)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -984,18 +1039,18 @@ func (c *Conn) sendBatchQueryExecModeCacheStatement(ctx context.Context, b *Batc
|
||||
distinctNewQueries := []*pgconn.StatementDescription{}
|
||||
distinctNewQueriesIdxMap := make(map[string]int)
|
||||
|
||||
for _, bi := range b.queuedQueries {
|
||||
for _, bi := range b.QueuedQueries {
|
||||
if bi.sd == nil {
|
||||
sd := c.statementCache.Get(bi.query)
|
||||
sd := c.statementCache.Get(bi.SQL)
|
||||
if sd != nil {
|
||||
bi.sd = sd
|
||||
} else {
|
||||
if idx, present := distinctNewQueriesIdxMap[bi.query]; present {
|
||||
if idx, present := distinctNewQueriesIdxMap[bi.SQL]; present {
|
||||
bi.sd = distinctNewQueries[idx]
|
||||
} else {
|
||||
sd = &pgconn.StatementDescription{
|
||||
Name: stmtcache.NextStatementName(),
|
||||
SQL: bi.query,
|
||||
Name: stmtcache.StatementName(bi.SQL),
|
||||
SQL: bi.SQL,
|
||||
}
|
||||
distinctNewQueriesIdxMap[sd.SQL] = len(distinctNewQueries)
|
||||
distinctNewQueries = append(distinctNewQueries, sd)
|
||||
@@ -1016,17 +1071,17 @@ func (c *Conn) sendBatchQueryExecModeCacheDescribe(ctx context.Context, b *Batch
|
||||
distinctNewQueries := []*pgconn.StatementDescription{}
|
||||
distinctNewQueriesIdxMap := make(map[string]int)
|
||||
|
||||
for _, bi := range b.queuedQueries {
|
||||
for _, bi := range b.QueuedQueries {
|
||||
if bi.sd == nil {
|
||||
sd := c.descriptionCache.Get(bi.query)
|
||||
sd := c.descriptionCache.Get(bi.SQL)
|
||||
if sd != nil {
|
||||
bi.sd = sd
|
||||
} else {
|
||||
if idx, present := distinctNewQueriesIdxMap[bi.query]; present {
|
||||
if idx, present := distinctNewQueriesIdxMap[bi.SQL]; present {
|
||||
bi.sd = distinctNewQueries[idx]
|
||||
} else {
|
||||
sd = &pgconn.StatementDescription{
|
||||
SQL: bi.query,
|
||||
SQL: bi.SQL,
|
||||
}
|
||||
distinctNewQueriesIdxMap[sd.SQL] = len(distinctNewQueries)
|
||||
distinctNewQueries = append(distinctNewQueries, sd)
|
||||
@@ -1043,13 +1098,13 @@ func (c *Conn) sendBatchQueryExecModeDescribeExec(ctx context.Context, b *Batch)
|
||||
distinctNewQueries := []*pgconn.StatementDescription{}
|
||||
distinctNewQueriesIdxMap := make(map[string]int)
|
||||
|
||||
for _, bi := range b.queuedQueries {
|
||||
for _, bi := range b.QueuedQueries {
|
||||
if bi.sd == nil {
|
||||
if idx, present := distinctNewQueriesIdxMap[bi.query]; present {
|
||||
if idx, present := distinctNewQueriesIdxMap[bi.SQL]; present {
|
||||
bi.sd = distinctNewQueries[idx]
|
||||
} else {
|
||||
sd := &pgconn.StatementDescription{
|
||||
SQL: bi.query,
|
||||
SQL: bi.SQL,
|
||||
}
|
||||
distinctNewQueriesIdxMap[sd.SQL] = len(distinctNewQueries)
|
||||
distinctNewQueries = append(distinctNewQueries, sd)
|
||||
@@ -1062,7 +1117,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) {
|
||||
pipeline := c.pgConn.StartPipeline(context.Background())
|
||||
pipeline := c.pgConn.StartPipeline(ctx)
|
||||
defer func() {
|
||||
if pbr != nil && pbr.err != nil {
|
||||
pipeline.Close()
|
||||
@@ -1115,11 +1170,11 @@ func (c *Conn) sendBatchExtendedWithDescription(ctx context.Context, b *Batch, d
|
||||
}
|
||||
|
||||
// Queue the queries.
|
||||
for _, bi := range b.queuedQueries {
|
||||
err := c.eqb.Build(c.typeMap, bi.sd, bi.arguments)
|
||||
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)
|
||||
err = fmt.Errorf("error building query %s: %w", bi.SQL, err)
|
||||
return &pipelineBatchResults{ctx: ctx, conn: c, err: err, closed: true}
|
||||
}
|
||||
|
||||
@@ -1164,7 +1219,15 @@ func (c *Conn) sanitizeForSimpleQuery(sql string, args ...any) (string, error) {
|
||||
return sanitize.SanitizeSQL(sql, valueArgs...)
|
||||
}
|
||||
|
||||
// LoadType inspects the database for typeName and produces a pgtype.Type suitable for registration.
|
||||
// LoadType inspects the database for typeName and produces a pgtype.Type suitable for registration. typeName must be
|
||||
// the name of a type where the underlying type(s) is already understood by pgx. It is for derived types. In particular,
|
||||
// typeName must be one of the following:
|
||||
// - An array type name of a type that is already registered. e.g. "_foo" when "foo" is registered.
|
||||
// - A composite type name where all field types are already registered.
|
||||
// - A domain type name where the base type is already registered.
|
||||
// - An enum type name.
|
||||
// - A range type name where the element type is already registered.
|
||||
// - A multirange type name where the element type is already registered.
|
||||
func (c *Conn) LoadType(ctx context.Context, typeName string) (*pgtype.Type, error) {
|
||||
var oid uint32
|
||||
|
||||
@@ -1307,17 +1370,17 @@ order by attnum`,
|
||||
}
|
||||
|
||||
func (c *Conn) deallocateInvalidatedCachedStatements(ctx context.Context) error {
|
||||
if c.pgConn.TxStatus() != 'I' {
|
||||
if txStatus := c.pgConn.TxStatus(); txStatus != 'I' && txStatus != 'T' {
|
||||
return nil
|
||||
}
|
||||
|
||||
if c.descriptionCache != nil {
|
||||
c.descriptionCache.HandleInvalidated()
|
||||
c.descriptionCache.RemoveInvalidated()
|
||||
}
|
||||
|
||||
var invalidatedStatements []*pgconn.StatementDescription
|
||||
if c.statementCache != nil {
|
||||
invalidatedStatements = c.statementCache.HandleInvalidated()
|
||||
invalidatedStatements = c.statementCache.GetInvalidated()
|
||||
}
|
||||
|
||||
if len(invalidatedStatements) == 0 {
|
||||
@@ -1329,7 +1392,6 @@ func (c *Conn) deallocateInvalidatedCachedStatements(ctx context.Context) error
|
||||
|
||||
for _, sd := range invalidatedStatements {
|
||||
pipeline.SendDeallocate(sd.Name)
|
||||
delete(c.preparedStatements, sd.Name)
|
||||
}
|
||||
|
||||
err := pipeline.Sync()
|
||||
@@ -1342,5 +1404,10 @@ func (c *Conn) deallocateInvalidatedCachedStatements(ctx context.Context) error
|
||||
return fmt.Errorf("failed to deallocate cached statement(s): %w", err)
|
||||
}
|
||||
|
||||
c.statementCache.RemoveInvalidated()
|
||||
for _, sd := range invalidatedStatements {
|
||||
delete(c.preparedStatements, sd.Name)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
27
vendor/github.com/jackc/pgx/v5/copy_from.go
generated
vendored
27
vendor/github.com/jackc/pgx/v5/copy_from.go
generated
vendored
@@ -64,6 +64,33 @@ func (cts *copyFromSlice) Err() error {
|
||||
return cts.err
|
||||
}
|
||||
|
||||
// CopyFromFunc returns a CopyFromSource interface that relies on nxtf for values.
|
||||
// nxtf returns rows until it either signals an 'end of data' by returning row=nil and err=nil,
|
||||
// or it returns an error. If nxtf returns an error, the copy is aborted.
|
||||
func CopyFromFunc(nxtf func() (row []any, err error)) CopyFromSource {
|
||||
return ©FromFunc{next: nxtf}
|
||||
}
|
||||
|
||||
type copyFromFunc struct {
|
||||
next func() ([]any, error)
|
||||
valueRow []any
|
||||
err error
|
||||
}
|
||||
|
||||
func (g *copyFromFunc) Next() bool {
|
||||
g.valueRow, g.err = g.next()
|
||||
// only return true if valueRow exists and no error
|
||||
return g.valueRow != nil && g.err == nil
|
||||
}
|
||||
|
||||
func (g *copyFromFunc) Values() ([]any, error) {
|
||||
return g.valueRow, g.err
|
||||
}
|
||||
|
||||
func (g *copyFromFunc) Err() error {
|
||||
return g.err
|
||||
}
|
||||
|
||||
// CopyFromSource is the interface used by *Conn.CopyFrom as the source for copy data.
|
||||
type CopyFromSource interface {
|
||||
// Next returns true if there is another row and makes the next row data
|
||||
|
||||
15
vendor/github.com/jackc/pgx/v5/doc.go
generated
vendored
15
vendor/github.com/jackc/pgx/v5/doc.go
generated
vendored
@@ -11,9 +11,10 @@ The primary way of establishing a connection is with [pgx.Connect]:
|
||||
|
||||
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
|
||||
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.
|
||||
The database connection string can be in URL or key/value 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 [ConnectConfig] to configure settings such as tracing that cannot be configured with a connection
|
||||
string.
|
||||
|
||||
Connection Pool
|
||||
|
||||
@@ -23,8 +24,8 @@ github.com/jackc/pgx/v5/pgxpool for a concurrency safe connection pool.
|
||||
Query Interface
|
||||
|
||||
pgx implements Query in the familiar database/sql style. However, pgx provides generic functions such as CollectRows and
|
||||
ForEachRow that are a simpler and safer way of processing rows than manually calling rows.Next(), rows.Scan, and
|
||||
rows.Err().
|
||||
ForEachRow that are a simpler and safer way of processing rows than manually calling defer rows.Close(), rows.Next(),
|
||||
rows.Scan, and rows.Err().
|
||||
|
||||
CollectRows can be used collect all returned rows into a slice.
|
||||
|
||||
@@ -174,7 +175,7 @@ notification is received or the context is canceled.
|
||||
|
||||
Tracing and Logging
|
||||
|
||||
pgx supports tracing by setting ConnConfig.Tracer.
|
||||
pgx supports tracing by setting ConnConfig.Tracer. To combine several tracers you can use the multitracer.Tracer.
|
||||
|
||||
In addition, the tracelog package provides the TraceLog type which lets a traditional logger act as a Tracer.
|
||||
|
||||
@@ -187,7 +188,7 @@ implemented on top of pgconn. The Conn.PgConn() method can be used to access thi
|
||||
|
||||
PgBouncer
|
||||
|
||||
By default pgx automatically uses prepared statements. Prepared statements are incompaptible with PgBouncer. This can be
|
||||
By default pgx automatically uses prepared statements. Prepared statements are incompatible with PgBouncer. This can be
|
||||
disabled by setting a different QueryExecMode in ConnConfig.DefaultQueryExecMode.
|
||||
*/
|
||||
package pgx
|
||||
|
||||
90
vendor/github.com/jackc/pgx/v5/extended_query_builder.go
generated
vendored
90
vendor/github.com/jackc/pgx/v5/extended_query_builder.go
generated
vendored
@@ -1,10 +1,8 @@
|
||||
package pgx
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
|
||||
"github.com/jackc/pgx/v5/internal/anynil"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
@@ -23,10 +21,15 @@ type ExtendedQueryBuilder struct {
|
||||
func (eqb *ExtendedQueryBuilder) Build(m *pgtype.Map, sd *pgconn.StatementDescription, args []any) error {
|
||||
eqb.reset()
|
||||
|
||||
anynil.NormalizeSlice(args)
|
||||
|
||||
if sd == nil {
|
||||
return eqb.appendParamsForQueryExecModeExec(m, args)
|
||||
for i := range args {
|
||||
err := eqb.appendParam(m, 0, pgtype.TextFormatCode, args[i])
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to encode args[%d]: %w", i, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(sd.ParamOIDs) != len(args) {
|
||||
@@ -36,7 +39,7 @@ func (eqb *ExtendedQueryBuilder) Build(m *pgtype.Map, sd *pgconn.StatementDescri
|
||||
for i := range args {
|
||||
err := eqb.appendParam(m, sd.ParamOIDs[i], -1, args[i])
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to encode args[%d]: %v", i, err)
|
||||
err = fmt.Errorf("failed to encode args[%d]: %w", i, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -113,10 +116,6 @@ func (eqb *ExtendedQueryBuilder) reset() {
|
||||
}
|
||||
|
||||
func (eqb *ExtendedQueryBuilder) encodeExtendedParamValue(m *pgtype.Map, oid uint32, formatCode int16, arg any) ([]byte, error) {
|
||||
if anynil.Is(arg) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if eqb.paramValueBytes == nil {
|
||||
eqb.paramValueBytes = make([]byte, 0, 128)
|
||||
}
|
||||
@@ -145,74 +144,3 @@ func (eqb *ExtendedQueryBuilder) chooseParameterFormatCode(m *pgtype.Map, oid ui
|
||||
|
||||
return m.FormatCodeForOID(oid)
|
||||
}
|
||||
|
||||
// appendParamsForQueryExecModeExec appends the args to eqb.
|
||||
//
|
||||
// Parameters must be encoded in the text format because of differences in type conversion between timestamps and
|
||||
// dates. In QueryExecModeExec we don't know what the actual PostgreSQL type is. To determine the type we use the
|
||||
// Go type to OID type mapping registered by RegisterDefaultPgType. However, the Go time.Time represents both
|
||||
// PostgreSQL timestamp[tz] and date. To use the binary format we would need to also specify what the PostgreSQL
|
||||
// type OID is. But that would mean telling PostgreSQL that we have sent a timestamp[tz] when what is needed is a date.
|
||||
// This means that the value is converted from text to timestamp[tz] to date. This means it does a time zone conversion
|
||||
// before converting it to date. This means that dates can be shifted by one day. In text format without that double
|
||||
// type conversion it takes the date directly and ignores time zone (i.e. it works).
|
||||
//
|
||||
// Given that the whole point of QueryExecModeExec is to operate without having to know the PostgreSQL types there is
|
||||
// no way to safely use binary or to specify the parameter OIDs.
|
||||
func (eqb *ExtendedQueryBuilder) appendParamsForQueryExecModeExec(m *pgtype.Map, args []any) error {
|
||||
for _, arg := range args {
|
||||
if arg == nil {
|
||||
err := eqb.appendParam(m, 0, TextFormatCode, arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
dt, ok := m.TypeForValue(arg)
|
||||
if !ok {
|
||||
var tv pgtype.TextValuer
|
||||
if tv, ok = arg.(pgtype.TextValuer); ok {
|
||||
t, err := tv.TextValue()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dt, ok = m.TypeForOID(pgtype.TextOID)
|
||||
if ok {
|
||||
arg = t
|
||||
}
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
var dv driver.Valuer
|
||||
if dv, ok = arg.(driver.Valuer); ok {
|
||||
v, err := dv.Value()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dt, ok = m.TypeForValue(v)
|
||||
if ok {
|
||||
arg = v
|
||||
}
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
var str fmt.Stringer
|
||||
if str, ok = arg.(fmt.Stringer); ok {
|
||||
dt, ok = m.TypeForOID(pgtype.TextOID)
|
||||
if ok {
|
||||
arg = str.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
return &unknownArgumentTypeQueryExecModeExecError{arg: arg}
|
||||
}
|
||||
err := eqb.appendParam(m, dt.OID, TextFormatCode, arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
36
vendor/github.com/jackc/pgx/v5/internal/anynil/anynil.go
generated
vendored
36
vendor/github.com/jackc/pgx/v5/internal/anynil/anynil.go
generated
vendored
@@ -1,36 +0,0 @@
|
||||
package anynil
|
||||
|
||||
import "reflect"
|
||||
|
||||
// Is returns true if value is any type of nil. e.g. nil or []byte(nil).
|
||||
func Is(value any) bool {
|
||||
if value == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
refVal := reflect.ValueOf(value)
|
||||
switch refVal.Kind() {
|
||||
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Interface, reflect.Slice:
|
||||
return refVal.IsNil()
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize converts typed nils (e.g. []byte(nil)) into untyped nil. Other values are returned unmodified.
|
||||
func Normalize(v any) any {
|
||||
if Is(v) {
|
||||
return nil
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// NormalizeSlice converts all typed nils (e.g. []byte(nil)) in s into untyped nils. Other values are unmodified. s is
|
||||
// mutated in place.
|
||||
func NormalizeSlice(s []any) {
|
||||
for i := range s {
|
||||
if Is(s[i]) {
|
||||
s[i] = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
9
vendor/github.com/jackc/pgx/v5/internal/sanitize/sanitize.go
generated
vendored
9
vendor/github.com/jackc/pgx/v5/internal/sanitize/sanitize.go
generated
vendored
@@ -35,6 +35,11 @@ func (q *Query) Sanitize(args ...any) (string, error) {
|
||||
str = part
|
||||
case int:
|
||||
argIdx := part - 1
|
||||
|
||||
if argIdx < 0 {
|
||||
return "", fmt.Errorf("first sql argument must be > 0")
|
||||
}
|
||||
|
||||
if argIdx >= len(args) {
|
||||
return "", fmt.Errorf("insufficient arguments")
|
||||
}
|
||||
@@ -58,6 +63,10 @@ func (q *Query) Sanitize(args ...any) (string, error) {
|
||||
return "", fmt.Errorf("invalid arg type: %T", arg)
|
||||
}
|
||||
argUse[argIdx] = true
|
||||
|
||||
// Prevent SQL injection via Line Comment Creation
|
||||
// https://github.com/jackc/pgx/security/advisories/GHSA-m7wr-2xf7-cm9p
|
||||
str = " " + str + " "
|
||||
default:
|
||||
return "", fmt.Errorf("invalid Part type: %T", part)
|
||||
}
|
||||
|
||||
22
vendor/github.com/jackc/pgx/v5/internal/stmtcache/lru_cache.go
generated
vendored
22
vendor/github.com/jackc/pgx/v5/internal/stmtcache/lru_cache.go
generated
vendored
@@ -34,7 +34,8 @@ func (c *LRUCache) Get(key string) *pgconn.StatementDescription {
|
||||
|
||||
}
|
||||
|
||||
// Put stores sd in the cache. Put panics if sd.SQL is "". Put does nothing if sd.SQL already exists in the cache.
|
||||
// Put stores sd in the cache. Put panics if sd.SQL is "". Put does nothing if sd.SQL already exists in the cache or
|
||||
// sd.SQL has been invalidated and HandleInvalidated has not been called yet.
|
||||
func (c *LRUCache) Put(sd *pgconn.StatementDescription) {
|
||||
if sd.SQL == "" {
|
||||
panic("cannot store statement description with empty SQL")
|
||||
@@ -44,6 +45,13 @@ func (c *LRUCache) Put(sd *pgconn.StatementDescription) {
|
||||
return
|
||||
}
|
||||
|
||||
// The statement may have been invalidated but not yet handled. Do not readd it to the cache.
|
||||
for _, invalidSD := range c.invalidStmts {
|
||||
if invalidSD.SQL == sd.SQL {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if c.l.Len() == c.cap {
|
||||
c.invalidateOldest()
|
||||
}
|
||||
@@ -73,10 +81,16 @@ func (c *LRUCache) InvalidateAll() {
|
||||
c.l = list.New()
|
||||
}
|
||||
|
||||
func (c *LRUCache) HandleInvalidated() []*pgconn.StatementDescription {
|
||||
invalidStmts := c.invalidStmts
|
||||
// GetInvalidated returns a slice of all statement descriptions invalidated since the last call to RemoveInvalidated.
|
||||
func (c *LRUCache) GetInvalidated() []*pgconn.StatementDescription {
|
||||
return c.invalidStmts
|
||||
}
|
||||
|
||||
// RemoveInvalidated removes all invalidated statement descriptions. No other calls to Cache must be made between a
|
||||
// call to GetInvalidated and RemoveInvalidated or RemoveInvalidated may remove statement descriptions that were
|
||||
// never seen by the call to GetInvalidated.
|
||||
func (c *LRUCache) RemoveInvalidated() {
|
||||
c.invalidStmts = nil
|
||||
return invalidStmts
|
||||
}
|
||||
|
||||
// Len returns the number of cached prepared statement descriptions.
|
||||
|
||||
40
vendor/github.com/jackc/pgx/v5/internal/stmtcache/stmtcache.go
generated
vendored
40
vendor/github.com/jackc/pgx/v5/internal/stmtcache/stmtcache.go
generated
vendored
@@ -2,18 +2,17 @@
|
||||
package stmtcache
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
)
|
||||
|
||||
var stmtCounter int64
|
||||
|
||||
// NextStatementName returns a statement name that will be unique for the lifetime of the program.
|
||||
func NextStatementName() string {
|
||||
n := atomic.AddInt64(&stmtCounter, 1)
|
||||
return "stmtcache_" + strconv.FormatInt(n, 10)
|
||||
// StatementName returns a statement name that will be stable for sql across multiple connections and program
|
||||
// executions.
|
||||
func StatementName(sql string) string {
|
||||
digest := sha256.Sum256([]byte(sql))
|
||||
return "stmtcache_" + hex.EncodeToString(digest[0:24])
|
||||
}
|
||||
|
||||
// Cache caches statement descriptions.
|
||||
@@ -30,8 +29,13 @@ type Cache interface {
|
||||
// InvalidateAll invalidates all statement descriptions.
|
||||
InvalidateAll()
|
||||
|
||||
// HandleInvalidated returns a slice of all statement descriptions invalidated since the last call to HandleInvalidated.
|
||||
HandleInvalidated() []*pgconn.StatementDescription
|
||||
// GetInvalidated returns a slice of all statement descriptions invalidated since the last call to RemoveInvalidated.
|
||||
GetInvalidated() []*pgconn.StatementDescription
|
||||
|
||||
// RemoveInvalidated removes all invalidated statement descriptions. No other calls to Cache must be made between a
|
||||
// call to GetInvalidated and RemoveInvalidated or RemoveInvalidated may remove statement descriptions that were
|
||||
// never seen by the call to GetInvalidated.
|
||||
RemoveInvalidated()
|
||||
|
||||
// Len returns the number of cached prepared statement descriptions.
|
||||
Len() int
|
||||
@@ -39,19 +43,3 @@ type Cache interface {
|
||||
// Cap returns the maximum number of cached prepared statement descriptions.
|
||||
Cap() int
|
||||
}
|
||||
|
||||
func IsStatementInvalid(err error) bool {
|
||||
pgErr, ok := err.(*pgconn.PgError)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// https://github.com/jackc/pgx/issues/1162
|
||||
//
|
||||
// We used to look for the message "cached plan must not change result type". However, that message can be localized.
|
||||
// Unfortunately, error code "0A000" - "FEATURE NOT SUPPORTED" is used for many different errors and the only way to
|
||||
// tell the difference is by the message. But all that happens is we clear a statement that we otherwise wouldn't
|
||||
// have so it should be safe.
|
||||
possibleInvalidCachedPlanError := pgErr.Code == "0A000"
|
||||
return possibleInvalidCachedPlanError
|
||||
}
|
||||
|
||||
12
vendor/github.com/jackc/pgx/v5/internal/stmtcache/unlimited_cache.go
generated
vendored
12
vendor/github.com/jackc/pgx/v5/internal/stmtcache/unlimited_cache.go
generated
vendored
@@ -54,10 +54,16 @@ func (c *UnlimitedCache) InvalidateAll() {
|
||||
c.m = make(map[string]*pgconn.StatementDescription)
|
||||
}
|
||||
|
||||
func (c *UnlimitedCache) HandleInvalidated() []*pgconn.StatementDescription {
|
||||
invalidStmts := c.invalidStmts
|
||||
// GetInvalidated returns a slice of all statement descriptions invalidated since the last call to RemoveInvalidated.
|
||||
func (c *UnlimitedCache) GetInvalidated() []*pgconn.StatementDescription {
|
||||
return c.invalidStmts
|
||||
}
|
||||
|
||||
// RemoveInvalidated removes all invalidated statement descriptions. No other calls to Cache must be made between a
|
||||
// call to GetInvalidated and RemoveInvalidated or RemoveInvalidated may remove statement descriptions that were
|
||||
// never seen by the call to GetInvalidated.
|
||||
func (c *UnlimitedCache) RemoveInvalidated() {
|
||||
c.invalidStmts = nil
|
||||
return invalidStmts
|
||||
}
|
||||
|
||||
// Len returns the number of cached prepared statement descriptions.
|
||||
|
||||
76
vendor/github.com/jackc/pgx/v5/large_objects.go
generated
vendored
76
vendor/github.com/jackc/pgx/v5/large_objects.go
generated
vendored
@@ -4,8 +4,15 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
// The PostgreSQL wire protocol has a limit of 1 GB - 1 per message. See definition of
|
||||
// PQ_LARGE_MESSAGE_LIMIT in the PostgreSQL source code. To allow for the other data
|
||||
// in the message,maxLargeObjectMessageLength should be no larger than 1 GB - 1 KB.
|
||||
var maxLargeObjectMessageLength = 1024*1024*1024 - 1024
|
||||
|
||||
// LargeObjects is a structure used to access the large objects API. It is only valid within the transaction where it
|
||||
// was created.
|
||||
//
|
||||
@@ -68,32 +75,65 @@ type LargeObject struct {
|
||||
|
||||
// Write writes p to the large object and returns the number of bytes written and an error if not all of p was written.
|
||||
func (o *LargeObject) Write(p []byte) (int, error) {
|
||||
var n int
|
||||
err := o.tx.QueryRow(o.ctx, "select lowrite($1, $2)", o.fd, p).Scan(&n)
|
||||
if err != nil {
|
||||
return n, err
|
||||
nTotal := 0
|
||||
for {
|
||||
expected := len(p) - nTotal
|
||||
if expected == 0 {
|
||||
break
|
||||
} else if expected > maxLargeObjectMessageLength {
|
||||
expected = maxLargeObjectMessageLength
|
||||
}
|
||||
|
||||
var n int
|
||||
err := o.tx.QueryRow(o.ctx, "select lowrite($1, $2)", o.fd, p[nTotal:nTotal+expected]).Scan(&n)
|
||||
if err != nil {
|
||||
return nTotal, err
|
||||
}
|
||||
|
||||
if n < 0 {
|
||||
return nTotal, errors.New("failed to write to large object")
|
||||
}
|
||||
|
||||
nTotal += n
|
||||
|
||||
if n < expected {
|
||||
return nTotal, errors.New("short write to large object")
|
||||
} else if n > expected {
|
||||
return nTotal, errors.New("invalid write to large object")
|
||||
}
|
||||
}
|
||||
|
||||
if n < 0 {
|
||||
return 0, errors.New("failed to write to large object")
|
||||
}
|
||||
|
||||
return n, nil
|
||||
return nTotal, nil
|
||||
}
|
||||
|
||||
// Read reads up to len(p) bytes into p returning the number of bytes read.
|
||||
func (o *LargeObject) Read(p []byte) (int, error) {
|
||||
var res []byte
|
||||
err := o.tx.QueryRow(o.ctx, "select loread($1, $2)", o.fd, len(p)).Scan(&res)
|
||||
copy(p, res)
|
||||
if err != nil {
|
||||
return len(res), err
|
||||
nTotal := 0
|
||||
for {
|
||||
expected := len(p) - nTotal
|
||||
if expected == 0 {
|
||||
break
|
||||
} else if expected > maxLargeObjectMessageLength {
|
||||
expected = maxLargeObjectMessageLength
|
||||
}
|
||||
|
||||
res := pgtype.PreallocBytes(p[nTotal:])
|
||||
err := o.tx.QueryRow(o.ctx, "select loread($1, $2)", o.fd, expected).Scan(&res)
|
||||
// We compute expected so that it always fits into p, so it should never happen
|
||||
// that PreallocBytes's ScanBytes had to allocate a new slice.
|
||||
nTotal += len(res)
|
||||
if err != nil {
|
||||
return nTotal, err
|
||||
}
|
||||
|
||||
if len(res) < expected {
|
||||
return nTotal, io.EOF
|
||||
} else if len(res) > expected {
|
||||
return nTotal, errors.New("invalid read of large object")
|
||||
}
|
||||
}
|
||||
|
||||
if len(res) < len(p) {
|
||||
err = io.EOF
|
||||
}
|
||||
return len(res), err
|
||||
return nTotal, nil
|
||||
}
|
||||
|
||||
// Seek moves the current location pointer to the new location specified by offset.
|
||||
|
||||
63
vendor/github.com/jackc/pgx/v5/named_args.go
generated
vendored
63
vendor/github.com/jackc/pgx/v5/named_args.go
generated
vendored
@@ -2,6 +2,7 @@ package pgx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
@@ -14,10 +15,41 @@ import (
|
||||
//
|
||||
// conn.Query(ctx, "select * from widgets where foo = @foo and bar = @bar", pgx.NamedArgs{"foo": 1, "bar": 2})
|
||||
// conn.Query(ctx, "select * from widgets where foo = $1 and bar = $2", 1, 2)
|
||||
//
|
||||
// Named placeholders are case sensitive and must start with a letter or underscore. Subsequent characters can be
|
||||
// letters, numbers, or underscores.
|
||||
type NamedArgs map[string]any
|
||||
|
||||
// RewriteQuery implements the QueryRewriter interface.
|
||||
func (na NamedArgs) RewriteQuery(ctx context.Context, conn *Conn, sql string, args []any) (newSQL string, newArgs []any, err error) {
|
||||
return rewriteQuery(na, sql, false)
|
||||
}
|
||||
|
||||
// StrictNamedArgs can be used in the same way as NamedArgs, but provided arguments are also checked to include all
|
||||
// named arguments that the sql query uses, and no extra arguments.
|
||||
type StrictNamedArgs map[string]any
|
||||
|
||||
// RewriteQuery implements the QueryRewriter interface.
|
||||
func (sna StrictNamedArgs) RewriteQuery(ctx context.Context, conn *Conn, sql string, args []any) (newSQL string, newArgs []any, err error) {
|
||||
return rewriteQuery(sna, sql, true)
|
||||
}
|
||||
|
||||
type namedArg string
|
||||
|
||||
type sqlLexer struct {
|
||||
src string
|
||||
start int
|
||||
pos int
|
||||
nested int // multiline comment nesting level.
|
||||
stateFn stateFn
|
||||
parts []any
|
||||
|
||||
nameToOrdinal map[namedArg]int
|
||||
}
|
||||
|
||||
type stateFn func(*sqlLexer) stateFn
|
||||
|
||||
func rewriteQuery(na map[string]any, sql string, isStrict bool) (newSQL string, newArgs []any, err error) {
|
||||
l := &sqlLexer{
|
||||
src: sql,
|
||||
stateFn: rawState,
|
||||
@@ -41,27 +73,24 @@ func (na NamedArgs) RewriteQuery(ctx context.Context, conn *Conn, sql string, ar
|
||||
|
||||
newArgs = make([]any, len(l.nameToOrdinal))
|
||||
for name, ordinal := range l.nameToOrdinal {
|
||||
newArgs[ordinal-1] = na[string(name)]
|
||||
var found bool
|
||||
newArgs[ordinal-1], found = na[string(name)]
|
||||
if isStrict && !found {
|
||||
return "", nil, fmt.Errorf("argument %s found in sql query but not present in StrictNamedArgs", name)
|
||||
}
|
||||
}
|
||||
|
||||
if isStrict {
|
||||
for name := range na {
|
||||
if _, found := l.nameToOrdinal[namedArg(name)]; !found {
|
||||
return "", nil, fmt.Errorf("argument %s of StrictNamedArgs not found in sql query", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sb.String(), newArgs, nil
|
||||
}
|
||||
|
||||
type namedArg string
|
||||
|
||||
type sqlLexer struct {
|
||||
src string
|
||||
start int
|
||||
pos int
|
||||
nested int // multiline comment nesting level.
|
||||
stateFn stateFn
|
||||
parts []any
|
||||
|
||||
nameToOrdinal map[namedArg]int
|
||||
}
|
||||
|
||||
type stateFn func(*sqlLexer) stateFn
|
||||
|
||||
func rawState(l *sqlLexer) stateFn {
|
||||
for {
|
||||
r, width := utf8.DecodeRuneInString(l.src[l.pos:])
|
||||
@@ -80,7 +109,7 @@ func rawState(l *sqlLexer) stateFn {
|
||||
return doubleQuoteState
|
||||
case '@':
|
||||
nextRune, _ := utf8.DecodeRuneInString(l.src[l.pos:])
|
||||
if isLetter(nextRune) {
|
||||
if isLetter(nextRune) || nextRune == '_' {
|
||||
if l.pos-l.start > 0 {
|
||||
l.parts = append(l.parts, l.src[l.start:l.pos-width])
|
||||
}
|
||||
|
||||
4
vendor/github.com/jackc/pgx/v5/pgconn/auth_scram.go
generated
vendored
4
vendor/github.com/jackc/pgx/v5/pgconn/auth_scram.go
generated
vendored
@@ -47,7 +47,7 @@ func (c *PgConn) scramAuth(serverAuthMechanisms []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Receive server-first-message payload in a AuthenticationSASLContinue.
|
||||
// Receive server-first-message payload in an AuthenticationSASLContinue.
|
||||
saslContinue, err := c.rxSASLContinue()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -67,7 +67,7 @@ func (c *PgConn) scramAuth(serverAuthMechanisms []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Receive server-final-message payload in a AuthenticationSASLFinal.
|
||||
// Receive server-final-message payload in an AuthenticationSASLFinal.
|
||||
saslFinal, err := c.rxSASLFinal()
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
147
vendor/github.com/jackc/pgx/v5/pgconn/config.go
generated
vendored
147
vendor/github.com/jackc/pgx/v5/pgconn/config.go
generated
vendored
@@ -19,6 +19,7 @@ import (
|
||||
|
||||
"github.com/jackc/pgpassfile"
|
||||
"github.com/jackc/pgservicefile"
|
||||
"github.com/jackc/pgx/v5/pgconn/ctxwatch"
|
||||
"github.com/jackc/pgx/v5/pgproto3"
|
||||
)
|
||||
|
||||
@@ -39,7 +40,12 @@ type Config struct {
|
||||
DialFunc DialFunc // e.g. net.Dialer.DialContext
|
||||
LookupFunc LookupFunc // e.g. net.Resolver.LookupHost
|
||||
BuildFrontend BuildFrontendFunc
|
||||
RuntimeParams map[string]string // Run-time parameters to set on connection as session default values (e.g. search_path or application_name)
|
||||
|
||||
// BuildContextWatcherHandler is called to create a ContextWatcherHandler for a connection. The handler is called
|
||||
// when a context passed to a PgConn method is canceled.
|
||||
BuildContextWatcherHandler func(*PgConn) ctxwatch.Handler
|
||||
|
||||
RuntimeParams map[string]string // Run-time parameters to set on connection as session default values (e.g. search_path or application_name)
|
||||
|
||||
KerberosSrvName string
|
||||
KerberosSpn string
|
||||
@@ -60,12 +66,17 @@ type Config struct {
|
||||
// OnNotification is a callback function called when a notification from the LISTEN/NOTIFY system is received.
|
||||
OnNotification NotificationHandler
|
||||
|
||||
// OnPgError is a callback function called when a Postgres error is received by the server. The default handler will close
|
||||
// the connection on any FATAL errors. If you override this handler you should call the previously set handler or ensure
|
||||
// that you close on FATAL errors by returning false.
|
||||
OnPgError PgErrorHandler
|
||||
|
||||
createdByParseConfig bool // Used to enforce created by ParseConfig rule.
|
||||
}
|
||||
|
||||
// ParseConfigOptions contains options that control how a config is built such as GetSSLPassword.
|
||||
type ParseConfigOptions struct {
|
||||
// GetSSLPassword gets the password to decrypt a SSL client certificate. This is analogous to the the libpq function
|
||||
// GetSSLPassword gets the password to decrypt a SSL client certificate. This is analogous to the libpq function
|
||||
// PQsetSSLKeyPassHook_OpenSSL.
|
||||
GetSSLPassword GetSSLPasswordFunc
|
||||
}
|
||||
@@ -107,6 +118,14 @@ type FallbackConfig struct {
|
||||
TLSConfig *tls.Config // nil disables TLS
|
||||
}
|
||||
|
||||
// connectOneConfig is the configuration for a single attempt to connect to a single host.
|
||||
type connectOneConfig struct {
|
||||
network string
|
||||
address string
|
||||
originalHostname string // original hostname before resolving
|
||||
tlsConfig *tls.Config // nil disables TLS
|
||||
}
|
||||
|
||||
// isAbsolutePath checks if the provided value is an absolute path either
|
||||
// beginning with a forward slash (as on Linux-based systems) or with a capital
|
||||
// letter A-Z followed by a colon and a backslash, e.g., "C:\", (as on Windows).
|
||||
@@ -141,11 +160,11 @@ func NetworkAddress(host string, port uint16) (network, address string) {
|
||||
|
||||
// ParseConfig builds a *Config from connString with similar behavior to the PostgreSQL standard C library libpq. It
|
||||
// uses the same defaults as libpq (e.g. port=5432) and understands most PG* environment variables. ParseConfig closely
|
||||
// matches the parsing behavior of libpq. connString may either be in URL format or keyword = value format (DSN style).
|
||||
// See https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING for details. connString also may be
|
||||
// empty to only read from the environment. If a password is not supplied it will attempt to read the .pgpass file.
|
||||
// matches the parsing behavior of libpq. connString may either be in URL format or keyword = value format. See
|
||||
// https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING for details. connString also may be empty
|
||||
// to only read from the environment. If a password is not supplied it will attempt to read the .pgpass file.
|
||||
//
|
||||
// # Example DSN
|
||||
// # Example Keyword/Value
|
||||
// user=jack password=secret host=pg.example.com port=5432 dbname=mydb sslmode=verify-ca
|
||||
//
|
||||
// # Example URL
|
||||
@@ -164,7 +183,7 @@ func NetworkAddress(host string, port uint16) (network, address string) {
|
||||
// postgres://jack:secret@foo.example.com:5432,bar.example.com:5432/mydb
|
||||
//
|
||||
// ParseConfig currently recognizes the following environment variable and their parameter key word equivalents passed
|
||||
// via database URL or DSN:
|
||||
// via database URL or keyword/value:
|
||||
//
|
||||
// PGHOST
|
||||
// PGPORT
|
||||
@@ -228,16 +247,16 @@ func ParseConfigWithOptions(connString string, options ParseConfigOptions) (*Con
|
||||
connStringSettings := make(map[string]string)
|
||||
if connString != "" {
|
||||
var err error
|
||||
// connString may be a database URL or a DSN
|
||||
// connString may be a database URL or in PostgreSQL keyword/value format
|
||||
if strings.HasPrefix(connString, "postgres://") || strings.HasPrefix(connString, "postgresql://") {
|
||||
connStringSettings, err = parseURLSettings(connString)
|
||||
if err != nil {
|
||||
return nil, &parseConfigError{connString: connString, msg: "failed to parse as URL", err: err}
|
||||
return nil, &ParseConfigError{ConnString: connString, msg: "failed to parse as URL", err: err}
|
||||
}
|
||||
} else {
|
||||
connStringSettings, err = parseDSNSettings(connString)
|
||||
connStringSettings, err = parseKeywordValueSettings(connString)
|
||||
if err != nil {
|
||||
return nil, &parseConfigError{connString: connString, msg: "failed to parse as DSN", err: err}
|
||||
return nil, &ParseConfigError{ConnString: connString, msg: "failed to parse as keyword/value", err: err}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -246,7 +265,7 @@ func ParseConfigWithOptions(connString string, options ParseConfigOptions) (*Con
|
||||
if service, present := settings["service"]; present {
|
||||
serviceSettings, err := parseServiceSettings(settings["servicefile"], service)
|
||||
if err != nil {
|
||||
return nil, &parseConfigError{connString: connString, msg: "failed to read service", err: err}
|
||||
return nil, &ParseConfigError{ConnString: connString, msg: "failed to read service", err: err}
|
||||
}
|
||||
|
||||
settings = mergeSettings(defaultSettings, envSettings, serviceSettings, connStringSettings)
|
||||
@@ -261,12 +280,22 @@ func ParseConfigWithOptions(connString string, options ParseConfigOptions) (*Con
|
||||
BuildFrontend: func(r io.Reader, w io.Writer) *pgproto3.Frontend {
|
||||
return pgproto3.NewFrontend(r, w)
|
||||
},
|
||||
BuildContextWatcherHandler: func(pgConn *PgConn) ctxwatch.Handler {
|
||||
return &DeadlineContextWatcherHandler{Conn: pgConn.conn}
|
||||
},
|
||||
OnPgError: func(_ *PgConn, pgErr *PgError) bool {
|
||||
// we want to automatically close any fatal errors
|
||||
if strings.EqualFold(pgErr.Severity, "FATAL") {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
}
|
||||
|
||||
if connectTimeoutSetting, present := settings["connect_timeout"]; present {
|
||||
connectTimeout, err := parseConnectTimeoutSetting(connectTimeoutSetting)
|
||||
if err != nil {
|
||||
return nil, &parseConfigError{connString: connString, msg: "invalid connect_timeout", err: err}
|
||||
return nil, &ParseConfigError{ConnString: connString, msg: "invalid connect_timeout", err: err}
|
||||
}
|
||||
config.ConnectTimeout = connectTimeout
|
||||
config.DialFunc = makeConnectTimeoutDialFunc(connectTimeout)
|
||||
@@ -328,7 +357,7 @@ func ParseConfigWithOptions(connString string, options ParseConfigOptions) (*Con
|
||||
|
||||
port, err := parsePort(portStr)
|
||||
if err != nil {
|
||||
return nil, &parseConfigError{connString: connString, msg: "invalid port", err: err}
|
||||
return nil, &ParseConfigError{ConnString: connString, msg: "invalid port", err: err}
|
||||
}
|
||||
|
||||
var tlsConfigs []*tls.Config
|
||||
@@ -340,7 +369,7 @@ func ParseConfigWithOptions(connString string, options ParseConfigOptions) (*Con
|
||||
var err error
|
||||
tlsConfigs, err = configTLS(settings, host, options)
|
||||
if err != nil {
|
||||
return nil, &parseConfigError{connString: connString, msg: "failed to configure TLS", err: err}
|
||||
return nil, &ParseConfigError{ConnString: connString, msg: "failed to configure TLS", err: err}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,7 +413,7 @@ func ParseConfigWithOptions(connString string, options ParseConfigOptions) (*Con
|
||||
case "any":
|
||||
// do nothing
|
||||
default:
|
||||
return nil, &parseConfigError{connString: connString, msg: fmt.Sprintf("unknown target_session_attrs value: %v", tsa)}
|
||||
return nil, &ParseConfigError{ConnString: connString, msg: fmt.Sprintf("unknown target_session_attrs value: %v", tsa)}
|
||||
}
|
||||
|
||||
return config, nil
|
||||
@@ -438,14 +467,17 @@ func parseEnvSettings() map[string]string {
|
||||
func parseURLSettings(connString string) (map[string]string, error) {
|
||||
settings := make(map[string]string)
|
||||
|
||||
url, err := url.Parse(connString)
|
||||
parsedURL, err := url.Parse(connString)
|
||||
if err != nil {
|
||||
if urlErr := new(url.Error); errors.As(err, &urlErr) {
|
||||
return nil, urlErr.Err
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if url.User != nil {
|
||||
settings["user"] = url.User.Username()
|
||||
if password, present := url.User.Password(); present {
|
||||
if parsedURL.User != nil {
|
||||
settings["user"] = parsedURL.User.Username()
|
||||
if password, present := parsedURL.User.Password(); present {
|
||||
settings["password"] = password
|
||||
}
|
||||
}
|
||||
@@ -453,7 +485,7 @@ func parseURLSettings(connString string) (map[string]string, error) {
|
||||
// Handle multiple host:port's in url.Host by splitting them into host,host,host and port,port,port.
|
||||
var hosts []string
|
||||
var ports []string
|
||||
for _, host := range strings.Split(url.Host, ",") {
|
||||
for _, host := range strings.Split(parsedURL.Host, ",") {
|
||||
if host == "" {
|
||||
continue
|
||||
}
|
||||
@@ -479,7 +511,7 @@ func parseURLSettings(connString string) (map[string]string, error) {
|
||||
settings["port"] = strings.Join(ports, ",")
|
||||
}
|
||||
|
||||
database := strings.TrimLeft(url.Path, "/")
|
||||
database := strings.TrimLeft(parsedURL.Path, "/")
|
||||
if database != "" {
|
||||
settings["database"] = database
|
||||
}
|
||||
@@ -488,7 +520,7 @@ func parseURLSettings(connString string) (map[string]string, error) {
|
||||
"dbname": "database",
|
||||
}
|
||||
|
||||
for k, v := range url.Query() {
|
||||
for k, v := range parsedURL.Query() {
|
||||
if k2, present := nameMap[k]; present {
|
||||
k = k2
|
||||
}
|
||||
@@ -505,7 +537,7 @@ func isIPOnly(host string) bool {
|
||||
|
||||
var asciiSpace = [256]uint8{'\t': 1, '\n': 1, '\v': 1, '\f': 1, '\r': 1, ' ': 1}
|
||||
|
||||
func parseDSNSettings(s string) (map[string]string, error) {
|
||||
func parseKeywordValueSettings(s string) (map[string]string, error) {
|
||||
settings := make(map[string]string)
|
||||
|
||||
nameMap := map[string]string{
|
||||
@@ -516,7 +548,7 @@ func parseDSNSettings(s string) (map[string]string, error) {
|
||||
var key, val string
|
||||
eqIdx := strings.IndexRune(s, '=')
|
||||
if eqIdx < 0 {
|
||||
return nil, errors.New("invalid dsn")
|
||||
return nil, errors.New("invalid keyword/value")
|
||||
}
|
||||
|
||||
key = strings.Trim(s[:eqIdx], " \t\n\r\v\f")
|
||||
@@ -568,7 +600,7 @@ func parseDSNSettings(s string) (map[string]string, error) {
|
||||
}
|
||||
|
||||
if key == "" {
|
||||
return nil, errors.New("invalid dsn")
|
||||
return nil, errors.New("invalid keyword/value")
|
||||
}
|
||||
|
||||
settings[key] = val
|
||||
@@ -625,6 +657,36 @@ func configTLS(settings map[string]string, thisHost string, parseConfigOptions P
|
||||
|
||||
tlsConfig := &tls.Config{}
|
||||
|
||||
if sslrootcert != "" {
|
||||
var caCertPool *x509.CertPool
|
||||
|
||||
if sslrootcert == "system" {
|
||||
var err error
|
||||
|
||||
caCertPool, err = x509.SystemCertPool()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to load system certificate pool: %w", err)
|
||||
}
|
||||
|
||||
sslmode = "verify-full"
|
||||
} else {
|
||||
caCertPool = x509.NewCertPool()
|
||||
|
||||
caPath := sslrootcert
|
||||
caCert, err := os.ReadFile(caPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read CA file: %w", err)
|
||||
}
|
||||
|
||||
if !caCertPool.AppendCertsFromPEM(caCert) {
|
||||
return nil, errors.New("unable to add CA to cert pool")
|
||||
}
|
||||
}
|
||||
|
||||
tlsConfig.RootCAs = caCertPool
|
||||
tlsConfig.ClientCAs = caCertPool
|
||||
}
|
||||
|
||||
switch sslmode {
|
||||
case "disable":
|
||||
return []*tls.Config{nil}, nil
|
||||
@@ -682,23 +744,6 @@ func configTLS(settings map[string]string, thisHost string, parseConfigOptions P
|
||||
return nil, errors.New("sslmode is invalid")
|
||||
}
|
||||
|
||||
if sslrootcert != "" {
|
||||
caCertPool := x509.NewCertPool()
|
||||
|
||||
caPath := sslrootcert
|
||||
caCert, err := os.ReadFile(caPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read CA file: %w", err)
|
||||
}
|
||||
|
||||
if !caCertPool.AppendCertsFromPEM(caCert) {
|
||||
return nil, errors.New("unable to add CA to cert pool")
|
||||
}
|
||||
|
||||
tlsConfig.RootCAs = caCertPool
|
||||
tlsConfig.ClientCAs = caCertPool
|
||||
}
|
||||
|
||||
if (sslcert != "" && sslkey == "") || (sslcert == "" && sslkey != "") {
|
||||
return nil, errors.New(`both "sslcert" and "sslkey" are required`)
|
||||
}
|
||||
@@ -709,6 +754,9 @@ func configTLS(settings map[string]string, thisHost string, parseConfigOptions P
|
||||
return nil, fmt.Errorf("unable to read sslkey: %w", err)
|
||||
}
|
||||
block, _ := pem.Decode(buf)
|
||||
if block == nil {
|
||||
return nil, errors.New("failed to decode sslkey")
|
||||
}
|
||||
var pemKey []byte
|
||||
var decryptedKey []byte
|
||||
var decryptedError error
|
||||
@@ -785,7 +833,8 @@ func parsePort(s string) (uint16, error) {
|
||||
}
|
||||
|
||||
func makeDefaultDialer() *net.Dialer {
|
||||
return &net.Dialer{KeepAlive: 5 * time.Minute}
|
||||
// rely on GOLANG KeepAlive settings
|
||||
return &net.Dialer{}
|
||||
}
|
||||
|
||||
func makeDefaultResolver() *net.Resolver {
|
||||
@@ -809,7 +858,7 @@ func makeConnectTimeoutDialFunc(timeout time.Duration) DialFunc {
|
||||
return d.DialContext
|
||||
}
|
||||
|
||||
// ValidateConnectTargetSessionAttrsReadWrite is an ValidateConnectFunc that implements libpq compatible
|
||||
// ValidateConnectTargetSessionAttrsReadWrite is a ValidateConnectFunc that implements libpq compatible
|
||||
// target_session_attrs=read-write.
|
||||
func ValidateConnectTargetSessionAttrsReadWrite(ctx context.Context, pgConn *PgConn) error {
|
||||
result := pgConn.ExecParams(ctx, "show transaction_read_only", nil, nil, nil, nil).Read()
|
||||
@@ -824,7 +873,7 @@ func ValidateConnectTargetSessionAttrsReadWrite(ctx context.Context, pgConn *PgC
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateConnectTargetSessionAttrsReadOnly is an ValidateConnectFunc that implements libpq compatible
|
||||
// ValidateConnectTargetSessionAttrsReadOnly is a ValidateConnectFunc that implements libpq compatible
|
||||
// target_session_attrs=read-only.
|
||||
func ValidateConnectTargetSessionAttrsReadOnly(ctx context.Context, pgConn *PgConn) error {
|
||||
result := pgConn.ExecParams(ctx, "show transaction_read_only", nil, nil, nil, nil).Read()
|
||||
@@ -839,7 +888,7 @@ func ValidateConnectTargetSessionAttrsReadOnly(ctx context.Context, pgConn *PgCo
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateConnectTargetSessionAttrsStandby is an ValidateConnectFunc that implements libpq compatible
|
||||
// ValidateConnectTargetSessionAttrsStandby is a ValidateConnectFunc that implements libpq compatible
|
||||
// target_session_attrs=standby.
|
||||
func ValidateConnectTargetSessionAttrsStandby(ctx context.Context, pgConn *PgConn) error {
|
||||
result := pgConn.ExecParams(ctx, "select pg_is_in_recovery()", nil, nil, nil, nil).Read()
|
||||
@@ -854,7 +903,7 @@ func ValidateConnectTargetSessionAttrsStandby(ctx context.Context, pgConn *PgCon
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateConnectTargetSessionAttrsPrimary is an ValidateConnectFunc that implements libpq compatible
|
||||
// ValidateConnectTargetSessionAttrsPrimary is a ValidateConnectFunc that implements libpq compatible
|
||||
// target_session_attrs=primary.
|
||||
func ValidateConnectTargetSessionAttrsPrimary(ctx context.Context, pgConn *PgConn) error {
|
||||
result := pgConn.ExecParams(ctx, "select pg_is_in_recovery()", nil, nil, nil, nil).Read()
|
||||
@@ -869,7 +918,7 @@ func ValidateConnectTargetSessionAttrsPrimary(ctx context.Context, pgConn *PgCon
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateConnectTargetSessionAttrsPreferStandby is an ValidateConnectFunc that implements libpq compatible
|
||||
// ValidateConnectTargetSessionAttrsPreferStandby is a ValidateConnectFunc that implements libpq compatible
|
||||
// target_session_attrs=prefer-standby.
|
||||
func ValidateConnectTargetSessionAttrsPreferStandby(ctx context.Context, pgConn *PgConn) error {
|
||||
result := pgConn.ExecParams(ctx, "select pg_is_in_recovery()", nil, nil, nil, nil).Read()
|
||||
|
||||
16
vendor/github.com/jackc/pgx/v5/pgconn/doc.go
generated
vendored
16
vendor/github.com/jackc/pgx/v5/pgconn/doc.go
generated
vendored
@@ -5,8 +5,8 @@ nearly the same level is the C library libpq.
|
||||
|
||||
Establishing a Connection
|
||||
|
||||
Use Connect to establish a connection. It accepts a connection string in URL or DSN and will read the environment for
|
||||
libpq style environment variables.
|
||||
Use Connect to establish a connection. It accepts a connection string in URL or keyword/value format and will read the
|
||||
environment for libpq style environment variables.
|
||||
|
||||
Executing a Query
|
||||
|
||||
@@ -20,13 +20,17 @@ result. The ReadAll method reads all query results into memory.
|
||||
|
||||
Pipeline Mode
|
||||
|
||||
Pipeline mode allows sending queries without having read the results of previously sent queries. It allows
|
||||
control of exactly how many and when network round trips occur.
|
||||
Pipeline mode allows sending queries without having read the results of previously sent queries. It allows control of
|
||||
exactly how many and when network round trips occur.
|
||||
|
||||
Context Support
|
||||
|
||||
All potentially blocking operations take a context.Context. If a context is canceled while the method is in progress the
|
||||
method immediately returns. In most circumstances, this will close the underlying connection.
|
||||
All potentially blocking operations take a context.Context. The default behavior when a context is canceled is for the
|
||||
method to immediately return. In most circumstances, this will also close the underlying connection. This behavior can
|
||||
be customized by using BuildContextWatcherHandler on the Config to create a ctxwatch.Handler with different behavior.
|
||||
This can be especially useful when queries that are frequently canceled and the overhead of creating new connections is
|
||||
a problem. DeadlineContextWatcherHandler and CancelRequestContextWatcherHandler can be used to introduce a delay before
|
||||
interrupting the query in such a way as to close the connection.
|
||||
|
||||
The CancelRequest method may be used to request the PostgreSQL server cancel an in-progress query without forcing the
|
||||
client to abort.
|
||||
|
||||
104
vendor/github.com/jackc/pgx/v5/pgconn/errors.go
generated
vendored
104
vendor/github.com/jackc/pgx/v5/pgconn/errors.go
generated
vendored
@@ -12,13 +12,14 @@ import (
|
||||
|
||||
// SafeToRetry checks if the err is guaranteed to have occurred before sending any data to the server.
|
||||
func SafeToRetry(err error) bool {
|
||||
if e, ok := err.(interface{ SafeToRetry() bool }); ok {
|
||||
return e.SafeToRetry()
|
||||
var retryableErr interface{ SafeToRetry() bool }
|
||||
if errors.As(err, &retryableErr) {
|
||||
return retryableErr.SafeToRetry()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Timeout checks if err was was caused by a timeout. To be specific, it is true if err was caused within pgconn by a
|
||||
// Timeout checks if err was caused by a timeout. To be specific, it is true if err was caused within pgconn by a
|
||||
// context.DeadlineExceeded or an implementer of net.Error where Timeout() is true.
|
||||
func Timeout(err error) bool {
|
||||
var timeoutErr *errTimeout
|
||||
@@ -29,23 +30,24 @@ func Timeout(err error) bool {
|
||||
// http://www.postgresql.org/docs/11/static/protocol-error-fields.html for
|
||||
// detailed field description.
|
||||
type PgError struct {
|
||||
Severity string
|
||||
Code string
|
||||
Message string
|
||||
Detail string
|
||||
Hint string
|
||||
Position int32
|
||||
InternalPosition int32
|
||||
InternalQuery string
|
||||
Where string
|
||||
SchemaName string
|
||||
TableName string
|
||||
ColumnName string
|
||||
DataTypeName string
|
||||
ConstraintName string
|
||||
File string
|
||||
Line int32
|
||||
Routine string
|
||||
Severity string
|
||||
SeverityUnlocalized string
|
||||
Code string
|
||||
Message string
|
||||
Detail string
|
||||
Hint string
|
||||
Position int32
|
||||
InternalPosition int32
|
||||
InternalQuery string
|
||||
Where string
|
||||
SchemaName string
|
||||
TableName string
|
||||
ColumnName string
|
||||
DataTypeName string
|
||||
ConstraintName string
|
||||
File string
|
||||
Line int32
|
||||
Routine string
|
||||
}
|
||||
|
||||
func (pe *PgError) Error() string {
|
||||
@@ -57,22 +59,37 @@ func (pe *PgError) SQLState() string {
|
||||
return pe.Code
|
||||
}
|
||||
|
||||
type connectError struct {
|
||||
config *Config
|
||||
msg string
|
||||
// ConnectError is the error returned when a connection attempt fails.
|
||||
type ConnectError struct {
|
||||
Config *Config // The configuration that was used in the connection attempt.
|
||||
err error
|
||||
}
|
||||
|
||||
func (e *connectError) Error() string {
|
||||
sb := &strings.Builder{}
|
||||
fmt.Fprintf(sb, "failed to connect to `host=%s user=%s database=%s`: %s", e.config.Host, e.config.User, e.config.Database, e.msg)
|
||||
if e.err != nil {
|
||||
fmt.Fprintf(sb, " (%s)", e.err.Error())
|
||||
func (e *ConnectError) Error() string {
|
||||
prefix := fmt.Sprintf("failed to connect to `user=%s database=%s`:", e.Config.User, e.Config.Database)
|
||||
details := e.err.Error()
|
||||
if strings.Contains(details, "\n") {
|
||||
return prefix + "\n\t" + strings.ReplaceAll(details, "\n", "\n\t")
|
||||
} else {
|
||||
return prefix + " " + details
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func (e *connectError) Unwrap() error {
|
||||
func (e *ConnectError) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
type perDialConnectError struct {
|
||||
address string
|
||||
originalHostname string
|
||||
err error
|
||||
}
|
||||
|
||||
func (e *perDialConnectError) Error() string {
|
||||
return fmt.Sprintf("%s (%s): %s", e.address, e.originalHostname, e.err.Error())
|
||||
}
|
||||
|
||||
func (e *perDialConnectError) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
@@ -88,33 +105,38 @@ func (e *connLockError) Error() string {
|
||||
return e.status
|
||||
}
|
||||
|
||||
type parseConfigError struct {
|
||||
connString string
|
||||
// ParseConfigError is the error returned when a connection string cannot be parsed.
|
||||
type ParseConfigError struct {
|
||||
ConnString string // The connection string that could not be parsed.
|
||||
msg string
|
||||
err error
|
||||
}
|
||||
|
||||
func (e *parseConfigError) Error() string {
|
||||
connString := redactPW(e.connString)
|
||||
func (e *ParseConfigError) Error() string {
|
||||
// Now that ParseConfigError is public and ConnString is available to the developer, perhaps it would be better only
|
||||
// return a static string. That would ensure that the error message cannot leak a password. The ConnString field would
|
||||
// allow access to the original string if desired and Unwrap would allow access to the underlying error.
|
||||
connString := redactPW(e.ConnString)
|
||||
if e.err == nil {
|
||||
return fmt.Sprintf("cannot parse `%s`: %s", connString, e.msg)
|
||||
}
|
||||
return fmt.Sprintf("cannot parse `%s`: %s (%s)", connString, e.msg, e.err.Error())
|
||||
}
|
||||
|
||||
func (e *parseConfigError) Unwrap() error {
|
||||
func (e *ParseConfigError) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
func normalizeTimeoutError(ctx context.Context, err error) error {
|
||||
if err, ok := err.(net.Error); ok && err.Timeout() {
|
||||
var netErr net.Error
|
||||
if errors.As(err, &netErr) && netErr.Timeout() {
|
||||
if ctx.Err() == context.Canceled {
|
||||
// Since the timeout was caused by a context cancellation, the actual error is context.Canceled not the timeout error.
|
||||
return context.Canceled
|
||||
} else if ctx.Err() == context.DeadlineExceeded {
|
||||
return &errTimeout{err: ctx.Err()}
|
||||
} else {
|
||||
return &errTimeout{err: err}
|
||||
return &errTimeout{err: netErr}
|
||||
}
|
||||
}
|
||||
return err
|
||||
@@ -189,10 +211,10 @@ func redactPW(connString string) string {
|
||||
return redactURL(u)
|
||||
}
|
||||
}
|
||||
quotedDSN := regexp.MustCompile(`password='[^']*'`)
|
||||
connString = quotedDSN.ReplaceAllLiteralString(connString, "password=xxxxx")
|
||||
plainDSN := regexp.MustCompile(`password=[^ ]*`)
|
||||
connString = plainDSN.ReplaceAllLiteralString(connString, "password=xxxxx")
|
||||
quotedKV := regexp.MustCompile(`password='[^']*'`)
|
||||
connString = quotedKV.ReplaceAllLiteralString(connString, "password=xxxxx")
|
||||
plainKV := regexp.MustCompile(`password=[^ ]*`)
|
||||
connString = plainKV.ReplaceAllLiteralString(connString, "password=xxxxx")
|
||||
brokenURL := regexp.MustCompile(`:[^:@]+?@`)
|
||||
connString = brokenURL.ReplaceAllLiteralString(connString, ":xxxxxx@")
|
||||
return connString
|
||||
|
||||
73
vendor/github.com/jackc/pgx/v5/pgconn/internal/ctxwatch/context_watcher.go
generated
vendored
73
vendor/github.com/jackc/pgx/v5/pgconn/internal/ctxwatch/context_watcher.go
generated
vendored
@@ -1,73 +0,0 @@
|
||||
package ctxwatch
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ContextWatcher watches a context and performs an action when the context is canceled. It can watch one context at a
|
||||
// time.
|
||||
type ContextWatcher struct {
|
||||
onCancel func()
|
||||
onUnwatchAfterCancel func()
|
||||
unwatchChan chan struct{}
|
||||
|
||||
lock sync.Mutex
|
||||
watchInProgress bool
|
||||
onCancelWasCalled bool
|
||||
}
|
||||
|
||||
// NewContextWatcher returns a ContextWatcher. onCancel will be called when a watched context is canceled.
|
||||
// OnUnwatchAfterCancel will be called when Unwatch is called and the watched context had already been canceled and
|
||||
// onCancel called.
|
||||
func NewContextWatcher(onCancel func(), onUnwatchAfterCancel func()) *ContextWatcher {
|
||||
cw := &ContextWatcher{
|
||||
onCancel: onCancel,
|
||||
onUnwatchAfterCancel: onUnwatchAfterCancel,
|
||||
unwatchChan: make(chan struct{}),
|
||||
}
|
||||
|
||||
return cw
|
||||
}
|
||||
|
||||
// Watch starts watching ctx. If ctx is canceled then the onCancel function passed to NewContextWatcher will be called.
|
||||
func (cw *ContextWatcher) Watch(ctx context.Context) {
|
||||
cw.lock.Lock()
|
||||
defer cw.lock.Unlock()
|
||||
|
||||
if cw.watchInProgress {
|
||||
panic("Watch already in progress")
|
||||
}
|
||||
|
||||
cw.onCancelWasCalled = false
|
||||
|
||||
if ctx.Done() != nil {
|
||||
cw.watchInProgress = true
|
||||
go func() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
cw.onCancel()
|
||||
cw.onCancelWasCalled = true
|
||||
<-cw.unwatchChan
|
||||
case <-cw.unwatchChan:
|
||||
}
|
||||
}()
|
||||
} else {
|
||||
cw.watchInProgress = false
|
||||
}
|
||||
}
|
||||
|
||||
// Unwatch stops watching the previously watched context. If the onCancel function passed to NewContextWatcher was
|
||||
// called then onUnwatchAfterCancel will also be called.
|
||||
func (cw *ContextWatcher) Unwatch() {
|
||||
cw.lock.Lock()
|
||||
defer cw.lock.Unlock()
|
||||
|
||||
if cw.watchInProgress {
|
||||
cw.unwatchChan <- struct{}{}
|
||||
if cw.onCancelWasCalled {
|
||||
cw.onUnwatchAfterCancel()
|
||||
}
|
||||
cw.watchInProgress = false
|
||||
}
|
||||
}
|
||||
608
vendor/github.com/jackc/pgx/v5/pgconn/pgconn.go
generated
vendored
608
vendor/github.com/jackc/pgx/v5/pgconn/pgconn.go
generated
vendored
@@ -18,8 +18,8 @@ import (
|
||||
|
||||
"github.com/jackc/pgx/v5/internal/iobufpool"
|
||||
"github.com/jackc/pgx/v5/internal/pgio"
|
||||
"github.com/jackc/pgx/v5/pgconn/ctxwatch"
|
||||
"github.com/jackc/pgx/v5/pgconn/internal/bgreader"
|
||||
"github.com/jackc/pgx/v5/pgconn/internal/ctxwatch"
|
||||
"github.com/jackc/pgx/v5/pgproto3"
|
||||
)
|
||||
|
||||
@@ -52,6 +52,12 @@ type LookupFunc func(ctx context.Context, host string) (addrs []string, err erro
|
||||
// BuildFrontendFunc is a function that can be used to create Frontend implementation for connection.
|
||||
type BuildFrontendFunc func(r io.Reader, w io.Writer) *pgproto3.Frontend
|
||||
|
||||
// PgErrorHandler is a function that handles errors returned from Postgres. This function must return true to keep
|
||||
// the connection open. Returning false will cause the connection to be closed immediately. You should return
|
||||
// false on any FATAL-severity errors. This will not receive network errors. The *PgConn is provided so the handler is
|
||||
// aware of the origin of the error, but it must not invoke any query method.
|
||||
type PgErrorHandler func(*PgConn, *PgError) bool
|
||||
|
||||
// NoticeHandler is a function that can handle notices received from the PostgreSQL server. Notices can be received at
|
||||
// any time, usually during handling of a query response. The *PgConn is provided so the handler is aware of the origin
|
||||
// of the notice, but it must not invoke any query method. Be aware that this is distinct from LISTEN/NOTIFY
|
||||
@@ -74,6 +80,9 @@ type PgConn struct {
|
||||
frontend *pgproto3.Frontend
|
||||
bgReader *bgreader.BGReader
|
||||
slowWriteTimer *time.Timer
|
||||
bgReaderStarted chan struct{}
|
||||
|
||||
customData map[string]any
|
||||
|
||||
config *Config
|
||||
|
||||
@@ -96,8 +105,9 @@ type PgConn struct {
|
||||
cleanupDone chan struct{}
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Connect establishes a connection to a PostgreSQL server using the environment and connString (in URL or keyword/value
|
||||
// format) 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) {
|
||||
config, err := ParseConfig(connString)
|
||||
if err != nil {
|
||||
@@ -107,9 +117,9 @@ func Connect(ctx context.Context, connString string) (*PgConn, error) {
|
||||
return ConnectConfig(ctx, config)
|
||||
}
|
||||
|
||||
// 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
|
||||
// used to cancel a connect attempt.
|
||||
// Connect establishes a connection to a PostgreSQL server using the environment and connString (in URL or keyword/value
|
||||
// format) and ParseConfigOptions to provide additional configuration. See documentation for [ParseConfig] for details.
|
||||
// ctx can be used to cancel a connect attempt.
|
||||
func ConnectWithOptions(ctx context.Context, connString string, parseConfigOptions ParseConfigOptions) (*PgConn, error) {
|
||||
config, err := ParseConfigWithOptions(connString, parseConfigOptions)
|
||||
if err != nil {
|
||||
@@ -124,15 +134,46 @@ func ConnectWithOptions(ctx context.Context, connString string, parseConfigOptio
|
||||
//
|
||||
// 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:
|
||||
// https://www.postgresql.org/docs/11/libpq-connect.html#LIBPQ-MULTIPLE-HOSTS) and be returned as the error. Otherwise,
|
||||
// if all attempts fail the last error is returned.
|
||||
func ConnectConfig(octx context.Context, config *Config) (pgConn *PgConn, err error) {
|
||||
// https://www.postgresql.org/docs/11/libpq-connect.html#LIBPQ-MULTIPLE-HOSTS) and be returned as the error.
|
||||
func ConnectConfig(ctx context.Context, config *Config) (*PgConn, error) {
|
||||
// Default values are set in ParseConfig. Enforce initial creation by ParseConfig rather than setting defaults from
|
||||
// zero values.
|
||||
if !config.createdByParseConfig {
|
||||
panic("config must be created by ParseConfig")
|
||||
}
|
||||
|
||||
var allErrors []error
|
||||
|
||||
connectConfigs, errs := buildConnectOneConfigs(ctx, config)
|
||||
if len(errs) > 0 {
|
||||
allErrors = append(allErrors, errs...)
|
||||
}
|
||||
|
||||
if len(connectConfigs) == 0 {
|
||||
return nil, &ConnectError{Config: config, err: fmt.Errorf("hostname resolving error: %w", errors.Join(allErrors...))}
|
||||
}
|
||||
|
||||
pgConn, errs := connectPreferred(ctx, config, connectConfigs)
|
||||
if len(errs) > 0 {
|
||||
allErrors = append(allErrors, errs...)
|
||||
return nil, &ConnectError{Config: config, err: errors.Join(allErrors...)}
|
||||
}
|
||||
|
||||
if config.AfterConnect != nil {
|
||||
err := config.AfterConnect(ctx, pgConn)
|
||||
if err != nil {
|
||||
pgConn.conn.Close()
|
||||
return nil, &ConnectError{Config: config, err: fmt.Errorf("AfterConnect error: %w", err)}
|
||||
}
|
||||
}
|
||||
|
||||
return pgConn, nil
|
||||
}
|
||||
|
||||
// buildConnectOneConfigs resolves hostnames and builds a list of connectOneConfigs to try connecting to. It returns a
|
||||
// slice of successfully resolved connectOneConfigs and a slice of errors. It is possible for both slices to contain
|
||||
// values if some hosts were successfully resolved and others were not.
|
||||
func buildConnectOneConfigs(ctx context.Context, config *Config) ([]*connectOneConfig, []error) {
|
||||
// Simplify usage by treating primary config and fallbacks the same.
|
||||
fallbackConfigs := []*FallbackConfig{
|
||||
{
|
||||
@@ -142,95 +183,28 @@ func ConnectConfig(octx context.Context, config *Config) (pgConn *PgConn, err er
|
||||
},
|
||||
}
|
||||
fallbackConfigs = append(fallbackConfigs, config.Fallbacks...)
|
||||
ctx := octx
|
||||
fallbackConfigs, err = expandWithIPs(ctx, config.LookupFunc, fallbackConfigs)
|
||||
if err != nil {
|
||||
return nil, &connectError{config: config, msg: "hostname resolving error", err: err}
|
||||
}
|
||||
|
||||
if len(fallbackConfigs) == 0 {
|
||||
return nil, &connectError{config: config, msg: "hostname resolving error", err: errors.New("ip addr wasn't found")}
|
||||
}
|
||||
var configs []*connectOneConfig
|
||||
|
||||
foundBestServer := false
|
||||
var fallbackConfig *FallbackConfig
|
||||
for i, fc := range fallbackConfigs {
|
||||
// ConnectTimeout restricts the whole connection process.
|
||||
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
|
||||
ctx, cancel = context.WithTimeout(octx, config.ConnectTimeout)
|
||||
defer cancel()
|
||||
}
|
||||
} else {
|
||||
ctx = octx
|
||||
}
|
||||
pgConn, err = connect(ctx, config, fc, false)
|
||||
if err == nil {
|
||||
foundBestServer = true
|
||||
break
|
||||
} else if pgerr, ok := err.(*PgError); ok {
|
||||
err = &connectError{config: config, msg: "server error", err: pgerr}
|
||||
const ERRCODE_INVALID_PASSWORD = "28P01" // wrong password
|
||||
const ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION = "28000" // wrong password or bad pg_hba.conf settings
|
||||
const ERRCODE_INVALID_CATALOG_NAME = "3D000" // db does not exist
|
||||
const ERRCODE_INSUFFICIENT_PRIVILEGE = "42501" // missing connect privilege
|
||||
if pgerr.Code == ERRCODE_INVALID_PASSWORD ||
|
||||
pgerr.Code == ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION && fc.TLSConfig != nil ||
|
||||
pgerr.Code == ERRCODE_INVALID_CATALOG_NAME ||
|
||||
pgerr.Code == ERRCODE_INSUFFICIENT_PRIVILEGE {
|
||||
break
|
||||
}
|
||||
} else if cerr, ok := err.(*connectError); ok {
|
||||
if _, ok := cerr.err.(*NotPreferredError); ok {
|
||||
fallbackConfig = fc
|
||||
}
|
||||
}
|
||||
}
|
||||
var allErrors []error
|
||||
|
||||
if !foundBestServer && fallbackConfig != nil {
|
||||
pgConn, err = connect(ctx, config, fallbackConfig, true)
|
||||
if pgerr, ok := err.(*PgError); ok {
|
||||
err = &connectError{config: config, msg: "server error", err: pgerr}
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err // no need to wrap in connectError because it will already be wrapped in all cases except PgError
|
||||
}
|
||||
|
||||
if config.AfterConnect != nil {
|
||||
err := config.AfterConnect(ctx, pgConn)
|
||||
if err != nil {
|
||||
pgConn.conn.Close()
|
||||
return nil, &connectError{config: config, msg: "AfterConnect error", err: err}
|
||||
}
|
||||
}
|
||||
|
||||
return pgConn, nil
|
||||
}
|
||||
|
||||
func expandWithIPs(ctx context.Context, lookupFn LookupFunc, fallbacks []*FallbackConfig) ([]*FallbackConfig, error) {
|
||||
var configs []*FallbackConfig
|
||||
|
||||
var lookupErrors []error
|
||||
|
||||
for _, fb := range fallbacks {
|
||||
for _, fb := range fallbackConfigs {
|
||||
// skip resolve for unix sockets
|
||||
if isAbsolutePath(fb.Host) {
|
||||
configs = append(configs, &FallbackConfig{
|
||||
Host: fb.Host,
|
||||
Port: fb.Port,
|
||||
TLSConfig: fb.TLSConfig,
|
||||
network, address := NetworkAddress(fb.Host, fb.Port)
|
||||
configs = append(configs, &connectOneConfig{
|
||||
network: network,
|
||||
address: address,
|
||||
originalHostname: fb.Host,
|
||||
tlsConfig: fb.TLSConfig,
|
||||
})
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
ips, err := lookupFn(ctx, fb.Host)
|
||||
ips, err := config.LookupFunc(ctx, fb.Host)
|
||||
if err != nil {
|
||||
lookupErrors = append(lookupErrors, err)
|
||||
allErrors = append(allErrors, err)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -239,70 +213,139 @@ func expandWithIPs(ctx context.Context, lookupFn LookupFunc, fallbacks []*Fallba
|
||||
if err == nil {
|
||||
port, err := strconv.ParseUint(splitPort, 10, 16)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing port (%s) from lookup: %w", splitPort, err)
|
||||
return nil, []error{fmt.Errorf("error parsing port (%s) from lookup: %w", splitPort, err)}
|
||||
}
|
||||
configs = append(configs, &FallbackConfig{
|
||||
Host: splitIP,
|
||||
Port: uint16(port),
|
||||
TLSConfig: fb.TLSConfig,
|
||||
network, address := NetworkAddress(splitIP, uint16(port))
|
||||
configs = append(configs, &connectOneConfig{
|
||||
network: network,
|
||||
address: address,
|
||||
originalHostname: fb.Host,
|
||||
tlsConfig: fb.TLSConfig,
|
||||
})
|
||||
} else {
|
||||
configs = append(configs, &FallbackConfig{
|
||||
Host: ip,
|
||||
Port: fb.Port,
|
||||
TLSConfig: fb.TLSConfig,
|
||||
network, address := NetworkAddress(ip, fb.Port)
|
||||
configs = append(configs, &connectOneConfig{
|
||||
network: network,
|
||||
address: address,
|
||||
originalHostname: fb.Host,
|
||||
tlsConfig: fb.TLSConfig,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
return configs, allErrors
|
||||
}
|
||||
|
||||
func connect(ctx context.Context, config *Config, fallbackConfig *FallbackConfig,
|
||||
// connectPreferred attempts to connect to the preferred host from connectOneConfigs. The connections are attempted in
|
||||
// order. If a connection is successful it is returned. If no connection is successful then all errors are returned. If
|
||||
// a connection attempt returns a [NotPreferredError], then that host will be used if no other hosts are successful.
|
||||
func connectPreferred(ctx context.Context, config *Config, connectOneConfigs []*connectOneConfig) (*PgConn, []error) {
|
||||
octx := ctx
|
||||
var allErrors []error
|
||||
|
||||
var fallbackConnectOneConfig *connectOneConfig
|
||||
for i, c := range connectOneConfigs {
|
||||
// ConnectTimeout restricts the whole connection process.
|
||||
if config.ConnectTimeout != 0 {
|
||||
// create new context first time or when previous host was different
|
||||
if i == 0 || (connectOneConfigs[i].address != connectOneConfigs[i-1].address) {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(octx, config.ConnectTimeout)
|
||||
defer cancel()
|
||||
}
|
||||
} else {
|
||||
ctx = octx
|
||||
}
|
||||
|
||||
pgConn, err := connectOne(ctx, config, c, false)
|
||||
if pgConn != nil {
|
||||
return pgConn, nil
|
||||
}
|
||||
|
||||
allErrors = append(allErrors, err)
|
||||
|
||||
var pgErr *PgError
|
||||
if errors.As(err, &pgErr) {
|
||||
const ERRCODE_INVALID_PASSWORD = "28P01" // wrong password
|
||||
const ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION = "28000" // wrong password or bad pg_hba.conf settings
|
||||
const ERRCODE_INVALID_CATALOG_NAME = "3D000" // db does not exist
|
||||
const ERRCODE_INSUFFICIENT_PRIVILEGE = "42501" // missing connect privilege
|
||||
if pgErr.Code == ERRCODE_INVALID_PASSWORD ||
|
||||
pgErr.Code == ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION && c.tlsConfig != nil ||
|
||||
pgErr.Code == ERRCODE_INVALID_CATALOG_NAME ||
|
||||
pgErr.Code == ERRCODE_INSUFFICIENT_PRIVILEGE {
|
||||
return nil, allErrors
|
||||
}
|
||||
}
|
||||
|
||||
var npErr *NotPreferredError
|
||||
if errors.As(err, &npErr) {
|
||||
fallbackConnectOneConfig = c
|
||||
}
|
||||
}
|
||||
|
||||
if fallbackConnectOneConfig != nil {
|
||||
pgConn, err := connectOne(ctx, config, fallbackConnectOneConfig, true)
|
||||
if err == nil {
|
||||
return pgConn, nil
|
||||
}
|
||||
allErrors = append(allErrors, err)
|
||||
}
|
||||
|
||||
return nil, allErrors
|
||||
}
|
||||
|
||||
// connectOne makes one connection attempt to a single host.
|
||||
func connectOne(ctx context.Context, config *Config, connectConfig *connectOneConfig,
|
||||
ignoreNotPreferredErr bool,
|
||||
) (*PgConn, error) {
|
||||
pgConn := new(PgConn)
|
||||
pgConn.config = config
|
||||
pgConn.cleanupDone = make(chan struct{})
|
||||
pgConn.customData = make(map[string]any)
|
||||
|
||||
var err error
|
||||
network, address := NetworkAddress(fallbackConfig.Host, fallbackConfig.Port)
|
||||
netConn, err := config.DialFunc(ctx, network, address)
|
||||
if err != nil {
|
||||
return nil, &connectError{config: config, msg: "dial error", err: normalizeTimeoutError(ctx, err)}
|
||||
|
||||
newPerDialConnectError := func(msg string, err error) *perDialConnectError {
|
||||
err = normalizeTimeoutError(ctx, err)
|
||||
e := &perDialConnectError{address: connectConfig.address, originalHostname: connectConfig.originalHostname, err: fmt.Errorf("%s: %w", msg, err)}
|
||||
return e
|
||||
}
|
||||
|
||||
pgConn.conn = netConn
|
||||
pgConn.contextWatcher = newContextWatcher(netConn)
|
||||
pgConn.contextWatcher.Watch(ctx)
|
||||
pgConn.conn, err = config.DialFunc(ctx, connectConfig.network, connectConfig.address)
|
||||
if err != nil {
|
||||
return nil, newPerDialConnectError("dial error", err)
|
||||
}
|
||||
|
||||
if fallbackConfig.TLSConfig != nil {
|
||||
nbTLSConn, err := startTLS(netConn, fallbackConfig.TLSConfig)
|
||||
if connectConfig.tlsConfig != nil {
|
||||
pgConn.contextWatcher = ctxwatch.NewContextWatcher(&DeadlineContextWatcherHandler{Conn: pgConn.conn})
|
||||
pgConn.contextWatcher.Watch(ctx)
|
||||
tlsConn, err := startTLS(pgConn.conn, connectConfig.tlsConfig)
|
||||
pgConn.contextWatcher.Unwatch() // Always unwatch `netConn` after TLS.
|
||||
if err != nil {
|
||||
netConn.Close()
|
||||
return nil, &connectError{config: config, msg: "tls error", err: err}
|
||||
pgConn.conn.Close()
|
||||
return nil, newPerDialConnectError("tls error", err)
|
||||
}
|
||||
|
||||
pgConn.conn = nbTLSConn
|
||||
pgConn.contextWatcher = newContextWatcher(nbTLSConn)
|
||||
pgConn.contextWatcher.Watch(ctx)
|
||||
pgConn.conn = tlsConn
|
||||
}
|
||||
|
||||
pgConn.contextWatcher = ctxwatch.NewContextWatcher(config.BuildContextWatcherHandler(pgConn))
|
||||
pgConn.contextWatcher.Watch(ctx)
|
||||
defer pgConn.contextWatcher.Unwatch()
|
||||
|
||||
pgConn.parameterStatuses = make(map[string]string)
|
||||
pgConn.status = connStatusConnecting
|
||||
pgConn.bgReader = bgreader.New(pgConn.conn)
|
||||
pgConn.slowWriteTimer = time.AfterFunc(time.Duration(math.MaxInt64), pgConn.bgReader.Start)
|
||||
pgConn.slowWriteTimer = time.AfterFunc(time.Duration(math.MaxInt64),
|
||||
func() {
|
||||
pgConn.bgReader.Start()
|
||||
pgConn.bgReaderStarted <- struct{}{}
|
||||
},
|
||||
)
|
||||
pgConn.slowWriteTimer.Stop()
|
||||
pgConn.bgReaderStarted = make(chan struct{})
|
||||
pgConn.frontend = config.BuildFrontend(pgConn.bgReader, pgConn.conn)
|
||||
|
||||
startupMsg := pgproto3.StartupMessage{
|
||||
@@ -323,7 +366,7 @@ func connect(ctx context.Context, config *Config, fallbackConfig *FallbackConfig
|
||||
pgConn.frontend.Send(&startupMsg)
|
||||
if err := pgConn.flushWithPotentialWriteReadDeadlock(); err != nil {
|
||||
pgConn.conn.Close()
|
||||
return nil, &connectError{config: config, msg: "failed to write startup message", err: normalizeTimeoutError(ctx, err)}
|
||||
return nil, newPerDialConnectError("failed to write startup message", err)
|
||||
}
|
||||
|
||||
for {
|
||||
@@ -331,9 +374,9 @@ func connect(ctx context.Context, config *Config, fallbackConfig *FallbackConfig
|
||||
if err != nil {
|
||||
pgConn.conn.Close()
|
||||
if err, ok := err.(*PgError); ok {
|
||||
return nil, err
|
||||
return nil, newPerDialConnectError("server error", err)
|
||||
}
|
||||
return nil, &connectError{config: config, msg: "failed to receive message", err: normalizeTimeoutError(ctx, err)}
|
||||
return nil, newPerDialConnectError("failed to receive message", err)
|
||||
}
|
||||
|
||||
switch msg := msg.(type) {
|
||||
@@ -346,26 +389,26 @@ func connect(ctx context.Context, config *Config, fallbackConfig *FallbackConfig
|
||||
err = pgConn.txPasswordMessage(pgConn.config.Password)
|
||||
if err != nil {
|
||||
pgConn.conn.Close()
|
||||
return nil, &connectError{config: config, msg: "failed to write password message", err: err}
|
||||
return nil, newPerDialConnectError("failed to write password message", err)
|
||||
}
|
||||
case *pgproto3.AuthenticationMD5Password:
|
||||
digestedPassword := "md5" + hexMD5(hexMD5(pgConn.config.Password+pgConn.config.User)+string(msg.Salt[:]))
|
||||
err = pgConn.txPasswordMessage(digestedPassword)
|
||||
if err != nil {
|
||||
pgConn.conn.Close()
|
||||
return nil, &connectError{config: config, msg: "failed to write password message", err: err}
|
||||
return nil, newPerDialConnectError("failed to write password message", err)
|
||||
}
|
||||
case *pgproto3.AuthenticationSASL:
|
||||
err = pgConn.scramAuth(msg.AuthMechanisms)
|
||||
if err != nil {
|
||||
pgConn.conn.Close()
|
||||
return nil, &connectError{config: config, msg: "failed SASL auth", err: err}
|
||||
return nil, newPerDialConnectError("failed SASL auth", err)
|
||||
}
|
||||
case *pgproto3.AuthenticationGSS:
|
||||
err = pgConn.gssAuth()
|
||||
if err != nil {
|
||||
pgConn.conn.Close()
|
||||
return nil, &connectError{config: config, msg: "failed GSS auth", err: err}
|
||||
return nil, newPerDialConnectError("failed GSS auth", err)
|
||||
}
|
||||
case *pgproto3.ReadyForQuery:
|
||||
pgConn.status = connStatusIdle
|
||||
@@ -383,7 +426,7 @@ func connect(ctx context.Context, config *Config, fallbackConfig *FallbackConfig
|
||||
return pgConn, nil
|
||||
}
|
||||
pgConn.conn.Close()
|
||||
return nil, &connectError{config: config, msg: "ValidateConnect failed", err: err}
|
||||
return nil, newPerDialConnectError("ValidateConnect failed", err)
|
||||
}
|
||||
}
|
||||
return pgConn, nil
|
||||
@@ -391,21 +434,14 @@ func connect(ctx context.Context, config *Config, fallbackConfig *FallbackConfig
|
||||
// handled by ReceiveMessage
|
||||
case *pgproto3.ErrorResponse:
|
||||
pgConn.conn.Close()
|
||||
return nil, ErrorResponseToPgError(msg)
|
||||
return nil, newPerDialConnectError("server error", ErrorResponseToPgError(msg))
|
||||
default:
|
||||
pgConn.conn.Close()
|
||||
return nil, &connectError{config: config, msg: "received unexpected message", err: err}
|
||||
return nil, newPerDialConnectError("received unexpected message", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newContextWatcher(conn net.Conn) *ctxwatch.ContextWatcher {
|
||||
return ctxwatch.NewContextWatcher(
|
||||
func() { conn.SetDeadline(time.Date(1, 1, 1, 1, 1, 1, 1, time.UTC)) },
|
||||
func() { conn.SetDeadline(time.Time{}) },
|
||||
)
|
||||
}
|
||||
|
||||
func startTLS(conn net.Conn, tlsConfig *tls.Config) (net.Conn, error) {
|
||||
err := binary.Write(conn, binary.BigEndian, []int32{8, 80877103})
|
||||
if err != nil {
|
||||
@@ -540,11 +576,12 @@ func (pgConn *PgConn) receiveMessage() (pgproto3.BackendMessage, error) {
|
||||
case *pgproto3.ParameterStatus:
|
||||
pgConn.parameterStatuses[msg.Name] = msg.Value
|
||||
case *pgproto3.ErrorResponse:
|
||||
if msg.Severity == "FATAL" {
|
||||
err := ErrorResponseToPgError(msg)
|
||||
if pgConn.config.OnPgError != nil && !pgConn.config.OnPgError(pgConn, err) {
|
||||
pgConn.status = connStatusClosed
|
||||
pgConn.conn.Close() // Ignore error as the connection is already broken and there is already an error to return.
|
||||
close(pgConn.cleanupDone)
|
||||
return nil, ErrorResponseToPgError(msg)
|
||||
return nil, err
|
||||
}
|
||||
case *pgproto3.NoticeResponse:
|
||||
if pgConn.config.OnNotice != nil {
|
||||
@@ -593,7 +630,7 @@ func (pgConn *PgConn) Frontend() *pgproto3.Frontend {
|
||||
return pgConn.frontend
|
||||
}
|
||||
|
||||
// Close closes a connection. It is safe to call Close on a already closed connection. Close attempts a clean close by
|
||||
// Close closes a connection. It is safe to call Close on an already closed connection. Close attempts a clean close by
|
||||
// sending the exit message to PostgreSQL. However, this could block so ctx is available to limit the time to wait. The
|
||||
// underlying net.Conn.Close() will always be called regardless of any other errors.
|
||||
func (pgConn *PgConn) Close(ctx context.Context) error {
|
||||
@@ -806,6 +843,9 @@ type StatementDescription struct {
|
||||
|
||||
// Prepare creates a prepared statement. If the name is empty, the anonymous prepared statement will be used. This
|
||||
// allows Prepare to also to describe statements without creating a server-side prepared statement.
|
||||
//
|
||||
// Prepare does not send a PREPARE statement to the server. It uses the PostgreSQL Parse and Describe protocol messages
|
||||
// directly.
|
||||
func (pgConn *PgConn) Prepare(ctx context.Context, name, sql string, paramOIDs []uint32) (*StatementDescription, error) {
|
||||
if err := pgConn.lock(); err != nil {
|
||||
return nil, err
|
||||
@@ -862,26 +902,73 @@ readloop:
|
||||
return psd, nil
|
||||
}
|
||||
|
||||
// Deallocate deallocates a prepared statement.
|
||||
//
|
||||
// Deallocate does not send a DEALLOCATE statement to the server. It uses the PostgreSQL Close protocol message
|
||||
// directly. This has slightly different behavior than executing DEALLOCATE statement.
|
||||
// - Deallocate can succeed in an aborted transaction.
|
||||
// - Deallocating a non-existent prepared statement is not an error.
|
||||
func (pgConn *PgConn) Deallocate(ctx context.Context, name string) error {
|
||||
if err := pgConn.lock(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer pgConn.unlock()
|
||||
|
||||
if ctx != context.Background() {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return newContextAlreadyDoneError(ctx)
|
||||
default:
|
||||
}
|
||||
pgConn.contextWatcher.Watch(ctx)
|
||||
defer pgConn.contextWatcher.Unwatch()
|
||||
}
|
||||
|
||||
pgConn.frontend.SendClose(&pgproto3.Close{ObjectType: 'S', Name: name})
|
||||
pgConn.frontend.SendSync(&pgproto3.Sync{})
|
||||
err := pgConn.flushWithPotentialWriteReadDeadlock()
|
||||
if err != nil {
|
||||
pgConn.asyncClose()
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
msg, err := pgConn.receiveMessage()
|
||||
if err != nil {
|
||||
pgConn.asyncClose()
|
||||
return normalizeTimeoutError(ctx, err)
|
||||
}
|
||||
|
||||
switch msg := msg.(type) {
|
||||
case *pgproto3.ErrorResponse:
|
||||
return ErrorResponseToPgError(msg)
|
||||
case *pgproto3.ReadyForQuery:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ErrorResponseToPgError converts a wire protocol error message to a *PgError.
|
||||
func ErrorResponseToPgError(msg *pgproto3.ErrorResponse) *PgError {
|
||||
return &PgError{
|
||||
Severity: msg.Severity,
|
||||
Code: string(msg.Code),
|
||||
Message: string(msg.Message),
|
||||
Detail: string(msg.Detail),
|
||||
Hint: msg.Hint,
|
||||
Position: msg.Position,
|
||||
InternalPosition: msg.InternalPosition,
|
||||
InternalQuery: string(msg.InternalQuery),
|
||||
Where: string(msg.Where),
|
||||
SchemaName: string(msg.SchemaName),
|
||||
TableName: string(msg.TableName),
|
||||
ColumnName: string(msg.ColumnName),
|
||||
DataTypeName: string(msg.DataTypeName),
|
||||
ConstraintName: msg.ConstraintName,
|
||||
File: string(msg.File),
|
||||
Line: msg.Line,
|
||||
Routine: string(msg.Routine),
|
||||
Severity: msg.Severity,
|
||||
SeverityUnlocalized: msg.SeverityUnlocalized,
|
||||
Code: string(msg.Code),
|
||||
Message: string(msg.Message),
|
||||
Detail: string(msg.Detail),
|
||||
Hint: msg.Hint,
|
||||
Position: msg.Position,
|
||||
InternalPosition: msg.InternalPosition,
|
||||
InternalQuery: string(msg.InternalQuery),
|
||||
Where: string(msg.Where),
|
||||
SchemaName: string(msg.SchemaName),
|
||||
TableName: string(msg.TableName),
|
||||
ColumnName: string(msg.ColumnName),
|
||||
DataTypeName: string(msg.DataTypeName),
|
||||
ConstraintName: msg.ConstraintName,
|
||||
File: string(msg.File),
|
||||
Line: msg.Line,
|
||||
Routine: string(msg.Routine),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -924,10 +1011,7 @@ func (pgConn *PgConn) CancelRequest(ctx context.Context) error {
|
||||
defer cancelConn.Close()
|
||||
|
||||
if ctx != context.Background() {
|
||||
contextWatcher := ctxwatch.NewContextWatcher(
|
||||
func() { cancelConn.SetDeadline(time.Date(1, 1, 1, 1, 1, 1, 1, time.UTC)) },
|
||||
func() { cancelConn.SetDeadline(time.Time{}) },
|
||||
)
|
||||
contextWatcher := ctxwatch.NewContextWatcher(&DeadlineContextWatcherHandler{Conn: cancelConn})
|
||||
contextWatcher.Watch(ctx)
|
||||
defer contextWatcher.Unwatch()
|
||||
}
|
||||
@@ -935,16 +1019,21 @@ func (pgConn *PgConn) CancelRequest(ctx context.Context) error {
|
||||
buf := make([]byte, 16)
|
||||
binary.BigEndian.PutUint32(buf[0:4], 16)
|
||||
binary.BigEndian.PutUint32(buf[4:8], 80877102)
|
||||
binary.BigEndian.PutUint32(buf[8:12], uint32(pgConn.pid))
|
||||
binary.BigEndian.PutUint32(buf[12:16], uint32(pgConn.secretKey))
|
||||
// Postgres will process the request and close the connection
|
||||
// so when don't need to read the reply
|
||||
// https://www.postgresql.org/docs/current/protocol-flow.html#id-1.10.6.7.10
|
||||
_, err = cancelConn.Write(buf)
|
||||
return err
|
||||
binary.BigEndian.PutUint32(buf[8:12], pgConn.pid)
|
||||
binary.BigEndian.PutUint32(buf[12:16], pgConn.secretKey)
|
||||
|
||||
if _, err := cancelConn.Write(buf); err != nil {
|
||||
return fmt.Errorf("write to connection for cancellation: %w", err)
|
||||
}
|
||||
|
||||
// Wait for the cancel request to be acknowledged by the server.
|
||||
// It copies the behavior of the libpq: https://github.com/postgres/postgres/blob/REL_16_0/src/interfaces/libpq/fe-connect.c#L4946-L4960
|
||||
_, _ = cancelConn.Read(buf)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WaitForNotification waits for a LISTON/NOTIFY message to be received. It returns an error if a notification was not
|
||||
// WaitForNotification waits for a LISTEN/NOTIFY message to be received. It returns an error if a notification was not
|
||||
// received.
|
||||
func (pgConn *PgConn) WaitForNotification(ctx context.Context) error {
|
||||
if err := pgConn.lock(); err != nil {
|
||||
@@ -1455,8 +1544,10 @@ func (rr *ResultReader) Read() *Result {
|
||||
values := rr.Values()
|
||||
row := make([][]byte, len(values))
|
||||
for i := range row {
|
||||
row[i] = make([]byte, len(values[i]))
|
||||
copy(row[i], values[i])
|
||||
if values[i] != nil {
|
||||
row[i] = make([]byte, len(values[i]))
|
||||
copy(row[i], values[i])
|
||||
}
|
||||
}
|
||||
br.Rows = append(br.Rows, row)
|
||||
}
|
||||
@@ -1606,25 +1697,55 @@ func (rr *ResultReader) concludeCommand(commandTag CommandTag, err error) {
|
||||
// Batch is a collection of queries that can be sent to the PostgreSQL server in a single round-trip.
|
||||
type Batch struct {
|
||||
buf []byte
|
||||
err error
|
||||
}
|
||||
|
||||
// ExecParams appends an ExecParams command to the batch. See PgConn.ExecParams for parameter descriptions.
|
||||
func (batch *Batch) ExecParams(sql string, paramValues [][]byte, paramOIDs []uint32, paramFormats []int16, resultFormats []int16) {
|
||||
batch.buf = (&pgproto3.Parse{Query: sql, ParameterOIDs: paramOIDs}).Encode(batch.buf)
|
||||
if batch.err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
batch.buf, batch.err = (&pgproto3.Parse{Query: sql, ParameterOIDs: paramOIDs}).Encode(batch.buf)
|
||||
if batch.err != nil {
|
||||
return
|
||||
}
|
||||
batch.ExecPrepared("", paramValues, paramFormats, resultFormats)
|
||||
}
|
||||
|
||||
// ExecPrepared appends an ExecPrepared e command to the batch. See PgConn.ExecPrepared for parameter descriptions.
|
||||
func (batch *Batch) ExecPrepared(stmtName string, paramValues [][]byte, paramFormats []int16, resultFormats []int16) {
|
||||
batch.buf = (&pgproto3.Bind{PreparedStatement: stmtName, ParameterFormatCodes: paramFormats, Parameters: paramValues, ResultFormatCodes: resultFormats}).Encode(batch.buf)
|
||||
batch.buf = (&pgproto3.Describe{ObjectType: 'P'}).Encode(batch.buf)
|
||||
batch.buf = (&pgproto3.Execute{}).Encode(batch.buf)
|
||||
if batch.err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
batch.buf, batch.err = (&pgproto3.Bind{PreparedStatement: stmtName, ParameterFormatCodes: paramFormats, Parameters: paramValues, ResultFormatCodes: resultFormats}).Encode(batch.buf)
|
||||
if batch.err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
batch.buf, batch.err = (&pgproto3.Describe{ObjectType: 'P'}).Encode(batch.buf)
|
||||
if batch.err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
batch.buf, batch.err = (&pgproto3.Execute{}).Encode(batch.buf)
|
||||
if batch.err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// ExecBatch executes all the queries in batch in a single round-trip. Execution is implicitly transactional unless a
|
||||
// transaction is already in progress or SQL contains transaction control statements. This is a simpler way of executing
|
||||
// multiple queries in a single round trip than using pipeline mode.
|
||||
func (pgConn *PgConn) ExecBatch(ctx context.Context, batch *Batch) *MultiResultReader {
|
||||
if batch.err != nil {
|
||||
return &MultiResultReader{
|
||||
closed: true,
|
||||
err: batch.err,
|
||||
}
|
||||
}
|
||||
|
||||
if err := pgConn.lock(); err != nil {
|
||||
return &MultiResultReader{
|
||||
closed: true,
|
||||
@@ -1650,7 +1771,13 @@ func (pgConn *PgConn) ExecBatch(ctx context.Context, batch *Batch) *MultiResultR
|
||||
pgConn.contextWatcher.Watch(ctx)
|
||||
}
|
||||
|
||||
batch.buf = (&pgproto3.Sync{}).Encode(batch.buf)
|
||||
batch.buf, batch.err = (&pgproto3.Sync{}).Encode(batch.buf)
|
||||
if batch.err != nil {
|
||||
multiResult.closed = true
|
||||
multiResult.err = batch.err
|
||||
pgConn.unlock()
|
||||
return multiResult
|
||||
}
|
||||
|
||||
pgConn.enterPotentialWriteReadDeadlock()
|
||||
defer pgConn.exitPotentialWriteReadDeadlock()
|
||||
@@ -1732,10 +1859,16 @@ func (pgConn *PgConn) enterPotentialWriteReadDeadlock() {
|
||||
|
||||
// exitPotentialWriteReadDeadlock must be called after a call to enterPotentialWriteReadDeadlock.
|
||||
func (pgConn *PgConn) exitPotentialWriteReadDeadlock() {
|
||||
// The state of the timer is not relevant upon exiting the potential slow write. It may both
|
||||
// fire (due to a slow write), or not fire (due to a fast write).
|
||||
_ = pgConn.slowWriteTimer.Stop()
|
||||
pgConn.bgReader.Stop()
|
||||
if !pgConn.slowWriteTimer.Stop() {
|
||||
// The timer starts its function in a separate goroutine. It is necessary to ensure the background reader has
|
||||
// started before calling Stop. Otherwise, the background reader may not be stopped. That on its own is not a
|
||||
// serious problem. But what is a serious problem is that the background reader may start at an inopportune time in
|
||||
// a subsequent query. For example, if a subsequent query was canceled then a deadline may be set on the net.Conn to
|
||||
// interrupt an in-progress read. After the read is interrupted, but before the deadline is cleared, the background
|
||||
// reader could start and read a deadline error. Then the next query would receive the an unexpected deadline error.
|
||||
<-pgConn.bgReaderStarted
|
||||
pgConn.bgReader.Stop()
|
||||
}
|
||||
}
|
||||
|
||||
func (pgConn *PgConn) flushWithPotentialWriteReadDeadlock() error {
|
||||
@@ -1764,11 +1897,16 @@ func (pgConn *PgConn) SyncConn(ctx context.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
// This should never happen. Only way I can imagine this occuring is if the server is constantly sending data such as
|
||||
// This should never happen. Only way I can imagine this occurring is if the server is constantly sending data such as
|
||||
// LISTEN/NOTIFY or log notifications such that we never can get an empty buffer.
|
||||
return errors.New("SyncConn: conn never synchronized")
|
||||
}
|
||||
|
||||
// CustomData returns a map that can be used to associate custom data with the connection.
|
||||
func (pgConn *PgConn) CustomData() map[string]any {
|
||||
return pgConn.customData
|
||||
}
|
||||
|
||||
// HijackedConn is the result of hijacking a connection.
|
||||
//
|
||||
// Due to the necessary exposure of internal implementation details, it is not covered by the semantic versioning
|
||||
@@ -1781,6 +1919,7 @@ type HijackedConn struct {
|
||||
TxStatus byte
|
||||
Frontend *pgproto3.Frontend
|
||||
Config *Config
|
||||
CustomData map[string]any
|
||||
}
|
||||
|
||||
// Hijack extracts the internal connection data. pgConn must be in an idle state. SyncConn should be called immediately
|
||||
@@ -1803,6 +1942,7 @@ func (pgConn *PgConn) Hijack() (*HijackedConn, error) {
|
||||
TxStatus: pgConn.txStatus,
|
||||
Frontend: pgConn.frontend,
|
||||
Config: pgConn.config,
|
||||
CustomData: pgConn.customData,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -1822,16 +1962,23 @@ func Construct(hc *HijackedConn) (*PgConn, error) {
|
||||
txStatus: hc.TxStatus,
|
||||
frontend: hc.Frontend,
|
||||
config: hc.Config,
|
||||
customData: hc.CustomData,
|
||||
|
||||
status: connStatusIdle,
|
||||
|
||||
cleanupDone: make(chan struct{}),
|
||||
}
|
||||
|
||||
pgConn.contextWatcher = newContextWatcher(pgConn.conn)
|
||||
pgConn.contextWatcher = ctxwatch.NewContextWatcher(hc.Config.BuildContextWatcherHandler(pgConn))
|
||||
pgConn.bgReader = bgreader.New(pgConn.conn)
|
||||
pgConn.slowWriteTimer = time.AfterFunc(time.Duration(math.MaxInt64), pgConn.bgReader.Start)
|
||||
pgConn.slowWriteTimer = time.AfterFunc(time.Duration(math.MaxInt64),
|
||||
func() {
|
||||
pgConn.bgReader.Start()
|
||||
pgConn.bgReaderStarted <- struct{}{}
|
||||
},
|
||||
)
|
||||
pgConn.slowWriteTimer.Stop()
|
||||
pgConn.bgReaderStarted = make(chan struct{})
|
||||
pgConn.frontend = hc.Config.BuildFrontend(pgConn.bgReader, pgConn.conn)
|
||||
|
||||
return pgConn, nil
|
||||
@@ -1973,6 +2120,13 @@ func (p *Pipeline) Flush() error {
|
||||
|
||||
// Sync establishes a synchronization point and flushes the queued requests.
|
||||
func (p *Pipeline) Sync() error {
|
||||
if p.closed {
|
||||
if p.err != nil {
|
||||
return p.err
|
||||
}
|
||||
return errors.New("pipeline closed")
|
||||
}
|
||||
|
||||
p.conn.frontend.SendSync(&pgproto3.Sync{})
|
||||
err := p.Flush()
|
||||
if err != nil {
|
||||
@@ -1989,14 +2143,28 @@ func (p *Pipeline) Sync() error {
|
||||
// *PipelineSync. If an ErrorResponse is received from the server, results will be nil and err will be a *PgError. If no
|
||||
// results are available, results and err will both be nil.
|
||||
func (p *Pipeline) GetResults() (results any, err error) {
|
||||
if p.closed {
|
||||
if p.err != nil {
|
||||
return nil, p.err
|
||||
}
|
||||
return nil, errors.New("pipeline closed")
|
||||
}
|
||||
|
||||
if p.expectedReadyForQueryCount == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return p.getResults()
|
||||
}
|
||||
|
||||
func (p *Pipeline) getResults() (results any, err error) {
|
||||
for {
|
||||
msg, err := p.conn.receiveMessage()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
p.closed = true
|
||||
p.err = err
|
||||
p.conn.asyncClose()
|
||||
return nil, normalizeTimeoutError(p.ctx, err)
|
||||
}
|
||||
|
||||
switch msg := msg.(type) {
|
||||
@@ -2018,7 +2186,8 @@ func (p *Pipeline) GetResults() (results any, err error) {
|
||||
case *pgproto3.ParseComplete:
|
||||
peekedMsg, err := p.conn.peekMessage()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
p.conn.asyncClose()
|
||||
return nil, normalizeTimeoutError(p.ctx, err)
|
||||
}
|
||||
if _, ok := peekedMsg.(*pgproto3.ParameterDescription); ok {
|
||||
return p.getResultsPrepare()
|
||||
@@ -2078,6 +2247,7 @@ func (p *Pipeline) Close() error {
|
||||
if p.closed {
|
||||
return p.err
|
||||
}
|
||||
|
||||
p.closed = true
|
||||
|
||||
if p.pendingSync {
|
||||
@@ -2090,7 +2260,7 @@ func (p *Pipeline) Close() error {
|
||||
}
|
||||
|
||||
for p.expectedReadyForQueryCount > 0 {
|
||||
_, err := p.GetResults()
|
||||
_, err := p.getResults()
|
||||
if err != nil {
|
||||
p.err = err
|
||||
var pgErr *PgError
|
||||
@@ -2106,3 +2276,71 @@ func (p *Pipeline) Close() error {
|
||||
|
||||
return p.err
|
||||
}
|
||||
|
||||
// DeadlineContextWatcherHandler handles canceled contexts by setting a deadline on a net.Conn.
|
||||
type DeadlineContextWatcherHandler struct {
|
||||
Conn net.Conn
|
||||
|
||||
// DeadlineDelay is the delay to set on the deadline set on net.Conn when the context is canceled.
|
||||
DeadlineDelay time.Duration
|
||||
}
|
||||
|
||||
func (h *DeadlineContextWatcherHandler) HandleCancel(ctx context.Context) {
|
||||
h.Conn.SetDeadline(time.Now().Add(h.DeadlineDelay))
|
||||
}
|
||||
|
||||
func (h *DeadlineContextWatcherHandler) HandleUnwatchAfterCancel() {
|
||||
h.Conn.SetDeadline(time.Time{})
|
||||
}
|
||||
|
||||
// CancelRequestContextWatcherHandler handles canceled contexts by sending a cancel request to the server. It also sets
|
||||
// a deadline on a net.Conn as a fallback.
|
||||
type CancelRequestContextWatcherHandler struct {
|
||||
Conn *PgConn
|
||||
|
||||
// CancelRequestDelay is the delay before sending the cancel request to the server.
|
||||
CancelRequestDelay time.Duration
|
||||
|
||||
// DeadlineDelay is the delay to set on the deadline set on net.Conn when the context is canceled.
|
||||
DeadlineDelay time.Duration
|
||||
|
||||
cancelFinishedChan chan struct{}
|
||||
handleUnwatchAfterCancelCalled func()
|
||||
}
|
||||
|
||||
func (h *CancelRequestContextWatcherHandler) HandleCancel(context.Context) {
|
||||
h.cancelFinishedChan = make(chan struct{})
|
||||
var handleUnwatchedAfterCancelCalledCtx context.Context
|
||||
handleUnwatchedAfterCancelCalledCtx, h.handleUnwatchAfterCancelCalled = context.WithCancel(context.Background())
|
||||
|
||||
deadline := time.Now().Add(h.DeadlineDelay)
|
||||
h.Conn.conn.SetDeadline(deadline)
|
||||
|
||||
go func() {
|
||||
defer close(h.cancelFinishedChan)
|
||||
|
||||
select {
|
||||
case <-handleUnwatchedAfterCancelCalledCtx.Done():
|
||||
return
|
||||
case <-time.After(h.CancelRequestDelay):
|
||||
}
|
||||
|
||||
cancelRequestCtx, cancel := context.WithDeadline(handleUnwatchedAfterCancelCalledCtx, deadline)
|
||||
defer cancel()
|
||||
h.Conn.CancelRequest(cancelRequestCtx)
|
||||
|
||||
// CancelRequest is inherently racy. Even though the cancel request has been received by the server at this point,
|
||||
// it hasn't necessarily been delivered to the other connection. If we immediately return and the connection is
|
||||
// immediately used then it is possible the CancelRequest will actually cancel our next query. The
|
||||
// TestCancelRequestContextWatcherHandler Stress test can produce this error without the sleep below. The sleep time
|
||||
// is arbitrary, but should be sufficient to prevent this error case.
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}()
|
||||
}
|
||||
|
||||
func (h *CancelRequestContextWatcherHandler) HandleUnwatchAfterCancel() {
|
||||
h.handleUnwatchAfterCancelCalled()
|
||||
<-h.cancelFinishedChan
|
||||
|
||||
h.Conn.conn.SetDeadline(time.Time{})
|
||||
}
|
||||
|
||||
2
vendor/github.com/jackc/pgx/v5/pgproto3/README.md
generated
vendored
2
vendor/github.com/jackc/pgx/v5/pgproto3/README.md
generated
vendored
@@ -1,6 +1,6 @@
|
||||
# pgproto3
|
||||
|
||||
Package pgproto3 is a encoder and decoder of the PostgreSQL wire protocol version 3.
|
||||
Package pgproto3 is an encoder and decoder of the PostgreSQL wire protocol version 3.
|
||||
|
||||
pgproto3 can be used as a foundation for PostgreSQL drivers, proxies, mock servers, load balancers and more.
|
||||
|
||||
|
||||
7
vendor/github.com/jackc/pgx/v5/pgproto3/authentication_cleartext_password.go
generated
vendored
7
vendor/github.com/jackc/pgx/v5/pgproto3/authentication_cleartext_password.go
generated
vendored
@@ -35,11 +35,10 @@ func (dst *AuthenticationCleartextPassword) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *AuthenticationCleartextPassword) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'R')
|
||||
dst = pgio.AppendInt32(dst, 8)
|
||||
func (src *AuthenticationCleartextPassword) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'R')
|
||||
dst = pgio.AppendUint32(dst, AuthTypeCleartextPassword)
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
7
vendor/github.com/jackc/pgx/v5/pgproto3/authentication_gss.go
generated
vendored
7
vendor/github.com/jackc/pgx/v5/pgproto3/authentication_gss.go
generated
vendored
@@ -27,11 +27,10 @@ func (a *AuthenticationGSS) Decode(src []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *AuthenticationGSS) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'R')
|
||||
dst = pgio.AppendInt32(dst, 4)
|
||||
func (a *AuthenticationGSS) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'R')
|
||||
dst = pgio.AppendUint32(dst, AuthTypeGSS)
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
func (a *AuthenticationGSS) MarshalJSON() ([]byte, error) {
|
||||
|
||||
7
vendor/github.com/jackc/pgx/v5/pgproto3/authentication_gss_continue.go
generated
vendored
7
vendor/github.com/jackc/pgx/v5/pgproto3/authentication_gss_continue.go
generated
vendored
@@ -31,12 +31,11 @@ func (a *AuthenticationGSSContinue) Decode(src []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *AuthenticationGSSContinue) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'R')
|
||||
dst = pgio.AppendInt32(dst, int32(len(a.Data))+8)
|
||||
func (a *AuthenticationGSSContinue) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'R')
|
||||
dst = pgio.AppendUint32(dst, AuthTypeGSSCont)
|
||||
dst = append(dst, a.Data...)
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
func (a *AuthenticationGSSContinue) MarshalJSON() ([]byte, error) {
|
||||
|
||||
7
vendor/github.com/jackc/pgx/v5/pgproto3/authentication_md5_password.go
generated
vendored
7
vendor/github.com/jackc/pgx/v5/pgproto3/authentication_md5_password.go
generated
vendored
@@ -38,12 +38,11 @@ func (dst *AuthenticationMD5Password) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *AuthenticationMD5Password) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'R')
|
||||
dst = pgio.AppendInt32(dst, 12)
|
||||
func (src *AuthenticationMD5Password) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'R')
|
||||
dst = pgio.AppendUint32(dst, AuthTypeMD5Password)
|
||||
dst = append(dst, src.Salt[:]...)
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
7
vendor/github.com/jackc/pgx/v5/pgproto3/authentication_ok.go
generated
vendored
7
vendor/github.com/jackc/pgx/v5/pgproto3/authentication_ok.go
generated
vendored
@@ -35,11 +35,10 @@ func (dst *AuthenticationOk) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *AuthenticationOk) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'R')
|
||||
dst = pgio.AppendInt32(dst, 8)
|
||||
func (src *AuthenticationOk) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'R')
|
||||
dst = pgio.AppendUint32(dst, AuthTypeOk)
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
10
vendor/github.com/jackc/pgx/v5/pgproto3/authentication_sasl.go
generated
vendored
10
vendor/github.com/jackc/pgx/v5/pgproto3/authentication_sasl.go
generated
vendored
@@ -47,10 +47,8 @@ func (dst *AuthenticationSASL) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *AuthenticationSASL) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'R')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
func (src *AuthenticationSASL) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'R')
|
||||
dst = pgio.AppendUint32(dst, AuthTypeSASL)
|
||||
|
||||
for _, s := range src.AuthMechanisms {
|
||||
@@ -59,9 +57,7 @@ func (src *AuthenticationSASL) Encode(dst []byte) []byte {
|
||||
}
|
||||
dst = append(dst, 0)
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
12
vendor/github.com/jackc/pgx/v5/pgproto3/authentication_sasl_continue.go
generated
vendored
12
vendor/github.com/jackc/pgx/v5/pgproto3/authentication_sasl_continue.go
generated
vendored
@@ -38,17 +38,11 @@ func (dst *AuthenticationSASLContinue) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *AuthenticationSASLContinue) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'R')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
func (src *AuthenticationSASLContinue) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'R')
|
||||
dst = pgio.AppendUint32(dst, AuthTypeSASLContinue)
|
||||
|
||||
dst = append(dst, src.Data...)
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
12
vendor/github.com/jackc/pgx/v5/pgproto3/authentication_sasl_final.go
generated
vendored
12
vendor/github.com/jackc/pgx/v5/pgproto3/authentication_sasl_final.go
generated
vendored
@@ -38,17 +38,11 @@ func (dst *AuthenticationSASLFinal) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *AuthenticationSASLFinal) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'R')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
func (src *AuthenticationSASLFinal) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'R')
|
||||
dst = pgio.AppendUint32(dst, AuthTypeSASLFinal)
|
||||
|
||||
dst = append(dst, src.Data...)
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Unmarshaler.
|
||||
|
||||
38
vendor/github.com/jackc/pgx/v5/pgproto3/backend.go
generated
vendored
38
vendor/github.com/jackc/pgx/v5/pgproto3/backend.go
generated
vendored
@@ -16,7 +16,8 @@ type Backend struct {
|
||||
// before it is actually transmitted (i.e. before Flush).
|
||||
tracer *tracer
|
||||
|
||||
wbuf []byte
|
||||
wbuf []byte
|
||||
encodeError error
|
||||
|
||||
// Frontend message flyweights
|
||||
bind Bind
|
||||
@@ -38,6 +39,7 @@ type Backend struct {
|
||||
terminate Terminate
|
||||
|
||||
bodyLen int
|
||||
maxBodyLen int // maxBodyLen is the maximum length of a message body in octets. If a message body exceeds this length, Receive will return an error.
|
||||
msgType byte
|
||||
partialMsg bool
|
||||
authType uint32
|
||||
@@ -54,11 +56,21 @@ func NewBackend(r io.Reader, w io.Writer) *Backend {
|
||||
return &Backend{cr: cr, w: w}
|
||||
}
|
||||
|
||||
// Send sends a message to the frontend (i.e. the client). The message is not guaranteed to be written until Flush is
|
||||
// called.
|
||||
// Send sends a message to the frontend (i.e. the client). The message is buffered until Flush is called. Any error
|
||||
// encountered will be returned from Flush.
|
||||
func (b *Backend) Send(msg BackendMessage) {
|
||||
if b.encodeError != nil {
|
||||
return
|
||||
}
|
||||
|
||||
prevLen := len(b.wbuf)
|
||||
b.wbuf = msg.Encode(b.wbuf)
|
||||
newBuf, err := msg.Encode(b.wbuf)
|
||||
if err != nil {
|
||||
b.encodeError = err
|
||||
return
|
||||
}
|
||||
b.wbuf = newBuf
|
||||
|
||||
if b.tracer != nil {
|
||||
b.tracer.traceMessage('B', int32(len(b.wbuf)-prevLen), msg)
|
||||
}
|
||||
@@ -66,6 +78,12 @@ func (b *Backend) Send(msg BackendMessage) {
|
||||
|
||||
// Flush writes any pending messages to the frontend (i.e. the client).
|
||||
func (b *Backend) Flush() error {
|
||||
if err := b.encodeError; err != nil {
|
||||
b.encodeError = nil
|
||||
b.wbuf = b.wbuf[:0]
|
||||
return &writeError{err: err, safeToRetry: true}
|
||||
}
|
||||
|
||||
n, err := b.w.Write(b.wbuf)
|
||||
|
||||
const maxLen = 1024
|
||||
@@ -158,6 +176,9 @@ func (b *Backend) Receive() (FrontendMessage, error) {
|
||||
|
||||
b.msgType = header[0]
|
||||
b.bodyLen = int(binary.BigEndian.Uint32(header[1:])) - 4
|
||||
if b.maxBodyLen > 0 && b.bodyLen > b.maxBodyLen {
|
||||
return nil, &ExceededMaxBodyLenErr{b.maxBodyLen, b.bodyLen}
|
||||
}
|
||||
b.partialMsg = true
|
||||
}
|
||||
|
||||
@@ -260,3 +281,12 @@ func (b *Backend) SetAuthType(authType uint32) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetMaxBodyLen sets the maximum length of a message body in octets. If a message body exceeds this length, Receive will return
|
||||
// an error. This is useful for protecting against malicious clients that send large messages with the intent of
|
||||
// causing memory exhaustion.
|
||||
// The default value is 0.
|
||||
// If maxBodyLen is 0, then no maximum is enforced.
|
||||
func (b *Backend) SetMaxBodyLen(maxBodyLen int) {
|
||||
b.maxBodyLen = maxBodyLen
|
||||
}
|
||||
|
||||
7
vendor/github.com/jackc/pgx/v5/pgproto3/backend_key_data.go
generated
vendored
7
vendor/github.com/jackc/pgx/v5/pgproto3/backend_key_data.go
generated
vendored
@@ -29,12 +29,11 @@ func (dst *BackendKeyData) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *BackendKeyData) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'K')
|
||||
dst = pgio.AppendUint32(dst, 12)
|
||||
func (src *BackendKeyData) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'K')
|
||||
dst = pgio.AppendUint32(dst, src.ProcessID)
|
||||
dst = pgio.AppendUint32(dst, src.SecretKey)
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
21
vendor/github.com/jackc/pgx/v5/pgproto3/bind.go
generated
vendored
21
vendor/github.com/jackc/pgx/v5/pgproto3/bind.go
generated
vendored
@@ -5,7 +5,9 @@ import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/jackc/pgx/v5/internal/pgio"
|
||||
)
|
||||
@@ -108,21 +110,25 @@ func (dst *Bind) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *Bind) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'B')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
func (src *Bind) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'B')
|
||||
|
||||
dst = append(dst, src.DestinationPortal...)
|
||||
dst = append(dst, 0)
|
||||
dst = append(dst, src.PreparedStatement...)
|
||||
dst = append(dst, 0)
|
||||
|
||||
if len(src.ParameterFormatCodes) > math.MaxUint16 {
|
||||
return nil, errors.New("too many parameter format codes")
|
||||
}
|
||||
dst = pgio.AppendUint16(dst, uint16(len(src.ParameterFormatCodes)))
|
||||
for _, fc := range src.ParameterFormatCodes {
|
||||
dst = pgio.AppendInt16(dst, fc)
|
||||
}
|
||||
|
||||
if len(src.Parameters) > math.MaxUint16 {
|
||||
return nil, errors.New("too many parameters")
|
||||
}
|
||||
dst = pgio.AppendUint16(dst, uint16(len(src.Parameters)))
|
||||
for _, p := range src.Parameters {
|
||||
if p == nil {
|
||||
@@ -134,14 +140,15 @@ func (src *Bind) Encode(dst []byte) []byte {
|
||||
dst = append(dst, p...)
|
||||
}
|
||||
|
||||
if len(src.ResultFormatCodes) > math.MaxUint16 {
|
||||
return nil, errors.New("too many result format codes")
|
||||
}
|
||||
dst = pgio.AppendUint16(dst, uint16(len(src.ResultFormatCodes)))
|
||||
for _, fc := range src.ResultFormatCodes {
|
||||
dst = pgio.AppendInt16(dst, fc)
|
||||
}
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
4
vendor/github.com/jackc/pgx/v5/pgproto3/bind_complete.go
generated
vendored
4
vendor/github.com/jackc/pgx/v5/pgproto3/bind_complete.go
generated
vendored
@@ -20,8 +20,8 @@ func (dst *BindComplete) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *BindComplete) Encode(dst []byte) []byte {
|
||||
return append(dst, '2', 0, 0, 0, 4)
|
||||
func (src *BindComplete) Encode(dst []byte) ([]byte, error) {
|
||||
return append(dst, '2', 0, 0, 0, 4), nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
4
vendor/github.com/jackc/pgx/v5/pgproto3/cancel_request.go
generated
vendored
4
vendor/github.com/jackc/pgx/v5/pgproto3/cancel_request.go
generated
vendored
@@ -36,12 +36,12 @@ func (dst *CancelRequest) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 4 byte message length.
|
||||
func (src *CancelRequest) Encode(dst []byte) []byte {
|
||||
func (src *CancelRequest) Encode(dst []byte) ([]byte, error) {
|
||||
dst = pgio.AppendInt32(dst, 16)
|
||||
dst = pgio.AppendInt32(dst, cancelRequestCode)
|
||||
dst = pgio.AppendUint32(dst, src.ProcessID)
|
||||
dst = pgio.AppendUint32(dst, src.SecretKey)
|
||||
return dst
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
14
vendor/github.com/jackc/pgx/v5/pgproto3/close.go
generated
vendored
14
vendor/github.com/jackc/pgx/v5/pgproto3/close.go
generated
vendored
@@ -4,8 +4,6 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/jackc/pgx/v5/internal/pgio"
|
||||
)
|
||||
|
||||
type Close struct {
|
||||
@@ -37,18 +35,12 @@ func (dst *Close) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *Close) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'C')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
|
||||
func (src *Close) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'C')
|
||||
dst = append(dst, src.ObjectType)
|
||||
dst = append(dst, src.Name...)
|
||||
dst = append(dst, 0)
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
4
vendor/github.com/jackc/pgx/v5/pgproto3/close_complete.go
generated
vendored
4
vendor/github.com/jackc/pgx/v5/pgproto3/close_complete.go
generated
vendored
@@ -20,8 +20,8 @@ func (dst *CloseComplete) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *CloseComplete) Encode(dst []byte) []byte {
|
||||
return append(dst, '3', 0, 0, 0, 4)
|
||||
func (src *CloseComplete) Encode(dst []byte) ([]byte, error) {
|
||||
return append(dst, '3', 0, 0, 0, 4), nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
14
vendor/github.com/jackc/pgx/v5/pgproto3/command_complete.go
generated
vendored
14
vendor/github.com/jackc/pgx/v5/pgproto3/command_complete.go
generated
vendored
@@ -3,8 +3,6 @@ package pgproto3
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/jackc/pgx/v5/internal/pgio"
|
||||
)
|
||||
|
||||
type CommandComplete struct {
|
||||
@@ -31,17 +29,11 @@ func (dst *CommandComplete) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *CommandComplete) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'C')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
|
||||
func (src *CommandComplete) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'C')
|
||||
dst = append(dst, src.CommandTag...)
|
||||
dst = append(dst, 0)
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
14
vendor/github.com/jackc/pgx/v5/pgproto3/copy_both_response.go
generated
vendored
14
vendor/github.com/jackc/pgx/v5/pgproto3/copy_both_response.go
generated
vendored
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math"
|
||||
|
||||
"github.com/jackc/pgx/v5/internal/pgio"
|
||||
)
|
||||
@@ -44,19 +45,18 @@ func (dst *CopyBothResponse) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *CopyBothResponse) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'W')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
func (src *CopyBothResponse) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'W')
|
||||
dst = append(dst, src.OverallFormat)
|
||||
if len(src.ColumnFormatCodes) > math.MaxUint16 {
|
||||
return nil, errors.New("too many column format codes")
|
||||
}
|
||||
dst = pgio.AppendUint16(dst, uint16(len(src.ColumnFormatCodes)))
|
||||
for _, fc := range src.ColumnFormatCodes {
|
||||
dst = pgio.AppendUint16(dst, fc)
|
||||
}
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
9
vendor/github.com/jackc/pgx/v5/pgproto3/copy_data.go
generated
vendored
9
vendor/github.com/jackc/pgx/v5/pgproto3/copy_data.go
generated
vendored
@@ -3,8 +3,6 @@ package pgproto3
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/jackc/pgx/v5/internal/pgio"
|
||||
)
|
||||
|
||||
type CopyData struct {
|
||||
@@ -25,11 +23,10 @@ func (dst *CopyData) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *CopyData) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'd')
|
||||
dst = pgio.AppendInt32(dst, int32(4+len(src.Data)))
|
||||
func (src *CopyData) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'd')
|
||||
dst = append(dst, src.Data...)
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
4
vendor/github.com/jackc/pgx/v5/pgproto3/copy_done.go
generated
vendored
4
vendor/github.com/jackc/pgx/v5/pgproto3/copy_done.go
generated
vendored
@@ -24,8 +24,8 @@ func (dst *CopyDone) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *CopyDone) Encode(dst []byte) []byte {
|
||||
return append(dst, 'c', 0, 0, 0, 4)
|
||||
func (src *CopyDone) Encode(dst []byte) ([]byte, error) {
|
||||
return append(dst, 'c', 0, 0, 0, 4), nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
14
vendor/github.com/jackc/pgx/v5/pgproto3/copy_fail.go
generated
vendored
14
vendor/github.com/jackc/pgx/v5/pgproto3/copy_fail.go
generated
vendored
@@ -3,8 +3,6 @@ package pgproto3
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/jackc/pgx/v5/internal/pgio"
|
||||
)
|
||||
|
||||
type CopyFail struct {
|
||||
@@ -28,17 +26,11 @@ func (dst *CopyFail) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *CopyFail) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'f')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
|
||||
func (src *CopyFail) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'f')
|
||||
dst = append(dst, src.Message...)
|
||||
dst = append(dst, 0)
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
14
vendor/github.com/jackc/pgx/v5/pgproto3/copy_in_response.go
generated
vendored
14
vendor/github.com/jackc/pgx/v5/pgproto3/copy_in_response.go
generated
vendored
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math"
|
||||
|
||||
"github.com/jackc/pgx/v5/internal/pgio"
|
||||
)
|
||||
@@ -44,20 +45,19 @@ func (dst *CopyInResponse) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *CopyInResponse) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'G')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
func (src *CopyInResponse) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'G')
|
||||
|
||||
dst = append(dst, src.OverallFormat)
|
||||
if len(src.ColumnFormatCodes) > math.MaxUint16 {
|
||||
return nil, errors.New("too many column format codes")
|
||||
}
|
||||
dst = pgio.AppendUint16(dst, uint16(len(src.ColumnFormatCodes)))
|
||||
for _, fc := range src.ColumnFormatCodes {
|
||||
dst = pgio.AppendUint16(dst, fc)
|
||||
}
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
14
vendor/github.com/jackc/pgx/v5/pgproto3/copy_out_response.go
generated
vendored
14
vendor/github.com/jackc/pgx/v5/pgproto3/copy_out_response.go
generated
vendored
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math"
|
||||
|
||||
"github.com/jackc/pgx/v5/internal/pgio"
|
||||
)
|
||||
@@ -43,21 +44,20 @@ func (dst *CopyOutResponse) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *CopyOutResponse) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'H')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
func (src *CopyOutResponse) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'H')
|
||||
|
||||
dst = append(dst, src.OverallFormat)
|
||||
|
||||
if len(src.ColumnFormatCodes) > math.MaxUint16 {
|
||||
return nil, errors.New("too many column format codes")
|
||||
}
|
||||
dst = pgio.AppendUint16(dst, uint16(len(src.ColumnFormatCodes)))
|
||||
for _, fc := range src.ColumnFormatCodes {
|
||||
dst = pgio.AppendUint16(dst, fc)
|
||||
}
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
15
vendor/github.com/jackc/pgx/v5/pgproto3/data_row.go
generated
vendored
15
vendor/github.com/jackc/pgx/v5/pgproto3/data_row.go
generated
vendored
@@ -4,6 +4,8 @@ import (
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math"
|
||||
|
||||
"github.com/jackc/pgx/v5/internal/pgio"
|
||||
)
|
||||
@@ -63,11 +65,12 @@ func (dst *DataRow) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *DataRow) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'D')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
func (src *DataRow) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'D')
|
||||
|
||||
if len(src.Values) > math.MaxUint16 {
|
||||
return nil, errors.New("too many values")
|
||||
}
|
||||
dst = pgio.AppendUint16(dst, uint16(len(src.Values)))
|
||||
for _, v := range src.Values {
|
||||
if v == nil {
|
||||
@@ -79,9 +82,7 @@ func (src *DataRow) Encode(dst []byte) []byte {
|
||||
dst = append(dst, v...)
|
||||
}
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
14
vendor/github.com/jackc/pgx/v5/pgproto3/describe.go
generated
vendored
14
vendor/github.com/jackc/pgx/v5/pgproto3/describe.go
generated
vendored
@@ -4,8 +4,6 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/jackc/pgx/v5/internal/pgio"
|
||||
)
|
||||
|
||||
type Describe struct {
|
||||
@@ -37,18 +35,12 @@ func (dst *Describe) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *Describe) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'D')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
|
||||
func (src *Describe) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'D')
|
||||
dst = append(dst, src.ObjectType)
|
||||
dst = append(dst, src.Name...)
|
||||
dst = append(dst, 0)
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
4
vendor/github.com/jackc/pgx/v5/pgproto3/doc.go
generated
vendored
4
vendor/github.com/jackc/pgx/v5/pgproto3/doc.go
generated
vendored
@@ -1,7 +1,7 @@
|
||||
// Package pgproto3 is a encoder and decoder of the PostgreSQL wire protocol version 3.
|
||||
// Package pgproto3 is an encoder and decoder of the PostgreSQL wire protocol version 3.
|
||||
//
|
||||
// The primary interfaces are Frontend and Backend. They correspond to a client and server respectively. Messages are
|
||||
// sent with Send (or a specialized Send variant). Messages are automatically bufferred to minimize small writes. Call
|
||||
// sent with Send (or a specialized Send variant). Messages are automatically buffered to minimize small writes. Call
|
||||
// Flush to ensure a message has actually been sent.
|
||||
//
|
||||
// The Trace method of Frontend and Backend can be used to examine the wire-level message traffic. It outputs in a
|
||||
|
||||
4
vendor/github.com/jackc/pgx/v5/pgproto3/empty_query_response.go
generated
vendored
4
vendor/github.com/jackc/pgx/v5/pgproto3/empty_query_response.go
generated
vendored
@@ -20,8 +20,8 @@ func (dst *EmptyQueryResponse) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *EmptyQueryResponse) Encode(dst []byte) []byte {
|
||||
return append(dst, 'I', 0, 0, 0, 4)
|
||||
func (src *EmptyQueryResponse) Encode(dst []byte) ([]byte, error) {
|
||||
return append(dst, 'I', 0, 0, 0, 4), nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
135
vendor/github.com/jackc/pgx/v5/pgproto3/error_response.go
generated
vendored
135
vendor/github.com/jackc/pgx/v5/pgproto3/error_response.go
generated
vendored
@@ -2,7 +2,6 @@ package pgproto3
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
)
|
||||
@@ -111,119 +110,113 @@ func (dst *ErrorResponse) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *ErrorResponse) Encode(dst []byte) []byte {
|
||||
return append(dst, src.marshalBinary('E')...)
|
||||
func (src *ErrorResponse) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'E')
|
||||
dst = src.appendFields(dst)
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
func (src *ErrorResponse) marshalBinary(typeByte byte) []byte {
|
||||
var bigEndian BigEndianBuf
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
buf.WriteByte(typeByte)
|
||||
buf.Write(bigEndian.Uint32(0))
|
||||
|
||||
func (src *ErrorResponse) appendFields(dst []byte) []byte {
|
||||
if src.Severity != "" {
|
||||
buf.WriteByte('S')
|
||||
buf.WriteString(src.Severity)
|
||||
buf.WriteByte(0)
|
||||
dst = append(dst, 'S')
|
||||
dst = append(dst, src.Severity...)
|
||||
dst = append(dst, 0)
|
||||
}
|
||||
if src.SeverityUnlocalized != "" {
|
||||
buf.WriteByte('V')
|
||||
buf.WriteString(src.SeverityUnlocalized)
|
||||
buf.WriteByte(0)
|
||||
dst = append(dst, 'V')
|
||||
dst = append(dst, src.SeverityUnlocalized...)
|
||||
dst = append(dst, 0)
|
||||
}
|
||||
if src.Code != "" {
|
||||
buf.WriteByte('C')
|
||||
buf.WriteString(src.Code)
|
||||
buf.WriteByte(0)
|
||||
dst = append(dst, 'C')
|
||||
dst = append(dst, src.Code...)
|
||||
dst = append(dst, 0)
|
||||
}
|
||||
if src.Message != "" {
|
||||
buf.WriteByte('M')
|
||||
buf.WriteString(src.Message)
|
||||
buf.WriteByte(0)
|
||||
dst = append(dst, 'M')
|
||||
dst = append(dst, src.Message...)
|
||||
dst = append(dst, 0)
|
||||
}
|
||||
if src.Detail != "" {
|
||||
buf.WriteByte('D')
|
||||
buf.WriteString(src.Detail)
|
||||
buf.WriteByte(0)
|
||||
dst = append(dst, 'D')
|
||||
dst = append(dst, src.Detail...)
|
||||
dst = append(dst, 0)
|
||||
}
|
||||
if src.Hint != "" {
|
||||
buf.WriteByte('H')
|
||||
buf.WriteString(src.Hint)
|
||||
buf.WriteByte(0)
|
||||
dst = append(dst, 'H')
|
||||
dst = append(dst, src.Hint...)
|
||||
dst = append(dst, 0)
|
||||
}
|
||||
if src.Position != 0 {
|
||||
buf.WriteByte('P')
|
||||
buf.WriteString(strconv.Itoa(int(src.Position)))
|
||||
buf.WriteByte(0)
|
||||
dst = append(dst, 'P')
|
||||
dst = append(dst, strconv.Itoa(int(src.Position))...)
|
||||
dst = append(dst, 0)
|
||||
}
|
||||
if src.InternalPosition != 0 {
|
||||
buf.WriteByte('p')
|
||||
buf.WriteString(strconv.Itoa(int(src.InternalPosition)))
|
||||
buf.WriteByte(0)
|
||||
dst = append(dst, 'p')
|
||||
dst = append(dst, strconv.Itoa(int(src.InternalPosition))...)
|
||||
dst = append(dst, 0)
|
||||
}
|
||||
if src.InternalQuery != "" {
|
||||
buf.WriteByte('q')
|
||||
buf.WriteString(src.InternalQuery)
|
||||
buf.WriteByte(0)
|
||||
dst = append(dst, 'q')
|
||||
dst = append(dst, src.InternalQuery...)
|
||||
dst = append(dst, 0)
|
||||
}
|
||||
if src.Where != "" {
|
||||
buf.WriteByte('W')
|
||||
buf.WriteString(src.Where)
|
||||
buf.WriteByte(0)
|
||||
dst = append(dst, 'W')
|
||||
dst = append(dst, src.Where...)
|
||||
dst = append(dst, 0)
|
||||
}
|
||||
if src.SchemaName != "" {
|
||||
buf.WriteByte('s')
|
||||
buf.WriteString(src.SchemaName)
|
||||
buf.WriteByte(0)
|
||||
dst = append(dst, 's')
|
||||
dst = append(dst, src.SchemaName...)
|
||||
dst = append(dst, 0)
|
||||
}
|
||||
if src.TableName != "" {
|
||||
buf.WriteByte('t')
|
||||
buf.WriteString(src.TableName)
|
||||
buf.WriteByte(0)
|
||||
dst = append(dst, 't')
|
||||
dst = append(dst, src.TableName...)
|
||||
dst = append(dst, 0)
|
||||
}
|
||||
if src.ColumnName != "" {
|
||||
buf.WriteByte('c')
|
||||
buf.WriteString(src.ColumnName)
|
||||
buf.WriteByte(0)
|
||||
dst = append(dst, 'c')
|
||||
dst = append(dst, src.ColumnName...)
|
||||
dst = append(dst, 0)
|
||||
}
|
||||
if src.DataTypeName != "" {
|
||||
buf.WriteByte('d')
|
||||
buf.WriteString(src.DataTypeName)
|
||||
buf.WriteByte(0)
|
||||
dst = append(dst, 'd')
|
||||
dst = append(dst, src.DataTypeName...)
|
||||
dst = append(dst, 0)
|
||||
}
|
||||
if src.ConstraintName != "" {
|
||||
buf.WriteByte('n')
|
||||
buf.WriteString(src.ConstraintName)
|
||||
buf.WriteByte(0)
|
||||
dst = append(dst, 'n')
|
||||
dst = append(dst, src.ConstraintName...)
|
||||
dst = append(dst, 0)
|
||||
}
|
||||
if src.File != "" {
|
||||
buf.WriteByte('F')
|
||||
buf.WriteString(src.File)
|
||||
buf.WriteByte(0)
|
||||
dst = append(dst, 'F')
|
||||
dst = append(dst, src.File...)
|
||||
dst = append(dst, 0)
|
||||
}
|
||||
if src.Line != 0 {
|
||||
buf.WriteByte('L')
|
||||
buf.WriteString(strconv.Itoa(int(src.Line)))
|
||||
buf.WriteByte(0)
|
||||
dst = append(dst, 'L')
|
||||
dst = append(dst, strconv.Itoa(int(src.Line))...)
|
||||
dst = append(dst, 0)
|
||||
}
|
||||
if src.Routine != "" {
|
||||
buf.WriteByte('R')
|
||||
buf.WriteString(src.Routine)
|
||||
buf.WriteByte(0)
|
||||
dst = append(dst, 'R')
|
||||
dst = append(dst, src.Routine...)
|
||||
dst = append(dst, 0)
|
||||
}
|
||||
|
||||
for k, v := range src.UnknownFields {
|
||||
buf.WriteByte(k)
|
||||
buf.WriteString(v)
|
||||
buf.WriteByte(0)
|
||||
dst = append(dst, k)
|
||||
dst = append(dst, v...)
|
||||
dst = append(dst, 0)
|
||||
}
|
||||
|
||||
buf.WriteByte(0)
|
||||
dst = append(dst, 0)
|
||||
|
||||
binary.BigEndian.PutUint32(buf.Bytes()[1:5], uint32(buf.Len()-1))
|
||||
|
||||
return buf.Bytes()
|
||||
return dst
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
13
vendor/github.com/jackc/pgx/v5/pgproto3/execute.go
generated
vendored
13
vendor/github.com/jackc/pgx/v5/pgproto3/execute.go
generated
vendored
@@ -36,19 +36,12 @@ func (dst *Execute) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *Execute) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'E')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
|
||||
func (src *Execute) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'E')
|
||||
dst = append(dst, src.Portal...)
|
||||
dst = append(dst, 0)
|
||||
|
||||
dst = pgio.AppendUint32(dst, src.MaxRows)
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
4
vendor/github.com/jackc/pgx/v5/pgproto3/flush.go
generated
vendored
4
vendor/github.com/jackc/pgx/v5/pgproto3/flush.go
generated
vendored
@@ -20,8 +20,8 @@ func (dst *Flush) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *Flush) Encode(dst []byte) []byte {
|
||||
return append(dst, 'H', 0, 0, 0, 4)
|
||||
func (src *Flush) Encode(dst []byte) ([]byte, error) {
|
||||
return append(dst, 'H', 0, 0, 0, 4), nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
137
vendor/github.com/jackc/pgx/v5/pgproto3/frontend.go
generated
vendored
137
vendor/github.com/jackc/pgx/v5/pgproto3/frontend.go
generated
vendored
@@ -18,7 +18,8 @@ type Frontend struct {
|
||||
// idle. Setting and unsetting tracer provides equivalent functionality to PQtrace and PQuntrace in libpq.
|
||||
tracer *tracer
|
||||
|
||||
wbuf []byte
|
||||
wbuf []byte
|
||||
encodeError error
|
||||
|
||||
// Backend message flyweights
|
||||
authenticationOk AuthenticationOk
|
||||
@@ -64,16 +65,26 @@ func NewFrontend(r io.Reader, w io.Writer) *Frontend {
|
||||
return &Frontend{cr: cr, w: w}
|
||||
}
|
||||
|
||||
// Send sends a message to the backend (i.e. the server). The message is not guaranteed to be written until Flush is
|
||||
// called.
|
||||
// Send sends a message to the backend (i.e. the server). The message is buffered until Flush is called. Any error
|
||||
// encountered will be returned from Flush.
|
||||
//
|
||||
// Send can work with any FrontendMessage. Some commonly used message types such as Bind have specialized send methods
|
||||
// such as SendBind. These methods should be preferred when the type of message is known up front (e.g. when building an
|
||||
// extended query protocol query) as they may be faster due to knowing the type of msg rather than it being hidden
|
||||
// behind an interface.
|
||||
func (f *Frontend) Send(msg FrontendMessage) {
|
||||
if f.encodeError != nil {
|
||||
return
|
||||
}
|
||||
|
||||
prevLen := len(f.wbuf)
|
||||
f.wbuf = msg.Encode(f.wbuf)
|
||||
newBuf, err := msg.Encode(f.wbuf)
|
||||
if err != nil {
|
||||
f.encodeError = err
|
||||
return
|
||||
}
|
||||
f.wbuf = newBuf
|
||||
|
||||
if f.tracer != nil {
|
||||
f.tracer.traceMessage('F', int32(len(f.wbuf)-prevLen), msg)
|
||||
}
|
||||
@@ -81,6 +92,12 @@ func (f *Frontend) Send(msg FrontendMessage) {
|
||||
|
||||
// Flush writes any pending messages to the backend (i.e. the server).
|
||||
func (f *Frontend) Flush() error {
|
||||
if err := f.encodeError; err != nil {
|
||||
f.encodeError = nil
|
||||
f.wbuf = f.wbuf[:0]
|
||||
return &writeError{err: err, safeToRetry: true}
|
||||
}
|
||||
|
||||
if len(f.wbuf) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -116,71 +133,141 @@ func (f *Frontend) Untrace() {
|
||||
f.tracer = nil
|
||||
}
|
||||
|
||||
// SendBind sends a Bind message to the backend (i.e. the server). The message is not guaranteed to be written until
|
||||
// Flush is called.
|
||||
// SendBind sends a Bind message to the backend (i.e. the server). The message is buffered until Flush is called. Any
|
||||
// error encountered will be returned from Flush.
|
||||
func (f *Frontend) SendBind(msg *Bind) {
|
||||
if f.encodeError != nil {
|
||||
return
|
||||
}
|
||||
|
||||
prevLen := len(f.wbuf)
|
||||
f.wbuf = msg.Encode(f.wbuf)
|
||||
newBuf, err := msg.Encode(f.wbuf)
|
||||
if err != nil {
|
||||
f.encodeError = err
|
||||
return
|
||||
}
|
||||
f.wbuf = newBuf
|
||||
|
||||
if f.tracer != nil {
|
||||
f.tracer.traceBind('F', int32(len(f.wbuf)-prevLen), msg)
|
||||
}
|
||||
}
|
||||
|
||||
// SendParse sends a Parse message to the backend (i.e. the server). The message is not guaranteed to be written until
|
||||
// Flush is called.
|
||||
// SendParse sends a Parse message to the backend (i.e. the server). The message is buffered until Flush is called. Any
|
||||
// error encountered will be returned from Flush.
|
||||
func (f *Frontend) SendParse(msg *Parse) {
|
||||
if f.encodeError != nil {
|
||||
return
|
||||
}
|
||||
|
||||
prevLen := len(f.wbuf)
|
||||
f.wbuf = msg.Encode(f.wbuf)
|
||||
newBuf, err := msg.Encode(f.wbuf)
|
||||
if err != nil {
|
||||
f.encodeError = err
|
||||
return
|
||||
}
|
||||
f.wbuf = newBuf
|
||||
|
||||
if f.tracer != nil {
|
||||
f.tracer.traceParse('F', int32(len(f.wbuf)-prevLen), msg)
|
||||
}
|
||||
}
|
||||
|
||||
// SendClose sends a Close message to the backend (i.e. the server). The message is not guaranteed to be written until
|
||||
// Flush is called.
|
||||
// SendClose sends a Close message to the backend (i.e. the server). The message is buffered until Flush is called. Any
|
||||
// error encountered will be returned from Flush.
|
||||
func (f *Frontend) SendClose(msg *Close) {
|
||||
if f.encodeError != nil {
|
||||
return
|
||||
}
|
||||
|
||||
prevLen := len(f.wbuf)
|
||||
f.wbuf = msg.Encode(f.wbuf)
|
||||
newBuf, err := msg.Encode(f.wbuf)
|
||||
if err != nil {
|
||||
f.encodeError = err
|
||||
return
|
||||
}
|
||||
f.wbuf = newBuf
|
||||
|
||||
if f.tracer != nil {
|
||||
f.tracer.traceClose('F', int32(len(f.wbuf)-prevLen), msg)
|
||||
}
|
||||
}
|
||||
|
||||
// SendDescribe sends a Describe message to the backend (i.e. the server). The message is not guaranteed to be written until
|
||||
// Flush is called.
|
||||
// SendDescribe sends a Describe message to the backend (i.e. the server). The message is buffered until Flush is
|
||||
// called. Any error encountered will be returned from Flush.
|
||||
func (f *Frontend) SendDescribe(msg *Describe) {
|
||||
if f.encodeError != nil {
|
||||
return
|
||||
}
|
||||
|
||||
prevLen := len(f.wbuf)
|
||||
f.wbuf = msg.Encode(f.wbuf)
|
||||
newBuf, err := msg.Encode(f.wbuf)
|
||||
if err != nil {
|
||||
f.encodeError = err
|
||||
return
|
||||
}
|
||||
f.wbuf = newBuf
|
||||
|
||||
if f.tracer != nil {
|
||||
f.tracer.traceDescribe('F', int32(len(f.wbuf)-prevLen), msg)
|
||||
}
|
||||
}
|
||||
|
||||
// SendExecute sends a Execute message to the backend (i.e. the server). The message is not guaranteed to be written until
|
||||
// Flush is called.
|
||||
// SendExecute sends an Execute message to the backend (i.e. the server). The message is buffered until Flush is called.
|
||||
// Any error encountered will be returned from Flush.
|
||||
func (f *Frontend) SendExecute(msg *Execute) {
|
||||
if f.encodeError != nil {
|
||||
return
|
||||
}
|
||||
|
||||
prevLen := len(f.wbuf)
|
||||
f.wbuf = msg.Encode(f.wbuf)
|
||||
newBuf, err := msg.Encode(f.wbuf)
|
||||
if err != nil {
|
||||
f.encodeError = err
|
||||
return
|
||||
}
|
||||
f.wbuf = newBuf
|
||||
|
||||
if f.tracer != nil {
|
||||
f.tracer.TraceQueryute('F', int32(len(f.wbuf)-prevLen), msg)
|
||||
}
|
||||
}
|
||||
|
||||
// SendSync sends a Sync message to the backend (i.e. the server). The message is not guaranteed to be written until
|
||||
// Flush is called.
|
||||
// SendSync sends a Sync message to the backend (i.e. the server). The message is buffered until Flush is called. Any
|
||||
// error encountered will be returned from Flush.
|
||||
func (f *Frontend) SendSync(msg *Sync) {
|
||||
if f.encodeError != nil {
|
||||
return
|
||||
}
|
||||
|
||||
prevLen := len(f.wbuf)
|
||||
f.wbuf = msg.Encode(f.wbuf)
|
||||
newBuf, err := msg.Encode(f.wbuf)
|
||||
if err != nil {
|
||||
f.encodeError = err
|
||||
return
|
||||
}
|
||||
f.wbuf = newBuf
|
||||
|
||||
if f.tracer != nil {
|
||||
f.tracer.traceSync('F', int32(len(f.wbuf)-prevLen), msg)
|
||||
}
|
||||
}
|
||||
|
||||
// SendQuery sends a Query message to the backend (i.e. the server). The message is not guaranteed to be written until
|
||||
// Flush is called.
|
||||
// SendQuery sends a Query message to the backend (i.e. the server). The message is buffered until Flush is called. Any
|
||||
// error encountered will be returned from Flush.
|
||||
func (f *Frontend) SendQuery(msg *Query) {
|
||||
if f.encodeError != nil {
|
||||
return
|
||||
}
|
||||
|
||||
prevLen := len(f.wbuf)
|
||||
f.wbuf = msg.Encode(f.wbuf)
|
||||
newBuf, err := msg.Encode(f.wbuf)
|
||||
if err != nil {
|
||||
f.encodeError = err
|
||||
return
|
||||
}
|
||||
f.wbuf = newBuf
|
||||
|
||||
if f.tracer != nil {
|
||||
f.tracer.traceQuery('F', int32(len(f.wbuf)-prevLen), msg)
|
||||
}
|
||||
|
||||
19
vendor/github.com/jackc/pgx/v5/pgproto3/function_call.go
generated
vendored
19
vendor/github.com/jackc/pgx/v5/pgproto3/function_call.go
generated
vendored
@@ -2,6 +2,8 @@ package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math"
|
||||
|
||||
"github.com/jackc/pgx/v5/internal/pgio"
|
||||
)
|
||||
@@ -71,15 +73,21 @@ func (dst *FunctionCall) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *FunctionCall) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'F')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendUint32(dst, 0) // Unknown length, set it at the end
|
||||
func (src *FunctionCall) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'F')
|
||||
dst = pgio.AppendUint32(dst, src.Function)
|
||||
|
||||
if len(src.ArgFormatCodes) > math.MaxUint16 {
|
||||
return nil, errors.New("too many arg format codes")
|
||||
}
|
||||
dst = pgio.AppendUint16(dst, uint16(len(src.ArgFormatCodes)))
|
||||
for _, argFormatCode := range src.ArgFormatCodes {
|
||||
dst = pgio.AppendUint16(dst, argFormatCode)
|
||||
}
|
||||
|
||||
if len(src.Arguments) > math.MaxUint16 {
|
||||
return nil, errors.New("too many arguments")
|
||||
}
|
||||
dst = pgio.AppendUint16(dst, uint16(len(src.Arguments)))
|
||||
for _, argument := range src.Arguments {
|
||||
if argument == nil {
|
||||
@@ -90,6 +98,5 @@ func (src *FunctionCall) Encode(dst []byte) []byte {
|
||||
}
|
||||
}
|
||||
dst = pgio.AppendUint16(dst, src.ResultFormatCode)
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
10
vendor/github.com/jackc/pgx/v5/pgproto3/function_call_response.go
generated
vendored
10
vendor/github.com/jackc/pgx/v5/pgproto3/function_call_response.go
generated
vendored
@@ -39,10 +39,8 @@ func (dst *FunctionCallResponse) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *FunctionCallResponse) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'V')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
func (src *FunctionCallResponse) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'V')
|
||||
|
||||
if src.Result == nil {
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
@@ -51,9 +49,7 @@ func (src *FunctionCallResponse) Encode(dst []byte) []byte {
|
||||
dst = append(dst, src.Result...)
|
||||
}
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
4
vendor/github.com/jackc/pgx/v5/pgproto3/gss_enc_request.go
generated
vendored
4
vendor/github.com/jackc/pgx/v5/pgproto3/gss_enc_request.go
generated
vendored
@@ -31,10 +31,10 @@ func (dst *GSSEncRequest) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 4 byte message length.
|
||||
func (src *GSSEncRequest) Encode(dst []byte) []byte {
|
||||
func (src *GSSEncRequest) Encode(dst []byte) ([]byte, error) {
|
||||
dst = pgio.AppendInt32(dst, 8)
|
||||
dst = pgio.AppendInt32(dst, gssEncReqNumber)
|
||||
return dst
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
9
vendor/github.com/jackc/pgx/v5/pgproto3/gss_response.go
generated
vendored
9
vendor/github.com/jackc/pgx/v5/pgproto3/gss_response.go
generated
vendored
@@ -2,8 +2,6 @@ package pgproto3
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/jackc/pgx/v5/internal/pgio"
|
||||
)
|
||||
|
||||
type GSSResponse struct {
|
||||
@@ -18,11 +16,10 @@ func (g *GSSResponse) Decode(data []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *GSSResponse) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'p')
|
||||
dst = pgio.AppendInt32(dst, int32(4+len(g.Data)))
|
||||
func (g *GSSResponse) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'p')
|
||||
dst = append(dst, g.Data...)
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
4
vendor/github.com/jackc/pgx/v5/pgproto3/no_data.go
generated
vendored
4
vendor/github.com/jackc/pgx/v5/pgproto3/no_data.go
generated
vendored
@@ -20,8 +20,8 @@ func (dst *NoData) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *NoData) Encode(dst []byte) []byte {
|
||||
return append(dst, 'n', 0, 0, 0, 4)
|
||||
func (src *NoData) Encode(dst []byte) ([]byte, error) {
|
||||
return append(dst, 'n', 0, 0, 0, 4), nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
6
vendor/github.com/jackc/pgx/v5/pgproto3/notice_response.go
generated
vendored
6
vendor/github.com/jackc/pgx/v5/pgproto3/notice_response.go
generated
vendored
@@ -12,6 +12,8 @@ func (dst *NoticeResponse) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *NoticeResponse) Encode(dst []byte) []byte {
|
||||
return append(dst, (*ErrorResponse)(src).marshalBinary('N')...)
|
||||
func (src *NoticeResponse) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'N')
|
||||
dst = (*ErrorResponse)(src).appendFields(dst)
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
12
vendor/github.com/jackc/pgx/v5/pgproto3/notification_response.go
generated
vendored
12
vendor/github.com/jackc/pgx/v5/pgproto3/notification_response.go
generated
vendored
@@ -45,20 +45,14 @@ func (dst *NotificationResponse) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *NotificationResponse) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'A')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
|
||||
func (src *NotificationResponse) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'A')
|
||||
dst = pgio.AppendUint32(dst, src.PID)
|
||||
dst = append(dst, src.Channel...)
|
||||
dst = append(dst, 0)
|
||||
dst = append(dst, src.Payload...)
|
||||
dst = append(dst, 0)
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
15
vendor/github.com/jackc/pgx/v5/pgproto3/parameter_description.go
generated
vendored
15
vendor/github.com/jackc/pgx/v5/pgproto3/parameter_description.go
generated
vendored
@@ -4,6 +4,8 @@ import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math"
|
||||
|
||||
"github.com/jackc/pgx/v5/internal/pgio"
|
||||
)
|
||||
@@ -39,19 +41,18 @@ func (dst *ParameterDescription) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *ParameterDescription) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 't')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
func (src *ParameterDescription) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 't')
|
||||
|
||||
if len(src.ParameterOIDs) > math.MaxUint16 {
|
||||
return nil, errors.New("too many parameter oids")
|
||||
}
|
||||
dst = pgio.AppendUint16(dst, uint16(len(src.ParameterOIDs)))
|
||||
for _, oid := range src.ParameterOIDs {
|
||||
dst = pgio.AppendUint32(dst, oid)
|
||||
}
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
14
vendor/github.com/jackc/pgx/v5/pgproto3/parameter_status.go
generated
vendored
14
vendor/github.com/jackc/pgx/v5/pgproto3/parameter_status.go
generated
vendored
@@ -3,8 +3,6 @@ package pgproto3
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/jackc/pgx/v5/internal/pgio"
|
||||
)
|
||||
|
||||
type ParameterStatus struct {
|
||||
@@ -37,19 +35,13 @@ func (dst *ParameterStatus) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *ParameterStatus) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'S')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
|
||||
func (src *ParameterStatus) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'S')
|
||||
dst = append(dst, src.Name...)
|
||||
dst = append(dst, 0)
|
||||
dst = append(dst, src.Value...)
|
||||
dst = append(dst, 0)
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
15
vendor/github.com/jackc/pgx/v5/pgproto3/parse.go
generated
vendored
15
vendor/github.com/jackc/pgx/v5/pgproto3/parse.go
generated
vendored
@@ -4,6 +4,8 @@ import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math"
|
||||
|
||||
"github.com/jackc/pgx/v5/internal/pgio"
|
||||
)
|
||||
@@ -52,24 +54,23 @@ func (dst *Parse) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *Parse) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'P')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
func (src *Parse) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'P')
|
||||
|
||||
dst = append(dst, src.Name...)
|
||||
dst = append(dst, 0)
|
||||
dst = append(dst, src.Query...)
|
||||
dst = append(dst, 0)
|
||||
|
||||
if len(src.ParameterOIDs) > math.MaxUint16 {
|
||||
return nil, errors.New("too many parameter oids")
|
||||
}
|
||||
dst = pgio.AppendUint16(dst, uint16(len(src.ParameterOIDs)))
|
||||
for _, oid := range src.ParameterOIDs {
|
||||
dst = pgio.AppendUint32(dst, oid)
|
||||
}
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
4
vendor/github.com/jackc/pgx/v5/pgproto3/parse_complete.go
generated
vendored
4
vendor/github.com/jackc/pgx/v5/pgproto3/parse_complete.go
generated
vendored
@@ -20,8 +20,8 @@ func (dst *ParseComplete) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *ParseComplete) Encode(dst []byte) []byte {
|
||||
return append(dst, '1', 0, 0, 0, 4)
|
||||
func (src *ParseComplete) Encode(dst []byte) ([]byte, error) {
|
||||
return append(dst, '1', 0, 0, 0, 4), nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
11
vendor/github.com/jackc/pgx/v5/pgproto3/password_message.go
generated
vendored
11
vendor/github.com/jackc/pgx/v5/pgproto3/password_message.go
generated
vendored
@@ -3,8 +3,6 @@ package pgproto3
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/jackc/pgx/v5/internal/pgio"
|
||||
)
|
||||
|
||||
type PasswordMessage struct {
|
||||
@@ -32,14 +30,11 @@ func (dst *PasswordMessage) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *PasswordMessage) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'p')
|
||||
dst = pgio.AppendInt32(dst, int32(4+len(src.Password)+1))
|
||||
|
||||
func (src *PasswordMessage) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'p')
|
||||
dst = append(dst, src.Password...)
|
||||
dst = append(dst, 0)
|
||||
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
37
vendor/github.com/jackc/pgx/v5/pgproto3/pgproto3.go
generated
vendored
37
vendor/github.com/jackc/pgx/v5/pgproto3/pgproto3.go
generated
vendored
@@ -4,8 +4,14 @@ import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/jackc/pgx/v5/internal/pgio"
|
||||
)
|
||||
|
||||
// maxMessageBodyLen is the maximum length of a message body in bytes. See PG_LARGE_MESSAGE_LIMIT in the PostgreSQL
|
||||
// source. It is defined as (MaxAllocSize - 1). MaxAllocSize is defined as 0x3fffffff.
|
||||
const maxMessageBodyLen = (0x3fffffff - 1)
|
||||
|
||||
// Message is the interface implemented by an object that can decode and encode
|
||||
// a particular PostgreSQL message.
|
||||
type Message interface {
|
||||
@@ -14,7 +20,7 @@ type Message interface {
|
||||
Decode(data []byte) error
|
||||
|
||||
// Encode appends itself to dst and returns the new buffer.
|
||||
Encode(dst []byte) []byte
|
||||
Encode(dst []byte) ([]byte, error)
|
||||
}
|
||||
|
||||
// FrontendMessage is a message sent by the frontend (i.e. the client).
|
||||
@@ -70,6 +76,15 @@ func (e *writeError) Unwrap() error {
|
||||
return e.err
|
||||
}
|
||||
|
||||
type ExceededMaxBodyLenErr struct {
|
||||
MaxExpectedBodyLen int
|
||||
ActualBodyLen int
|
||||
}
|
||||
|
||||
func (e *ExceededMaxBodyLenErr) Error() string {
|
||||
return fmt.Sprintf("invalid body length: expected at most %d, but got %d", e.MaxExpectedBodyLen, e.ActualBodyLen)
|
||||
}
|
||||
|
||||
// getValueFromJSON gets the value from a protocol message representation in JSON.
|
||||
func getValueFromJSON(v map[string]string) ([]byte, error) {
|
||||
if v == nil {
|
||||
@@ -83,3 +98,23 @@ func getValueFromJSON(v map[string]string) ([]byte, error) {
|
||||
}
|
||||
return nil, errors.New("unknown protocol representation")
|
||||
}
|
||||
|
||||
// beginMessage begins a new message of type t. It appends the message type and a placeholder for the message length to
|
||||
// dst. It returns the new buffer and the position of the message length placeholder.
|
||||
func beginMessage(dst []byte, t byte) ([]byte, int) {
|
||||
dst = append(dst, t)
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
return dst, sp
|
||||
}
|
||||
|
||||
// finishMessage finishes a message that was started with beginMessage. It computes the message length and writes it to
|
||||
// dst[sp]. If the message length is too large it returns an error. Otherwise it returns the final message buffer.
|
||||
func finishMessage(dst []byte, sp int) ([]byte, error) {
|
||||
messageBodyLen := len(dst[sp:])
|
||||
if messageBodyLen > maxMessageBodyLen {
|
||||
return nil, errors.New("message body too large")
|
||||
}
|
||||
pgio.SetInt32(dst[sp:], int32(messageBodyLen))
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
4
vendor/github.com/jackc/pgx/v5/pgproto3/portal_suspended.go
generated
vendored
4
vendor/github.com/jackc/pgx/v5/pgproto3/portal_suspended.go
generated
vendored
@@ -20,8 +20,8 @@ func (dst *PortalSuspended) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *PortalSuspended) Encode(dst []byte) []byte {
|
||||
return append(dst, 's', 0, 0, 0, 4)
|
||||
func (src *PortalSuspended) Encode(dst []byte) ([]byte, error) {
|
||||
return append(dst, 's', 0, 0, 0, 4), nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
11
vendor/github.com/jackc/pgx/v5/pgproto3/query.go
generated
vendored
11
vendor/github.com/jackc/pgx/v5/pgproto3/query.go
generated
vendored
@@ -3,8 +3,6 @@ package pgproto3
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/jackc/pgx/v5/internal/pgio"
|
||||
)
|
||||
|
||||
type Query struct {
|
||||
@@ -28,14 +26,11 @@ func (dst *Query) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *Query) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'Q')
|
||||
dst = pgio.AppendInt32(dst, int32(4+len(src.String)+1))
|
||||
|
||||
func (src *Query) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'Q')
|
||||
dst = append(dst, src.String...)
|
||||
dst = append(dst, 0)
|
||||
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
4
vendor/github.com/jackc/pgx/v5/pgproto3/ready_for_query.go
generated
vendored
4
vendor/github.com/jackc/pgx/v5/pgproto3/ready_for_query.go
generated
vendored
@@ -25,8 +25,8 @@ func (dst *ReadyForQuery) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *ReadyForQuery) Encode(dst []byte) []byte {
|
||||
return append(dst, 'Z', 0, 0, 0, 5, src.TxStatus)
|
||||
func (src *ReadyForQuery) Encode(dst []byte) ([]byte, error) {
|
||||
return append(dst, 'Z', 0, 0, 0, 5, src.TxStatus), nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
15
vendor/github.com/jackc/pgx/v5/pgproto3/row_description.go
generated
vendored
15
vendor/github.com/jackc/pgx/v5/pgproto3/row_description.go
generated
vendored
@@ -4,6 +4,8 @@ import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math"
|
||||
|
||||
"github.com/jackc/pgx/v5/internal/pgio"
|
||||
)
|
||||
@@ -99,11 +101,12 @@ func (dst *RowDescription) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *RowDescription) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'T')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
func (src *RowDescription) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'T')
|
||||
|
||||
if len(src.Fields) > math.MaxUint16 {
|
||||
return nil, errors.New("too many fields")
|
||||
}
|
||||
dst = pgio.AppendUint16(dst, uint16(len(src.Fields)))
|
||||
for _, fd := range src.Fields {
|
||||
dst = append(dst, fd.Name...)
|
||||
@@ -117,9 +120,7 @@ func (src *RowDescription) Encode(dst []byte) []byte {
|
||||
dst = pgio.AppendInt16(dst, fd.Format)
|
||||
}
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
10
vendor/github.com/jackc/pgx/v5/pgproto3/sasl_initial_response.go
generated
vendored
10
vendor/github.com/jackc/pgx/v5/pgproto3/sasl_initial_response.go
generated
vendored
@@ -39,10 +39,8 @@ func (dst *SASLInitialResponse) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *SASLInitialResponse) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'p')
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
func (src *SASLInitialResponse) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'p')
|
||||
|
||||
dst = append(dst, []byte(src.AuthMechanism)...)
|
||||
dst = append(dst, 0)
|
||||
@@ -50,9 +48,7 @@ func (src *SASLInitialResponse) Encode(dst []byte) []byte {
|
||||
dst = pgio.AppendInt32(dst, int32(len(src.Data)))
|
||||
dst = append(dst, src.Data...)
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
11
vendor/github.com/jackc/pgx/v5/pgproto3/sasl_response.go
generated
vendored
11
vendor/github.com/jackc/pgx/v5/pgproto3/sasl_response.go
generated
vendored
@@ -3,8 +3,6 @@ package pgproto3
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/jackc/pgx/v5/internal/pgio"
|
||||
)
|
||||
|
||||
type SASLResponse struct {
|
||||
@@ -22,13 +20,10 @@ func (dst *SASLResponse) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *SASLResponse) Encode(dst []byte) []byte {
|
||||
dst = append(dst, 'p')
|
||||
dst = pgio.AppendInt32(dst, int32(4+len(src.Data)))
|
||||
|
||||
func (src *SASLResponse) Encode(dst []byte) ([]byte, error) {
|
||||
dst, sp := beginMessage(dst, 'p')
|
||||
dst = append(dst, src.Data...)
|
||||
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
4
vendor/github.com/jackc/pgx/v5/pgproto3/ssl_request.go
generated
vendored
4
vendor/github.com/jackc/pgx/v5/pgproto3/ssl_request.go
generated
vendored
@@ -31,10 +31,10 @@ func (dst *SSLRequest) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 4 byte message length.
|
||||
func (src *SSLRequest) Encode(dst []byte) []byte {
|
||||
func (src *SSLRequest) Encode(dst []byte) ([]byte, error) {
|
||||
dst = pgio.AppendInt32(dst, 8)
|
||||
dst = pgio.AppendInt32(dst, sslRequestNumber)
|
||||
return dst
|
||||
return dst, nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
10
vendor/github.com/jackc/pgx/v5/pgproto3/startup_message.go
generated
vendored
10
vendor/github.com/jackc/pgx/v5/pgproto3/startup_message.go
generated
vendored
@@ -38,14 +38,14 @@ func (dst *StartupMessage) Decode(src []byte) error {
|
||||
for {
|
||||
idx := bytes.IndexByte(src[rp:], 0)
|
||||
if idx < 0 {
|
||||
return &invalidMessageFormatErr{messageType: "StartupMesage"}
|
||||
return &invalidMessageFormatErr{messageType: "StartupMessage"}
|
||||
}
|
||||
key := string(src[rp : rp+idx])
|
||||
rp += idx + 1
|
||||
|
||||
idx = bytes.IndexByte(src[rp:], 0)
|
||||
if idx < 0 {
|
||||
return &invalidMessageFormatErr{messageType: "StartupMesage"}
|
||||
return &invalidMessageFormatErr{messageType: "StartupMessage"}
|
||||
}
|
||||
value := string(src[rp : rp+idx])
|
||||
rp += idx + 1
|
||||
@@ -64,7 +64,7 @@ func (dst *StartupMessage) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *StartupMessage) Encode(dst []byte) []byte {
|
||||
func (src *StartupMessage) Encode(dst []byte) ([]byte, error) {
|
||||
sp := len(dst)
|
||||
dst = pgio.AppendInt32(dst, -1)
|
||||
|
||||
@@ -77,9 +77,7 @@ func (src *StartupMessage) Encode(dst []byte) []byte {
|
||||
}
|
||||
dst = append(dst, 0)
|
||||
|
||||
pgio.SetInt32(dst[sp:], int32(len(dst[sp:])))
|
||||
|
||||
return dst
|
||||
return finishMessage(dst, sp)
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
4
vendor/github.com/jackc/pgx/v5/pgproto3/sync.go
generated
vendored
4
vendor/github.com/jackc/pgx/v5/pgproto3/sync.go
generated
vendored
@@ -20,8 +20,8 @@ func (dst *Sync) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *Sync) Encode(dst []byte) []byte {
|
||||
return append(dst, 'S', 0, 0, 0, 4)
|
||||
func (src *Sync) Encode(dst []byte) ([]byte, error) {
|
||||
return append(dst, 'S', 0, 0, 0, 4), nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
4
vendor/github.com/jackc/pgx/v5/pgproto3/terminate.go
generated
vendored
4
vendor/github.com/jackc/pgx/v5/pgproto3/terminate.go
generated
vendored
@@ -20,8 +20,8 @@ func (dst *Terminate) Decode(src []byte) error {
|
||||
}
|
||||
|
||||
// Encode encodes src into dst. dst will include the 1 byte message type identifier and the 4 byte message length.
|
||||
func (src *Terminate) Encode(dst []byte) []byte {
|
||||
return append(dst, 'X', 0, 0, 0, 4)
|
||||
func (src *Terminate) Encode(dst []byte) ([]byte, error) {
|
||||
return append(dst, 'X', 0, 0, 0, 4), nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements encoding/json.Marshaler.
|
||||
|
||||
22
vendor/github.com/jackc/pgx/v5/pgtype/array.go
generated
vendored
22
vendor/github.com/jackc/pgx/v5/pgtype/array.go
generated
vendored
@@ -110,7 +110,7 @@ func parseUntypedTextArray(src string) (*untypedTextArray, error) {
|
||||
|
||||
r, _, err := buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid array: %v", err)
|
||||
return nil, fmt.Errorf("invalid array: %w", err)
|
||||
}
|
||||
|
||||
var explicitDimensions []ArrayDimension
|
||||
@@ -122,7 +122,7 @@ func parseUntypedTextArray(src string) (*untypedTextArray, error) {
|
||||
for {
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid array: %v", err)
|
||||
return nil, fmt.Errorf("invalid array: %w", err)
|
||||
}
|
||||
|
||||
if r == '=' {
|
||||
@@ -133,12 +133,12 @@ func parseUntypedTextArray(src string) (*untypedTextArray, error) {
|
||||
|
||||
lower, err := arrayParseInteger(buf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid array: %v", err)
|
||||
return nil, fmt.Errorf("invalid array: %w", err)
|
||||
}
|
||||
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid array: %v", err)
|
||||
return nil, fmt.Errorf("invalid array: %w", err)
|
||||
}
|
||||
|
||||
if r != ':' {
|
||||
@@ -147,12 +147,12 @@ func parseUntypedTextArray(src string) (*untypedTextArray, error) {
|
||||
|
||||
upper, err := arrayParseInteger(buf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid array: %v", err)
|
||||
return nil, fmt.Errorf("invalid array: %w", err)
|
||||
}
|
||||
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid array: %v", err)
|
||||
return nil, fmt.Errorf("invalid array: %w", err)
|
||||
}
|
||||
|
||||
if r != ']' {
|
||||
@@ -164,12 +164,12 @@ func parseUntypedTextArray(src string) (*untypedTextArray, error) {
|
||||
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid array: %v", err)
|
||||
return nil, fmt.Errorf("invalid array: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if r != '{' {
|
||||
return nil, fmt.Errorf("invalid array, expected '{': %v", err)
|
||||
return nil, fmt.Errorf("invalid array, expected '{' got %v", r)
|
||||
}
|
||||
|
||||
implicitDimensions := []ArrayDimension{{LowerBound: 1, Length: 0}}
|
||||
@@ -178,7 +178,7 @@ func parseUntypedTextArray(src string) (*untypedTextArray, error) {
|
||||
for {
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid array: %v", err)
|
||||
return nil, fmt.Errorf("invalid array: %w", err)
|
||||
}
|
||||
|
||||
if r == '{' {
|
||||
@@ -195,7 +195,7 @@ func parseUntypedTextArray(src string) (*untypedTextArray, error) {
|
||||
for {
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid array: %v", err)
|
||||
return nil, fmt.Errorf("invalid array: %w", err)
|
||||
}
|
||||
|
||||
switch r {
|
||||
@@ -214,7 +214,7 @@ func parseUntypedTextArray(src string) (*untypedTextArray, error) {
|
||||
buf.UnreadRune()
|
||||
value, quoted, err := arrayParseValue(buf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid array value: %v", err)
|
||||
return nil, fmt.Errorf("invalid array value: %w", err)
|
||||
}
|
||||
if currentDim == counterDim {
|
||||
implicitDimensions[currentDim].Length++
|
||||
|
||||
3
vendor/github.com/jackc/pgx/v5/pgtype/array_codec.go
generated
vendored
3
vendor/github.com/jackc/pgx/v5/pgtype/array_codec.go
generated
vendored
@@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/jackc/pgx/v5/internal/anynil"
|
||||
"github.com/jackc/pgx/v5/internal/pgio"
|
||||
)
|
||||
|
||||
@@ -230,7 +229,7 @@ func (c *ArrayCodec) PlanScan(m *Map, oid uint32, format int16, target any) Scan
|
||||
|
||||
// target / arrayScanner might be a pointer to a nil. If it is create one so we can call ScanIndexType to plan the
|
||||
// scan of the elements.
|
||||
if anynil.Is(target) {
|
||||
if isNil, _ := isNilDriverValuer(target); isNil {
|
||||
arrayScanner = reflect.New(reflect.TypeOf(target).Elem()).Interface().(ArraySetter)
|
||||
}
|
||||
|
||||
|
||||
4
vendor/github.com/jackc/pgx/v5/pgtype/bits.go
generated
vendored
4
vendor/github.com/jackc/pgx/v5/pgtype/bits.go
generated
vendored
@@ -176,8 +176,10 @@ func (scanPlanBinaryBitsToBitsScanner) Scan(src []byte, dst any) error {
|
||||
|
||||
bitLen := int32(binary.BigEndian.Uint32(src))
|
||||
rp := 4
|
||||
buf := make([]byte, len(src[rp:]))
|
||||
copy(buf, src[rp:])
|
||||
|
||||
return scanner.ScanBits(Bits{Bytes: src[rp:], Len: bitLen, Valid: true})
|
||||
return scanner.ScanBits(Bits{Bytes: buf, Len: bitLen, Valid: true})
|
||||
}
|
||||
|
||||
type scanPlanTextAnyToBitsScanner struct{}
|
||||
|
||||
4
vendor/github.com/jackc/pgx/v5/pgtype/builtin_wrappers.go
generated
vendored
4
vendor/github.com/jackc/pgx/v5/pgtype/builtin_wrappers.go
generated
vendored
@@ -231,7 +231,7 @@ func (w *uint64Wrapper) ScanNumeric(v Numeric) error {
|
||||
|
||||
bi, err := v.toBigInt()
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot scan into *uint64: %v", err)
|
||||
return fmt.Errorf("cannot scan into *uint64: %w", err)
|
||||
}
|
||||
|
||||
if !bi.IsUint64() {
|
||||
@@ -284,7 +284,7 @@ func (w *uintWrapper) ScanNumeric(v Numeric) error {
|
||||
|
||||
bi, err := v.toBigInt()
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot scan into *uint: %v", err)
|
||||
return fmt.Errorf("cannot scan into *uint: %w", err)
|
||||
}
|
||||
|
||||
if !bi.IsUint64() {
|
||||
|
||||
6
vendor/github.com/jackc/pgx/v5/pgtype/date.go
generated
vendored
6
vendor/github.com/jackc/pgx/v5/pgtype/date.go
generated
vendored
@@ -282,17 +282,17 @@ func (scanPlanTextAnyToDateScanner) Scan(src []byte, dst any) error {
|
||||
if match != nil {
|
||||
year, err := strconv.ParseInt(match[1], 10, 32)
|
||||
if err != nil {
|
||||
return fmt.Errorf("BUG: cannot parse date that regexp matched (year): %v", err)
|
||||
return fmt.Errorf("BUG: cannot parse date that regexp matched (year): %w", err)
|
||||
}
|
||||
|
||||
month, err := strconv.ParseInt(match[2], 10, 32)
|
||||
if err != nil {
|
||||
return fmt.Errorf("BUG: cannot parse date that regexp matched (month): %v", err)
|
||||
return fmt.Errorf("BUG: cannot parse date that regexp matched (month): %w", err)
|
||||
}
|
||||
|
||||
day, err := strconv.ParseInt(match[3], 10, 32)
|
||||
if err != nil {
|
||||
return fmt.Errorf("BUG: cannot parse date that regexp matched (month): %v", err)
|
||||
return fmt.Errorf("BUG: cannot parse date that regexp matched (month): %w", err)
|
||||
}
|
||||
|
||||
// BC matched
|
||||
|
||||
17
vendor/github.com/jackc/pgx/v5/pgtype/doc.go
generated
vendored
17
vendor/github.com/jackc/pgx/v5/pgtype/doc.go
generated
vendored
@@ -53,6 +53,9 @@ similar fashion to database/sql. The second is to use a pointer to a pointer.
|
||||
return err
|
||||
}
|
||||
|
||||
When using nullable pgtype types as parameters for queries, one has to remember
|
||||
to explicitly set their Valid field to true, otherwise the parameter's value will be NULL.
|
||||
|
||||
JSON Support
|
||||
|
||||
pgtype automatically marshals and unmarshals data from json and jsonb PostgreSQL types.
|
||||
@@ -67,7 +70,7 @@ See example_custom_type_test.go for an example of a custom type for the PostgreS
|
||||
|
||||
Sometimes pgx supports a PostgreSQL type such as numeric but the Go type is in an external package that does not have
|
||||
pgx support such as github.com/shopspring/decimal. These types can be registered with pgtype with custom conversion
|
||||
logic. See https://github.com/jackc/pgx-shopspring-decimal and https://github.com/jackc/pgx-gofrs-uuid for a example
|
||||
logic. See https://github.com/jackc/pgx-shopspring-decimal and https://github.com/jackc/pgx-gofrs-uuid for example
|
||||
integrations.
|
||||
|
||||
New PostgreSQL Type Support
|
||||
@@ -139,6 +142,16 @@ Compatibility with database/sql
|
||||
pgtype also includes support for custom types implementing the database/sql.Scanner and database/sql/driver.Valuer
|
||||
interfaces.
|
||||
|
||||
Encoding Typed Nils
|
||||
|
||||
pgtype encodes untyped and typed nils (e.g. nil and []byte(nil)) to the SQL NULL value without going through the Codec
|
||||
system. This means that Codecs and other encoding logic do not have to handle nil or *T(nil).
|
||||
|
||||
However, database/sql compatibility requires Value to be called on T(nil) when T implements driver.Valuer. Therefore,
|
||||
driver.Valuer values are only considered NULL when *T(nil) where driver.Valuer is implemented on T not on *T. See
|
||||
https://github.com/golang/go/issues/8415 and
|
||||
https://github.com/golang/go/commit/0ce1d79a6a771f7449ec493b993ed2a720917870.
|
||||
|
||||
Child Records
|
||||
|
||||
pgtype's support for arrays and composite records can be used to load records and their children in a single query. See
|
||||
@@ -149,7 +162,7 @@ Overview of Scanning Implementation
|
||||
The first step is to use the OID to lookup the correct Codec. If the OID is unavailable, Map will try to find the OID
|
||||
from previous calls of Map.RegisterDefaultPgType. The Map will call the Codec's PlanScan method to get a plan for
|
||||
scanning into the Go value. A Codec will support scanning into one or more Go types. Oftentime these Go types are
|
||||
interfaces rather than explicit types. For example, PointCodec can use any Go type that implments the PointScanner and
|
||||
interfaces rather than explicit types. For example, PointCodec can use any Go type that implements the PointScanner and
|
||||
PointValuer interfaces.
|
||||
|
||||
If a Go value is not supported directly by a Codec then Map will try wrapping it with additional logic and try again.
|
||||
|
||||
28
vendor/github.com/jackc/pgx/v5/pgtype/float4.go
generated
vendored
28
vendor/github.com/jackc/pgx/v5/pgtype/float4.go
generated
vendored
@@ -3,6 +3,7 @@ package pgtype
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
@@ -65,6 +66,29 @@ func (f Float4) Value() (driver.Value, error) {
|
||||
return float64(f.Float32), nil
|
||||
}
|
||||
|
||||
func (f Float4) MarshalJSON() ([]byte, error) {
|
||||
if !f.Valid {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
return json.Marshal(f.Float32)
|
||||
}
|
||||
|
||||
func (f *Float4) UnmarshalJSON(b []byte) error {
|
||||
var n *float32
|
||||
err := json.Unmarshal(b, &n)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if n == nil {
|
||||
*f = Float4{}
|
||||
} else {
|
||||
*f = Float4{Float32: *n, Valid: true}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Float4Codec struct{}
|
||||
|
||||
func (Float4Codec) FormatSupported(format int16) bool {
|
||||
@@ -273,12 +297,12 @@ func (c Float4Codec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, sr
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var n float64
|
||||
var n float32
|
||||
err := codecScan(c, m, oid, format, src, &n)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return n, nil
|
||||
return float64(n), nil
|
||||
}
|
||||
|
||||
func (c Float4Codec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {
|
||||
|
||||
30
vendor/github.com/jackc/pgx/v5/pgtype/float8.go
generated
vendored
30
vendor/github.com/jackc/pgx/v5/pgtype/float8.go
generated
vendored
@@ -74,6 +74,29 @@ func (f Float8) Value() (driver.Value, error) {
|
||||
return f.Float64, nil
|
||||
}
|
||||
|
||||
func (f Float8) MarshalJSON() ([]byte, error) {
|
||||
if !f.Valid {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
return json.Marshal(f.Float64)
|
||||
}
|
||||
|
||||
func (f *Float8) UnmarshalJSON(b []byte) error {
|
||||
var n *float64
|
||||
err := json.Unmarshal(b, &n)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if n == nil {
|
||||
*f = Float8{}
|
||||
} else {
|
||||
*f = Float8{Float64: *n, Valid: true}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type Float8Codec struct{}
|
||||
|
||||
func (Float8Codec) FormatSupported(format int16) bool {
|
||||
@@ -109,13 +132,6 @@ func (Float8Codec) PlanEncode(m *Map, oid uint32, format int16, value any) Encod
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Float8) MarshalJSON() ([]byte, error) {
|
||||
if !f.Valid {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
return json.Marshal(f.Float64)
|
||||
}
|
||||
|
||||
type encodePlanFloat8CodecBinaryFloat64 struct{}
|
||||
|
||||
func (encodePlanFloat8CodecBinaryFloat64) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
||||
|
||||
2
vendor/github.com/jackc/pgx/v5/pgtype/inet.go
generated
vendored
2
vendor/github.com/jackc/pgx/v5/pgtype/inet.go
generated
vendored
@@ -156,7 +156,7 @@ func (scanPlanBinaryInetToNetipPrefixScanner) Scan(src []byte, dst any) error {
|
||||
}
|
||||
|
||||
if len(src) != 8 && len(src) != 20 {
|
||||
return fmt.Errorf("Received an invalid size for a inet: %d", len(src))
|
||||
return fmt.Errorf("Received an invalid size for an inet: %d", len(src))
|
||||
}
|
||||
|
||||
// ignore family
|
||||
|
||||
25
vendor/github.com/jackc/pgx/v5/pgtype/interval.go
generated
vendored
25
vendor/github.com/jackc/pgx/v5/pgtype/interval.go
generated
vendored
@@ -144,10 +144,15 @@ func (encodePlanIntervalCodecText) Encode(value any, buf []byte) (newBuf []byte,
|
||||
hours := absMicroseconds / microsecondsPerHour
|
||||
minutes := (absMicroseconds % microsecondsPerHour) / microsecondsPerMinute
|
||||
seconds := (absMicroseconds % microsecondsPerMinute) / microsecondsPerSecond
|
||||
microseconds := absMicroseconds % microsecondsPerSecond
|
||||
|
||||
timeStr := fmt.Sprintf("%02d:%02d:%02d.%06d", hours, minutes, seconds, microseconds)
|
||||
timeStr := fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds)
|
||||
buf = append(buf, timeStr...)
|
||||
|
||||
microseconds := absMicroseconds % microsecondsPerSecond
|
||||
if microseconds != 0 {
|
||||
buf = append(buf, fmt.Sprintf(".%06d", microseconds)...)
|
||||
}
|
||||
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
@@ -179,7 +184,7 @@ func (scanPlanBinaryIntervalToIntervalScanner) Scan(src []byte, dst any) error {
|
||||
}
|
||||
|
||||
if len(src) != 16 {
|
||||
return fmt.Errorf("Received an invalid size for a interval: %d", len(src))
|
||||
return fmt.Errorf("Received an invalid size for an interval: %d", len(src))
|
||||
}
|
||||
|
||||
microseconds := int64(binary.BigEndian.Uint64(src))
|
||||
@@ -242,21 +247,21 @@ func (scanPlanTextAnyToIntervalScanner) Scan(src []byte, dst any) error {
|
||||
return fmt.Errorf("bad interval minute format: %s", timeParts[1])
|
||||
}
|
||||
|
||||
secondParts := strings.SplitN(timeParts[2], ".", 2)
|
||||
sec, secFrac, secFracFound := strings.Cut(timeParts[2], ".")
|
||||
|
||||
seconds, err := strconv.ParseInt(secondParts[0], 10, 64)
|
||||
seconds, err := strconv.ParseInt(sec, 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("bad interval second format: %s", secondParts[0])
|
||||
return fmt.Errorf("bad interval second format: %s", sec)
|
||||
}
|
||||
|
||||
var uSeconds int64
|
||||
if len(secondParts) == 2 {
|
||||
uSeconds, err = strconv.ParseInt(secondParts[1], 10, 64)
|
||||
if secFracFound {
|
||||
uSeconds, err = strconv.ParseInt(secFrac, 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("bad interval decimal format: %s", secondParts[1])
|
||||
return fmt.Errorf("bad interval decimal format: %s", secFrac)
|
||||
}
|
||||
|
||||
for i := 0; i < 6-len(secondParts[1]); i++ {
|
||||
for i := 0; i < 6-len(secFrac); i++ {
|
||||
uSeconds *= 10
|
||||
}
|
||||
}
|
||||
|
||||
84
vendor/github.com/jackc/pgx/v5/pgtype/json.go
generated
vendored
84
vendor/github.com/jackc/pgx/v5/pgtype/json.go
generated
vendored
@@ -8,35 +8,48 @@ import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type JSONCodec struct{}
|
||||
type JSONCodec struct {
|
||||
Marshal func(v any) ([]byte, error)
|
||||
Unmarshal func(data []byte, v any) error
|
||||
}
|
||||
|
||||
func (JSONCodec) FormatSupported(format int16) bool {
|
||||
func (*JSONCodec) FormatSupported(format int16) bool {
|
||||
return format == TextFormatCode || format == BinaryFormatCode
|
||||
}
|
||||
|
||||
func (JSONCodec) PreferredFormat() int16 {
|
||||
func (*JSONCodec) PreferredFormat() int16 {
|
||||
return TextFormatCode
|
||||
}
|
||||
|
||||
func (c JSONCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {
|
||||
func (c *JSONCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {
|
||||
switch value.(type) {
|
||||
case string:
|
||||
return encodePlanJSONCodecEitherFormatString{}
|
||||
case []byte:
|
||||
return encodePlanJSONCodecEitherFormatByteSlice{}
|
||||
|
||||
// Handle json.RawMessage specifically because if it is run through json.Marshal it may be mutated.
|
||||
// e.g. `{"foo": "bar"}` -> `{"foo":"bar"}`.
|
||||
case json.RawMessage:
|
||||
return encodePlanJSONCodecEitherFormatJSONRawMessage{}
|
||||
|
||||
// Cannot rely on driver.Valuer being handled later because anything can be marshalled.
|
||||
//
|
||||
// https://github.com/jackc/pgx/issues/1430
|
||||
//
|
||||
// Check for driver.Valuer must come before json.Marshaler so that it is guaranteed to be used
|
||||
// when both are implemented https://github.com/jackc/pgx/issues/1805
|
||||
case driver.Valuer:
|
||||
return &encodePlanDriverValuer{m: m, oid: oid, formatCode: format}
|
||||
|
||||
// Must come before trying wrap encode plans because a pointer to a struct may be unwrapped to a struct that can be
|
||||
// marshalled.
|
||||
//
|
||||
// https://github.com/jackc/pgx/issues/1681
|
||||
case json.Marshaler:
|
||||
return encodePlanJSONCodecEitherFormatMarshal{}
|
||||
|
||||
// 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}
|
||||
return &encodePlanJSONCodecEitherFormatMarshal{
|
||||
marshal: c.Marshal,
|
||||
}
|
||||
}
|
||||
|
||||
// Because anything can be marshalled the normal wrapping in Map.PlanScan doesn't get a chance to run. So try the
|
||||
@@ -53,7 +66,9 @@ func (c JSONCodec) PlanEncode(m *Map, oid uint32, format int16, value any) Encod
|
||||
}
|
||||
}
|
||||
|
||||
return encodePlanJSONCodecEitherFormatMarshal{}
|
||||
return &encodePlanJSONCodecEitherFormatMarshal{
|
||||
marshal: c.Marshal,
|
||||
}
|
||||
}
|
||||
|
||||
type encodePlanJSONCodecEitherFormatString struct{}
|
||||
@@ -76,10 +91,24 @@ func (encodePlanJSONCodecEitherFormatByteSlice) Encode(value any, buf []byte) (n
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
type encodePlanJSONCodecEitherFormatMarshal struct{}
|
||||
type encodePlanJSONCodecEitherFormatJSONRawMessage struct{}
|
||||
|
||||
func (encodePlanJSONCodecEitherFormatMarshal) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
||||
jsonBytes, err := json.Marshal(value)
|
||||
func (encodePlanJSONCodecEitherFormatJSONRawMessage) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
||||
jsonBytes := value.(json.RawMessage)
|
||||
if jsonBytes == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
buf = append(buf, jsonBytes...)
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
type encodePlanJSONCodecEitherFormatMarshal struct {
|
||||
marshal func(v any) ([]byte, error)
|
||||
}
|
||||
|
||||
func (e *encodePlanJSONCodecEitherFormatMarshal) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
||||
jsonBytes, err := e.marshal(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -88,7 +117,7 @@ func (encodePlanJSONCodecEitherFormatMarshal) Encode(value any, buf []byte) (new
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
func (JSONCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
|
||||
func (c *JSONCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
|
||||
switch target.(type) {
|
||||
case *string:
|
||||
return scanPlanAnyToString{}
|
||||
@@ -121,7 +150,9 @@ func (JSONCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan
|
||||
return &scanPlanSQLScanner{formatCode: format}
|
||||
}
|
||||
|
||||
return scanPlanJSONToJSONUnmarshal{}
|
||||
return &scanPlanJSONToJSONUnmarshal{
|
||||
unmarshal: c.Unmarshal,
|
||||
}
|
||||
}
|
||||
|
||||
type scanPlanAnyToString struct{}
|
||||
@@ -146,16 +177,11 @@ func (scanPlanJSONToByteSlice) Scan(src []byte, dst any) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type scanPlanJSONToBytesScanner struct{}
|
||||
|
||||
func (scanPlanJSONToBytesScanner) Scan(src []byte, dst any) error {
|
||||
scanner := (dst).(BytesScanner)
|
||||
return scanner.ScanBytes(src)
|
||||
type scanPlanJSONToJSONUnmarshal struct {
|
||||
unmarshal func(data []byte, v any) error
|
||||
}
|
||||
|
||||
type scanPlanJSONToJSONUnmarshal struct{}
|
||||
|
||||
func (scanPlanJSONToJSONUnmarshal) Scan(src []byte, dst any) error {
|
||||
func (s *scanPlanJSONToJSONUnmarshal) Scan(src []byte, dst any) error {
|
||||
if src == nil {
|
||||
dstValue := reflect.ValueOf(dst)
|
||||
if dstValue.Kind() == reflect.Ptr {
|
||||
@@ -173,10 +199,10 @@ func (scanPlanJSONToJSONUnmarshal) Scan(src []byte, dst any) error {
|
||||
elem := reflect.ValueOf(dst).Elem()
|
||||
elem.Set(reflect.Zero(elem.Type()))
|
||||
|
||||
return json.Unmarshal(src, dst)
|
||||
return s.unmarshal(src, dst)
|
||||
}
|
||||
|
||||
func (c JSONCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {
|
||||
func (c *JSONCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {
|
||||
if src == nil {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -186,12 +212,12 @@ func (c JSONCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src
|
||||
return dstBuf, nil
|
||||
}
|
||||
|
||||
func (c JSONCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {
|
||||
func (c *JSONCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {
|
||||
if src == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var dst any
|
||||
err := json.Unmarshal(src, &dst)
|
||||
err := c.Unmarshal(src, &dst)
|
||||
return dst, err
|
||||
}
|
||||
|
||||
28
vendor/github.com/jackc/pgx/v5/pgtype/jsonb.go
generated
vendored
28
vendor/github.com/jackc/pgx/v5/pgtype/jsonb.go
generated
vendored
@@ -2,29 +2,31 @@ package pgtype
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type JSONBCodec struct{}
|
||||
type JSONBCodec struct {
|
||||
Marshal func(v any) ([]byte, error)
|
||||
Unmarshal func(data []byte, v any) error
|
||||
}
|
||||
|
||||
func (JSONBCodec) FormatSupported(format int16) bool {
|
||||
func (*JSONBCodec) FormatSupported(format int16) bool {
|
||||
return format == TextFormatCode || format == BinaryFormatCode
|
||||
}
|
||||
|
||||
func (JSONBCodec) PreferredFormat() int16 {
|
||||
func (*JSONBCodec) PreferredFormat() int16 {
|
||||
return TextFormatCode
|
||||
}
|
||||
|
||||
func (JSONBCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {
|
||||
func (c *JSONBCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {
|
||||
switch format {
|
||||
case BinaryFormatCode:
|
||||
plan := JSONCodec{}.PlanEncode(m, oid, TextFormatCode, value)
|
||||
plan := (&JSONCodec{Marshal: c.Marshal, Unmarshal: c.Unmarshal}).PlanEncode(m, oid, TextFormatCode, value)
|
||||
if plan != nil {
|
||||
return &encodePlanJSONBCodecBinaryWrapper{textPlan: plan}
|
||||
}
|
||||
case TextFormatCode:
|
||||
return JSONCodec{}.PlanEncode(m, oid, format, value)
|
||||
return (&JSONCodec{Marshal: c.Marshal, Unmarshal: c.Unmarshal}).PlanEncode(m, oid, format, value)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -39,15 +41,15 @@ func (plan *encodePlanJSONBCodecBinaryWrapper) Encode(value any, buf []byte) (ne
|
||||
return plan.textPlan.Encode(value, buf)
|
||||
}
|
||||
|
||||
func (JSONBCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
|
||||
func (c *JSONBCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
|
||||
switch format {
|
||||
case BinaryFormatCode:
|
||||
plan := JSONCodec{}.PlanScan(m, oid, TextFormatCode, target)
|
||||
plan := (&JSONCodec{Marshal: c.Marshal, Unmarshal: c.Unmarshal}).PlanScan(m, oid, TextFormatCode, target)
|
||||
if plan != nil {
|
||||
return &scanPlanJSONBCodecBinaryUnwrapper{textPlan: plan}
|
||||
}
|
||||
case TextFormatCode:
|
||||
return JSONCodec{}.PlanScan(m, oid, format, target)
|
||||
return (&JSONCodec{Marshal: c.Marshal, Unmarshal: c.Unmarshal}).PlanScan(m, oid, format, target)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -73,7 +75,7 @@ func (plan *scanPlanJSONBCodecBinaryUnwrapper) Scan(src []byte, dst any) error {
|
||||
return plan.textPlan.Scan(src[1:], dst)
|
||||
}
|
||||
|
||||
func (c JSONBCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {
|
||||
func (c *JSONBCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {
|
||||
if src == nil {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -100,7 +102,7 @@ func (c JSONBCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src
|
||||
}
|
||||
}
|
||||
|
||||
func (c JSONBCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {
|
||||
func (c *JSONBCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {
|
||||
if src == nil {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -122,6 +124,6 @@ func (c JSONBCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (a
|
||||
}
|
||||
|
||||
var dst any
|
||||
err := json.Unmarshal(src, &dst)
|
||||
err := c.Unmarshal(src, &dst)
|
||||
return dst, err
|
||||
}
|
||||
|
||||
8
vendor/github.com/jackc/pgx/v5/pgtype/multirange.go
generated
vendored
8
vendor/github.com/jackc/pgx/v5/pgtype/multirange.go
generated
vendored
@@ -339,18 +339,18 @@ func parseUntypedTextMultirange(src []byte) ([]string, error) {
|
||||
|
||||
r, _, err := buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid array: %v", err)
|
||||
return nil, fmt.Errorf("invalid array: %w", err)
|
||||
}
|
||||
|
||||
if r != '{' {
|
||||
return nil, fmt.Errorf("invalid multirange, expected '{': %v", err)
|
||||
return nil, fmt.Errorf("invalid multirange, expected '{' got %v", r)
|
||||
}
|
||||
|
||||
parseValueLoop:
|
||||
for {
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid multirange: %v", err)
|
||||
return nil, fmt.Errorf("invalid multirange: %w", err)
|
||||
}
|
||||
|
||||
switch r {
|
||||
@@ -361,7 +361,7 @@ parseValueLoop:
|
||||
buf.UnreadRune()
|
||||
value, err := parseRange(buf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid multirange value: %v", err)
|
||||
return nil, fmt.Errorf("invalid multirange value: %w", err)
|
||||
}
|
||||
elements = append(elements, value)
|
||||
}
|
||||
|
||||
20
vendor/github.com/jackc/pgx/v5/pgtype/numeric.go
generated
vendored
20
vendor/github.com/jackc/pgx/v5/pgtype/numeric.go
generated
vendored
@@ -119,6 +119,26 @@ func (n Numeric) Int64Value() (Int8, error) {
|
||||
return Int8{Int64: bi.Int64(), Valid: true}, nil
|
||||
}
|
||||
|
||||
func (n *Numeric) ScanScientific(src string) error {
|
||||
if !strings.ContainsAny("eE", src) {
|
||||
return scanPlanTextAnyToNumericScanner{}.Scan([]byte(src), n)
|
||||
}
|
||||
|
||||
if bigF, ok := new(big.Float).SetString(string(src)); ok {
|
||||
smallF, _ := bigF.Float64()
|
||||
src = strconv.FormatFloat(smallF, 'f', -1, 64)
|
||||
}
|
||||
|
||||
num, exp, err := parseNumericString(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*n = Numeric{Int: num, Exp: exp, Valid: true}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *Numeric) toBigInt() (*big.Int, error) {
|
||||
if n.Exp == 0 {
|
||||
return n.Int, nil
|
||||
|
||||
131
vendor/github.com/jackc/pgx/v5/pgtype/pgtype.go
generated
vendored
131
vendor/github.com/jackc/pgx/v5/pgtype/pgtype.go
generated
vendored
@@ -26,6 +26,8 @@ const (
|
||||
XIDOID = 28
|
||||
CIDOID = 29
|
||||
JSONOID = 114
|
||||
XMLOID = 142
|
||||
XMLArrayOID = 143
|
||||
JSONArrayOID = 199
|
||||
PointOID = 600
|
||||
LsegOID = 601
|
||||
@@ -41,6 +43,7 @@ const (
|
||||
CircleOID = 718
|
||||
CircleArrayOID = 719
|
||||
UnknownOID = 705
|
||||
Macaddr8OID = 774
|
||||
MacaddrOID = 829
|
||||
InetOID = 869
|
||||
BoolArrayOID = 1000
|
||||
@@ -81,6 +84,8 @@ const (
|
||||
IntervalOID = 1186
|
||||
IntervalArrayOID = 1187
|
||||
NumericArrayOID = 1231
|
||||
TimetzOID = 1266
|
||||
TimetzArrayOID = 1270
|
||||
BitOID = 1560
|
||||
BitArrayOID = 1561
|
||||
VarbitOID = 1562
|
||||
@@ -211,6 +216,15 @@ type Map struct {
|
||||
TryWrapScanPlanFuncs []TryWrapScanPlanFunc
|
||||
}
|
||||
|
||||
// Copy returns a new Map containing the same registered types.
|
||||
func (m *Map) Copy() *Map {
|
||||
newMap := NewMap()
|
||||
for _, type_ := range m.oidToType {
|
||||
newMap.RegisterType(type_)
|
||||
}
|
||||
return newMap
|
||||
}
|
||||
|
||||
func NewMap() *Map {
|
||||
defaultMapInitOnce.Do(initDefaultMap)
|
||||
|
||||
@@ -245,6 +259,13 @@ func NewMap() *Map {
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterTypes registers multiple data types in the sequence they are provided.
|
||||
func (m *Map) RegisterTypes(types []*Type) {
|
||||
for _, t := range types {
|
||||
m.RegisterType(t)
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterType registers a data type with the Map. t must not be mutated after it is registered.
|
||||
func (m *Map) RegisterType(t *Type) {
|
||||
m.oidToType[t.OID] = t
|
||||
@@ -552,17 +573,24 @@ func TryFindUnderlyingTypeScanPlan(dst any) (plan WrappedScanPlanNextSetter, nex
|
||||
elemValue = dstValue.Elem()
|
||||
}
|
||||
nextDstType := elemKindToPointerTypes[elemValue.Kind()]
|
||||
if nextDstType == nil && elemValue.Kind() == reflect.Slice {
|
||||
if elemValue.Type().Elem().Kind() == reflect.Uint8 {
|
||||
var v *[]byte
|
||||
nextDstType = reflect.TypeOf(v)
|
||||
if nextDstType == nil {
|
||||
if elemValue.Kind() == reflect.Slice {
|
||||
if elemValue.Type().Elem().Kind() == reflect.Uint8 {
|
||||
var v *[]byte
|
||||
nextDstType = reflect.TypeOf(v)
|
||||
}
|
||||
}
|
||||
|
||||
// Get underlying type of any array.
|
||||
// https://github.com/jackc/pgx/issues/2107
|
||||
if elemValue.Kind() == reflect.Array {
|
||||
nextDstType = reflect.PointerTo(reflect.ArrayOf(elemValue.Len(), elemValue.Type().Elem()))
|
||||
}
|
||||
}
|
||||
|
||||
if nextDstType != nil && dstValue.Type() != nextDstType {
|
||||
if nextDstType != nil && dstValue.Type() != nextDstType && dstValue.CanConvert(nextDstType) {
|
||||
return &underlyingTypeScanPlan{dstType: dstValue.Type(), nextDstType: nextDstType}, dstValue.Convert(nextDstType).Interface(), true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nil, nil, false
|
||||
@@ -1328,7 +1356,7 @@ func (plan *derefPointerEncodePlan) Encode(value any, buf []byte) (newBuf []byte
|
||||
}
|
||||
|
||||
// TryWrapDerefPointerEncodePlan tries to dereference a pointer. e.g. If value was of type *string then a wrapper plan
|
||||
// would be returned that derefences the value.
|
||||
// would be returned that dereferences the value.
|
||||
func TryWrapDerefPointerEncodePlan(value any) (plan WrappedEncodePlanNextSetter, nextValue any, ok bool) {
|
||||
if _, ok := value.(driver.Valuer); ok {
|
||||
return nil, nil, false
|
||||
@@ -1358,6 +1386,8 @@ var kindToTypes map[reflect.Kind]reflect.Type = map[reflect.Kind]reflect.Type{
|
||||
reflect.Bool: reflect.TypeOf(false),
|
||||
}
|
||||
|
||||
var byteSliceType = reflect.TypeOf([]byte{})
|
||||
|
||||
type underlyingTypeEncodePlan struct {
|
||||
nextValueType reflect.Type
|
||||
next EncodePlan
|
||||
@@ -1372,6 +1402,10 @@ func (plan *underlyingTypeEncodePlan) Encode(value any, buf []byte) (newBuf []by
|
||||
// TryWrapFindUnderlyingTypeEncodePlan tries to convert to a Go builtin type. e.g. If value was of type MyString and
|
||||
// MyString was defined as a string then a wrapper plan would be returned that converts MyString to string.
|
||||
func TryWrapFindUnderlyingTypeEncodePlan(value any) (plan WrappedEncodePlanNextSetter, nextValue any, ok bool) {
|
||||
if value == nil {
|
||||
return nil, nil, false
|
||||
}
|
||||
|
||||
if _, ok := value.(driver.Valuer); ok {
|
||||
return nil, nil, false
|
||||
}
|
||||
@@ -1387,6 +1421,24 @@ func TryWrapFindUnderlyingTypeEncodePlan(value any) (plan WrappedEncodePlanNextS
|
||||
return &underlyingTypeEncodePlan{nextValueType: nextValueType}, refValue.Convert(nextValueType).Interface(), true
|
||||
}
|
||||
|
||||
// []byte is a special case. It is a slice but we treat it as a scalar type. In the case of a named type like
|
||||
// json.RawMessage which is defined as []byte the underlying type should be considered as []byte. But any other slice
|
||||
// does not have a special underlying type.
|
||||
//
|
||||
// https://github.com/jackc/pgx/issues/1763
|
||||
if refValue.Type() != byteSliceType && refValue.Type().AssignableTo(byteSliceType) {
|
||||
return &underlyingTypeEncodePlan{nextValueType: byteSliceType}, refValue.Convert(byteSliceType).Interface(), true
|
||||
}
|
||||
|
||||
// Get underlying type of any array.
|
||||
// https://github.com/jackc/pgx/issues/2107
|
||||
if refValue.Kind() == reflect.Array {
|
||||
underlyingArrayType := reflect.ArrayOf(refValue.Len(), refValue.Type().Elem())
|
||||
if refValue.Type() != underlyingArrayType {
|
||||
return &underlyingTypeEncodePlan{nextValueType: underlyingArrayType}, refValue.Convert(underlyingArrayType).Interface(), true
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil, false
|
||||
}
|
||||
|
||||
@@ -1894,8 +1946,17 @@ func newEncodeError(value any, m *Map, oid uint32, formatCode int16, err error)
|
||||
// (nil, nil). The caller of Encode is responsible for writing the correct NULL value or the length of the data
|
||||
// written.
|
||||
func (m *Map) Encode(oid uint32, formatCode int16, value any, buf []byte) (newBuf []byte, err error) {
|
||||
if value == nil {
|
||||
return nil, nil
|
||||
if isNil, callNilDriverValuer := isNilDriverValuer(value); isNil {
|
||||
if callNilDriverValuer {
|
||||
newBuf, err = (&encodePlanDriverValuer{m: m, oid: oid, formatCode: formatCode}).Encode(value, buf)
|
||||
if err != nil {
|
||||
return nil, newEncodeError(value, m, oid, formatCode, err)
|
||||
}
|
||||
|
||||
return newBuf, nil
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
plan := m.PlanEncode(oid, formatCode, value)
|
||||
@@ -1950,3 +2011,55 @@ func (w *sqlScannerWrapper) Scan(src any) error {
|
||||
|
||||
return w.m.Scan(t.OID, TextFormatCode, bufSrc, w.v)
|
||||
}
|
||||
|
||||
// canBeNil returns true if value can be nil.
|
||||
func canBeNil(value any) bool {
|
||||
refVal := reflect.ValueOf(value)
|
||||
kind := refVal.Kind()
|
||||
switch kind {
|
||||
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Interface, reflect.Slice:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// valuerReflectType is a reflect.Type for driver.Valuer. It has confusing syntax because reflect.TypeOf returns nil
|
||||
// when it's argument is a nil interface value. So we use a pointer to the interface and call Elem to get the actual
|
||||
// type. Yuck.
|
||||
//
|
||||
// This can be simplified in Go 1.22 with reflect.TypeFor.
|
||||
//
|
||||
// var valuerReflectType = reflect.TypeFor[driver.Valuer]()
|
||||
var valuerReflectType = reflect.TypeOf((*driver.Valuer)(nil)).Elem()
|
||||
|
||||
// isNilDriverValuer returns true if value is any type of nil unless it implements driver.Valuer. *T is not considered to implement
|
||||
// driver.Valuer if it is only implemented by T.
|
||||
func isNilDriverValuer(value any) (isNil bool, callNilDriverValuer bool) {
|
||||
if value == nil {
|
||||
return true, false
|
||||
}
|
||||
|
||||
refVal := reflect.ValueOf(value)
|
||||
kind := refVal.Kind()
|
||||
switch kind {
|
||||
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Interface, reflect.Slice:
|
||||
if !refVal.IsNil() {
|
||||
return false, false
|
||||
}
|
||||
|
||||
if _, ok := value.(driver.Valuer); ok {
|
||||
if kind == reflect.Ptr {
|
||||
// The type assertion will succeed if driver.Valuer is implemented on T or *T. Check if it is implemented on *T
|
||||
// by checking if it is not implemented on *T.
|
||||
return true, !refVal.Type().Elem().Implements(valuerReflectType)
|
||||
} else {
|
||||
return true, true
|
||||
}
|
||||
}
|
||||
|
||||
return true, false
|
||||
default:
|
||||
return false, false
|
||||
}
|
||||
}
|
||||
|
||||
14
vendor/github.com/jackc/pgx/v5/pgtype/pgtype_default.go
generated
vendored
14
vendor/github.com/jackc/pgx/v5/pgtype/pgtype_default.go
generated
vendored
@@ -1,6 +1,8 @@
|
||||
package pgtype
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"encoding/xml"
|
||||
"net"
|
||||
"net/netip"
|
||||
"reflect"
|
||||
@@ -64,11 +66,12 @@ func initDefaultMap() {
|
||||
defaultMap.RegisterType(&Type{Name: "int4", OID: Int4OID, Codec: Int4Codec{}})
|
||||
defaultMap.RegisterType(&Type{Name: "int8", OID: Int8OID, Codec: Int8Codec{}})
|
||||
defaultMap.RegisterType(&Type{Name: "interval", OID: IntervalOID, Codec: IntervalCodec{}})
|
||||
defaultMap.RegisterType(&Type{Name: "json", OID: JSONOID, Codec: JSONCodec{}})
|
||||
defaultMap.RegisterType(&Type{Name: "jsonb", OID: JSONBOID, Codec: JSONBCodec{}})
|
||||
defaultMap.RegisterType(&Type{Name: "json", OID: JSONOID, Codec: &JSONCodec{Marshal: json.Marshal, Unmarshal: json.Unmarshal}})
|
||||
defaultMap.RegisterType(&Type{Name: "jsonb", OID: JSONBOID, Codec: &JSONBCodec{Marshal: json.Marshal, Unmarshal: json.Unmarshal}})
|
||||
defaultMap.RegisterType(&Type{Name: "jsonpath", OID: JSONPathOID, Codec: &TextFormatOnlyCodec{TextCodec{}}})
|
||||
defaultMap.RegisterType(&Type{Name: "line", OID: LineOID, Codec: LineCodec{}})
|
||||
defaultMap.RegisterType(&Type{Name: "lseg", OID: LsegOID, Codec: LsegCodec{}})
|
||||
defaultMap.RegisterType(&Type{Name: "macaddr8", OID: Macaddr8OID, Codec: MacaddrCodec{}})
|
||||
defaultMap.RegisterType(&Type{Name: "macaddr", OID: MacaddrOID, Codec: MacaddrCodec{}})
|
||||
defaultMap.RegisterType(&Type{Name: "name", OID: NameOID, Codec: TextCodec{}})
|
||||
defaultMap.RegisterType(&Type{Name: "numeric", OID: NumericOID, Codec: NumericCodec{}})
|
||||
@@ -80,13 +83,14 @@ func initDefaultMap() {
|
||||
defaultMap.RegisterType(&Type{Name: "text", OID: TextOID, Codec: TextCodec{}})
|
||||
defaultMap.RegisterType(&Type{Name: "tid", OID: TIDOID, Codec: TIDCodec{}})
|
||||
defaultMap.RegisterType(&Type{Name: "time", OID: TimeOID, Codec: TimeCodec{}})
|
||||
defaultMap.RegisterType(&Type{Name: "timestamp", OID: TimestampOID, Codec: TimestampCodec{}})
|
||||
defaultMap.RegisterType(&Type{Name: "timestamptz", OID: TimestamptzOID, Codec: TimestamptzCodec{}})
|
||||
defaultMap.RegisterType(&Type{Name: "timestamp", OID: TimestampOID, Codec: &TimestampCodec{}})
|
||||
defaultMap.RegisterType(&Type{Name: "timestamptz", OID: TimestamptzOID, Codec: &TimestamptzCodec{}})
|
||||
defaultMap.RegisterType(&Type{Name: "unknown", OID: UnknownOID, Codec: TextCodec{}})
|
||||
defaultMap.RegisterType(&Type{Name: "uuid", OID: UUIDOID, Codec: UUIDCodec{}})
|
||||
defaultMap.RegisterType(&Type{Name: "varbit", OID: VarbitOID, Codec: BitsCodec{}})
|
||||
defaultMap.RegisterType(&Type{Name: "varchar", OID: VarcharOID, Codec: TextCodec{}})
|
||||
defaultMap.RegisterType(&Type{Name: "xid", OID: XIDOID, Codec: Uint32Codec{}})
|
||||
defaultMap.RegisterType(&Type{Name: "xml", OID: XMLOID, Codec: &XMLCodec{Marshal: xml.Marshal, Unmarshal: xml.Unmarshal}})
|
||||
|
||||
// Range types
|
||||
defaultMap.RegisterType(&Type{Name: "daterange", OID: DaterangeOID, Codec: &RangeCodec{ElementType: defaultMap.oidToType[DateOID]}})
|
||||
@@ -151,6 +155,7 @@ func initDefaultMap() {
|
||||
defaultMap.RegisterType(&Type{Name: "_varbit", OID: VarbitArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[VarbitOID]}})
|
||||
defaultMap.RegisterType(&Type{Name: "_varchar", OID: VarcharArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[VarcharOID]}})
|
||||
defaultMap.RegisterType(&Type{Name: "_xid", OID: XIDArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[XIDOID]}})
|
||||
defaultMap.RegisterType(&Type{Name: "_xml", OID: XMLArrayOID, Codec: &ArrayCodec{ElementType: defaultMap.oidToType[XMLOID]}})
|
||||
|
||||
// Integer types that directly map to a PostgreSQL type
|
||||
registerDefaultPgTypeVariants[int16](defaultMap, "int2")
|
||||
@@ -173,6 +178,7 @@ func initDefaultMap() {
|
||||
registerDefaultPgTypeVariants[time.Time](defaultMap, "timestamptz")
|
||||
registerDefaultPgTypeVariants[time.Duration](defaultMap, "interval")
|
||||
registerDefaultPgTypeVariants[string](defaultMap, "text")
|
||||
registerDefaultPgTypeVariants[json.RawMessage](defaultMap, "json")
|
||||
registerDefaultPgTypeVariants[[]byte](defaultMap, "bytea")
|
||||
|
||||
registerDefaultPgTypeVariants[net.IP](defaultMap, "inet")
|
||||
|
||||
16
vendor/github.com/jackc/pgx/v5/pgtype/point.go
generated
vendored
16
vendor/github.com/jackc/pgx/v5/pgtype/point.go
generated
vendored
@@ -50,17 +50,17 @@ func parsePoint(src []byte) (*Point, error) {
|
||||
if src[0] == '"' && src[len(src)-1] == '"' {
|
||||
src = src[1 : len(src)-1]
|
||||
}
|
||||
parts := strings.SplitN(string(src[1:len(src)-1]), ",", 2)
|
||||
if len(parts) < 2 {
|
||||
sx, sy, found := strings.Cut(string(src[1:len(src)-1]), ",")
|
||||
if !found {
|
||||
return nil, fmt.Errorf("invalid format for point")
|
||||
}
|
||||
|
||||
x, err := strconv.ParseFloat(parts[0], 64)
|
||||
x, err := strconv.ParseFloat(sx, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
y, err := strconv.ParseFloat(parts[1], 64)
|
||||
y, err := strconv.ParseFloat(sy, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -247,17 +247,17 @@ func (scanPlanTextAnyToPointScanner) Scan(src []byte, dst any) error {
|
||||
return fmt.Errorf("invalid length for point: %v", len(src))
|
||||
}
|
||||
|
||||
parts := strings.SplitN(string(src[1:len(src)-1]), ",", 2)
|
||||
if len(parts) < 2 {
|
||||
sx, sy, found := strings.Cut(string(src[1:len(src)-1]), ",")
|
||||
if !found {
|
||||
return fmt.Errorf("invalid format for point")
|
||||
}
|
||||
|
||||
x, err := strconv.ParseFloat(parts[0], 64)
|
||||
x, err := strconv.ParseFloat(sx, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
y, err := strconv.ParseFloat(parts[1], 64)
|
||||
y, err := strconv.ParseFloat(sy, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
14
vendor/github.com/jackc/pgx/v5/pgtype/range.go
generated
vendored
14
vendor/github.com/jackc/pgx/v5/pgtype/range.go
generated
vendored
@@ -40,7 +40,7 @@ func parseUntypedTextRange(src string) (*untypedTextRange, error) {
|
||||
|
||||
r, _, err := buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid lower bound: %v", err)
|
||||
return nil, fmt.Errorf("invalid lower bound: %w", err)
|
||||
}
|
||||
switch r {
|
||||
case '(':
|
||||
@@ -53,7 +53,7 @@ func parseUntypedTextRange(src string) (*untypedTextRange, error) {
|
||||
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid lower value: %v", err)
|
||||
return nil, fmt.Errorf("invalid lower value: %w", err)
|
||||
}
|
||||
buf.UnreadRune()
|
||||
|
||||
@@ -62,13 +62,13 @@ func parseUntypedTextRange(src string) (*untypedTextRange, error) {
|
||||
} else {
|
||||
utr.Lower, err = rangeParseValue(buf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid lower value: %v", err)
|
||||
return nil, fmt.Errorf("invalid lower value: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("missing range separator: %v", err)
|
||||
return nil, fmt.Errorf("missing range separator: %w", err)
|
||||
}
|
||||
if r != ',' {
|
||||
return nil, fmt.Errorf("missing range separator: %v", r)
|
||||
@@ -76,7 +76,7 @@ func parseUntypedTextRange(src string) (*untypedTextRange, error) {
|
||||
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid upper value: %v", err)
|
||||
return nil, fmt.Errorf("invalid upper value: %w", err)
|
||||
}
|
||||
|
||||
if r == ')' || r == ']' {
|
||||
@@ -85,12 +85,12 @@ func parseUntypedTextRange(src string) (*untypedTextRange, error) {
|
||||
buf.UnreadRune()
|
||||
utr.Upper, err = rangeParseValue(buf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid upper value: %v", err)
|
||||
return nil, fmt.Errorf("invalid upper value: %w", err)
|
||||
}
|
||||
|
||||
r, _, err = buf.ReadRune()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("missing upper bound: %v", err)
|
||||
return nil, fmt.Errorf("missing upper bound: %w", err)
|
||||
}
|
||||
switch r {
|
||||
case ')':
|
||||
|
||||
16
vendor/github.com/jackc/pgx/v5/pgtype/range_codec.go
generated
vendored
16
vendor/github.com/jackc/pgx/v5/pgtype/range_codec.go
generated
vendored
@@ -120,7 +120,7 @@ func (plan *encodePlanRangeCodecRangeValuerToBinary) Encode(value any, buf []byt
|
||||
|
||||
buf, err = lowerPlan.Encode(lower, buf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to encode %v as element of range: %v", lower, err)
|
||||
return nil, fmt.Errorf("failed to encode %v as element of range: %w", lower, err)
|
||||
}
|
||||
if buf == nil {
|
||||
return nil, fmt.Errorf("Lower cannot be NULL unless LowerType is Unbounded")
|
||||
@@ -144,7 +144,7 @@ func (plan *encodePlanRangeCodecRangeValuerToBinary) Encode(value any, buf []byt
|
||||
|
||||
buf, err = upperPlan.Encode(upper, buf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to encode %v as element of range: %v", upper, err)
|
||||
return nil, fmt.Errorf("failed to encode %v as element of range: %w", upper, err)
|
||||
}
|
||||
if buf == nil {
|
||||
return nil, fmt.Errorf("Upper cannot be NULL unless UpperType is Unbounded")
|
||||
@@ -194,7 +194,7 @@ func (plan *encodePlanRangeCodecRangeValuerToText) Encode(value any, buf []byte)
|
||||
|
||||
buf, err = lowerPlan.Encode(lower, buf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to encode %v as element of range: %v", lower, err)
|
||||
return nil, fmt.Errorf("failed to encode %v as element of range: %w", lower, err)
|
||||
}
|
||||
if buf == nil {
|
||||
return nil, fmt.Errorf("Lower cannot be NULL unless LowerType is Unbounded")
|
||||
@@ -215,7 +215,7 @@ func (plan *encodePlanRangeCodecRangeValuerToText) Encode(value any, buf []byte)
|
||||
|
||||
buf, err = upperPlan.Encode(upper, buf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to encode %v as element of range: %v", upper, err)
|
||||
return nil, fmt.Errorf("failed to encode %v as element of range: %w", upper, err)
|
||||
}
|
||||
if buf == nil {
|
||||
return nil, fmt.Errorf("Upper cannot be NULL unless UpperType is Unbounded")
|
||||
@@ -282,7 +282,7 @@ func (plan *scanPlanBinaryRangeToRangeScanner) Scan(src []byte, target any) erro
|
||||
|
||||
err = lowerPlan.Scan(ubr.Lower, lowerTarget)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot scan into %v from range element: %v", lowerTarget, err)
|
||||
return fmt.Errorf("cannot scan into %v from range element: %w", lowerTarget, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,7 +294,7 @@ func (plan *scanPlanBinaryRangeToRangeScanner) Scan(src []byte, target any) erro
|
||||
|
||||
err = upperPlan.Scan(ubr.Upper, upperTarget)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot scan into %v from range element: %v", upperTarget, err)
|
||||
return fmt.Errorf("cannot scan into %v from range element: %w", upperTarget, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,7 +332,7 @@ func (plan *scanPlanTextRangeToRangeScanner) Scan(src []byte, target any) error
|
||||
|
||||
err = lowerPlan.Scan([]byte(utr.Lower), lowerTarget)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot scan into %v from range element: %v", lowerTarget, err)
|
||||
return fmt.Errorf("cannot scan into %v from range element: %w", lowerTarget, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -344,7 +344,7 @@ func (plan *scanPlanTextRangeToRangeScanner) Scan(src []byte, target any) error
|
||||
|
||||
err = upperPlan.Scan([]byte(utr.Upper), upperTarget)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot scan into %v from range element: %v", upperTarget, err)
|
||||
return fmt.Errorf("cannot scan into %v from range element: %w", upperTarget, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
8
vendor/github.com/jackc/pgx/v5/pgtype/tid.go
generated
vendored
8
vendor/github.com/jackc/pgx/v5/pgtype/tid.go
generated
vendored
@@ -205,17 +205,17 @@ func (scanPlanTextAnyToTIDScanner) Scan(src []byte, dst any) error {
|
||||
return fmt.Errorf("invalid length for tid: %v", len(src))
|
||||
}
|
||||
|
||||
parts := strings.SplitN(string(src[1:len(src)-1]), ",", 2)
|
||||
if len(parts) < 2 {
|
||||
block, offset, found := strings.Cut(string(src[1:len(src)-1]), ",")
|
||||
if !found {
|
||||
return fmt.Errorf("invalid format for tid")
|
||||
}
|
||||
|
||||
blockNumber, err := strconv.ParseUint(parts[0], 10, 32)
|
||||
blockNumber, err := strconv.ParseUint(block, 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
offsetNumber, err := strconv.ParseUint(parts[1], 10, 16)
|
||||
offsetNumber, err := strconv.ParseUint(offset, 10, 16)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
51
vendor/github.com/jackc/pgx/v5/pgtype/time.go
generated
vendored
51
vendor/github.com/jackc/pgx/v5/pgtype/time.go
generated
vendored
@@ -19,9 +19,11 @@ type TimeValuer interface {
|
||||
|
||||
// Time represents the PostgreSQL time type. The PostgreSQL time is a time of day without time zone.
|
||||
//
|
||||
// Time is represented as the number of microseconds since midnight in the same way that PostgreSQL does. Other time
|
||||
// and date types in pgtype can use time.Time as the underlying representation. However, pgtype.Time type cannot due
|
||||
// to needing to handle 24:00:00. time.Time converts that to 00:00:00 on the following day.
|
||||
// Time is represented as the number of microseconds since midnight in the same way that PostgreSQL does. Other time and
|
||||
// date types in pgtype can use time.Time as the underlying representation. However, pgtype.Time type cannot due to
|
||||
// needing to handle 24:00:00. time.Time converts that to 00:00:00 on the following day.
|
||||
//
|
||||
// The time with time zone type is not supported. Use of time with time zone is discouraged by the PostgreSQL documentation.
|
||||
type Time struct {
|
||||
Microseconds int64 // Number of microseconds since midnight
|
||||
Valid bool
|
||||
@@ -45,7 +47,12 @@ func (t *Time) Scan(src any) error {
|
||||
|
||||
switch src := src.(type) {
|
||||
case string:
|
||||
return scanPlanTextAnyToTimeScanner{}.Scan([]byte(src), t)
|
||||
err := scanPlanTextAnyToTimeScanner{}.Scan([]byte(src), t)
|
||||
if err != nil {
|
||||
t.Microseconds = 0
|
||||
t.Valid = false
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return fmt.Errorf("cannot scan %T", src)
|
||||
@@ -136,6 +143,8 @@ func (TimeCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan
|
||||
switch target.(type) {
|
||||
case TimeScanner:
|
||||
return scanPlanBinaryTimeToTimeScanner{}
|
||||
case TextScanner:
|
||||
return scanPlanBinaryTimeToTextScanner{}
|
||||
}
|
||||
case TextFormatCode:
|
||||
switch target.(type) {
|
||||
@@ -165,6 +174,34 @@ func (scanPlanBinaryTimeToTimeScanner) Scan(src []byte, dst any) error {
|
||||
return scanner.ScanTime(Time{Microseconds: usec, Valid: true})
|
||||
}
|
||||
|
||||
type scanPlanBinaryTimeToTextScanner struct{}
|
||||
|
||||
func (scanPlanBinaryTimeToTextScanner) Scan(src []byte, dst any) error {
|
||||
ts, ok := (dst).(TextScanner)
|
||||
if !ok {
|
||||
return ErrScanTargetTypeChanged
|
||||
}
|
||||
|
||||
if src == nil {
|
||||
return ts.ScanText(Text{})
|
||||
}
|
||||
|
||||
if len(src) != 8 {
|
||||
return fmt.Errorf("invalid length for time: %v", len(src))
|
||||
}
|
||||
|
||||
usec := int64(binary.BigEndian.Uint64(src))
|
||||
|
||||
tim := Time{Microseconds: usec, Valid: true}
|
||||
|
||||
buf, err := TimeCodec{}.PlanEncode(nil, 0, TextFormatCode, tim).Encode(tim, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ts.ScanText(Text{String: string(buf), Valid: true})
|
||||
}
|
||||
|
||||
type scanPlanTextAnyToTimeScanner struct{}
|
||||
|
||||
func (scanPlanTextAnyToTimeScanner) Scan(src []byte, dst any) error {
|
||||
@@ -176,7 +213,7 @@ func (scanPlanTextAnyToTimeScanner) Scan(src []byte, dst any) error {
|
||||
|
||||
s := string(src)
|
||||
|
||||
if len(s) < 8 {
|
||||
if len(s) < 8 || s[2] != ':' || s[5] != ':' {
|
||||
return fmt.Errorf("cannot decode %v into Time", s)
|
||||
}
|
||||
|
||||
@@ -199,6 +236,10 @@ func (scanPlanTextAnyToTimeScanner) Scan(src []byte, dst any) error {
|
||||
usec += seconds * microsecondsPerSecond
|
||||
|
||||
if len(s) > 9 {
|
||||
if s[8] != '.' || len(s) > 15 {
|
||||
return fmt.Errorf("cannot decode %v into Time", s)
|
||||
}
|
||||
|
||||
fraction := s[9:]
|
||||
n, err := strconv.ParseInt(fraction, 10, 64)
|
||||
if err != nil {
|
||||
|
||||
39
vendor/github.com/jackc/pgx/v5/pgtype/timestamp.go
generated
vendored
39
vendor/github.com/jackc/pgx/v5/pgtype/timestamp.go
generated
vendored
@@ -46,7 +46,7 @@ func (ts *Timestamp) Scan(src any) error {
|
||||
|
||||
switch src := src.(type) {
|
||||
case string:
|
||||
return scanPlanTextTimestampToTimestampScanner{}.Scan([]byte(src), ts)
|
||||
return (&scanPlanTextTimestampToTimestampScanner{}).Scan([]byte(src), ts)
|
||||
case time.Time:
|
||||
*ts = Timestamp{Time: src, Valid: true}
|
||||
return nil
|
||||
@@ -116,17 +116,21 @@ func (ts *Timestamp) UnmarshalJSON(b []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type TimestampCodec struct{}
|
||||
type TimestampCodec struct {
|
||||
// ScanLocation is the location that the time is assumed to be in for scanning. This is different from
|
||||
// TimestamptzCodec.ScanLocation in that this setting does change the instant in time that the timestamp represents.
|
||||
ScanLocation *time.Location
|
||||
}
|
||||
|
||||
func (TimestampCodec) FormatSupported(format int16) bool {
|
||||
func (*TimestampCodec) FormatSupported(format int16) bool {
|
||||
return format == TextFormatCode || format == BinaryFormatCode
|
||||
}
|
||||
|
||||
func (TimestampCodec) PreferredFormat() int16 {
|
||||
func (*TimestampCodec) PreferredFormat() int16 {
|
||||
return BinaryFormatCode
|
||||
}
|
||||
|
||||
func (TimestampCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {
|
||||
func (*TimestampCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {
|
||||
if _, ok := value.(TimestampValuer); !ok {
|
||||
return nil
|
||||
}
|
||||
@@ -220,27 +224,27 @@ func discardTimeZone(t time.Time) time.Time {
|
||||
return t
|
||||
}
|
||||
|
||||
func (TimestampCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
|
||||
func (c *TimestampCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
|
||||
|
||||
switch format {
|
||||
case BinaryFormatCode:
|
||||
switch target.(type) {
|
||||
case TimestampScanner:
|
||||
return scanPlanBinaryTimestampToTimestampScanner{}
|
||||
return &scanPlanBinaryTimestampToTimestampScanner{location: c.ScanLocation}
|
||||
}
|
||||
case TextFormatCode:
|
||||
switch target.(type) {
|
||||
case TimestampScanner:
|
||||
return scanPlanTextTimestampToTimestampScanner{}
|
||||
return &scanPlanTextTimestampToTimestampScanner{location: c.ScanLocation}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type scanPlanBinaryTimestampToTimestampScanner struct{}
|
||||
type scanPlanBinaryTimestampToTimestampScanner struct{ location *time.Location }
|
||||
|
||||
func (scanPlanBinaryTimestampToTimestampScanner) Scan(src []byte, dst any) error {
|
||||
func (plan *scanPlanBinaryTimestampToTimestampScanner) Scan(src []byte, dst any) error {
|
||||
scanner := (dst).(TimestampScanner)
|
||||
|
||||
if src == nil {
|
||||
@@ -264,15 +268,18 @@ func (scanPlanBinaryTimestampToTimestampScanner) Scan(src []byte, dst any) error
|
||||
microsecFromUnixEpochToY2K/1000000+microsecSinceY2K/1000000,
|
||||
(microsecFromUnixEpochToY2K%1000000*1000)+(microsecSinceY2K%1000000*1000),
|
||||
).UTC()
|
||||
if plan.location != nil {
|
||||
tim = time.Date(tim.Year(), tim.Month(), tim.Day(), tim.Hour(), tim.Minute(), tim.Second(), tim.Nanosecond(), plan.location)
|
||||
}
|
||||
ts = Timestamp{Time: tim, Valid: true}
|
||||
}
|
||||
|
||||
return scanner.ScanTimestamp(ts)
|
||||
}
|
||||
|
||||
type scanPlanTextTimestampToTimestampScanner struct{}
|
||||
type scanPlanTextTimestampToTimestampScanner struct{ location *time.Location }
|
||||
|
||||
func (scanPlanTextTimestampToTimestampScanner) Scan(src []byte, dst any) error {
|
||||
func (plan *scanPlanTextTimestampToTimestampScanner) Scan(src []byte, dst any) error {
|
||||
scanner := (dst).(TimestampScanner)
|
||||
|
||||
if src == nil {
|
||||
@@ -302,13 +309,17 @@ func (scanPlanTextTimestampToTimestampScanner) Scan(src []byte, dst any) error {
|
||||
tim = time.Date(year, tim.Month(), tim.Day(), tim.Hour(), tim.Minute(), tim.Second(), tim.Nanosecond(), tim.Location())
|
||||
}
|
||||
|
||||
if plan.location != nil {
|
||||
tim = time.Date(tim.Year(), tim.Month(), tim.Day(), tim.Hour(), tim.Minute(), tim.Second(), tim.Nanosecond(), plan.location)
|
||||
}
|
||||
|
||||
ts = Timestamp{Time: tim, Valid: true}
|
||||
}
|
||||
|
||||
return scanner.ScanTimestamp(ts)
|
||||
}
|
||||
|
||||
func (c TimestampCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {
|
||||
func (c *TimestampCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {
|
||||
if src == nil {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -326,7 +337,7 @@ func (c TimestampCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16,
|
||||
return ts.Time, nil
|
||||
}
|
||||
|
||||
func (c TimestampCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {
|
||||
func (c *TimestampCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {
|
||||
if src == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user