[chore]: Bump github.com/jackc/pgx/v5 from 5.5.2 to 5.5.3 (#2664)
Bumps [github.com/jackc/pgx/v5](https://github.com/jackc/pgx) from 5.5.2 to 5.5.3. - [Changelog](https://github.com/jackc/pgx/blob/master/CHANGELOG.md) - [Commits](https://github.com/jackc/pgx/compare/v5.5.2...v5.5.3) --- updated-dependencies: - dependency-name: github.com/jackc/pgx/v5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
parent
af1a26a68f
commit
e2ebcbb516
2
go.mod
2
go.mod
|
@ -36,7 +36,7 @@ require (
|
||||||
github.com/gorilla/feeds v1.1.2
|
github.com/gorilla/feeds v1.1.2
|
||||||
github.com/gorilla/websocket v1.5.1
|
github.com/gorilla/websocket v1.5.1
|
||||||
github.com/h2non/filetype v1.1.3
|
github.com/h2non/filetype v1.1.3
|
||||||
github.com/jackc/pgx/v5 v5.5.2
|
github.com/jackc/pgx/v5 v5.5.3
|
||||||
github.com/microcosm-cc/bluemonday v1.0.26
|
github.com/microcosm-cc/bluemonday v1.0.26
|
||||||
github.com/miekg/dns v1.1.58
|
github.com/miekg/dns v1.1.58
|
||||||
github.com/minio/minio-go/v7 v7.0.67
|
github.com/minio/minio-go/v7 v7.0.67
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -327,8 +327,8 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI
|
||||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||||
github.com/jackc/pgx/v5 v5.5.2 h1:iLlpgp4Cp/gC9Xuscl7lFL1PhhW+ZLtXZcrfCt4C3tA=
|
github.com/jackc/pgx/v5 v5.5.3 h1:Ces6/M3wbDXYpM8JyyPD57ivTtJACFZJd885pdIaV2s=
|
||||||
github.com/jackc/pgx/v5 v5.5.2/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
|
github.com/jackc/pgx/v5 v5.5.3/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
|
||||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
|
|
|
@ -1,3 +1,13 @@
|
||||||
|
# 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)
|
# 5.5.2 (January 13, 2024)
|
||||||
|
|
||||||
* Allow NamedArgs to start with underscore
|
* Allow NamedArgs to start with underscore
|
||||||
|
|
|
@ -79,20 +79,11 @@ echo "listen_addresses = '127.0.0.1'" >> .testdb/$POSTGRESQL_DATA_DIR/postgresql
|
||||||
echo "port = $PGPORT" >> .testdb/$POSTGRESQL_DATA_DIR/postgresql.conf
|
echo "port = $PGPORT" >> .testdb/$POSTGRESQL_DATA_DIR/postgresql.conf
|
||||||
cat testsetup/postgresql_ssl.conf >> .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/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
|
cd .testdb
|
||||||
|
|
||||||
# Generate a CA public / private key pair.
|
# Generate CA, server, and encrypted client certificates.
|
||||||
openssl genrsa -out ca.key 4096
|
go run ../testsetup/generate_certs.go
|
||||||
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
|
|
||||||
|
|
||||||
# Copy certificates to server directory and set permissions.
|
# Copy certificates to server directory and set permissions.
|
||||||
cp ca.pem $POSTGRESQL_DATA_DIR/root.crt
|
cp ca.pem $POSTGRESQL_DATA_DIR/root.crt
|
||||||
|
@ -100,11 +91,6 @@ cp localhost.key $POSTGRESQL_DATA_DIR/server.key
|
||||||
chmod 600 $POSTGRESQL_DATA_DIR/server.key
|
chmod 600 $POSTGRESQL_DATA_DIR/server.key
|
||||||
cp localhost.crt $POSTGRESQL_DATA_DIR/server.crt
|
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 ..
|
cd ..
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,8 @@ import (
|
||||||
|
|
||||||
// QueuedQuery is a query that has been queued for execution via a Batch.
|
// QueuedQuery is a query that has been queued for execution via a Batch.
|
||||||
type QueuedQuery struct {
|
type QueuedQuery struct {
|
||||||
query string
|
SQL string
|
||||||
arguments []any
|
Arguments []any
|
||||||
fn batchItemFunc
|
fn batchItemFunc
|
||||||
sd *pgconn.StatementDescription
|
sd *pgconn.StatementDescription
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ func (qq *QueuedQuery) Exec(fn func(ct pgconn.CommandTag) error) {
|
||||||
// Batch queries are a way of bundling multiple queries together to avoid
|
// Batch queries are a way of bundling multiple queries together to avoid
|
||||||
// unnecessary network round trips. A Batch must only be sent once.
|
// unnecessary network round trips. A Batch must only be sent once.
|
||||||
type Batch struct {
|
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.
|
||||||
|
@ -65,16 +65,16 @@ type Batch struct {
|
||||||
// connection's DefaultQueryExecMode.
|
// connection's DefaultQueryExecMode.
|
||||||
func (b *Batch) Queue(query string, arguments ...any) *QueuedQuery {
|
func (b *Batch) Queue(query string, arguments ...any) *QueuedQuery {
|
||||||
qq := &QueuedQuery{
|
qq := &QueuedQuery{
|
||||||
query: query,
|
SQL: query,
|
||||||
arguments: arguments,
|
Arguments: arguments,
|
||||||
}
|
}
|
||||||
b.queuedQueries = append(b.queuedQueries, qq)
|
b.QueuedQueries = append(b.QueuedQueries, qq)
|
||||||
return qq
|
return qq
|
||||||
}
|
}
|
||||||
|
|
||||||
// Len returns number of queries that have been queued so far.
|
// Len returns number of queries that have been queued so far.
|
||||||
func (b *Batch) Len() int {
|
func (b *Batch) Len() int {
|
||||||
return len(b.queuedQueries)
|
return len(b.QueuedQueries)
|
||||||
}
|
}
|
||||||
|
|
||||||
type BatchResults interface {
|
type BatchResults interface {
|
||||||
|
@ -227,9 +227,9 @@ func (br *batchResults) Close() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read and run fn for all remaining items
|
// Read and run fn for all remaining items
|
||||||
for br.err == nil && !br.closed && br.b != nil && br.qqIdx < len(br.b.queuedQueries) {
|
for br.err == nil && !br.closed && br.b != nil && br.qqIdx < len(br.b.QueuedQueries) {
|
||||||
if br.b.queuedQueries[br.qqIdx].fn != nil {
|
if br.b.QueuedQueries[br.qqIdx].fn != nil {
|
||||||
err := br.b.queuedQueries[br.qqIdx].fn(br)
|
err := br.b.QueuedQueries[br.qqIdx].fn(br)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
br.err = err
|
br.err = err
|
||||||
}
|
}
|
||||||
|
@ -253,10 +253,10 @@ func (br *batchResults) earlyError() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (br *batchResults) nextQueryAndArgs() (query string, args []any, ok bool) {
|
func (br *batchResults) nextQueryAndArgs() (query string, args []any, ok bool) {
|
||||||
if br.b != nil && br.qqIdx < len(br.b.queuedQueries) {
|
if br.b != nil && br.qqIdx < len(br.b.QueuedQueries) {
|
||||||
bi := br.b.queuedQueries[br.qqIdx]
|
bi := br.b.QueuedQueries[br.qqIdx]
|
||||||
query = bi.query
|
query = bi.SQL
|
||||||
args = bi.arguments
|
args = bi.Arguments
|
||||||
ok = true
|
ok = true
|
||||||
br.qqIdx++
|
br.qqIdx++
|
||||||
}
|
}
|
||||||
|
@ -396,9 +396,9 @@ func (br *pipelineBatchResults) Close() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read and run fn for all remaining items
|
// Read and run fn for all remaining items
|
||||||
for br.err == nil && !br.closed && br.b != nil && br.qqIdx < len(br.b.queuedQueries) {
|
for br.err == nil && !br.closed && br.b != nil && br.qqIdx < len(br.b.QueuedQueries) {
|
||||||
if br.b.queuedQueries[br.qqIdx].fn != nil {
|
if br.b.QueuedQueries[br.qqIdx].fn != nil {
|
||||||
err := br.b.queuedQueries[br.qqIdx].fn(br)
|
err := br.b.QueuedQueries[br.qqIdx].fn(br)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
br.err = err
|
br.err = err
|
||||||
}
|
}
|
||||||
|
@ -422,10 +422,10 @@ func (br *pipelineBatchResults) earlyError() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (br *pipelineBatchResults) nextQueryAndArgs() (query string, args []any, ok bool) {
|
func (br *pipelineBatchResults) nextQueryAndArgs() (query string, args []any, ok bool) {
|
||||||
if br.b != nil && br.qqIdx < len(br.b.queuedQueries) {
|
if br.b != nil && br.qqIdx < len(br.b.QueuedQueries) {
|
||||||
bi := br.b.queuedQueries[br.qqIdx]
|
bi := br.b.QueuedQueries[br.qqIdx]
|
||||||
query = bi.query
|
query = bi.SQL
|
||||||
args = bi.arguments
|
args = bi.Arguments
|
||||||
ok = true
|
ok = true
|
||||||
br.qqIdx++
|
br.qqIdx++
|
||||||
}
|
}
|
||||||
|
|
|
@ -903,10 +903,10 @@ func (c *Conn) SendBatch(ctx context.Context, b *Batch) (br BatchResults) {
|
||||||
return &batchResults{ctx: ctx, conn: c, err: err}
|
return &batchResults{ctx: ctx, conn: c, err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, bi := range b.queuedQueries {
|
for _, bi := range b.QueuedQueries {
|
||||||
var queryRewriter QueryRewriter
|
var queryRewriter QueryRewriter
|
||||||
sql := bi.query
|
sql := bi.SQL
|
||||||
arguments := bi.arguments
|
arguments := bi.Arguments
|
||||||
|
|
||||||
optionLoop:
|
optionLoop:
|
||||||
for len(arguments) > 0 {
|
for len(arguments) > 0 {
|
||||||
|
@ -928,8 +928,8 @@ func (c *Conn) SendBatch(ctx context.Context, b *Batch) (br BatchResults) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bi.query = sql
|
bi.SQL = sql
|
||||||
bi.arguments = arguments
|
bi.Arguments = arguments
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: changing mode per batch? Update Batch.Queue function comment when implemented
|
// TODO: changing mode per batch? Update Batch.Queue function comment when implemented
|
||||||
|
@ -939,8 +939,8 @@ func (c *Conn) SendBatch(ctx context.Context, b *Batch) (br BatchResults) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// All other modes use extended protocol and thus can use prepared statements.
|
// All other modes use extended protocol and thus can use prepared statements.
|
||||||
for _, bi := range b.queuedQueries {
|
for _, bi := range b.QueuedQueries {
|
||||||
if sd, ok := c.preparedStatements[bi.query]; ok {
|
if sd, ok := c.preparedStatements[bi.SQL]; ok {
|
||||||
bi.sd = sd
|
bi.sd = sd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -961,11 +961,11 @@ func (c *Conn) SendBatch(ctx context.Context, b *Batch) (br BatchResults) {
|
||||||
|
|
||||||
func (c *Conn) sendBatchQueryExecModeSimpleProtocol(ctx context.Context, b *Batch) *batchResults {
|
func (c *Conn) sendBatchQueryExecModeSimpleProtocol(ctx context.Context, b *Batch) *batchResults {
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
for i, bi := range b.queuedQueries {
|
for i, bi := range b.QueuedQueries {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
sb.WriteByte(';')
|
sb.WriteByte(';')
|
||||||
}
|
}
|
||||||
sql, err := c.sanitizeForSimpleQuery(bi.query, bi.arguments...)
|
sql, err := c.sanitizeForSimpleQuery(bi.SQL, bi.Arguments...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &batchResults{ctx: ctx, conn: c, err: err}
|
return &batchResults{ctx: ctx, conn: c, err: err}
|
||||||
}
|
}
|
||||||
|
@ -984,21 +984,21 @@ func (c *Conn) sendBatchQueryExecModeSimpleProtocol(ctx context.Context, b *Batc
|
||||||
func (c *Conn) sendBatchQueryExecModeExec(ctx context.Context, b *Batch) *batchResults {
|
func (c *Conn) sendBatchQueryExecModeExec(ctx context.Context, b *Batch) *batchResults {
|
||||||
batch := &pgconn.Batch{}
|
batch := &pgconn.Batch{}
|
||||||
|
|
||||||
for _, bi := range b.queuedQueries {
|
for _, bi := range b.QueuedQueries {
|
||||||
sd := bi.sd
|
sd := bi.sd
|
||||||
if sd != nil {
|
if sd != nil {
|
||||||
err := c.eqb.Build(c.typeMap, sd, bi.arguments)
|
err := c.eqb.Build(c.typeMap, sd, bi.Arguments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &batchResults{ctx: ctx, conn: c, err: err}
|
return &batchResults{ctx: ctx, conn: c, err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
batch.ExecPrepared(sd.Name, c.eqb.ParamValues, c.eqb.ParamFormats, c.eqb.ResultFormats)
|
batch.ExecPrepared(sd.Name, c.eqb.ParamValues, c.eqb.ParamFormats, c.eqb.ResultFormats)
|
||||||
} else {
|
} else {
|
||||||
err := c.eqb.Build(c.typeMap, nil, bi.arguments)
|
err := c.eqb.Build(c.typeMap, nil, bi.Arguments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &batchResults{ctx: ctx, conn: c, err: err}
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1023,18 +1023,18 @@ func (c *Conn) sendBatchQueryExecModeCacheStatement(ctx context.Context, b *Batc
|
||||||
distinctNewQueries := []*pgconn.StatementDescription{}
|
distinctNewQueries := []*pgconn.StatementDescription{}
|
||||||
distinctNewQueriesIdxMap := make(map[string]int)
|
distinctNewQueriesIdxMap := make(map[string]int)
|
||||||
|
|
||||||
for _, bi := range b.queuedQueries {
|
for _, bi := range b.QueuedQueries {
|
||||||
if bi.sd == nil {
|
if bi.sd == nil {
|
||||||
sd := c.statementCache.Get(bi.query)
|
sd := c.statementCache.Get(bi.SQL)
|
||||||
if sd != nil {
|
if sd != nil {
|
||||||
bi.sd = sd
|
bi.sd = sd
|
||||||
} else {
|
} else {
|
||||||
if idx, present := distinctNewQueriesIdxMap[bi.query]; present {
|
if idx, present := distinctNewQueriesIdxMap[bi.SQL]; present {
|
||||||
bi.sd = distinctNewQueries[idx]
|
bi.sd = distinctNewQueries[idx]
|
||||||
} else {
|
} else {
|
||||||
sd = &pgconn.StatementDescription{
|
sd = &pgconn.StatementDescription{
|
||||||
Name: stmtcache.StatementName(bi.query),
|
Name: stmtcache.StatementName(bi.SQL),
|
||||||
SQL: bi.query,
|
SQL: bi.SQL,
|
||||||
}
|
}
|
||||||
distinctNewQueriesIdxMap[sd.SQL] = len(distinctNewQueries)
|
distinctNewQueriesIdxMap[sd.SQL] = len(distinctNewQueries)
|
||||||
distinctNewQueries = append(distinctNewQueries, sd)
|
distinctNewQueries = append(distinctNewQueries, sd)
|
||||||
|
@ -1055,17 +1055,17 @@ func (c *Conn) sendBatchQueryExecModeCacheDescribe(ctx context.Context, b *Batch
|
||||||
distinctNewQueries := []*pgconn.StatementDescription{}
|
distinctNewQueries := []*pgconn.StatementDescription{}
|
||||||
distinctNewQueriesIdxMap := make(map[string]int)
|
distinctNewQueriesIdxMap := make(map[string]int)
|
||||||
|
|
||||||
for _, bi := range b.queuedQueries {
|
for _, bi := range b.QueuedQueries {
|
||||||
if bi.sd == nil {
|
if bi.sd == nil {
|
||||||
sd := c.descriptionCache.Get(bi.query)
|
sd := c.descriptionCache.Get(bi.SQL)
|
||||||
if sd != nil {
|
if sd != nil {
|
||||||
bi.sd = sd
|
bi.sd = sd
|
||||||
} else {
|
} else {
|
||||||
if idx, present := distinctNewQueriesIdxMap[bi.query]; present {
|
if idx, present := distinctNewQueriesIdxMap[bi.SQL]; present {
|
||||||
bi.sd = distinctNewQueries[idx]
|
bi.sd = distinctNewQueries[idx]
|
||||||
} else {
|
} else {
|
||||||
sd = &pgconn.StatementDescription{
|
sd = &pgconn.StatementDescription{
|
||||||
SQL: bi.query,
|
SQL: bi.SQL,
|
||||||
}
|
}
|
||||||
distinctNewQueriesIdxMap[sd.SQL] = len(distinctNewQueries)
|
distinctNewQueriesIdxMap[sd.SQL] = len(distinctNewQueries)
|
||||||
distinctNewQueries = append(distinctNewQueries, sd)
|
distinctNewQueries = append(distinctNewQueries, sd)
|
||||||
|
@ -1082,13 +1082,13 @@ func (c *Conn) sendBatchQueryExecModeDescribeExec(ctx context.Context, b *Batch)
|
||||||
distinctNewQueries := []*pgconn.StatementDescription{}
|
distinctNewQueries := []*pgconn.StatementDescription{}
|
||||||
distinctNewQueriesIdxMap := make(map[string]int)
|
distinctNewQueriesIdxMap := make(map[string]int)
|
||||||
|
|
||||||
for _, bi := range b.queuedQueries {
|
for _, bi := range b.QueuedQueries {
|
||||||
if bi.sd == nil {
|
if bi.sd == nil {
|
||||||
if idx, present := distinctNewQueriesIdxMap[bi.query]; present {
|
if idx, present := distinctNewQueriesIdxMap[bi.SQL]; present {
|
||||||
bi.sd = distinctNewQueries[idx]
|
bi.sd = distinctNewQueries[idx]
|
||||||
} else {
|
} else {
|
||||||
sd := &pgconn.StatementDescription{
|
sd := &pgconn.StatementDescription{
|
||||||
SQL: bi.query,
|
SQL: bi.SQL,
|
||||||
}
|
}
|
||||||
distinctNewQueriesIdxMap[sd.SQL] = len(distinctNewQueries)
|
distinctNewQueriesIdxMap[sd.SQL] = len(distinctNewQueries)
|
||||||
distinctNewQueries = append(distinctNewQueries, sd)
|
distinctNewQueries = append(distinctNewQueries, sd)
|
||||||
|
@ -1154,11 +1154,11 @@ func (c *Conn) sendBatchExtendedWithDescription(ctx context.Context, b *Batch, d
|
||||||
}
|
}
|
||||||
|
|
||||||
// Queue the queries.
|
// Queue the queries.
|
||||||
for _, bi := range b.queuedQueries {
|
for _, bi := range b.QueuedQueries {
|
||||||
err := c.eqb.Build(c.typeMap, bi.sd, bi.arguments)
|
err := c.eqb.Build(c.typeMap, bi.sd, bi.Arguments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// we wrap the error so we the user can understand which query failed inside the batch
|
// 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}
|
return &pipelineBatchResults{ctx: ctx, conn: c, err: err, closed: true}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1203,7 +1203,15 @@ func (c *Conn) sanitizeForSimpleQuery(sql string, args ...any) (string, error) {
|
||||||
return sanitize.SanitizeSQL(sql, valueArgs...)
|
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) {
|
func (c *Conn) LoadType(ctx context.Context, typeName string) (*pgtype.Type, error) {
|
||||||
var oid uint32
|
var oid uint32
|
||||||
|
|
||||||
|
@ -1351,12 +1359,12 @@ func (c *Conn) deallocateInvalidatedCachedStatements(ctx context.Context) error
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.descriptionCache != nil {
|
if c.descriptionCache != nil {
|
||||||
c.descriptionCache.HandleInvalidated()
|
c.descriptionCache.RemoveInvalidated()
|
||||||
}
|
}
|
||||||
|
|
||||||
var invalidatedStatements []*pgconn.StatementDescription
|
var invalidatedStatements []*pgconn.StatementDescription
|
||||||
if c.statementCache != nil {
|
if c.statementCache != nil {
|
||||||
invalidatedStatements = c.statementCache.HandleInvalidated()
|
invalidatedStatements = c.statementCache.GetInvalidated()
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(invalidatedStatements) == 0 {
|
if len(invalidatedStatements) == 0 {
|
||||||
|
@ -1368,7 +1376,6 @@ func (c *Conn) deallocateInvalidatedCachedStatements(ctx context.Context) error
|
||||||
|
|
||||||
for _, sd := range invalidatedStatements {
|
for _, sd := range invalidatedStatements {
|
||||||
pipeline.SendDeallocate(sd.Name)
|
pipeline.SendDeallocate(sd.Name)
|
||||||
delete(c.preparedStatements, sd.Name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err := pipeline.Sync()
|
err := pipeline.Sync()
|
||||||
|
@ -1381,5 +1388,10 @@ func (c *Conn) deallocateInvalidatedCachedStatements(ctx context.Context) error
|
||||||
return fmt.Errorf("failed to deallocate cached statement(s): %w", err)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,12 +81,16 @@ func (c *LRUCache) InvalidateAll() {
|
||||||
c.l = list.New()
|
c.l = list.New()
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleInvalidated returns a slice of all statement descriptions invalidated since the last call to HandleInvalidated.
|
// GetInvalidated returns a slice of all statement descriptions invalidated since the last call to RemoveInvalidated.
|
||||||
// Typically, the caller will then deallocate them.
|
func (c *LRUCache) GetInvalidated() []*pgconn.StatementDescription {
|
||||||
func (c *LRUCache) HandleInvalidated() []*pgconn.StatementDescription {
|
return c.invalidStmts
|
||||||
invalidStmts := 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
|
c.invalidStmts = nil
|
||||||
return invalidStmts
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Len returns the number of cached prepared statement descriptions.
|
// Len returns the number of cached prepared statement descriptions.
|
||||||
|
|
|
@ -29,8 +29,13 @@ type Cache interface {
|
||||||
// InvalidateAll invalidates all statement descriptions.
|
// InvalidateAll invalidates all statement descriptions.
|
||||||
InvalidateAll()
|
InvalidateAll()
|
||||||
|
|
||||||
// HandleInvalidated returns a slice of all statement descriptions invalidated since the last call to HandleInvalidated.
|
// GetInvalidated returns a slice of all statement descriptions invalidated since the last call to RemoveInvalidated.
|
||||||
HandleInvalidated() []*pgconn.StatementDescription
|
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 returns the number of cached prepared statement descriptions.
|
||||||
Len() int
|
Len() int
|
||||||
|
|
|
@ -54,10 +54,16 @@ func (c *UnlimitedCache) InvalidateAll() {
|
||||||
c.m = make(map[string]*pgconn.StatementDescription)
|
c.m = make(map[string]*pgconn.StatementDescription)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *UnlimitedCache) HandleInvalidated() []*pgconn.StatementDescription {
|
// GetInvalidated returns a slice of all statement descriptions invalidated since the last call to RemoveInvalidated.
|
||||||
invalidStmts := c.invalidStmts
|
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
|
c.invalidStmts = nil
|
||||||
return invalidStmts
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Len returns the number of cached prepared statement descriptions.
|
// Len returns the number of cached prepared statement descriptions.
|
||||||
|
|
|
@ -6,6 +6,11 @@ import (
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 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
|
// LargeObjects is a structure used to access the large objects API. It is only valid within the transaction where it
|
||||||
// was created.
|
// was created.
|
||||||
//
|
//
|
||||||
|
@ -67,41 +72,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.
|
// Write writes p to the large object and returns the number of bytes written and an error if not all of p was written.
|
||||||
//
|
|
||||||
// Write is implemented with a single call to lowrite. 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,
|
|
||||||
// len(p) should be no larger than 1 GB - 1 KB.
|
|
||||||
func (o *LargeObject) Write(p []byte) (int, error) {
|
func (o *LargeObject) Write(p []byte) (int, error) {
|
||||||
|
nTotal := 0
|
||||||
|
for {
|
||||||
|
expected := len(p) - nTotal
|
||||||
|
if expected == 0 {
|
||||||
|
break
|
||||||
|
} else if expected > maxLargeObjectMessageLength {
|
||||||
|
expected = maxLargeObjectMessageLength
|
||||||
|
}
|
||||||
|
|
||||||
var n int
|
var n int
|
||||||
err := o.tx.QueryRow(o.ctx, "select lowrite($1, $2)", o.fd, p).Scan(&n)
|
err := o.tx.QueryRow(o.ctx, "select lowrite($1, $2)", o.fd, p[nTotal:nTotal+expected]).Scan(&n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return n, err
|
return nTotal, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if n < 0 {
|
if n < 0 {
|
||||||
return 0, errors.New("failed to write to large object")
|
return nTotal, errors.New("failed to write to large object")
|
||||||
}
|
}
|
||||||
|
|
||||||
return n, nil
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nTotal, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read reads up to len(p) bytes into p returning the number of bytes read.
|
// Read reads up to len(p) bytes into p returning the number of bytes read.
|
||||||
//
|
|
||||||
// Read is implemented with a single call to loread. PostgreSQL internally allocates a single buffer for the response.
|
|
||||||
// The largest buffer PostgreSQL will allocate is 1 GB - 1. See definition of MaxAllocSize in the PostgreSQL source
|
|
||||||
// code. To allow for the other data in the message, len(p) should be no larger than 1 GB - 1 KB.
|
|
||||||
func (o *LargeObject) Read(p []byte) (int, error) {
|
func (o *LargeObject) Read(p []byte) (int, error) {
|
||||||
var res []byte
|
nTotal := 0
|
||||||
err := o.tx.QueryRow(o.ctx, "select loread($1, $2)", o.fd, len(p)).Scan(&res)
|
for {
|
||||||
copy(p, res)
|
expected := len(p) - nTotal
|
||||||
if err != nil {
|
if expected == 0 {
|
||||||
return len(res), err
|
break
|
||||||
|
} else if expected > maxLargeObjectMessageLength {
|
||||||
|
expected = maxLargeObjectMessageLength
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(res) < len(p) {
|
var res []byte
|
||||||
err = io.EOF
|
err := o.tx.QueryRow(o.ctx, "select loread($1, $2)", o.fd, expected).Scan(&res)
|
||||||
|
copy(p[nTotal:], res)
|
||||||
|
nTotal += len(res)
|
||||||
|
if err != nil {
|
||||||
|
return nTotal, err
|
||||||
}
|
}
|
||||||
return len(res), err
|
|
||||||
|
if len(res) < expected {
|
||||||
|
return nTotal, io.EOF
|
||||||
|
} else if len(res) > expected {
|
||||||
|
return nTotal, errors.New("invalid read of large object")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nTotal, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Seek moves the current location pointer to the new location specified by offset.
|
// Seek moves the current location pointer to the new location specified by offset.
|
||||||
|
|
|
@ -0,0 +1,122 @@
|
||||||
|
package pgtype
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql/driver"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LtreeCodec struct{}
|
||||||
|
|
||||||
|
func (l LtreeCodec) FormatSupported(format int16) bool {
|
||||||
|
return format == TextFormatCode || format == BinaryFormatCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// PreferredFormat returns the preferred format.
|
||||||
|
func (l LtreeCodec) PreferredFormat() int16 {
|
||||||
|
return TextFormatCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// PlanEncode returns an EncodePlan for encoding value into PostgreSQL format for oid and format. If no plan can be
|
||||||
|
// found then nil is returned.
|
||||||
|
func (l LtreeCodec) PlanEncode(m *Map, oid uint32, format int16, value any) EncodePlan {
|
||||||
|
switch format {
|
||||||
|
case TextFormatCode:
|
||||||
|
return (TextCodec)(l).PlanEncode(m, oid, format, value)
|
||||||
|
case BinaryFormatCode:
|
||||||
|
switch value.(type) {
|
||||||
|
case string:
|
||||||
|
return encodeLtreeCodecBinaryString{}
|
||||||
|
case []byte:
|
||||||
|
return encodeLtreeCodecBinaryByteSlice{}
|
||||||
|
case TextValuer:
|
||||||
|
return encodeLtreeCodecBinaryTextValuer{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type encodeLtreeCodecBinaryString struct{}
|
||||||
|
|
||||||
|
func (encodeLtreeCodecBinaryString) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
||||||
|
ltree := value.(string)
|
||||||
|
buf = append(buf, 1)
|
||||||
|
return append(buf, ltree...), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type encodeLtreeCodecBinaryByteSlice struct{}
|
||||||
|
|
||||||
|
func (encodeLtreeCodecBinaryByteSlice) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
||||||
|
ltree := value.([]byte)
|
||||||
|
buf = append(buf, 1)
|
||||||
|
return append(buf, ltree...), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type encodeLtreeCodecBinaryTextValuer struct{}
|
||||||
|
|
||||||
|
func (encodeLtreeCodecBinaryTextValuer) Encode(value any, buf []byte) (newBuf []byte, err error) {
|
||||||
|
t, err := value.(TextValuer).TextValue()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !t.Valid {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = append(buf, 1)
|
||||||
|
return append(buf, t.String...), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PlanScan returns a ScanPlan for scanning a PostgreSQL value into a destination with the same type as target. If
|
||||||
|
// no plan can be found then nil is returned.
|
||||||
|
func (l LtreeCodec) PlanScan(m *Map, oid uint32, format int16, target any) ScanPlan {
|
||||||
|
switch format {
|
||||||
|
case TextFormatCode:
|
||||||
|
return (TextCodec)(l).PlanScan(m, oid, format, target)
|
||||||
|
case BinaryFormatCode:
|
||||||
|
switch target.(type) {
|
||||||
|
case *string:
|
||||||
|
return scanPlanBinaryLtreeToString{}
|
||||||
|
case TextScanner:
|
||||||
|
return scanPlanBinaryLtreeToTextScanner{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type scanPlanBinaryLtreeToString struct{}
|
||||||
|
|
||||||
|
func (scanPlanBinaryLtreeToString) Scan(src []byte, target any) error {
|
||||||
|
version := src[0]
|
||||||
|
if version != 1 {
|
||||||
|
return fmt.Errorf("unsupported ltree version %d", version)
|
||||||
|
}
|
||||||
|
|
||||||
|
p := (target).(*string)
|
||||||
|
*p = string(src[1:])
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type scanPlanBinaryLtreeToTextScanner struct{}
|
||||||
|
|
||||||
|
func (scanPlanBinaryLtreeToTextScanner) Scan(src []byte, target any) error {
|
||||||
|
version := src[0]
|
||||||
|
if version != 1 {
|
||||||
|
return fmt.Errorf("unsupported ltree version %d", version)
|
||||||
|
}
|
||||||
|
|
||||||
|
scanner := (target).(TextScanner)
|
||||||
|
return scanner.ScanText(Text{String: string(src[1:]), Valid: true})
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeDatabaseSQLValue returns src decoded into a value compatible with the sql.Scanner interface.
|
||||||
|
func (l LtreeCodec) DecodeDatabaseSQLValue(m *Map, oid uint32, format int16, src []byte) (driver.Value, error) {
|
||||||
|
return (TextCodec)(l).DecodeDatabaseSQLValue(m, oid, format, src)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecodeValue returns src decoded into its default format.
|
||||||
|
func (l LtreeCodec) DecodeValue(m *Map, oid uint32, format int16, src []byte) (any, error) {
|
||||||
|
return (TextCodec)(l).DecodeValue(m, oid, format, src)
|
||||||
|
}
|
|
@ -81,6 +81,8 @@ const (
|
||||||
IntervalOID = 1186
|
IntervalOID = 1186
|
||||||
IntervalArrayOID = 1187
|
IntervalArrayOID = 1187
|
||||||
NumericArrayOID = 1231
|
NumericArrayOID = 1231
|
||||||
|
TimetzOID = 1266
|
||||||
|
TimetzArrayOID = 1270
|
||||||
BitOID = 1560
|
BitOID = 1560
|
||||||
BitArrayOID = 1561
|
BitArrayOID = 1561
|
||||||
VarbitOID = 1562
|
VarbitOID = 1562
|
||||||
|
|
|
@ -52,7 +52,19 @@ func parseUUID(src string) (dst [16]byte, err error) {
|
||||||
|
|
||||||
// encodeUUID converts a uuid byte array to UUID standard string form.
|
// encodeUUID converts a uuid byte array to UUID standard string form.
|
||||||
func encodeUUID(src [16]byte) string {
|
func encodeUUID(src [16]byte) string {
|
||||||
return fmt.Sprintf("%x-%x-%x-%x-%x", src[0:4], src[4:6], src[6:8], src[8:10], src[10:16])
|
var buf [36]byte
|
||||||
|
|
||||||
|
hex.Encode(buf[0:8], src[:4])
|
||||||
|
buf[8] = '-'
|
||||||
|
hex.Encode(buf[9:13], src[4:6])
|
||||||
|
buf[13] = '-'
|
||||||
|
hex.Encode(buf[14:18], src[6:8])
|
||||||
|
buf[18] = '-'
|
||||||
|
hex.Encode(buf[19:23], src[8:10])
|
||||||
|
buf[23] = '-'
|
||||||
|
hex.Encode(buf[24:], src[10:])
|
||||||
|
|
||||||
|
return string(buf[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scan implements the database/sql Scanner interface.
|
// Scan implements the database/sql Scanner interface.
|
||||||
|
|
|
@ -417,12 +417,10 @@ type CollectableRow interface {
|
||||||
// RowToFunc is a function that scans or otherwise converts row to a T.
|
// RowToFunc is a function that scans or otherwise converts row to a T.
|
||||||
type RowToFunc[T any] func(row CollectableRow) (T, error)
|
type RowToFunc[T any] func(row CollectableRow) (T, error)
|
||||||
|
|
||||||
// CollectRows iterates through rows, calling fn for each row, and collecting the results into a slice of T.
|
// AppendRows iterates through rows, calling fn for each row, and appending the results into a slice of T.
|
||||||
func CollectRows[T any](rows Rows, fn RowToFunc[T]) ([]T, error) {
|
func AppendRows[T any, S ~[]T](slice S, rows Rows, fn RowToFunc[T]) (S, error) {
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
slice := []T{}
|
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
value, err := fn(rows)
|
value, err := fn(rows)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -438,6 +436,11 @@ func CollectRows[T any](rows Rows, fn RowToFunc[T]) ([]T, error) {
|
||||||
return slice, nil
|
return slice, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CollectRows iterates through rows, calling fn for each row, and collecting the results into a slice of T.
|
||||||
|
func CollectRows[T any](rows Rows, fn RowToFunc[T]) ([]T, error) {
|
||||||
|
return AppendRows([]T(nil), rows, fn)
|
||||||
|
}
|
||||||
|
|
||||||
// CollectOneRow calls fn for the first row in rows and returns the result. If no rows are found returns an error where errors.Is(ErrNoRows) is true.
|
// CollectOneRow calls fn for the first row in rows and returns the result. If no rows are found returns an error where errors.Is(ErrNoRows) is true.
|
||||||
// CollectOneRow is to CollectRows as QueryRow is to Query.
|
// CollectOneRow is to CollectRows as QueryRow is to Query.
|
||||||
func CollectOneRow[T any](rows Rows, fn RowToFunc[T]) (T, error) {
|
func CollectOneRow[T any](rows Rows, fn RowToFunc[T]) (T, error) {
|
||||||
|
|
|
@ -55,7 +55,11 @@ func encodeCopyValue(m *pgtype.Map, buf []byte, oid uint32, arg any) ([]byte, er
|
||||||
func tryScanStringCopyValueThenEncode(m *pgtype.Map, buf []byte, oid uint32, arg any) ([]byte, error) {
|
func tryScanStringCopyValueThenEncode(m *pgtype.Map, buf []byte, oid uint32, arg any) ([]byte, error) {
|
||||||
s, ok := arg.(string)
|
s, ok := arg.(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("not a string")
|
textBuf, err := m.Encode(oid, TextFormatCode, arg, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New("not a string and cannot be encoded as text")
|
||||||
|
}
|
||||||
|
s = string(textBuf)
|
||||||
}
|
}
|
||||||
|
|
||||||
var v any
|
var v any
|
||||||
|
|
|
@ -324,7 +324,7 @@ github.com/jackc/pgpassfile
|
||||||
# github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a
|
# github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a
|
||||||
## explicit; go 1.14
|
## explicit; go 1.14
|
||||||
github.com/jackc/pgservicefile
|
github.com/jackc/pgservicefile
|
||||||
# github.com/jackc/pgx/v5 v5.5.2
|
# github.com/jackc/pgx/v5 v5.5.3
|
||||||
## explicit; go 1.19
|
## explicit; go 1.19
|
||||||
github.com/jackc/pgx/v5
|
github.com/jackc/pgx/v5
|
||||||
github.com/jackc/pgx/v5/internal/anynil
|
github.com/jackc/pgx/v5/internal/anynil
|
||||||
|
|
Loading…
Reference in New Issue