mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-29 10:57:44 +09:00 
			
		
		
		
	Update vendor github.com/lib/pq (#2752)
This commit is contained in:
		
				
					committed by
					
						 Lunny Xiao
						Lunny Xiao
					
				
			
			
				
	
			
			
			
						parent
						
							d4c9399576
						
					
				
				
					commit
					2112eb8741
				
			
							
								
								
									
										2
									
								
								vendor/github.com/lib/pq/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/lib/pq/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,6 +1,6 @@ | |||||||
| # pq - A pure Go postgres driver for Go's database/sql package | # pq - A pure Go postgres driver for Go's database/sql package | ||||||
|  |  | ||||||
| [](https://travis-ci.org/lib/pq) | [](https://travis-ci.org/lib/pq) | ||||||
|  |  | ||||||
| ## Install | ## Install | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										51
									
								
								vendor/github.com/lib/pq/array.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										51
									
								
								vendor/github.com/lib/pq/array.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -13,7 +13,7 @@ import ( | |||||||
|  |  | ||||||
| var typeByteSlice = reflect.TypeOf([]byte{}) | var typeByteSlice = reflect.TypeOf([]byte{}) | ||||||
| var typeDriverValuer = reflect.TypeOf((*driver.Valuer)(nil)).Elem() | var typeDriverValuer = reflect.TypeOf((*driver.Valuer)(nil)).Elem() | ||||||
| var typeSqlScanner = reflect.TypeOf((*sql.Scanner)(nil)).Elem() | var typeSQLScanner = reflect.TypeOf((*sql.Scanner)(nil)).Elem() | ||||||
|  |  | ||||||
| // Array returns the optimal driver.Valuer and sql.Scanner for an array or | // Array returns the optimal driver.Valuer and sql.Scanner for an array or | ||||||
| // slice of any dimension. | // slice of any dimension. | ||||||
| @@ -70,6 +70,9 @@ func (a *BoolArray) Scan(src interface{}) error { | |||||||
| 		return a.scanBytes(src) | 		return a.scanBytes(src) | ||||||
| 	case string: | 	case string: | ||||||
| 		return a.scanBytes([]byte(src)) | 		return a.scanBytes([]byte(src)) | ||||||
|  | 	case nil: | ||||||
|  | 		*a = nil | ||||||
|  | 		return nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return fmt.Errorf("pq: cannot convert %T to BoolArray", src) | 	return fmt.Errorf("pq: cannot convert %T to BoolArray", src) | ||||||
| @@ -80,7 +83,7 @@ func (a *BoolArray) scanBytes(src []byte) error { | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	if len(elems) == 0 { | 	if *a != nil && len(elems) == 0 { | ||||||
| 		*a = (*a)[:0] | 		*a = (*a)[:0] | ||||||
| 	} else { | 	} else { | ||||||
| 		b := make(BoolArray, len(elems)) | 		b := make(BoolArray, len(elems)) | ||||||
| @@ -141,6 +144,9 @@ func (a *ByteaArray) Scan(src interface{}) error { | |||||||
| 		return a.scanBytes(src) | 		return a.scanBytes(src) | ||||||
| 	case string: | 	case string: | ||||||
| 		return a.scanBytes([]byte(src)) | 		return a.scanBytes([]byte(src)) | ||||||
|  | 	case nil: | ||||||
|  | 		*a = nil | ||||||
|  | 		return nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return fmt.Errorf("pq: cannot convert %T to ByteaArray", src) | 	return fmt.Errorf("pq: cannot convert %T to ByteaArray", src) | ||||||
| @@ -151,7 +157,7 @@ func (a *ByteaArray) scanBytes(src []byte) error { | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	if len(elems) == 0 { | 	if *a != nil && len(elems) == 0 { | ||||||
| 		*a = (*a)[:0] | 		*a = (*a)[:0] | ||||||
| 	} else { | 	} else { | ||||||
| 		b := make(ByteaArray, len(elems)) | 		b := make(ByteaArray, len(elems)) | ||||||
| @@ -210,6 +216,9 @@ func (a *Float64Array) Scan(src interface{}) error { | |||||||
| 		return a.scanBytes(src) | 		return a.scanBytes(src) | ||||||
| 	case string: | 	case string: | ||||||
| 		return a.scanBytes([]byte(src)) | 		return a.scanBytes([]byte(src)) | ||||||
|  | 	case nil: | ||||||
|  | 		*a = nil | ||||||
|  | 		return nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return fmt.Errorf("pq: cannot convert %T to Float64Array", src) | 	return fmt.Errorf("pq: cannot convert %T to Float64Array", src) | ||||||
| @@ -220,7 +229,7 @@ func (a *Float64Array) scanBytes(src []byte) error { | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	if len(elems) == 0 { | 	if *a != nil && len(elems) == 0 { | ||||||
| 		*a = (*a)[:0] | 		*a = (*a)[:0] | ||||||
| 	} else { | 	} else { | ||||||
| 		b := make(Float64Array, len(elems)) | 		b := make(Float64Array, len(elems)) | ||||||
| @@ -269,7 +278,7 @@ func (GenericArray) evaluateDestination(rt reflect.Type) (reflect.Type, func([]b | |||||||
| 	// TODO calculate the assign function for other types | 	// TODO calculate the assign function for other types | ||||||
| 	// TODO repeat this section on the element type of arrays or slices (multidimensional) | 	// TODO repeat this section on the element type of arrays or slices (multidimensional) | ||||||
| 	{ | 	{ | ||||||
| 		if reflect.PtrTo(rt).Implements(typeSqlScanner) { | 		if reflect.PtrTo(rt).Implements(typeSQLScanner) { | ||||||
| 			// dest is always addressable because it is an element of a slice. | 			// dest is always addressable because it is an element of a slice. | ||||||
| 			assign = func(src []byte, dest reflect.Value) (err error) { | 			assign = func(src []byte, dest reflect.Value) (err error) { | ||||||
| 				ss := dest.Addr().Interface().(sql.Scanner) | 				ss := dest.Addr().Interface().(sql.Scanner) | ||||||
| @@ -320,6 +329,11 @@ func (a GenericArray) Scan(src interface{}) error { | |||||||
| 		return a.scanBytes(src, dv) | 		return a.scanBytes(src, dv) | ||||||
| 	case string: | 	case string: | ||||||
| 		return a.scanBytes([]byte(src), dv) | 		return a.scanBytes([]byte(src), dv) | ||||||
|  | 	case nil: | ||||||
|  | 		if dv.Kind() == reflect.Slice { | ||||||
|  | 			dv.Set(reflect.Zero(dv.Type())) | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return fmt.Errorf("pq: cannot convert %T to %s", src, dv.Type()) | 	return fmt.Errorf("pq: cannot convert %T to %s", src, dv.Type()) | ||||||
| @@ -386,7 +400,13 @@ func (a GenericArray) Value() (driver.Value, error) { | |||||||
|  |  | ||||||
| 	rv := reflect.ValueOf(a.A) | 	rv := reflect.ValueOf(a.A) | ||||||
|  |  | ||||||
| 	if k := rv.Kind(); k != reflect.Array && k != reflect.Slice { | 	switch rv.Kind() { | ||||||
|  | 	case reflect.Slice: | ||||||
|  | 		if rv.IsNil() { | ||||||
|  | 			return nil, nil | ||||||
|  | 		} | ||||||
|  | 	case reflect.Array: | ||||||
|  | 	default: | ||||||
| 		return nil, fmt.Errorf("pq: Unable to convert %T to array", a.A) | 		return nil, fmt.Errorf("pq: Unable to convert %T to array", a.A) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -412,6 +432,9 @@ func (a *Int64Array) Scan(src interface{}) error { | |||||||
| 		return a.scanBytes(src) | 		return a.scanBytes(src) | ||||||
| 	case string: | 	case string: | ||||||
| 		return a.scanBytes([]byte(src)) | 		return a.scanBytes([]byte(src)) | ||||||
|  | 	case nil: | ||||||
|  | 		*a = nil | ||||||
|  | 		return nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return fmt.Errorf("pq: cannot convert %T to Int64Array", src) | 	return fmt.Errorf("pq: cannot convert %T to Int64Array", src) | ||||||
| @@ -422,7 +445,7 @@ func (a *Int64Array) scanBytes(src []byte) error { | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	if len(elems) == 0 { | 	if *a != nil && len(elems) == 0 { | ||||||
| 		*a = (*a)[:0] | 		*a = (*a)[:0] | ||||||
| 	} else { | 	} else { | ||||||
| 		b := make(Int64Array, len(elems)) | 		b := make(Int64Array, len(elems)) | ||||||
| @@ -470,6 +493,9 @@ func (a *StringArray) Scan(src interface{}) error { | |||||||
| 		return a.scanBytes(src) | 		return a.scanBytes(src) | ||||||
| 	case string: | 	case string: | ||||||
| 		return a.scanBytes([]byte(src)) | 		return a.scanBytes([]byte(src)) | ||||||
|  | 	case nil: | ||||||
|  | 		*a = nil | ||||||
|  | 		return nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return fmt.Errorf("pq: cannot convert %T to StringArray", src) | 	return fmt.Errorf("pq: cannot convert %T to StringArray", src) | ||||||
| @@ -480,7 +506,7 @@ func (a *StringArray) scanBytes(src []byte) error { | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	if len(elems) == 0 { | 	if *a != nil && len(elems) == 0 { | ||||||
| 		*a = (*a)[:0] | 		*a = (*a)[:0] | ||||||
| 	} else { | 	} else { | ||||||
| 		b := make(StringArray, len(elems)) | 		b := make(StringArray, len(elems)) | ||||||
| @@ -561,7 +587,7 @@ func appendArrayElement(b []byte, rv reflect.Value) ([]byte, string, error) { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	var del string = "," | 	var del = "," | ||||||
| 	var err error | 	var err error | ||||||
| 	var iv interface{} = rv.Interface() | 	var iv interface{} = rv.Interface() | ||||||
|  |  | ||||||
| @@ -639,6 +665,9 @@ Element: | |||||||
| 	for i < len(src) { | 	for i < len(src) { | ||||||
| 		switch src[i] { | 		switch src[i] { | ||||||
| 		case '{': | 		case '{': | ||||||
|  | 			if depth == len(dims) { | ||||||
|  | 				break Element | ||||||
|  | 			} | ||||||
| 			depth++ | 			depth++ | ||||||
| 			dims[depth-1] = 0 | 			dims[depth-1] = 0 | ||||||
| 			i++ | 			i++ | ||||||
| @@ -680,11 +709,11 @@ Element: | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for i < len(src) { | 	for i < len(src) { | ||||||
| 		if bytes.HasPrefix(src[i:], del) { | 		if bytes.HasPrefix(src[i:], del) && depth > 0 { | ||||||
| 			dims[depth-1]++ | 			dims[depth-1]++ | ||||||
| 			i += len(del) | 			i += len(del) | ||||||
| 			goto Element | 			goto Element | ||||||
| 		} else if src[i] == '}' { | 		} else if src[i] == '}' && depth > 0 { | ||||||
| 			dims[depth-1]++ | 			dims[depth-1]++ | ||||||
| 			depth-- | 			depth-- | ||||||
| 			i++ | 			i++ | ||||||
|   | |||||||
							
								
								
									
										444
									
								
								vendor/github.com/lib/pq/conn.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										444
									
								
								vendor/github.com/lib/pq/conn.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -3,15 +3,12 @@ package pq | |||||||
| import ( | import ( | ||||||
| 	"bufio" | 	"bufio" | ||||||
| 	"crypto/md5" | 	"crypto/md5" | ||||||
| 	"crypto/tls" |  | ||||||
| 	"crypto/x509" |  | ||||||
| 	"database/sql" | 	"database/sql" | ||||||
| 	"database/sql/driver" | 	"database/sql/driver" | ||||||
| 	"encoding/binary" | 	"encoding/binary" | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"io" | 	"io" | ||||||
| 	"io/ioutil" |  | ||||||
| 	"net" | 	"net" | ||||||
| 	"os" | 	"os" | ||||||
| 	"os/user" | 	"os/user" | ||||||
| @@ -30,22 +27,22 @@ var ( | |||||||
| 	ErrNotSupported              = errors.New("pq: Unsupported command") | 	ErrNotSupported              = errors.New("pq: Unsupported command") | ||||||
| 	ErrInFailedTransaction       = errors.New("pq: Could not complete operation in a failed transaction") | 	ErrInFailedTransaction       = errors.New("pq: Could not complete operation in a failed transaction") | ||||||
| 	ErrSSLNotSupported           = errors.New("pq: SSL is not enabled on the server") | 	ErrSSLNotSupported           = errors.New("pq: SSL is not enabled on the server") | ||||||
| 	ErrSSLKeyHasWorldPermissions = errors.New("pq: Private key file has group or world access. Permissions should be u=rw (0600) or less.") | 	ErrSSLKeyHasWorldPermissions = errors.New("pq: Private key file has group or world access. Permissions should be u=rw (0600) or less") | ||||||
| 	ErrCouldNotDetectUsername    = errors.New("pq: Could not detect default username. Please provide one explicitly.") | 	ErrCouldNotDetectUsername    = errors.New("pq: Could not detect default username. Please provide one explicitly") | ||||||
|  |  | ||||||
| 	errUnexpectedReady = errors.New("unexpected ReadyForQuery") | 	errUnexpectedReady = errors.New("unexpected ReadyForQuery") | ||||||
| 	errNoRowsAffected  = errors.New("no RowsAffected available after the empty statement") | 	errNoRowsAffected  = errors.New("no RowsAffected available after the empty statement") | ||||||
| 	errNoLastInsertId  = errors.New("no LastInsertId available after the empty statement") | 	errNoLastInsertID  = errors.New("no LastInsertId available after the empty statement") | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type drv struct{} | type Driver struct{} | ||||||
|  |  | ||||||
| func (d *drv) Open(name string) (driver.Conn, error) { | func (d *Driver) Open(name string) (driver.Conn, error) { | ||||||
| 	return Open(name) | 	return Open(name) | ||||||
| } | } | ||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
| 	sql.Register("postgres", &drv{}) | 	sql.Register("postgres", &Driver{}) | ||||||
| } | } | ||||||
|  |  | ||||||
| type parameterStatus struct { | type parameterStatus struct { | ||||||
| @@ -101,6 +98,15 @@ type conn struct { | |||||||
| 	namei     int | 	namei     int | ||||||
| 	scratch   [512]byte | 	scratch   [512]byte | ||||||
| 	txnStatus transactionStatus | 	txnStatus transactionStatus | ||||||
|  | 	txnFinish func() | ||||||
|  |  | ||||||
|  | 	// Save connection arguments to use during CancelRequest. | ||||||
|  | 	dialer Dialer | ||||||
|  | 	opts   values | ||||||
|  |  | ||||||
|  | 	// Cancellation key data for use with CancelRequest messages. | ||||||
|  | 	processID int | ||||||
|  | 	secretKey int | ||||||
|  |  | ||||||
| 	parameterStatus parameterStatus | 	parameterStatus parameterStatus | ||||||
|  |  | ||||||
| @@ -125,9 +131,9 @@ type conn struct { | |||||||
| } | } | ||||||
|  |  | ||||||
| // Handle driver-side settings in parsed connection string. | // Handle driver-side settings in parsed connection string. | ||||||
| func (c *conn) handleDriverSettings(o values) (err error) { | func (cn *conn) handleDriverSettings(o values) (err error) { | ||||||
| 	boolSetting := func(key string, val *bool) error { | 	boolSetting := func(key string, val *bool) error { | ||||||
| 		if value := o.Get(key); value != "" { | 		if value, ok := o[key]; ok { | ||||||
| 			if value == "yes" { | 			if value == "yes" { | ||||||
| 				*val = true | 				*val = true | ||||||
| 			} else if value == "no" { | 			} else if value == "no" { | ||||||
| @@ -139,32 +145,32 @@ func (c *conn) handleDriverSettings(o values) (err error) { | |||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	err = boolSetting("disable_prepared_binary_result", &c.disablePreparedBinaryResult) | 	err = boolSetting("disable_prepared_binary_result", &cn.disablePreparedBinaryResult) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	err = boolSetting("binary_parameters", &c.binaryParameters) | 	return boolSetting("binary_parameters", &cn.binaryParameters) | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (c *conn) handlePgpass(o values) { | func (cn *conn) handlePgpass(o values) { | ||||||
| 	// if a password was supplied, do not process .pgpass | 	// if a password was supplied, do not process .pgpass | ||||||
| 	_, ok := o["password"] | 	if _, ok := o["password"]; ok { | ||||||
| 	if ok { |  | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	filename := os.Getenv("PGPASSFILE") | 	filename := os.Getenv("PGPASSFILE") | ||||||
| 	if filename == "" { | 	if filename == "" { | ||||||
| 		// XXX this code doesn't work on Windows where the default filename is | 		// XXX this code doesn't work on Windows where the default filename is | ||||||
| 		// XXX %APPDATA%\postgresql\pgpass.conf | 		// XXX %APPDATA%\postgresql\pgpass.conf | ||||||
| 		user, err := user.Current() | 		// Prefer $HOME over user.Current due to glibc bug: golang.org/issue/13470 | ||||||
| 		if err != nil { | 		userHome := os.Getenv("HOME") | ||||||
| 			return | 		if userHome == "" { | ||||||
|  | 			user, err := user.Current() | ||||||
|  | 			if err != nil { | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			userHome = user.HomeDir | ||||||
| 		} | 		} | ||||||
| 		filename = filepath.Join(user.HomeDir, ".pgpass") | 		filename = filepath.Join(userHome, ".pgpass") | ||||||
| 	} | 	} | ||||||
| 	fileinfo, err := os.Stat(filename) | 	fileinfo, err := os.Stat(filename) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -181,11 +187,11 @@ func (c *conn) handlePgpass(o values) { | |||||||
| 	} | 	} | ||||||
| 	defer file.Close() | 	defer file.Close() | ||||||
| 	scanner := bufio.NewScanner(io.Reader(file)) | 	scanner := bufio.NewScanner(io.Reader(file)) | ||||||
| 	hostname := o.Get("host") | 	hostname := o["host"] | ||||||
| 	ntw, _ := network(o) | 	ntw, _ := network(o) | ||||||
| 	port := o.Get("port") | 	port := o["port"] | ||||||
| 	db := o.Get("dbname") | 	db := o["dbname"] | ||||||
| 	username := o.Get("user") | 	username := o["user"] | ||||||
| 	// From: https://github.com/tg/pgpass/blob/master/reader.go | 	// From: https://github.com/tg/pgpass/blob/master/reader.go | ||||||
| 	getFields := func(s string) []string { | 	getFields := func(s string) []string { | ||||||
| 		fs := make([]string, 0, 5) | 		fs := make([]string, 0, 5) | ||||||
| @@ -224,10 +230,10 @@ func (c *conn) handlePgpass(o values) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (c *conn) writeBuf(b byte) *writeBuf { | func (cn *conn) writeBuf(b byte) *writeBuf { | ||||||
| 	c.scratch[0] = b | 	cn.scratch[0] = b | ||||||
| 	return &writeBuf{ | 	return &writeBuf{ | ||||||
| 		buf: c.scratch[:5], | 		buf: cn.scratch[:5], | ||||||
| 		pos: 1, | 		pos: 1, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -250,13 +256,13 @@ func DialOpen(d Dialer, name string) (_ driver.Conn, err error) { | |||||||
| 	// * Very low precedence defaults applied in every situation | 	// * Very low precedence defaults applied in every situation | ||||||
| 	// * Environment variables | 	// * Environment variables | ||||||
| 	// * Explicitly passed connection information | 	// * Explicitly passed connection information | ||||||
| 	o.Set("host", "localhost") | 	o["host"] = "localhost" | ||||||
| 	o.Set("port", "5432") | 	o["port"] = "5432" | ||||||
| 	// N.B.: Extra float digits should be set to 3, but that breaks | 	// N.B.: Extra float digits should be set to 3, but that breaks | ||||||
| 	// Postgres 8.4 and older, where the max is 2. | 	// Postgres 8.4 and older, where the max is 2. | ||||||
| 	o.Set("extra_float_digits", "2") | 	o["extra_float_digits"] = "2" | ||||||
| 	for k, v := range parseEnviron(os.Environ()) { | 	for k, v := range parseEnviron(os.Environ()) { | ||||||
| 		o.Set(k, v) | 		o[k] = v | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if strings.HasPrefix(name, "postgres://") || strings.HasPrefix(name, "postgresql://") { | 	if strings.HasPrefix(name, "postgres://") || strings.HasPrefix(name, "postgresql://") { | ||||||
| @@ -271,9 +277,9 @@ func DialOpen(d Dialer, name string) (_ driver.Conn, err error) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Use the "fallback" application name if necessary | 	// Use the "fallback" application name if necessary | ||||||
| 	if fallback := o.Get("fallback_application_name"); fallback != "" { | 	if fallback, ok := o["fallback_application_name"]; ok { | ||||||
| 		if !o.Isset("application_name") { | 		if _, ok := o["application_name"]; !ok { | ||||||
| 			o.Set("application_name", fallback) | 			o["application_name"] = fallback | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -284,33 +290,35 @@ func DialOpen(d Dialer, name string) (_ driver.Conn, err error) { | |||||||
| 	// parsing its value is not worth it.  Instead, we always explicitly send | 	// parsing its value is not worth it.  Instead, we always explicitly send | ||||||
| 	// client_encoding as a separate run-time parameter, which should override | 	// client_encoding as a separate run-time parameter, which should override | ||||||
| 	// anything set in options. | 	// anything set in options. | ||||||
| 	if enc := o.Get("client_encoding"); enc != "" && !isUTF8(enc) { | 	if enc, ok := o["client_encoding"]; ok && !isUTF8(enc) { | ||||||
| 		return nil, errors.New("client_encoding must be absent or 'UTF8'") | 		return nil, errors.New("client_encoding must be absent or 'UTF8'") | ||||||
| 	} | 	} | ||||||
| 	o.Set("client_encoding", "UTF8") | 	o["client_encoding"] = "UTF8" | ||||||
| 	// DateStyle needs a similar treatment. | 	// DateStyle needs a similar treatment. | ||||||
| 	if datestyle := o.Get("datestyle"); datestyle != "" { | 	if datestyle, ok := o["datestyle"]; ok { | ||||||
| 		if datestyle != "ISO, MDY" { | 		if datestyle != "ISO, MDY" { | ||||||
| 			panic(fmt.Sprintf("setting datestyle must be absent or %v; got %v", | 			panic(fmt.Sprintf("setting datestyle must be absent or %v; got %v", | ||||||
| 				"ISO, MDY", datestyle)) | 				"ISO, MDY", datestyle)) | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		o.Set("datestyle", "ISO, MDY") | 		o["datestyle"] = "ISO, MDY" | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// If a user is not provided by any other means, the last | 	// If a user is not provided by any other means, the last | ||||||
| 	// resort is to use the current operating system provided user | 	// resort is to use the current operating system provided user | ||||||
| 	// name. | 	// name. | ||||||
| 	if o.Get("user") == "" { | 	if _, ok := o["user"]; !ok { | ||||||
| 		u, err := userCurrent() | 		u, err := userCurrent() | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, err | 			return nil, err | ||||||
| 		} else { |  | ||||||
| 			o.Set("user", u) |  | ||||||
| 		} | 		} | ||||||
|  | 		o["user"] = u | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	cn := &conn{} | 	cn := &conn{ | ||||||
|  | 		opts:   o, | ||||||
|  | 		dialer: d, | ||||||
|  | 	} | ||||||
| 	err = cn.handleDriverSettings(o) | 	err = cn.handleDriverSettings(o) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| @@ -326,7 +334,7 @@ func DialOpen(d Dialer, name string) (_ driver.Conn, err error) { | |||||||
| 	cn.startup(o) | 	cn.startup(o) | ||||||
|  |  | ||||||
| 	// reset the deadline, in case one was set (see dial) | 	// reset the deadline, in case one was set (see dial) | ||||||
| 	if timeout := o.Get("connect_timeout"); timeout != "" && timeout != "0" { | 	if timeout, ok := o["connect_timeout"]; ok && timeout != "0" { | ||||||
| 		err = cn.c.SetDeadline(time.Time{}) | 		err = cn.c.SetDeadline(time.Time{}) | ||||||
| 	} | 	} | ||||||
| 	return cn, err | 	return cn, err | ||||||
| @@ -340,7 +348,7 @@ func dial(d Dialer, o values) (net.Conn, error) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Zero or not specified means wait indefinitely. | 	// Zero or not specified means wait indefinitely. | ||||||
| 	if timeout := o.Get("connect_timeout"); timeout != "" && timeout != "0" { | 	if timeout, ok := o["connect_timeout"]; ok && timeout != "0" { | ||||||
| 		seconds, err := strconv.ParseInt(timeout, 10, 0) | 		seconds, err := strconv.ParseInt(timeout, 10, 0) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, fmt.Errorf("invalid value for parameter connect_timeout: %s", err) | 			return nil, fmt.Errorf("invalid value for parameter connect_timeout: %s", err) | ||||||
| @@ -362,31 +370,18 @@ func dial(d Dialer, o values) (net.Conn, error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func network(o values) (string, string) { | func network(o values) (string, string) { | ||||||
| 	host := o.Get("host") | 	host := o["host"] | ||||||
|  |  | ||||||
| 	if strings.HasPrefix(host, "/") { | 	if strings.HasPrefix(host, "/") { | ||||||
| 		sockPath := path.Join(host, ".s.PGSQL."+o.Get("port")) | 		sockPath := path.Join(host, ".s.PGSQL."+o["port"]) | ||||||
| 		return "unix", sockPath | 		return "unix", sockPath | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return "tcp", net.JoinHostPort(host, o.Get("port")) | 	return "tcp", net.JoinHostPort(host, o["port"]) | ||||||
| } | } | ||||||
|  |  | ||||||
| type values map[string]string | type values map[string]string | ||||||
|  |  | ||||||
| func (vs values) Set(k, v string) { |  | ||||||
| 	vs[k] = v |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (vs values) Get(k string) (v string) { |  | ||||||
| 	return vs[k] |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (vs values) Isset(k string) bool { |  | ||||||
| 	_, ok := vs[k] |  | ||||||
| 	return ok |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // scanner implements a tokenizer for libpq-style option strings. | // scanner implements a tokenizer for libpq-style option strings. | ||||||
| type scanner struct { | type scanner struct { | ||||||
| 	s []rune | 	s []rune | ||||||
| @@ -457,7 +452,7 @@ func parseOpts(name string, o values) error { | |||||||
| 		// Skip any whitespace after the = | 		// Skip any whitespace after the = | ||||||
| 		if r, ok = s.SkipSpaces(); !ok { | 		if r, ok = s.SkipSpaces(); !ok { | ||||||
| 			// If we reach the end here, the last value is just an empty string as per libpq. | 			// If we reach the end here, the last value is just an empty string as per libpq. | ||||||
| 			o.Set(string(keyRunes), "") | 			o[string(keyRunes)] = "" | ||||||
| 			break | 			break | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| @@ -492,7 +487,7 @@ func parseOpts(name string, o values) error { | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		o.Set(string(keyRunes), string(valRunes)) | 		o[string(keyRunes)] = string(valRunes) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| @@ -511,13 +506,17 @@ func (cn *conn) checkIsInTransaction(intxn bool) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (cn *conn) Begin() (_ driver.Tx, err error) { | func (cn *conn) Begin() (_ driver.Tx, err error) { | ||||||
|  | 	return cn.begin("") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (cn *conn) begin(mode string) (_ driver.Tx, err error) { | ||||||
| 	if cn.bad { | 	if cn.bad { | ||||||
| 		return nil, driver.ErrBadConn | 		return nil, driver.ErrBadConn | ||||||
| 	} | 	} | ||||||
| 	defer cn.errRecover(&err) | 	defer cn.errRecover(&err) | ||||||
|  |  | ||||||
| 	cn.checkIsInTransaction(false) | 	cn.checkIsInTransaction(false) | ||||||
| 	_, commandTag, err := cn.simpleExec("BEGIN") | 	_, commandTag, err := cn.simpleExec("BEGIN" + mode) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| @@ -532,7 +531,14 @@ func (cn *conn) Begin() (_ driver.Tx, err error) { | |||||||
| 	return cn, nil | 	return cn, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (cn *conn) closeTxn() { | ||||||
|  | 	if finish := cn.txnFinish; finish != nil { | ||||||
|  | 		finish() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func (cn *conn) Commit() (err error) { | func (cn *conn) Commit() (err error) { | ||||||
|  | 	defer cn.closeTxn() | ||||||
| 	if cn.bad { | 	if cn.bad { | ||||||
| 		return driver.ErrBadConn | 		return driver.ErrBadConn | ||||||
| 	} | 	} | ||||||
| @@ -568,6 +574,7 @@ func (cn *conn) Commit() (err error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (cn *conn) Rollback() (err error) { | func (cn *conn) Rollback() (err error) { | ||||||
|  | 	defer cn.closeTxn() | ||||||
| 	if cn.bad { | 	if cn.bad { | ||||||
| 		return driver.ErrBadConn | 		return driver.ErrBadConn | ||||||
| 	} | 	} | ||||||
| @@ -647,6 +654,12 @@ func (cn *conn) simpleQuery(q string) (res *rows, err error) { | |||||||
| 					cn: cn, | 					cn: cn, | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
|  | 			// Set the result and tag to the last command complete if there wasn't a | ||||||
|  | 			// query already run. Although queries usually return from here and cede | ||||||
|  | 			// control to Next, a query with zero results does not. | ||||||
|  | 			if t == 'C' && res.colNames == nil { | ||||||
|  | 				res.result, res.tag = cn.parseComplete(r.string()) | ||||||
|  | 			} | ||||||
| 			res.done = true | 			res.done = true | ||||||
| 		case 'Z': | 		case 'Z': | ||||||
| 			cn.processReadyForQuery(r) | 			cn.processReadyForQuery(r) | ||||||
| @@ -685,7 +698,7 @@ var emptyRows noRows | |||||||
| var _ driver.Result = noRows{} | var _ driver.Result = noRows{} | ||||||
|  |  | ||||||
| func (noRows) LastInsertId() (int64, error) { | func (noRows) LastInsertId() (int64, error) { | ||||||
| 	return 0, errNoLastInsertId | 	return 0, errNoLastInsertID | ||||||
| } | } | ||||||
|  |  | ||||||
| func (noRows) RowsAffected() (int64, error) { | func (noRows) RowsAffected() (int64, error) { | ||||||
| @@ -694,7 +707,7 @@ func (noRows) RowsAffected() (int64, error) { | |||||||
|  |  | ||||||
| // Decides which column formats to use for a prepared statement.  The input is | // Decides which column formats to use for a prepared statement.  The input is | ||||||
| // an array of type oids, one element per result column. | // an array of type oids, one element per result column. | ||||||
| func decideColumnFormats(colTyps []oid.Oid, forceText bool) (colFmts []format, colFmtData []byte) { | func decideColumnFormats(colTyps []fieldDesc, forceText bool) (colFmts []format, colFmtData []byte) { | ||||||
| 	if len(colTyps) == 0 { | 	if len(colTyps) == 0 { | ||||||
| 		return nil, colFmtDataAllText | 		return nil, colFmtDataAllText | ||||||
| 	} | 	} | ||||||
| @@ -706,8 +719,8 @@ func decideColumnFormats(colTyps []oid.Oid, forceText bool) (colFmts []format, c | |||||||
|  |  | ||||||
| 	allBinary := true | 	allBinary := true | ||||||
| 	allText := true | 	allText := true | ||||||
| 	for i, o := range colTyps { | 	for i, t := range colTyps { | ||||||
| 		switch o { | 		switch t.OID { | ||||||
| 		// This is the list of types to use binary mode for when receiving them | 		// This is the list of types to use binary mode for when receiving them | ||||||
| 		// through a prepared statement.  If a type appears in this list, it | 		// through a prepared statement.  If a type appears in this list, it | ||||||
| 		// must also be implemented in binaryDecode in encode.go. | 		// must also be implemented in binaryDecode in encode.go. | ||||||
| @@ -718,6 +731,8 @@ func decideColumnFormats(colTyps []oid.Oid, forceText bool) (colFmts []format, c | |||||||
| 		case oid.T_int4: | 		case oid.T_int4: | ||||||
| 			fallthrough | 			fallthrough | ||||||
| 		case oid.T_int2: | 		case oid.T_int2: | ||||||
|  | 			fallthrough | ||||||
|  | 		case oid.T_uuid: | ||||||
| 			colFmts[i] = formatBinary | 			colFmts[i] = formatBinary | ||||||
| 			allText = false | 			allText = false | ||||||
|  |  | ||||||
| @@ -797,7 +812,11 @@ func (cn *conn) Close() (err error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // Implement the "Queryer" interface | // Implement the "Queryer" interface | ||||||
| func (cn *conn) Query(query string, args []driver.Value) (_ driver.Rows, err error) { | func (cn *conn) Query(query string, args []driver.Value) (driver.Rows, error) { | ||||||
|  | 	return cn.query(query, args) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (cn *conn) query(query string, args []driver.Value) (_ *rows, err error) { | ||||||
| 	if cn.bad { | 	if cn.bad { | ||||||
| 		return nil, driver.ErrBadConn | 		return nil, driver.ErrBadConn | ||||||
| 	} | 	} | ||||||
| @@ -821,16 +840,15 @@ func (cn *conn) Query(query string, args []driver.Value) (_ driver.Rows, err err | |||||||
| 		rows.colNames, rows.colFmts, rows.colTyps = cn.readPortalDescribeResponse() | 		rows.colNames, rows.colFmts, rows.colTyps = cn.readPortalDescribeResponse() | ||||||
| 		cn.postExecuteWorkaround() | 		cn.postExecuteWorkaround() | ||||||
| 		return rows, nil | 		return rows, nil | ||||||
| 	} else { |  | ||||||
| 		st := cn.prepareTo(query, "") |  | ||||||
| 		st.exec(args) |  | ||||||
| 		return &rows{ |  | ||||||
| 			cn:       cn, |  | ||||||
| 			colNames: st.colNames, |  | ||||||
| 			colTyps:  st.colTyps, |  | ||||||
| 			colFmts:  st.colFmts, |  | ||||||
| 		}, nil |  | ||||||
| 	} | 	} | ||||||
|  | 	st := cn.prepareTo(query, "") | ||||||
|  | 	st.exec(args) | ||||||
|  | 	return &rows{ | ||||||
|  | 		cn:       cn, | ||||||
|  | 		colNames: st.colNames, | ||||||
|  | 		colTyps:  st.colTyps, | ||||||
|  | 		colFmts:  st.colFmts, | ||||||
|  | 	}, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // Implement the optional "Execer" interface for one-shot queries | // Implement the optional "Execer" interface for one-shot queries | ||||||
| @@ -857,17 +875,16 @@ func (cn *conn) Exec(query string, args []driver.Value) (res driver.Result, err | |||||||
| 		cn.postExecuteWorkaround() | 		cn.postExecuteWorkaround() | ||||||
| 		res, _, err = cn.readExecuteResponse("Execute") | 		res, _, err = cn.readExecuteResponse("Execute") | ||||||
| 		return res, err | 		return res, err | ||||||
| 	} else { |  | ||||||
| 		// Use the unnamed statement to defer planning until bind |  | ||||||
| 		// time, or else value-based selectivity estimates cannot be |  | ||||||
| 		// used. |  | ||||||
| 		st := cn.prepareTo(query, "") |  | ||||||
| 		r, err := st.Exec(args) |  | ||||||
| 		if err != nil { |  | ||||||
| 			panic(err) |  | ||||||
| 		} |  | ||||||
| 		return r, err |  | ||||||
| 	} | 	} | ||||||
|  | 	// Use the unnamed statement to defer planning until bind | ||||||
|  | 	// time, or else value-based selectivity estimates cannot be | ||||||
|  | 	// used. | ||||||
|  | 	st := cn.prepareTo(query, "") | ||||||
|  | 	r, err := st.Exec(args) | ||||||
|  | 	if err != nil { | ||||||
|  | 		panic(err) | ||||||
|  | 	} | ||||||
|  | 	return r, err | ||||||
| } | } | ||||||
|  |  | ||||||
| func (cn *conn) send(m *writeBuf) { | func (cn *conn) send(m *writeBuf) { | ||||||
| @@ -877,16 +894,9 @@ func (cn *conn) send(m *writeBuf) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (cn *conn) sendStartupPacket(m *writeBuf) { | func (cn *conn) sendStartupPacket(m *writeBuf) error { | ||||||
| 	// sanity check |  | ||||||
| 	if m.buf[0] != 0 { |  | ||||||
| 		panic("oops") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	_, err := cn.c.Write((m.wrap())[1:]) | 	_, err := cn.c.Write((m.wrap())[1:]) | ||||||
| 	if err != nil { | 	return err | ||||||
| 		panic(err) |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // Send a message of type typ to the server on the other end of cn.  The | // Send a message of type typ to the server on the other end of cn.  The | ||||||
| @@ -1000,45 +1010,17 @@ func (cn *conn) recv1() (t byte, r *readBuf) { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (cn *conn) ssl(o values) { | func (cn *conn) ssl(o values) { | ||||||
| 	verifyCaOnly := false | 	upgrade := ssl(o) | ||||||
| 	tlsConf := tls.Config{} | 	if upgrade == nil { | ||||||
| 	switch mode := o.Get("sslmode"); mode { | 		// Nothing to do | ||||||
| 	// "require" is the default. |  | ||||||
| 	case "", "require": |  | ||||||
| 		// We must skip TLS's own verification since it requires full |  | ||||||
| 		// verification since Go 1.3. |  | ||||||
| 		tlsConf.InsecureSkipVerify = true |  | ||||||
|  |  | ||||||
| 		// From http://www.postgresql.org/docs/current/static/libpq-ssl.html: |  | ||||||
| 		// Note: For backwards compatibility with earlier versions of PostgreSQL, if a |  | ||||||
| 		// root CA file exists, the behavior of sslmode=require will be the same as |  | ||||||
| 		// that of verify-ca, meaning the server certificate is validated against the |  | ||||||
| 		// CA. Relying on this behavior is discouraged, and applications that need |  | ||||||
| 		// certificate validation should always use verify-ca or verify-full. |  | ||||||
| 		if _, err := os.Stat(o.Get("sslrootcert")); err == nil { |  | ||||||
| 			verifyCaOnly = true |  | ||||||
| 		} else { |  | ||||||
| 			o.Set("sslrootcert", "") |  | ||||||
| 		} |  | ||||||
| 	case "verify-ca": |  | ||||||
| 		// We must skip TLS's own verification since it requires full |  | ||||||
| 		// verification since Go 1.3. |  | ||||||
| 		tlsConf.InsecureSkipVerify = true |  | ||||||
| 		verifyCaOnly = true |  | ||||||
| 	case "verify-full": |  | ||||||
| 		tlsConf.ServerName = o.Get("host") |  | ||||||
| 	case "disable": |  | ||||||
| 		return | 		return | ||||||
| 	default: |  | ||||||
| 		errorf(`unsupported sslmode %q; only "require" (default), "verify-full", "verify-ca", and "disable" supported`, mode) |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	cn.setupSSLClientCertificates(&tlsConf, o) |  | ||||||
| 	cn.setupSSLCA(&tlsConf, o) |  | ||||||
|  |  | ||||||
| 	w := cn.writeBuf(0) | 	w := cn.writeBuf(0) | ||||||
| 	w.int32(80877103) | 	w.int32(80877103) | ||||||
| 	cn.sendStartupPacket(w) | 	if err := cn.sendStartupPacket(w); err != nil { | ||||||
|  | 		panic(err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	b := cn.scratch[:1] | 	b := cn.scratch[:1] | ||||||
| 	_, err := io.ReadFull(cn.c, b) | 	_, err := io.ReadFull(cn.c, b) | ||||||
| @@ -1050,114 +1032,7 @@ func (cn *conn) ssl(o values) { | |||||||
| 		panic(ErrSSLNotSupported) | 		panic(ErrSSLNotSupported) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	client := tls.Client(cn.c, &tlsConf) | 	cn.c = upgrade(cn.c) | ||||||
| 	if verifyCaOnly { |  | ||||||
| 		cn.verifyCA(client, &tlsConf) |  | ||||||
| 	} |  | ||||||
| 	cn.c = client |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // verifyCA carries out a TLS handshake to the server and verifies the |  | ||||||
| // presented certificate against the effective CA, i.e. the one specified in |  | ||||||
| // sslrootcert or the system CA if sslrootcert was not specified. |  | ||||||
| func (cn *conn) verifyCA(client *tls.Conn, tlsConf *tls.Config) { |  | ||||||
| 	err := client.Handshake() |  | ||||||
| 	if err != nil { |  | ||||||
| 		panic(err) |  | ||||||
| 	} |  | ||||||
| 	certs := client.ConnectionState().PeerCertificates |  | ||||||
| 	opts := x509.VerifyOptions{ |  | ||||||
| 		DNSName:       client.ConnectionState().ServerName, |  | ||||||
| 		Intermediates: x509.NewCertPool(), |  | ||||||
| 		Roots:         tlsConf.RootCAs, |  | ||||||
| 	} |  | ||||||
| 	for i, cert := range certs { |  | ||||||
| 		if i == 0 { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		opts.Intermediates.AddCert(cert) |  | ||||||
| 	} |  | ||||||
| 	_, err = certs[0].Verify(opts) |  | ||||||
| 	if err != nil { |  | ||||||
| 		panic(err) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // This function sets up SSL client certificates based on either the "sslkey" |  | ||||||
| // and "sslcert" settings (possibly set via the environment variables PGSSLKEY |  | ||||||
| // and PGSSLCERT, respectively), or if they aren't set, from the .postgresql |  | ||||||
| // directory in the user's home directory.  If the file paths are set |  | ||||||
| // explicitly, the files must exist.  The key file must also not be |  | ||||||
| // world-readable, or this function will panic with |  | ||||||
| // ErrSSLKeyHasWorldPermissions. |  | ||||||
| func (cn *conn) setupSSLClientCertificates(tlsConf *tls.Config, o values) { |  | ||||||
| 	var missingOk bool |  | ||||||
|  |  | ||||||
| 	sslkey := o.Get("sslkey") |  | ||||||
| 	sslcert := o.Get("sslcert") |  | ||||||
| 	if sslkey != "" && sslcert != "" { |  | ||||||
| 		// If the user has set an sslkey and sslcert, they *must* exist. |  | ||||||
| 		missingOk = false |  | ||||||
| 	} else { |  | ||||||
| 		// Automatically load certificates from ~/.postgresql. |  | ||||||
| 		user, err := user.Current() |  | ||||||
| 		if err != nil { |  | ||||||
| 			// user.Current() might fail when cross-compiling.  We have to |  | ||||||
| 			// ignore the error and continue without client certificates, since |  | ||||||
| 			// we wouldn't know where to load them from. |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		sslkey = filepath.Join(user.HomeDir, ".postgresql", "postgresql.key") |  | ||||||
| 		sslcert = filepath.Join(user.HomeDir, ".postgresql", "postgresql.crt") |  | ||||||
| 		missingOk = true |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Check that both files exist, and report the error or stop, depending on |  | ||||||
| 	// which behaviour we want.  Note that we don't do any more extensive |  | ||||||
| 	// checks than this (such as checking that the paths aren't directories); |  | ||||||
| 	// LoadX509KeyPair() will take care of the rest. |  | ||||||
| 	keyfinfo, err := os.Stat(sslkey) |  | ||||||
| 	if err != nil && missingOk { |  | ||||||
| 		return |  | ||||||
| 	} else if err != nil { |  | ||||||
| 		panic(err) |  | ||||||
| 	} |  | ||||||
| 	_, err = os.Stat(sslcert) |  | ||||||
| 	if err != nil && missingOk { |  | ||||||
| 		return |  | ||||||
| 	} else if err != nil { |  | ||||||
| 		panic(err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// If we got this far, the key file must also have the correct permissions |  | ||||||
| 	kmode := keyfinfo.Mode() |  | ||||||
| 	if kmode != kmode&0600 { |  | ||||||
| 		panic(ErrSSLKeyHasWorldPermissions) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	cert, err := tls.LoadX509KeyPair(sslcert, sslkey) |  | ||||||
| 	if err != nil { |  | ||||||
| 		panic(err) |  | ||||||
| 	} |  | ||||||
| 	tlsConf.Certificates = []tls.Certificate{cert} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Sets up RootCAs in the TLS configuration if sslrootcert is set. |  | ||||||
| func (cn *conn) setupSSLCA(tlsConf *tls.Config, o values) { |  | ||||||
| 	if sslrootcert := o.Get("sslrootcert"); sslrootcert != "" { |  | ||||||
| 		tlsConf.RootCAs = x509.NewCertPool() |  | ||||||
|  |  | ||||||
| 		cert, err := ioutil.ReadFile(sslrootcert) |  | ||||||
| 		if err != nil { |  | ||||||
| 			panic(err) |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		ok := tlsConf.RootCAs.AppendCertsFromPEM(cert) |  | ||||||
| 		if !ok { |  | ||||||
| 			errorf("couldn't parse pem in sslrootcert") |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // isDriverSetting returns true iff a setting is purely for configuring the | // isDriverSetting returns true iff a setting is purely for configuring the | ||||||
| @@ -1206,12 +1081,15 @@ func (cn *conn) startup(o values) { | |||||||
| 		w.string(v) | 		w.string(v) | ||||||
| 	} | 	} | ||||||
| 	w.string("") | 	w.string("") | ||||||
| 	cn.sendStartupPacket(w) | 	if err := cn.sendStartupPacket(w); err != nil { | ||||||
|  | 		panic(err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	for { | 	for { | ||||||
| 		t, r := cn.recv() | 		t, r := cn.recv() | ||||||
| 		switch t { | 		switch t { | ||||||
| 		case 'K': | 		case 'K': | ||||||
|  | 			cn.processBackendKeyData(r) | ||||||
| 		case 'S': | 		case 'S': | ||||||
| 			cn.processParameterStatus(r) | 			cn.processParameterStatus(r) | ||||||
| 		case 'R': | 		case 'R': | ||||||
| @@ -1231,7 +1109,7 @@ func (cn *conn) auth(r *readBuf, o values) { | |||||||
| 		// OK | 		// OK | ||||||
| 	case 3: | 	case 3: | ||||||
| 		w := cn.writeBuf('p') | 		w := cn.writeBuf('p') | ||||||
| 		w.string(o.Get("password")) | 		w.string(o["password"]) | ||||||
| 		cn.send(w) | 		cn.send(w) | ||||||
|  |  | ||||||
| 		t, r := cn.recv() | 		t, r := cn.recv() | ||||||
| @@ -1245,7 +1123,7 @@ func (cn *conn) auth(r *readBuf, o values) { | |||||||
| 	case 5: | 	case 5: | ||||||
| 		s := string(r.next(4)) | 		s := string(r.next(4)) | ||||||
| 		w := cn.writeBuf('p') | 		w := cn.writeBuf('p') | ||||||
| 		w.string("md5" + md5s(md5s(o.Get("password")+o.Get("user"))+s)) | 		w.string("md5" + md5s(md5s(o["password"]+o["user"])+s)) | ||||||
| 		cn.send(w) | 		cn.send(w) | ||||||
|  |  | ||||||
| 		t, r := cn.recv() | 		t, r := cn.recv() | ||||||
| @@ -1267,10 +1145,10 @@ const formatText format = 0 | |||||||
| const formatBinary format = 1 | const formatBinary format = 1 | ||||||
|  |  | ||||||
| // One result-column format code with the value 1 (i.e. all binary). | // One result-column format code with the value 1 (i.e. all binary). | ||||||
| var colFmtDataAllBinary []byte = []byte{0, 1, 0, 1} | var colFmtDataAllBinary = []byte{0, 1, 0, 1} | ||||||
|  |  | ||||||
| // No result-column format codes (i.e. all text). | // No result-column format codes (i.e. all text). | ||||||
| var colFmtDataAllText []byte = []byte{0, 0} | var colFmtDataAllText = []byte{0, 0} | ||||||
|  |  | ||||||
| type stmt struct { | type stmt struct { | ||||||
| 	cn         *conn | 	cn         *conn | ||||||
| @@ -1278,7 +1156,7 @@ type stmt struct { | |||||||
| 	colNames   []string | 	colNames   []string | ||||||
| 	colFmts    []format | 	colFmts    []format | ||||||
| 	colFmtData []byte | 	colFmtData []byte | ||||||
| 	colTyps    []oid.Oid | 	colTyps    []fieldDesc | ||||||
| 	paramTyps  []oid.Oid | 	paramTyps  []oid.Oid | ||||||
| 	closed     bool | 	closed     bool | ||||||
| } | } | ||||||
| @@ -1439,21 +1317,32 @@ func (cn *conn) parseComplete(commandTag string) (driver.Result, string) { | |||||||
|  |  | ||||||
| type rows struct { | type rows struct { | ||||||
| 	cn       *conn | 	cn       *conn | ||||||
|  | 	finish   func() | ||||||
| 	colNames []string | 	colNames []string | ||||||
| 	colTyps  []oid.Oid | 	colTyps  []fieldDesc | ||||||
| 	colFmts  []format | 	colFmts  []format | ||||||
| 	done     bool | 	done     bool | ||||||
| 	rb       readBuf | 	rb       readBuf | ||||||
|  | 	result   driver.Result | ||||||
|  | 	tag      string | ||||||
| } | } | ||||||
|  |  | ||||||
| func (rs *rows) Close() error { | func (rs *rows) Close() error { | ||||||
|  | 	if finish := rs.finish; finish != nil { | ||||||
|  | 		defer finish() | ||||||
|  | 	} | ||||||
| 	// no need to look at cn.bad as Next() will | 	// no need to look at cn.bad as Next() will | ||||||
| 	for { | 	for { | ||||||
| 		err := rs.Next(nil) | 		err := rs.Next(nil) | ||||||
| 		switch err { | 		switch err { | ||||||
| 		case nil: | 		case nil: | ||||||
| 		case io.EOF: | 		case io.EOF: | ||||||
| 			return nil | 			// rs.Next can return io.EOF on both 'Z' (ready for query) and 'T' (row | ||||||
|  | 			// description, used with HasNextResultSet). We need to fetch messages until | ||||||
|  | 			// we hit a 'Z', which is done by waiting for done to be set. | ||||||
|  | 			if rs.done { | ||||||
|  | 				return nil | ||||||
|  | 			} | ||||||
| 		default: | 		default: | ||||||
| 			return err | 			return err | ||||||
| 		} | 		} | ||||||
| @@ -1464,6 +1353,17 @@ func (rs *rows) Columns() []string { | |||||||
| 	return rs.colNames | 	return rs.colNames | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (rs *rows) Result() driver.Result { | ||||||
|  | 	if rs.result == nil { | ||||||
|  | 		return emptyRows | ||||||
|  | 	} | ||||||
|  | 	return rs.result | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (rs *rows) Tag() string { | ||||||
|  | 	return rs.tag | ||||||
|  | } | ||||||
|  |  | ||||||
| func (rs *rows) Next(dest []driver.Value) (err error) { | func (rs *rows) Next(dest []driver.Value) (err error) { | ||||||
| 	if rs.done { | 	if rs.done { | ||||||
| 		return io.EOF | 		return io.EOF | ||||||
| @@ -1481,6 +1381,9 @@ func (rs *rows) Next(dest []driver.Value) (err error) { | |||||||
| 		case 'E': | 		case 'E': | ||||||
| 			err = parseError(&rs.rb) | 			err = parseError(&rs.rb) | ||||||
| 		case 'C', 'I': | 		case 'C', 'I': | ||||||
|  | 			if t == 'C' { | ||||||
|  | 				rs.result, rs.tag = conn.parseComplete(rs.rb.string()) | ||||||
|  | 			} | ||||||
| 			continue | 			continue | ||||||
| 		case 'Z': | 		case 'Z': | ||||||
| 			conn.processReadyForQuery(&rs.rb) | 			conn.processReadyForQuery(&rs.rb) | ||||||
| @@ -1504,7 +1407,7 @@ func (rs *rows) Next(dest []driver.Value) (err error) { | |||||||
| 					dest[i] = nil | 					dest[i] = nil | ||||||
| 					continue | 					continue | ||||||
| 				} | 				} | ||||||
| 				dest[i] = decode(&conn.parameterStatus, rs.rb.next(l), rs.colTyps[i], rs.colFmts[i]) | 				dest[i] = decode(&conn.parameterStatus, rs.rb.next(l), rs.colTyps[i].OID, rs.colFmts[i]) | ||||||
| 			} | 			} | ||||||
| 			return | 			return | ||||||
| 		case 'T': | 		case 'T': | ||||||
| @@ -1610,7 +1513,7 @@ func (cn *conn) sendBinaryModeQuery(query string, args []driver.Value) { | |||||||
| 	cn.send(b) | 	cn.send(b) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (c *conn) processParameterStatus(r *readBuf) { | func (cn *conn) processParameterStatus(r *readBuf) { | ||||||
| 	var err error | 	var err error | ||||||
|  |  | ||||||
| 	param := r.string() | 	param := r.string() | ||||||
| @@ -1621,13 +1524,13 @@ func (c *conn) processParameterStatus(r *readBuf) { | |||||||
| 		var minor int | 		var minor int | ||||||
| 		_, err = fmt.Sscanf(r.string(), "%d.%d.%d", &major1, &major2, &minor) | 		_, err = fmt.Sscanf(r.string(), "%d.%d.%d", &major1, &major2, &minor) | ||||||
| 		if err == nil { | 		if err == nil { | ||||||
| 			c.parameterStatus.serverVersion = major1*10000 + major2*100 + minor | 			cn.parameterStatus.serverVersion = major1*10000 + major2*100 + minor | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 	case "TimeZone": | 	case "TimeZone": | ||||||
| 		c.parameterStatus.currentLocation, err = time.LoadLocation(r.string()) | 		cn.parameterStatus.currentLocation, err = time.LoadLocation(r.string()) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			c.parameterStatus.currentLocation = nil | 			cn.parameterStatus.currentLocation = nil | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 	default: | 	default: | ||||||
| @@ -1635,8 +1538,8 @@ func (c *conn) processParameterStatus(r *readBuf) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (c *conn) processReadyForQuery(r *readBuf) { | func (cn *conn) processReadyForQuery(r *readBuf) { | ||||||
| 	c.txnStatus = transactionStatus(r.byte()) | 	cn.txnStatus = transactionStatus(r.byte()) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (cn *conn) readReadyForQuery() { | func (cn *conn) readReadyForQuery() { | ||||||
| @@ -1651,6 +1554,11 @@ func (cn *conn) readReadyForQuery() { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (cn *conn) processBackendKeyData(r *readBuf) { | ||||||
|  | 	cn.processID = r.int32() | ||||||
|  | 	cn.secretKey = r.int32() | ||||||
|  | } | ||||||
|  |  | ||||||
| func (cn *conn) readParseResponse() { | func (cn *conn) readParseResponse() { | ||||||
| 	t, r := cn.recv1() | 	t, r := cn.recv1() | ||||||
| 	switch t { | 	switch t { | ||||||
| @@ -1666,7 +1574,7 @@ func (cn *conn) readParseResponse() { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (cn *conn) readStatementDescribeResponse() (paramTyps []oid.Oid, colNames []string, colTyps []oid.Oid) { | func (cn *conn) readStatementDescribeResponse() (paramTyps []oid.Oid, colNames []string, colTyps []fieldDesc) { | ||||||
| 	for { | 	for { | ||||||
| 		t, r := cn.recv1() | 		t, r := cn.recv1() | ||||||
| 		switch t { | 		switch t { | ||||||
| @@ -1692,7 +1600,7 @@ func (cn *conn) readStatementDescribeResponse() (paramTyps []oid.Oid, colNames [ | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (cn *conn) readPortalDescribeResponse() (colNames []string, colFmts []format, colTyps []oid.Oid) { | func (cn *conn) readPortalDescribeResponse() (colNames []string, colFmts []format, colTyps []fieldDesc) { | ||||||
| 	t, r := cn.recv1() | 	t, r := cn.recv1() | ||||||
| 	switch t { | 	switch t { | ||||||
| 	case 'T': | 	case 'T': | ||||||
| @@ -1788,31 +1696,33 @@ func (cn *conn) readExecuteResponse(protocolState string) (res driver.Result, co | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func parseStatementRowDescribe(r *readBuf) (colNames []string, colTyps []oid.Oid) { | func parseStatementRowDescribe(r *readBuf) (colNames []string, colTyps []fieldDesc) { | ||||||
| 	n := r.int16() | 	n := r.int16() | ||||||
| 	colNames = make([]string, n) | 	colNames = make([]string, n) | ||||||
| 	colTyps = make([]oid.Oid, n) | 	colTyps = make([]fieldDesc, n) | ||||||
| 	for i := range colNames { | 	for i := range colNames { | ||||||
| 		colNames[i] = r.string() | 		colNames[i] = r.string() | ||||||
| 		r.next(6) | 		r.next(6) | ||||||
| 		colTyps[i] = r.oid() | 		colTyps[i].OID = r.oid() | ||||||
| 		r.next(6) | 		colTyps[i].Len = r.int16() | ||||||
|  | 		colTyps[i].Mod = r.int32() | ||||||
| 		// format code not known when describing a statement; always 0 | 		// format code not known when describing a statement; always 0 | ||||||
| 		r.next(2) | 		r.next(2) | ||||||
| 	} | 	} | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
|  |  | ||||||
| func parsePortalRowDescribe(r *readBuf) (colNames []string, colFmts []format, colTyps []oid.Oid) { | func parsePortalRowDescribe(r *readBuf) (colNames []string, colFmts []format, colTyps []fieldDesc) { | ||||||
| 	n := r.int16() | 	n := r.int16() | ||||||
| 	colNames = make([]string, n) | 	colNames = make([]string, n) | ||||||
| 	colFmts = make([]format, n) | 	colFmts = make([]format, n) | ||||||
| 	colTyps = make([]oid.Oid, n) | 	colTyps = make([]fieldDesc, n) | ||||||
| 	for i := range colNames { | 	for i := range colNames { | ||||||
| 		colNames[i] = r.string() | 		colNames[i] = r.string() | ||||||
| 		r.next(6) | 		r.next(6) | ||||||
| 		colTyps[i] = r.oid() | 		colTyps[i].OID = r.oid() | ||||||
| 		r.next(6) | 		colTyps[i].Len = r.int16() | ||||||
|  | 		colTyps[i].Mod = r.int32() | ||||||
| 		colFmts[i] = format(r.int16()) | 		colFmts[i] = format(r.int16()) | ||||||
| 	} | 	} | ||||||
| 	return | 	return | ||||||
|   | |||||||
							
								
								
									
										128
									
								
								vendor/github.com/lib/pq/conn_go18.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								vendor/github.com/lib/pq/conn_go18.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,128 @@ | |||||||
|  | // +build go1.8 | ||||||
|  |  | ||||||
|  | package pq | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"database/sql" | ||||||
|  | 	"database/sql/driver" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"io/ioutil" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Implement the "QueryerContext" interface | ||||||
|  | func (cn *conn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) { | ||||||
|  | 	list := make([]driver.Value, len(args)) | ||||||
|  | 	for i, nv := range args { | ||||||
|  | 		list[i] = nv.Value | ||||||
|  | 	} | ||||||
|  | 	finish := cn.watchCancel(ctx) | ||||||
|  | 	r, err := cn.query(query, list) | ||||||
|  | 	if err != nil { | ||||||
|  | 		if finish != nil { | ||||||
|  | 			finish() | ||||||
|  | 		} | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	r.finish = finish | ||||||
|  | 	return r, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Implement the "ExecerContext" interface | ||||||
|  | func (cn *conn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) { | ||||||
|  | 	list := make([]driver.Value, len(args)) | ||||||
|  | 	for i, nv := range args { | ||||||
|  | 		list[i] = nv.Value | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if finish := cn.watchCancel(ctx); finish != nil { | ||||||
|  | 		defer finish() | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return cn.Exec(query, list) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Implement the "ConnBeginTx" interface | ||||||
|  | func (cn *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { | ||||||
|  | 	var mode string | ||||||
|  |  | ||||||
|  | 	switch sql.IsolationLevel(opts.Isolation) { | ||||||
|  | 	case sql.LevelDefault: | ||||||
|  | 		// Don't touch mode: use the server's default | ||||||
|  | 	case sql.LevelReadUncommitted: | ||||||
|  | 		mode = " ISOLATION LEVEL READ UNCOMMITTED" | ||||||
|  | 	case sql.LevelReadCommitted: | ||||||
|  | 		mode = " ISOLATION LEVEL READ COMMITTED" | ||||||
|  | 	case sql.LevelRepeatableRead: | ||||||
|  | 		mode = " ISOLATION LEVEL REPEATABLE READ" | ||||||
|  | 	case sql.LevelSerializable: | ||||||
|  | 		mode = " ISOLATION LEVEL SERIALIZABLE" | ||||||
|  | 	default: | ||||||
|  | 		return nil, fmt.Errorf("pq: isolation level not supported: %d", opts.Isolation) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if opts.ReadOnly { | ||||||
|  | 		mode += " READ ONLY" | ||||||
|  | 	} else { | ||||||
|  | 		mode += " READ WRITE" | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	tx, err := cn.begin(mode) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	cn.txnFinish = cn.watchCancel(ctx) | ||||||
|  | 	return tx, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (cn *conn) watchCancel(ctx context.Context) func() { | ||||||
|  | 	if done := ctx.Done(); done != nil { | ||||||
|  | 		finished := make(chan struct{}) | ||||||
|  | 		go func() { | ||||||
|  | 			select { | ||||||
|  | 			case <-done: | ||||||
|  | 				_ = cn.cancel() | ||||||
|  | 				finished <- struct{}{} | ||||||
|  | 			case <-finished: | ||||||
|  | 			} | ||||||
|  | 		}() | ||||||
|  | 		return func() { | ||||||
|  | 			select { | ||||||
|  | 			case <-finished: | ||||||
|  | 			case finished <- struct{}{}: | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (cn *conn) cancel() error { | ||||||
|  | 	c, err := dial(cn.dialer, cn.opts) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	defer c.Close() | ||||||
|  |  | ||||||
|  | 	{ | ||||||
|  | 		can := conn{ | ||||||
|  | 			c: c, | ||||||
|  | 		} | ||||||
|  | 		can.ssl(cn.opts) | ||||||
|  |  | ||||||
|  | 		w := can.writeBuf(0) | ||||||
|  | 		w.int32(80877102) // cancel request code | ||||||
|  | 		w.int32(cn.processID) | ||||||
|  | 		w.int32(cn.secretKey) | ||||||
|  |  | ||||||
|  | 		if err := can.sendStartupPacket(w); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Read until EOF to ensure that the server received the cancel. | ||||||
|  | 	{ | ||||||
|  | 		_, err := io.Copy(ioutil.Discard, c) | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										27
									
								
								vendor/github.com/lib/pq/copy.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										27
									
								
								vendor/github.com/lib/pq/copy.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -97,13 +97,13 @@ awaitCopyInResponse: | |||||||
| 			err = parseError(r) | 			err = parseError(r) | ||||||
| 		case 'Z': | 		case 'Z': | ||||||
| 			if err == nil { | 			if err == nil { | ||||||
| 				cn.bad = true | 				ci.setBad() | ||||||
| 				errorf("unexpected ReadyForQuery in response to COPY") | 				errorf("unexpected ReadyForQuery in response to COPY") | ||||||
| 			} | 			} | ||||||
| 			cn.processReadyForQuery(r) | 			cn.processReadyForQuery(r) | ||||||
| 			return nil, err | 			return nil, err | ||||||
| 		default: | 		default: | ||||||
| 			cn.bad = true | 			ci.setBad() | ||||||
| 			errorf("unknown response for copy query: %q", t) | 			errorf("unknown response for copy query: %q", t) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -122,7 +122,7 @@ awaitCopyInResponse: | |||||||
| 			cn.processReadyForQuery(r) | 			cn.processReadyForQuery(r) | ||||||
| 			return nil, err | 			return nil, err | ||||||
| 		default: | 		default: | ||||||
| 			cn.bad = true | 			ci.setBad() | ||||||
| 			errorf("unknown response for CopyFail: %q", t) | 			errorf("unknown response for CopyFail: %q", t) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| @@ -143,7 +143,7 @@ func (ci *copyin) resploop() { | |||||||
| 		var r readBuf | 		var r readBuf | ||||||
| 		t, err := ci.cn.recvMessage(&r) | 		t, err := ci.cn.recvMessage(&r) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			ci.cn.bad = true | 			ci.setBad() | ||||||
| 			ci.setError(err) | 			ci.setError(err) | ||||||
| 			ci.done <- true | 			ci.done <- true | ||||||
| 			return | 			return | ||||||
| @@ -161,7 +161,7 @@ func (ci *copyin) resploop() { | |||||||
| 			err := parseError(&r) | 			err := parseError(&r) | ||||||
| 			ci.setError(err) | 			ci.setError(err) | ||||||
| 		default: | 		default: | ||||||
| 			ci.cn.bad = true | 			ci.setBad() | ||||||
| 			ci.setError(fmt.Errorf("unknown response during CopyIn: %q", t)) | 			ci.setError(fmt.Errorf("unknown response during CopyIn: %q", t)) | ||||||
| 			ci.done <- true | 			ci.done <- true | ||||||
| 			return | 			return | ||||||
| @@ -169,6 +169,19 @@ func (ci *copyin) resploop() { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (ci *copyin) setBad() { | ||||||
|  | 	ci.Lock() | ||||||
|  | 	ci.cn.bad = true | ||||||
|  | 	ci.Unlock() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (ci *copyin) isBad() bool { | ||||||
|  | 	ci.Lock() | ||||||
|  | 	b := ci.cn.bad | ||||||
|  | 	ci.Unlock() | ||||||
|  | 	return b | ||||||
|  | } | ||||||
|  |  | ||||||
| func (ci *copyin) isErrorSet() bool { | func (ci *copyin) isErrorSet() bool { | ||||||
| 	ci.Lock() | 	ci.Lock() | ||||||
| 	isSet := (ci.err != nil) | 	isSet := (ci.err != nil) | ||||||
| @@ -206,7 +219,7 @@ func (ci *copyin) Exec(v []driver.Value) (r driver.Result, err error) { | |||||||
| 		return nil, errCopyInClosed | 		return nil, errCopyInClosed | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if ci.cn.bad { | 	if ci.isBad() { | ||||||
| 		return nil, driver.ErrBadConn | 		return nil, driver.ErrBadConn | ||||||
| 	} | 	} | ||||||
| 	defer ci.cn.errRecover(&err) | 	defer ci.cn.errRecover(&err) | ||||||
| @@ -244,7 +257,7 @@ func (ci *copyin) Close() (err error) { | |||||||
| 	} | 	} | ||||||
| 	ci.closed = true | 	ci.closed = true | ||||||
|  |  | ||||||
| 	if ci.cn.bad { | 	if ci.isBad() { | ||||||
| 		return driver.ErrBadConn | 		return driver.ErrBadConn | ||||||
| 	} | 	} | ||||||
| 	defer ci.cn.errRecover(&err) | 	defer ci.cn.errRecover(&err) | ||||||
|   | |||||||
							
								
								
									
										20
									
								
								vendor/github.com/lib/pq/encode.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								vendor/github.com/lib/pq/encode.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -76,6 +76,12 @@ func binaryDecode(parameterStatus *parameterStatus, s []byte, typ oid.Oid) inter | |||||||
| 		return int64(int32(binary.BigEndian.Uint32(s))) | 		return int64(int32(binary.BigEndian.Uint32(s))) | ||||||
| 	case oid.T_int2: | 	case oid.T_int2: | ||||||
| 		return int64(int16(binary.BigEndian.Uint16(s))) | 		return int64(int16(binary.BigEndian.Uint16(s))) | ||||||
|  | 	case oid.T_uuid: | ||||||
|  | 		b, err := decodeUUIDBinary(s) | ||||||
|  | 		if err != nil { | ||||||
|  | 			panic(err) | ||||||
|  | 		} | ||||||
|  | 		return b | ||||||
|  |  | ||||||
| 	default: | 	default: | ||||||
| 		errorf("don't know how to decode binary parameter of type %d", uint32(typ)) | 		errorf("don't know how to decode binary parameter of type %d", uint32(typ)) | ||||||
| @@ -361,8 +367,15 @@ func ParseTimestamp(currentLocation *time.Location, str string) (time.Time, erro | |||||||
| 	timeSep := daySep + 3 | 	timeSep := daySep + 3 | ||||||
| 	day := p.mustAtoi(str, daySep+1, timeSep) | 	day := p.mustAtoi(str, daySep+1, timeSep) | ||||||
|  |  | ||||||
|  | 	minLen := monSep + len("01-01") + 1 | ||||||
|  |  | ||||||
|  | 	isBC := strings.HasSuffix(str, " BC") | ||||||
|  | 	if isBC { | ||||||
|  | 		minLen += 3 | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	var hour, minute, second int | 	var hour, minute, second int | ||||||
| 	if len(str) > monSep+len("01-01")+1 { | 	if len(str) > minLen { | ||||||
| 		p.expect(str, ' ', timeSep) | 		p.expect(str, ' ', timeSep) | ||||||
| 		minSep := timeSep + 3 | 		minSep := timeSep + 3 | ||||||
| 		p.expect(str, ':', minSep) | 		p.expect(str, ':', minSep) | ||||||
| @@ -418,7 +431,8 @@ func ParseTimestamp(currentLocation *time.Location, str string) (time.Time, erro | |||||||
| 		tzOff = tzSign * ((tzHours * 60 * 60) + (tzMin * 60) + tzSec) | 		tzOff = tzSign * ((tzHours * 60 * 60) + (tzMin * 60) + tzSec) | ||||||
| 	} | 	} | ||||||
| 	var isoYear int | 	var isoYear int | ||||||
| 	if remainderIdx+3 <= len(str) && str[remainderIdx:remainderIdx+3] == " BC" { |  | ||||||
|  | 	if isBC { | ||||||
| 		isoYear = 1 - year | 		isoYear = 1 - year | ||||||
| 		remainderIdx += 3 | 		remainderIdx += 3 | ||||||
| 	} else { | 	} else { | ||||||
| @@ -471,7 +485,7 @@ func FormatTimestamp(t time.Time) []byte { | |||||||
| 		t = t.AddDate((-t.Year())*2+1, 0, 0) | 		t = t.AddDate((-t.Year())*2+1, 0, 0) | ||||||
| 		bc = true | 		bc = true | ||||||
| 	} | 	} | ||||||
| 	b := []byte(t.Format(time.RFC3339Nano)) | 	b := []byte(t.Format("2006-01-02 15:04:05.999999999Z07:00")) | ||||||
|  |  | ||||||
| 	_, offset := t.Zone() | 	_, offset := t.Zone() | ||||||
| 	offset = offset % 60 | 	offset = offset % 60 | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								vendor/github.com/lib/pq/notify.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/lib/pq/notify.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -639,7 +639,7 @@ func (l *Listener) resync(cn *ListenerConn, notificationChan <-chan *Notificatio | |||||||
| 			// close and then return the error message from the connection, as | 			// close and then return the error message from the connection, as | ||||||
| 			// per ListenerConn's interface. | 			// per ListenerConn's interface. | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				for _ = range notificationChan { | 				for range notificationChan { | ||||||
| 				} | 				} | ||||||
| 				doneChan <- cn.Err() | 				doneChan <- cn.Err() | ||||||
| 				return | 				return | ||||||
|   | |||||||
							
								
								
									
										74
									
								
								vendor/github.com/lib/pq/oid/gen.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										74
									
								
								vendor/github.com/lib/pq/oid/gen.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,74 +0,0 @@ | |||||||
| // +build ignore |  | ||||||
|  |  | ||||||
| // Generate the table of OID values |  | ||||||
| // Run with 'go run gen.go'. |  | ||||||
| package main |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"database/sql" |  | ||||||
| 	"fmt" |  | ||||||
| 	"log" |  | ||||||
| 	"os" |  | ||||||
| 	"os/exec" |  | ||||||
|  |  | ||||||
| 	_ "github.com/lib/pq" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func main() { |  | ||||||
| 	datname := os.Getenv("PGDATABASE") |  | ||||||
| 	sslmode := os.Getenv("PGSSLMODE") |  | ||||||
|  |  | ||||||
| 	if datname == "" { |  | ||||||
| 		os.Setenv("PGDATABASE", "pqgotest") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if sslmode == "" { |  | ||||||
| 		os.Setenv("PGSSLMODE", "disable") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	db, err := sql.Open("postgres", "") |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Fatal(err) |  | ||||||
| 	} |  | ||||||
| 	cmd := exec.Command("gofmt") |  | ||||||
| 	cmd.Stderr = os.Stderr |  | ||||||
| 	w, err := cmd.StdinPipe() |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Fatal(err) |  | ||||||
| 	} |  | ||||||
| 	f, err := os.Create("types.go") |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Fatal(err) |  | ||||||
| 	} |  | ||||||
| 	cmd.Stdout = f |  | ||||||
| 	err = cmd.Start() |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Fatal(err) |  | ||||||
| 	} |  | ||||||
| 	fmt.Fprintln(w, "// generated by 'go run gen.go'; do not edit") |  | ||||||
| 	fmt.Fprintln(w, "\npackage oid") |  | ||||||
| 	fmt.Fprintln(w, "const (") |  | ||||||
| 	rows, err := db.Query(` |  | ||||||
| 		SELECT typname, oid |  | ||||||
| 		FROM pg_type WHERE oid < 10000 |  | ||||||
| 		ORDER BY oid; |  | ||||||
| 	`) |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Fatal(err) |  | ||||||
| 	} |  | ||||||
| 	var name string |  | ||||||
| 	var oid int |  | ||||||
| 	for rows.Next() { |  | ||||||
| 		err = rows.Scan(&name, &oid) |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Fatal(err) |  | ||||||
| 		} |  | ||||||
| 		fmt.Fprintf(w, "T_%s Oid = %d\n", name, oid) |  | ||||||
| 	} |  | ||||||
| 	if err = rows.Err(); err != nil { |  | ||||||
| 		log.Fatal(err) |  | ||||||
| 	} |  | ||||||
| 	fmt.Fprintln(w, ")") |  | ||||||
| 	w.Close() |  | ||||||
| 	cmd.Wait() |  | ||||||
| } |  | ||||||
							
								
								
									
										184
									
								
								vendor/github.com/lib/pq/oid/types.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										184
									
								
								vendor/github.com/lib/pq/oid/types.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,4 +1,4 @@ | |||||||
| // generated by 'go run gen.go'; do not edit | // Code generated by gen.go. DO NOT EDIT. | ||||||
|  |  | ||||||
| package oid | package oid | ||||||
|  |  | ||||||
| @@ -18,6 +18,7 @@ const ( | |||||||
| 	T_xid              Oid = 28 | 	T_xid              Oid = 28 | ||||||
| 	T_cid              Oid = 29 | 	T_cid              Oid = 29 | ||||||
| 	T_oidvector        Oid = 30 | 	T_oidvector        Oid = 30 | ||||||
|  | 	T_pg_ddl_command   Oid = 32 | ||||||
| 	T_pg_type          Oid = 71 | 	T_pg_type          Oid = 71 | ||||||
| 	T_pg_attribute     Oid = 75 | 	T_pg_attribute     Oid = 75 | ||||||
| 	T_pg_proc          Oid = 81 | 	T_pg_proc          Oid = 81 | ||||||
| @@ -28,6 +29,7 @@ const ( | |||||||
| 	T_pg_node_tree     Oid = 194 | 	T_pg_node_tree     Oid = 194 | ||||||
| 	T__json            Oid = 199 | 	T__json            Oid = 199 | ||||||
| 	T_smgr             Oid = 210 | 	T_smgr             Oid = 210 | ||||||
|  | 	T_index_am_handler Oid = 325 | ||||||
| 	T_point            Oid = 600 | 	T_point            Oid = 600 | ||||||
| 	T_lseg             Oid = 601 | 	T_lseg             Oid = 601 | ||||||
| 	T_path             Oid = 602 | 	T_path             Oid = 602 | ||||||
| @@ -133,6 +135,9 @@ const ( | |||||||
| 	T__uuid            Oid = 2951 | 	T__uuid            Oid = 2951 | ||||||
| 	T_txid_snapshot    Oid = 2970 | 	T_txid_snapshot    Oid = 2970 | ||||||
| 	T_fdw_handler      Oid = 3115 | 	T_fdw_handler      Oid = 3115 | ||||||
|  | 	T_pg_lsn           Oid = 3220 | ||||||
|  | 	T__pg_lsn          Oid = 3221 | ||||||
|  | 	T_tsm_handler      Oid = 3310 | ||||||
| 	T_anyenum          Oid = 3500 | 	T_anyenum          Oid = 3500 | ||||||
| 	T_tsvector         Oid = 3614 | 	T_tsvector         Oid = 3614 | ||||||
| 	T_tsquery          Oid = 3615 | 	T_tsquery          Oid = 3615 | ||||||
| @@ -144,6 +149,8 @@ const ( | |||||||
| 	T__regconfig       Oid = 3735 | 	T__regconfig       Oid = 3735 | ||||||
| 	T_regdictionary    Oid = 3769 | 	T_regdictionary    Oid = 3769 | ||||||
| 	T__regdictionary   Oid = 3770 | 	T__regdictionary   Oid = 3770 | ||||||
|  | 	T_jsonb            Oid = 3802 | ||||||
|  | 	T__jsonb           Oid = 3807 | ||||||
| 	T_anyrange         Oid = 3831 | 	T_anyrange         Oid = 3831 | ||||||
| 	T_event_trigger    Oid = 3838 | 	T_event_trigger    Oid = 3838 | ||||||
| 	T_int4range        Oid = 3904 | 	T_int4range        Oid = 3904 | ||||||
| @@ -158,4 +165,179 @@ const ( | |||||||
| 	T__daterange       Oid = 3913 | 	T__daterange       Oid = 3913 | ||||||
| 	T_int8range        Oid = 3926 | 	T_int8range        Oid = 3926 | ||||||
| 	T__int8range       Oid = 3927 | 	T__int8range       Oid = 3927 | ||||||
|  | 	T_pg_shseclabel    Oid = 4066 | ||||||
|  | 	T_regnamespace     Oid = 4089 | ||||||
|  | 	T__regnamespace    Oid = 4090 | ||||||
|  | 	T_regrole          Oid = 4096 | ||||||
|  | 	T__regrole         Oid = 4097 | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | var TypeName = map[Oid]string{ | ||||||
|  | 	T_bool:             "BOOL", | ||||||
|  | 	T_bytea:            "BYTEA", | ||||||
|  | 	T_char:             "CHAR", | ||||||
|  | 	T_name:             "NAME", | ||||||
|  | 	T_int8:             "INT8", | ||||||
|  | 	T_int2:             "INT2", | ||||||
|  | 	T_int2vector:       "INT2VECTOR", | ||||||
|  | 	T_int4:             "INT4", | ||||||
|  | 	T_regproc:          "REGPROC", | ||||||
|  | 	T_text:             "TEXT", | ||||||
|  | 	T_oid:              "OID", | ||||||
|  | 	T_tid:              "TID", | ||||||
|  | 	T_xid:              "XID", | ||||||
|  | 	T_cid:              "CID", | ||||||
|  | 	T_oidvector:        "OIDVECTOR", | ||||||
|  | 	T_pg_ddl_command:   "PG_DDL_COMMAND", | ||||||
|  | 	T_pg_type:          "PG_TYPE", | ||||||
|  | 	T_pg_attribute:     "PG_ATTRIBUTE", | ||||||
|  | 	T_pg_proc:          "PG_PROC", | ||||||
|  | 	T_pg_class:         "PG_CLASS", | ||||||
|  | 	T_json:             "JSON", | ||||||
|  | 	T_xml:              "XML", | ||||||
|  | 	T__xml:             "_XML", | ||||||
|  | 	T_pg_node_tree:     "PG_NODE_TREE", | ||||||
|  | 	T__json:            "_JSON", | ||||||
|  | 	T_smgr:             "SMGR", | ||||||
|  | 	T_index_am_handler: "INDEX_AM_HANDLER", | ||||||
|  | 	T_point:            "POINT", | ||||||
|  | 	T_lseg:             "LSEG", | ||||||
|  | 	T_path:             "PATH", | ||||||
|  | 	T_box:              "BOX", | ||||||
|  | 	T_polygon:          "POLYGON", | ||||||
|  | 	T_line:             "LINE", | ||||||
|  | 	T__line:            "_LINE", | ||||||
|  | 	T_cidr:             "CIDR", | ||||||
|  | 	T__cidr:            "_CIDR", | ||||||
|  | 	T_float4:           "FLOAT4", | ||||||
|  | 	T_float8:           "FLOAT8", | ||||||
|  | 	T_abstime:          "ABSTIME", | ||||||
|  | 	T_reltime:          "RELTIME", | ||||||
|  | 	T_tinterval:        "TINTERVAL", | ||||||
|  | 	T_unknown:          "UNKNOWN", | ||||||
|  | 	T_circle:           "CIRCLE", | ||||||
|  | 	T__circle:          "_CIRCLE", | ||||||
|  | 	T_money:            "MONEY", | ||||||
|  | 	T__money:           "_MONEY", | ||||||
|  | 	T_macaddr:          "MACADDR", | ||||||
|  | 	T_inet:             "INET", | ||||||
|  | 	T__bool:            "_BOOL", | ||||||
|  | 	T__bytea:           "_BYTEA", | ||||||
|  | 	T__char:            "_CHAR", | ||||||
|  | 	T__name:            "_NAME", | ||||||
|  | 	T__int2:            "_INT2", | ||||||
|  | 	T__int2vector:      "_INT2VECTOR", | ||||||
|  | 	T__int4:            "_INT4", | ||||||
|  | 	T__regproc:         "_REGPROC", | ||||||
|  | 	T__text:            "_TEXT", | ||||||
|  | 	T__tid:             "_TID", | ||||||
|  | 	T__xid:             "_XID", | ||||||
|  | 	T__cid:             "_CID", | ||||||
|  | 	T__oidvector:       "_OIDVECTOR", | ||||||
|  | 	T__bpchar:          "_BPCHAR", | ||||||
|  | 	T__varchar:         "_VARCHAR", | ||||||
|  | 	T__int8:            "_INT8", | ||||||
|  | 	T__point:           "_POINT", | ||||||
|  | 	T__lseg:            "_LSEG", | ||||||
|  | 	T__path:            "_PATH", | ||||||
|  | 	T__box:             "_BOX", | ||||||
|  | 	T__float4:          "_FLOAT4", | ||||||
|  | 	T__float8:          "_FLOAT8", | ||||||
|  | 	T__abstime:         "_ABSTIME", | ||||||
|  | 	T__reltime:         "_RELTIME", | ||||||
|  | 	T__tinterval:       "_TINTERVAL", | ||||||
|  | 	T__polygon:         "_POLYGON", | ||||||
|  | 	T__oid:             "_OID", | ||||||
|  | 	T_aclitem:          "ACLITEM", | ||||||
|  | 	T__aclitem:         "_ACLITEM", | ||||||
|  | 	T__macaddr:         "_MACADDR", | ||||||
|  | 	T__inet:            "_INET", | ||||||
|  | 	T_bpchar:           "BPCHAR", | ||||||
|  | 	T_varchar:          "VARCHAR", | ||||||
|  | 	T_date:             "DATE", | ||||||
|  | 	T_time:             "TIME", | ||||||
|  | 	T_timestamp:        "TIMESTAMP", | ||||||
|  | 	T__timestamp:       "_TIMESTAMP", | ||||||
|  | 	T__date:            "_DATE", | ||||||
|  | 	T__time:            "_TIME", | ||||||
|  | 	T_timestamptz:      "TIMESTAMPTZ", | ||||||
|  | 	T__timestamptz:     "_TIMESTAMPTZ", | ||||||
|  | 	T_interval:         "INTERVAL", | ||||||
|  | 	T__interval:        "_INTERVAL", | ||||||
|  | 	T__numeric:         "_NUMERIC", | ||||||
|  | 	T_pg_database:      "PG_DATABASE", | ||||||
|  | 	T__cstring:         "_CSTRING", | ||||||
|  | 	T_timetz:           "TIMETZ", | ||||||
|  | 	T__timetz:          "_TIMETZ", | ||||||
|  | 	T_bit:              "BIT", | ||||||
|  | 	T__bit:             "_BIT", | ||||||
|  | 	T_varbit:           "VARBIT", | ||||||
|  | 	T__varbit:          "_VARBIT", | ||||||
|  | 	T_numeric:          "NUMERIC", | ||||||
|  | 	T_refcursor:        "REFCURSOR", | ||||||
|  | 	T__refcursor:       "_REFCURSOR", | ||||||
|  | 	T_regprocedure:     "REGPROCEDURE", | ||||||
|  | 	T_regoper:          "REGOPER", | ||||||
|  | 	T_regoperator:      "REGOPERATOR", | ||||||
|  | 	T_regclass:         "REGCLASS", | ||||||
|  | 	T_regtype:          "REGTYPE", | ||||||
|  | 	T__regprocedure:    "_REGPROCEDURE", | ||||||
|  | 	T__regoper:         "_REGOPER", | ||||||
|  | 	T__regoperator:     "_REGOPERATOR", | ||||||
|  | 	T__regclass:        "_REGCLASS", | ||||||
|  | 	T__regtype:         "_REGTYPE", | ||||||
|  | 	T_record:           "RECORD", | ||||||
|  | 	T_cstring:          "CSTRING", | ||||||
|  | 	T_any:              "ANY", | ||||||
|  | 	T_anyarray:         "ANYARRAY", | ||||||
|  | 	T_void:             "VOID", | ||||||
|  | 	T_trigger:          "TRIGGER", | ||||||
|  | 	T_language_handler: "LANGUAGE_HANDLER", | ||||||
|  | 	T_internal:         "INTERNAL", | ||||||
|  | 	T_opaque:           "OPAQUE", | ||||||
|  | 	T_anyelement:       "ANYELEMENT", | ||||||
|  | 	T__record:          "_RECORD", | ||||||
|  | 	T_anynonarray:      "ANYNONARRAY", | ||||||
|  | 	T_pg_authid:        "PG_AUTHID", | ||||||
|  | 	T_pg_auth_members:  "PG_AUTH_MEMBERS", | ||||||
|  | 	T__txid_snapshot:   "_TXID_SNAPSHOT", | ||||||
|  | 	T_uuid:             "UUID", | ||||||
|  | 	T__uuid:            "_UUID", | ||||||
|  | 	T_txid_snapshot:    "TXID_SNAPSHOT", | ||||||
|  | 	T_fdw_handler:      "FDW_HANDLER", | ||||||
|  | 	T_pg_lsn:           "PG_LSN", | ||||||
|  | 	T__pg_lsn:          "_PG_LSN", | ||||||
|  | 	T_tsm_handler:      "TSM_HANDLER", | ||||||
|  | 	T_anyenum:          "ANYENUM", | ||||||
|  | 	T_tsvector:         "TSVECTOR", | ||||||
|  | 	T_tsquery:          "TSQUERY", | ||||||
|  | 	T_gtsvector:        "GTSVECTOR", | ||||||
|  | 	T__tsvector:        "_TSVECTOR", | ||||||
|  | 	T__gtsvector:       "_GTSVECTOR", | ||||||
|  | 	T__tsquery:         "_TSQUERY", | ||||||
|  | 	T_regconfig:        "REGCONFIG", | ||||||
|  | 	T__regconfig:       "_REGCONFIG", | ||||||
|  | 	T_regdictionary:    "REGDICTIONARY", | ||||||
|  | 	T__regdictionary:   "_REGDICTIONARY", | ||||||
|  | 	T_jsonb:            "JSONB", | ||||||
|  | 	T__jsonb:           "_JSONB", | ||||||
|  | 	T_anyrange:         "ANYRANGE", | ||||||
|  | 	T_event_trigger:    "EVENT_TRIGGER", | ||||||
|  | 	T_int4range:        "INT4RANGE", | ||||||
|  | 	T__int4range:       "_INT4RANGE", | ||||||
|  | 	T_numrange:         "NUMRANGE", | ||||||
|  | 	T__numrange:        "_NUMRANGE", | ||||||
|  | 	T_tsrange:          "TSRANGE", | ||||||
|  | 	T__tsrange:         "_TSRANGE", | ||||||
|  | 	T_tstzrange:        "TSTZRANGE", | ||||||
|  | 	T__tstzrange:       "_TSTZRANGE", | ||||||
|  | 	T_daterange:        "DATERANGE", | ||||||
|  | 	T__daterange:       "_DATERANGE", | ||||||
|  | 	T_int8range:        "INT8RANGE", | ||||||
|  | 	T__int8range:       "_INT8RANGE", | ||||||
|  | 	T_pg_shseclabel:    "PG_SHSECLABEL", | ||||||
|  | 	T_regnamespace:     "REGNAMESPACE", | ||||||
|  | 	T__regnamespace:    "_REGNAMESPACE", | ||||||
|  | 	T_regrole:          "REGROLE", | ||||||
|  | 	T__regrole:         "_REGROLE", | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										93
									
								
								vendor/github.com/lib/pq/rows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								vendor/github.com/lib/pq/rows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | |||||||
|  | package pq | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"math" | ||||||
|  | 	"reflect" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/lib/pq/oid" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | const headerSize = 4 | ||||||
|  |  | ||||||
|  | type fieldDesc struct { | ||||||
|  | 	// The object ID of the data type. | ||||||
|  | 	OID oid.Oid | ||||||
|  | 	// The data type size (see pg_type.typlen). | ||||||
|  | 	// Note that negative values denote variable-width types. | ||||||
|  | 	Len int | ||||||
|  | 	// The type modifier (see pg_attribute.atttypmod). | ||||||
|  | 	// The meaning of the modifier is type-specific. | ||||||
|  | 	Mod int | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (fd fieldDesc) Type() reflect.Type { | ||||||
|  | 	switch fd.OID { | ||||||
|  | 	case oid.T_int8: | ||||||
|  | 		return reflect.TypeOf(int64(0)) | ||||||
|  | 	case oid.T_int4: | ||||||
|  | 		return reflect.TypeOf(int32(0)) | ||||||
|  | 	case oid.T_int2: | ||||||
|  | 		return reflect.TypeOf(int16(0)) | ||||||
|  | 	case oid.T_varchar, oid.T_text: | ||||||
|  | 		return reflect.TypeOf("") | ||||||
|  | 	case oid.T_bool: | ||||||
|  | 		return reflect.TypeOf(false) | ||||||
|  | 	case oid.T_date, oid.T_time, oid.T_timetz, oid.T_timestamp, oid.T_timestamptz: | ||||||
|  | 		return reflect.TypeOf(time.Time{}) | ||||||
|  | 	case oid.T_bytea: | ||||||
|  | 		return reflect.TypeOf([]byte(nil)) | ||||||
|  | 	default: | ||||||
|  | 		return reflect.TypeOf(new(interface{})).Elem() | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (fd fieldDesc) Name() string { | ||||||
|  | 	return oid.TypeName[fd.OID] | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (fd fieldDesc) Length() (length int64, ok bool) { | ||||||
|  | 	switch fd.OID { | ||||||
|  | 	case oid.T_text, oid.T_bytea: | ||||||
|  | 		return math.MaxInt64, true | ||||||
|  | 	case oid.T_varchar, oid.T_bpchar: | ||||||
|  | 		return int64(fd.Mod - headerSize), true | ||||||
|  | 	default: | ||||||
|  | 		return 0, false | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (fd fieldDesc) PrecisionScale() (precision, scale int64, ok bool) { | ||||||
|  | 	switch fd.OID { | ||||||
|  | 	case oid.T_numeric, oid.T__numeric: | ||||||
|  | 		mod := fd.Mod - headerSize | ||||||
|  | 		precision = int64((mod >> 16) & 0xffff) | ||||||
|  | 		scale = int64(mod & 0xffff) | ||||||
|  | 		return precision, scale, true | ||||||
|  | 	default: | ||||||
|  | 		return 0, 0, false | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ColumnTypeScanType returns the value type that can be used to scan types into. | ||||||
|  | func (rs *rows) ColumnTypeScanType(index int) reflect.Type { | ||||||
|  | 	return rs.colTyps[index].Type() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ColumnTypeDatabaseTypeName return the database system type name. | ||||||
|  | func (rs *rows) ColumnTypeDatabaseTypeName(index int) string { | ||||||
|  | 	return rs.colTyps[index].Name() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ColumnTypeLength returns the length of the column type if the column is a | ||||||
|  | // variable length type. If the column is not a variable length type ok | ||||||
|  | // should return false. | ||||||
|  | func (rs *rows) ColumnTypeLength(index int) (length int64, ok bool) { | ||||||
|  | 	return rs.colTyps[index].Length() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ColumnTypePrecisionScale should return the precision and scale for decimal | ||||||
|  | // types. If not applicable, ok should be false. | ||||||
|  | func (rs *rows) ColumnTypePrecisionScale(index int) (precision, scale int64, ok bool) { | ||||||
|  | 	return rs.colTyps[index].PrecisionScale() | ||||||
|  | } | ||||||
							
								
								
									
										158
									
								
								vendor/github.com/lib/pq/ssl.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										158
									
								
								vendor/github.com/lib/pq/ssl.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,158 @@ | |||||||
|  | package pq | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"crypto/tls" | ||||||
|  | 	"crypto/x509" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"net" | ||||||
|  | 	"os" | ||||||
|  | 	"os/user" | ||||||
|  | 	"path/filepath" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // ssl generates a function to upgrade a net.Conn based on the "sslmode" and | ||||||
|  | // related settings. The function is nil when no upgrade should take place. | ||||||
|  | func ssl(o values) func(net.Conn) net.Conn { | ||||||
|  | 	verifyCaOnly := false | ||||||
|  | 	tlsConf := tls.Config{} | ||||||
|  | 	switch mode := o["sslmode"]; mode { | ||||||
|  | 	// "require" is the default. | ||||||
|  | 	case "", "require": | ||||||
|  | 		// We must skip TLS's own verification since it requires full | ||||||
|  | 		// verification since Go 1.3. | ||||||
|  | 		tlsConf.InsecureSkipVerify = true | ||||||
|  |  | ||||||
|  | 		// From http://www.postgresql.org/docs/current/static/libpq-ssl.html: | ||||||
|  | 		// | ||||||
|  | 		// Note: For backwards compatibility with earlier versions of | ||||||
|  | 		// PostgreSQL, if a root CA file exists, the behavior of | ||||||
|  | 		// sslmode=require will be the same as that of verify-ca, meaning the | ||||||
|  | 		// server certificate is validated against the CA. Relying on this | ||||||
|  | 		// behavior is discouraged, and applications that need certificate | ||||||
|  | 		// validation should always use verify-ca or verify-full. | ||||||
|  | 		if sslrootcert, ok := o["sslrootcert"]; ok { | ||||||
|  | 			if _, err := os.Stat(sslrootcert); err == nil { | ||||||
|  | 				verifyCaOnly = true | ||||||
|  | 			} else { | ||||||
|  | 				delete(o, "sslrootcert") | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	case "verify-ca": | ||||||
|  | 		// We must skip TLS's own verification since it requires full | ||||||
|  | 		// verification since Go 1.3. | ||||||
|  | 		tlsConf.InsecureSkipVerify = true | ||||||
|  | 		verifyCaOnly = true | ||||||
|  | 	case "verify-full": | ||||||
|  | 		tlsConf.ServerName = o["host"] | ||||||
|  | 	case "disable": | ||||||
|  | 		return nil | ||||||
|  | 	default: | ||||||
|  | 		errorf(`unsupported sslmode %q; only "require" (default), "verify-full", "verify-ca", and "disable" supported`, mode) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	sslClientCertificates(&tlsConf, o) | ||||||
|  | 	sslCertificateAuthority(&tlsConf, o) | ||||||
|  | 	sslRenegotiation(&tlsConf) | ||||||
|  |  | ||||||
|  | 	return func(conn net.Conn) net.Conn { | ||||||
|  | 		client := tls.Client(conn, &tlsConf) | ||||||
|  | 		if verifyCaOnly { | ||||||
|  | 			sslVerifyCertificateAuthority(client, &tlsConf) | ||||||
|  | 		} | ||||||
|  | 		return client | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // sslClientCertificates adds the certificate specified in the "sslcert" and | ||||||
|  | // "sslkey" settings, or if they aren't set, from the .postgresql directory | ||||||
|  | // in the user's home directory. The configured files must exist and have | ||||||
|  | // the correct permissions. | ||||||
|  | func sslClientCertificates(tlsConf *tls.Config, o values) { | ||||||
|  | 	// user.Current() might fail when cross-compiling. We have to ignore the | ||||||
|  | 	// error and continue without home directory defaults, since we wouldn't | ||||||
|  | 	// know from where to load them. | ||||||
|  | 	user, _ := user.Current() | ||||||
|  |  | ||||||
|  | 	// In libpq, the client certificate is only loaded if the setting is not blank. | ||||||
|  | 	// | ||||||
|  | 	// https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1036-L1037 | ||||||
|  | 	sslcert := o["sslcert"] | ||||||
|  | 	if len(sslcert) == 0 && user != nil { | ||||||
|  | 		sslcert = filepath.Join(user.HomeDir, ".postgresql", "postgresql.crt") | ||||||
|  | 	} | ||||||
|  | 	// https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1045 | ||||||
|  | 	if len(sslcert) == 0 { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	// https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1050:L1054 | ||||||
|  | 	if _, err := os.Stat(sslcert); os.IsNotExist(err) { | ||||||
|  | 		return | ||||||
|  | 	} else if err != nil { | ||||||
|  | 		panic(err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// In libpq, the ssl key is only loaded if the setting is not blank. | ||||||
|  | 	// | ||||||
|  | 	// https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1123-L1222 | ||||||
|  | 	sslkey := o["sslkey"] | ||||||
|  | 	if len(sslkey) == 0 && user != nil { | ||||||
|  | 		sslkey = filepath.Join(user.HomeDir, ".postgresql", "postgresql.key") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if len(sslkey) > 0 { | ||||||
|  | 		if err := sslKeyPermissions(sslkey); err != nil { | ||||||
|  | 			panic(err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	cert, err := tls.LoadX509KeyPair(sslcert, sslkey) | ||||||
|  | 	if err != nil { | ||||||
|  | 		panic(err) | ||||||
|  | 	} | ||||||
|  | 	tlsConf.Certificates = []tls.Certificate{cert} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // sslCertificateAuthority adds the RootCA specified in the "sslrootcert" setting. | ||||||
|  | func sslCertificateAuthority(tlsConf *tls.Config, o values) { | ||||||
|  | 	// In libpq, the root certificate is only loaded if the setting is not blank. | ||||||
|  | 	// | ||||||
|  | 	// https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L950-L951 | ||||||
|  | 	if sslrootcert := o["sslrootcert"]; len(sslrootcert) > 0 { | ||||||
|  | 		tlsConf.RootCAs = x509.NewCertPool() | ||||||
|  |  | ||||||
|  | 		cert, err := ioutil.ReadFile(sslrootcert) | ||||||
|  | 		if err != nil { | ||||||
|  | 			panic(err) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if !tlsConf.RootCAs.AppendCertsFromPEM(cert) { | ||||||
|  | 			errorf("couldn't parse pem in sslrootcert") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // sslVerifyCertificateAuthority carries out a TLS handshake to the server and | ||||||
|  | // verifies the presented certificate against the CA, i.e. the one specified in | ||||||
|  | // sslrootcert or the system CA if sslrootcert was not specified. | ||||||
|  | func sslVerifyCertificateAuthority(client *tls.Conn, tlsConf *tls.Config) { | ||||||
|  | 	err := client.Handshake() | ||||||
|  | 	if err != nil { | ||||||
|  | 		panic(err) | ||||||
|  | 	} | ||||||
|  | 	certs := client.ConnectionState().PeerCertificates | ||||||
|  | 	opts := x509.VerifyOptions{ | ||||||
|  | 		DNSName:       client.ConnectionState().ServerName, | ||||||
|  | 		Intermediates: x509.NewCertPool(), | ||||||
|  | 		Roots:         tlsConf.RootCAs, | ||||||
|  | 	} | ||||||
|  | 	for i, cert := range certs { | ||||||
|  | 		if i == 0 { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		opts.Intermediates.AddCert(cert) | ||||||
|  | 	} | ||||||
|  | 	_, err = certs[0].Verify(opts) | ||||||
|  | 	if err != nil { | ||||||
|  | 		panic(err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										14
									
								
								vendor/github.com/lib/pq/ssl_go1.7.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								vendor/github.com/lib/pq/ssl_go1.7.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | // +build go1.7 | ||||||
|  |  | ||||||
|  | package pq | ||||||
|  |  | ||||||
|  | import "crypto/tls" | ||||||
|  |  | ||||||
|  | // Accept renegotiation requests initiated by the backend. | ||||||
|  | // | ||||||
|  | // Renegotiation was deprecated then removed from PostgreSQL 9.5, but | ||||||
|  | // the default configuration of older versions has it enabled. Redshift | ||||||
|  | // also initiates renegotiations and cannot be reconfigured. | ||||||
|  | func sslRenegotiation(conf *tls.Config) { | ||||||
|  | 	conf.Renegotiation = tls.RenegotiateFreelyAsClient | ||||||
|  | } | ||||||
							
								
								
									
										20
									
								
								vendor/github.com/lib/pq/ssl_permissions.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								vendor/github.com/lib/pq/ssl_permissions.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | // +build !windows | ||||||
|  |  | ||||||
|  | package pq | ||||||
|  |  | ||||||
|  | import "os" | ||||||
|  |  | ||||||
|  | // sslKeyPermissions checks the permissions on user-supplied ssl key files. | ||||||
|  | // The key file should have very little access. | ||||||
|  | // | ||||||
|  | // libpq does not check key file permissions on Windows. | ||||||
|  | func sslKeyPermissions(sslkey string) error { | ||||||
|  | 	info, err := os.Stat(sslkey) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if info.Mode().Perm()&0077 != 0 { | ||||||
|  | 		return ErrSSLKeyHasWorldPermissions | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										8
									
								
								vendor/github.com/lib/pq/ssl_renegotiation.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								vendor/github.com/lib/pq/ssl_renegotiation.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | // +build !go1.7 | ||||||
|  |  | ||||||
|  | package pq | ||||||
|  |  | ||||||
|  | import "crypto/tls" | ||||||
|  |  | ||||||
|  | // Renegotiation is not supported by crypto/tls until Go 1.7. | ||||||
|  | func sslRenegotiation(*tls.Config) {} | ||||||
							
								
								
									
										9
									
								
								vendor/github.com/lib/pq/ssl_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/lib/pq/ssl_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | // +build windows | ||||||
|  |  | ||||||
|  | package pq | ||||||
|  |  | ||||||
|  | // sslKeyPermissions checks the permissions on user-supplied ssl key files. | ||||||
|  | // The key file should have very little access. | ||||||
|  | // | ||||||
|  | // libpq does not check key file permissions on Windows. | ||||||
|  | func sslKeyPermissions(string) error { return nil } | ||||||
							
								
								
									
										23
									
								
								vendor/github.com/lib/pq/uuid.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								vendor/github.com/lib/pq/uuid.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | |||||||
|  | package pq | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"encoding/hex" | ||||||
|  | 	"fmt" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // decodeUUIDBinary interprets the binary format of a uuid, returning it in text format. | ||||||
|  | func decodeUUIDBinary(src []byte) ([]byte, error) { | ||||||
|  | 	if len(src) != 16 { | ||||||
|  | 		return nil, fmt.Errorf("pq: unable to decode uuid; bad length: %d", len(src)) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	dst := make([]byte, 36) | ||||||
|  | 	dst[8], dst[13], dst[18], dst[23] = '-', '-', '-', '-' | ||||||
|  | 	hex.Encode(dst[0:], src[0:4]) | ||||||
|  | 	hex.Encode(dst[9:], src[4:6]) | ||||||
|  | 	hex.Encode(dst[14:], src[6:8]) | ||||||
|  | 	hex.Encode(dst[19:], src[8:10]) | ||||||
|  | 	hex.Encode(dst[24:], src[10:16]) | ||||||
|  |  | ||||||
|  | 	return dst, nil | ||||||
|  | } | ||||||
							
								
								
									
										12
									
								
								vendor/vendor.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								vendor/vendor.json
									
									
									
									
										vendored
									
									
								
							| @@ -624,16 +624,16 @@ | |||||||
| 			"revisionTime": "2016-10-16T15:41:25Z" | 			"revisionTime": "2016-10-16T15:41:25Z" | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			"checksumSHA1": "avqi4lkviHdrNJ92cXCwrw9x870=", | 			"checksumSHA1": "QV4HZTfaXvhD+5PcGM2p+7aCYYI=", | ||||||
| 			"path": "github.com/lib/pq", | 			"path": "github.com/lib/pq", | ||||||
| 			"revision": "d8eeeb8bae8896dd8e1b7e514ab0d396c4f12a1b", | 			"revision": "456514e2defec52e0cd37f90ccf17ec8b28295e2", | ||||||
| 			"revisionTime": "2016-11-03T02:43:54Z" | 			"revisionTime": "2017-10-19T22:30:07Z" | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			"checksumSHA1": "xppHi82MLqVx1eyQmbhTesAEjx8=", | 			"checksumSHA1": "AU3fA8Sm33Vj9PBoRPSeYfxLRuE=", | ||||||
| 			"path": "github.com/lib/pq/oid", | 			"path": "github.com/lib/pq/oid", | ||||||
| 			"revision": "d8eeeb8bae8896dd8e1b7e514ab0d396c4f12a1b", | 			"revision": "456514e2defec52e0cd37f90ccf17ec8b28295e2", | ||||||
| 			"revisionTime": "2016-11-03T02:43:54Z" | 			"revisionTime": "2017-10-19T22:30:07Z" | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			"checksumSHA1": "O3KUfEXQPfdQ+tCMpP2RAIRJJqY=", | 			"checksumSHA1": "O3KUfEXQPfdQ+tCMpP2RAIRJJqY=", | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user