summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/lib/pq
diff options
context:
space:
mode:
authorChristopher Speller <crspeller@gmail.com>2016-09-23 10:17:51 -0400
committerGitHub <noreply@github.com>2016-09-23 10:17:51 -0400
commit2ca0e8f9a0f9863555a26e984cde15efff9ef8f8 (patch)
treedaae1ee67b14a3d0a84424f2a304885d9e75ce2b /vendor/github.com/lib/pq
parent6d62d65b2dc85855aabea036cbd44f6059e19d13 (diff)
downloadchat-2ca0e8f9a0f9863555a26e984cde15efff9ef8f8.tar.gz
chat-2ca0e8f9a0f9863555a26e984cde15efff9ef8f8.tar.bz2
chat-2ca0e8f9a0f9863555a26e984cde15efff9ef8f8.zip
Updating golang dependancies (#4075)
Diffstat (limited to 'vendor/github.com/lib/pq')
-rwxr-xr-xvendor/github.com/lib/pq/.travis.sh73
-rw-r--r--vendor/github.com/lib/pq/.travis.yml75
-rw-r--r--vendor/github.com/lib/pq/README.md2
-rw-r--r--vendor/github.com/lib/pq/array.go727
-rw-r--r--vendor/github.com/lib/pq/array_test.go1153
-rw-r--r--vendor/github.com/lib/pq/certs/bogus_root.crt19
-rw-r--r--vendor/github.com/lib/pq/conn.go17
-rw-r--r--vendor/github.com/lib/pq/conn_test.go16
-rw-r--r--vendor/github.com/lib/pq/encode.go117
-rw-r--r--vendor/github.com/lib/pq/encode_test.go11
-rw-r--r--vendor/github.com/lib/pq/ssl_test.go43
11 files changed, 2151 insertions, 102 deletions
diff --git a/vendor/github.com/lib/pq/.travis.sh b/vendor/github.com/lib/pq/.travis.sh
new file mode 100755
index 000000000..ebf447030
--- /dev/null
+++ b/vendor/github.com/lib/pq/.travis.sh
@@ -0,0 +1,73 @@
+#!/bin/bash
+
+set -eu
+
+client_configure() {
+ sudo chmod 600 $PQSSLCERTTEST_PATH/postgresql.key
+}
+
+pgdg_repository() {
+ local sourcelist='sources.list.d/postgresql.list'
+
+ curl -sS 'https://www.postgresql.org/media/keys/ACCC4CF8.asc' | sudo apt-key add -
+ echo deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main $PGVERSION | sudo tee "/etc/apt/$sourcelist"
+ sudo apt-get -o Dir::Etc::sourcelist="$sourcelist" -o Dir::Etc::sourceparts='-' -o APT::Get::List-Cleanup='0' update
+}
+
+postgresql_configure() {
+ sudo tee /etc/postgresql/$PGVERSION/main/pg_hba.conf > /dev/null <<-config
+ local all all trust
+ hostnossl all pqgossltest 127.0.0.1/32 reject
+ hostnossl all pqgosslcert 127.0.0.1/32 reject
+ hostssl all pqgossltest 127.0.0.1/32 trust
+ hostssl all pqgosslcert 127.0.0.1/32 cert
+ host all all 127.0.0.1/32 trust
+ hostnossl all pqgossltest ::1/128 reject
+ hostnossl all pqgosslcert ::1/128 reject
+ hostssl all pqgossltest ::1/128 trust
+ hostssl all pqgosslcert ::1/128 cert
+ host all all ::1/128 trust
+ config
+
+ xargs sudo install -o postgres -g postgres -m 600 -t /var/lib/postgresql/$PGVERSION/main/ <<-certificates
+ certs/root.crt
+ certs/server.crt
+ certs/server.key
+ certificates
+
+ sort -VCu <<-versions ||
+ $PGVERSION
+ 9.2
+ versions
+ sudo tee -a /etc/postgresql/$PGVERSION/main/postgresql.conf > /dev/null <<-config
+ ssl_ca_file = 'root.crt'
+ ssl_cert_file = 'server.crt'
+ ssl_key_file = 'server.key'
+ config
+
+ echo 127.0.0.1 postgres | sudo tee -a /etc/hosts > /dev/null
+
+ sudo service postgresql restart
+}
+
+postgresql_install() {
+ xargs sudo apt-get -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confnew' install <<-packages
+ postgresql-$PGVERSION
+ postgresql-server-dev-$PGVERSION
+ postgresql-contrib-$PGVERSION
+ packages
+}
+
+postgresql_uninstall() {
+ sudo service postgresql stop
+ xargs sudo apt-get -y --purge remove <<-packages
+ libpq-dev
+ libpq5
+ postgresql
+ postgresql-client-common
+ postgresql-common
+ packages
+ sudo rm -rf /var/lib/postgresql
+}
+
+$1
diff --git a/vendor/github.com/lib/pq/.travis.yml b/vendor/github.com/lib/pq/.travis.yml
index d63afcb9f..d7b60f899 100644
--- a/vendor/github.com/lib/pq/.travis.yml
+++ b/vendor/github.com/lib/pq/.travis.yml
@@ -1,43 +1,12 @@
language: go
go:
- - 1.4
- 1.5
- 1.6
+ - 1.7
- tip
-before_install:
- - psql --version
- - sudo /etc/init.d/postgresql stop
- - sudo apt-get -y --purge remove postgresql libpq-dev libpq5 postgresql-client-common postgresql-common
- - sudo rm -rf /var/lib/postgresql
- - wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
- - sudo sh -c "echo deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main $PGVERSION >> /etc/apt/sources.list.d/postgresql.list"
- - sudo apt-get update -qq
- - sudo apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::="--force-confnew" install postgresql-$PGVERSION postgresql-server-dev-$PGVERSION postgresql-contrib-$PGVERSION
- - sudo chmod 777 /etc/postgresql/$PGVERSION/main/pg_hba.conf
- - echo "local all postgres trust" > /etc/postgresql/$PGVERSION/main/pg_hba.conf
- - echo "local all all trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- - echo "hostnossl all pqgossltest 127.0.0.1/32 reject" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- - echo "hostnossl all pqgosslcert 127.0.0.1/32 reject" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- - echo "hostssl all pqgossltest 127.0.0.1/32 trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- - echo "hostssl all pqgosslcert 127.0.0.1/32 cert" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- - echo "host all all 127.0.0.1/32 trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- - echo "hostnossl all pqgossltest ::1/128 reject" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- - echo "hostnossl all pqgosslcert ::1/128 reject" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- - echo "hostssl all pqgossltest ::1/128 trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- - echo "hostssl all pqgosslcert ::1/128 cert" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- - echo "host all all ::1/128 trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- - sudo install -o postgres -g postgres -m 600 -t /var/lib/postgresql/$PGVERSION/main/ certs/server.key certs/server.crt certs/root.crt
- - sudo bash -c "[[ '${PGVERSION}' < '9.2' ]] || (echo \"ssl_cert_file = 'server.crt'\" >> /etc/postgresql/$PGVERSION/main/postgresql.conf)"
- - sudo bash -c "[[ '${PGVERSION}' < '9.2' ]] || (echo \"ssl_key_file = 'server.key'\" >> /etc/postgresql/$PGVERSION/main/postgresql.conf)"
- - sudo bash -c "[[ '${PGVERSION}' < '9.2' ]] || (echo \"ssl_ca_file = 'root.crt'\" >> /etc/postgresql/$PGVERSION/main/postgresql.conf)"
- - sudo sh -c "echo 127.0.0.1 postgres >> /etc/hosts"
- - sudo ls -l /var/lib/postgresql/$PGVERSION/main/
- - sudo cat /etc/postgresql/$PGVERSION/main/postgresql.conf
- - sudo chmod 600 $PQSSLCERTTEST_PATH/postgresql.key
- - sudo /etc/init.d/postgresql restart
- - go get golang.org/x/tools/cmd/goimports
+sudo: true
env:
global:
@@ -46,23 +15,29 @@ env:
- PQSSLCERTTEST_PATH=$PWD/certs
- PGHOST=127.0.0.1
matrix:
- - PGVERSION=9.5 PQTEST_BINARY_PARAMETERS=yes
- - PGVERSION=9.4 PQTEST_BINARY_PARAMETERS=yes
- - PGVERSION=9.3 PQTEST_BINARY_PARAMETERS=yes
- - PGVERSION=9.2 PQTEST_BINARY_PARAMETERS=yes
- - PGVERSION=9.1 PQTEST_BINARY_PARAMETERS=yes
- - PGVERSION=9.0 PQTEST_BINARY_PARAMETERS=yes
- - PGVERSION=9.5 PQTEST_BINARY_PARAMETERS=no
- - PGVERSION=9.4 PQTEST_BINARY_PARAMETERS=no
- - PGVERSION=9.3 PQTEST_BINARY_PARAMETERS=no
- - PGVERSION=9.2 PQTEST_BINARY_PARAMETERS=no
- - PGVERSION=9.1 PQTEST_BINARY_PARAMETERS=no
- - PGVERSION=9.0 PQTEST_BINARY_PARAMETERS=no
+ - PGVERSION=9.5
+ - PGVERSION=9.4
+ - PGVERSION=9.3
+ - PGVERSION=9.2
+ - PGVERSION=9.1
+ - PGVERSION=9.0
-script:
- - result=$(goimports -d -e $(find -name \*.go)); test -z "$result" || (echo "$result" && false) && go vet ./... && go test -v ./...
+before_install:
+ - ./.travis.sh postgresql_uninstall
+ - ./.travis.sh pgdg_repository
+ - ./.travis.sh postgresql_install
+ - ./.travis.sh postgresql_configure
+ - ./.travis.sh client_configure
+ - go get golang.org/x/tools/cmd/goimports
before_script:
- - psql -c 'create database pqgotest' -U postgres
- - psql -c 'create user pqgossltest' -U postgres
- - psql -c 'create user pqgosslcert' -U postgres
+ - createdb pqgotest
+ - createuser -DRS pqgossltest
+ - createuser -DRS pqgosslcert
+
+script:
+ - >
+ goimports -d -e $(find -name '*.go') | awk '{ print } END { exit NR == 0 ? 0 : 1 }'
+ - go vet ./...
+ - PQTEST_BINARY_PARAMETERS=no go test -v ./...
+ - PQTEST_BINARY_PARAMETERS=yes go test -v ./...
diff --git a/vendor/github.com/lib/pq/README.md b/vendor/github.com/lib/pq/README.md
index 148451e80..5eb9e1445 100644
--- a/vendor/github.com/lib/pq/README.md
+++ b/vendor/github.com/lib/pq/README.md
@@ -85,7 +85,7 @@ code still exists in here.
* Keith Rarick (kr)
* Kir Shatrov (kirs)
* Lann Martin (lann)
-* Maciek Sakrejda (deafbybeheading)
+* Maciek Sakrejda (uhoh-itsmaciek)
* Marc Brinkmann (mbr)
* Marko Tiikkaja (johto)
* Matt Newberry (MattNewberry)
diff --git a/vendor/github.com/lib/pq/array.go b/vendor/github.com/lib/pq/array.go
new file mode 100644
index 000000000..27eb07a9e
--- /dev/null
+++ b/vendor/github.com/lib/pq/array.go
@@ -0,0 +1,727 @@
+package pq
+
+import (
+ "bytes"
+ "database/sql"
+ "database/sql/driver"
+ "encoding/hex"
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+)
+
+var typeByteSlice = reflect.TypeOf([]byte{})
+var typeDriverValuer = reflect.TypeOf((*driver.Valuer)(nil)).Elem()
+var typeSqlScanner = reflect.TypeOf((*sql.Scanner)(nil)).Elem()
+
+// Array returns the optimal driver.Valuer and sql.Scanner for an array or
+// slice of any dimension.
+//
+// For example:
+// db.Query(`SELECT * FROM t WHERE id = ANY($1)`, pq.Array([]int{235, 401}))
+//
+// var x []sql.NullInt64
+// db.QueryRow('SELECT ARRAY[235, 401]').Scan(pq.Array(&x))
+//
+// Scanning multi-dimensional arrays is not supported. Arrays where the lower
+// bound is not one (such as `[0:0]={1}') are not supported.
+func Array(a interface{}) interface {
+ driver.Valuer
+ sql.Scanner
+} {
+ switch a := a.(type) {
+ case []bool:
+ return (*BoolArray)(&a)
+ case []float64:
+ return (*Float64Array)(&a)
+ case []int64:
+ return (*Int64Array)(&a)
+ case []string:
+ return (*StringArray)(&a)
+
+ case *[]bool:
+ return (*BoolArray)(a)
+ case *[]float64:
+ return (*Float64Array)(a)
+ case *[]int64:
+ return (*Int64Array)(a)
+ case *[]string:
+ return (*StringArray)(a)
+ }
+
+ return GenericArray{a}
+}
+
+// ArrayDelimiter may be optionally implemented by driver.Valuer or sql.Scanner
+// to override the array delimiter used by GenericArray.
+type ArrayDelimiter interface {
+ // ArrayDelimiter returns the delimiter character(s) for this element's type.
+ ArrayDelimiter() string
+}
+
+// BoolArray represents a one-dimensional array of the PostgreSQL boolean type.
+type BoolArray []bool
+
+// Scan implements the sql.Scanner interface.
+func (a *BoolArray) Scan(src interface{}) error {
+ switch src := src.(type) {
+ case []byte:
+ return a.scanBytes(src)
+ case string:
+ return a.scanBytes([]byte(src))
+ }
+
+ return fmt.Errorf("pq: cannot convert %T to BoolArray", src)
+}
+
+func (a *BoolArray) scanBytes(src []byte) error {
+ elems, err := scanLinearArray(src, []byte{','}, "BoolArray")
+ if err != nil {
+ return err
+ }
+ if len(elems) == 0 {
+ *a = (*a)[:0]
+ } else {
+ b := make(BoolArray, len(elems))
+ for i, v := range elems {
+ if len(v) != 1 {
+ return fmt.Errorf("pq: could not parse boolean array index %d: invalid boolean %q", i, v)
+ }
+ switch v[0] {
+ case 't':
+ b[i] = true
+ case 'f':
+ b[i] = false
+ default:
+ return fmt.Errorf("pq: could not parse boolean array index %d: invalid boolean %q", i, v)
+ }
+ }
+ *a = b
+ }
+ return nil
+}
+
+// Value implements the driver.Valuer interface.
+func (a BoolArray) Value() (driver.Value, error) {
+ if a == nil {
+ return nil, nil
+ }
+
+ if n := len(a); n > 0 {
+ // There will be exactly two curly brackets, N bytes of values,
+ // and N-1 bytes of delimiters.
+ b := make([]byte, 1+2*n)
+
+ for i := 0; i < n; i++ {
+ b[2*i] = ','
+ if a[i] {
+ b[1+2*i] = 't'
+ } else {
+ b[1+2*i] = 'f'
+ }
+ }
+
+ b[0] = '{'
+ b[2*n] = '}'
+
+ return string(b), nil
+ }
+
+ return "{}", nil
+}
+
+// ByteaArray represents a one-dimensional array of the PostgreSQL bytea type.
+type ByteaArray [][]byte
+
+// Scan implements the sql.Scanner interface.
+func (a *ByteaArray) Scan(src interface{}) error {
+ switch src := src.(type) {
+ case []byte:
+ return a.scanBytes(src)
+ case string:
+ return a.scanBytes([]byte(src))
+ }
+
+ return fmt.Errorf("pq: cannot convert %T to ByteaArray", src)
+}
+
+func (a *ByteaArray) scanBytes(src []byte) error {
+ elems, err := scanLinearArray(src, []byte{','}, "ByteaArray")
+ if err != nil {
+ return err
+ }
+ if len(elems) == 0 {
+ *a = (*a)[:0]
+ } else {
+ b := make(ByteaArray, len(elems))
+ for i, v := range elems {
+ b[i], err = parseBytea(v)
+ if err != nil {
+ return fmt.Errorf("could not parse bytea array index %d: %s", i, err.Error())
+ }
+ }
+ *a = b
+ }
+ return nil
+}
+
+// Value implements the driver.Valuer interface. It uses the "hex" format which
+// is only supported on PostgreSQL 9.0 or newer.
+func (a ByteaArray) Value() (driver.Value, error) {
+ if a == nil {
+ return nil, nil
+ }
+
+ if n := len(a); n > 0 {
+ // There will be at least two curly brackets, 2*N bytes of quotes,
+ // 3*N bytes of hex formatting, and N-1 bytes of delimiters.
+ size := 1 + 6*n
+ for _, x := range a {
+ size += hex.EncodedLen(len(x))
+ }
+
+ b := make([]byte, size)
+
+ for i, s := 0, b; i < n; i++ {
+ o := copy(s, `,"\\x`)
+ o += hex.Encode(s[o:], a[i])
+ s[o] = '"'
+ s = s[o+1:]
+ }
+
+ b[0] = '{'
+ b[size-1] = '}'
+
+ return string(b), nil
+ }
+
+ return "{}", nil
+}
+
+// Float64Array represents a one-dimensional array of the PostgreSQL double
+// precision type.
+type Float64Array []float64
+
+// Scan implements the sql.Scanner interface.
+func (a *Float64Array) Scan(src interface{}) error {
+ switch src := src.(type) {
+ case []byte:
+ return a.scanBytes(src)
+ case string:
+ return a.scanBytes([]byte(src))
+ }
+
+ return fmt.Errorf("pq: cannot convert %T to Float64Array", src)
+}
+
+func (a *Float64Array) scanBytes(src []byte) error {
+ elems, err := scanLinearArray(src, []byte{','}, "Float64Array")
+ if err != nil {
+ return err
+ }
+ if len(elems) == 0 {
+ *a = (*a)[:0]
+ } else {
+ b := make(Float64Array, len(elems))
+ for i, v := range elems {
+ if b[i], err = strconv.ParseFloat(string(v), 64); err != nil {
+ return fmt.Errorf("pq: parsing array element index %d: %v", i, err)
+ }
+ }
+ *a = b
+ }
+ return nil
+}
+
+// Value implements the driver.Valuer interface.
+func (a Float64Array) Value() (driver.Value, error) {
+ if a == nil {
+ return nil, nil
+ }
+
+ if n := len(a); n > 0 {
+ // There will be at least two curly brackets, N bytes of values,
+ // and N-1 bytes of delimiters.
+ b := make([]byte, 1, 1+2*n)
+ b[0] = '{'
+
+ b = strconv.AppendFloat(b, a[0], 'f', -1, 64)
+ for i := 1; i < n; i++ {
+ b = append(b, ',')
+ b = strconv.AppendFloat(b, a[i], 'f', -1, 64)
+ }
+
+ return string(append(b, '}')), nil
+ }
+
+ return "{}", nil
+}
+
+// GenericArray implements the driver.Valuer and sql.Scanner interfaces for
+// an array or slice of any dimension.
+type GenericArray struct{ A interface{} }
+
+func (GenericArray) evaluateDestination(rt reflect.Type) (reflect.Type, func([]byte, reflect.Value) error, string) {
+ var assign func([]byte, reflect.Value) error
+ var del = ","
+
+ // TODO calculate the assign function for other types
+ // TODO repeat this section on the element type of arrays or slices (multidimensional)
+ {
+ if reflect.PtrTo(rt).Implements(typeSqlScanner) {
+ // dest is always addressable because it is an element of a slice.
+ assign = func(src []byte, dest reflect.Value) (err error) {
+ ss := dest.Addr().Interface().(sql.Scanner)
+ if src == nil {
+ err = ss.Scan(nil)
+ } else {
+ err = ss.Scan(src)
+ }
+ return
+ }
+ goto FoundType
+ }
+
+ assign = func([]byte, reflect.Value) error {
+ return fmt.Errorf("pq: scanning to %s is not implemented; only sql.Scanner", rt)
+ }
+ }
+
+FoundType:
+
+ if ad, ok := reflect.Zero(rt).Interface().(ArrayDelimiter); ok {
+ del = ad.ArrayDelimiter()
+ }
+
+ return rt, assign, del
+}
+
+// Scan implements the sql.Scanner interface.
+func (a GenericArray) Scan(src interface{}) error {
+ dpv := reflect.ValueOf(a.A)
+ switch {
+ case dpv.Kind() != reflect.Ptr:
+ return fmt.Errorf("pq: destination %T is not a pointer to array or slice", a.A)
+ case dpv.IsNil():
+ return fmt.Errorf("pq: destination %T is nil", a.A)
+ }
+
+ dv := dpv.Elem()
+ switch dv.Kind() {
+ case reflect.Slice:
+ case reflect.Array:
+ default:
+ return fmt.Errorf("pq: destination %T is not a pointer to array or slice", a.A)
+ }
+
+ switch src := src.(type) {
+ case []byte:
+ return a.scanBytes(src, dv)
+ case string:
+ return a.scanBytes([]byte(src), dv)
+ }
+
+ return fmt.Errorf("pq: cannot convert %T to %s", src, dv.Type())
+}
+
+func (a GenericArray) scanBytes(src []byte, dv reflect.Value) error {
+ dtype, assign, del := a.evaluateDestination(dv.Type().Elem())
+ dims, elems, err := parseArray(src, []byte(del))
+ if err != nil {
+ return err
+ }
+
+ // TODO allow multidimensional
+
+ if len(dims) > 1 {
+ return fmt.Errorf("pq: scanning from multidimensional ARRAY%s is not implemented",
+ strings.Replace(fmt.Sprint(dims), " ", "][", -1))
+ }
+
+ // Treat a zero-dimensional array like an array with a single dimension of zero.
+ if len(dims) == 0 {
+ dims = append(dims, 0)
+ }
+
+ for i, rt := 0, dv.Type(); i < len(dims); i, rt = i+1, rt.Elem() {
+ switch rt.Kind() {
+ case reflect.Slice:
+ case reflect.Array:
+ if rt.Len() != dims[i] {
+ return fmt.Errorf("pq: cannot convert ARRAY%s to %s",
+ strings.Replace(fmt.Sprint(dims), " ", "][", -1), dv.Type())
+ }
+ default:
+ // TODO handle multidimensional
+ }
+ }
+
+ values := reflect.MakeSlice(reflect.SliceOf(dtype), len(elems), len(elems))
+ for i, e := range elems {
+ if err := assign(e, values.Index(i)); err != nil {
+ return fmt.Errorf("pq: parsing array element index %d: %v", i, err)
+ }
+ }
+
+ // TODO handle multidimensional
+
+ switch dv.Kind() {
+ case reflect.Slice:
+ dv.Set(values.Slice(0, dims[0]))
+ case reflect.Array:
+ for i := 0; i < dims[0]; i++ {
+ dv.Index(i).Set(values.Index(i))
+ }
+ }
+
+ return nil
+}
+
+// Value implements the driver.Valuer interface.
+func (a GenericArray) Value() (driver.Value, error) {
+ if a.A == nil {
+ return nil, nil
+ }
+
+ rv := reflect.ValueOf(a.A)
+
+ if k := rv.Kind(); k != reflect.Array && k != reflect.Slice {
+ return nil, fmt.Errorf("pq: Unable to convert %T to array", a.A)
+ }
+
+ if n := rv.Len(); n > 0 {
+ // There will be at least two curly brackets, N bytes of values,
+ // and N-1 bytes of delimiters.
+ b := make([]byte, 0, 1+2*n)
+
+ b, _, err := appendArray(b, rv, n)
+ return string(b), err
+ }
+
+ return "{}", nil
+}
+
+// Int64Array represents a one-dimensional array of the PostgreSQL integer types.
+type Int64Array []int64
+
+// Scan implements the sql.Scanner interface.
+func (a *Int64Array) Scan(src interface{}) error {
+ switch src := src.(type) {
+ case []byte:
+ return a.scanBytes(src)
+ case string:
+ return a.scanBytes([]byte(src))
+ }
+
+ return fmt.Errorf("pq: cannot convert %T to Int64Array", src)
+}
+
+func (a *Int64Array) scanBytes(src []byte) error {
+ elems, err := scanLinearArray(src, []byte{','}, "Int64Array")
+ if err != nil {
+ return err
+ }
+ if len(elems) == 0 {
+ *a = (*a)[:0]
+ } else {
+ b := make(Int64Array, len(elems))
+ for i, v := range elems {
+ if b[i], err = strconv.ParseInt(string(v), 10, 64); err != nil {
+ return fmt.Errorf("pq: parsing array element index %d: %v", i, err)
+ }
+ }
+ *a = b
+ }
+ return nil
+}
+
+// Value implements the driver.Valuer interface.
+func (a Int64Array) Value() (driver.Value, error) {
+ if a == nil {
+ return nil, nil
+ }
+
+ if n := len(a); n > 0 {
+ // There will be at least two curly brackets, N bytes of values,
+ // and N-1 bytes of delimiters.
+ b := make([]byte, 1, 1+2*n)
+ b[0] = '{'
+
+ b = strconv.AppendInt(b, a[0], 10)
+ for i := 1; i < n; i++ {
+ b = append(b, ',')
+ b = strconv.AppendInt(b, a[i], 10)
+ }
+
+ return string(append(b, '}')), nil
+ }
+
+ return "{}", nil
+}
+
+// StringArray represents a one-dimensional array of the PostgreSQL character types.
+type StringArray []string
+
+// Scan implements the sql.Scanner interface.
+func (a *StringArray) Scan(src interface{}) error {
+ switch src := src.(type) {
+ case []byte:
+ return a.scanBytes(src)
+ case string:
+ return a.scanBytes([]byte(src))
+ }
+
+ return fmt.Errorf("pq: cannot convert %T to StringArray", src)
+}
+
+func (a *StringArray) scanBytes(src []byte) error {
+ elems, err := scanLinearArray(src, []byte{','}, "StringArray")
+ if err != nil {
+ return err
+ }
+ if len(elems) == 0 {
+ *a = (*a)[:0]
+ } else {
+ b := make(StringArray, len(elems))
+ for i, v := range elems {
+ if b[i] = string(v); v == nil {
+ return fmt.Errorf("pq: parsing array element index %d: cannot convert nil to string", i)
+ }
+ }
+ *a = b
+ }
+ return nil
+}
+
+// Value implements the driver.Valuer interface.
+func (a StringArray) Value() (driver.Value, error) {
+ if a == nil {
+ return nil, nil
+ }
+
+ if n := len(a); n > 0 {
+ // There will be at least two curly brackets, 2*N bytes of quotes,
+ // and N-1 bytes of delimiters.
+ b := make([]byte, 1, 1+3*n)
+ b[0] = '{'
+
+ b = appendArrayQuotedBytes(b, []byte(a[0]))
+ for i := 1; i < n; i++ {
+ b = append(b, ',')
+ b = appendArrayQuotedBytes(b, []byte(a[i]))
+ }
+
+ return string(append(b, '}')), nil
+ }
+
+ return "{}", nil
+}
+
+// appendArray appends rv to the buffer, returning the extended buffer and
+// the delimiter used between elements.
+//
+// It panics when n <= 0 or rv's Kind is not reflect.Array nor reflect.Slice.
+func appendArray(b []byte, rv reflect.Value, n int) ([]byte, string, error) {
+ var del string
+ var err error
+
+ b = append(b, '{')
+
+ if b, del, err = appendArrayElement(b, rv.Index(0)); err != nil {
+ return b, del, err
+ }
+
+ for i := 1; i < n; i++ {
+ b = append(b, del...)
+ if b, del, err = appendArrayElement(b, rv.Index(i)); err != nil {
+ return b, del, err
+ }
+ }
+
+ return append(b, '}'), del, nil
+}
+
+// appendArrayElement appends rv to the buffer, returning the extended buffer
+// and the delimiter to use before the next element.
+//
+// When rv's Kind is neither reflect.Array nor reflect.Slice, it is converted
+// using driver.DefaultParameterConverter and the resulting []byte or string
+// is double-quoted.
+//
+// See http://www.postgresql.org/docs/current/static/arrays.html#ARRAYS-IO
+func appendArrayElement(b []byte, rv reflect.Value) ([]byte, string, error) {
+ if k := rv.Kind(); k == reflect.Array || k == reflect.Slice {
+ if t := rv.Type(); t != typeByteSlice && !t.Implements(typeDriverValuer) {
+ if n := rv.Len(); n > 0 {
+ return appendArray(b, rv, n)
+ }
+
+ return b, "", nil
+ }
+ }
+
+ var del string = ","
+ var err error
+ var iv interface{} = rv.Interface()
+
+ if ad, ok := iv.(ArrayDelimiter); ok {
+ del = ad.ArrayDelimiter()
+ }
+
+ if iv, err = driver.DefaultParameterConverter.ConvertValue(iv); err != nil {
+ return b, del, err
+ }
+
+ switch v := iv.(type) {
+ case nil:
+ return append(b, "NULL"...), del, nil
+ case []byte:
+ return appendArrayQuotedBytes(b, v), del, nil
+ case string:
+ return appendArrayQuotedBytes(b, []byte(v)), del, nil
+ }
+
+ b, err = appendValue(b, iv)
+ return b, del, err
+}
+
+func appendArrayQuotedBytes(b, v []byte) []byte {
+ b = append(b, '"')
+ for {
+ i := bytes.IndexAny(v, `"\`)
+ if i < 0 {
+ b = append(b, v...)
+ break
+ }
+ if i > 0 {
+ b = append(b, v[:i]...)
+ }
+ b = append(b, '\\', v[i])
+ v = v[i+1:]
+ }
+ return append(b, '"')
+}
+
+func appendValue(b []byte, v driver.Value) ([]byte, error) {
+ return append(b, encode(nil, v, 0)...), nil
+}
+
+// parseArray extracts the dimensions and elements of an array represented in
+// text format. Only representations emitted by the backend are supported.
+// Notably, whitespace around brackets and delimiters is significant, and NULL
+// is case-sensitive.
+//
+// See http://www.postgresql.org/docs/current/static/arrays.html#ARRAYS-IO
+func parseArray(src, del []byte) (dims []int, elems [][]byte, err error) {
+ var depth, i int
+
+ if len(src) < 1 || src[0] != '{' {
+ return nil, nil, fmt.Errorf("pq: unable to parse array; expected %q at offset %d", '{', 0)
+ }
+
+Open:
+ for i < len(src) {
+ switch src[i] {
+ case '{':
+ depth++
+ i++
+ case '}':
+ elems = make([][]byte, 0)
+ goto Close
+ default:
+ break Open
+ }
+ }
+ dims = make([]int, i)
+
+Element:
+ for i < len(src) {
+ switch src[i] {
+ case '{':
+ depth++
+ dims[depth-1] = 0
+ i++
+ case '"':
+ var elem = []byte{}
+ var escape bool
+ for i++; i < len(src); i++ {
+ if escape {
+ elem = append(elem, src[i])
+ escape = false
+ } else {
+ switch src[i] {
+ default:
+ elem = append(elem, src[i])
+ case '\\':
+ escape = true
+ case '"':
+ elems = append(elems, elem)
+ i++
+ break Element
+ }
+ }
+ }
+ default:
+ for start := i; i < len(src); i++ {
+ if bytes.HasPrefix(src[i:], del) || src[i] == '}' {
+ elem := src[start:i]
+ if len(elem) == 0 {
+ return nil, nil, fmt.Errorf("pq: unable to parse array; unexpected %q at offset %d", src[i], i)
+ }
+ if bytes.Equal(elem, []byte("NULL")) {
+ elem = nil
+ }
+ elems = append(elems, elem)
+ break Element
+ }
+ }
+ }
+ }
+
+ for i < len(src) {
+ if bytes.HasPrefix(src[i:], del) {
+ dims[depth-1]++
+ i += len(del)
+ goto Element
+ } else if src[i] == '}' {
+ dims[depth-1]++
+ depth--
+ i++
+ } else {
+ return nil, nil, fmt.Errorf("pq: unable to parse array; unexpected %q at offset %d", src[i], i)
+ }
+ }
+
+Close:
+ for i < len(src) {
+ if src[i] == '}' && depth > 0 {
+ depth--
+ i++
+ } else {
+ return nil, nil, fmt.Errorf("pq: unable to parse array; unexpected %q at offset %d", src[i], i)
+ }
+ }
+ if depth > 0 {
+ err = fmt.Errorf("pq: unable to parse array; expected %q at offset %d", '}', i)
+ }
+ if err == nil {
+ for _, d := range dims {
+ if (len(elems) % d) != 0 {
+ err = fmt.Errorf("pq: multidimensional arrays must have elements with matching dimensions")
+ }
+ }
+ }
+ return
+}
+
+func scanLinearArray(src, del []byte, typ string) (elems [][]byte, err error) {
+ dims, elems, err := parseArray(src, del)
+ if err != nil {
+ return nil, err
+ }
+ if len(dims) > 1 {
+ return nil, fmt.Errorf("pq: cannot convert ARRAY%s to %s", strings.Replace(fmt.Sprint(dims), " ", "][", -1), typ)
+ }
+ return elems, err
+}
diff --git a/vendor/github.com/lib/pq/array_test.go b/vendor/github.com/lib/pq/array_test.go
new file mode 100644
index 000000000..96402fd4a
--- /dev/null
+++ b/vendor/github.com/lib/pq/array_test.go
@@ -0,0 +1,1153 @@
+package pq
+
+import (
+ "bytes"
+ "database/sql"
+ "database/sql/driver"
+ "math/rand"
+ "reflect"
+ "strings"
+ "testing"
+)
+
+func TestParseArray(t *testing.T) {
+ for _, tt := range []struct {
+ input string
+ delim string
+ dims []int
+ elems [][]byte
+ }{
+ {`{}`, `,`, nil, [][]byte{}},
+ {`{NULL}`, `,`, []int{1}, [][]byte{nil}},
+ {`{a}`, `,`, []int{1}, [][]byte{{'a'}}},
+ {`{a,b}`, `,`, []int{2}, [][]byte{{'a'}, {'b'}}},
+ {`{{a,b}}`, `,`, []int{1, 2}, [][]byte{{'a'}, {'b'}}},
+ {`{{a},{b}}`, `,`, []int{2, 1}, [][]byte{{'a'}, {'b'}}},
+ {`{{{a,b},{c,d},{e,f}}}`, `,`, []int{1, 3, 2}, [][]byte{
+ {'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'},
+ }},
+ {`{""}`, `,`, []int{1}, [][]byte{{}}},
+ {`{","}`, `,`, []int{1}, [][]byte{{','}}},
+ {`{",",","}`, `,`, []int{2}, [][]byte{{','}, {','}}},
+ {`{{",",","}}`, `,`, []int{1, 2}, [][]byte{{','}, {','}}},
+ {`{{","},{","}}`, `,`, []int{2, 1}, [][]byte{{','}, {','}}},
+ {`{{{",",","},{",",","},{",",","}}}`, `,`, []int{1, 3, 2}, [][]byte{
+ {','}, {','}, {','}, {','}, {','}, {','},
+ }},
+ {`{"\"}"}`, `,`, []int{1}, [][]byte{{'"', '}'}}},
+ {`{"\"","\""}`, `,`, []int{2}, [][]byte{{'"'}, {'"'}}},
+ {`{{"\"","\""}}`, `,`, []int{1, 2}, [][]byte{{'"'}, {'"'}}},
+ {`{{"\""},{"\""}}`, `,`, []int{2, 1}, [][]byte{{'"'}, {'"'}}},
+ {`{{{"\"","\""},{"\"","\""},{"\"","\""}}}`, `,`, []int{1, 3, 2}, [][]byte{
+ {'"'}, {'"'}, {'"'}, {'"'}, {'"'}, {'"'},
+ }},
+ {`{axyzb}`, `xyz`, []int{2}, [][]byte{{'a'}, {'b'}}},
+ } {
+ dims, elems, err := parseArray([]byte(tt.input), []byte(tt.delim))
+
+ if err != nil {
+ t.Fatalf("Expected no error for %q, got %q", tt.input, err)
+ }
+ if !reflect.DeepEqual(dims, tt.dims) {
+ t.Errorf("Expected %v dimensions for %q, got %v", tt.dims, tt.input, dims)
+ }
+ if !reflect.DeepEqual(elems, tt.elems) {
+ t.Errorf("Expected %v elements for %q, got %v", tt.elems, tt.input, elems)
+ }
+ }
+}
+
+func TestParseArrayError(t *testing.T) {
+ for _, tt := range []struct {
+ input, err string
+ }{
+ {``, "expected '{' at offset 0"},
+ {`x`, "expected '{' at offset 0"},
+ {`}`, "expected '{' at offset 0"},
+ {`{`, "expected '}' at offset 1"},
+ {`{{}`, "expected '}' at offset 3"},
+ {`{}}`, "unexpected '}' at offset 2"},
+ {`{,}`, "unexpected ',' at offset 1"},
+ {`{,x}`, "unexpected ',' at offset 1"},
+ {`{x,}`, "unexpected '}' at offset 3"},
+ {`{""x}`, "unexpected 'x' at offset 3"},
+ {`{{a},{b,c}}`, "multidimensional arrays must have elements with matching dimensions"},
+ } {
+ _, _, err := parseArray([]byte(tt.input), []byte{','})
+
+ if err == nil {
+ t.Fatalf("Expected error for %q, got none", tt.input)
+ }
+ if !strings.Contains(err.Error(), tt.err) {
+ t.Errorf("Expected error to contain %q for %q, got %q", tt.err, tt.input, err)
+ }
+ }
+}
+
+func TestArrayScanner(t *testing.T) {
+ var s sql.Scanner
+
+ s = Array(&[]bool{})
+ if _, ok := s.(*BoolArray); !ok {
+ t.Errorf("Expected *BoolArray, got %T", s)
+ }
+
+ s = Array(&[]float64{})
+ if _, ok := s.(*Float64Array); !ok {
+ t.Errorf("Expected *Float64Array, got %T", s)
+ }
+
+ s = Array(&[]int64{})
+ if _, ok := s.(*Int64Array); !ok {
+ t.Errorf("Expected *Int64Array, got %T", s)
+ }
+
+ s = Array(&[]string{})
+ if _, ok := s.(*StringArray); !ok {
+ t.Errorf("Expected *StringArray, got %T", s)
+ }
+
+ for _, tt := range []interface{}{
+ &[]sql.Scanner{},
+ &[][]bool{},
+ &[][]float64{},
+ &[][]int64{},
+ &[][]string{},
+ } {
+ s = Array(tt)
+ if _, ok := s.(GenericArray); !ok {
+ t.Errorf("Expected GenericArray for %T, got %T", tt, s)
+ }
+ }
+}
+
+func TestArrayValuer(t *testing.T) {
+ var v driver.Valuer
+
+ v = Array([]bool{})
+ if _, ok := v.(*BoolArray); !ok {
+ t.Errorf("Expected *BoolArray, got %T", v)
+ }
+
+ v = Array([]float64{})
+ if _, ok := v.(*Float64Array); !ok {
+ t.Errorf("Expected *Float64Array, got %T", v)
+ }
+
+ v = Array([]int64{})
+ if _, ok := v.(*Int64Array); !ok {
+ t.Errorf("Expected *Int64Array, got %T", v)
+ }
+
+ v = Array([]string{})
+ if _, ok := v.(*StringArray); !ok {
+ t.Errorf("Expected *StringArray, got %T", v)
+ }
+
+ for _, tt := range []interface{}{
+ nil,
+ []driver.Value{},
+ [][]bool{},
+ [][]float64{},
+ [][]int64{},
+ [][]string{},
+ } {
+ v = Array(tt)
+ if _, ok := v.(GenericArray); !ok {
+ t.Errorf("Expected GenericArray for %T, got %T", tt, v)
+ }
+ }
+}
+
+func TestBoolArrayScanUnsupported(t *testing.T) {
+ var arr BoolArray
+ err := arr.Scan(1)
+
+ if err == nil {
+ t.Fatal("Expected error when scanning from int")
+ }
+ if !strings.Contains(err.Error(), "int to BoolArray") {
+ t.Errorf("Expected type to be mentioned when scanning, got %q", err)
+ }
+}
+
+var BoolArrayStringTests = []struct {
+ str string
+ arr BoolArray
+}{
+ {`{}`, BoolArray{}},
+ {`{t}`, BoolArray{true}},
+ {`{f,t}`, BoolArray{false, true}},
+}
+
+func TestBoolArrayScanBytes(t *testing.T) {
+ for _, tt := range BoolArrayStringTests {
+ bytes := []byte(tt.str)
+ arr := BoolArray{true, true, true}
+ err := arr.Scan(bytes)
+
+ if err != nil {
+ t.Fatalf("Expected no error for %q, got %v", bytes, err)
+ }
+ if !reflect.DeepEqual(arr, tt.arr) {
+ t.Errorf("Expected %+v for %q, got %+v", tt.arr, bytes, arr)
+ }
+ }
+}
+
+func BenchmarkBoolArrayScanBytes(b *testing.B) {
+ var a BoolArray
+ var x interface{} = []byte(`{t,f,t,f,t,f,t,f,t,f}`)
+
+ for i := 0; i < b.N; i++ {
+ a = BoolArray{}
+ a.Scan(x)
+ }
+}
+
+func TestBoolArrayScanString(t *testing.T) {
+ for _, tt := range BoolArrayStringTests {
+ arr := BoolArray{true, true, true}
+ err := arr.Scan(tt.str)
+
+ if err != nil {
+ t.Fatalf("Expected no error for %q, got %v", tt.str, err)
+ }
+ if !reflect.DeepEqual(arr, tt.arr) {
+ t.Errorf("Expected %+v for %q, got %+v", tt.arr, tt.str, arr)
+ }
+ }
+}
+
+func TestBoolArrayScanError(t *testing.T) {
+ for _, tt := range []struct {
+ input, err string
+ }{
+ {``, "unable to parse array"},
+ {`{`, "unable to parse array"},
+ {`{{t},{f}}`, "cannot convert ARRAY[2][1] to BoolArray"},
+ {`{NULL}`, `could not parse boolean array index 0: invalid boolean ""`},
+ {`{a}`, `could not parse boolean array index 0: invalid boolean "a"`},
+ {`{t,b}`, `could not parse boolean array index 1: invalid boolean "b"`},
+ {`{t,f,cd}`, `could not parse boolean array index 2: invalid boolean "cd"`},
+ } {
+ arr := BoolArray{true, true, true}
+ err := arr.Scan(tt.input)
+
+ if err == nil {
+ t.Fatalf("Expected error for %q, got none", tt.input)
+ }
+ if !strings.Contains(err.Error(), tt.err) {
+ t.Errorf("Expected error to contain %q for %q, got %q", tt.err, tt.input, err)
+ }
+ if !reflect.DeepEqual(arr, BoolArray{true, true, true}) {
+ t.Errorf("Expected destination not to change for %q, got %+v", tt.input, arr)
+ }
+ }
+}
+
+func TestBoolArrayValue(t *testing.T) {
+ result, err := BoolArray(nil).Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error for nil, got %v", err)
+ }
+ if result != nil {
+ t.Errorf("Expected nil, got %q", result)
+ }
+
+ result, err = BoolArray([]bool{}).Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error for empty, got %v", err)
+ }
+ if expected := `{}`; !reflect.DeepEqual(result, expected) {
+ t.Errorf("Expected empty, got %q", result)
+ }
+
+ result, err = BoolArray([]bool{false, true, false}).Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error, got %v", err)
+ }
+ if expected := `{f,t,f}`; !reflect.DeepEqual(result, expected) {
+ t.Errorf("Expected %q, got %q", expected, result)
+ }
+}
+
+func BenchmarkBoolArrayValue(b *testing.B) {
+ rand.Seed(1)
+ x := make([]bool, 10)
+ for i := 0; i < len(x); i++ {
+ x[i] = rand.Intn(2) == 0
+ }
+ a := BoolArray(x)
+
+ for i := 0; i < b.N; i++ {
+ a.Value()
+ }
+}
+
+func TestByteaArrayScanUnsupported(t *testing.T) {
+ var arr ByteaArray
+ err := arr.Scan(1)
+
+ if err == nil {
+ t.Fatal("Expected error when scanning from int")
+ }
+ if !strings.Contains(err.Error(), "int to ByteaArray") {
+ t.Errorf("Expected type to be mentioned when scanning, got %q", err)
+ }
+}
+
+var ByteaArrayStringTests = []struct {
+ str string
+ arr ByteaArray
+}{
+ {`{}`, ByteaArray{}},
+ {`{NULL}`, ByteaArray{nil}},
+ {`{"\\xfeff"}`, ByteaArray{{'\xFE', '\xFF'}}},
+ {`{"\\xdead","\\xbeef"}`, ByteaArray{{'\xDE', '\xAD'}, {'\xBE', '\xEF'}}},
+}
+
+func TestByteaArrayScanBytes(t *testing.T) {
+ for _, tt := range ByteaArrayStringTests {
+ bytes := []byte(tt.str)
+ arr := ByteaArray{{2}, {6}, {0, 0}}
+ err := arr.Scan(bytes)
+
+ if err != nil {
+ t.Fatalf("Expected no error for %q, got %v", bytes, err)
+ }
+ if !reflect.DeepEqual(arr, tt.arr) {
+ t.Errorf("Expected %+v for %q, got %+v", tt.arr, bytes, arr)
+ }
+ }
+}
+
+func BenchmarkByteaArrayScanBytes(b *testing.B) {
+ var a ByteaArray
+ var x interface{} = []byte(`{"\\xfe","\\xff","\\xdead","\\xbeef","\\xfe","\\xff","\\xdead","\\xbeef","\\xfe","\\xff"}`)
+
+ for i := 0; i < b.N; i++ {
+ a = ByteaArray{}
+ a.Scan(x)
+ }
+}
+
+func TestByteaArrayScanString(t *testing.T) {
+ for _, tt := range ByteaArrayStringTests {
+ arr := ByteaArray{{2}, {6}, {0, 0}}
+ err := arr.Scan(tt.str)
+
+ if err != nil {
+ t.Fatalf("Expected no error for %q, got %v", tt.str, err)
+ }
+ if !reflect.DeepEqual(arr, tt.arr) {
+ t.Errorf("Expected %+v for %q, got %+v", tt.arr, tt.str, arr)
+ }
+ }
+}
+
+func TestByteaArrayScanError(t *testing.T) {
+ for _, tt := range []struct {
+ input, err string
+ }{
+ {``, "unable to parse array"},
+ {`{`, "unable to parse array"},
+ {`{{"\\xfeff"},{"\\xbeef"}}`, "cannot convert ARRAY[2][1] to ByteaArray"},
+ {`{"\\abc"}`, "could not parse bytea array index 0: could not parse bytea value"},
+ } {
+ arr := ByteaArray{{2}, {6}, {0, 0}}
+ err := arr.Scan(tt.input)
+
+ if err == nil {
+ t.Fatalf("Expected error for %q, got none", tt.input)
+ }
+ if !strings.Contains(err.Error(), tt.err) {
+ t.Errorf("Expected error to contain %q for %q, got %q", tt.err, tt.input, err)
+ }
+ if !reflect.DeepEqual(arr, ByteaArray{{2}, {6}, {0, 0}}) {
+ t.Errorf("Expected destination not to change for %q, got %+v", tt.input, arr)
+ }
+ }
+}
+
+func TestByteaArrayValue(t *testing.T) {
+ result, err := ByteaArray(nil).Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error for nil, got %v", err)
+ }
+ if result != nil {
+ t.Errorf("Expected nil, got %q", result)
+ }
+
+ result, err = ByteaArray([][]byte{}).Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error for empty, got %v", err)
+ }
+ if expected := `{}`; !reflect.DeepEqual(result, expected) {
+ t.Errorf("Expected empty, got %q", result)
+ }
+
+ result, err = ByteaArray([][]byte{{'\xDE', '\xAD', '\xBE', '\xEF'}, {'\xFE', '\xFF'}, {}}).Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error, got %v", err)
+ }
+ if expected := `{"\\xdeadbeef","\\xfeff","\\x"}`; !reflect.DeepEqual(result, expected) {
+ t.Errorf("Expected %q, got %q", expected, result)
+ }
+}
+
+func BenchmarkByteaArrayValue(b *testing.B) {
+ rand.Seed(1)
+ x := make([][]byte, 10)
+ for i := 0; i < len(x); i++ {
+ x[i] = make([]byte, len(x))
+ for j := 0; j < len(x); j++ {
+ x[i][j] = byte(rand.Int())
+ }
+ }
+ a := ByteaArray(x)
+
+ for i := 0; i < b.N; i++ {
+ a.Value()
+ }
+}
+
+func TestFloat64ArrayScanUnsupported(t *testing.T) {
+ var arr Float64Array
+ err := arr.Scan(true)
+
+ if err == nil {
+ t.Fatal("Expected error when scanning from bool")
+ }
+ if !strings.Contains(err.Error(), "bool to Float64Array") {
+ t.Errorf("Expected type to be mentioned when scanning, got %q", err)
+ }
+}
+
+var Float64ArrayStringTests = []struct {
+ str string
+ arr Float64Array
+}{
+ {`{}`, Float64Array{}},
+ {`{1.2}`, Float64Array{1.2}},
+ {`{3.456,7.89}`, Float64Array{3.456, 7.89}},
+ {`{3,1,2}`, Float64Array{3, 1, 2}},
+}
+
+func TestFloat64ArrayScanBytes(t *testing.T) {
+ for _, tt := range Float64ArrayStringTests {
+ bytes := []byte(tt.str)
+ arr := Float64Array{5, 5, 5}
+ err := arr.Scan(bytes)
+
+ if err != nil {
+ t.Fatalf("Expected no error for %q, got %v", bytes, err)
+ }
+ if !reflect.DeepEqual(arr, tt.arr) {
+ t.Errorf("Expected %+v for %q, got %+v", tt.arr, bytes, arr)
+ }
+ }
+}
+
+func BenchmarkFloat64ArrayScanBytes(b *testing.B) {
+ var a Float64Array
+ var x interface{} = []byte(`{1.2,3.4,5.6,7.8,9.01,2.34,5.67,8.90,1.234,5.678}`)
+
+ for i := 0; i < b.N; i++ {
+ a = Float64Array{}
+ a.Scan(x)
+ }
+}
+
+func TestFloat64ArrayScanString(t *testing.T) {
+ for _, tt := range Float64ArrayStringTests {
+ arr := Float64Array{5, 5, 5}
+ err := arr.Scan(tt.str)
+
+ if err != nil {
+ t.Fatalf("Expected no error for %q, got %v", tt.str, err)
+ }
+ if !reflect.DeepEqual(arr, tt.arr) {
+ t.Errorf("Expected %+v for %q, got %+v", tt.arr, tt.str, arr)
+ }
+ }
+}
+
+func TestFloat64ArrayScanError(t *testing.T) {
+ for _, tt := range []struct {
+ input, err string
+ }{
+ {``, "unable to parse array"},
+ {`{`, "unable to parse array"},
+ {`{{5.6},{7.8}}`, "cannot convert ARRAY[2][1] to Float64Array"},
+ {`{NULL}`, "parsing array element index 0:"},
+ {`{a}`, "parsing array element index 0:"},
+ {`{5.6,a}`, "parsing array element index 1:"},
+ {`{5.6,7.8,a}`, "parsing array element index 2:"},
+ } {
+ arr := Float64Array{5, 5, 5}
+ err := arr.Scan(tt.input)
+
+ if err == nil {
+ t.Fatalf("Expected error for %q, got none", tt.input)
+ }
+ if !strings.Contains(err.Error(), tt.err) {
+ t.Errorf("Expected error to contain %q for %q, got %q", tt.err, tt.input, err)
+ }
+ if !reflect.DeepEqual(arr, Float64Array{5, 5, 5}) {
+ t.Errorf("Expected destination not to change for %q, got %+v", tt.input, arr)
+ }
+ }
+}
+
+func TestFloat64ArrayValue(t *testing.T) {
+ result, err := Float64Array(nil).Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error for nil, got %v", err)
+ }
+ if result != nil {
+ t.Errorf("Expected nil, got %q", result)
+ }
+
+ result, err = Float64Array([]float64{}).Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error for empty, got %v", err)
+ }
+ if expected := `{}`; !reflect.DeepEqual(result, expected) {
+ t.Errorf("Expected empty, got %q", result)
+ }
+
+ result, err = Float64Array([]float64{1.2, 3.4, 5.6}).Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error, got %v", err)
+ }
+ if expected := `{1.2,3.4,5.6}`; !reflect.DeepEqual(result, expected) {
+ t.Errorf("Expected %q, got %q", expected, result)
+ }
+}
+
+func BenchmarkFloat64ArrayValue(b *testing.B) {
+ rand.Seed(1)
+ x := make([]float64, 10)
+ for i := 0; i < len(x); i++ {
+ x[i] = rand.NormFloat64()
+ }
+ a := Float64Array(x)
+
+ for i := 0; i < b.N; i++ {
+ a.Value()
+ }
+}
+
+func TestInt64ArrayScanUnsupported(t *testing.T) {
+ var arr Int64Array
+ err := arr.Scan(true)
+
+ if err == nil {
+ t.Fatal("Expected error when scanning from bool")
+ }
+ if !strings.Contains(err.Error(), "bool to Int64Array") {
+ t.Errorf("Expected type to be mentioned when scanning, got %q", err)
+ }
+}
+
+var Int64ArrayStringTests = []struct {
+ str string
+ arr Int64Array
+}{
+ {`{}`, Int64Array{}},
+ {`{12}`, Int64Array{12}},
+ {`{345,678}`, Int64Array{345, 678}},
+}
+
+func TestInt64ArrayScanBytes(t *testing.T) {
+ for _, tt := range Int64ArrayStringTests {
+ bytes := []byte(tt.str)
+ arr := Int64Array{5, 5, 5}
+ err := arr.Scan(bytes)
+
+ if err != nil {
+ t.Fatalf("Expected no error for %q, got %v", bytes, err)
+ }
+ if !reflect.DeepEqual(arr, tt.arr) {
+ t.Errorf("Expected %+v for %q, got %+v", tt.arr, bytes, arr)
+ }
+ }
+}
+
+func BenchmarkInt64ArrayScanBytes(b *testing.B) {
+ var a Int64Array
+ var x interface{} = []byte(`{1,2,3,4,5,6,7,8,9,0}`)
+
+ for i := 0; i < b.N; i++ {
+ a = Int64Array{}
+ a.Scan(x)
+ }
+}
+
+func TestInt64ArrayScanString(t *testing.T) {
+ for _, tt := range Int64ArrayStringTests {
+ arr := Int64Array{5, 5, 5}
+ err := arr.Scan(tt.str)
+
+ if err != nil {
+ t.Fatalf("Expected no error for %q, got %v", tt.str, err)
+ }
+ if !reflect.DeepEqual(arr, tt.arr) {
+ t.Errorf("Expected %+v for %q, got %+v", tt.arr, tt.str, arr)
+ }
+ }
+}
+
+func TestInt64ArrayScanError(t *testing.T) {
+ for _, tt := range []struct {
+ input, err string
+ }{
+ {``, "unable to parse array"},
+ {`{`, "unable to parse array"},
+ {`{{5},{6}}`, "cannot convert ARRAY[2][1] to Int64Array"},
+ {`{NULL}`, "parsing array element index 0:"},
+ {`{a}`, "parsing array element index 0:"},
+ {`{5,a}`, "parsing array element index 1:"},
+ {`{5,6,a}`, "parsing array element index 2:"},
+ } {
+ arr := Int64Array{5, 5, 5}
+ err := arr.Scan(tt.input)
+
+ if err == nil {
+ t.Fatalf("Expected error for %q, got none", tt.input)
+ }
+ if !strings.Contains(err.Error(), tt.err) {
+ t.Errorf("Expected error to contain %q for %q, got %q", tt.err, tt.input, err)
+ }
+ if !reflect.DeepEqual(arr, Int64Array{5, 5, 5}) {
+ t.Errorf("Expected destination not to change for %q, got %+v", tt.input, arr)
+ }
+ }
+}
+
+func TestInt64ArrayValue(t *testing.T) {
+ result, err := Int64Array(nil).Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error for nil, got %v", err)
+ }
+ if result != nil {
+ t.Errorf("Expected nil, got %q", result)
+ }
+
+ result, err = Int64Array([]int64{}).Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error for empty, got %v", err)
+ }
+ if expected := `{}`; !reflect.DeepEqual(result, expected) {
+ t.Errorf("Expected empty, got %q", result)
+ }
+
+ result, err = Int64Array([]int64{1, 2, 3}).Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error, got %v", err)
+ }
+ if expected := `{1,2,3}`; !reflect.DeepEqual(result, expected) {
+ t.Errorf("Expected %q, got %q", expected, result)
+ }
+}
+
+func BenchmarkInt64ArrayValue(b *testing.B) {
+ rand.Seed(1)
+ x := make([]int64, 10)
+ for i := 0; i < len(x); i++ {
+ x[i] = rand.Int63()
+ }
+ a := Int64Array(x)
+
+ for i := 0; i < b.N; i++ {
+ a.Value()
+ }
+}
+
+func TestStringArrayScanUnsupported(t *testing.T) {
+ var arr StringArray
+ err := arr.Scan(true)
+
+ if err == nil {
+ t.Fatal("Expected error when scanning from bool")
+ }
+ if !strings.Contains(err.Error(), "bool to StringArray") {
+ t.Errorf("Expected type to be mentioned when scanning, got %q", err)
+ }
+}
+
+var StringArrayStringTests = []struct {
+ str string
+ arr StringArray
+}{
+ {`{}`, StringArray{}},
+ {`{t}`, StringArray{"t"}},
+ {`{f,1}`, StringArray{"f", "1"}},
+ {`{"a\\b","c d",","}`, StringArray{"a\\b", "c d", ","}},
+}
+
+func TestStringArrayScanBytes(t *testing.T) {
+ for _, tt := range StringArrayStringTests {
+ bytes := []byte(tt.str)
+ arr := StringArray{"x", "x", "x"}
+ err := arr.Scan(bytes)
+
+ if err != nil {
+ t.Fatalf("Expected no error for %q, got %v", bytes, err)
+ }
+ if !reflect.DeepEqual(arr, tt.arr) {
+ t.Errorf("Expected %+v for %q, got %+v", tt.arr, bytes, arr)
+ }
+ }
+}
+
+func BenchmarkStringArrayScanBytes(b *testing.B) {
+ var a StringArray
+ var x interface{} = []byte(`{a,b,c,d,e,f,g,h,i,j}`)
+ var y interface{} = []byte(`{"\a","\b","\c","\d","\e","\f","\g","\h","\i","\j"}`)
+
+ for i := 0; i < b.N; i++ {
+ a = StringArray{}
+ a.Scan(x)
+ a = StringArray{}
+ a.Scan(y)
+ }
+}
+
+func TestStringArrayScanString(t *testing.T) {
+ for _, tt := range StringArrayStringTests {
+ arr := StringArray{"x", "x", "x"}
+ err := arr.Scan(tt.str)
+
+ if err != nil {
+ t.Fatalf("Expected no error for %q, got %v", tt.str, err)
+ }
+ if !reflect.DeepEqual(arr, tt.arr) {
+ t.Errorf("Expected %+v for %q, got %+v", tt.arr, tt.str, arr)
+ }
+ }
+}
+
+func TestStringArrayScanError(t *testing.T) {
+ for _, tt := range []struct {
+ input, err string
+ }{
+ {``, "unable to parse array"},
+ {`{`, "unable to parse array"},
+ {`{{a},{b}}`, "cannot convert ARRAY[2][1] to StringArray"},
+ {`{NULL}`, "parsing array element index 0: cannot convert nil to string"},
+ {`{a,NULL}`, "parsing array element index 1: cannot convert nil to string"},
+ {`{a,b,NULL}`, "parsing array element index 2: cannot convert nil to string"},
+ } {
+ arr := StringArray{"x", "x", "x"}
+ err := arr.Scan(tt.input)
+
+ if err == nil {
+ t.Fatalf("Expected error for %q, got none", tt.input)
+ }
+ if !strings.Contains(err.Error(), tt.err) {
+ t.Errorf("Expected error to contain %q for %q, got %q", tt.err, tt.input, err)
+ }
+ if !reflect.DeepEqual(arr, StringArray{"x", "x", "x"}) {
+ t.Errorf("Expected destination not to change for %q, got %+v", tt.input, arr)
+ }
+ }
+}
+
+func TestStringArrayValue(t *testing.T) {
+ result, err := StringArray(nil).Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error for nil, got %v", err)
+ }
+ if result != nil {
+ t.Errorf("Expected nil, got %q", result)
+ }
+
+ result, err = StringArray([]string{}).Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error for empty, got %v", err)
+ }
+ if expected := `{}`; !reflect.DeepEqual(result, expected) {
+ t.Errorf("Expected empty, got %q", result)
+ }
+
+ result, err = StringArray([]string{`a`, `\b`, `c"`, `d,e`}).Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error, got %v", err)
+ }
+ if expected := `{"a","\\b","c\"","d,e"}`; !reflect.DeepEqual(result, expected) {
+ t.Errorf("Expected %q, got %q", expected, result)
+ }
+}
+
+func BenchmarkStringArrayValue(b *testing.B) {
+ x := make([]string, 10)
+ for i := 0; i < len(x); i++ {
+ x[i] = strings.Repeat(`abc"def\ghi`, 5)
+ }
+ a := StringArray(x)
+
+ for i := 0; i < b.N; i++ {
+ a.Value()
+ }
+}
+
+func TestGenericArrayScanUnsupported(t *testing.T) {
+ var s string
+ var ss []string
+
+ for _, tt := range []struct {
+ src, dest interface{}
+ err string
+ }{
+ {nil, nil, "destination <nil> is not a pointer to array or slice"},
+ {nil, true, "destination bool is not a pointer to array or slice"},
+ {nil, &s, "destination *string is not a pointer to array or slice"},
+ {nil, ss, "destination []string is not a pointer to array or slice"},
+ {true, &ss, "bool to []string"},
+ {`{{x}}`, &ss, "multidimensional ARRAY[1][1] is not implemented"},
+ {`{{x},{x}}`, &ss, "multidimensional ARRAY[2][1] is not implemented"},
+ {`{x}`, &ss, "scanning to string is not implemented"},
+ } {
+ err := GenericArray{tt.dest}.Scan(tt.src)
+
+ if err == nil {
+ t.Fatalf("Expected error for [%#v %#v]", tt.src, tt.dest)
+ }
+ if !strings.Contains(err.Error(), tt.err) {
+ t.Errorf("Expected error to contain %q for [%#v %#v], got %q", tt.err, tt.src, tt.dest, err)
+ }
+ }
+}
+
+func TestGenericArrayScanScannerArrayBytes(t *testing.T) {
+ src, expected, nsa := []byte(`{NULL,abc,"\""}`),
+ [3]sql.NullString{{}, {String: `abc`, Valid: true}, {String: `"`, Valid: true}},
+ [3]sql.NullString{{String: ``, Valid: true}, {}, {}}
+
+ if err := (GenericArray{&nsa}).Scan(src); err != nil {
+ t.Fatalf("Expected no error, got %v", err)
+ }
+ if !reflect.DeepEqual(nsa, expected) {
+ t.Errorf("Expected %v, got %v", expected, nsa)
+ }
+}
+
+func TestGenericArrayScanScannerArrayString(t *testing.T) {
+ src, expected, nsa := `{NULL,"\"",xyz}`,
+ [3]sql.NullString{{}, {String: `"`, Valid: true}, {String: `xyz`, Valid: true}},
+ [3]sql.NullString{{String: ``, Valid: true}, {}, {}}
+
+ if err := (GenericArray{&nsa}).Scan(src); err != nil {
+ t.Fatalf("Expected no error, got %v", err)
+ }
+ if !reflect.DeepEqual(nsa, expected) {
+ t.Errorf("Expected %v, got %v", expected, nsa)
+ }
+}
+
+func TestGenericArrayScanScannerSliceBytes(t *testing.T) {
+ src, expected, nss := []byte(`{NULL,abc,"\""}`),
+ []sql.NullString{{}, {String: `abc`, Valid: true}, {String: `"`, Valid: true}},
+ []sql.NullString{{String: ``, Valid: true}, {}, {}, {}, {}}
+
+ if err := (GenericArray{&nss}).Scan(src); err != nil {
+ t.Fatalf("Expected no error, got %v", err)
+ }
+ if !reflect.DeepEqual(nss, expected) {
+ t.Errorf("Expected %v, got %v", expected, nss)
+ }
+}
+
+func BenchmarkGenericArrayScanScannerSliceBytes(b *testing.B) {
+ var a GenericArray
+ var x interface{} = []byte(`{a,b,c,d,e,f,g,h,i,j}`)
+ var y interface{} = []byte(`{"\a","\b","\c","\d","\e","\f","\g","\h","\i","\j"}`)
+
+ for i := 0; i < b.N; i++ {
+ a = GenericArray{new([]sql.NullString)}
+ a.Scan(x)
+ a = GenericArray{new([]sql.NullString)}
+ a.Scan(y)
+ }
+}
+
+func TestGenericArrayScanScannerSliceString(t *testing.T) {
+ src, expected, nss := `{NULL,"\"",xyz}`,
+ []sql.NullString{{}, {String: `"`, Valid: true}, {String: `xyz`, Valid: true}},
+ []sql.NullString{{String: ``, Valid: true}, {}, {}}
+
+ if err := (GenericArray{&nss}).Scan(src); err != nil {
+ t.Fatalf("Expected no error, got %v", err)
+ }
+ if !reflect.DeepEqual(nss, expected) {
+ t.Errorf("Expected %v, got %v", expected, nss)
+ }
+}
+
+type TildeNullInt64 struct{ sql.NullInt64 }
+
+func (TildeNullInt64) ArrayDelimiter() string { return "~" }
+
+func TestGenericArrayScanDelimiter(t *testing.T) {
+ src, expected, tnis := `{12~NULL~76}`,
+ []TildeNullInt64{{sql.NullInt64{Int64: 12, Valid: true}}, {}, {sql.NullInt64{Int64: 76, Valid: true}}},
+ []TildeNullInt64{{sql.NullInt64{Int64: 0, Valid: true}}, {}}
+
+ if err := (GenericArray{&tnis}).Scan(src); err != nil {
+ t.Fatalf("Expected no error for %#v, got %v", src, err)
+ }
+ if !reflect.DeepEqual(tnis, expected) {
+ t.Errorf("Expected %v for %#v, got %v", expected, src, tnis)
+ }
+}
+
+func TestGenericArrayScanErrors(t *testing.T) {
+ var sa [1]string
+ var nis []sql.NullInt64
+ var pss *[]string
+
+ for _, tt := range []struct {
+ src, dest interface{}
+ err string
+ }{
+ {nil, pss, "destination *[]string is nil"},
+ {`{`, &sa, "unable to parse"},
+ {`{}`, &sa, "cannot convert ARRAY[0] to [1]string"},
+ {`{x,x}`, &sa, "cannot convert ARRAY[2] to [1]string"},
+ {`{x}`, &nis, `parsing array element index 0: converting`},
+ } {
+ err := GenericArray{tt.dest}.Scan(tt.src)
+
+ if err == nil {
+ t.Fatalf("Expected error for [%#v %#v]", tt.src, tt.dest)
+ }
+ if !strings.Contains(err.Error(), tt.err) {
+ t.Errorf("Expected error to contain %q for [%#v %#v], got %q", tt.err, tt.src, tt.dest, err)
+ }
+ }
+}
+
+func TestGenericArrayValueUnsupported(t *testing.T) {
+ _, err := GenericArray{true}.Value()
+
+ if err == nil {
+ t.Fatal("Expected error for bool")
+ }
+ if !strings.Contains(err.Error(), "bool to array") {
+ t.Errorf("Expected type to be mentioned, got %q", err)
+ }
+}
+
+type ByteArrayValuer [1]byte
+type ByteSliceValuer []byte
+type FuncArrayValuer struct {
+ delimiter func() string
+ value func() (driver.Value, error)
+}
+
+func (a ByteArrayValuer) Value() (driver.Value, error) { return a[:], nil }
+func (b ByteSliceValuer) Value() (driver.Value, error) { return []byte(b), nil }
+func (f FuncArrayValuer) ArrayDelimiter() string { return f.delimiter() }
+func (f FuncArrayValuer) Value() (driver.Value, error) { return f.value() }
+
+func TestGenericArrayValue(t *testing.T) {
+ result, err := GenericArray{nil}.Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error for nil, got %v", err)
+ }
+ if result != nil {
+ t.Errorf("Expected nil, got %q", result)
+ }
+
+ Tilde := func(v driver.Value) FuncArrayValuer {
+ return FuncArrayValuer{
+ func() string { return "~" },
+ func() (driver.Value, error) { return v, nil }}
+ }
+
+ for _, tt := range []struct {
+ result string
+ input interface{}
+ }{
+ {`{}`, []bool{}},
+ {`{true}`, []bool{true}},
+ {`{true,false}`, []bool{true, false}},
+ {`{true,false}`, [2]bool{true, false}},
+
+ {`{}`, [][]int{{}}},
+ {`{}`, [][]int{{}, {}}},
+ {`{{1}}`, [][]int{{1}}},
+ {`{{1},{2}}`, [][]int{{1}, {2}}},
+ {`{{1,2},{3,4}}`, [][]int{{1, 2}, {3, 4}}},
+ {`{{1,2},{3,4}}`, [2][2]int{{1, 2}, {3, 4}}},
+
+ {`{"a","\\b","c\"","d,e"}`, []string{`a`, `\b`, `c"`, `d,e`}},
+ {`{"a","\\b","c\"","d,e"}`, [][]byte{{'a'}, {'\\', 'b'}, {'c', '"'}, {'d', ',', 'e'}}},
+
+ {`{NULL}`, []*int{nil}},
+ {`{0,NULL}`, []*int{new(int), nil}},
+
+ {`{NULL}`, []sql.NullString{{}}},
+ {`{"\"",NULL}`, []sql.NullString{{String: `"`, Valid: true}, {}}},
+
+ {`{"a","b"}`, []ByteArrayValuer{{'a'}, {'b'}}},
+ {`{{"a","b"},{"c","d"}}`, [][]ByteArrayValuer{{{'a'}, {'b'}}, {{'c'}, {'d'}}}},
+
+ {`{"e","f"}`, []ByteSliceValuer{{'e'}, {'f'}}},
+ {`{{"e","f"},{"g","h"}}`, [][]ByteSliceValuer{{{'e'}, {'f'}}, {{'g'}, {'h'}}}},
+
+ {`{1~2}`, []FuncArrayValuer{Tilde(int64(1)), Tilde(int64(2))}},
+ {`{{1~2}~{3~4}}`, [][]FuncArrayValuer{{Tilde(int64(1)), Tilde(int64(2))}, {Tilde(int64(3)), Tilde(int64(4))}}},
+ } {
+ result, err := GenericArray{tt.input}.Value()
+
+ if err != nil {
+ t.Fatalf("Expected no error for %q, got %v", tt.input, err)
+ }
+ if !reflect.DeepEqual(result, tt.result) {
+ t.Errorf("Expected %q for %q, got %q", tt.result, tt.input, result)
+ }
+ }
+}
+
+func TestGenericArrayValueErrors(t *testing.T) {
+ var v []interface{}
+
+ v = []interface{}{func() {}}
+ if _, err := (GenericArray{v}).Value(); err == nil {
+ t.Errorf("Expected error for %q, got nil", v)
+ }
+
+ v = []interface{}{nil, func() {}}
+ if _, err := (GenericArray{v}).Value(); err == nil {
+ t.Errorf("Expected error for %q, got nil", v)
+ }
+}
+
+func BenchmarkGenericArrayValueBools(b *testing.B) {
+ rand.Seed(1)
+ x := make([]bool, 10)
+ for i := 0; i < len(x); i++ {
+ x[i] = rand.Intn(2) == 0
+ }
+ a := GenericArray{x}
+
+ for i := 0; i < b.N; i++ {
+ a.Value()
+ }
+}
+
+func BenchmarkGenericArrayValueFloat64s(b *testing.B) {
+ rand.Seed(1)
+ x := make([]float64, 10)
+ for i := 0; i < len(x); i++ {
+ x[i] = rand.NormFloat64()
+ }
+ a := GenericArray{x}
+
+ for i := 0; i < b.N; i++ {
+ a.Value()
+ }
+}
+
+func BenchmarkGenericArrayValueInt64s(b *testing.B) {
+ rand.Seed(1)
+ x := make([]int64, 10)
+ for i := 0; i < len(x); i++ {
+ x[i] = rand.Int63()
+ }
+ a := GenericArray{x}
+
+ for i := 0; i < b.N; i++ {
+ a.Value()
+ }
+}
+
+func BenchmarkGenericArrayValueByteSlices(b *testing.B) {
+ x := make([][]byte, 10)
+ for i := 0; i < len(x); i++ {
+ x[i] = bytes.Repeat([]byte(`abc"def\ghi`), 5)
+ }
+ a := GenericArray{x}
+
+ for i := 0; i < b.N; i++ {
+ a.Value()
+ }
+}
+
+func BenchmarkGenericArrayValueStrings(b *testing.B) {
+ x := make([]string, 10)
+ for i := 0; i < len(x); i++ {
+ x[i] = strings.Repeat(`abc"def\ghi`, 5)
+ }
+ a := GenericArray{x}
+
+ for i := 0; i < b.N; i++ {
+ a.Value()
+ }
+}
+
+func TestArrayScanBackend(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ for _, tt := range []struct {
+ s string
+ d sql.Scanner
+ e interface{}
+ }{
+ {`ARRAY[true, false]`, new(BoolArray), &BoolArray{true, false}},
+ {`ARRAY[E'\\xdead', E'\\xbeef']`, new(ByteaArray), &ByteaArray{{'\xDE', '\xAD'}, {'\xBE', '\xEF'}}},
+ {`ARRAY[1.2, 3.4]`, new(Float64Array), &Float64Array{1.2, 3.4}},
+ {`ARRAY[1, 2, 3]`, new(Int64Array), &Int64Array{1, 2, 3}},
+ {`ARRAY['a', E'\\b', 'c"', 'd,e']`, new(StringArray), &StringArray{`a`, `\b`, `c"`, `d,e`}},
+ } {
+ err := db.QueryRow(`SELECT ` + tt.s).Scan(tt.d)
+ if err != nil {
+ t.Errorf("Expected no error when scanning %s into %T, got %v", tt.s, tt.d, err)
+ }
+ if !reflect.DeepEqual(tt.d, tt.e) {
+ t.Errorf("Expected %v when scanning %s into %T, got %v", tt.e, tt.s, tt.d, tt.d)
+ }
+ }
+}
+
+func TestArrayValueBackend(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ for _, tt := range []struct {
+ s string
+ v driver.Valuer
+ }{
+ {`ARRAY[true, false]`, BoolArray{true, false}},
+ {`ARRAY[E'\\xdead', E'\\xbeef']`, ByteaArray{{'\xDE', '\xAD'}, {'\xBE', '\xEF'}}},
+ {`ARRAY[1.2, 3.4]`, Float64Array{1.2, 3.4}},
+ {`ARRAY[1, 2, 3]`, Int64Array{1, 2, 3}},
+ {`ARRAY['a', E'\\b', 'c"', 'd,e']`, StringArray{`a`, `\b`, `c"`, `d,e`}},
+ } {
+ var x int
+ err := db.QueryRow(`SELECT 1 WHERE `+tt.s+` <> $1`, tt.v).Scan(&x)
+ if err != sql.ErrNoRows {
+ t.Errorf("Expected %v to equal %s, got %v", tt.v, tt.s, err)
+ }
+ }
+}
diff --git a/vendor/github.com/lib/pq/certs/bogus_root.crt b/vendor/github.com/lib/pq/certs/bogus_root.crt
new file mode 100644
index 000000000..1239db3a4
--- /dev/null
+++ b/vendor/github.com/lib/pq/certs/bogus_root.crt
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDBjCCAe6gAwIBAgIQSnDYp/Naet9HOZljF5PuwDANBgkqhkiG9w0BAQsFADAr
+MRIwEAYDVQQKEwlDb2Nrcm9hY2gxFTATBgNVBAMTDENvY2tyb2FjaCBDQTAeFw0x
+NjAyMDcxNjQ0MzdaFw0xNzAyMDYxNjQ0MzdaMCsxEjAQBgNVBAoTCUNvY2tyb2Fj
+aDEVMBMGA1UEAxMMQ29ja3JvYWNoIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAxdln3/UdgP7ayA/G1kT7upjLe4ERwQjYQ25q0e1+vgsB5jhiirxJ
+e0+WkhhYu/mwoSAXzvlsbZ2PWFyfdanZeD/Lh6SvIeWXVVaPcWVWL1TEcoN2jr5+
+E85MMHmbbmaT2he8s6br2tM/UZxyTQ2XRprIzApbDssyw1c0Yufcpu3C6267FLEl
+IfcWrzDhnluFhthhtGXv3ToD8IuMScMC5qlKBXtKmD1B5x14ngO/ecNJ+OlEi0HU
+mavK4KWgI2rDXRZ2EnCpyTZdkc3kkRnzKcg653oOjMDRZdrhfIrha+Jq38ACsUmZ
+Su7Sp5jkIHOCO8Zg+l6GKVSq37dKMapD8wIDAQABoyYwJDAOBgNVHQ8BAf8EBAMC
+AuQwEgYDVR0TAQH/BAgwBgEB/wIBATANBgkqhkiG9w0BAQsFAAOCAQEAwZ2Tu0Yu
+rrSVdMdoPEjT1IZd+5OhM/SLzL0ddtvTithRweLHsw2lDQYlXFqr24i3UGZJQ1sp
+cqSrNwswgLUQT3vWyTjmM51HEb2vMYWKmjZ+sBQYAUP1CadrN/+OTfNGnlF1+B4w
+IXOzh7EvQmJJnNybLe4a/aRvj1NE2n8Z898B76SVU9WbfKKz8VwLzuIPDqkKcZda
+lMy5yzthyztV9YjcWs2zVOUGZvGdAhDrvZuUq6mSmxrBEvR2LBOggmVf3tGRT+Ls
+lW7c9Lrva5zLHuqmoPP07A+vuI9a0D1X44jwGDuPWJ5RnTOQ63Uez12mKNjqleHw
+DnkwNanuO8dhAA==
+-----END CERTIFICATE-----
diff --git a/vendor/github.com/lib/pq/conn.go b/vendor/github.com/lib/pq/conn.go
index 336c89449..8e1aee9f0 100644
--- a/vendor/github.com/lib/pq/conn.go
+++ b/vendor/github.com/lib/pq/conn.go
@@ -968,8 +968,23 @@ func (cn *conn) ssl(o values) {
verifyCaOnly := false
tlsConf := tls.Config{}
switch mode := o.Get("sslmode"); mode {
- case "require", "":
+ // "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.
diff --git a/vendor/github.com/lib/pq/conn_test.go b/vendor/github.com/lib/pq/conn_test.go
index 274147c27..592860f8a 100644
--- a/vendor/github.com/lib/pq/conn_test.go
+++ b/vendor/github.com/lib/pq/conn_test.go
@@ -138,6 +138,10 @@ func TestOpenURL(t *testing.T) {
const pgpass_file = "/tmp/pqgotest_pgpass"
func TestPgpass(t *testing.T) {
+ if os.Getenv("TRAVIS") != "true" {
+ t.Skip("not running under Travis, skipping pgpass tests")
+ }
+
testAssert := func(conninfo string, expected string, reason string) {
conn, err := openTestConnConninfo(conninfo)
if err != nil {
@@ -186,7 +190,17 @@ localhost:*:*:*:pass_C
pgpass.Close()
assertPassword := func(extra values, expected string) {
- o := &values{"host": "localhost", "sslmode": "disable", "connect_timeout": "20", "user": "majid", "port": "5432", "extra_float_digits": "2", "dbname": "pqgotest", "client_encoding": "UTF8", "datestyle": "ISO, MDY"}
+ o := &values{
+ "host": "localhost",
+ "sslmode": "disable",
+ "connect_timeout": "20",
+ "user": "majid",
+ "port": "5432",
+ "extra_float_digits": "2",
+ "dbname": "pqgotest",
+ "client_encoding": "UTF8",
+ "datestyle": "ISO, MDY",
+ }
for k, v := range extra {
(*o)[k] = v
}
diff --git a/vendor/github.com/lib/pq/encode.go b/vendor/github.com/lib/pq/encode.go
index 6681bd3e7..29e8f6ff7 100644
--- a/vendor/github.com/lib/pq/encode.go
+++ b/vendor/github.com/lib/pq/encode.go
@@ -56,10 +56,13 @@ func encode(parameterStatus *parameterStatus, x interface{}, pgtypOid oid.Oid) [
}
func decode(parameterStatus *parameterStatus, s []byte, typ oid.Oid, f format) interface{} {
- if f == formatBinary {
+ switch f {
+ case formatBinary:
return binaryDecode(parameterStatus, s, typ)
- } else {
+ case formatText:
return textDecode(parameterStatus, s, typ)
+ default:
+ panic("not reached")
}
}
@@ -83,8 +86,14 @@ func binaryDecode(parameterStatus *parameterStatus, s []byte, typ oid.Oid) inter
func textDecode(parameterStatus *parameterStatus, s []byte, typ oid.Oid) interface{} {
switch typ {
+ case oid.T_char, oid.T_varchar, oid.T_text:
+ return string(s)
case oid.T_bytea:
- return parseBytea(s)
+ b, err := parseBytea(s)
+ if err != nil {
+ errorf("%s", err)
+ }
+ return b
case oid.T_timestamptz:
return parseTs(parameterStatus.currentLocation, string(s))
case oid.T_timestamp, oid.T_date:
@@ -195,21 +204,21 @@ func mustParse(f string, typ oid.Oid, s []byte) time.Time {
return t
}
-var invalidTimestampErr = errors.New("invalid timestamp")
+var errInvalidTimestamp = errors.New("invalid timestamp")
type timestampParser struct {
err error
}
-func (p *timestampParser) expect(str, char string, pos int) {
+func (p *timestampParser) expect(str string, char byte, pos int) {
if p.err != nil {
return
}
if pos+1 > len(str) {
- p.err = invalidTimestampErr
+ p.err = errInvalidTimestamp
return
}
- if c := str[pos : pos+1]; c != char && p.err == nil {
+ if c := str[pos]; c != char && p.err == nil {
p.err = fmt.Errorf("expected '%v' at position %v; got '%v'", char, pos, c)
}
}
@@ -219,7 +228,7 @@ func (p *timestampParser) mustAtoi(str string, begin int, end int) int {
return 0
}
if begin < 0 || end < 0 || begin > end || end > len(str) {
- p.err = invalidTimestampErr
+ p.err = errInvalidTimestamp
return 0
}
result, err := strconv.Atoi(str[begin:end])
@@ -242,7 +251,7 @@ type locationCache struct {
// about 5% speed could be gained by putting the cache in the connection and
// losing the mutex, at the cost of a small amount of memory and a somewhat
// significant increase in code complexity.
-var globalLocationCache *locationCache = newLocationCache()
+var globalLocationCache = newLocationCache()
func newLocationCache() *locationCache {
return &locationCache{cache: make(map[int]*time.Location)}
@@ -272,26 +281,26 @@ const (
infinityTsNegativeMustBeSmaller = "pq: infinity timestamp: negative value must be smaller (before) than positive"
)
-/*
- * If EnableInfinityTs is not called, "-infinity" and "infinity" will return
- * []byte("-infinity") and []byte("infinity") respectively, and potentially
- * cause error "sql: Scan error on column index 0: unsupported driver -> Scan pair: []uint8 -> *time.Time",
- * when scanning into a time.Time value.
- *
- * Once EnableInfinityTs has been called, all connections created using this
- * driver will decode Postgres' "-infinity" and "infinity" for "timestamp",
- * "timestamp with time zone" and "date" types to the predefined minimum and
- * maximum times, respectively. When encoding time.Time values, any time which
- * equals or precedes the predefined minimum time will be encoded to
- * "-infinity". Any values at or past the maximum time will similarly be
- * encoded to "infinity".
- *
- *
- * If EnableInfinityTs is called with negative >= positive, it will panic.
- * Calling EnableInfinityTs after a connection has been established results in
- * undefined behavior. If EnableInfinityTs is called more than once, it will
- * panic.
- */
+// EnableInfinityTs controls the handling of Postgres' "-infinity" and
+// "infinity" "timestamp"s.
+//
+// If EnableInfinityTs is not called, "-infinity" and "infinity" will return
+// []byte("-infinity") and []byte("infinity") respectively, and potentially
+// cause error "sql: Scan error on column index 0: unsupported driver -> Scan
+// pair: []uint8 -> *time.Time", when scanning into a time.Time value.
+//
+// Once EnableInfinityTs has been called, all connections created using this
+// driver will decode Postgres' "-infinity" and "infinity" for "timestamp",
+// "timestamp with time zone" and "date" types to the predefined minimum and
+// maximum times, respectively. When encoding time.Time values, any time which
+// equals or precedes the predefined minimum time will be encoded to
+// "-infinity". Any values at or past the maximum time will similarly be
+// encoded to "infinity".
+//
+// If EnableInfinityTs is called with negative >= positive, it will panic.
+// Calling EnableInfinityTs after a connection has been established results in
+// undefined behavior. If EnableInfinityTs is called more than once, it will
+// panic.
func EnableInfinityTs(negative time.Time, positive time.Time) {
if infinityTsEnabled {
panic(infinityTsEnabledAlready)
@@ -335,6 +344,10 @@ func parseTs(currentLocation *time.Location, str string) interface{} {
return t
}
+// ParseTimestamp parses Postgres' text format. It returns a time.Time in
+// currentLocation iff that time's offset agrees with the offset sent from the
+// Postgres server. Otherwise, ParseTimestamp returns a time.Time with the
+// fixed offset offset provided by the Postgres server.
func ParseTimestamp(currentLocation *time.Location, str string) (time.Time, error) {
p := timestampParser{}
@@ -344,18 +357,18 @@ func ParseTimestamp(currentLocation *time.Location, str string) (time.Time, erro
year := p.mustAtoi(str, 0, monSep)
daySep := monSep + 3
month := p.mustAtoi(str, monSep+1, daySep)
- p.expect(str, "-", daySep)
+ p.expect(str, '-', daySep)
timeSep := daySep + 3
day := p.mustAtoi(str, daySep+1, timeSep)
var hour, minute, second int
if len(str) > monSep+len("01-01")+1 {
- p.expect(str, " ", timeSep)
+ p.expect(str, ' ', timeSep)
minSep := timeSep + 3
- p.expect(str, ":", minSep)
+ p.expect(str, ':', minSep)
hour = p.mustAtoi(str, timeSep+1, minSep)
secSep := minSep + 3
- p.expect(str, ":", secSep)
+ p.expect(str, ':', secSep)
minute = p.mustAtoi(str, minSep+1, secSep)
secEnd := secSep + 3
second = p.mustAtoi(str, secSep+1, secEnd)
@@ -369,7 +382,7 @@ func ParseTimestamp(currentLocation *time.Location, str string) (time.Time, erro
nanoSec := 0
tzOff := 0
- if remainderIdx+1 <= len(str) && str[remainderIdx:remainderIdx+1] == "." {
+ if remainderIdx < len(str) && str[remainderIdx] == '.' {
fracStart := remainderIdx + 1
fracOff := strings.IndexAny(str[fracStart:], "-+ ")
if fracOff < 0 {
@@ -380,25 +393,26 @@ func ParseTimestamp(currentLocation *time.Location, str string) (time.Time, erro
remainderIdx += fracOff + 1
}
- if tzStart := remainderIdx; tzStart+1 <= len(str) && (str[tzStart:tzStart+1] == "-" || str[tzStart:tzStart+1] == "+") {
+ if tzStart := remainderIdx; tzStart < len(str) && (str[tzStart] == '-' || str[tzStart] == '+') {
// time zone separator is always '-' or '+' (UTC is +00)
var tzSign int
- if c := str[tzStart : tzStart+1]; c == "-" {
+ switch c := str[tzStart]; c {
+ case '-':
tzSign = -1
- } else if c == "+" {
+ case '+':
tzSign = +1
- } else {
+ default:
return time.Time{}, fmt.Errorf("expected '-' or '+' at position %v; got %v", tzStart, c)
}
tzHours := p.mustAtoi(str, tzStart+1, tzStart+3)
remainderIdx += 3
var tzMin, tzSec int
- if tzStart+4 <= len(str) && str[tzStart+3:tzStart+4] == ":" {
- tzMin = p.mustAtoi(str, tzStart+4, tzStart+6)
+ if remainderIdx < len(str) && str[remainderIdx] == ':' {
+ tzMin = p.mustAtoi(str, remainderIdx+1, remainderIdx+3)
remainderIdx += 3
}
- if tzStart+7 <= len(str) && str[tzStart+6:tzStart+7] == ":" {
- tzSec = p.mustAtoi(str, tzStart+7, tzStart+9)
+ if remainderIdx < len(str) && str[remainderIdx] == ':' {
+ tzSec = p.mustAtoi(str, remainderIdx+1, remainderIdx+3)
remainderIdx += 3
}
tzOff = tzSign * ((tzHours * 60 * 60) + (tzMin * 60) + tzSec)
@@ -432,7 +446,7 @@ func ParseTimestamp(currentLocation *time.Location, str string) (time.Time, erro
}
// formatTs formats t into a format postgres understands.
-func formatTs(t time.Time) (b []byte) {
+func formatTs(t time.Time) []byte {
if infinityTsEnabled {
// t <= -infinity : ! (t > -infinity)
if !t.After(infinityTsNegative) {
@@ -443,6 +457,11 @@ func formatTs(t time.Time) (b []byte) {
return []byte("infinity")
}
}
+ return FormatTimestamp(t)
+}
+
+// FormatTimestamp formats t into Postgres' text format for timestamps.
+func FormatTimestamp(t time.Time) []byte {
// Need to send dates before 0001 A.D. with " BC" suffix, instead of the
// minus sign preferred by Go.
// Beware, "0000" in ISO is "1 BC", "-0001" is "2 BC" and so on
@@ -452,7 +471,7 @@ func formatTs(t time.Time) (b []byte) {
t = t.AddDate((-t.Year())*2+1, 0, 0)
bc = true
}
- b = []byte(t.Format(time.RFC3339Nano))
+ b := []byte(t.Format(time.RFC3339Nano))
_, offset := t.Zone()
offset = offset % 60
@@ -477,14 +496,14 @@ func formatTs(t time.Time) (b []byte) {
// Parse a bytea value received from the server. Both "hex" and the legacy
// "escape" format are supported.
-func parseBytea(s []byte) (result []byte) {
+func parseBytea(s []byte) (result []byte, err error) {
if len(s) >= 2 && bytes.Equal(s[:2], []byte("\\x")) {
// bytea_output = hex
s = s[2:] // trim off leading "\\x"
result = make([]byte, hex.DecodedLen(len(s)))
_, err := hex.Decode(result, s)
if err != nil {
- errorf("%s", err)
+ return nil, err
}
} else {
// bytea_output = escape
@@ -499,11 +518,11 @@ func parseBytea(s []byte) (result []byte) {
// '\\' followed by an octal number
if len(s) < 4 {
- errorf("invalid bytea sequence %v", s)
+ return nil, fmt.Errorf("invalid bytea sequence %v", s)
}
r, err := strconv.ParseInt(string(s[1:4]), 8, 9)
if err != nil {
- errorf("could not parse bytea value: %s", err.Error())
+ return nil, fmt.Errorf("could not parse bytea value: %s", err.Error())
}
result = append(result, byte(r))
s = s[4:]
@@ -521,7 +540,7 @@ func parseBytea(s []byte) (result []byte) {
}
}
- return result
+ return result, nil
}
func encodeBytea(serverVersion int, v []byte) (result []byte) {
diff --git a/vendor/github.com/lib/pq/encode_test.go b/vendor/github.com/lib/pq/encode_test.go
index 984abbc94..1e89f7f6f 100644
--- a/vendor/github.com/lib/pq/encode_test.go
+++ b/vendor/github.com/lib/pq/encode_test.go
@@ -575,6 +575,17 @@ func TestBinaryByteSliceToInt(t *testing.T) {
}
}
+func TestTextDecodeIntoString(t *testing.T) {
+ input := []byte("hello world")
+ want := string(input)
+ for _, typ := range []oid.Oid{oid.T_char, oid.T_varchar, oid.T_text} {
+ got := decode(&parameterStatus{}, input, typ, formatText)
+ if got != want {
+ t.Errorf("invalid string decoding output for %T(%+v), got %v but expected %v", typ, typ, got, want)
+ }
+ }
+}
+
func TestByteaOutputFormatEncoding(t *testing.T) {
input := []byte("\\x\x00\x01\x02\xFF\xFEabcdefg0123")
want := []byte("\\x5c78000102fffe6162636465666730313233")
diff --git a/vendor/github.com/lib/pq/ssl_test.go b/vendor/github.com/lib/pq/ssl_test.go
index 932b336f5..f70a5fd57 100644
--- a/vendor/github.com/lib/pq/ssl_test.go
+++ b/vendor/github.com/lib/pq/ssl_test.go
@@ -100,6 +100,49 @@ func TestSSLVerifyFull(t *testing.T) {
}
}
+// Test sslmode=require sslrootcert=rootCertPath
+func TestSSLRequireWithRootCert(t *testing.T) {
+ maybeSkipSSLTests(t)
+ // Environment sanity check: should fail without SSL
+ checkSSLSetup(t, "sslmode=disable user=pqgossltest")
+
+ bogusRootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "bogus_root.crt")
+ bogusRootCert := "sslrootcert=" + bogusRootCertPath + " "
+
+ // Not OK according to the bogus CA
+ _, err := openSSLConn(t, bogusRootCert+"host=postgres sslmode=require user=pqgossltest")
+ if err == nil {
+ t.Fatal("expected error")
+ }
+ _, ok := err.(x509.UnknownAuthorityError)
+ if !ok {
+ t.Fatalf("expected x509.UnknownAuthorityError, got %s, %#+v", err, err)
+ }
+
+ nonExistentCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "non_existent.crt")
+ nonExistentCert := "sslrootcert=" + nonExistentCertPath + " "
+
+ // No match on Common Name, but that's OK because we're not validating anything.
+ _, err = openSSLConn(t, nonExistentCert+"host=127.0.0.1 sslmode=require user=pqgossltest")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt")
+ rootCert := "sslrootcert=" + rootCertPath + " "
+
+ // No match on Common Name, but that's OK because we're not validating the CN.
+ _, err = openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=require user=pqgossltest")
+ if err != nil {
+ t.Fatal(err)
+ }
+ // Everything OK
+ _, err = openSSLConn(t, rootCert+"host=postgres sslmode=require user=pqgossltest")
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
// Test sslmode=verify-ca
func TestSSLVerifyCA(t *testing.T) {
maybeSkipSSLTests(t)