go.mod: Update openshift-online/ocm-sdk-go

This requires golang-jwt/jwt/v4.
This commit is contained in:
Sanne Raymaekers 2022-05-17 19:15:17 +02:00
parent 56a7059b40
commit 7529382890
111 changed files with 4401 additions and 1462 deletions

View file

@ -1,3 +1,9 @@
# 1.12.0 (April 21, 2022)
* Add pluggable GSSAPI support (Oliver Tan)
* Fix: Consider any "0A000" error a possible cached plan changed error due to locale
* Better match psql fallback behavior with multiple hosts
# 1.11.0 (February 7, 2022)
* Support port in ip from LookupFunc to override config (James Hartig)

View file

@ -176,8 +176,6 @@ func NetworkAddress(host string, port uint16) (network, address string) {
//
// Other known differences with libpq:
//
// If a host name resolves into multiple addresses, libpq will try all addresses. pgconn will only try the first.
//
// When multiple hosts are specified, libpq allows them to have different passwords set via the .pgpass file. pgconn
// does not.
//
@ -259,6 +257,8 @@ func ParseConfig(connString string) (*Config, error) {
"sslkey": {},
"sslcert": {},
"sslrootcert": {},
"krbspn": {},
"krbsrvname": {},
"target_session_attrs": {},
"min_read_buffer_size": {},
"service": {},

View file

@ -7,9 +7,9 @@ require (
github.com/jackc/pgio v1.0.0
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65
github.com/jackc/pgpassfile v1.0.0
github.com/jackc/pgproto3/v2 v2.1.1
github.com/jackc/pgproto3/v2 v2.3.0
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b
github.com/stretchr/testify v1.7.0
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
golang.org/x/text v0.3.6
golang.org/x/text v0.3.7
)

View file

@ -31,8 +31,11 @@ github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.1.1 h1:7PQ/4gLoqnl87ZxL7xjO0DR5gYuviDCZxQJsUlFW1eI=
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.2.1-0.20220412121321-175856ffd3c8 h1:KxsCQec+1iwJXtxnbbS/dY0EJ6rJEUlFsrJUnL5A2XI=
github.com/jackc/pgproto3/v2 v2.2.1-0.20220412121321-175856ffd3c8/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.3.0 h1:brH0pCGBDkBW07HWlN/oSBXrmo3WB0UvZd1pIuDcL8Y=
github.com/jackc/pgproto3/v2 v2.3.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
@ -112,8 +115,9 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=

94
vendor/github.com/jackc/pgconn/krb5.go generated vendored Normal file
View file

@ -0,0 +1,94 @@
package pgconn
import (
"errors"
"github.com/jackc/pgproto3/v2"
)
// NewGSSFunc creates a GSS authentication provider, for use with
// RegisterGSSProvider.
type NewGSSFunc func() (GSS, error)
var newGSS NewGSSFunc
// RegisterGSSProvider registers a GSS authentication provider. For example, if
// you need to use Kerberos to authenticate with your server, add this to your
// main package:
//
// import "github.com/otan/gopgkrb5"
//
// func init() {
// pgconn.RegisterGSSProvider(func() (pgconn.GSS, error) { return gopgkrb5.NewGSS() })
// }
func RegisterGSSProvider(newGSSArg NewGSSFunc) {
newGSS = newGSSArg
}
// GSS provides GSSAPI authentication (e.g., Kerberos).
type GSS interface {
GetInitToken(host string, service string) ([]byte, error)
GetInitTokenFromSPN(spn string) ([]byte, error)
Continue(inToken []byte) (done bool, outToken []byte, err error)
}
func (c *PgConn) gssAuth() error {
if newGSS == nil {
return errors.New("kerberos error: no GSSAPI provider registered, see https://github.com/otan/gopgkrb5")
}
cli, err := newGSS()
if err != nil {
return err
}
var nextData []byte
if spn, ok := c.config.RuntimeParams["krbspn"]; ok {
// Use the supplied SPN if provided.
nextData, err = cli.GetInitTokenFromSPN(spn)
} else {
// Allow the kerberos service name to be overridden
service := "postgres"
if val, ok := c.config.RuntimeParams["krbsrvname"]; ok {
service = val
}
nextData, err = cli.GetInitToken(c.config.Host, service)
}
if err != nil {
return err
}
for {
gssResponse := &pgproto3.GSSResponse{
Data: nextData,
}
_, err = c.conn.Write(gssResponse.Encode(nil))
if err != nil {
return err
}
resp, err := c.rxGSSContinue()
if err != nil {
return err
}
var done bool
done, nextData, err = cli.Continue(resp.Data)
if err != nil {
return err
}
if done {
break
}
}
return nil
}
func (c *PgConn) rxGSSContinue() (*pgproto3.AuthenticationGSSContinue, error) {
msg, err := c.receiveMessage()
if err != nil {
return nil, err
}
gssContinue, ok := msg.(*pgproto3.AuthenticationGSSContinue)
if ok {
return gssContinue, nil
}
return nil, errors.New("expected AuthenticationGSSContinue message but received unexpected message")
}

View file

@ -99,7 +99,7 @@ type PgConn struct {
}
// Connect establishes a connection to a PostgreSQL server using the environment and connString (in URL or DSN format)
// to provide configuration. See documention for ParseConfig for details. ctx can be used to cancel a connect attempt.
// to provide configuration. See documentation for ParseConfig for details. ctx can be used to cancel a connect attempt.
func Connect(ctx context.Context, connString string) (*PgConn, error) {
config, err := ParseConfig(connString)
if err != nil {
@ -154,9 +154,14 @@ func ConnectConfig(ctx context.Context, config *Config) (pgConn *PgConn, err err
break
} else if pgerr, ok := err.(*PgError); ok {
err = &connectError{config: config, msg: "server error", err: pgerr}
ERRCODE_INVALID_PASSWORD := "28P01" // worng password
ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION := "28000" // db does not exist
if pgerr.Code == ERRCODE_INVALID_PASSWORD || pgerr.Code == ERRCODE_INVALID_AUTHORIZATION_SPECIFICATION {
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 ||
pgerr.Code == ERRCODE_INVALID_CATALOG_NAME ||
pgerr.Code == ERRCODE_INSUFFICIENT_PRIVILEGE {
break
}
}
@ -317,7 +322,12 @@ func connect(ctx context.Context, config *Config, fallbackConfig *FallbackConfig
pgConn.conn.Close()
return nil, &connectError{config: config, msg: "failed SASL auth", err: err}
}
case *pgproto3.AuthenticationGSS:
err = pgConn.gssAuth()
if err != nil {
pgConn.conn.Close()
return nil, &connectError{config: config, msg: "failed GSS auth", err: err}
}
case *pgproto3.ReadyForQuery:
pgConn.status = connStatusIdle
if config.ValidateConnect != nil {

View file

@ -102,10 +102,14 @@ func (c *LRU) StatementErrored(sql string, err error) {
return
}
isInvalidCachedPlanError := pgErr.Severity == "ERROR" &&
pgErr.Code == "0A000" &&
pgErr.Message == "cached plan must not change result type"
if isInvalidCachedPlanError {
// 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"
if possibleInvalidCachedPlanError {
c.stmtsToClear = append(c.stmtsToClear, sql)
}
}

View file

@ -0,0 +1,58 @@
package pgproto3
import (
"encoding/binary"
"encoding/json"
"errors"
"github.com/jackc/pgio"
)
type AuthenticationGSS struct{}
func (a *AuthenticationGSS) Backend() {}
func (a *AuthenticationGSS) AuthenticationResponse() {}
func (a *AuthenticationGSS) Decode(src []byte) error {
if len(src) < 4 {
return errors.New("authentication message too short")
}
authType := binary.BigEndian.Uint32(src)
if authType != AuthTypeGSS {
return errors.New("bad auth type")
}
return nil
}
func (a *AuthenticationGSS) Encode(dst []byte) []byte {
dst = append(dst, 'R')
dst = pgio.AppendInt32(dst, 4)
dst = pgio.AppendUint32(dst, AuthTypeGSS)
return dst
}
func (a *AuthenticationGSS) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Type string
Data []byte
}{
Type: "AuthenticationGSS",
})
}
func (a *AuthenticationGSS) UnmarshalJSON(data []byte) error {
// Ignore null, like in the main JSON package.
if string(data) == "null" {
return nil
}
var msg struct {
Type string
}
if err := json.Unmarshal(data, &msg); err != nil {
return err
}
return nil
}

View file

@ -0,0 +1,67 @@
package pgproto3
import (
"encoding/binary"
"encoding/json"
"errors"
"github.com/jackc/pgio"
)
type AuthenticationGSSContinue struct {
Data []byte
}
func (a *AuthenticationGSSContinue) Backend() {}
func (a *AuthenticationGSSContinue) AuthenticationResponse() {}
func (a *AuthenticationGSSContinue) Decode(src []byte) error {
if len(src) < 4 {
return errors.New("authentication message too short")
}
authType := binary.BigEndian.Uint32(src)
if authType != AuthTypeGSSCont {
return errors.New("bad auth type")
}
a.Data = src[4:]
return nil
}
func (a *AuthenticationGSSContinue) Encode(dst []byte) []byte {
dst = append(dst, 'R')
dst = pgio.AppendInt32(dst, int32(len(a.Data))+8)
dst = pgio.AppendUint32(dst, AuthTypeGSSCont)
dst = append(dst, a.Data...)
return dst
}
func (a *AuthenticationGSSContinue) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Type string
Data []byte
}{
Type: "AuthenticationGSSContinue",
Data: a.Data,
})
}
func (a *AuthenticationGSSContinue) UnmarshalJSON(data []byte) error {
// Ignore null, like in the main JSON package.
if string(data) == "null" {
return nil
}
var msg struct {
Type string
Data []byte
}
if err := json.Unmarshal(data, &msg); err != nil {
return err
}
a.Data = msg.Data
return nil
}

View file

@ -30,11 +30,10 @@ type Backend struct {
sync Sync
terminate Terminate
bodyLen int
msgType byte
partialMsg bool
authType uint32
bodyLen int
msgType byte
partialMsg bool
authType uint32
}
const (
@ -147,6 +146,8 @@ func (b *Backend) Receive() (FrontendMessage, error) {
msg = &SASLResponse{}
case AuthTypeSASLFinal:
msg = &SASLResponse{}
case AuthTypeGSS, AuthTypeGSSCont:
msg = &GSSResponse{}
case AuthTypeCleartextPassword, AuthTypeMD5Password:
fallthrough
default:

View file

@ -48,7 +48,7 @@ func (src *CopyBothResponse) Encode(dst []byte) []byte {
dst = append(dst, 'W')
sp := len(dst)
dst = pgio.AppendInt32(dst, -1)
dst = append(dst, src.OverallFormat)
dst = pgio.AppendUint16(dst, uint16(len(src.ColumnFormatCodes)))
for _, fc := range src.ColumnFormatCodes {
dst = pgio.AppendUint16(dst, fc)

View file

@ -16,6 +16,8 @@ type Frontend struct {
authenticationOk AuthenticationOk
authenticationCleartextPassword AuthenticationCleartextPassword
authenticationMD5Password AuthenticationMD5Password
authenticationGSS AuthenticationGSS
authenticationGSSContinue AuthenticationGSSContinue
authenticationSASL AuthenticationSASL
authenticationSASLContinue AuthenticationSASLContinue
authenticationSASLFinal AuthenticationSASLFinal
@ -178,9 +180,9 @@ func (f *Frontend) findAuthenticationMessageType(src []byte) (BackendMessage, er
case AuthTypeSCMCreds:
return nil, errors.New("AuthTypeSCMCreds is unimplemented")
case AuthTypeGSS:
return nil, errors.New("AuthTypeGSS is unimplemented")
return &f.authenticationGSS, nil
case AuthTypeGSSCont:
return nil, errors.New("AuthTypeGSSCont is unimplemented")
return &f.authenticationGSSContinue, nil
case AuthTypeSSPI:
return nil, errors.New("AuthTypeSSPI is unimplemented")
case AuthTypeSASL:

View file

@ -9,6 +9,7 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

48
vendor/github.com/jackc/pgproto3/v2/gss_response.go generated vendored Normal file
View file

@ -0,0 +1,48 @@
package pgproto3
import (
"encoding/json"
"github.com/jackc/pgio"
)
type GSSResponse struct {
Data []byte
}
// Frontend identifies this message as sendable by a PostgreSQL frontend.
func (g *GSSResponse) Frontend() {}
func (g *GSSResponse) Decode(data []byte) error {
g.Data = data
return nil
}
func (g *GSSResponse) Encode(dst []byte) []byte {
dst = append(dst, 'p')
dst = pgio.AppendInt32(dst, int32(4+len(g.Data)))
dst = append(dst, g.Data...)
return dst
}
// MarshalJSON implements encoding/json.Marshaler.
func (g *GSSResponse) MarshalJSON() ([]byte, error) {
return json.Marshal(struct {
Type string
Data []byte
}{
Type: "GSSResponse",
Data: g.Data,
})
}
// UnmarshalJSON implements encoding/json.Unmarshaler.
func (g *GSSResponse) UnmarshalJSON(data []byte) error {
var msg struct {
Data []byte
}
if err := json.Unmarshal(data, &msg); err != nil {
return err
}
g.Data = msg.Data
return nil
}

View file

@ -1,3 +1,11 @@
# 1.11.0 (April 21, 2022)
* Add multirange for numeric, int4, and int8 (Vu)
* JSONBArray now supports json.RawMessage (Jens Emil Schulz Østergaard)
* Add RecordArray (WGH)
* Add UnmarshalJSON to pgtype.Int2
* Hstore.Set accepts map[string]Text
# 1.10.0 (February 7, 2022)
* Normalize UTC timestamps to comply with stdlib (Torkel Rogstad)

View file

@ -50,6 +50,8 @@ func (dst *Hstore) Set(src interface{}) error {
}
}
*dst = Hstore{Map: m, Status: Present}
case map[string]Text:
*dst = Hstore{Map: value, Status: Present}
default:
return fmt.Errorf("cannot convert %v to Hstore", src)
}

View file

@ -3,6 +3,7 @@ package pgtype
import (
"database/sql/driver"
"encoding/binary"
"encoding/json"
"fmt"
"math"
"strconv"
@ -302,3 +303,19 @@ func (src Int2) MarshalJSON() ([]byte, error) {
return nil, errBadStatus
}
func (dst *Int2) UnmarshalJSON(b []byte) error {
var n *int16
err := json.Unmarshal(b, &n)
if err != nil {
return err
}
if n == nil {
*dst = Int2{Status: Null}
} else {
*dst = Int2{Int: *n, Status: Present}
}
return nil
}

239
vendor/github.com/jackc/pgtype/int4_multirange.go generated vendored Normal file
View file

@ -0,0 +1,239 @@
package pgtype
import (
"database/sql/driver"
"encoding/binary"
"fmt"
"github.com/jackc/pgio"
)
type Int4multirange struct {
Ranges []Int4range
Status Status
}
func (dst *Int4multirange) Set(src interface{}) error {
//untyped nil and typed nil interfaces are different
if src == nil {
*dst = Int4multirange{Status: Null}
return nil
}
switch value := src.(type) {
case Int4multirange:
*dst = value
case *Int4multirange:
*dst = *value
case string:
return dst.DecodeText(nil, []byte(value))
case []Int4range:
if value == nil {
*dst = Int4multirange{Status: Null}
} else if len(value) == 0 {
*dst = Int4multirange{Status: Present}
} else {
elements := make([]Int4range, len(value))
for i := range value {
if err := elements[i].Set(value[i]); err != nil {
return err
}
}
*dst = Int4multirange{
Ranges: elements,
Status: Present,
}
}
case []*Int4range:
if value == nil {
*dst = Int4multirange{Status: Null}
} else if len(value) == 0 {
*dst = Int4multirange{Status: Present}
} else {
elements := make([]Int4range, len(value))
for i := range value {
if err := elements[i].Set(value[i]); err != nil {
return err
}
}
*dst = Int4multirange{
Ranges: elements,
Status: Present,
}
}
default:
return fmt.Errorf("cannot convert %v to Int4multirange", src)
}
return nil
}
func (dst Int4multirange) Get() interface{} {
switch dst.Status {
case Present:
return dst
case Null:
return nil
default:
return dst.Status
}
}
func (src *Int4multirange) AssignTo(dst interface{}) error {
return fmt.Errorf("cannot assign %v to %T", src, dst)
}
func (dst *Int4multirange) DecodeText(ci *ConnInfo, src []byte) error {
if src == nil {
*dst = Int4multirange{Status: Null}
return nil
}
utmr, err := ParseUntypedTextMultirange(string(src))
if err != nil {
return err
}
var elements []Int4range
if len(utmr.Elements) > 0 {
elements = make([]Int4range, len(utmr.Elements))
for i, s := range utmr.Elements {
var elem Int4range
elemSrc := []byte(s)
err = elem.DecodeText(ci, elemSrc)
if err != nil {
return err
}
elements[i] = elem
}
}
*dst = Int4multirange{Ranges: elements, Status: Present}
return nil
}
func (dst *Int4multirange) DecodeBinary(ci *ConnInfo, src []byte) error {
if src == nil {
*dst = Int4multirange{Status: Null}
return nil
}
rp := 0
numElems := int(binary.BigEndian.Uint32(src[rp:]))
rp += 4
if numElems == 0 {
*dst = Int4multirange{Status: Present}
return nil
}
elements := make([]Int4range, numElems)
for i := range elements {
elemLen := int(int32(binary.BigEndian.Uint32(src[rp:])))
rp += 4
var elemSrc []byte
if elemLen >= 0 {
elemSrc = src[rp : rp+elemLen]
rp += elemLen
}
err := elements[i].DecodeBinary(ci, elemSrc)
if err != nil {
return err
}
}
*dst = Int4multirange{Ranges: elements, Status: Present}
return nil
}
func (src Int4multirange) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
switch src.Status {
case Null:
return nil, nil
case Undefined:
return nil, errUndefined
}
buf = append(buf, '{')
inElemBuf := make([]byte, 0, 32)
for i, elem := range src.Ranges {
if i > 0 {
buf = append(buf, ',')
}
elemBuf, err := elem.EncodeText(ci, inElemBuf)
if err != nil {
return nil, err
}
if elemBuf == nil {
return nil, fmt.Errorf("multi-range does not allow null range")
} else {
buf = append(buf, string(elemBuf)...)
}
}
buf = append(buf, '}')
return buf, nil
}
func (src Int4multirange) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
switch src.Status {
case Null:
return nil, nil
case Undefined:
return nil, errUndefined
}
buf = pgio.AppendInt32(buf, int32(len(src.Ranges)))
for i := range src.Ranges {
sp := len(buf)
buf = pgio.AppendInt32(buf, -1)
elemBuf, err := src.Ranges[i].EncodeBinary(ci, buf)
if err != nil {
return nil, err
}
if elemBuf != nil {
buf = elemBuf
pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4))
}
}
return buf, nil
}
// Scan implements the database/sql Scanner interface.
func (dst *Int4multirange) Scan(src interface{}) error {
if src == nil {
return dst.DecodeText(nil, nil)
}
switch src := src.(type) {
case string:
return dst.DecodeText(nil, []byte(src))
case []byte:
srcCopy := make([]byte, len(src))
copy(srcCopy, src)
return dst.DecodeText(nil, srcCopy)
}
return fmt.Errorf("cannot scan %T", src)
}
// Value implements the database/sql/driver Valuer interface.
func (src Int4multirange) Value() (driver.Value, error) {
return EncodeValueText(src)
}

239
vendor/github.com/jackc/pgtype/int8_multirange.go generated vendored Normal file
View file

@ -0,0 +1,239 @@
package pgtype
import (
"database/sql/driver"
"encoding/binary"
"fmt"
"github.com/jackc/pgio"
)
type Int8multirange struct {
Ranges []Int8range
Status Status
}
func (dst *Int8multirange) Set(src interface{}) error {
//untyped nil and typed nil interfaces are different
if src == nil {
*dst = Int8multirange{Status: Null}
return nil
}
switch value := src.(type) {
case Int8multirange:
*dst = value
case *Int8multirange:
*dst = *value
case string:
return dst.DecodeText(nil, []byte(value))
case []Int8range:
if value == nil {
*dst = Int8multirange{Status: Null}
} else if len(value) == 0 {
*dst = Int8multirange{Status: Present}
} else {
elements := make([]Int8range, len(value))
for i := range value {
if err := elements[i].Set(value[i]); err != nil {
return err
}
}
*dst = Int8multirange{
Ranges: elements,
Status: Present,
}
}
case []*Int8range:
if value == nil {
*dst = Int8multirange{Status: Null}
} else if len(value) == 0 {
*dst = Int8multirange{Status: Present}
} else {
elements := make([]Int8range, len(value))
for i := range value {
if err := elements[i].Set(value[i]); err != nil {
return err
}
}
*dst = Int8multirange{
Ranges: elements,
Status: Present,
}
}
default:
return fmt.Errorf("cannot convert %v to Int8multirange", src)
}
return nil
}
func (dst Int8multirange) Get() interface{} {
switch dst.Status {
case Present:
return dst
case Null:
return nil
default:
return dst.Status
}
}
func (src *Int8multirange) AssignTo(dst interface{}) error {
return fmt.Errorf("cannot assign %v to %T", src, dst)
}
func (dst *Int8multirange) DecodeText(ci *ConnInfo, src []byte) error {
if src == nil {
*dst = Int8multirange{Status: Null}
return nil
}
utmr, err := ParseUntypedTextMultirange(string(src))
if err != nil {
return err
}
var elements []Int8range
if len(utmr.Elements) > 0 {
elements = make([]Int8range, len(utmr.Elements))
for i, s := range utmr.Elements {
var elem Int8range
elemSrc := []byte(s)
err = elem.DecodeText(ci, elemSrc)
if err != nil {
return err
}
elements[i] = elem
}
}
*dst = Int8multirange{Ranges: elements, Status: Present}
return nil
}
func (dst *Int8multirange) DecodeBinary(ci *ConnInfo, src []byte) error {
if src == nil {
*dst = Int8multirange{Status: Null}
return nil
}
rp := 0
numElems := int(binary.BigEndian.Uint32(src[rp:]))
rp += 4
if numElems == 0 {
*dst = Int8multirange{Status: Present}
return nil
}
elements := make([]Int8range, numElems)
for i := range elements {
elemLen := int(int32(binary.BigEndian.Uint32(src[rp:])))
rp += 4
var elemSrc []byte
if elemLen >= 0 {
elemSrc = src[rp : rp+elemLen]
rp += elemLen
}
err := elements[i].DecodeBinary(ci, elemSrc)
if err != nil {
return err
}
}
*dst = Int8multirange{Ranges: elements, Status: Present}
return nil
}
func (src Int8multirange) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
switch src.Status {
case Null:
return nil, nil
case Undefined:
return nil, errUndefined
}
buf = append(buf, '{')
inElemBuf := make([]byte, 0, 32)
for i, elem := range src.Ranges {
if i > 0 {
buf = append(buf, ',')
}
elemBuf, err := elem.EncodeText(ci, inElemBuf)
if err != nil {
return nil, err
}
if elemBuf == nil {
return nil, fmt.Errorf("multi-range does not allow null range")
} else {
buf = append(buf, string(elemBuf)...)
}
}
buf = append(buf, '}')
return buf, nil
}
func (src Int8multirange) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
switch src.Status {
case Null:
return nil, nil
case Undefined:
return nil, errUndefined
}
buf = pgio.AppendInt32(buf, int32(len(src.Ranges)))
for i := range src.Ranges {
sp := len(buf)
buf = pgio.AppendInt32(buf, -1)
elemBuf, err := src.Ranges[i].EncodeBinary(ci, buf)
if err != nil {
return nil, err
}
if elemBuf != nil {
buf = elemBuf
pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4))
}
}
return buf, nil
}
// Scan implements the database/sql Scanner interface.
func (dst *Int8multirange) Scan(src interface{}) error {
if src == nil {
return dst.DecodeText(nil, nil)
}
switch src := src.(type) {
case string:
return dst.DecodeText(nil, []byte(src))
case []byte:
srcCopy := make([]byte, len(src))
copy(srcCopy, src)
return dst.DecodeText(nil, srcCopy)
}
return fmt.Errorf("cannot scan %T", src)
}
// Value implements the database/sql/driver Valuer interface.
func (src Int8multirange) Value() (driver.Value, error) {
return EncodeValueText(src)
}

View file

@ -5,6 +5,7 @@ package pgtype
import (
"database/sql/driver"
"encoding/binary"
"encoding/json"
"fmt"
"reflect"
@ -72,6 +73,25 @@ func (dst *JSONBArray) Set(src interface{}) error {
}
}
case []json.RawMessage:
if value == nil {
*dst = JSONBArray{Status: Null}
} else if len(value) == 0 {
*dst = JSONBArray{Status: Present}
} else {
elements := make([]JSONB, len(value))
for i := range value {
if err := elements[i].Set(value[i]); err != nil {
return err
}
}
*dst = JSONBArray{
Elements: elements,
Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}},
Status: Present,
}
}
case []JSONB:
if value == nil {
*dst = JSONBArray{Status: Null}
@ -214,6 +234,15 @@ func (src *JSONBArray) AssignTo(dst interface{}) error {
}
return nil
case *[]json.RawMessage:
*v = make([]json.RawMessage, len(src.Elements))
for i := range src.Elements {
if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil {
return err
}
}
return nil
}
}

83
vendor/github.com/jackc/pgtype/multirange.go generated vendored Normal file
View file

@ -0,0 +1,83 @@
package pgtype
import (
"bytes"
"fmt"
)
type UntypedTextMultirange struct {
Elements []string
}
func ParseUntypedTextMultirange(src string) (*UntypedTextMultirange, error) {
utmr := &UntypedTextMultirange{}
utmr.Elements = make([]string, 0)
buf := bytes.NewBufferString(src)
skipWhitespace(buf)
r, _, err := buf.ReadRune()
if err != nil {
return nil, fmt.Errorf("invalid array: %v", err)
}
if r != '{' {
return nil, fmt.Errorf("invalid multirange, expected '{': %v", err)
}
parseValueLoop:
for {
r, _, err = buf.ReadRune()
if err != nil {
return nil, fmt.Errorf("invalid multirange: %v", err)
}
switch r {
case ',': // skip range separator
case '}':
break parseValueLoop
default:
buf.UnreadRune()
value, err := parseRange(buf)
if err != nil {
return nil, fmt.Errorf("invalid multirange value: %v", err)
}
utmr.Elements = append(utmr.Elements, value)
}
}
skipWhitespace(buf)
if buf.Len() > 0 {
return nil, fmt.Errorf("unexpected trailing data: %v", buf.String())
}
return utmr, nil
}
func parseRange(buf *bytes.Buffer) (string, error) {
s := &bytes.Buffer{}
boundSepRead := false
for {
r, _, err := buf.ReadRune()
if err != nil {
return "", err
}
switch r {
case ',', '}':
if r == ',' && !boundSepRead {
boundSepRead = true
break
}
buf.UnreadRune()
return s.String(), nil
}
s.WriteRune(r)
}
}

239
vendor/github.com/jackc/pgtype/num_multirange.go generated vendored Normal file
View file

@ -0,0 +1,239 @@
package pgtype
import (
"database/sql/driver"
"encoding/binary"
"fmt"
"github.com/jackc/pgio"
)
type Nummultirange struct {
Ranges []Numrange
Status Status
}
func (dst *Nummultirange) Set(src interface{}) error {
//untyped nil and typed nil interfaces are different
if src == nil {
*dst = Nummultirange{Status: Null}
return nil
}
switch value := src.(type) {
case Nummultirange:
*dst = value
case *Nummultirange:
*dst = *value
case string:
return dst.DecodeText(nil, []byte(value))
case []Numrange:
if value == nil {
*dst = Nummultirange{Status: Null}
} else if len(value) == 0 {
*dst = Nummultirange{Status: Present}
} else {
elements := make([]Numrange, len(value))
for i := range value {
if err := elements[i].Set(value[i]); err != nil {
return err
}
}
*dst = Nummultirange{
Ranges: elements,
Status: Present,
}
}
case []*Numrange:
if value == nil {
*dst = Nummultirange{Status: Null}
} else if len(value) == 0 {
*dst = Nummultirange{Status: Present}
} else {
elements := make([]Numrange, len(value))
for i := range value {
if err := elements[i].Set(value[i]); err != nil {
return err
}
}
*dst = Nummultirange{
Ranges: elements,
Status: Present,
}
}
default:
return fmt.Errorf("cannot convert %v to Nummultirange", src)
}
return nil
}
func (dst Nummultirange) Get() interface{} {
switch dst.Status {
case Present:
return dst
case Null:
return nil
default:
return dst.Status
}
}
func (src *Nummultirange) AssignTo(dst interface{}) error {
return fmt.Errorf("cannot assign %v to %T", src, dst)
}
func (dst *Nummultirange) DecodeText(ci *ConnInfo, src []byte) error {
if src == nil {
*dst = Nummultirange{Status: Null}
return nil
}
utmr, err := ParseUntypedTextMultirange(string(src))
if err != nil {
return err
}
var elements []Numrange
if len(utmr.Elements) > 0 {
elements = make([]Numrange, len(utmr.Elements))
for i, s := range utmr.Elements {
var elem Numrange
elemSrc := []byte(s)
err = elem.DecodeText(ci, elemSrc)
if err != nil {
return err
}
elements[i] = elem
}
}
*dst = Nummultirange{Ranges: elements, Status: Present}
return nil
}
func (dst *Nummultirange) DecodeBinary(ci *ConnInfo, src []byte) error {
if src == nil {
*dst = Nummultirange{Status: Null}
return nil
}
rp := 0
numElems := int(binary.BigEndian.Uint32(src[rp:]))
rp += 4
if numElems == 0 {
*dst = Nummultirange{Status: Present}
return nil
}
elements := make([]Numrange, numElems)
for i := range elements {
elemLen := int(int32(binary.BigEndian.Uint32(src[rp:])))
rp += 4
var elemSrc []byte
if elemLen >= 0 {
elemSrc = src[rp : rp+elemLen]
rp += elemLen
}
err := elements[i].DecodeBinary(ci, elemSrc)
if err != nil {
return err
}
}
*dst = Nummultirange{Ranges: elements, Status: Present}
return nil
}
func (src Nummultirange) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
switch src.Status {
case Null:
return nil, nil
case Undefined:
return nil, errUndefined
}
buf = append(buf, '{')
inElemBuf := make([]byte, 0, 32)
for i, elem := range src.Ranges {
if i > 0 {
buf = append(buf, ',')
}
elemBuf, err := elem.EncodeText(ci, inElemBuf)
if err != nil {
return nil, err
}
if elemBuf == nil {
return nil, fmt.Errorf("multi-range does not allow null range")
} else {
buf = append(buf, string(elemBuf)...)
}
}
buf = append(buf, '}')
return buf, nil
}
func (src Nummultirange) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
switch src.Status {
case Null:
return nil, nil
case Undefined:
return nil, errUndefined
}
buf = pgio.AppendInt32(buf, int32(len(src.Ranges)))
for i := range src.Ranges {
sp := len(buf)
buf = pgio.AppendInt32(buf, -1)
elemBuf, err := src.Ranges[i].EncodeBinary(ci, buf)
if err != nil {
return nil, err
}
if elemBuf != nil {
buf = elemBuf
pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4))
}
}
return buf, nil
}
// Scan implements the database/sql Scanner interface.
func (dst *Nummultirange) Scan(src interface{}) error {
if src == nil {
return dst.DecodeText(nil, nil)
}
switch src := src.(type) {
case string:
return dst.DecodeText(nil, []byte(src))
case []byte:
srcCopy := make([]byte, len(src))
copy(srcCopy, src)
return dst.DecodeText(nil, srcCopy)
}
return fmt.Errorf("cannot scan %T", src)
}
// Value implements the database/sql/driver Valuer interface.
func (src Nummultirange) Value() (driver.Value, error) {
return EncodeValueText(src)
}

View file

@ -74,12 +74,15 @@ const (
JSONBArrayOID = 3807
DaterangeOID = 3912
Int4rangeOID = 3904
Int4multirangeOID = 4451
NumrangeOID = 3906
NummultirangeOID = 4532
TsrangeOID = 3908
TsrangeArrayOID = 3909
TstzrangeOID = 3910
TstzrangeArrayOID = 3911
Int8rangeOID = 3926
Int8multirangeOID = 4536
)
type Status byte
@ -288,8 +291,10 @@ func NewConnInfo() *ConnInfo {
ci.RegisterDataType(DataType{Value: &Int2{}, Name: "int2", OID: Int2OID})
ci.RegisterDataType(DataType{Value: &Int4{}, Name: "int4", OID: Int4OID})
ci.RegisterDataType(DataType{Value: &Int4range{}, Name: "int4range", OID: Int4rangeOID})
ci.RegisterDataType(DataType{Value: &Int4multirange{}, Name: "int4multirange", OID: Int4multirangeOID})
ci.RegisterDataType(DataType{Value: &Int8{}, Name: "int8", OID: Int8OID})
ci.RegisterDataType(DataType{Value: &Int8range{}, Name: "int8range", OID: Int8rangeOID})
ci.RegisterDataType(DataType{Value: &Int8multirange{}, Name: "int8multirange", OID: Int8multirangeOID})
ci.RegisterDataType(DataType{Value: &Interval{}, Name: "interval", OID: IntervalOID})
ci.RegisterDataType(DataType{Value: &JSON{}, Name: "json", OID: JSONOID})
ci.RegisterDataType(DataType{Value: &JSONB{}, Name: "jsonb", OID: JSONBOID})
@ -300,6 +305,7 @@ func NewConnInfo() *ConnInfo {
ci.RegisterDataType(DataType{Value: &Name{}, Name: "name", OID: NameOID})
ci.RegisterDataType(DataType{Value: &Numeric{}, Name: "numeric", OID: NumericOID})
ci.RegisterDataType(DataType{Value: &Numrange{}, Name: "numrange", OID: NumrangeOID})
ci.RegisterDataType(DataType{Value: &Nummultirange{}, Name: "nummultirange", OID: NummultirangeOID})
ci.RegisterDataType(DataType{Value: &OIDValue{}, Name: "oid", OID: OIDOID})
ci.RegisterDataType(DataType{Value: &Path{}, Name: "path", OID: PathOID})
ci.RegisterDataType(DataType{Value: &Point{}, Name: "point", OID: PointOID})
@ -873,72 +879,75 @@ var nameValues map[string]Value
func init() {
nameValues = map[string]Value{
"_aclitem": &ACLItemArray{},
"_bool": &BoolArray{},
"_bpchar": &BPCharArray{},
"_bytea": &ByteaArray{},
"_cidr": &CIDRArray{},
"_date": &DateArray{},
"_float4": &Float4Array{},
"_float8": &Float8Array{},
"_inet": &InetArray{},
"_int2": &Int2Array{},
"_int4": &Int4Array{},
"_int8": &Int8Array{},
"_numeric": &NumericArray{},
"_text": &TextArray{},
"_timestamp": &TimestampArray{},
"_timestamptz": &TimestamptzArray{},
"_uuid": &UUIDArray{},
"_varchar": &VarcharArray{},
"_jsonb": &JSONBArray{},
"aclitem": &ACLItem{},
"bit": &Bit{},
"bool": &Bool{},
"box": &Box{},
"bpchar": &BPChar{},
"bytea": &Bytea{},
"char": &QChar{},
"cid": &CID{},
"cidr": &CIDR{},
"circle": &Circle{},
"date": &Date{},
"daterange": &Daterange{},
"float4": &Float4{},
"float8": &Float8{},
"hstore": &Hstore{},
"inet": &Inet{},
"int2": &Int2{},
"int4": &Int4{},
"int4range": &Int4range{},
"int8": &Int8{},
"int8range": &Int8range{},
"interval": &Interval{},
"json": &JSON{},
"jsonb": &JSONB{},
"line": &Line{},
"lseg": &Lseg{},
"macaddr": &Macaddr{},
"name": &Name{},
"numeric": &Numeric{},
"numrange": &Numrange{},
"oid": &OIDValue{},
"path": &Path{},
"point": &Point{},
"polygon": &Polygon{},
"record": &Record{},
"text": &Text{},
"tid": &TID{},
"timestamp": &Timestamp{},
"timestamptz": &Timestamptz{},
"tsrange": &Tsrange{},
"_tsrange": &TsrangeArray{},
"tstzrange": &Tstzrange{},
"_tstzrange": &TstzrangeArray{},
"unknown": &Unknown{},
"uuid": &UUID{},
"varbit": &Varbit{},
"varchar": &Varchar{},
"xid": &XID{},
"_aclitem": &ACLItemArray{},
"_bool": &BoolArray{},
"_bpchar": &BPCharArray{},
"_bytea": &ByteaArray{},
"_cidr": &CIDRArray{},
"_date": &DateArray{},
"_float4": &Float4Array{},
"_float8": &Float8Array{},
"_inet": &InetArray{},
"_int2": &Int2Array{},
"_int4": &Int4Array{},
"_int8": &Int8Array{},
"_numeric": &NumericArray{},
"_text": &TextArray{},
"_timestamp": &TimestampArray{},
"_timestamptz": &TimestamptzArray{},
"_uuid": &UUIDArray{},
"_varchar": &VarcharArray{},
"_jsonb": &JSONBArray{},
"aclitem": &ACLItem{},
"bit": &Bit{},
"bool": &Bool{},
"box": &Box{},
"bpchar": &BPChar{},
"bytea": &Bytea{},
"char": &QChar{},
"cid": &CID{},
"cidr": &CIDR{},
"circle": &Circle{},
"date": &Date{},
"daterange": &Daterange{},
"float4": &Float4{},
"float8": &Float8{},
"hstore": &Hstore{},
"inet": &Inet{},
"int2": &Int2{},
"int4": &Int4{},
"int4range": &Int4range{},
"int4multirange": &Int4multirange{},
"int8": &Int8{},
"int8range": &Int8range{},
"int8multirange": &Int8multirange{},
"interval": &Interval{},
"json": &JSON{},
"jsonb": &JSONB{},
"line": &Line{},
"lseg": &Lseg{},
"macaddr": &Macaddr{},
"name": &Name{},
"numeric": &Numeric{},
"numrange": &Numrange{},
"nummultirange": &Nummultirange{},
"oid": &OIDValue{},
"path": &Path{},
"point": &Point{},
"polygon": &Polygon{},
"record": &Record{},
"text": &Text{},
"tid": &TID{},
"timestamp": &Timestamp{},
"timestamptz": &Timestamptz{},
"tsrange": &Tsrange{},
"_tsrange": &TsrangeArray{},
"tstzrange": &Tstzrange{},
"_tstzrange": &TstzrangeArray{},
"unknown": &Unknown{},
"uuid": &UUID{},
"varbit": &Varbit{},
"varchar": &Varchar{},
"xid": &XID{},
}
}

View file

@ -6,7 +6,7 @@ import (
)
// Record is the generic PostgreSQL record type such as is created with the
// "row" function. Record only implements BinaryEncoder and Value. The text
// "row" function. Record only implements BinaryDecoder and Value. The text
// format output format from PostgreSQL does not include type information and is
// therefore impossible to decode. No encoders are implemented because
// PostgreSQL does not support input of generic records.

318
vendor/github.com/jackc/pgtype/record_array.go generated vendored Normal file
View file

@ -0,0 +1,318 @@
// Code generated by erb. DO NOT EDIT.
package pgtype
import (
"encoding/binary"
"fmt"
"reflect"
)
type RecordArray struct {
Elements []Record
Dimensions []ArrayDimension
Status Status
}
func (dst *RecordArray) Set(src interface{}) error {
// untyped nil and typed nil interfaces are different
if src == nil {
*dst = RecordArray{Status: Null}
return nil
}
if value, ok := src.(interface{ Get() interface{} }); ok {
value2 := value.Get()
if value2 != value {
return dst.Set(value2)
}
}
// Attempt to match to select common types:
switch value := src.(type) {
case [][]Value:
if value == nil {
*dst = RecordArray{Status: Null}
} else if len(value) == 0 {
*dst = RecordArray{Status: Present}
} else {
elements := make([]Record, len(value))
for i := range value {
if err := elements[i].Set(value[i]); err != nil {
return err
}
}
*dst = RecordArray{
Elements: elements,
Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}},
Status: Present,
}
}
case []Record:
if value == nil {
*dst = RecordArray{Status: Null}
} else if len(value) == 0 {
*dst = RecordArray{Status: Present}
} else {
*dst = RecordArray{
Elements: value,
Dimensions: []ArrayDimension{{Length: int32(len(value)), LowerBound: 1}},
Status: Present,
}
}
default:
// Fallback to reflection if an optimised match was not found.
// The reflection is necessary for arrays and multidimensional slices,
// but it comes with a 20-50% performance penalty for large arrays/slices
reflectedValue := reflect.ValueOf(src)
if !reflectedValue.IsValid() || reflectedValue.IsZero() {
*dst = RecordArray{Status: Null}
return nil
}
dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0)
if !ok {
return fmt.Errorf("cannot find dimensions of %v for RecordArray", src)
}
if elementsLength == 0 {
*dst = RecordArray{Status: Present}
return nil
}
if len(dimensions) == 0 {
if originalSrc, ok := underlyingSliceType(src); ok {
return dst.Set(originalSrc)
}
return fmt.Errorf("cannot convert %v to RecordArray", src)
}
*dst = RecordArray{
Elements: make([]Record, elementsLength),
Dimensions: dimensions,
Status: Present,
}
elementCount, err := dst.setRecursive(reflectedValue, 0, 0)
if err != nil {
// Maybe the target was one dimension too far, try again:
if len(dst.Dimensions) > 1 {
dst.Dimensions = dst.Dimensions[:len(dst.Dimensions)-1]
elementsLength = 0
for _, dim := range dst.Dimensions {
if elementsLength == 0 {
elementsLength = int(dim.Length)
} else {
elementsLength *= int(dim.Length)
}
}
dst.Elements = make([]Record, elementsLength)
elementCount, err = dst.setRecursive(reflectedValue, 0, 0)
if err != nil {
return err
}
} else {
return err
}
}
if elementCount != len(dst.Elements) {
return fmt.Errorf("cannot convert %v to RecordArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount)
}
}
return nil
}
func (dst *RecordArray) setRecursive(value reflect.Value, index, dimension int) (int, error) {
switch value.Kind() {
case reflect.Array:
fallthrough
case reflect.Slice:
if len(dst.Dimensions) == dimension {
break
}
valueLen := value.Len()
if int32(valueLen) != dst.Dimensions[dimension].Length {
return 0, fmt.Errorf("multidimensional arrays must have array expressions with matching dimensions")
}
for i := 0; i < valueLen; i++ {
var err error
index, err = dst.setRecursive(value.Index(i), index, dimension+1)
if err != nil {
return 0, err
}
}
return index, nil
}
if !value.CanInterface() {
return 0, fmt.Errorf("cannot convert all values to RecordArray")
}
if err := dst.Elements[index].Set(value.Interface()); err != nil {
return 0, fmt.Errorf("%v in RecordArray", err)
}
index++
return index, nil
}
func (dst RecordArray) Get() interface{} {
switch dst.Status {
case Present:
return dst
case Null:
return nil
default:
return dst.Status
}
}
func (src *RecordArray) AssignTo(dst interface{}) error {
switch src.Status {
case Present:
if len(src.Dimensions) <= 1 {
// Attempt to match to select common types:
switch v := dst.(type) {
case *[][]Value:
*v = make([][]Value, len(src.Elements))
for i := range src.Elements {
if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil {
return err
}
}
return nil
}
}
// Try to convert to something AssignTo can use directly.
if nextDst, retry := GetAssignToDstType(dst); retry {
return src.AssignTo(nextDst)
}
// Fallback to reflection if an optimised match was not found.
// The reflection is necessary for arrays and multidimensional slices,
// but it comes with a 20-50% performance penalty for large arrays/slices
value := reflect.ValueOf(dst)
if value.Kind() == reflect.Ptr {
value = value.Elem()
}
switch value.Kind() {
case reflect.Array, reflect.Slice:
default:
return fmt.Errorf("cannot assign %T to %T", src, dst)
}
if len(src.Elements) == 0 {
if value.Kind() == reflect.Slice {
value.Set(reflect.MakeSlice(value.Type(), 0, 0))
return nil
}
}
elementCount, err := src.assignToRecursive(value, 0, 0)
if err != nil {
return err
}
if elementCount != len(src.Elements) {
return fmt.Errorf("cannot assign %v, needed to assign %d elements, but only assigned %d", dst, len(src.Elements), elementCount)
}
return nil
case Null:
return NullAssignTo(dst)
}
return fmt.Errorf("cannot decode %#v into %T", src, dst)
}
func (src *RecordArray) assignToRecursive(value reflect.Value, index, dimension int) (int, error) {
switch kind := value.Kind(); kind {
case reflect.Array:
fallthrough
case reflect.Slice:
if len(src.Dimensions) == dimension {
break
}
length := int(src.Dimensions[dimension].Length)
if reflect.Array == kind {
typ := value.Type()
if typ.Len() != length {
return 0, fmt.Errorf("expected size %d array, but %s has size %d array", length, typ, typ.Len())
}
value.Set(reflect.New(typ).Elem())
} else {
value.Set(reflect.MakeSlice(value.Type(), length, length))
}
var err error
for i := 0; i < length; i++ {
index, err = src.assignToRecursive(value.Index(i), index, dimension+1)
if err != nil {
return 0, err
}
}
return index, nil
}
if len(src.Dimensions) != dimension {
return 0, fmt.Errorf("incorrect dimensions, expected %d, found %d", len(src.Dimensions), dimension)
}
if !value.CanAddr() {
return 0, fmt.Errorf("cannot assign all values from RecordArray")
}
addr := value.Addr()
if !addr.CanInterface() {
return 0, fmt.Errorf("cannot assign all values from RecordArray")
}
if err := src.Elements[index].AssignTo(addr.Interface()); err != nil {
return 0, err
}
index++
return index, nil
}
func (dst *RecordArray) DecodeBinary(ci *ConnInfo, src []byte) error {
if src == nil {
*dst = RecordArray{Status: Null}
return nil
}
var arrayHeader ArrayHeader
rp, err := arrayHeader.DecodeBinary(ci, src)
if err != nil {
return err
}
if len(arrayHeader.Dimensions) == 0 {
*dst = RecordArray{Dimensions: arrayHeader.Dimensions, Status: Present}
return nil
}
elementCount := arrayHeader.Dimensions[0].Length
for _, d := range arrayHeader.Dimensions[1:] {
elementCount *= d.Length
}
elements := make([]Record, elementCount)
for i := range elements {
elemLen := int(int32(binary.BigEndian.Uint32(src[rp:])))
rp += 4
var elemSrc []byte
if elemLen >= 0 {
elemSrc = src[rp : rp+elemLen]
rp += elemLen
}
err = elements[i].DecodeBinary(ci, elemSrc)
if err != nil {
return err
}
}
*dst = RecordArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present}
return nil
}

View file

@ -1,5 +1,17 @@
// Code generated by erb. DO NOT EDIT.
<%
# defaults when not explicitly set on command line
binary_format ||= "true"
text_format ||= "true"
text_null ||= "NULL"
encode_binary ||= binary_format
decode_binary ||= binary_format
%>
package pgtype
import (
@ -279,6 +291,7 @@ func (src *<%= pgtype_array_type %>) assignToRecursive(value reflect.Value, inde
return index, nil
}
<% if text_format == "true" %>
func (dst *<%= pgtype_array_type %>) DecodeText(ci *ConnInfo, src []byte) error {
if src == nil {
*dst = <%= pgtype_array_type %>{Status: Null}
@ -314,8 +327,9 @@ func (dst *<%= pgtype_array_type %>) DecodeText(ci *ConnInfo, src []byte) error
return nil
}
<% end %>
<% if binary_format == "true" %>
<% if decode_binary == "true" %>
func (dst *<%= pgtype_array_type %>) DecodeBinary(ci *ConnInfo, src []byte) error {
if src == nil {
*dst = <%= pgtype_array_type %>{Status: Null}
@ -359,6 +373,7 @@ func (dst *<%= pgtype_array_type %>) DecodeBinary(ci *ConnInfo, src []byte) erro
}
<% end %>
<% if text_format == "true" %>
func (src <%= pgtype_array_type %>) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
switch src.Status {
case Null:
@ -415,8 +430,9 @@ func (src <%= pgtype_array_type %>) EncodeText(ci *ConnInfo, buf []byte) ([]byte
return buf, nil
}
<% end %>
<% if binary_format == "true" %>
<% if encode_binary == "true" %>
func (src <%= pgtype_array_type %>) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
switch src.Status {
case Null:
@ -462,6 +478,7 @@ func (src <%= pgtype_array_type %>) EncodeText(ci *ConnInfo, buf []byte) ([]byte
}
<% end %>
<% if text_format == "true" %>
// Scan implements the database/sql Scanner interface.
func (dst *<%= pgtype_array_type %>) Scan(src interface{}) error {
if src == nil {
@ -492,3 +509,4 @@ func (src <%= pgtype_array_type %>) Value() (driver.Value, error) {
return string(buf), nil
}
<% end %>

View file

@ -1,28 +1,30 @@
erb pgtype_array_type=Int2Array pgtype_element_type=Int2 go_array_types=[]int16,[]*int16,[]uint16,[]*uint16,[]int32,[]*int32,[]uint32,[]*uint32,[]int64,[]*int64,[]uint64,[]*uint64,[]int,[]*int,[]uint,[]*uint element_type_name=int2 text_null=NULL binary_format=true typed_array.go.erb > int2_array.go
erb pgtype_array_type=Int4Array pgtype_element_type=Int4 go_array_types=[]int16,[]*int16,[]uint16,[]*uint16,[]int32,[]*int32,[]uint32,[]*uint32,[]int64,[]*int64,[]uint64,[]*uint64,[]int,[]*int,[]uint,[]*uint element_type_name=int4 text_null=NULL binary_format=true typed_array.go.erb > int4_array.go
erb pgtype_array_type=Int8Array pgtype_element_type=Int8 go_array_types=[]int16,[]*int16,[]uint16,[]*uint16,[]int32,[]*int32,[]uint32,[]*uint32,[]int64,[]*int64,[]uint64,[]*uint64,[]int,[]*int,[]uint,[]*uint element_type_name=int8 text_null=NULL binary_format=true typed_array.go.erb > int8_array.go
erb pgtype_array_type=BoolArray pgtype_element_type=Bool go_array_types=[]bool,[]*bool element_type_name=bool text_null=NULL binary_format=true typed_array.go.erb > bool_array.go
erb pgtype_array_type=DateArray pgtype_element_type=Date go_array_types=[]time.Time,[]*time.Time element_type_name=date text_null=NULL binary_format=true typed_array.go.erb > date_array.go
erb pgtype_array_type=TimestamptzArray pgtype_element_type=Timestamptz go_array_types=[]time.Time,[]*time.Time element_type_name=timestamptz text_null=NULL binary_format=true typed_array.go.erb > timestamptz_array.go
erb pgtype_array_type=TstzrangeArray pgtype_element_type=Tstzrange go_array_types=[]Tstzrange element_type_name=tstzrange text_null=NULL binary_format=true typed_array.go.erb > tstzrange_array.go
erb pgtype_array_type=TsrangeArray pgtype_element_type=Tsrange go_array_types=[]Tsrange element_type_name=tsrange text_null=NULL binary_format=true typed_array.go.erb > tsrange_array.go
erb pgtype_array_type=TimestampArray pgtype_element_type=Timestamp go_array_types=[]time.Time,[]*time.Time element_type_name=timestamp text_null=NULL binary_format=true typed_array.go.erb > timestamp_array.go
erb pgtype_array_type=Float4Array pgtype_element_type=Float4 go_array_types=[]float32,[]*float32 element_type_name=float4 text_null=NULL binary_format=true typed_array.go.erb > float4_array.go
erb pgtype_array_type=Float8Array pgtype_element_type=Float8 go_array_types=[]float64,[]*float64 element_type_name=float8 text_null=NULL binary_format=true typed_array.go.erb > float8_array.go
erb pgtype_array_type=InetArray pgtype_element_type=Inet go_array_types=[]*net.IPNet,[]net.IP,[]*net.IP element_type_name=inet text_null=NULL binary_format=true typed_array.go.erb > inet_array.go
erb pgtype_array_type=MacaddrArray pgtype_element_type=Macaddr go_array_types=[]net.HardwareAddr,[]*net.HardwareAddr element_type_name=macaddr text_null=NULL binary_format=true typed_array.go.erb > macaddr_array.go
erb pgtype_array_type=CIDRArray pgtype_element_type=CIDR go_array_types=[]*net.IPNet,[]net.IP,[]*net.IP element_type_name=cidr text_null=NULL binary_format=true typed_array.go.erb > cidr_array.go
erb pgtype_array_type=TextArray pgtype_element_type=Text go_array_types=[]string,[]*string element_type_name=text text_null=NULL binary_format=true typed_array.go.erb > text_array.go
erb pgtype_array_type=VarcharArray pgtype_element_type=Varchar go_array_types=[]string,[]*string element_type_name=varchar text_null=NULL binary_format=true typed_array.go.erb > varchar_array.go
erb pgtype_array_type=BPCharArray pgtype_element_type=BPChar go_array_types=[]string,[]*string element_type_name=bpchar text_null=NULL binary_format=true typed_array.go.erb > bpchar_array.go
erb pgtype_array_type=ByteaArray pgtype_element_type=Bytea go_array_types=[][]byte element_type_name=bytea text_null=NULL binary_format=true typed_array.go.erb > bytea_array.go
erb pgtype_array_type=ACLItemArray pgtype_element_type=ACLItem go_array_types=[]string,[]*string element_type_name=aclitem text_null=NULL binary_format=false typed_array.go.erb > aclitem_array.go
erb pgtype_array_type=HstoreArray pgtype_element_type=Hstore go_array_types=[]map[string]string element_type_name=hstore text_null=NULL binary_format=true typed_array.go.erb > hstore_array.go
erb pgtype_array_type=NumericArray pgtype_element_type=Numeric go_array_types=[]float32,[]*float32,[]float64,[]*float64,[]int64,[]*int64,[]uint64,[]*uint64 element_type_name=numeric text_null=NULL binary_format=true typed_array.go.erb > numeric_array.go
erb pgtype_array_type=UUIDArray pgtype_element_type=UUID go_array_types=[][16]byte,[][]byte,[]string,[]*string element_type_name=uuid text_null=NULL binary_format=true typed_array.go.erb > uuid_array.go
erb pgtype_array_type=JSONBArray pgtype_element_type=JSONB go_array_types=[]string,[][]byte element_type_name=jsonb text_null=NULL binary_format=true typed_array.go.erb > jsonb_array.go
erb pgtype_array_type=Int2Array pgtype_element_type=Int2 go_array_types=[]int16,[]*int16,[]uint16,[]*uint16,[]int32,[]*int32,[]uint32,[]*uint32,[]int64,[]*int64,[]uint64,[]*uint64,[]int,[]*int,[]uint,[]*uint element_type_name=int2 typed_array.go.erb > int2_array.go
erb pgtype_array_type=Int4Array pgtype_element_type=Int4 go_array_types=[]int16,[]*int16,[]uint16,[]*uint16,[]int32,[]*int32,[]uint32,[]*uint32,[]int64,[]*int64,[]uint64,[]*uint64,[]int,[]*int,[]uint,[]*uint element_type_name=int4 typed_array.go.erb > int4_array.go
erb pgtype_array_type=Int8Array pgtype_element_type=Int8 go_array_types=[]int16,[]*int16,[]uint16,[]*uint16,[]int32,[]*int32,[]uint32,[]*uint32,[]int64,[]*int64,[]uint64,[]*uint64,[]int,[]*int,[]uint,[]*uint element_type_name=int8 typed_array.go.erb > int8_array.go
erb pgtype_array_type=BoolArray pgtype_element_type=Bool go_array_types=[]bool,[]*bool element_type_name=bool typed_array.go.erb > bool_array.go
erb pgtype_array_type=DateArray pgtype_element_type=Date go_array_types=[]time.Time,[]*time.Time element_type_name=date typed_array.go.erb > date_array.go
erb pgtype_array_type=TimestamptzArray pgtype_element_type=Timestamptz go_array_types=[]time.Time,[]*time.Time element_type_name=timestamptz typed_array.go.erb > timestamptz_array.go
erb pgtype_array_type=TstzrangeArray pgtype_element_type=Tstzrange go_array_types=[]Tstzrange element_type_name=tstzrange typed_array.go.erb > tstzrange_array.go
erb pgtype_array_type=TsrangeArray pgtype_element_type=Tsrange go_array_types=[]Tsrange element_type_name=tsrange typed_array.go.erb > tsrange_array.go
erb pgtype_array_type=TimestampArray pgtype_element_type=Timestamp go_array_types=[]time.Time,[]*time.Time element_type_name=timestamp typed_array.go.erb > timestamp_array.go
erb pgtype_array_type=Float4Array pgtype_element_type=Float4 go_array_types=[]float32,[]*float32 element_type_name=float4 typed_array.go.erb > float4_array.go
erb pgtype_array_type=Float8Array pgtype_element_type=Float8 go_array_types=[]float64,[]*float64 element_type_name=float8 typed_array.go.erb > float8_array.go
erb pgtype_array_type=InetArray pgtype_element_type=Inet go_array_types=[]*net.IPNet,[]net.IP,[]*net.IP element_type_name=inet typed_array.go.erb > inet_array.go
erb pgtype_array_type=MacaddrArray pgtype_element_type=Macaddr go_array_types=[]net.HardwareAddr,[]*net.HardwareAddr element_type_name=macaddr typed_array.go.erb > macaddr_array.go
erb pgtype_array_type=CIDRArray pgtype_element_type=CIDR go_array_types=[]*net.IPNet,[]net.IP,[]*net.IP element_type_name=cidr typed_array.go.erb > cidr_array.go
erb pgtype_array_type=TextArray pgtype_element_type=Text go_array_types=[]string,[]*string element_type_name=text typed_array.go.erb > text_array.go
erb pgtype_array_type=VarcharArray pgtype_element_type=Varchar go_array_types=[]string,[]*string element_type_name=varchar typed_array.go.erb > varchar_array.go
erb pgtype_array_type=BPCharArray pgtype_element_type=BPChar go_array_types=[]string,[]*string element_type_name=bpchar typed_array.go.erb > bpchar_array.go
erb pgtype_array_type=ByteaArray pgtype_element_type=Bytea go_array_types=[][]byte element_type_name=bytea typed_array.go.erb > bytea_array.go
erb pgtype_array_type=ACLItemArray pgtype_element_type=ACLItem go_array_types=[]string,[]*string element_type_name=aclitem binary_format=false typed_array.go.erb > aclitem_array.go
erb pgtype_array_type=HstoreArray pgtype_element_type=Hstore go_array_types=[]map[string]string element_type_name=hstore typed_array.go.erb > hstore_array.go
erb pgtype_array_type=NumericArray pgtype_element_type=Numeric go_array_types=[]float32,[]*float32,[]float64,[]*float64,[]int64,[]*int64,[]uint64,[]*uint64 element_type_name=numeric typed_array.go.erb > numeric_array.go
erb pgtype_array_type=UUIDArray pgtype_element_type=UUID go_array_types=[][16]byte,[][]byte,[]string,[]*string element_type_name=uuid typed_array.go.erb > uuid_array.go
erb pgtype_array_type=JSONBArray pgtype_element_type=JSONB go_array_types=[]string,[][]byte,[]json.RawMessage element_type_name=jsonb typed_array.go.erb > jsonb_array.go
# While the binary format is theoretically possible it is only practical to use the text format.
erb pgtype_array_type=EnumArray pgtype_element_type=GenericText go_array_types=[]string,[]*string text_null=NULL binary_format=false typed_array.go.erb > enum_array.go
erb pgtype_array_type=EnumArray pgtype_element_type=GenericText go_array_types=[]string,[]*string binary_format=false typed_array.go.erb > enum_array.go
erb pgtype_array_type=RecordArray pgtype_element_type=Record go_array_types=[][]Value element_type_name=record text_null=NULL encode_binary=false text_format=false typed_array.go.erb > record_array.go
goimports -w *_array.go

239
vendor/github.com/jackc/pgtype/typed_multirange.go.erb generated vendored Normal file
View file

@ -0,0 +1,239 @@
package pgtype
import (
"database/sql/driver"
"encoding/binary"
"fmt"
"github.com/jackc/pgio"
)
type <%= multirange_type %> struct {
Ranges []<%= range_type %>
Status Status
}
func (dst *<%= multirange_type %>) Set(src interface{}) error {
//untyped nil and typed nil interfaces are different
if src == nil {
*dst = <%= multirange_type %>{Status: Null}
return nil
}
switch value := src.(type) {
case <%= multirange_type %>:
*dst = value
case *<%= multirange_type %>:
*dst = *value
case string:
return dst.DecodeText(nil, []byte(value))
case []<%= range_type %>:
if value == nil {
*dst = <%= multirange_type %>{Status: Null}
} else if len(value) == 0 {
*dst = <%= multirange_type %>{Status: Present}
} else {
elements := make([]<%= range_type %>, len(value))
for i := range value {
if err := elements[i].Set(value[i]); err != nil {
return err
}
}
*dst = <%= multirange_type %>{
Ranges: elements,
Status: Present,
}
}
case []*<%= range_type %>:
if value == nil {
*dst = <%= multirange_type %>{Status: Null}
} else if len(value) == 0 {
*dst = <%= multirange_type %>{Status: Present}
} else {
elements := make([]<%= range_type %>, len(value))
for i := range value {
if err := elements[i].Set(value[i]); err != nil {
return err
}
}
*dst = <%= multirange_type %>{
Ranges: elements,
Status: Present,
}
}
default:
return fmt.Errorf("cannot convert %v to <%= multirange_type %>", src)
}
return nil
}
func (dst <%= multirange_type %>) Get() interface{} {
switch dst.Status {
case Present:
return dst
case Null:
return nil
default:
return dst.Status
}
}
func (src *<%= multirange_type %>) AssignTo(dst interface{}) error {
return fmt.Errorf("cannot assign %v to %T", src, dst)
}
func (dst *<%= multirange_type %>) DecodeText(ci *ConnInfo, src []byte) error {
if src == nil {
*dst = <%= multirange_type %>{Status: Null}
return nil
}
utmr, err := ParseUntypedTextMultirange(string(src))
if err != nil {
return err
}
var elements []<%= range_type %>
if len(utmr.Elements) > 0 {
elements = make([]<%= range_type %>, len(utmr.Elements))
for i, s := range utmr.Elements {
var elem <%= range_type %>
elemSrc := []byte(s)
err = elem.DecodeText(ci, elemSrc)
if err != nil {
return err
}
elements[i] = elem
}
}
*dst = <%= multirange_type %>{Ranges: elements, Status: Present}
return nil
}
func (dst *<%= multirange_type %>) DecodeBinary(ci *ConnInfo, src []byte) error {
if src == nil {
*dst = <%= multirange_type %>{Status: Null}
return nil
}
rp := 0
numElems := int(binary.BigEndian.Uint32(src[rp:]))
rp += 4
if numElems == 0 {
*dst = <%= multirange_type %>{Status: Present}
return nil
}
elements := make([]<%= range_type %>, numElems)
for i := range elements {
elemLen := int(int32(binary.BigEndian.Uint32(src[rp:])))
rp += 4
var elemSrc []byte
if elemLen >= 0 {
elemSrc = src[rp : rp+elemLen]
rp += elemLen
}
err := elements[i].DecodeBinary(ci, elemSrc)
if err != nil {
return err
}
}
*dst = <%= multirange_type %>{Ranges: elements, Status: Present}
return nil
}
func (src <%= multirange_type %>) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) {
switch src.Status {
case Null:
return nil, nil
case Undefined:
return nil, errUndefined
}
buf = append(buf, '{')
inElemBuf := make([]byte, 0, 32)
for i, elem := range src.Ranges {
if i > 0 {
buf = append(buf, ',')
}
elemBuf, err := elem.EncodeText(ci, inElemBuf)
if err != nil {
return nil, err
}
if elemBuf == nil {
return nil, fmt.Errorf("multi-range does not allow null range")
} else {
buf = append(buf, string(elemBuf)...)
}
}
buf = append(buf, '}')
return buf, nil
}
func (src <%= multirange_type %>) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) {
switch src.Status {
case Null:
return nil, nil
case Undefined:
return nil, errUndefined
}
buf = pgio.AppendInt32(buf, int32(len(src.Ranges)))
for i := range src.Ranges {
sp := len(buf)
buf = pgio.AppendInt32(buf, -1)
elemBuf, err := src.Ranges[i].EncodeBinary(ci, buf)
if err != nil {
return nil, err
}
if elemBuf != nil {
buf = elemBuf
pgio.SetInt32(buf[sp:], int32(len(buf[sp:])-4))
}
}
return buf, nil
}
// Scan implements the database/sql Scanner interface.
func (dst *<%= multirange_type %>) Scan(src interface{}) error {
if src == nil {
return dst.DecodeText(nil, nil)
}
switch src := src.(type) {
case string:
return dst.DecodeText(nil, []byte(src))
case []byte:
srcCopy := make([]byte, len(src))
copy(srcCopy, src)
return dst.DecodeText(nil, srcCopy)
}
return fmt.Errorf("cannot scan %T", src)
}
// Value implements the database/sql/driver Valuer interface.
func (src <%= multirange_type %>) Value() (driver.Value, error) {
return EncodeValueText(src)
}

View file

@ -0,0 +1,8 @@
erb range_type=Numrange multirange_type=Nummultirange typed_multirange.go.erb > num_multirange.go
erb range_type=Int4range multirange_type=Int4multirange typed_multirange.go.erb > int4_multirange.go
erb range_type=Int8range multirange_type=Int8multirange typed_multirange.go.erb > int8_multirange.go
# TODO
# erb range_type=Tsrange multirange_type=Tsmultirange typed_multirange.go.erb > ts_multirange.go
# erb range_type=Tstzrange multirange_type=Tstzmultirange typed_multirange.go.erb > tstz_multirange.go
# erb range_type=Daterange multirange_type=Datemultirange typed_multirange.go.erb > date_multirange.go
goimports -w *multirange.go

View file

@ -1,3 +1,13 @@
# 4.16.0 (April 21, 2022)
* Upgrade pgconn to v1.12.0
* Upgrade pgproto3 to v2.3.0
* Upgrade pgtype to v1.11.0
* Fix: Do not panic when context cancelled while getting statement from cache.
* Fix: Less memory pinning from old Rows.
* Fix: Support '\r' line ending when sanitizing SQL comment.
* Add pluggable GSSAPI support (Oliver Tan)
# 4.15.0 (February 7, 2022)
* Upgrade to pgconn v1.11.0

View file

@ -98,26 +98,6 @@ There are three areas in particular where pgx can provide a significant performa
perform nearly 3x the number of queries per second.
3. Batched queries - Multiple queries can be batched together to minimize network round trips.
## Comparison with Alternatives
* [pq](http://godoc.org/github.com/lib/pq)
* [go-pg](https://github.com/go-pg/pg)
For prepared queries with small sets of simple data types, all drivers will have have similar performance. However, if prepared statements aren't being explicitly used, pgx can have a significant performance advantage due to automatic statement preparation.
pgx also can perform better when using PostgreSQL-specific data types or query batching. See
[go_db_bench](https://github.com/jackc/go_db_bench) for some database driver benchmarks.
### Compatibility with `database/sql`
pq is exclusively used with `database/sql`. go-pg does not use `database/sql` at all. pgx supports `database/sql` as well as
its own interface.
### Level of access, ORM
go-pg is a PostgreSQL client and ORM. It includes many features that traditionally sit above the database driver, such as ORM, struct mapping, soft deletes, schema migrations, and sharding support.
pgx is "closer to the metal" and such abstractions are beyond the scope of the pgx project, which first and foremost, aims to be a performant driver and toolkit.
## Testing
pgx tests naturally require a PostgreSQL database. It will connect to the database specified in the `PGX_TEST_DATABASE` environment
@ -201,3 +181,7 @@ pgerrcode contains constants for the PostgreSQL error codes.
### [github.com/georgysavva/scany](https://github.com/georgysavva/scany)
Library for scanning data from a database into Go structs and more.
### [https://github.com/otan/gopgkrb5](https://github.com/otan/gopgkrb5)
Adds GSSAPI / Kerberos authentication support.

View file

@ -73,9 +73,8 @@ type Conn struct {
connInfo *pgtype.ConnInfo
wbuf []byte
preallocatedRows []connRows
eqb extendedQueryBuilder
wbuf []byte
eqb extendedQueryBuilder
}
// Identifier a PostgreSQL identifier or name. Identifiers can be composed of
@ -366,30 +365,6 @@ func (c *Conn) Ping(ctx context.Context) error {
return err
}
func connInfoFromRows(rows Rows, err error) (map[string]uint32, error) {
if err != nil {
return nil, err
}
defer rows.Close()
nameOIDs := make(map[string]uint32, 256)
for rows.Next() {
var oid uint32
var name pgtype.Text
if err = rows.Scan(&oid, &name); err != nil {
return nil, err
}
nameOIDs[name.String] = oid
}
if err = rows.Err(); err != nil {
return nil, err
}
return nameOIDs, err
}
// PgConn returns the underlying *pgconn.PgConn. This is an escape hatch method that allows lower level access to the
// PostgreSQL connection than pgx exposes.
//
@ -414,7 +389,8 @@ func (c *Conn) Exec(ctx context.Context, sql string, arguments ...interface{}) (
commandTag, err := c.exec(ctx, sql, arguments...)
if err != nil {
if c.shouldLog(LogLevelError) {
c.log(ctx, LogLevelError, "Exec", map[string]interface{}{"sql": sql, "args": logQueryArgs(arguments), "err": err})
endTime := time.Now()
c.log(ctx, LogLevelError, "Exec", map[string]interface{}{"sql": sql, "args": logQueryArgs(arguments), "err": err, "time": endTime.Sub(startTime)})
}
return commandTag, err
}
@ -537,12 +513,7 @@ func (c *Conn) execPrepared(ctx context.Context, sd *pgconn.StatementDescription
}
func (c *Conn) getRows(ctx context.Context, sql string, args []interface{}) *connRows {
if len(c.preallocatedRows) == 0 {
c.preallocatedRows = make([]connRows, 64)
}
r := &c.preallocatedRows[len(c.preallocatedRows)-1]
c.preallocatedRows = c.preallocatedRows[0 : len(c.preallocatedRows)-1]
r := &connRows{}
r.ctx = ctx
r.logger = c
@ -797,8 +768,7 @@ func (c *Conn) SendBatch(ctx context.Context, b *Batch) BatchResults {
var err error
sd, err = stmtCache.Get(ctx, bi.query)
if err != nil {
// the stmtCache was prefilled from distinctUnpreparedQueries above so we are guaranteed no errors
panic("BUG: unexpected error from stmtCache")
return &batchResults{ctx: ctx, conn: c, err: err}
}
}

View file

@ -7,10 +7,10 @@ require (
github.com/cockroachdb/apd v1.1.0
github.com/go-kit/log v0.1.0
github.com/gofrs/uuid v4.0.0+incompatible
github.com/jackc/pgconn v1.11.0
github.com/jackc/pgconn v1.12.0
github.com/jackc/pgio v1.0.0
github.com/jackc/pgproto3/v2 v2.2.0
github.com/jackc/pgtype v1.10.0
github.com/jackc/pgproto3/v2 v2.3.0
github.com/jackc/pgtype v1.11.0
github.com/jackc/puddle v1.2.1
github.com/rs/zerolog v1.15.0
github.com/shopspring/decimal v1.2.0

View file

@ -36,6 +36,8 @@ github.com/jackc/pgconn v1.10.1 h1:DzdIHIjG1AxGwoEEqS+mGsURyjt4enSmqzACXvVzOT8=
github.com/jackc/pgconn v1.10.1/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
github.com/jackc/pgconn v1.11.0 h1:HiHArx4yFbwl91X3qqIHtUFoiIfLNJXCQRsnzkiwwaQ=
github.com/jackc/pgconn v1.11.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
github.com/jackc/pgconn v1.12.0 h1:/RvQ24k3TnNdfBSW0ou9EOi5jx2cX7zfE8n2nLKuiP0=
github.com/jackc/pgconn v1.12.0/go.mod h1:ZkhRC59Llhrq3oSfrikvwQ5NaxYExr6twkdkMLaKono=
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
@ -55,6 +57,8 @@ github.com/jackc/pgproto3/v2 v2.1.1 h1:7PQ/4gLoqnl87ZxL7xjO0DR5gYuviDCZxQJsUlFW1
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.2.0 h1:r7JypeP2D3onoQTCxWdTpCtJ4D+qpKr0TxvoyMhZ5ns=
github.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.3.0 h1:brH0pCGBDkBW07HWlN/oSBXrmo3WB0UvZd1pIuDcL8Y=
github.com/jackc/pgproto3/v2 v2.3.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
@ -69,6 +73,8 @@ github.com/jackc/pgtype v1.9.1 h1:MJc2s0MFS8C3ok1wQTdQxWuXQcB6+HwAm5x1CzW7mf0=
github.com/jackc/pgtype v1.9.1/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgtype v1.10.0 h1:ILnBWrRMSXGczYvmkYD6PsYyVFUNLTnIUJHHDLmqk38=
github.com/jackc/pgtype v1.10.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgtype v1.11.0 h1:u4uiGPz/1hryuXzyaBhSk6dnIyyG2683olG2OV+UUgs=
github.com/jackc/pgtype v1.11.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
@ -187,6 +193,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=

View file

@ -246,7 +246,7 @@ func oneLineCommentState(l *sqlLexer) stateFn {
case '\\':
_, width = utf8.DecodeRuneInString(l.src[l.pos:])
l.pos += width
case '\n':
case '\n', '\r':
return rawState
case utf8.RuneError:
if l.pos-l.start > 0 {

View file

@ -108,13 +108,13 @@ func (o *LargeObject) Tell() (n int64, err error) {
return n, err
}
// Trunctes the large object to size.
// Truncate the large object to size.
func (o *LargeObject) Truncate(size int64) (err error) {
_, err = o.tx.Exec(o.ctx, "select lo_truncate64($1, $2)", o.fd, size)
return err
}
// Close closees the large object descriptor.
// Close the large object descriptor.
func (o *LargeObject) Close() error {
_, err := o.tx.Exec(o.ctx, "select lo_close($1)", o.fd)
return err

View file

@ -112,7 +112,7 @@ type Config struct {
// MaxConnIdleTime is the duration after which an idle connection will be automatically closed by the health check.
MaxConnIdleTime time.Duration
// MaxConns is the maximum size of the pool.
// MaxConns is the maximum size of the pool. The default is the greater of 4 or runtime.NumCPU().
MaxConns int32
// MinConns is the minimum size of the pool. The health check will increase the number of connections to this

32
vendor/github.com/jackc/pgx/v4/tx.go generated vendored
View file

@ -192,7 +192,7 @@ func (tx *dbTx) Begin(ctx context.Context) (Tx, error) {
return nil, err
}
return &dbSavepoint{tx: tx, savepointNum: tx.savepointNum}, nil
return &dbSimulatedNestedTx{tx: tx, savepointNum: tx.savepointNum}, nil
}
func (tx *dbTx) BeginFunc(ctx context.Context, f func(Tx) error) (err error) {
@ -329,15 +329,15 @@ func (tx *dbTx) Conn() *Conn {
return tx.conn
}
// dbSavepoint represents a nested transaction implemented by a savepoint.
type dbSavepoint struct {
// dbSimulatedNestedTx represents a simulated nested transaction implemented by a savepoint.
type dbSimulatedNestedTx struct {
tx Tx
savepointNum int64
closed bool
}
// Begin starts a pseudo nested transaction implemented with a savepoint.
func (sp *dbSavepoint) Begin(ctx context.Context) (Tx, error) {
func (sp *dbSimulatedNestedTx) Begin(ctx context.Context) (Tx, error) {
if sp.closed {
return nil, ErrTxClosed
}
@ -345,7 +345,7 @@ func (sp *dbSavepoint) Begin(ctx context.Context) (Tx, error) {
return sp.tx.Begin(ctx)
}
func (sp *dbSavepoint) BeginFunc(ctx context.Context, f func(Tx) error) (err error) {
func (sp *dbSimulatedNestedTx) BeginFunc(ctx context.Context, f func(Tx) error) (err error) {
if sp.closed {
return ErrTxClosed
}
@ -354,7 +354,7 @@ func (sp *dbSavepoint) BeginFunc(ctx context.Context, f func(Tx) error) (err err
}
// Commit releases the savepoint essentially committing the pseudo nested transaction.
func (sp *dbSavepoint) Commit(ctx context.Context) error {
func (sp *dbSimulatedNestedTx) Commit(ctx context.Context) error {
if sp.closed {
return ErrTxClosed
}
@ -367,7 +367,7 @@ func (sp *dbSavepoint) Commit(ctx context.Context) error {
// Rollback rolls back to the savepoint essentially rolling back the pseudo nested transaction. Rollback will return
// ErrTxClosed if the dbSavepoint is already closed, but is otherwise safe to call multiple times. Hence, a defer sp.Rollback()
// is safe even if sp.Commit() will be called first in a non-error condition.
func (sp *dbSavepoint) Rollback(ctx context.Context) error {
func (sp *dbSimulatedNestedTx) Rollback(ctx context.Context) error {
if sp.closed {
return ErrTxClosed
}
@ -378,7 +378,7 @@ func (sp *dbSavepoint) Rollback(ctx context.Context) error {
}
// Exec delegates to the underlying Tx
func (sp *dbSavepoint) Exec(ctx context.Context, sql string, arguments ...interface{}) (commandTag pgconn.CommandTag, err error) {
func (sp *dbSimulatedNestedTx) Exec(ctx context.Context, sql string, arguments ...interface{}) (commandTag pgconn.CommandTag, err error) {
if sp.closed {
return nil, ErrTxClosed
}
@ -387,7 +387,7 @@ func (sp *dbSavepoint) Exec(ctx context.Context, sql string, arguments ...interf
}
// Prepare delegates to the underlying Tx
func (sp *dbSavepoint) Prepare(ctx context.Context, name, sql string) (*pgconn.StatementDescription, error) {
func (sp *dbSimulatedNestedTx) Prepare(ctx context.Context, name, sql string) (*pgconn.StatementDescription, error) {
if sp.closed {
return nil, ErrTxClosed
}
@ -396,7 +396,7 @@ func (sp *dbSavepoint) Prepare(ctx context.Context, name, sql string) (*pgconn.S
}
// Query delegates to the underlying Tx
func (sp *dbSavepoint) Query(ctx context.Context, sql string, args ...interface{}) (Rows, error) {
func (sp *dbSimulatedNestedTx) Query(ctx context.Context, sql string, args ...interface{}) (Rows, error) {
if sp.closed {
// Because checking for errors can be deferred to the *Rows, build one with the error
err := ErrTxClosed
@ -407,13 +407,13 @@ func (sp *dbSavepoint) Query(ctx context.Context, sql string, args ...interface{
}
// QueryRow delegates to the underlying Tx
func (sp *dbSavepoint) QueryRow(ctx context.Context, sql string, args ...interface{}) Row {
func (sp *dbSimulatedNestedTx) QueryRow(ctx context.Context, sql string, args ...interface{}) Row {
rows, _ := sp.Query(ctx, sql, args...)
return (*connRow)(rows.(*connRows))
}
// QueryFunc delegates to the underlying Tx.
func (sp *dbSavepoint) QueryFunc(ctx context.Context, sql string, args []interface{}, scans []interface{}, f func(QueryFuncRow) error) (pgconn.CommandTag, error) {
func (sp *dbSimulatedNestedTx) QueryFunc(ctx context.Context, sql string, args []interface{}, scans []interface{}, f func(QueryFuncRow) error) (pgconn.CommandTag, error) {
if sp.closed {
return nil, ErrTxClosed
}
@ -422,7 +422,7 @@ func (sp *dbSavepoint) QueryFunc(ctx context.Context, sql string, args []interfa
}
// CopyFrom delegates to the underlying *Conn
func (sp *dbSavepoint) CopyFrom(ctx context.Context, tableName Identifier, columnNames []string, rowSrc CopyFromSource) (int64, error) {
func (sp *dbSimulatedNestedTx) CopyFrom(ctx context.Context, tableName Identifier, columnNames []string, rowSrc CopyFromSource) (int64, error) {
if sp.closed {
return 0, ErrTxClosed
}
@ -431,7 +431,7 @@ func (sp *dbSavepoint) CopyFrom(ctx context.Context, tableName Identifier, colum
}
// SendBatch delegates to the underlying *Conn
func (sp *dbSavepoint) SendBatch(ctx context.Context, b *Batch) BatchResults {
func (sp *dbSimulatedNestedTx) SendBatch(ctx context.Context, b *Batch) BatchResults {
if sp.closed {
return &batchResults{err: ErrTxClosed}
}
@ -439,10 +439,10 @@ func (sp *dbSavepoint) SendBatch(ctx context.Context, b *Batch) BatchResults {
return sp.tx.SendBatch(ctx, b)
}
func (sp *dbSavepoint) LargeObjects() LargeObjects {
func (sp *dbSimulatedNestedTx) LargeObjects() LargeObjects {
return LargeObjects{tx: sp}
}
func (sp *dbSavepoint) Conn() *Conn {
func (sp *dbSimulatedNestedTx) Conn() *Conn {
return sp.tx.Conn()
}