diff options
Diffstat (limited to 'vendor/github.com/jackc')
49 files changed, 2961 insertions, 275 deletions
| diff --git a/vendor/github.com/jackc/pgconn/CHANGELOG.md b/vendor/github.com/jackc/pgconn/CHANGELOG.md index a37eecfe1..f6a6807f0 100644 --- a/vendor/github.com/jackc/pgconn/CHANGELOG.md +++ b/vendor/github.com/jackc/pgconn/CHANGELOG.md @@ -1,3 +1,21 @@ +# 1.13.0 (August 6, 2022) + +* Add sslpassword support (Eric McCormack and yun.xu) +* Add prefer-standby target_session_attrs support (sergey.bashilov) +* Fix GSS ErrorResponse handling (Oliver Tan) + +# 1.12.1 (May 7, 2022) + +* Fix: setting krbspn and krbsrvname in connection string (sireax) +* Add support for Unix sockets on Windows (Eno Compton) +* Stop ignoring ErrorResponse during SCRAM auth (Rafi Shamim) + +# 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) diff --git a/vendor/github.com/jackc/pgconn/auth_scram.go b/vendor/github.com/jackc/pgconn/auth_scram.go index 6a143fcdc..d8d711163 100644 --- a/vendor/github.com/jackc/pgconn/auth_scram.go +++ b/vendor/github.com/jackc/pgconn/auth_scram.go @@ -78,12 +78,14 @@ func (c *PgConn) rxSASLContinue() (*pgproto3.AuthenticationSASLContinue, error)  	if err != nil {  		return nil, err  	} -	saslContinue, ok := msg.(*pgproto3.AuthenticationSASLContinue) -	if ok { -		return saslContinue, nil +	switch m := msg.(type) { +	case *pgproto3.AuthenticationSASLContinue: +		return m, nil +	case *pgproto3.ErrorResponse: +		return nil, ErrorResponseToPgError(m)  	} -	return nil, errors.New("expected AuthenticationSASLContinue message but received unexpected message") +	return nil, fmt.Errorf("expected AuthenticationSASLContinue message but received unexpected message %T", msg)  }  func (c *PgConn) rxSASLFinal() (*pgproto3.AuthenticationSASLFinal, error) { @@ -91,12 +93,14 @@ func (c *PgConn) rxSASLFinal() (*pgproto3.AuthenticationSASLFinal, error) {  	if err != nil {  		return nil, err  	} -	saslFinal, ok := msg.(*pgproto3.AuthenticationSASLFinal) -	if ok { -		return saslFinal, nil +	switch m := msg.(type) { +	case *pgproto3.AuthenticationSASLFinal: +		return m, nil +	case *pgproto3.ErrorResponse: +		return nil, ErrorResponseToPgError(m)  	} -	return nil, errors.New("expected AuthenticationSASLFinal message but received unexpected message") +	return nil, fmt.Errorf("expected AuthenticationSASLFinal message but received unexpected message %T", msg)  }  type scramClient struct { diff --git a/vendor/github.com/jackc/pgconn/config.go b/vendor/github.com/jackc/pgconn/config.go index 0eab23af9..2277dc1de 100644 --- a/vendor/github.com/jackc/pgconn/config.go +++ b/vendor/github.com/jackc/pgconn/config.go @@ -4,6 +4,7 @@ import (  	"context"  	"crypto/tls"  	"crypto/x509" +	"encoding/pem"  	"errors"  	"fmt"  	"io" @@ -25,6 +26,7 @@ import (  type AfterConnectFunc func(ctx context.Context, pgconn *PgConn) error  type ValidateConnectFunc func(ctx context.Context, pgconn *PgConn) error +type GetSSLPasswordFunc func(ctx context.Context) string  // Config is the settings used to establish a connection to a PostgreSQL server. It must be created by ParseConfig. A  // manually initialized Config will cause ConnectConfig to panic. @@ -41,7 +43,9 @@ type Config struct {  	BuildFrontend  BuildFrontendFunc  	RuntimeParams  map[string]string // Run-time parameters to set on connection as session default values (e.g. search_path or application_name) -	Fallbacks []*FallbackConfig +	KerberosSrvName string +	KerberosSpn     string +	Fallbacks       []*FallbackConfig  	// ValidateConnect is called during a connection attempt after a successful authentication with the PostgreSQL server.  	// It can be used to validate that the server is acceptable. If this returns an error the connection is closed and the next @@ -61,6 +65,13 @@ type Config struct {  	createdByParseConfig bool // Used to enforce created by ParseConfig rule.  } +// ParseConfigOptions contains options that control how a config is built such as getsslpassword. +type ParseConfigOptions struct { +	// GetSSLPassword gets the password to decrypt a SSL client certificate. This is analogous to the the libpq function +	// PQsetSSLKeyPassHook_OpenSSL. +	GetSSLPassword GetSSLPasswordFunc +} +  // Copy returns a deep copy of the config that is safe to use and modify.  // The only exception is the TLSConfig field:  // according to the tls.Config docs it must not be modified after creation. @@ -98,10 +109,29 @@ type FallbackConfig struct {  	TLSConfig *tls.Config // nil disables TLS  } +// isAbsolutePath checks if the provided value is an absolute path either +// beginning with a forward slash (as on Linux-based systems) or with a capital +// letter A-Z followed by a colon and a backslash, e.g., "C:\", (as on Windows). +func isAbsolutePath(path string) bool { +	isWindowsPath := func(p string) bool { +		if len(p) < 3 { +			return false +		} +		drive := p[0] +		colon := p[1] +		backslash := p[2] +		if drive >= 'A' && drive <= 'Z' && colon == ':' && backslash == '\\' { +			return true +		} +		return false +	} +	return strings.HasPrefix(path, "/") || isWindowsPath(path) +} +  // NetworkAddress converts a PostgreSQL host and port into network and address suitable for use with  // net.Dial.  func NetworkAddress(host string, port uint16) (network, address string) { -	if strings.HasPrefix(host, "/") { +	if isAbsolutePath(host) {  		network = "unix"  		address = filepath.Join(host, ".s.PGSQL.") + strconv.FormatInt(int64(port), 10)  	} else { @@ -111,10 +141,10 @@ func NetworkAddress(host string, port uint16) (network, address string) {  	return network, address  } -// ParseConfig builds a *Config with similar behavior to the PostgreSQL standard C library libpq. It uses the same -// defaults as libpq (e.g. port=5432) and understands most PG* environment variables. ParseConfig closely matches -// the parsing behavior of libpq. connString may either be in URL format or keyword = value format (DSN style). See -// https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING for details. connString also may be +// ParseConfig builds a *Config from connString with similar behavior to the PostgreSQL standard C library libpq. It +// uses the same defaults as libpq (e.g. port=5432) and understands most PG* environment variables. ParseConfig closely +// matches the parsing behavior of libpq. connString may either be in URL format or keyword = value format (DSN style). +// See https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING for details. connString also may be  // empty to only read from the environment. If a password is not supplied it will attempt to read the .pgpass file.  //  //   # Example DSN @@ -138,21 +168,22 @@ func NetworkAddress(host string, port uint16) (network, address string) {  // ParseConfig currently recognizes the following environment variable and their parameter key word equivalents passed  // via database URL or DSN:  // -// 	 PGHOST -// 	 PGPORT -// 	 PGDATABASE -// 	 PGUSER -// 	 PGPASSWORD -// 	 PGPASSFILE -// 	 PGSERVICE -// 	 PGSERVICEFILE -// 	 PGSSLMODE -// 	 PGSSLCERT -// 	 PGSSLKEY -// 	 PGSSLROOTCERT -// 	 PGAPPNAME -// 	 PGCONNECT_TIMEOUT -// 	 PGTARGETSESSIONATTRS +//   PGHOST +//   PGPORT +//   PGDATABASE +//   PGUSER +//   PGPASSWORD +//   PGPASSFILE +//   PGSERVICE +//   PGSERVICEFILE +//   PGSSLMODE +//   PGSSLCERT +//   PGSSLKEY +//   PGSSLROOTCERT +//   PGSSLPASSWORD +//   PGAPPNAME +//   PGCONNECT_TIMEOUT +//   PGTARGETSESSIONATTRS  //  // See http://www.postgresql.org/docs/11/static/libpq-envars.html for details on the meaning of environment variables.  // @@ -172,23 +203,29 @@ func NetworkAddress(host string, port uint16) (network, address string) {  // sslmode "prefer" this means it will first try the main Config settings which use TLS, then it will try the fallback  // which does not use TLS. This can lead to an unexpected unencrypted connection if the main TLS config is manually  // changed later but the unencrypted fallback is present. Ensure there are no stale fallbacks when manually setting -// TLCConfig. +// TLSConfig.  //  // 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.  //  // In addition, ParseConfig accepts the following options:  // -// 	min_read_buffer_size -// 		The minimum size of the internal read buffer. Default 8192. -// 	servicefile -// 		libpq only reads servicefile from the PGSERVICEFILE environment variable. ParseConfig accepts servicefile as a -// 		part of the connection string. +//  min_read_buffer_size +//    The minimum size of the internal read buffer. Default 8192. +//  servicefile +//    libpq only reads servicefile from the PGSERVICEFILE environment variable. ParseConfig accepts servicefile as a +//    part of the connection string.  func ParseConfig(connString string) (*Config, error) { +	var parseConfigOptions ParseConfigOptions +	return ParseConfigWithOptions(connString, parseConfigOptions) +} + +// ParseConfigWithOptions builds a *Config from connString and options with similar behavior to the PostgreSQL standard +// C library libpq. options contains settings that cannot be specified in a connString such as providing a function to +// get the SSL password. +func ParseConfigWithOptions(connString string, options ParseConfigOptions) (*Config, error) {  	defaultSettings := defaultSettings()  	envSettings := parseEnvSettings() @@ -259,12 +296,23 @@ func ParseConfig(connString string) (*Config, error) {  		"sslkey":               {},  		"sslcert":              {},  		"sslrootcert":          {}, +		"sslpassword":          {}, +		"krbspn":               {}, +		"krbsrvname":           {},  		"target_session_attrs": {},  		"min_read_buffer_size": {},  		"service":              {},  		"servicefile":          {},  	} +	// Adding kerberos configuration +	if _, present := settings["krbsrvname"]; present { +		config.KerberosSrvName = settings["krbsrvname"] +	} +	if _, present := settings["krbspn"]; present { +		config.KerberosSpn = settings["krbspn"] +	} +  	for k, v := range settings {  		if _, present := notRuntimeParams[k]; present {  			continue @@ -297,7 +345,7 @@ func ParseConfig(connString string) (*Config, error) {  			tlsConfigs = append(tlsConfigs, nil)  		} else {  			var err error -			tlsConfigs, err = configTLS(settings, host) +			tlsConfigs, err = configTLS(settings, host, options)  			if err != nil {  				return nil, &parseConfigError{connString: connString, msg: "failed to configure TLS", err: err}  			} @@ -338,7 +386,9 @@ func ParseConfig(connString string) (*Config, error) {  		config.ValidateConnect = ValidateConnectTargetSessionAttrsPrimary  	case "standby":  		config.ValidateConnect = ValidateConnectTargetSessionAttrsStandby -	case "any", "prefer-standby": +	case "prefer-standby": +		config.ValidateConnect = ValidateConnectTargetSessionAttrsPreferStandby +	case "any":  		// do nothing  	default:  		return nil, &parseConfigError{connString: connString, msg: fmt.Sprintf("unknown target_session_attrs value: %v", tsa)} @@ -375,6 +425,7 @@ func parseEnvSettings() map[string]string {  		"PGSSLKEY":             "sslkey",  		"PGSSLCERT":            "sslcert",  		"PGSSLROOTCERT":        "sslrootcert", +		"PGSSLPASSWORD":        "sslpassword",  		"PGTARGETSESSIONATTRS": "target_session_attrs",  		"PGSERVICE":            "service",  		"PGSERVICEFILE":        "servicefile", @@ -561,12 +612,13 @@ func parseServiceSettings(servicefilePath, serviceName string) (map[string]strin  // configTLS uses libpq's TLS parameters to construct  []*tls.Config. It is  // necessary to allow returning multiple TLS configs as sslmode "allow" and  // "prefer" allow fallback. -func configTLS(settings map[string]string, thisHost string) ([]*tls.Config, error) { +func configTLS(settings map[string]string, thisHost string, parseConfigOptions ParseConfigOptions) ([]*tls.Config, error) {  	host := thisHost  	sslmode := settings["sslmode"]  	sslrootcert := settings["sslrootcert"]  	sslcert := settings["sslcert"]  	sslkey := settings["sslkey"] +	sslpassword := settings["sslpassword"]  	// Match libpq default behavior  	if sslmode == "" { @@ -654,11 +706,53 @@ func configTLS(settings map[string]string, thisHost string) ([]*tls.Config, erro  	}  	if sslcert != "" && sslkey != "" { -		cert, err := tls.LoadX509KeyPair(sslcert, sslkey) +		buf, err := ioutil.ReadFile(sslkey)  		if err != nil { -			return nil, fmt.Errorf("unable to read cert: %w", err) +			return nil, fmt.Errorf("unable to read sslkey: %w", err)  		} +		block, _ := pem.Decode(buf) +		var pemKey []byte +		var decryptedKey []byte +		var decryptedError error +		// If PEM is encrypted, attempt to decrypt using pass phrase +		if x509.IsEncryptedPEMBlock(block) { +			// Attempt decryption with pass phrase +			// NOTE: only supports RSA (PKCS#1) +			if sslpassword != "" { +				decryptedKey, decryptedError = x509.DecryptPEMBlock(block, []byte(sslpassword)) +			} +			//if sslpassword not provided or has decryption error when use it +			//try to find sslpassword with callback function +			if sslpassword == "" || decryptedError != nil { +				if parseConfigOptions.GetSSLPassword != nil { +					sslpassword = parseConfigOptions.GetSSLPassword(context.Background()) +				} +				if sslpassword == "" { +					return nil, fmt.Errorf("unable to find sslpassword") +				} +			} +			decryptedKey, decryptedError = x509.DecryptPEMBlock(block, []byte(sslpassword)) +			// Should we also provide warning for PKCS#1 needed? +			if decryptedError != nil { +				return nil, fmt.Errorf("unable to decrypt key: %w", err) +			} +			pemBytes := pem.Block{ +				Type:  "RSA PRIVATE KEY", +				Bytes: decryptedKey, +			} +			pemKey = pem.EncodeToMemory(&pemBytes) +		} else { +			pemKey = pem.EncodeToMemory(block) +		} +		certfile, err := ioutil.ReadFile(sslcert) +		if err != nil { +			return nil, fmt.Errorf("unable to read cert: %w", err) +		} +		cert, err := tls.X509KeyPair(certfile, pemKey) +		if err != nil { +			return nil, fmt.Errorf("unable to load cert: %w", err) +		}  		tlsConfig.Certificates = []tls.Certificate{cert}  	} @@ -781,3 +875,18 @@ func ValidateConnectTargetSessionAttrsPrimary(ctx context.Context, pgConn *PgCon  	return nil  } + +// ValidateConnectTargetSessionAttrsPreferStandby is an ValidateConnectFunc that implements libpq compatible +// target_session_attrs=prefer-standby. +func ValidateConnectTargetSessionAttrsPreferStandby(ctx context.Context, pgConn *PgConn) error { +	result := pgConn.ExecParams(ctx, "select pg_is_in_recovery()", nil, nil, nil, nil).Read() +	if result.Err != nil { +		return result.Err +	} + +	if string(result.Rows[0][0]) != "t" { +		return &NotPreferredError{err: errors.New("server is not in hot standby mode")} +	} + +	return nil +} diff --git a/vendor/github.com/jackc/pgconn/defaults.go b/vendor/github.com/jackc/pgconn/defaults.go index f69cad317..c7209fdd3 100644 --- a/vendor/github.com/jackc/pgconn/defaults.go +++ b/vendor/github.com/jackc/pgconn/defaults.go @@ -1,3 +1,4 @@ +//go:build !windows  // +build !windows  package pgconn diff --git a/vendor/github.com/jackc/pgconn/errors.go b/vendor/github.com/jackc/pgconn/errors.go index a32b29c92..66d35584a 100644 --- a/vendor/github.com/jackc/pgconn/errors.go +++ b/vendor/github.com/jackc/pgconn/errors.go @@ -219,3 +219,20 @@ func redactURL(u *url.URL) string {  	}  	return u.String()  } + +type NotPreferredError struct { +	err         error +	safeToRetry bool +} + +func (e *NotPreferredError) Error() string { +	return fmt.Sprintf("standby server not found: %s", e.err.Error()) +} + +func (e *NotPreferredError) SafeToRetry() bool { +	return e.safeToRetry +} + +func (e *NotPreferredError) Unwrap() error { +	return e.err +} diff --git a/vendor/github.com/jackc/pgconn/krb5.go b/vendor/github.com/jackc/pgconn/krb5.go new file mode 100644 index 000000000..08427b8e6 --- /dev/null +++ b/vendor/github.com/jackc/pgconn/krb5.go @@ -0,0 +1,99 @@ +package pgconn + +import ( +	"errors" +	"fmt" + +	"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 c.config.KerberosSpn != "" { +		// Use the supplied SPN if provided. +		nextData, err = cli.GetInitTokenFromSPN(c.config.KerberosSpn) +	} else { +		// Allow the kerberos service name to be overridden +		service := "postgres" +		if c.config.KerberosSrvName != "" { +			service = c.config.KerberosSrvName +		} +		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 +	} + +	switch m := msg.(type) { +	case *pgproto3.AuthenticationGSSContinue: +		return m, nil +	case *pgproto3.ErrorResponse: +		return nil, ErrorResponseToPgError(m) +	} + +	return nil, fmt.Errorf("expected AuthenticationGSSContinue message but received unexpected message %T", msg) +} diff --git a/vendor/github.com/jackc/pgconn/pgconn.go b/vendor/github.com/jackc/pgconn/pgconn.go index 7bf2f20ef..17f19e955 100644 --- a/vendor/github.com/jackc/pgconn/pgconn.go +++ b/vendor/github.com/jackc/pgconn/pgconn.go @@ -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 { @@ -109,6 +109,18 @@ func Connect(ctx context.Context, connString string) (*PgConn, error) {  	return ConnectConfig(ctx, config)  } +// Connect establishes a connection to a PostgreSQL server using the environment and connString (in URL or DSN format) +// and ParseConfigOptions to provide additional configuration. See documentation for ParseConfig for details. ctx can be +// used to cancel a connect attempt. +func ConnectWithOptions(ctx context.Context, connString string, parseConfigOptions ParseConfigOptions) (*PgConn, error) { +	config, err := ParseConfigWithOptions(connString, parseConfigOptions) +	if err != nil { +		return nil, err +	} + +	return ConnectConfig(ctx, config) +} +  // Connect establishes a connection to a PostgreSQL server using config. config must have been constructed with  // ParseConfig. ctx can be used to cancel a connect attempt.  // @@ -148,17 +160,36 @@ func ConnectConfig(ctx context.Context, config *Config) (pgConn *PgConn, err err  		return nil, &connectError{config: config, msg: "hostname resolving error", err: errors.New("ip addr wasn't found")}  	} +	foundBestServer := false +	var fallbackConfig *FallbackConfig  	for _, fc := range fallbackConfigs { -		pgConn, err = connect(ctx, config, fc) +		pgConn, err = connect(ctx, config, fc, false)  		if err == nil { +			foundBestServer = true  			break  		} else if pgerr, ok := err.(*PgError); ok {  			err = &connectError{config: config, msg: "server error", err: pgerr} -			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  			} +		} else if cerr, ok := err.(*connectError); ok { +			if _, ok := cerr.err.(*NotPreferredError); ok { +				fallbackConfig = fc +			} +		} +	} + +	if !foundBestServer && fallbackConfig != nil { +		pgConn, err = connect(ctx, config, fallbackConfig, true) +		if pgerr, ok := err.(*PgError); ok { +			err = &connectError{config: config, msg: "server error", err: pgerr}  		}  	} @@ -182,7 +213,7 @@ func expandWithIPs(ctx context.Context, lookupFn LookupFunc, fallbacks []*Fallba  	for _, fb := range fallbacks {  		// skip resolve for unix sockets -		if strings.HasPrefix(fb.Host, "/") { +		if isAbsolutePath(fb.Host) {  			configs = append(configs, &FallbackConfig{  				Host:      fb.Host,  				Port:      fb.Port, @@ -222,7 +253,8 @@ func expandWithIPs(ctx context.Context, lookupFn LookupFunc, fallbacks []*Fallba  	return configs, nil  } -func connect(ctx context.Context, config *Config, fallbackConfig *FallbackConfig) (*PgConn, error) { +func connect(ctx context.Context, config *Config, fallbackConfig *FallbackConfig, +	ignoreNotPreferredErr bool) (*PgConn, error) {  	pgConn := new(PgConn)  	pgConn.config = config  	pgConn.wbuf = make([]byte, 0, wbufLen) @@ -317,7 +349,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 { @@ -330,6 +367,9 @@ func connect(ctx context.Context, config *Config, fallbackConfig *FallbackConfig  				err := config.ValidateConnect(ctx, pgConn)  				if err != nil { +					if _, ok := err.(*NotPreferredError); ignoreNotPreferredErr && ok { +						return pgConn, nil +					}  					pgConn.conn.Close()  					return nil, &connectError{config: config, msg: "ValidateConnect failed", err: err}  				} diff --git a/vendor/github.com/jackc/pgconn/stmtcache/lru.go b/vendor/github.com/jackc/pgconn/stmtcache/lru.go index 90fb76c2f..f0fb53b9c 100644 --- a/vendor/github.com/jackc/pgconn/stmtcache/lru.go +++ b/vendor/github.com/jackc/pgconn/stmtcache/lru.go @@ -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)  	}  } diff --git a/vendor/github.com/jackc/pgproto3/v2/authentication_gss.go b/vendor/github.com/jackc/pgproto3/v2/authentication_gss.go new file mode 100644 index 000000000..5a3f3b1d5 --- /dev/null +++ b/vendor/github.com/jackc/pgproto3/v2/authentication_gss.go @@ -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 +} diff --git a/vendor/github.com/jackc/pgproto3/v2/authentication_gss_continue.go b/vendor/github.com/jackc/pgproto3/v2/authentication_gss_continue.go new file mode 100644 index 000000000..cf8b18345 --- /dev/null +++ b/vendor/github.com/jackc/pgproto3/v2/authentication_gss_continue.go @@ -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 +} diff --git a/vendor/github.com/jackc/pgproto3/v2/backend.go b/vendor/github.com/jackc/pgproto3/v2/backend.go index 9c42ad02e..1f1436529 100644 --- a/vendor/github.com/jackc/pgproto3/v2/backend.go +++ b/vendor/github.com/jackc/pgproto3/v2/backend.go @@ -2,6 +2,7 @@ package pgproto3  import (  	"encoding/binary" +	"errors"  	"fmt"  	"io"  ) @@ -30,11 +31,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 ( @@ -115,6 +115,9 @@ func (b *Backend) Receive() (FrontendMessage, error) {  		b.msgType = header[0]  		b.bodyLen = int(binary.BigEndian.Uint32(header[1:])) - 4  		b.partialMsg = true +		if b.bodyLen < 0 { +			return nil, errors.New("invalid message with negative body length received") +		}  	}  	var msg FrontendMessage @@ -147,6 +150,8 @@ func (b *Backend) Receive() (FrontendMessage, error) {  			msg = &SASLResponse{}  		case AuthTypeSASLFinal:  			msg = &SASLResponse{} +		case AuthTypeGSS, AuthTypeGSSCont: +			msg = &GSSResponse{}  		case AuthTypeCleartextPassword, AuthTypeMD5Password:  			fallthrough  		default: diff --git a/vendor/github.com/jackc/pgproto3/v2/copy_both_response.go b/vendor/github.com/jackc/pgproto3/v2/copy_both_response.go index fbd985d86..4a1c3a07b 100644 --- a/vendor/github.com/jackc/pgproto3/v2/copy_both_response.go +++ b/vendor/github.com/jackc/pgproto3/v2/copy_both_response.go @@ -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) diff --git a/vendor/github.com/jackc/pgproto3/v2/frontend.go b/vendor/github.com/jackc/pgproto3/v2/frontend.go index c33dfb084..5be8de808 100644 --- a/vendor/github.com/jackc/pgproto3/v2/frontend.go +++ b/vendor/github.com/jackc/pgproto3/v2/frontend.go @@ -16,6 +16,8 @@ type Frontend struct {  	authenticationOk                AuthenticationOk  	authenticationCleartextPassword AuthenticationCleartextPassword  	authenticationMD5Password       AuthenticationMD5Password +	authenticationGSS               AuthenticationGSS +	authenticationGSSContinue       AuthenticationGSSContinue  	authenticationSASL              AuthenticationSASL  	authenticationSASLContinue      AuthenticationSASLContinue  	authenticationSASLFinal         AuthenticationSASLFinal @@ -77,6 +79,9 @@ func (f *Frontend) Receive() (BackendMessage, error) {  		f.msgType = header[0]  		f.bodyLen = int(binary.BigEndian.Uint32(header[1:])) - 4  		f.partialMsg = true +		if f.bodyLen < 0 { +			return nil, errors.New("invalid message with negative body length received") +		}  	}  	msgBody, err := f.cr.Next(f.bodyLen) @@ -178,9 +183,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: diff --git a/vendor/github.com/jackc/pgproto3/v2/gss_response.go b/vendor/github.com/jackc/pgproto3/v2/gss_response.go new file mode 100644 index 000000000..62da99c79 --- /dev/null +++ b/vendor/github.com/jackc/pgproto3/v2/gss_response.go @@ -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 +} diff --git a/vendor/github.com/jackc/pgtype/CHANGELOG.md b/vendor/github.com/jackc/pgtype/CHANGELOG.md index 73126cf36..f0c037eff 100644 --- a/vendor/github.com/jackc/pgtype/CHANGELOG.md +++ b/vendor/github.com/jackc/pgtype/CHANGELOG.md @@ -1,3 +1,24 @@ +# 1.12.0 (August 6, 2022) + +* Add JSONArray (Jakob Ackermann) +* Support Inet from fmt.Stringer and encoding.TextMarshaler (Ville Skyttä) +* Support UUID from fmt.Stringer interface (Lasse Hyldahl Jensen) +* Fix: shopspring-numeric extension does not panic on NaN +* Numeric can be assigned to string +* Fix: Do not send IPv4 networks as IPv4-mapped IPv6 (William Storey) +* Fix: PlanScan for interface{}(nil) (James Hartig) +* Fix: *sql.Scanner for NULL handling (James Hartig) +* Timestamp[tz].Set() supports string (Harmen) +* Fix: Hstore AssignTo with map of *string (Diego Becciolini) + +# 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) diff --git a/vendor/github.com/jackc/pgtype/array_type.go b/vendor/github.com/jackc/pgtype/array_type.go index 1bd0244b7..714665544 100644 --- a/vendor/github.com/jackc/pgtype/array_type.go +++ b/vendor/github.com/jackc/pgtype/array_type.go @@ -11,7 +11,7 @@ import (  // ArrayType represents an array type. While it implements Value, this is only in service of its type conversion duties  // when registered as a data type in a ConnType. It should not be used directly as a Value. ArrayType is a convenience -// type for types that do not have an concrete array type. +// type for types that do not have a concrete array type.  type ArrayType struct {  	elements   []ValueTranscoder  	dimensions []ArrayDimension diff --git a/vendor/github.com/jackc/pgtype/date.go b/vendor/github.com/jackc/pgtype/date.go index e8d21a78c..ca84970ec 100644 --- a/vendor/github.com/jackc/pgtype/date.go +++ b/vendor/github.com/jackc/pgtype/date.go @@ -37,14 +37,14 @@ func (dst *Date) Set(src interface{}) error {  	switch value := src.(type) {  	case time.Time:  		*dst = Date{Time: value, Status: Present} -	case string: -		return dst.DecodeText(nil, []byte(value))  	case *time.Time:  		if value == nil {  			*dst = Date{Status: Null}  		} else {  			return dst.Set(*value)  		} +	case string: +		return dst.DecodeText(nil, []byte(value))  	case *string:  		if value == nil {  			*dst = Date{Status: Null} diff --git a/vendor/github.com/jackc/pgtype/enum_type.go b/vendor/github.com/jackc/pgtype/enum_type.go index d340320fa..526578226 100644 --- a/vendor/github.com/jackc/pgtype/enum_type.go +++ b/vendor/github.com/jackc/pgtype/enum_type.go @@ -2,7 +2,7 @@ package pgtype  import "fmt" -// EnumType represents a enum type. While it implements Value, this is only in service of its type conversion duties +// EnumType represents an enum type. While it implements Value, this is only in service of its type conversion duties  // when registered as a data type in a ConnType. It should not be used directly as a Value.  type EnumType struct {  	value  string diff --git a/vendor/github.com/jackc/pgtype/hstore.go b/vendor/github.com/jackc/pgtype/hstore.go index f46eeaf62..e42b75512 100644 --- a/vendor/github.com/jackc/pgtype/hstore.go +++ b/vendor/github.com/jackc/pgtype/hstore.go @@ -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)  	} @@ -88,7 +90,8 @@ func (src *Hstore) AssignTo(dst interface{}) error {  				case Null:  					(*v)[k] = nil  				case Present: -					(*v)[k] = &val.String +					str := val.String +					(*v)[k] = &str  				default:  					return fmt.Errorf("cannot decode %#v into %T", src, dst)  				} @@ -411,7 +414,7 @@ func parseHstore(s string) (k []string, v []Text, err error) {  				r, end = p.Consume()  				switch {  				case end: -					err = errors.New("Found EOS after ',', expcting space") +					err = errors.New("Found EOS after ',', expecting space")  				case (unicode.IsSpace(r)):  					r, end = p.Consume()  					state = hsKey diff --git a/vendor/github.com/jackc/pgtype/inet.go b/vendor/github.com/jackc/pgtype/inet.go index f35f88bad..976f0d7b9 100644 --- a/vendor/github.com/jackc/pgtype/inet.go +++ b/vendor/github.com/jackc/pgtype/inet.go @@ -2,8 +2,10 @@ package pgtype  import (  	"database/sql/driver" +	"encoding"  	"fmt"  	"net" +	"strings"  )  // Network address family is dependent on server socket.h value for AF_INET. @@ -47,17 +49,26 @@ func (dst *Inet) Set(src interface{}) error {  	case string:  		ip, ipnet, err := net.ParseCIDR(value)  		if err != nil { -			ip = net.ParseIP(value) +			ip := net.ParseIP(value)  			if ip == nil {  				return fmt.Errorf("unable to parse inet address: %s", value)  			} -			ipnet = &net.IPNet{IP: ip, Mask: net.CIDRMask(128, 128)} -			if ipv4 := ip.To4(); ipv4 != nil { -				ip = ipv4 -				ipnet.Mask = net.CIDRMask(32, 32) + +			if ipv4 := maybeGetIPv4(value, ip); ipv4 != nil { +				ipnet = &net.IPNet{IP: ipv4, Mask: net.CIDRMask(32, 32)} +			} else { +				ipnet = &net.IPNet{IP: ip, Mask: net.CIDRMask(128, 128)} +			} +		} else { +			ipnet.IP = ip +			if ipv4 := maybeGetIPv4(value, ipnet.IP); ipv4 != nil { +				ipnet.IP = ipv4 +				if len(ipnet.Mask) == 16 { +					ipnet.Mask = ipnet.Mask[12:] // Not sure this is ever needed. +				}  			}  		} -		ipnet.IP = ip +  		*dst = Inet{IPNet: ipnet, Status: Present}  	case *net.IPNet:  		if value == nil { @@ -78,6 +89,16 @@ func (dst *Inet) Set(src interface{}) error {  			return dst.Set(*value)  		}  	default: +		if tv, ok := src.(encoding.TextMarshaler); ok { +			text, err := tv.MarshalText() +			if err != nil { +				return fmt.Errorf("cannot marshal %v: %w", value, err) +			} +			return dst.Set(string(text)) +		} +		if sv, ok := src.(fmt.Stringer); ok { +			return dst.Set(sv.String()) +		}  		if originalSrc, ok := underlyingPtrType(src); ok {  			return dst.Set(originalSrc)  		} @@ -87,6 +108,25 @@ func (dst *Inet) Set(src interface{}) error {  	return nil  } +// Convert the net.IP to IPv4, if appropriate. +// +// When parsing a string to a net.IP using net.ParseIP() and the like, we get a +// 16 byte slice for IPv4 addresses as well as IPv6 addresses. This function +// calls To4() to convert them to a 4 byte slice. This is useful as it allows +// users of the net.IP check for IPv4 addresses based on the length and makes +// it clear we are handling IPv4 as opposed to IPv6 or IPv4-mapped IPv6 +// addresses. +func maybeGetIPv4(input string, ip net.IP) net.IP { +	// Do not do this if the provided input looks like IPv6. This is because +	// To4() on IPv4-mapped IPv6 addresses converts them to IPv4, which behave +	// different in some cases. +	if strings.Contains(input, ":") { +		return nil +	} + +	return ip.To4() +} +  func (dst Inet) Get() interface{} {  	switch dst.Status {  	case Present: @@ -118,6 +158,12 @@ func (src *Inet) AssignTo(dst interface{}) error {  			copy(*v, src.IPNet.IP)  			return nil  		default: +			if tv, ok := dst.(encoding.TextUnmarshaler); ok { +				if err := tv.UnmarshalText([]byte(src.IPNet.String())); err != nil { +					return fmt.Errorf("cannot unmarshal %v to %T: %w", src, dst, err) +				} +				return nil +			}  			if nextDst, retry := GetAssignToDstType(dst); retry {  				return src.AssignTo(nextDst)  			} @@ -169,7 +215,7 @@ func (dst *Inet) DecodeBinary(ci *ConnInfo, src []byte) error {  	}  	if len(src) != 8 && len(src) != 20 { -		return fmt.Errorf("Received an invalid size for a inet: %d", len(src)) +		return fmt.Errorf("Received an invalid size for an inet: %d", len(src))  	}  	// ignore family diff --git a/vendor/github.com/jackc/pgtype/int2.go b/vendor/github.com/jackc/pgtype/int2.go index 3eb5aeb55..0775882ab 100644 --- a/vendor/github.com/jackc/pgtype/int2.go +++ b/vendor/github.com/jackc/pgtype/int2.go @@ -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 +} diff --git a/vendor/github.com/jackc/pgtype/int4_multirange.go b/vendor/github.com/jackc/pgtype/int4_multirange.go new file mode 100644 index 000000000..c3432ce63 --- /dev/null +++ b/vendor/github.com/jackc/pgtype/int4_multirange.go @@ -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) +} diff --git a/vendor/github.com/jackc/pgtype/int8_multirange.go b/vendor/github.com/jackc/pgtype/int8_multirange.go new file mode 100644 index 000000000..e0976427a --- /dev/null +++ b/vendor/github.com/jackc/pgtype/int8_multirange.go @@ -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) +} diff --git a/vendor/github.com/jackc/pgtype/interval.go b/vendor/github.com/jackc/pgtype/interval.go index b01fbb7cb..00ec47c53 100644 --- a/vendor/github.com/jackc/pgtype/interval.go +++ b/vendor/github.com/jackc/pgtype/interval.go @@ -174,7 +174,7 @@ func (dst *Interval) DecodeBinary(ci *ConnInfo, src []byte) error {  	}  	if len(src) != 16 { -		return fmt.Errorf("Received an invalid size for a interval: %d", len(src)) +		return fmt.Errorf("Received an invalid size for an interval: %d", len(src))  	}  	microseconds := int64(binary.BigEndian.Uint64(src)) diff --git a/vendor/github.com/jackc/pgtype/json_array.go b/vendor/github.com/jackc/pgtype/json_array.go new file mode 100644 index 000000000..8d68882f0 --- /dev/null +++ b/vendor/github.com/jackc/pgtype/json_array.go @@ -0,0 +1,546 @@ +// Code generated by erb. DO NOT EDIT. + +package pgtype + +import ( +	"database/sql/driver" +	"encoding/binary" +	"encoding/json" +	"fmt" +	"reflect" + +	"github.com/jackc/pgio" +) + +type JSONArray struct { +	Elements   []JSON +	Dimensions []ArrayDimension +	Status     Status +} + +func (dst *JSONArray) Set(src interface{}) error { +	// untyped nil and typed nil interfaces are different +	if src == nil { +		*dst = JSONArray{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 []string: +		if value == nil { +			*dst = JSONArray{Status: Null} +		} else if len(value) == 0 { +			*dst = JSONArray{Status: Present} +		} else { +			elements := make([]JSON, len(value)) +			for i := range value { +				if err := elements[i].Set(value[i]); err != nil { +					return err +				} +			} +			*dst = JSONArray{ +				Elements:   elements, +				Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, +				Status:     Present, +			} +		} + +	case [][]byte: +		if value == nil { +			*dst = JSONArray{Status: Null} +		} else if len(value) == 0 { +			*dst = JSONArray{Status: Present} +		} else { +			elements := make([]JSON, len(value)) +			for i := range value { +				if err := elements[i].Set(value[i]); err != nil { +					return err +				} +			} +			*dst = JSONArray{ +				Elements:   elements, +				Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, +				Status:     Present, +			} +		} + +	case []json.RawMessage: +		if value == nil { +			*dst = JSONArray{Status: Null} +		} else if len(value) == 0 { +			*dst = JSONArray{Status: Present} +		} else { +			elements := make([]JSON, len(value)) +			for i := range value { +				if err := elements[i].Set(value[i]); err != nil { +					return err +				} +			} +			*dst = JSONArray{ +				Elements:   elements, +				Dimensions: []ArrayDimension{{Length: int32(len(elements)), LowerBound: 1}}, +				Status:     Present, +			} +		} + +	case []JSON: +		if value == nil { +			*dst = JSONArray{Status: Null} +		} else if len(value) == 0 { +			*dst = JSONArray{Status: Present} +		} else { +			*dst = JSONArray{ +				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 = JSONArray{Status: Null} +			return nil +		} + +		dimensions, elementsLength, ok := findDimensionsFromValue(reflectedValue, nil, 0) +		if !ok { +			return fmt.Errorf("cannot find dimensions of %v for JSONArray", src) +		} +		if elementsLength == 0 { +			*dst = JSONArray{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 JSONArray", src) +		} + +		*dst = JSONArray{ +			Elements:   make([]JSON, 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([]JSON, 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 JSONArray, expected %d dst.Elements, but got %d instead", src, len(dst.Elements), elementCount) +		} +	} + +	return nil +} + +func (dst *JSONArray) 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 JSONArray") +	} +	if err := dst.Elements[index].Set(value.Interface()); err != nil { +		return 0, fmt.Errorf("%v in JSONArray", err) +	} +	index++ + +	return index, nil +} + +func (dst JSONArray) Get() interface{} { +	switch dst.Status { +	case Present: +		return dst +	case Null: +		return nil +	default: +		return dst.Status +	} +} + +func (src *JSONArray) 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 *[]string: +				*v = make([]string, len(src.Elements)) +				for i := range src.Elements { +					if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { +						return err +					} +				} +				return nil + +			case *[][]byte: +				*v = make([][]byte, len(src.Elements)) +				for i := range src.Elements { +					if err := src.Elements[i].AssignTo(&((*v)[i])); err != nil { +						return err +					} +				} +				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 + +			} +		} + +		// 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 *JSONArray) 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 JSONArray") +	} +	addr := value.Addr() +	if !addr.CanInterface() { +		return 0, fmt.Errorf("cannot assign all values from JSONArray") +	} +	if err := src.Elements[index].AssignTo(addr.Interface()); err != nil { +		return 0, err +	} +	index++ +	return index, nil +} + +func (dst *JSONArray) DecodeText(ci *ConnInfo, src []byte) error { +	if src == nil { +		*dst = JSONArray{Status: Null} +		return nil +	} + +	uta, err := ParseUntypedTextArray(string(src)) +	if err != nil { +		return err +	} + +	var elements []JSON + +	if len(uta.Elements) > 0 { +		elements = make([]JSON, len(uta.Elements)) + +		for i, s := range uta.Elements { +			var elem JSON +			var elemSrc []byte +			if s != "NULL" || uta.Quoted[i] { +				elemSrc = []byte(s) +			} +			err = elem.DecodeText(ci, elemSrc) +			if err != nil { +				return err +			} + +			elements[i] = elem +		} +	} + +	*dst = JSONArray{Elements: elements, Dimensions: uta.Dimensions, Status: Present} + +	return nil +} + +func (dst *JSONArray) DecodeBinary(ci *ConnInfo, src []byte) error { +	if src == nil { +		*dst = JSONArray{Status: Null} +		return nil +	} + +	var arrayHeader ArrayHeader +	rp, err := arrayHeader.DecodeBinary(ci, src) +	if err != nil { +		return err +	} + +	if len(arrayHeader.Dimensions) == 0 { +		*dst = JSONArray{Dimensions: arrayHeader.Dimensions, Status: Present} +		return nil +	} + +	elementCount := arrayHeader.Dimensions[0].Length +	for _, d := range arrayHeader.Dimensions[1:] { +		elementCount *= d.Length +	} + +	elements := make([]JSON, 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 = JSONArray{Elements: elements, Dimensions: arrayHeader.Dimensions, Status: Present} +	return nil +} + +func (src JSONArray) EncodeText(ci *ConnInfo, buf []byte) ([]byte, error) { +	switch src.Status { +	case Null: +		return nil, nil +	case Undefined: +		return nil, errUndefined +	} + +	if len(src.Dimensions) == 0 { +		return append(buf, '{', '}'), nil +	} + +	buf = EncodeTextArrayDimensions(buf, src.Dimensions) + +	// dimElemCounts is the multiples of elements that each array lies on. For +	// example, a single dimension array of length 4 would have a dimElemCounts of +	// [4]. A multi-dimensional array of lengths [3,5,2] would have a +	// dimElemCounts of [30,10,2]. This is used to simplify when to render a '{' +	// or '}'. +	dimElemCounts := make([]int, len(src.Dimensions)) +	dimElemCounts[len(src.Dimensions)-1] = int(src.Dimensions[len(src.Dimensions)-1].Length) +	for i := len(src.Dimensions) - 2; i > -1; i-- { +		dimElemCounts[i] = int(src.Dimensions[i].Length) * dimElemCounts[i+1] +	} + +	inElemBuf := make([]byte, 0, 32) +	for i, elem := range src.Elements { +		if i > 0 { +			buf = append(buf, ',') +		} + +		for _, dec := range dimElemCounts { +			if i%dec == 0 { +				buf = append(buf, '{') +			} +		} + +		elemBuf, err := elem.EncodeText(ci, inElemBuf) +		if err != nil { +			return nil, err +		} +		if elemBuf == nil { +			buf = append(buf, `NULL`...) +		} else { +			buf = append(buf, QuoteArrayElementIfNeeded(string(elemBuf))...) +		} + +		for _, dec := range dimElemCounts { +			if (i+1)%dec == 0 { +				buf = append(buf, '}') +			} +		} +	} + +	return buf, nil +} + +func (src JSONArray) EncodeBinary(ci *ConnInfo, buf []byte) ([]byte, error) { +	switch src.Status { +	case Null: +		return nil, nil +	case Undefined: +		return nil, errUndefined +	} + +	arrayHeader := ArrayHeader{ +		Dimensions: src.Dimensions, +	} + +	if dt, ok := ci.DataTypeForName("json"); ok { +		arrayHeader.ElementOID = int32(dt.OID) +	} else { +		return nil, fmt.Errorf("unable to find oid for type name %v", "json") +	} + +	for i := range src.Elements { +		if src.Elements[i].Status == Null { +			arrayHeader.ContainsNull = true +			break +		} +	} + +	buf = arrayHeader.EncodeBinary(ci, buf) + +	for i := range src.Elements { +		sp := len(buf) +		buf = pgio.AppendInt32(buf, -1) + +		elemBuf, err := src.Elements[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 *JSONArray) 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 JSONArray) Value() (driver.Value, error) { +	buf, err := src.EncodeText(nil, nil) +	if err != nil { +		return nil, err +	} +	if buf == nil { +		return nil, nil +	} + +	return string(buf), nil +} diff --git a/vendor/github.com/jackc/pgtype/jsonb_array.go b/vendor/github.com/jackc/pgtype/jsonb_array.go index c4b7cd3d8..e78ad3776 100644 --- a/vendor/github.com/jackc/pgtype/jsonb_array.go +++ b/vendor/github.com/jackc/pgtype/jsonb_array.go @@ -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 +  			}  		} diff --git a/vendor/github.com/jackc/pgtype/multirange.go b/vendor/github.com/jackc/pgtype/multirange.go new file mode 100644 index 000000000..beb11f702 --- /dev/null +++ b/vendor/github.com/jackc/pgtype/multirange.go @@ -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) +	} +} diff --git a/vendor/github.com/jackc/pgtype/num_multirange.go b/vendor/github.com/jackc/pgtype/num_multirange.go new file mode 100644 index 000000000..cbabc8acb --- /dev/null +++ b/vendor/github.com/jackc/pgtype/num_multirange.go @@ -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) +} diff --git a/vendor/github.com/jackc/pgtype/numeric.go b/vendor/github.com/jackc/pgtype/numeric.go index cd057749e..1f32b36b4 100644 --- a/vendor/github.com/jackc/pgtype/numeric.go +++ b/vendor/github.com/jackc/pgtype/numeric.go @@ -1,6 +1,7 @@  package pgtype  import ( +	"bytes"  	"database/sql/driver"  	"encoding/binary"  	"fmt" @@ -375,6 +376,12 @@ func (src *Numeric) AssignTo(dst interface{}) error {  				return err  			}  			v.Set(rat) +		case *string: +			buf, err := encodeNumericText(*src, nil) +			if err != nil { +				return err +			} +			*v = string(buf)  		default:  			if nextDst, retry := GetAssignToDstType(dst); retry {  				return src.AssignTo(nextDst) @@ -792,3 +799,55 @@ func (src Numeric) Value() (driver.Value, error) {  		return nil, errUndefined  	}  } + +func encodeNumericText(n Numeric, buf []byte) (newBuf []byte, err error) { +	// if !n.Valid { +	// 	return nil, nil +	// } + +	if n.NaN { +		buf = append(buf, "NaN"...) +		return buf, nil +	} else if n.InfinityModifier == Infinity { +		buf = append(buf, "Infinity"...) +		return buf, nil +	} else if n.InfinityModifier == NegativeInfinity { +		buf = append(buf, "-Infinity"...) +		return buf, nil +	} + +	buf = append(buf, n.numberTextBytes()...) + +	return buf, nil +} + +// numberString returns a string of the number. undefined if NaN, infinite, or NULL +func (n Numeric) numberTextBytes() []byte { +	intStr := n.Int.String() +	buf := &bytes.Buffer{} +	exp := int(n.Exp) +	if exp > 0 { +		buf.WriteString(intStr) +		for i := 0; i < exp; i++ { +			buf.WriteByte('0') +		} +	} else if exp < 0 { +		if len(intStr) <= -exp { +			buf.WriteString("0.") +			leadingZeros := -exp - len(intStr) +			for i := 0; i < leadingZeros; i++ { +				buf.WriteByte('0') +			} +			buf.WriteString(intStr) +		} else if len(intStr) > -exp { +			dpPos := len(intStr) + exp +			buf.WriteString(intStr[:dpPos]) +			buf.WriteByte('.') +			buf.WriteString(intStr[dpPos:]) +		} +	} else { +		buf.WriteString(intStr) +	} + +	return buf.Bytes() +} diff --git a/vendor/github.com/jackc/pgtype/pgtype.go b/vendor/github.com/jackc/pgtype/pgtype.go index 200fb5626..addfc66fc 100644 --- a/vendor/github.com/jackc/pgtype/pgtype.go +++ b/vendor/github.com/jackc/pgtype/pgtype.go @@ -26,6 +26,7 @@ const (  	XIDOID              = 28  	CIDOID              = 29  	JSONOID             = 114 +	JSONArrayOID        = 199  	PointOID            = 600  	LsegOID             = 601  	PathOID             = 602 @@ -74,12 +75,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,10 +292,13 @@ 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: &JSONArray{}, Name: "_json", OID: JSONArrayOID})  	ci.RegisterDataType(DataType{Value: &JSONB{}, Name: "jsonb", OID: JSONBOID})  	ci.RegisterDataType(DataType{Value: &JSONBArray{}, Name: "_jsonb", OID: JSONBArrayOID})  	ci.RegisterDataType(DataType{Value: &Line{}, Name: "line", OID: LineOID}) @@ -300,6 +307,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}) @@ -527,8 +535,22 @@ type scanPlanDataTypeSQLScanner DataType  func (plan *scanPlanDataTypeSQLScanner) Scan(ci *ConnInfo, oid uint32, formatCode int16, src []byte, dst interface{}) error {  	scanner, ok := dst.(sql.Scanner)  	if !ok { -		newPlan := ci.PlanScan(oid, formatCode, dst) -		return newPlan.Scan(ci, oid, formatCode, src, dst) +		dv := reflect.ValueOf(dst) +		if dv.Kind() != reflect.Ptr || !dv.Type().Elem().Implements(scannerType) { +			newPlan := ci.PlanScan(oid, formatCode, dst) +			return newPlan.Scan(ci, oid, formatCode, src, dst) +		} +		if src == nil { +			// Ensure the pointer points to a zero version of the value +			dv.Elem().Set(reflect.Zero(dv.Type().Elem())) +			return nil +		} +		dv = dv.Elem() +		// If the pointer is to a nil pointer then set that before scanning +		if dv.Kind() == reflect.Ptr && dv.IsNil() { +			dv.Set(reflect.New(dv.Type().Elem())) +		} +		scanner = dv.Interface().(sql.Scanner)  	}  	dt := (*DataType)(plan) @@ -587,7 +609,25 @@ func (plan *scanPlanDataTypeAssignTo) Scan(ci *ConnInfo, oid uint32, formatCode  type scanPlanSQLScanner struct{}  func (scanPlanSQLScanner) Scan(ci *ConnInfo, oid uint32, formatCode int16, src []byte, dst interface{}) error { -	scanner := dst.(sql.Scanner) +	scanner, ok := dst.(sql.Scanner) +	if !ok { +		dv := reflect.ValueOf(dst) +		if dv.Kind() != reflect.Ptr || !dv.Type().Elem().Implements(scannerType) { +			newPlan := ci.PlanScan(oid, formatCode, dst) +			return newPlan.Scan(ci, oid, formatCode, src, dst) +		} +		if src == nil { +			// Ensure the pointer points to a zero version of the value +			dv.Elem().Set(reflect.Zero(dv.Type())) +			return nil +		} +		dv = dv.Elem() +		// If the pointer is to a nil pointer then set that before scanning +		if dv.Kind() == reflect.Ptr && dv.IsNil() { +			dv.Set(reflect.New(dv.Type().Elem())) +		} +		scanner = dv.Interface().(sql.Scanner) +	}  	if src == nil {  		// This is necessary because interface value []byte:nil does not equal nil:nil for the binary format path and the  		// text format path would be converted to empty string. @@ -755,6 +795,18 @@ func (scanPlanString) Scan(ci *ConnInfo, oid uint32, formatCode int16, src []byt  	return newPlan.Scan(ci, oid, formatCode, src, dst)  } +var scannerType = reflect.TypeOf((*sql.Scanner)(nil)).Elem() + +func isScanner(dst interface{}) bool { +	if _, ok := dst.(sql.Scanner); ok { +		return true +	} +	if t := reflect.TypeOf(dst); t != nil && t.Kind() == reflect.Ptr && t.Elem().Implements(scannerType) { +		return true +	} +	return false +} +  // PlanScan prepares a plan to scan a value into dst.  func (ci *ConnInfo) PlanScan(oid uint32, formatCode int16, dst interface{}) ScanPlan {  	switch formatCode { @@ -819,13 +871,13 @@ func (ci *ConnInfo) PlanScan(oid uint32, formatCode int16, dst interface{}) Scan  	}  	if dt != nil { -		if _, ok := dst.(sql.Scanner); ok { +		if isScanner(dst) {  			return (*scanPlanDataTypeSQLScanner)(dt)  		}  		return (*scanPlanDataTypeAssignTo)(dt)  	} -	if _, ok := dst.(sql.Scanner); ok { +	if isScanner(dst) {  		return scanPlanSQLScanner{}  	} @@ -873,72 +925,76 @@ 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{}, +		"_json":          &JSONArray{}, +		"_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{},  	}  } diff --git a/vendor/github.com/jackc/pgtype/record.go b/vendor/github.com/jackc/pgtype/record.go index 718c35702..5cf2c93ab 100644 --- a/vendor/github.com/jackc/pgtype/record.go +++ b/vendor/github.com/jackc/pgtype/record.go @@ -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. diff --git a/vendor/github.com/jackc/pgtype/record_array.go b/vendor/github.com/jackc/pgtype/record_array.go new file mode 100644 index 000000000..2271717a5 --- /dev/null +++ b/vendor/github.com/jackc/pgtype/record_array.go @@ -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 +} diff --git a/vendor/github.com/jackc/pgtype/timestamp.go b/vendor/github.com/jackc/pgtype/timestamp.go index 5517acb19..e043726d7 100644 --- a/vendor/github.com/jackc/pgtype/timestamp.go +++ b/vendor/github.com/jackc/pgtype/timestamp.go @@ -46,6 +46,14 @@ func (dst *Timestamp) Set(src interface{}) error {  		} else {  			return dst.Set(*value)  		} +	case string: +		return dst.DecodeText(nil, []byte(value)) +	case *string: +		if value == nil { +			*dst = Timestamp{Status: Null} +		} else { +			return dst.Set(*value) +		}  	case InfinityModifier:  		*dst = Timestamp{InfinityModifier: value, Status: Present}  	default: diff --git a/vendor/github.com/jackc/pgtype/timestamptz.go b/vendor/github.com/jackc/pgtype/timestamptz.go index 587019705..72ae4991d 100644 --- a/vendor/github.com/jackc/pgtype/timestamptz.go +++ b/vendor/github.com/jackc/pgtype/timestamptz.go @@ -48,6 +48,14 @@ func (dst *Timestamptz) Set(src interface{}) error {  		} else {  			return dst.Set(*value)  		} +	case string: +		return dst.DecodeText(nil, []byte(value)) +	case *string: +		if value == nil { +			*dst = Timestamptz{Status: Null} +		} else { +			return dst.Set(*value) +		}  	case InfinityModifier:  		*dst = Timestamptz{InfinityModifier: value, Status: Present}  	default: diff --git a/vendor/github.com/jackc/pgtype/typed_array.go.erb b/vendor/github.com/jackc/pgtype/typed_array.go.erb index 5788626b4..e8433c043 100644 --- a/vendor/github.com/jackc/pgtype/typed_array.go.erb +++ b/vendor/github.com/jackc/pgtype/typed_array.go.erb @@ -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 %> diff --git a/vendor/github.com/jackc/pgtype/typed_array_gen.sh b/vendor/github.com/jackc/pgtype/typed_array_gen.sh index ea28be077..9ec768bf1 100644 --- a/vendor/github.com/jackc/pgtype/typed_array_gen.sh +++ b/vendor/github.com/jackc/pgtype/typed_array_gen.sh @@ -1,28 +1,31 @@ -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=JSONArray pgtype_element_type=JSON go_array_types=[]string,[][]byte,[]json.RawMessage element_type_name=json typed_array.go.erb > json_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 diff --git a/vendor/github.com/jackc/pgtype/typed_multirange.go.erb b/vendor/github.com/jackc/pgtype/typed_multirange.go.erb new file mode 100644 index 000000000..84c8299fa --- /dev/null +++ b/vendor/github.com/jackc/pgtype/typed_multirange.go.erb @@ -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) +} diff --git a/vendor/github.com/jackc/pgtype/typed_multirange_gen.sh b/vendor/github.com/jackc/pgtype/typed_multirange_gen.sh new file mode 100644 index 000000000..610f40a1e --- /dev/null +++ b/vendor/github.com/jackc/pgtype/typed_multirange_gen.sh @@ -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
\ No newline at end of file diff --git a/vendor/github.com/jackc/pgtype/uuid.go b/vendor/github.com/jackc/pgtype/uuid.go index fa0be07fe..6839c052d 100644 --- a/vendor/github.com/jackc/pgtype/uuid.go +++ b/vendor/github.com/jackc/pgtype/uuid.go @@ -18,14 +18,15 @@ func (dst *UUID) Set(src interface{}) error {  		return nil  	} -	if value, ok := src.(interface{ Get() interface{} }); ok { +	switch value := src.(type) { +	case interface{ Get() interface{} }:  		value2 := value.Get()  		if value2 != value {  			return dst.Set(value2)  		} -	} - -	switch value := src.(type) { +	case fmt.Stringer: +		value2 := value.String() +		return dst.Set(value2)  	case [16]byte:  		*dst = UUID{Bytes: value, Status: Present}  	case []byte: diff --git a/vendor/github.com/jackc/pgx/v4/CHANGELOG.md b/vendor/github.com/jackc/pgx/v4/CHANGELOG.md index 4dd93b30e..e8f201295 100644 --- a/vendor/github.com/jackc/pgx/v4/CHANGELOG.md +++ b/vendor/github.com/jackc/pgx/v4/CHANGELOG.md @@ -1,3 +1,38 @@ +# 4.17.2 (September 3, 2022) + +* Fix panic when logging batch error (Tom Mòˆller) + +# 4.17.1 (August 27, 2022) + +* Upgrade puddle to v1.3.0 - fixes context failing to cancel Acquire when acquire is creating resource which was introduced in v4.17.0 (James Hartig) +* Fix atomic alignment on 32-bit platforms + +# 4.17.0 (August 6, 2022) + +* Upgrade pgconn to v1.13.0 +* Upgrade pgproto3 to v2.3.1 +* Upgrade pgtype to v1.12.0 +* Allow background pool connections to continue even if cause is canceled (James Hartig) +* Add LoggerFunc (Gabor Szabad) +* pgxpool: health check should avoid going below minConns (James Hartig) +* Add pgxpool.Conn.Hijack() +* Logging improvements (Stepan Rabotkin) + +# 4.16.1 (May 7, 2022) + +* Upgrade pgconn to v1.12.1 +* Fix explicitly prepared statements with describe statement cache mode + +# 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 diff --git a/vendor/github.com/jackc/pgx/v4/README.md b/vendor/github.com/jackc/pgx/v4/README.md index 110d4f02e..16d8f46f7 100644 --- a/vendor/github.com/jackc/pgx/v4/README.md +++ b/vendor/github.com/jackc/pgx/v4/README.md @@ -1,6 +1,11 @@  [](https://pkg.go.dev/github.com/jackc/pgx/v4)  [](https://travis-ci.org/jackc/pgx) +--- + +This is the stable `v4` release. `v5` is now in beta testing with final release expected in September. See https://github.com/jackc/pgx/issues/1273 for more information. Please consider testing `v5`. + +---  # pgx - PostgreSQL Driver and Toolkit  pgx is a pure Go driver and toolkit for PostgreSQL. @@ -98,26 +103,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 +186,11 @@ 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. + +### [https://github.com/vgarvardt/pgx-google-uuid](https://github.com/vgarvardt/pgx-google-uuid) + +Adds support for [`github.com/google/uuid`](https://github.com/google/uuid). diff --git a/vendor/github.com/jackc/pgx/v4/conn.go b/vendor/github.com/jackc/pgx/v4/conn.go index 102158ab5..854561e02 100644 --- a/vendor/github.com/jackc/pgx/v4/conn.go +++ b/vendor/github.com/jackc/pgx/v4/conn.go @@ -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 @@ -117,14 +116,14 @@ func ConnectConfig(ctx context.Context, connConfig *ConnConfig) (*Conn, error) {  // ParseConfig creates a ConnConfig from a connection string. ParseConfig handles all options that pgconn.ParseConfig  // does. In addition, it accepts the following options:  // -// 	statement_cache_capacity -// 		The maximum size of the automatic statement cache. Set to 0 to disable automatic statement caching. Default: 512. +//	statement_cache_capacity +//		The maximum size of the automatic statement cache. Set to 0 to disable automatic statement caching. Default: 512.  // -// 	statement_cache_mode -// 		Possible values: "prepare" and "describe". "prepare" will create prepared statements on the PostgreSQL server. -// 		"describe" will use the anonymous prepared statement to describe a statement without creating a statement on the -// 		server. "describe" is primarily useful when the environment does not allow prepared statements such as when -// 		running a connection pooler like PgBouncer. Default: "prepare" +//	statement_cache_mode +//		Possible values: "prepare" and "describe". "prepare" will create prepared statements on the PostgreSQL server. +//		"describe" will use the anonymous prepared statement to describe a statement without creating a statement on the +//		server. "describe" is primarily useful when the environment does not allow prepared statements such as when +//		running a connection pooler like PgBouncer. Default: "prepare"  //  //	prefer_simple_protocol  //		Possible values: "true" and "false". Use the simple protocol instead of extended protocol. Default: false @@ -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 @@ -674,7 +645,7 @@ optionLoop:  		resultFormats = c.eqb.resultFormats  	} -	if c.stmtcache != nil && c.stmtcache.Mode() == stmtcache.ModeDescribe { +	if c.stmtcache != nil && c.stmtcache.Mode() == stmtcache.ModeDescribe && !ok {  		rows.resultReader = c.pgConn.ExecParams(ctx, sql, c.eqb.paramValues, sd.ParamOIDs, c.eqb.paramFormats, resultFormats)  	} else {  		rows.resultReader = c.pgConn.ExecPrepared(ctx, sd.Name, c.eqb.paramValues, c.eqb.paramFormats, resultFormats) @@ -739,6 +710,8 @@ func (c *Conn) QueryFunc(ctx context.Context, sql string, args []interface{}, sc  // explicit transaction control statements are executed. The returned BatchResults must be closed before the connection  // is used again.  func (c *Conn) SendBatch(ctx context.Context, b *Batch) BatchResults { +	startTime := time.Now() +  	simpleProtocol := c.config.PreferSimpleProtocol  	var sb strings.Builder  	if simpleProtocol { @@ -797,24 +770,23 @@ 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 c.logBatchResults(ctx, startTime, &batchResults{ctx: ctx, conn: c, err: err})  			}  		}  		if len(sd.ParamOIDs) != len(bi.arguments) { -			return &batchResults{ctx: ctx, conn: c, err: fmt.Errorf("mismatched param and argument count")} +			return c.logBatchResults(ctx, startTime, &batchResults{ctx: ctx, conn: c, err: fmt.Errorf("mismatched param and argument count")})  		}  		args, err := convertDriverValuers(bi.arguments)  		if err != nil { -			return &batchResults{ctx: ctx, conn: c, err: err} +			return c.logBatchResults(ctx, startTime, &batchResults{ctx: ctx, conn: c, err: err})  		}  		for i := range args {  			err = c.eqb.AppendParam(c.connInfo, sd.ParamOIDs[i], args[i])  			if err != nil { -				return &batchResults{ctx: ctx, conn: c, err: err} +				return c.logBatchResults(ctx, startTime, &batchResults{ctx: ctx, conn: c, err: err})  			}  		} @@ -833,13 +805,30 @@ func (c *Conn) SendBatch(ctx context.Context, b *Batch) BatchResults {  	mrr := c.pgConn.ExecBatch(ctx, batch) -	return &batchResults{ +	return c.logBatchResults(ctx, startTime, &batchResults{  		ctx:  ctx,  		conn: c,  		mrr:  mrr,  		b:    b,  		ix:   0, +	}) +} + +func (c *Conn) logBatchResults(ctx context.Context, startTime time.Time, results *batchResults) BatchResults { +	if results.err != nil { +		if c.shouldLog(LogLevelError) { +			endTime := time.Now() +			c.log(ctx, LogLevelError, "SendBatch", map[string]interface{}{"err": results.err, "time": endTime.Sub(startTime)}) +		} +		return results  	} + +	if c.shouldLog(LogLevelInfo) { +		endTime := time.Now() +		c.log(ctx, LogLevelInfo, "SendBatch", map[string]interface{}{"batchLen": results.b.Len(), "time": endTime.Sub(startTime)}) +	} + +	return results  }  func (c *Conn) sanitizeForSimpleQuery(sql string, args ...interface{}) (string, error) { diff --git a/vendor/github.com/jackc/pgx/v4/copy_from.go b/vendor/github.com/jackc/pgx/v4/copy_from.go index 3494e28f9..49139d050 100644 --- a/vendor/github.com/jackc/pgx/v4/copy_from.go +++ b/vendor/github.com/jackc/pgx/v4/copy_from.go @@ -153,13 +153,13 @@ func (ct *copyFrom) run(ctx context.Context) (int64, error) {  	<-doneChan  	rowsAffected := commandTag.RowsAffected() +	endTime := time.Now()  	if err == nil {  		if ct.conn.shouldLog(LogLevelInfo) { -			endTime := time.Now()  			ct.conn.log(ctx, LogLevelInfo, "CopyFrom", map[string]interface{}{"tableName": ct.tableName, "columnNames": ct.columnNames, "time": endTime.Sub(startTime), "rowCount": rowsAffected})  		}  	} else if ct.conn.shouldLog(LogLevelError) { -		ct.conn.log(ctx, LogLevelError, "CopyFrom", map[string]interface{}{"err": err, "tableName": ct.tableName, "columnNames": ct.columnNames}) +		ct.conn.log(ctx, LogLevelError, "CopyFrom", map[string]interface{}{"err": err, "tableName": ct.tableName, "columnNames": ct.columnNames, "time": endTime.Sub(startTime)})  	}  	return rowsAffected, err diff --git a/vendor/github.com/jackc/pgx/v4/internal/sanitize/sanitize.go b/vendor/github.com/jackc/pgx/v4/internal/sanitize/sanitize.go index 2dba3b810..a7a94e93e 100644 --- a/vendor/github.com/jackc/pgx/v4/internal/sanitize/sanitize.go +++ b/vendor/github.com/jackc/pgx/v4/internal/sanitize/sanitize.go @@ -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 { diff --git a/vendor/github.com/jackc/pgx/v4/large_objects.go b/vendor/github.com/jackc/pgx/v4/large_objects.go index 5255a3b48..c238ab9c2 100644 --- a/vendor/github.com/jackc/pgx/v4/large_objects.go +++ b/vendor/github.com/jackc/pgx/v4/large_objects.go @@ -56,10 +56,10 @@ func (o *LargeObjects) Unlink(ctx context.Context, oid uint32) error {  // A LargeObject is a large object stored on the server. It is only valid within the transaction that it was initialized  // in. It uses the context it was initialized with for all operations. It implements these interfaces:  // -//    io.Writer -//    io.Reader -//    io.Seeker -//    io.Closer +//	io.Writer +//	io.Reader +//	io.Seeker +//	io.Closer  type LargeObject struct {  	ctx context.Context  	tx  Tx @@ -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 diff --git a/vendor/github.com/jackc/pgx/v4/logger.go b/vendor/github.com/jackc/pgx/v4/logger.go index 89fd5af51..41f8b7e87 100644 --- a/vendor/github.com/jackc/pgx/v4/logger.go +++ b/vendor/github.com/jackc/pgx/v4/logger.go @@ -47,9 +47,18 @@ type Logger interface {  	Log(ctx context.Context, level LogLevel, msg string, data map[string]interface{})  } +// LoggerFunc is a wrapper around a function to satisfy the pgx.Logger interface +type LoggerFunc func(ctx context.Context, level LogLevel, msg string, data map[string]interface{}) + +// Log delegates the logging request to the wrapped function +func (f LoggerFunc) Log(ctx context.Context, level LogLevel, msg string, data map[string]interface{}) { +	f(ctx, level, msg, data) +} +  // LogLevelFromString converts log level string to constant  //  // Valid levels: +//  //	trace  //	debug  //	info diff --git a/vendor/github.com/jackc/pgx/v4/rows.go b/vendor/github.com/jackc/pgx/v4/rows.go index 271c6e527..4749ead99 100644 --- a/vendor/github.com/jackc/pgx/v4/rows.go +++ b/vendor/github.com/jackc/pgx/v4/rows.go @@ -143,14 +143,15 @@ func (rows *connRows) Close() {  	}  	if rows.logger != nil { +		endTime := time.Now() +  		if rows.err == nil {  			if rows.logger.shouldLog(LogLevelInfo) { -				endTime := time.Now()  				rows.logger.log(rows.ctx, LogLevelInfo, "Query", map[string]interface{}{"sql": rows.sql, "args": logQueryArgs(rows.args), "time": endTime.Sub(rows.startTime), "rowCount": rows.rowCount})  			}  		} else {  			if rows.logger.shouldLog(LogLevelError) { -				rows.logger.log(rows.ctx, LogLevelError, "Query", map[string]interface{}{"err": rows.err, "sql": rows.sql, "args": logQueryArgs(rows.args)}) +				rows.logger.log(rows.ctx, LogLevelError, "Query", map[string]interface{}{"err": rows.err, "sql": rows.sql, "time": endTime.Sub(rows.startTime), "args": logQueryArgs(rows.args)})  			}  			if rows.err != nil && rows.conn.stmtcache != nil {  				rows.conn.stmtcache.StatementErrored(rows.sql, rows.err) diff --git a/vendor/github.com/jackc/pgx/v4/stdlib/sql.go b/vendor/github.com/jackc/pgx/v4/stdlib/sql.go index fa81e73d5..da377ecee 100644 --- a/vendor/github.com/jackc/pgx/v4/stdlib/sql.go +++ b/vendor/github.com/jackc/pgx/v4/stdlib/sql.go @@ -163,7 +163,7 @@ func RandomizeHostOrderFunc(ctx context.Context, connConfig *pgx.ConnConfig) err  	return nil  } -func OpenDB(config pgx.ConnConfig, opts ...OptionOpenDB) *sql.DB { +func GetConnector(config pgx.ConnConfig, opts ...OptionOpenDB) driver.Connector {  	c := connector{  		ConnConfig:    config,  		BeforeConnect: func(context.Context, *pgx.ConnConfig) error { return nil }, // noop before connect by default @@ -175,7 +175,11 @@ func OpenDB(config pgx.ConnConfig, opts ...OptionOpenDB) *sql.DB {  	for _, opt := range opts {  		opt(&c)  	} +	return c +} +func OpenDB(config pgx.ConnConfig, opts ...OptionOpenDB) *sql.DB { +	c := GetConnector(config, opts...)  	return sql.OpenDB(c)  } diff --git a/vendor/github.com/jackc/pgx/v4/tx.go b/vendor/github.com/jackc/pgx/v4/tx.go index 1971ed673..2914ada7d 100644 --- a/vendor/github.com/jackc/pgx/v4/tx.go +++ b/vendor/github.com/jackc/pgx/v4/tx.go @@ -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()  } | 
