summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/lib/pq
diff options
context:
space:
mode:
authorChristopher Speller <crspeller@gmail.com>2016-05-12 23:56:07 -0400
committerChristopher Speller <crspeller@gmail.com>2016-05-12 23:56:07 -0400
commit38ee83e45b4de7edf89bf9f0ef629eb4c6ad0fa8 (patch)
treea4fde09672192b97d453ad605b030bd5a10c5a45 /vendor/github.com/lib/pq
parent84d2482ddbff9564c9ad75b2d30af66e3ddfd44d (diff)
downloadchat-38ee83e45b4de7edf89bf9f0ef629eb4c6ad0fa8.tar.gz
chat-38ee83e45b4de7edf89bf9f0ef629eb4c6ad0fa8.tar.bz2
chat-38ee83e45b4de7edf89bf9f0ef629eb4c6ad0fa8.zip
Moving to glide
Diffstat (limited to 'vendor/github.com/lib/pq')
-rw-r--r--vendor/github.com/lib/pq/.travis.yml3
-rw-r--r--vendor/github.com/lib/pq/README.md4
-rw-r--r--vendor/github.com/lib/pq/bench_test.go435
-rw-r--r--vendor/github.com/lib/pq/certs/README3
-rw-r--r--vendor/github.com/lib/pq/certs/postgresql.crt69
-rw-r--r--vendor/github.com/lib/pq/certs/postgresql.key15
-rw-r--r--vendor/github.com/lib/pq/certs/root.crt24
-rw-r--r--vendor/github.com/lib/pq/certs/server.crt81
-rw-r--r--vendor/github.com/lib/pq/certs/server.key27
-rw-r--r--vendor/github.com/lib/pq/conn_test.go1434
-rw-r--r--vendor/github.com/lib/pq/copy_test.go465
-rw-r--r--vendor/github.com/lib/pq/encode.go3
-rw-r--r--vendor/github.com/lib/pq/encode_test.go727
-rw-r--r--vendor/github.com/lib/pq/hstore/hstore.go118
-rw-r--r--vendor/github.com/lib/pq/hstore/hstore_test.go148
-rw-r--r--vendor/github.com/lib/pq/listen_example/doc.go102
-rw-r--r--vendor/github.com/lib/pq/notify.go22
-rw-r--r--vendor/github.com/lib/pq/notify_test.go574
-rw-r--r--vendor/github.com/lib/pq/ssl_test.go226
-rw-r--r--vendor/github.com/lib/pq/url_test.go66
20 files changed, 4538 insertions, 8 deletions
diff --git a/vendor/github.com/lib/pq/.travis.yml b/vendor/github.com/lib/pq/.travis.yml
index 567c7c666..d63afcb9f 100644
--- a/vendor/github.com/lib/pq/.travis.yml
+++ b/vendor/github.com/lib/pq/.travis.yml
@@ -37,6 +37,7 @@ before_install:
- 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
env:
global:
@@ -59,7 +60,7 @@ env:
- PGVERSION=9.0 PQTEST_BINARY_PARAMETERS=no
script:
- - go test -v ./...
+ - result=$(goimports -d -e $(find -name \*.go)); test -z "$result" || (echo "$result" && false) && go vet ./... && go test -v ./...
before_script:
- psql -c 'create database pqgotest' -U postgres
diff --git a/vendor/github.com/lib/pq/README.md b/vendor/github.com/lib/pq/README.md
index b4e3f45cb..148451e80 100644
--- a/vendor/github.com/lib/pq/README.md
+++ b/vendor/github.com/lib/pq/README.md
@@ -20,11 +20,11 @@ variables.
Example:
- PGHOST=/var/run/postgresql go test github.com/lib/pq
+ PGHOST=/run/postgresql go test github.com/lib/pq
Optionally, a benchmark suite can be run as part of the tests:
- PGHOST=/var/run/postgresql go test -bench .
+ PGHOST=/run/postgresql go test -bench .
## Features
diff --git a/vendor/github.com/lib/pq/bench_test.go b/vendor/github.com/lib/pq/bench_test.go
new file mode 100644
index 000000000..e71f41d06
--- /dev/null
+++ b/vendor/github.com/lib/pq/bench_test.go
@@ -0,0 +1,435 @@
+// +build go1.1
+
+package pq
+
+import (
+ "bufio"
+ "bytes"
+ "database/sql"
+ "database/sql/driver"
+ "io"
+ "math/rand"
+ "net"
+ "runtime"
+ "strconv"
+ "strings"
+ "sync"
+ "testing"
+ "time"
+
+ "github.com/lib/pq/oid"
+)
+
+var (
+ selectStringQuery = "SELECT '" + strings.Repeat("0123456789", 10) + "'"
+ selectSeriesQuery = "SELECT generate_series(1, 100)"
+)
+
+func BenchmarkSelectString(b *testing.B) {
+ var result string
+ benchQuery(b, selectStringQuery, &result)
+}
+
+func BenchmarkSelectSeries(b *testing.B) {
+ var result int
+ benchQuery(b, selectSeriesQuery, &result)
+}
+
+func benchQuery(b *testing.B, query string, result interface{}) {
+ b.StopTimer()
+ db := openTestConn(b)
+ defer db.Close()
+ b.StartTimer()
+
+ for i := 0; i < b.N; i++ {
+ benchQueryLoop(b, db, query, result)
+ }
+}
+
+func benchQueryLoop(b *testing.B, db *sql.DB, query string, result interface{}) {
+ rows, err := db.Query(query)
+ if err != nil {
+ b.Fatal(err)
+ }
+ defer rows.Close()
+ for rows.Next() {
+ err = rows.Scan(result)
+ if err != nil {
+ b.Fatal("failed to scan", err)
+ }
+ }
+}
+
+// reading from circularConn yields content[:prefixLen] once, followed by
+// content[prefixLen:] over and over again. It never returns EOF.
+type circularConn struct {
+ content string
+ prefixLen int
+ pos int
+ net.Conn // for all other net.Conn methods that will never be called
+}
+
+func (r *circularConn) Read(b []byte) (n int, err error) {
+ n = copy(b, r.content[r.pos:])
+ r.pos += n
+ if r.pos >= len(r.content) {
+ r.pos = r.prefixLen
+ }
+ return
+}
+
+func (r *circularConn) Write(b []byte) (n int, err error) { return len(b), nil }
+
+func (r *circularConn) Close() error { return nil }
+
+func fakeConn(content string, prefixLen int) *conn {
+ c := &circularConn{content: content, prefixLen: prefixLen}
+ return &conn{buf: bufio.NewReader(c), c: c}
+}
+
+// This benchmark is meant to be the same as BenchmarkSelectString, but takes
+// out some of the factors this package can't control. The numbers are less noisy,
+// but also the costs of network communication aren't accurately represented.
+func BenchmarkMockSelectString(b *testing.B) {
+ b.StopTimer()
+ // taken from a recorded run of BenchmarkSelectString
+ // See: http://www.postgresql.org/docs/current/static/protocol-message-formats.html
+ const response = "1\x00\x00\x00\x04" +
+ "t\x00\x00\x00\x06\x00\x00" +
+ "T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" +
+ "Z\x00\x00\x00\x05I" +
+ "2\x00\x00\x00\x04" +
+ "D\x00\x00\x00n\x00\x01\x00\x00\x00d0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
+ "C\x00\x00\x00\rSELECT 1\x00" +
+ "Z\x00\x00\x00\x05I" +
+ "3\x00\x00\x00\x04" +
+ "Z\x00\x00\x00\x05I"
+ c := fakeConn(response, 0)
+ b.StartTimer()
+
+ for i := 0; i < b.N; i++ {
+ benchMockQuery(b, c, selectStringQuery)
+ }
+}
+
+var seriesRowData = func() string {
+ var buf bytes.Buffer
+ for i := 1; i <= 100; i++ {
+ digits := byte(2)
+ if i >= 100 {
+ digits = 3
+ } else if i < 10 {
+ digits = 1
+ }
+ buf.WriteString("D\x00\x00\x00")
+ buf.WriteByte(10 + digits)
+ buf.WriteString("\x00\x01\x00\x00\x00")
+ buf.WriteByte(digits)
+ buf.WriteString(strconv.Itoa(i))
+ }
+ return buf.String()
+}()
+
+func BenchmarkMockSelectSeries(b *testing.B) {
+ b.StopTimer()
+ var response = "1\x00\x00\x00\x04" +
+ "t\x00\x00\x00\x06\x00\x00" +
+ "T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" +
+ "Z\x00\x00\x00\x05I" +
+ "2\x00\x00\x00\x04" +
+ seriesRowData +
+ "C\x00\x00\x00\x0fSELECT 100\x00" +
+ "Z\x00\x00\x00\x05I" +
+ "3\x00\x00\x00\x04" +
+ "Z\x00\x00\x00\x05I"
+ c := fakeConn(response, 0)
+ b.StartTimer()
+
+ for i := 0; i < b.N; i++ {
+ benchMockQuery(b, c, selectSeriesQuery)
+ }
+}
+
+func benchMockQuery(b *testing.B, c *conn, query string) {
+ stmt, err := c.Prepare(query)
+ if err != nil {
+ b.Fatal(err)
+ }
+ defer stmt.Close()
+ rows, err := stmt.Query(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ defer rows.Close()
+ var dest [1]driver.Value
+ for {
+ if err := rows.Next(dest[:]); err != nil {
+ if err == io.EOF {
+ break
+ }
+ b.Fatal(err)
+ }
+ }
+}
+
+func BenchmarkPreparedSelectString(b *testing.B) {
+ var result string
+ benchPreparedQuery(b, selectStringQuery, &result)
+}
+
+func BenchmarkPreparedSelectSeries(b *testing.B) {
+ var result int
+ benchPreparedQuery(b, selectSeriesQuery, &result)
+}
+
+func benchPreparedQuery(b *testing.B, query string, result interface{}) {
+ b.StopTimer()
+ db := openTestConn(b)
+ defer db.Close()
+ stmt, err := db.Prepare(query)
+ if err != nil {
+ b.Fatal(err)
+ }
+ defer stmt.Close()
+ b.StartTimer()
+
+ for i := 0; i < b.N; i++ {
+ benchPreparedQueryLoop(b, db, stmt, result)
+ }
+}
+
+func benchPreparedQueryLoop(b *testing.B, db *sql.DB, stmt *sql.Stmt, result interface{}) {
+ rows, err := stmt.Query()
+ if err != nil {
+ b.Fatal(err)
+ }
+ if !rows.Next() {
+ rows.Close()
+ b.Fatal("no rows")
+ }
+ defer rows.Close()
+ for rows.Next() {
+ err = rows.Scan(&result)
+ if err != nil {
+ b.Fatal("failed to scan")
+ }
+ }
+}
+
+// See the comment for BenchmarkMockSelectString.
+func BenchmarkMockPreparedSelectString(b *testing.B) {
+ b.StopTimer()
+ const parseResponse = "1\x00\x00\x00\x04" +
+ "t\x00\x00\x00\x06\x00\x00" +
+ "T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" +
+ "Z\x00\x00\x00\x05I"
+ const responses = parseResponse +
+ "2\x00\x00\x00\x04" +
+ "D\x00\x00\x00n\x00\x01\x00\x00\x00d0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
+ "C\x00\x00\x00\rSELECT 1\x00" +
+ "Z\x00\x00\x00\x05I"
+ c := fakeConn(responses, len(parseResponse))
+
+ stmt, err := c.Prepare(selectStringQuery)
+ if err != nil {
+ b.Fatal(err)
+ }
+ b.StartTimer()
+
+ for i := 0; i < b.N; i++ {
+ benchPreparedMockQuery(b, c, stmt)
+ }
+}
+
+func BenchmarkMockPreparedSelectSeries(b *testing.B) {
+ b.StopTimer()
+ const parseResponse = "1\x00\x00\x00\x04" +
+ "t\x00\x00\x00\x06\x00\x00" +
+ "T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" +
+ "Z\x00\x00\x00\x05I"
+ var responses = parseResponse +
+ "2\x00\x00\x00\x04" +
+ seriesRowData +
+ "C\x00\x00\x00\x0fSELECT 100\x00" +
+ "Z\x00\x00\x00\x05I"
+ c := fakeConn(responses, len(parseResponse))
+
+ stmt, err := c.Prepare(selectSeriesQuery)
+ if err != nil {
+ b.Fatal(err)
+ }
+ b.StartTimer()
+
+ for i := 0; i < b.N; i++ {
+ benchPreparedMockQuery(b, c, stmt)
+ }
+}
+
+func benchPreparedMockQuery(b *testing.B, c *conn, stmt driver.Stmt) {
+ rows, err := stmt.Query(nil)
+ if err != nil {
+ b.Fatal(err)
+ }
+ defer rows.Close()
+ var dest [1]driver.Value
+ for {
+ if err := rows.Next(dest[:]); err != nil {
+ if err == io.EOF {
+ break
+ }
+ b.Fatal(err)
+ }
+ }
+}
+
+func BenchmarkEncodeInt64(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ encode(&parameterStatus{}, int64(1234), oid.T_int8)
+ }
+}
+
+func BenchmarkEncodeFloat64(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ encode(&parameterStatus{}, 3.14159, oid.T_float8)
+ }
+}
+
+var testByteString = []byte("abcdefghijklmnopqrstuvwxyz")
+
+func BenchmarkEncodeByteaHex(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ encode(&parameterStatus{serverVersion: 90000}, testByteString, oid.T_bytea)
+ }
+}
+func BenchmarkEncodeByteaEscape(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ encode(&parameterStatus{serverVersion: 84000}, testByteString, oid.T_bytea)
+ }
+}
+
+func BenchmarkEncodeBool(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ encode(&parameterStatus{}, true, oid.T_bool)
+ }
+}
+
+var testTimestamptz = time.Date(2001, time.January, 1, 0, 0, 0, 0, time.Local)
+
+func BenchmarkEncodeTimestamptz(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ encode(&parameterStatus{}, testTimestamptz, oid.T_timestamptz)
+ }
+}
+
+var testIntBytes = []byte("1234")
+
+func BenchmarkDecodeInt64(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ decode(&parameterStatus{}, testIntBytes, oid.T_int8, formatText)
+ }
+}
+
+var testFloatBytes = []byte("3.14159")
+
+func BenchmarkDecodeFloat64(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ decode(&parameterStatus{}, testFloatBytes, oid.T_float8, formatText)
+ }
+}
+
+var testBoolBytes = []byte{'t'}
+
+func BenchmarkDecodeBool(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ decode(&parameterStatus{}, testBoolBytes, oid.T_bool, formatText)
+ }
+}
+
+func TestDecodeBool(t *testing.T) {
+ db := openTestConn(t)
+ rows, err := db.Query("select true")
+ if err != nil {
+ t.Fatal(err)
+ }
+ rows.Close()
+}
+
+var testTimestamptzBytes = []byte("2013-09-17 22:15:32.360754-07")
+
+func BenchmarkDecodeTimestamptz(b *testing.B) {
+ for i := 0; i < b.N; i++ {
+ decode(&parameterStatus{}, testTimestamptzBytes, oid.T_timestamptz, formatText)
+ }
+}
+
+func BenchmarkDecodeTimestamptzMultiThread(b *testing.B) {
+ oldProcs := runtime.GOMAXPROCS(0)
+ defer runtime.GOMAXPROCS(oldProcs)
+ runtime.GOMAXPROCS(runtime.NumCPU())
+ globalLocationCache = newLocationCache()
+
+ f := func(wg *sync.WaitGroup, loops int) {
+ defer wg.Done()
+ for i := 0; i < loops; i++ {
+ decode(&parameterStatus{}, testTimestamptzBytes, oid.T_timestamptz, formatText)
+ }
+ }
+
+ wg := &sync.WaitGroup{}
+ b.ResetTimer()
+ for j := 0; j < 10; j++ {
+ wg.Add(1)
+ go f(wg, b.N/10)
+ }
+ wg.Wait()
+}
+
+func BenchmarkLocationCache(b *testing.B) {
+ globalLocationCache = newLocationCache()
+ for i := 0; i < b.N; i++ {
+ globalLocationCache.getLocation(rand.Intn(10000))
+ }
+}
+
+func BenchmarkLocationCacheMultiThread(b *testing.B) {
+ oldProcs := runtime.GOMAXPROCS(0)
+ defer runtime.GOMAXPROCS(oldProcs)
+ runtime.GOMAXPROCS(runtime.NumCPU())
+ globalLocationCache = newLocationCache()
+
+ f := func(wg *sync.WaitGroup, loops int) {
+ defer wg.Done()
+ for i := 0; i < loops; i++ {
+ globalLocationCache.getLocation(rand.Intn(10000))
+ }
+ }
+
+ wg := &sync.WaitGroup{}
+ b.ResetTimer()
+ for j := 0; j < 10; j++ {
+ wg.Add(1)
+ go f(wg, b.N/10)
+ }
+ wg.Wait()
+}
+
+// Stress test the performance of parsing results from the wire.
+func BenchmarkResultParsing(b *testing.B) {
+ b.StopTimer()
+
+ db := openTestConn(b)
+ defer db.Close()
+ _, err := db.Exec("BEGIN")
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ b.StartTimer()
+ for i := 0; i < b.N; i++ {
+ res, err := db.Query("SELECT generate_series(1, 50000)")
+ if err != nil {
+ b.Fatal(err)
+ }
+ res.Close()
+ }
+}
diff --git a/vendor/github.com/lib/pq/certs/README b/vendor/github.com/lib/pq/certs/README
new file mode 100644
index 000000000..24ab7b256
--- /dev/null
+++ b/vendor/github.com/lib/pq/certs/README
@@ -0,0 +1,3 @@
+This directory contains certificates and private keys for testing some
+SSL-related functionality in Travis. Do NOT use these certificates for
+anything other than testing.
diff --git a/vendor/github.com/lib/pq/certs/postgresql.crt b/vendor/github.com/lib/pq/certs/postgresql.crt
new file mode 100644
index 000000000..6e6b4284a
--- /dev/null
+++ b/vendor/github.com/lib/pq/certs/postgresql.crt
@@ -0,0 +1,69 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 2 (0x2)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, ST=Nevada, L=Las Vegas, O=github.com/lib/pq, CN=pq CA
+ Validity
+ Not Before: Oct 11 15:10:11 2014 GMT
+ Not After : Oct 8 15:10:11 2024 GMT
+ Subject: C=US, ST=Nevada, L=Las Vegas, O=github.com/lib/pq, CN=pqgosslcert
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (1024 bit)
+ Modulus (1024 bit):
+ 00:e3:8c:06:9a:70:54:51:d1:34:34:83:39:cd:a2:
+ 59:0f:05:ed:8d:d8:0e:34:d0:92:f4:09:4d:ee:8c:
+ 78:55:49:24:f8:3c:e0:34:58:02:b2:e7:94:58:c1:
+ e8:e5:bb:d1:af:f6:54:c1:40:b1:90:70:79:0d:35:
+ 54:9c:8f:16:e9:c2:f0:92:e6:64:49:38:c1:76:f8:
+ 47:66:c4:5b:4a:b6:a9:43:ce:c8:be:6c:4d:2b:94:
+ 97:3c:55:bc:d1:d0:6e:b7:53:ae:89:5c:4b:6b:86:
+ 40:be:c1:ae:1e:64:ce:9c:ae:87:0a:69:e5:c8:21:
+ 12:be:ae:1d:f6:45:df:16:a7
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ 9B:25:31:63:A2:D8:06:FF:CB:E3:E9:96:FF:0D:BA:DC:12:7D:04:CF
+ X509v3 Authority Key Identifier:
+ keyid:52:93:ED:1E:76:0A:9F:65:4F:DE:19:66:C1:D5:22:40:35:CB:A0:72
+
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Key Usage:
+ Digital Signature, Non Repudiation, Key Encipherment
+ Signature Algorithm: sha256WithRSAEncryption
+ 3e:f5:f8:0b:4e:11:bd:00:86:1f:ce:dc:97:02:98:91:11:f5:
+ 65:f6:f2:8a:b2:3e:47:92:05:69:28:c9:e9:b4:f7:cf:93:d1:
+ 2d:81:5d:00:3c:23:be:da:70:ea:59:e1:2c:d3:25:49:ae:a6:
+ 95:54:c1:10:df:23:e3:fe:d6:e4:76:c7:6b:73:ad:1b:34:7c:
+ e2:56:cc:c0:37:ae:c5:7a:11:20:6c:3d:05:0e:99:cd:22:6c:
+ cf:59:a1:da:28:d4:65:ba:7d:2f:2b:3d:69:6d:a6:c1:ae:57:
+ bf:56:64:13:79:f8:48:46:65:eb:81:67:28:0b:7b:de:47:10:
+ b3:80:3c:31:d1:58:94:01:51:4a:c7:c8:1a:01:a8:af:c4:cd:
+ bb:84:a5:d9:8b:b4:b9:a1:64:3e:95:d9:90:1d:d5:3f:67:cc:
+ 3b:ba:f5:b4:d1:33:77:ee:c2:d2:3e:7e:c5:66:6e:b7:35:4c:
+ 60:57:b0:b8:be:36:c8:f3:d3:95:8c:28:4a:c9:f7:27:a4:0d:
+ e5:96:99:eb:f5:c8:bd:f3:84:6d:ef:02:f9:8a:36:7d:6b:5f:
+ 36:68:37:41:d9:74:ae:c6:78:2e:44:86:a1:ad:43:ca:fb:b5:
+ 3e:ba:10:23:09:02:ac:62:d1:d0:83:c8:95:b9:e3:5e:30:ff:
+ 5b:2b:38:fa
+-----BEGIN CERTIFICATE-----
+MIIDEzCCAfugAwIBAgIBAjANBgkqhkiG9w0BAQsFADBeMQswCQYDVQQGEwJVUzEP
+MA0GA1UECBMGTmV2YWRhMRIwEAYDVQQHEwlMYXMgVmVnYXMxGjAYBgNVBAoTEWdp
+dGh1Yi5jb20vbGliL3BxMQ4wDAYDVQQDEwVwcSBDQTAeFw0xNDEwMTExNTEwMTFa
+Fw0yNDEwMDgxNTEwMTFaMGQxCzAJBgNVBAYTAlVTMQ8wDQYDVQQIEwZOZXZhZGEx
+EjAQBgNVBAcTCUxhcyBWZWdhczEaMBgGA1UEChMRZ2l0aHViLmNvbS9saWIvcHEx
+FDASBgNVBAMTC3BxZ29zc2xjZXJ0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
+gQDjjAaacFRR0TQ0gznNolkPBe2N2A400JL0CU3ujHhVSST4POA0WAKy55RYwejl
+u9Gv9lTBQLGQcHkNNVScjxbpwvCS5mRJOMF2+EdmxFtKtqlDzsi+bE0rlJc8VbzR
+0G63U66JXEtrhkC+wa4eZM6crocKaeXIIRK+rh32Rd8WpwIDAQABo1owWDAdBgNV
+HQ4EFgQUmyUxY6LYBv/L4+mW/w263BJ9BM8wHwYDVR0jBBgwFoAUUpPtHnYKn2VP
+3hlmwdUiQDXLoHIwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwDQYJKoZIhvcNAQEL
+BQADggEBAD71+AtOEb0Ahh/O3JcCmJER9WX28oqyPkeSBWkoyem098+T0S2BXQA8
+I77acOpZ4SzTJUmuppVUwRDfI+P+1uR2x2tzrRs0fOJWzMA3rsV6ESBsPQUOmc0i
+bM9Zodoo1GW6fS8rPWltpsGuV79WZBN5+EhGZeuBZygLe95HELOAPDHRWJQBUUrH
+yBoBqK/EzbuEpdmLtLmhZD6V2ZAd1T9nzDu69bTRM3fuwtI+fsVmbrc1TGBXsLi+
+Nsjz05WMKErJ9yekDeWWmev1yL3zhG3vAvmKNn1rXzZoN0HZdK7GeC5EhqGtQ8r7
+tT66ECMJAqxi0dCDyJW5414w/1srOPo=
+-----END CERTIFICATE-----
diff --git a/vendor/github.com/lib/pq/certs/postgresql.key b/vendor/github.com/lib/pq/certs/postgresql.key
new file mode 100644
index 000000000..eb8b20be9
--- /dev/null
+++ b/vendor/github.com/lib/pq/certs/postgresql.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQDjjAaacFRR0TQ0gznNolkPBe2N2A400JL0CU3ujHhVSST4POA0
+WAKy55RYwejlu9Gv9lTBQLGQcHkNNVScjxbpwvCS5mRJOMF2+EdmxFtKtqlDzsi+
+bE0rlJc8VbzR0G63U66JXEtrhkC+wa4eZM6crocKaeXIIRK+rh32Rd8WpwIDAQAB
+AoGAM5dM6/kp9P700i8qjOgRPym96Zoh5nGfz/rIE5z/r36NBkdvIg8OVZfR96nH
+b0b9TOMR5lsPp0sI9yivTWvX6qyvLJRWy2vvx17hXK9NxXUNTAm0PYZUTvCtcPeX
+RnJpzQKNZQPkFzF0uXBc4CtPK2Vz0+FGvAelrhYAxnw1dIkCQQD+9qaW5QhXjsjb
+Nl85CmXgxPmGROcgLQCO+omfrjf9UXrituU9Dz6auym5lDGEdMFnkzfr+wpasEy9
+mf5ZZOhDAkEA5HjXfVGaCtpydOt6hDon/uZsyssCK2lQ7NSuE3vP+sUsYMzIpEoy
+t3VWXqKbo+g9KNDTP4WEliqp1aiSIylzzQJANPeqzihQnlgEdD4MdD4rwhFJwVIp
+Le8Lcais1KaN7StzOwxB/XhgSibd2TbnPpw+3bSg5n5lvUdo+e62/31OHwJAU1jS
+I+F09KikQIr28u3UUWT2IzTT4cpVv1AHAQyV3sG3YsjSGT0IK20eyP9BEBZU2WL0
+7aNjrvR5aHxKc5FXsQJABsFtyGpgI5X4xufkJZVZ+Mklz2n7iXa+XPatMAHFxAtb
+EEMt60rngwMjXAzBSC6OYuYogRRAY3UCacNC5VhLYQ==
+-----END RSA PRIVATE KEY-----
diff --git a/vendor/github.com/lib/pq/certs/root.crt b/vendor/github.com/lib/pq/certs/root.crt
new file mode 100644
index 000000000..aecf8f621
--- /dev/null
+++ b/vendor/github.com/lib/pq/certs/root.crt
@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIIEAzCCAuugAwIBAgIJANmheROCdW1NMA0GCSqGSIb3DQEBBQUAMF4xCzAJBgNV
+BAYTAlVTMQ8wDQYDVQQIEwZOZXZhZGExEjAQBgNVBAcTCUxhcyBWZWdhczEaMBgG
+A1UEChMRZ2l0aHViLmNvbS9saWIvcHExDjAMBgNVBAMTBXBxIENBMB4XDTE0MTAx
+MTE1MDQyOVoXDTI0MTAwODE1MDQyOVowXjELMAkGA1UEBhMCVVMxDzANBgNVBAgT
+Bk5ldmFkYTESMBAGA1UEBxMJTGFzIFZlZ2FzMRowGAYDVQQKExFnaXRodWIuY29t
+L2xpYi9wcTEOMAwGA1UEAxMFcHEgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQCV4PxP7ShzWBzUCThcKk3qZtOLtHmszQVtbqhvgTpm1kTRtKBdVMu0
+pLAHQ3JgJCnAYgH0iZxVGoMP16T3irdgsdC48+nNTFM2T0cCdkfDURGIhSFN47cb
+Pgy306BcDUD2q7ucW33+dlFSRuGVewocoh4BWM/vMtMvvWzdi4Ag/L/jhb+5wZxZ
+sWymsadOVSDePEMKOvlCa3EdVwVFV40TVyDb+iWBUivDAYsS2a3KajuJrO6MbZiE
+Sp2RCIkZS2zFmzWxVRi9ZhzIZhh7EVF9JAaNC3T52jhGUdlRq3YpBTMnd89iOh74
+6jWXG7wSuPj3haFzyNhmJ0ZUh+2Ynoh1AgMBAAGjgcMwgcAwHQYDVR0OBBYEFFKT
+7R52Cp9lT94ZZsHVIkA1y6ByMIGQBgNVHSMEgYgwgYWAFFKT7R52Cp9lT94ZZsHV
+IkA1y6ByoWKkYDBeMQswCQYDVQQGEwJVUzEPMA0GA1UECBMGTmV2YWRhMRIwEAYD
+VQQHEwlMYXMgVmVnYXMxGjAYBgNVBAoTEWdpdGh1Yi5jb20vbGliL3BxMQ4wDAYD
+VQQDEwVwcSBDQYIJANmheROCdW1NMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF
+BQADggEBAAEhCLWkqJNMI8b4gkbmj5fqQ/4+oO83bZ3w2Oqf6eZ8I8BC4f2NOyE6
+tRUlq5+aU7eqC1cOAvGjO+YHN/bF/DFpwLlzvUSXt+JP/pYcUjL7v+pIvwqec9hD
+ndvM4iIbkD/H/OYQ3L+N3W+G1x7AcFIX+bGCb3PzYVQAjxreV6//wgKBosMGFbZo
+HPxT9RPMun61SViF04H5TNs0derVn1+5eiiYENeAhJzQNyZoOOUuX1X/Inx9bEPh
+C5vFBtSMgIytPgieRJVWAiMLYsfpIAStrHztRAbBs2DU01LmMgRvHdxgFEKinC/d
+UHZZQDP+6pT+zADrGhQGXe4eThaO6f0=
+-----END CERTIFICATE-----
diff --git a/vendor/github.com/lib/pq/certs/server.crt b/vendor/github.com/lib/pq/certs/server.crt
new file mode 100644
index 000000000..ddc995a6d
--- /dev/null
+++ b/vendor/github.com/lib/pq/certs/server.crt
@@ -0,0 +1,81 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 1 (0x1)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=US, ST=Nevada, L=Las Vegas, O=github.com/lib/pq, CN=pq CA
+ Validity
+ Not Before: Oct 11 15:05:15 2014 GMT
+ Not After : Oct 8 15:05:15 2024 GMT
+ Subject: C=US, ST=Nevada, L=Las Vegas, O=github.com/lib/pq, CN=postgres
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public Key: (2048 bit)
+ Modulus (2048 bit):
+ 00:d7:8a:4c:85:fb:17:a5:3c:8f:e0:72:11:29:ce:
+ 3f:b0:1f:3f:7d:c6:ee:7f:a7:fc:02:2b:35:47:08:
+ a6:3d:90:df:5c:56:14:94:00:c7:6d:d1:d2:e2:61:
+ 95:77:b8:e3:a6:66:31:f9:1f:21:7d:62:e1:27:da:
+ 94:37:61:4a:ea:63:53:a0:61:b8:9c:bb:a5:e2:e7:
+ b7:a6:d8:0f:05:04:c7:29:e2:ea:49:2b:7f:de:15:
+ 00:a6:18:70:50:c7:0c:de:9a:f9:5a:96:b0:e1:94:
+ 06:c6:6d:4a:21:3b:b4:0f:a5:6d:92:86:34:b2:4e:
+ d7:0e:a7:19:c0:77:0b:7b:87:c8:92:de:42:ff:86:
+ d2:b7:9a:a4:d4:15:23:ca:ad:a5:69:21:b8:ce:7e:
+ 66:cb:85:5d:b9:ed:8b:2d:09:8d:94:e4:04:1e:72:
+ ec:ef:d0:76:90:15:5a:a4:f7:91:4b:e9:ce:4e:9d:
+ 5d:9a:70:17:9c:d8:e9:73:83:ea:3d:61:99:a6:cd:
+ ac:91:40:5a:88:77:e5:4e:2a:8e:3d:13:f3:f9:38:
+ 6f:81:6b:8a:95:ca:0e:07:ab:6f:da:b4:8c:d9:ff:
+ aa:78:03:aa:c7:c2:cf:6f:64:92:d3:d8:83:d5:af:
+ f1:23:18:a7:2e:7b:17:0b:e7:7d:f1:fa:a8:41:a3:
+ 04:57
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ EE:F0:B3:46:DC:C7:09:EB:0E:B6:2F:E5:FE:62:60:45:44:9F:59:CC
+ X509v3 Authority Key Identifier:
+ keyid:52:93:ED:1E:76:0A:9F:65:4F:DE:19:66:C1:D5:22:40:35:CB:A0:72
+
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Key Usage:
+ Digital Signature, Non Repudiation, Key Encipherment
+ Signature Algorithm: sha256WithRSAEncryption
+ 7e:5a:6e:be:bf:d2:6c:c1:d6:fa:b6:fb:3f:06:53:36:08:87:
+ 9d:95:b1:39:af:9e:f6:47:38:17:39:da:25:7c:f2:ad:0c:e3:
+ ab:74:19:ca:fb:8c:a0:50:c0:1d:19:8a:9c:21:ed:0f:3a:d1:
+ 96:54:2e:10:09:4f:b8:70:f7:2b:99:43:d2:c6:15:bc:3f:24:
+ 7d:28:39:32:3f:8d:a4:4f:40:75:7f:3e:0d:1c:d1:69:f2:4e:
+ 98:83:47:97:d2:25:ac:c9:36:86:2f:04:a6:c4:86:c7:c4:00:
+ 5f:7f:b9:ad:fc:bf:e9:f5:78:d7:82:1a:51:0d:fc:ab:9e:92:
+ 1d:5f:0c:18:d1:82:e0:14:c9:ce:91:89:71:ff:49:49:ff:35:
+ bf:7b:44:78:42:c1:d0:66:65:bb:28:2e:60:ca:9b:20:12:a9:
+ 90:61:b1:96:ec:15:46:c9:37:f7:07:90:8a:89:45:2a:3f:37:
+ ec:dc:e3:e5:8f:c3:3a:57:80:a5:54:60:0c:e1:b2:26:99:2b:
+ 40:7e:36:d1:9a:70:02:ec:63:f4:3b:72:ae:81:fb:30:20:6d:
+ cb:48:46:c6:b5:8f:39:b1:84:05:25:55:8d:f5:62:f6:1b:46:
+ 2e:da:a3:4c:26:12:44:d7:56:b6:b8:a9:ca:d3:ab:71:45:7c:
+ 9f:48:6d:1e
+-----BEGIN CERTIFICATE-----
+MIIDlDCCAnygAwIBAgIBATANBgkqhkiG9w0BAQsFADBeMQswCQYDVQQGEwJVUzEP
+MA0GA1UECBMGTmV2YWRhMRIwEAYDVQQHEwlMYXMgVmVnYXMxGjAYBgNVBAoTEWdp
+dGh1Yi5jb20vbGliL3BxMQ4wDAYDVQQDEwVwcSBDQTAeFw0xNDEwMTExNTA1MTVa
+Fw0yNDEwMDgxNTA1MTVaMGExCzAJBgNVBAYTAlVTMQ8wDQYDVQQIEwZOZXZhZGEx
+EjAQBgNVBAcTCUxhcyBWZWdhczEaMBgGA1UEChMRZ2l0aHViLmNvbS9saWIvcHEx
+ETAPBgNVBAMTCHBvc3RncmVzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEA14pMhfsXpTyP4HIRKc4/sB8/fcbuf6f8Ais1RwimPZDfXFYUlADHbdHS4mGV
+d7jjpmYx+R8hfWLhJ9qUN2FK6mNToGG4nLul4ue3ptgPBQTHKeLqSSt/3hUAphhw
+UMcM3pr5Wpaw4ZQGxm1KITu0D6VtkoY0sk7XDqcZwHcLe4fIkt5C/4bSt5qk1BUj
+yq2laSG4zn5my4Vdue2LLQmNlOQEHnLs79B2kBVapPeRS+nOTp1dmnAXnNjpc4Pq
+PWGZps2skUBaiHflTiqOPRPz+ThvgWuKlcoOB6tv2rSM2f+qeAOqx8LPb2SS09iD
+1a/xIxinLnsXC+d98fqoQaMEVwIDAQABo1owWDAdBgNVHQ4EFgQU7vCzRtzHCesO
+ti/l/mJgRUSfWcwwHwYDVR0jBBgwFoAUUpPtHnYKn2VP3hlmwdUiQDXLoHIwCQYD
+VR0TBAIwADALBgNVHQ8EBAMCBeAwDQYJKoZIhvcNAQELBQADggEBAH5abr6/0mzB
+1vq2+z8GUzYIh52VsTmvnvZHOBc52iV88q0M46t0Gcr7jKBQwB0Zipwh7Q860ZZU
+LhAJT7hw9yuZQ9LGFbw/JH0oOTI/jaRPQHV/Pg0c0WnyTpiDR5fSJazJNoYvBKbE
+hsfEAF9/ua38v+n1eNeCGlEN/Kuekh1fDBjRguAUyc6RiXH/SUn/Nb97RHhCwdBm
+ZbsoLmDKmyASqZBhsZbsFUbJN/cHkIqJRSo/N+zc4+WPwzpXgKVUYAzhsiaZK0B+
+NtGacALsY/Q7cq6B+zAgbctIRsa1jzmxhAUlVY31YvYbRi7ao0wmEkTXVra4qcrT
+q3FFfJ9IbR4=
+-----END CERTIFICATE-----
diff --git a/vendor/github.com/lib/pq/certs/server.key b/vendor/github.com/lib/pq/certs/server.key
new file mode 100644
index 000000000..bd7b019b6
--- /dev/null
+++ b/vendor/github.com/lib/pq/certs/server.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEA14pMhfsXpTyP4HIRKc4/sB8/fcbuf6f8Ais1RwimPZDfXFYU
+lADHbdHS4mGVd7jjpmYx+R8hfWLhJ9qUN2FK6mNToGG4nLul4ue3ptgPBQTHKeLq
+SSt/3hUAphhwUMcM3pr5Wpaw4ZQGxm1KITu0D6VtkoY0sk7XDqcZwHcLe4fIkt5C
+/4bSt5qk1BUjyq2laSG4zn5my4Vdue2LLQmNlOQEHnLs79B2kBVapPeRS+nOTp1d
+mnAXnNjpc4PqPWGZps2skUBaiHflTiqOPRPz+ThvgWuKlcoOB6tv2rSM2f+qeAOq
+x8LPb2SS09iD1a/xIxinLnsXC+d98fqoQaMEVwIDAQABAoIBAF3ZoihUhJ82F4+r
+Gz4QyDpv4L1reT2sb1aiabhcU8ZK5nbWJG+tRyjSS/i2dNaEcttpdCj9HR/zhgZM
+bm0OuAgG58rVwgS80CZUruq++Qs+YVojq8/gWPTiQD4SNhV2Fmx3HkwLgUk3oxuT
+SsvdqzGE3okGVrutCIcgy126eA147VPMoej1Bb3fO6npqK0pFPhZfAc0YoqJuM+k
+obRm5pAnGUipyLCFXjA9HYPKwYZw2RtfdA3CiImHeanSdqS+ctrC9y8BV40Th7gZ
+haXdKUNdjmIxV695QQ1mkGqpKLZFqhzKioGQ2/Ly2d1iaKN9fZltTusu8unepWJ2
+tlT9qMECgYEA9uHaF1t2CqE+AJvWTihHhPIIuLxoOQXYea1qvxfcH/UMtaLKzCNm
+lQ5pqCGsPvp+10f36yttO1ZehIvlVNXuJsjt0zJmPtIolNuJY76yeussfQ9jHheB
+5uPEzCFlHzxYbBUyqgWaF6W74okRGzEGJXjYSP0yHPPdU4ep2q3bGiUCgYEA34Af
+wBSuQSK7uLxArWHvQhyuvi43ZGXls6oRGl+Ysj54s8BP6XGkq9hEJ6G4yxgyV+BR
+DUOs5X8/TLT8POuIMYvKTQthQyCk0eLv2FLdESDuuKx0kBVY3s8lK3/z5HhrdOiN
+VMNZU+xDKgKc3hN9ypkk8vcZe6EtH7Y14e0rVcsCgYBTgxi8F/M5K0wG9rAqphNz
+VFBA9XKn/2M33cKjO5X5tXIEKzpAjaUQvNxexG04rJGljzG8+mar0M6ONahw5yD1
+O7i/XWgazgpuOEkkVYiYbd8RutfDgR4vFVMn3hAP3eDnRtBplRWH9Ec3HTiNIys6
+F8PKBOQjyRZQQC7jyzW3hQKBgACe5HeuFwXLSOYsb6mLmhR+6+VPT4wR1F95W27N
+USk9jyxAnngxfpmTkiziABdgS9N+pfr5cyN4BP77ia/Jn6kzkC5Cl9SN5KdIkA3z
+vPVtN/x/ThuQU5zaymmig1ThGLtMYggYOslG4LDfLPxY5YKIhle+Y+259twdr2yf
+Mf2dAoGAaGv3tWMgnIdGRk6EQL/yb9PKHo7ShN+tKNlGaK7WwzBdKs+Fe8jkgcr7
+pz4Ne887CmxejdISzOCcdT+Zm9Bx6I/uZwWOtDvWpIgIxVX9a9URj/+D1MxTE/y4
+d6H+c89yDY62I2+drMpdjCd3EtCaTlxpTbRS+s1eAHMH7aEkcCE=
+-----END RSA PRIVATE KEY-----
diff --git a/vendor/github.com/lib/pq/conn_test.go b/vendor/github.com/lib/pq/conn_test.go
new file mode 100644
index 000000000..274147c27
--- /dev/null
+++ b/vendor/github.com/lib/pq/conn_test.go
@@ -0,0 +1,1434 @@
+package pq
+
+import (
+ "database/sql"
+ "database/sql/driver"
+ "fmt"
+ "io"
+ "os"
+ "reflect"
+ "strings"
+ "testing"
+ "time"
+)
+
+type Fatalistic interface {
+ Fatal(args ...interface{})
+}
+
+func forceBinaryParameters() bool {
+ bp := os.Getenv("PQTEST_BINARY_PARAMETERS")
+ if bp == "yes" {
+ return true
+ } else if bp == "" || bp == "no" {
+ return false
+ } else {
+ panic("unexpected value for PQTEST_BINARY_PARAMETERS")
+ }
+}
+
+func openTestConnConninfo(conninfo string) (*sql.DB, error) {
+ defaultTo := func(envvar string, value string) {
+ if os.Getenv(envvar) == "" {
+ os.Setenv(envvar, value)
+ }
+ }
+ defaultTo("PGDATABASE", "pqgotest")
+ defaultTo("PGSSLMODE", "disable")
+ defaultTo("PGCONNECT_TIMEOUT", "20")
+
+ if forceBinaryParameters() &&
+ !strings.HasPrefix(conninfo, "postgres://") &&
+ !strings.HasPrefix(conninfo, "postgresql://") {
+ conninfo = conninfo + " binary_parameters=yes"
+ }
+
+ return sql.Open("postgres", conninfo)
+}
+
+func openTestConn(t Fatalistic) *sql.DB {
+ conn, err := openTestConnConninfo("")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ return conn
+}
+
+func getServerVersion(t *testing.T, db *sql.DB) int {
+ var version int
+ err := db.QueryRow("SHOW server_version_num").Scan(&version)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return version
+}
+
+func TestReconnect(t *testing.T) {
+ db1 := openTestConn(t)
+ defer db1.Close()
+ tx, err := db1.Begin()
+ if err != nil {
+ t.Fatal(err)
+ }
+ var pid1 int
+ err = tx.QueryRow("SELECT pg_backend_pid()").Scan(&pid1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ db2 := openTestConn(t)
+ defer db2.Close()
+ _, err = db2.Exec("SELECT pg_terminate_backend($1)", pid1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ // The rollback will probably "fail" because we just killed
+ // its connection above
+ _ = tx.Rollback()
+
+ const expected int = 42
+ var result int
+ err = db1.QueryRow(fmt.Sprintf("SELECT %d", expected)).Scan(&result)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if result != expected {
+ t.Errorf("got %v; expected %v", result, expected)
+ }
+}
+
+func TestCommitInFailedTransaction(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ txn, err := db.Begin()
+ if err != nil {
+ t.Fatal(err)
+ }
+ rows, err := txn.Query("SELECT error")
+ if err == nil {
+ rows.Close()
+ t.Fatal("expected failure")
+ }
+ err = txn.Commit()
+ if err != ErrInFailedTransaction {
+ t.Fatalf("expected ErrInFailedTransaction; got %#v", err)
+ }
+}
+
+func TestOpenURL(t *testing.T) {
+ testURL := func(url string) {
+ db, err := openTestConnConninfo(url)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer db.Close()
+ // database/sql might not call our Open at all unless we do something with
+ // the connection
+ txn, err := db.Begin()
+ if err != nil {
+ t.Fatal(err)
+ }
+ txn.Rollback()
+ }
+ testURL("postgres://")
+ testURL("postgresql://")
+}
+
+const pgpass_file = "/tmp/pqgotest_pgpass"
+
+func TestPgpass(t *testing.T) {
+ testAssert := func(conninfo string, expected string, reason string) {
+ conn, err := openTestConnConninfo(conninfo)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer conn.Close()
+
+ txn, err := conn.Begin()
+ if err != nil {
+ if expected != "fail" {
+ t.Fatalf(reason, err)
+ }
+ return
+ }
+ rows, err := txn.Query("SELECT USER")
+ if err != nil {
+ txn.Rollback()
+ rows.Close()
+ if expected != "fail" {
+ t.Fatalf(reason, err)
+ }
+ } else {
+ if expected != "ok" {
+ t.Fatalf(reason, err)
+ }
+ }
+ txn.Rollback()
+ }
+ testAssert("", "ok", "missing .pgpass, unexpected error %#v")
+ os.Setenv("PGPASSFILE", pgpass_file)
+ testAssert("host=/tmp", "fail", ", unexpected error %#v")
+ os.Remove(pgpass_file)
+ pgpass, err := os.OpenFile(pgpass_file, os.O_RDWR|os.O_CREATE, 0644)
+ if err != nil {
+ t.Fatalf("Unexpected error writing pgpass file %#v", err)
+ }
+ _, err = pgpass.WriteString(`# comment
+server:5432:some_db:some_user:pass_A
+*:5432:some_db:some_user:pass_B
+localhost:*:*:*:pass_C
+*:*:*:*:pass_fallback
+`)
+ if err != nil {
+ t.Fatalf("Unexpected error writing pgpass file %#v", err)
+ }
+ 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"}
+ for k, v := range extra {
+ (*o)[k] = v
+ }
+ (&conn{}).handlePgpass(*o)
+ if o.Get("password") != expected {
+ t.Fatalf("For %v expected %s got %s", extra, expected, o.Get("password"))
+ }
+ }
+ // wrong permissions for the pgpass file means it should be ignored
+ assertPassword(values{"host": "example.com", "user": "foo"}, "")
+ // fix the permissions and check if it has taken effect
+ os.Chmod(pgpass_file, 0600)
+ assertPassword(values{"host": "server", "dbname": "some_db", "user": "some_user"}, "pass_A")
+ assertPassword(values{"host": "example.com", "user": "foo"}, "pass_fallback")
+ assertPassword(values{"host": "example.com", "dbname": "some_db", "user": "some_user"}, "pass_B")
+ // localhost also matches the default "" and UNIX sockets
+ assertPassword(values{"host": "", "user": "some_user"}, "pass_C")
+ assertPassword(values{"host": "/tmp", "user": "some_user"}, "pass_C")
+ // cleanup
+ os.Remove(pgpass_file)
+ os.Setenv("PGPASSFILE", "")
+}
+
+func TestExec(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ _, err := db.Exec("CREATE TEMP TABLE temp (a int)")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ r, err := db.Exec("INSERT INTO temp VALUES (1)")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if n, _ := r.RowsAffected(); n != 1 {
+ t.Fatalf("expected 1 row affected, not %d", n)
+ }
+
+ r, err = db.Exec("INSERT INTO temp VALUES ($1), ($2), ($3)", 1, 2, 3)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if n, _ := r.RowsAffected(); n != 3 {
+ t.Fatalf("expected 3 rows affected, not %d", n)
+ }
+
+ // SELECT doesn't send the number of returned rows in the command tag
+ // before 9.0
+ if getServerVersion(t, db) >= 90000 {
+ r, err = db.Exec("SELECT g FROM generate_series(1, 2) g")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if n, _ := r.RowsAffected(); n != 2 {
+ t.Fatalf("expected 2 rows affected, not %d", n)
+ }
+
+ r, err = db.Exec("SELECT g FROM generate_series(1, $1) g", 3)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if n, _ := r.RowsAffected(); n != 3 {
+ t.Fatalf("expected 3 rows affected, not %d", n)
+ }
+ }
+}
+
+func TestStatment(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ st, err := db.Prepare("SELECT 1")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ st1, err := db.Prepare("SELECT 2")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ r, err := st.Query()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer r.Close()
+
+ if !r.Next() {
+ t.Fatal("expected row")
+ }
+
+ var i int
+ err = r.Scan(&i)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if i != 1 {
+ t.Fatalf("expected 1, got %d", i)
+ }
+
+ // st1
+
+ r1, err := st1.Query()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer r1.Close()
+
+ if !r1.Next() {
+ if r.Err() != nil {
+ t.Fatal(r1.Err())
+ }
+ t.Fatal("expected row")
+ }
+
+ err = r1.Scan(&i)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if i != 2 {
+ t.Fatalf("expected 2, got %d", i)
+ }
+}
+
+func TestRowsCloseBeforeDone(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ r, err := db.Query("SELECT 1")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = r.Close()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if r.Next() {
+ t.Fatal("unexpected row")
+ }
+
+ if r.Err() != nil {
+ t.Fatal(r.Err())
+ }
+}
+
+func TestParameterCountMismatch(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ var notused int
+ err := db.QueryRow("SELECT false", 1).Scan(&notused)
+ if err == nil {
+ t.Fatal("expected err")
+ }
+ // make sure we clean up correctly
+ err = db.QueryRow("SELECT 1").Scan(&notused)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = db.QueryRow("SELECT $1").Scan(&notused)
+ if err == nil {
+ t.Fatal("expected err")
+ }
+ // make sure we clean up correctly
+ err = db.QueryRow("SELECT 1").Scan(&notused)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+// Test that EmptyQueryResponses are handled correctly.
+func TestEmptyQuery(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ _, err := db.Exec("")
+ if err != nil {
+ t.Fatal(err)
+ }
+ rows, err := db.Query("")
+ if err != nil {
+ t.Fatal(err)
+ }
+ cols, err := rows.Columns()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(cols) != 0 {
+ t.Fatalf("unexpected number of columns %d in response to an empty query", len(cols))
+ }
+ if rows.Next() {
+ t.Fatal("unexpected row")
+ }
+ if rows.Err() != nil {
+ t.Fatal(rows.Err())
+ }
+
+ stmt, err := db.Prepare("")
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, err = stmt.Exec()
+ if err != nil {
+ t.Fatal(err)
+ }
+ rows, err = stmt.Query()
+ if err != nil {
+ t.Fatal(err)
+ }
+ cols, err = rows.Columns()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(cols) != 0 {
+ t.Fatalf("unexpected number of columns %d in response to an empty query", len(cols))
+ }
+ if rows.Next() {
+ t.Fatal("unexpected row")
+ }
+ if rows.Err() != nil {
+ t.Fatal(rows.Err())
+ }
+}
+
+// Test that rows.Columns() is correct even if there are no result rows.
+func TestEmptyResultSetColumns(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ rows, err := db.Query("SELECT 1 AS a, text 'bar' AS bar WHERE FALSE")
+ if err != nil {
+ t.Fatal(err)
+ }
+ cols, err := rows.Columns()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(cols) != 2 {
+ t.Fatalf("unexpected number of columns %d in response to an empty query", len(cols))
+ }
+ if rows.Next() {
+ t.Fatal("unexpected row")
+ }
+ if rows.Err() != nil {
+ t.Fatal(rows.Err())
+ }
+ if cols[0] != "a" || cols[1] != "bar" {
+ t.Fatalf("unexpected Columns result %v", cols)
+ }
+
+ stmt, err := db.Prepare("SELECT $1::int AS a, text 'bar' AS bar WHERE FALSE")
+ if err != nil {
+ t.Fatal(err)
+ }
+ rows, err = stmt.Query(1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ cols, err = rows.Columns()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(cols) != 2 {
+ t.Fatalf("unexpected number of columns %d in response to an empty query", len(cols))
+ }
+ if rows.Next() {
+ t.Fatal("unexpected row")
+ }
+ if rows.Err() != nil {
+ t.Fatal(rows.Err())
+ }
+ if cols[0] != "a" || cols[1] != "bar" {
+ t.Fatalf("unexpected Columns result %v", cols)
+ }
+
+}
+
+func TestEncodeDecode(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ q := `
+ SELECT
+ E'\\000\\001\\002'::bytea,
+ 'foobar'::text,
+ NULL::integer,
+ '2000-1-1 01:02:03.04-7'::timestamptz,
+ 0::boolean,
+ 123,
+ -321,
+ 3.14::float8
+ WHERE
+ E'\\000\\001\\002'::bytea = $1
+ AND 'foobar'::text = $2
+ AND $3::integer is NULL
+ `
+ // AND '2000-1-1 12:00:00.000000-7'::timestamp = $3
+
+ exp1 := []byte{0, 1, 2}
+ exp2 := "foobar"
+
+ r, err := db.Query(q, exp1, exp2, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer r.Close()
+
+ if !r.Next() {
+ if r.Err() != nil {
+ t.Fatal(r.Err())
+ }
+ t.Fatal("expected row")
+ }
+
+ var got1 []byte
+ var got2 string
+ var got3 = sql.NullInt64{Valid: true}
+ var got4 time.Time
+ var got5, got6, got7, got8 interface{}
+
+ err = r.Scan(&got1, &got2, &got3, &got4, &got5, &got6, &got7, &got8)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if !reflect.DeepEqual(exp1, got1) {
+ t.Errorf("expected %q byte: %q", exp1, got1)
+ }
+
+ if !reflect.DeepEqual(exp2, got2) {
+ t.Errorf("expected %q byte: %q", exp2, got2)
+ }
+
+ if got3.Valid {
+ t.Fatal("expected invalid")
+ }
+
+ if got4.Year() != 2000 {
+ t.Fatal("wrong year")
+ }
+
+ if got5 != false {
+ t.Fatalf("expected false, got %q", got5)
+ }
+
+ if got6 != int64(123) {
+ t.Fatalf("expected 123, got %d", got6)
+ }
+
+ if got7 != int64(-321) {
+ t.Fatalf("expected -321, got %d", got7)
+ }
+
+ if got8 != float64(3.14) {
+ t.Fatalf("expected 3.14, got %f", got8)
+ }
+}
+
+func TestNoData(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ st, err := db.Prepare("SELECT 1 WHERE true = false")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer st.Close()
+
+ r, err := st.Query()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer r.Close()
+
+ if r.Next() {
+ if r.Err() != nil {
+ t.Fatal(r.Err())
+ }
+ t.Fatal("unexpected row")
+ }
+
+ _, err = db.Query("SELECT * FROM nonexistenttable WHERE age=$1", 20)
+ if err == nil {
+ t.Fatal("Should have raised an error on non existent table")
+ }
+
+ _, err = db.Query("SELECT * FROM nonexistenttable")
+ if err == nil {
+ t.Fatal("Should have raised an error on non existent table")
+ }
+}
+
+func TestErrorDuringStartup(t *testing.T) {
+ // Don't use the normal connection setup, this is intended to
+ // blow up in the startup packet from a non-existent user.
+ db, err := openTestConnConninfo("user=thisuserreallydoesntexist")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer db.Close()
+
+ _, err = db.Begin()
+ if err == nil {
+ t.Fatal("expected error")
+ }
+
+ e, ok := err.(*Error)
+ if !ok {
+ t.Fatalf("expected Error, got %#v", err)
+ } else if e.Code.Name() != "invalid_authorization_specification" && e.Code.Name() != "invalid_password" {
+ t.Fatalf("expected invalid_authorization_specification or invalid_password, got %s (%+v)", e.Code.Name(), err)
+ }
+}
+
+func TestBadConn(t *testing.T) {
+ var err error
+
+ cn := conn{}
+ func() {
+ defer cn.errRecover(&err)
+ panic(io.EOF)
+ }()
+ if err != driver.ErrBadConn {
+ t.Fatalf("expected driver.ErrBadConn, got: %#v", err)
+ }
+ if !cn.bad {
+ t.Fatalf("expected cn.bad")
+ }
+
+ cn = conn{}
+ func() {
+ defer cn.errRecover(&err)
+ e := &Error{Severity: Efatal}
+ panic(e)
+ }()
+ if err != driver.ErrBadConn {
+ t.Fatalf("expected driver.ErrBadConn, got: %#v", err)
+ }
+ if !cn.bad {
+ t.Fatalf("expected cn.bad")
+ }
+}
+
+func TestErrorOnExec(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ txn, err := db.Begin()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer txn.Rollback()
+
+ _, err = txn.Exec("CREATE TEMPORARY TABLE foo(f1 int PRIMARY KEY)")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = txn.Exec("INSERT INTO foo VALUES (0), (0)")
+ if err == nil {
+ t.Fatal("Should have raised error")
+ }
+
+ e, ok := err.(*Error)
+ if !ok {
+ t.Fatalf("expected Error, got %#v", err)
+ } else if e.Code.Name() != "unique_violation" {
+ t.Fatalf("expected unique_violation, got %s (%+v)", e.Code.Name(), err)
+ }
+}
+
+func TestErrorOnQuery(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ txn, err := db.Begin()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer txn.Rollback()
+
+ _, err = txn.Exec("CREATE TEMPORARY TABLE foo(f1 int PRIMARY KEY)")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = txn.Query("INSERT INTO foo VALUES (0), (0)")
+ if err == nil {
+ t.Fatal("Should have raised error")
+ }
+
+ e, ok := err.(*Error)
+ if !ok {
+ t.Fatalf("expected Error, got %#v", err)
+ } else if e.Code.Name() != "unique_violation" {
+ t.Fatalf("expected unique_violation, got %s (%+v)", e.Code.Name(), err)
+ }
+}
+
+func TestErrorOnQueryRowSimpleQuery(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ txn, err := db.Begin()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer txn.Rollback()
+
+ _, err = txn.Exec("CREATE TEMPORARY TABLE foo(f1 int PRIMARY KEY)")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var v int
+ err = txn.QueryRow("INSERT INTO foo VALUES (0), (0)").Scan(&v)
+ if err == nil {
+ t.Fatal("Should have raised error")
+ }
+
+ e, ok := err.(*Error)
+ if !ok {
+ t.Fatalf("expected Error, got %#v", err)
+ } else if e.Code.Name() != "unique_violation" {
+ t.Fatalf("expected unique_violation, got %s (%+v)", e.Code.Name(), err)
+ }
+}
+
+// Test the QueryRow bug workarounds in stmt.exec() and simpleQuery()
+func TestQueryRowBugWorkaround(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ // stmt.exec()
+ _, err := db.Exec("CREATE TEMP TABLE notnulltemp (a varchar(10) not null)")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var a string
+ err = db.QueryRow("INSERT INTO notnulltemp(a) values($1) RETURNING a", nil).Scan(&a)
+ if err == sql.ErrNoRows {
+ t.Fatalf("expected constraint violation error; got: %v", err)
+ }
+ pge, ok := err.(*Error)
+ if !ok {
+ t.Fatalf("expected *Error; got: %#v", err)
+ }
+ if pge.Code.Name() != "not_null_violation" {
+ t.Fatalf("expected not_null_violation; got: %s (%+v)", pge.Code.Name(), err)
+ }
+
+ // Test workaround in simpleQuery()
+ tx, err := db.Begin()
+ if err != nil {
+ t.Fatalf("unexpected error %s in Begin", err)
+ }
+ defer tx.Rollback()
+
+ _, err = tx.Exec("SET LOCAL check_function_bodies TO FALSE")
+ if err != nil {
+ t.Fatalf("could not disable check_function_bodies: %s", err)
+ }
+ _, err = tx.Exec(`
+CREATE OR REPLACE FUNCTION bad_function()
+RETURNS integer
+-- hack to prevent the function from being inlined
+SET check_function_bodies TO TRUE
+AS $$
+ SELECT text 'bad'
+$$ LANGUAGE sql`)
+ if err != nil {
+ t.Fatalf("could not create function: %s", err)
+ }
+
+ err = tx.QueryRow("SELECT * FROM bad_function()").Scan(&a)
+ if err == nil {
+ t.Fatalf("expected error")
+ }
+ pge, ok = err.(*Error)
+ if !ok {
+ t.Fatalf("expected *Error; got: %#v", err)
+ }
+ if pge.Code.Name() != "invalid_function_definition" {
+ t.Fatalf("expected invalid_function_definition; got: %s (%+v)", pge.Code.Name(), err)
+ }
+
+ err = tx.Rollback()
+ if err != nil {
+ t.Fatalf("unexpected error %s in Rollback", err)
+ }
+
+ // Also test that simpleQuery()'s workaround works when the query fails
+ // after a row has been received.
+ rows, err := db.Query(`
+select
+ (select generate_series(1, ss.i))
+from (select gs.i
+ from generate_series(1, 2) gs(i)
+ order by gs.i limit 2) ss`)
+ if err != nil {
+ t.Fatalf("query failed: %s", err)
+ }
+ if !rows.Next() {
+ t.Fatalf("expected at least one result row; got %s", rows.Err())
+ }
+ var i int
+ err = rows.Scan(&i)
+ if err != nil {
+ t.Fatalf("rows.Scan() failed: %s", err)
+ }
+ if i != 1 {
+ t.Fatalf("unexpected value for i: %d", i)
+ }
+ if rows.Next() {
+ t.Fatalf("unexpected row")
+ }
+ pge, ok = rows.Err().(*Error)
+ if !ok {
+ t.Fatalf("expected *Error; got: %#v", err)
+ }
+ if pge.Code.Name() != "cardinality_violation" {
+ t.Fatalf("expected cardinality_violation; got: %s (%+v)", pge.Code.Name(), rows.Err())
+ }
+}
+
+func TestSimpleQuery(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ r, err := db.Query("select 1")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer r.Close()
+
+ if !r.Next() {
+ t.Fatal("expected row")
+ }
+}
+
+func TestBindError(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ _, err := db.Exec("create temp table test (i integer)")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = db.Query("select * from test where i=$1", "hhh")
+ if err == nil {
+ t.Fatal("expected an error")
+ }
+
+ // Should not get error here
+ r, err := db.Query("select * from test where i=$1", 1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer r.Close()
+}
+
+func TestParseErrorInExtendedQuery(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ rows, err := db.Query("PARSE_ERROR $1", 1)
+ if err == nil {
+ t.Fatal("expected error")
+ }
+
+ rows, err = db.Query("SELECT 1")
+ if err != nil {
+ t.Fatal(err)
+ }
+ rows.Close()
+}
+
+// TestReturning tests that an INSERT query using the RETURNING clause returns a row.
+func TestReturning(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ _, err := db.Exec("CREATE TEMP TABLE distributors (did integer default 0, dname text)")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ rows, err := db.Query("INSERT INTO distributors (did, dname) VALUES (DEFAULT, 'XYZ Widgets') " +
+ "RETURNING did;")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !rows.Next() {
+ t.Fatal("no rows")
+ }
+ var did int
+ err = rows.Scan(&did)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if did != 0 {
+ t.Fatalf("bad value for did: got %d, want %d", did, 0)
+ }
+
+ if rows.Next() {
+ t.Fatal("unexpected next row")
+ }
+ err = rows.Err()
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestIssue186(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ // Exec() a query which returns results
+ _, err := db.Exec("VALUES (1), (2), (3)")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = db.Exec("VALUES ($1), ($2), ($3)", 1, 2, 3)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // Query() a query which doesn't return any results
+ txn, err := db.Begin()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer txn.Rollback()
+
+ rows, err := txn.Query("CREATE TEMP TABLE foo(f1 int)")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err = rows.Close(); err != nil {
+ t.Fatal(err)
+ }
+
+ // small trick to get NoData from a parameterized query
+ _, err = txn.Exec("CREATE RULE nodata AS ON INSERT TO foo DO INSTEAD NOTHING")
+ if err != nil {
+ t.Fatal(err)
+ }
+ rows, err = txn.Query("INSERT INTO foo VALUES ($1)", 1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err = rows.Close(); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestIssue196(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ row := db.QueryRow("SELECT float4 '0.10000122' = $1, float8 '35.03554004971999' = $2",
+ float32(0.10000122), float64(35.03554004971999))
+
+ var float4match, float8match bool
+ err := row.Scan(&float4match, &float8match)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !float4match {
+ t.Errorf("Expected float4 fidelity to be maintained; got no match")
+ }
+ if !float8match {
+ t.Errorf("Expected float8 fidelity to be maintained; got no match")
+ }
+}
+
+// Test that any CommandComplete messages sent before the query results are
+// ignored.
+func TestIssue282(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ var search_path string
+ err := db.QueryRow(`
+ SET LOCAL search_path TO pg_catalog;
+ SET LOCAL search_path TO pg_catalog;
+ SHOW search_path`).Scan(&search_path)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if search_path != "pg_catalog" {
+ t.Fatalf("unexpected search_path %s", search_path)
+ }
+}
+
+func TestReadFloatPrecision(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ row := db.QueryRow("SELECT float4 '0.10000122', float8 '35.03554004971999'")
+ var float4val float32
+ var float8val float64
+ err := row.Scan(&float4val, &float8val)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if float4val != float32(0.10000122) {
+ t.Errorf("Expected float4 fidelity to be maintained; got no match")
+ }
+ if float8val != float64(35.03554004971999) {
+ t.Errorf("Expected float8 fidelity to be maintained; got no match")
+ }
+}
+
+func TestXactMultiStmt(t *testing.T) {
+ // minified test case based on bug reports from
+ // pico303@gmail.com and rangelspam@gmail.com
+ t.Skip("Skipping failing test")
+ db := openTestConn(t)
+ defer db.Close()
+
+ tx, err := db.Begin()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer tx.Commit()
+
+ rows, err := tx.Query("select 1")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if rows.Next() {
+ var val int32
+ if err = rows.Scan(&val); err != nil {
+ t.Fatal(err)
+ }
+ } else {
+ t.Fatal("Expected at least one row in first query in xact")
+ }
+
+ rows2, err := tx.Query("select 2")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if rows2.Next() {
+ var val2 int32
+ if err := rows2.Scan(&val2); err != nil {
+ t.Fatal(err)
+ }
+ } else {
+ t.Fatal("Expected at least one row in second query in xact")
+ }
+
+ if err = rows.Err(); err != nil {
+ t.Fatal(err)
+ }
+
+ if err = rows2.Err(); err != nil {
+ t.Fatal(err)
+ }
+
+ if err = tx.Commit(); err != nil {
+ t.Fatal(err)
+ }
+}
+
+var envParseTests = []struct {
+ Expected map[string]string
+ Env []string
+}{
+ {
+ Env: []string{"PGDATABASE=hello", "PGUSER=goodbye"},
+ Expected: map[string]string{"dbname": "hello", "user": "goodbye"},
+ },
+ {
+ Env: []string{"PGDATESTYLE=ISO, MDY"},
+ Expected: map[string]string{"datestyle": "ISO, MDY"},
+ },
+ {
+ Env: []string{"PGCONNECT_TIMEOUT=30"},
+ Expected: map[string]string{"connect_timeout": "30"},
+ },
+}
+
+func TestParseEnviron(t *testing.T) {
+ for i, tt := range envParseTests {
+ results := parseEnviron(tt.Env)
+ if !reflect.DeepEqual(tt.Expected, results) {
+ t.Errorf("%d: Expected: %#v Got: %#v", i, tt.Expected, results)
+ }
+ }
+}
+
+func TestParseComplete(t *testing.T) {
+ tpc := func(commandTag string, command string, affectedRows int64, shouldFail bool) {
+ defer func() {
+ if p := recover(); p != nil {
+ if !shouldFail {
+ t.Error(p)
+ }
+ }
+ }()
+ cn := &conn{}
+ res, c := cn.parseComplete(commandTag)
+ if c != command {
+ t.Errorf("Expected %v, got %v", command, c)
+ }
+ n, err := res.RowsAffected()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if n != affectedRows {
+ t.Errorf("Expected %d, got %d", affectedRows, n)
+ }
+ }
+
+ tpc("ALTER TABLE", "ALTER TABLE", 0, false)
+ tpc("INSERT 0 1", "INSERT", 1, false)
+ tpc("UPDATE 100", "UPDATE", 100, false)
+ tpc("SELECT 100", "SELECT", 100, false)
+ tpc("FETCH 100", "FETCH", 100, false)
+ // allow COPY (and others) without row count
+ tpc("COPY", "COPY", 0, false)
+ // don't fail on command tags we don't recognize
+ tpc("UNKNOWNCOMMANDTAG", "UNKNOWNCOMMANDTAG", 0, false)
+
+ // failure cases
+ tpc("INSERT 1", "", 0, true) // missing oid
+ tpc("UPDATE 0 1", "", 0, true) // too many numbers
+ tpc("SELECT foo", "", 0, true) // invalid row count
+}
+
+func TestExecerInterface(t *testing.T) {
+ // Gin up a straw man private struct just for the type check
+ cn := &conn{c: nil}
+ var cni interface{} = cn
+
+ _, ok := cni.(driver.Execer)
+ if !ok {
+ t.Fatal("Driver doesn't implement Execer")
+ }
+}
+
+func TestNullAfterNonNull(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ r, err := db.Query("SELECT 9::integer UNION SELECT NULL::integer")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var n sql.NullInt64
+
+ if !r.Next() {
+ if r.Err() != nil {
+ t.Fatal(err)
+ }
+ t.Fatal("expected row")
+ }
+
+ if err := r.Scan(&n); err != nil {
+ t.Fatal(err)
+ }
+
+ if n.Int64 != 9 {
+ t.Fatalf("expected 2, not %d", n.Int64)
+ }
+
+ if !r.Next() {
+ if r.Err() != nil {
+ t.Fatal(err)
+ }
+ t.Fatal("expected row")
+ }
+
+ if err := r.Scan(&n); err != nil {
+ t.Fatal(err)
+ }
+
+ if n.Valid {
+ t.Fatal("expected n to be invalid")
+ }
+
+ if n.Int64 != 0 {
+ t.Fatalf("expected n to 2, not %d", n.Int64)
+ }
+}
+
+func Test64BitErrorChecking(t *testing.T) {
+ defer func() {
+ if err := recover(); err != nil {
+ t.Fatal("panic due to 0xFFFFFFFF != -1 " +
+ "when int is 64 bits")
+ }
+ }()
+
+ db := openTestConn(t)
+ defer db.Close()
+
+ r, err := db.Query(`SELECT *
+FROM (VALUES (0::integer, NULL::text), (1, 'test string')) AS t;`)
+
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ defer r.Close()
+
+ for r.Next() {
+ }
+}
+
+func TestCommit(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ _, err := db.Exec("CREATE TEMP TABLE temp (a int)")
+ if err != nil {
+ t.Fatal(err)
+ }
+ sqlInsert := "INSERT INTO temp VALUES (1)"
+ sqlSelect := "SELECT * FROM temp"
+ tx, err := db.Begin()
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, err = tx.Exec(sqlInsert)
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = tx.Commit()
+ if err != nil {
+ t.Fatal(err)
+ }
+ var i int
+ err = db.QueryRow(sqlSelect).Scan(&i)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if i != 1 {
+ t.Fatalf("expected 1, got %d", i)
+ }
+}
+
+func TestErrorClass(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ _, err := db.Query("SELECT int 'notint'")
+ if err == nil {
+ t.Fatal("expected error")
+ }
+ pge, ok := err.(*Error)
+ if !ok {
+ t.Fatalf("expected *pq.Error, got %#+v", err)
+ }
+ if pge.Code.Class() != "22" {
+ t.Fatalf("expected class 28, got %v", pge.Code.Class())
+ }
+ if pge.Code.Class().Name() != "data_exception" {
+ t.Fatalf("expected data_exception, got %v", pge.Code.Class().Name())
+ }
+}
+
+func TestParseOpts(t *testing.T) {
+ tests := []struct {
+ in string
+ expected values
+ valid bool
+ }{
+ {"dbname=hello user=goodbye", values{"dbname": "hello", "user": "goodbye"}, true},
+ {"dbname=hello user=goodbye ", values{"dbname": "hello", "user": "goodbye"}, true},
+ {"dbname = hello user=goodbye", values{"dbname": "hello", "user": "goodbye"}, true},
+ {"dbname=hello user =goodbye", values{"dbname": "hello", "user": "goodbye"}, true},
+ {"dbname=hello user= goodbye", values{"dbname": "hello", "user": "goodbye"}, true},
+ {"host=localhost password='correct horse battery staple'", values{"host": "localhost", "password": "correct horse battery staple"}, true},
+ {"dbname=データベース password=パスワード", values{"dbname": "データベース", "password": "パスワード"}, true},
+ {"dbname=hello user=''", values{"dbname": "hello", "user": ""}, true},
+ {"user='' dbname=hello", values{"dbname": "hello", "user": ""}, true},
+ // The last option value is an empty string if there's no non-whitespace after its =
+ {"dbname=hello user= ", values{"dbname": "hello", "user": ""}, true},
+
+ // The parser ignores spaces after = and interprets the next set of non-whitespace characters as the value.
+ {"user= password=foo", values{"user": "password=foo"}, true},
+
+ // Backslash escapes next char
+ {`user=a\ \'\\b`, values{"user": `a '\b`}, true},
+ {`user='a \'b'`, values{"user": `a 'b`}, true},
+
+ // Incomplete escape
+ {`user=x\`, values{}, false},
+
+ // No '=' after the key
+ {"postgre://marko@internet", values{}, false},
+ {"dbname user=goodbye", values{}, false},
+ {"user=foo blah", values{}, false},
+ {"user=foo blah ", values{}, false},
+
+ // Unterminated quoted value
+ {"dbname=hello user='unterminated", values{}, false},
+ }
+
+ for _, test := range tests {
+ o := make(values)
+ err := parseOpts(test.in, o)
+
+ switch {
+ case err != nil && test.valid:
+ t.Errorf("%q got unexpected error: %s", test.in, err)
+ case err == nil && test.valid && !reflect.DeepEqual(test.expected, o):
+ t.Errorf("%q got: %#v want: %#v", test.in, o, test.expected)
+ case err == nil && !test.valid:
+ t.Errorf("%q expected an error", test.in)
+ }
+ }
+}
+
+func TestRuntimeParameters(t *testing.T) {
+ type RuntimeTestResult int
+ const (
+ ResultUnknown RuntimeTestResult = iota
+ ResultSuccess
+ ResultError // other error
+ )
+
+ tests := []struct {
+ conninfo string
+ param string
+ expected string
+ expectedOutcome RuntimeTestResult
+ }{
+ // invalid parameter
+ {"DOESNOTEXIST=foo", "", "", ResultError},
+ // we can only work with a specific value for these two
+ {"client_encoding=SQL_ASCII", "", "", ResultError},
+ {"datestyle='ISO, YDM'", "", "", ResultError},
+ // "options" should work exactly as it does in libpq
+ {"options='-c search_path=pqgotest'", "search_path", "pqgotest", ResultSuccess},
+ // pq should override client_encoding in this case
+ {"options='-c client_encoding=SQL_ASCII'", "client_encoding", "UTF8", ResultSuccess},
+ // allow client_encoding to be set explicitly
+ {"client_encoding=UTF8", "client_encoding", "UTF8", ResultSuccess},
+ // test a runtime parameter not supported by libpq
+ {"work_mem='139kB'", "work_mem", "139kB", ResultSuccess},
+ // test fallback_application_name
+ {"application_name=foo fallback_application_name=bar", "application_name", "foo", ResultSuccess},
+ {"application_name='' fallback_application_name=bar", "application_name", "", ResultSuccess},
+ {"fallback_application_name=bar", "application_name", "bar", ResultSuccess},
+ }
+
+ for _, test := range tests {
+ db, err := openTestConnConninfo(test.conninfo)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // application_name didn't exist before 9.0
+ if test.param == "application_name" && getServerVersion(t, db) < 90000 {
+ db.Close()
+ continue
+ }
+
+ tryGetParameterValue := func() (value string, outcome RuntimeTestResult) {
+ defer db.Close()
+ row := db.QueryRow("SELECT current_setting($1)", test.param)
+ err = row.Scan(&value)
+ if err != nil {
+ return "", ResultError
+ }
+ return value, ResultSuccess
+ }
+
+ value, outcome := tryGetParameterValue()
+ if outcome != test.expectedOutcome && outcome == ResultError {
+ t.Fatalf("%v: unexpected error: %v", test.conninfo, err)
+ }
+ if outcome != test.expectedOutcome {
+ t.Fatalf("unexpected outcome %v (was expecting %v) for conninfo \"%s\"",
+ outcome, test.expectedOutcome, test.conninfo)
+ }
+ if value != test.expected {
+ t.Fatalf("bad value for %s: got %s, want %s with conninfo \"%s\"",
+ test.param, value, test.expected, test.conninfo)
+ }
+ }
+}
+
+func TestIsUTF8(t *testing.T) {
+ var cases = []struct {
+ name string
+ want bool
+ }{
+ {"unicode", true},
+ {"utf-8", true},
+ {"utf_8", true},
+ {"UTF-8", true},
+ {"UTF8", true},
+ {"utf8", true},
+ {"u n ic_ode", true},
+ {"ut_f%8", true},
+ {"ubf8", false},
+ {"punycode", false},
+ }
+
+ for _, test := range cases {
+ if g := isUTF8(test.name); g != test.want {
+ t.Errorf("isUTF8(%q) = %v want %v", test.name, g, test.want)
+ }
+ }
+}
+
+func TestQuoteIdentifier(t *testing.T) {
+ var cases = []struct {
+ input string
+ want string
+ }{
+ {`foo`, `"foo"`},
+ {`foo bar baz`, `"foo bar baz"`},
+ {`foo"bar`, `"foo""bar"`},
+ {"foo\x00bar", `"foo"`},
+ {"\x00foo", `""`},
+ }
+
+ for _, test := range cases {
+ got := QuoteIdentifier(test.input)
+ if got != test.want {
+ t.Errorf("QuoteIdentifier(%q) = %v want %v", test.input, got, test.want)
+ }
+ }
+}
diff --git a/vendor/github.com/lib/pq/copy_test.go b/vendor/github.com/lib/pq/copy_test.go
new file mode 100644
index 000000000..86745b38f
--- /dev/null
+++ b/vendor/github.com/lib/pq/copy_test.go
@@ -0,0 +1,465 @@
+package pq
+
+import (
+ "bytes"
+ "database/sql"
+ "database/sql/driver"
+ "strings"
+ "testing"
+)
+
+func TestCopyInStmt(t *testing.T) {
+ var stmt string
+ stmt = CopyIn("table name")
+ if stmt != `COPY "table name" () FROM STDIN` {
+ t.Fatal(stmt)
+ }
+
+ stmt = CopyIn("table name", "column 1", "column 2")
+ if stmt != `COPY "table name" ("column 1", "column 2") FROM STDIN` {
+ t.Fatal(stmt)
+ }
+
+ stmt = CopyIn(`table " name """`, `co"lumn""`)
+ if stmt != `COPY "table "" name """"""" ("co""lumn""""") FROM STDIN` {
+ t.Fatal(stmt)
+ }
+}
+
+func TestCopyInSchemaStmt(t *testing.T) {
+ var stmt string
+ stmt = CopyInSchema("schema name", "table name")
+ if stmt != `COPY "schema name"."table name" () FROM STDIN` {
+ t.Fatal(stmt)
+ }
+
+ stmt = CopyInSchema("schema name", "table name", "column 1", "column 2")
+ if stmt != `COPY "schema name"."table name" ("column 1", "column 2") FROM STDIN` {
+ t.Fatal(stmt)
+ }
+
+ stmt = CopyInSchema(`schema " name """`, `table " name """`, `co"lumn""`)
+ if stmt != `COPY "schema "" name """"""".`+
+ `"table "" name """"""" ("co""lumn""""") FROM STDIN` {
+ t.Fatal(stmt)
+ }
+}
+
+func TestCopyInMultipleValues(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ txn, err := db.Begin()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer txn.Rollback()
+
+ _, err = txn.Exec("CREATE TEMP TABLE temp (a int, b varchar)")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ stmt, err := txn.Prepare(CopyIn("temp", "a", "b"))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ longString := strings.Repeat("#", 500)
+
+ for i := 0; i < 500; i++ {
+ _, err = stmt.Exec(int64(i), longString)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ _, err = stmt.Exec()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = stmt.Close()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var num int
+ err = txn.QueryRow("SELECT COUNT(*) FROM temp").Scan(&num)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if num != 500 {
+ t.Fatalf("expected 500 items, not %d", num)
+ }
+}
+
+func TestCopyInRaiseStmtTrigger(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ if getServerVersion(t, db) < 90000 {
+ var exists int
+ err := db.QueryRow("SELECT 1 FROM pg_language WHERE lanname = 'plpgsql'").Scan(&exists)
+ if err == sql.ErrNoRows {
+ t.Skip("language PL/PgSQL does not exist; skipping TestCopyInRaiseStmtTrigger")
+ } else if err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ txn, err := db.Begin()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer txn.Rollback()
+
+ _, err = txn.Exec("CREATE TEMP TABLE temp (a int, b varchar)")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = txn.Exec(`
+ CREATE OR REPLACE FUNCTION pg_temp.temptest()
+ RETURNS trigger AS
+ $BODY$ begin
+ raise notice 'Hello world';
+ return new;
+ end $BODY$
+ LANGUAGE plpgsql`)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = txn.Exec(`
+ CREATE TRIGGER temptest_trigger
+ BEFORE INSERT
+ ON temp
+ FOR EACH ROW
+ EXECUTE PROCEDURE pg_temp.temptest()`)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ stmt, err := txn.Prepare(CopyIn("temp", "a", "b"))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ longString := strings.Repeat("#", 500)
+
+ _, err = stmt.Exec(int64(1), longString)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = stmt.Exec()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = stmt.Close()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var num int
+ err = txn.QueryRow("SELECT COUNT(*) FROM temp").Scan(&num)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if num != 1 {
+ t.Fatalf("expected 1 items, not %d", num)
+ }
+}
+
+func TestCopyInTypes(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ txn, err := db.Begin()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer txn.Rollback()
+
+ _, err = txn.Exec("CREATE TEMP TABLE temp (num INTEGER, text VARCHAR, blob BYTEA, nothing VARCHAR)")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ stmt, err := txn.Prepare(CopyIn("temp", "num", "text", "blob", "nothing"))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = stmt.Exec(int64(1234567890), "Héllö\n ☃!\r\t\\", []byte{0, 255, 9, 10, 13}, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = stmt.Exec()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = stmt.Close()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var num int
+ var text string
+ var blob []byte
+ var nothing sql.NullString
+
+ err = txn.QueryRow("SELECT * FROM temp").Scan(&num, &text, &blob, &nothing)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if num != 1234567890 {
+ t.Fatal("unexpected result", num)
+ }
+ if text != "Héllö\n ☃!\r\t\\" {
+ t.Fatal("unexpected result", text)
+ }
+ if bytes.Compare(blob, []byte{0, 255, 9, 10, 13}) != 0 {
+ t.Fatal("unexpected result", blob)
+ }
+ if nothing.Valid {
+ t.Fatal("unexpected result", nothing.String)
+ }
+}
+
+func TestCopyInWrongType(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ txn, err := db.Begin()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer txn.Rollback()
+
+ _, err = txn.Exec("CREATE TEMP TABLE temp (num INTEGER)")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ stmt, err := txn.Prepare(CopyIn("temp", "num"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer stmt.Close()
+
+ _, err = stmt.Exec("Héllö\n ☃!\r\t\\")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = stmt.Exec()
+ if err == nil {
+ t.Fatal("expected error")
+ }
+ if pge := err.(*Error); pge.Code.Name() != "invalid_text_representation" {
+ t.Fatalf("expected 'invalid input syntax for integer' error, got %s (%+v)", pge.Code.Name(), pge)
+ }
+}
+
+func TestCopyOutsideOfTxnError(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ _, err := db.Prepare(CopyIn("temp", "num"))
+ if err == nil {
+ t.Fatal("COPY outside of transaction did not return an error")
+ }
+ if err != errCopyNotSupportedOutsideTxn {
+ t.Fatalf("expected %s, got %s", err, err.Error())
+ }
+}
+
+func TestCopyInBinaryError(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ txn, err := db.Begin()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer txn.Rollback()
+
+ _, err = txn.Exec("CREATE TEMP TABLE temp (num INTEGER)")
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, err = txn.Prepare("COPY temp (num) FROM STDIN WITH binary")
+ if err != errBinaryCopyNotSupported {
+ t.Fatalf("expected %s, got %+v", errBinaryCopyNotSupported, err)
+ }
+ // check that the protocol is in a valid state
+ err = txn.Rollback()
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestCopyFromError(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ txn, err := db.Begin()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer txn.Rollback()
+
+ _, err = txn.Exec("CREATE TEMP TABLE temp (num INTEGER)")
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, err = txn.Prepare("COPY temp (num) TO STDOUT")
+ if err != errCopyToNotSupported {
+ t.Fatalf("expected %s, got %+v", errCopyToNotSupported, err)
+ }
+ // check that the protocol is in a valid state
+ err = txn.Rollback()
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestCopySyntaxError(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ txn, err := db.Begin()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer txn.Rollback()
+
+ _, err = txn.Prepare("COPY ")
+ if err == nil {
+ t.Fatal("expected error")
+ }
+ if pge := err.(*Error); pge.Code.Name() != "syntax_error" {
+ t.Fatalf("expected syntax error, got %s (%+v)", pge.Code.Name(), pge)
+ }
+ // check that the protocol is in a valid state
+ err = txn.Rollback()
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+// Tests for connection errors in copyin.resploop()
+func TestCopyRespLoopConnectionError(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ txn, err := db.Begin()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer txn.Rollback()
+
+ var pid int
+ err = txn.QueryRow("SELECT pg_backend_pid()").Scan(&pid)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = txn.Exec("CREATE TEMP TABLE temp (a int)")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ stmt, err := txn.Prepare(CopyIn("temp", "a"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer stmt.Close()
+
+ _, err = db.Exec("SELECT pg_terminate_backend($1)", pid)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if getServerVersion(t, db) < 90500 {
+ // We have to try and send something over, since postgres before
+ // version 9.5 won't process SIGTERMs while it's waiting for
+ // CopyData/CopyEnd messages; see tcop/postgres.c.
+ _, err = stmt.Exec(1)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+ _, err = stmt.Exec()
+ if err == nil {
+ t.Fatalf("expected error")
+ }
+ pge, ok := err.(*Error)
+ if !ok {
+ if err == driver.ErrBadConn {
+ // likely an EPIPE
+ } else {
+ t.Fatalf("expected *pq.Error or driver.ErrBadConn, got %+#v", err)
+ }
+ } else if pge.Code.Name() != "admin_shutdown" {
+ t.Fatalf("expected admin_shutdown, got %s", pge.Code.Name())
+ }
+
+ _ = stmt.Close()
+}
+
+func BenchmarkCopyIn(b *testing.B) {
+ db := openTestConn(b)
+ defer db.Close()
+
+ txn, err := db.Begin()
+ if err != nil {
+ b.Fatal(err)
+ }
+ defer txn.Rollback()
+
+ _, err = txn.Exec("CREATE TEMP TABLE temp (a int, b varchar)")
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ stmt, err := txn.Prepare(CopyIn("temp", "a", "b"))
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ for i := 0; i < b.N; i++ {
+ _, err = stmt.Exec(int64(i), "hello world!")
+ if err != nil {
+ b.Fatal(err)
+ }
+ }
+
+ _, err = stmt.Exec()
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ err = stmt.Close()
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ var num int
+ err = txn.QueryRow("SELECT COUNT(*) FROM temp").Scan(&num)
+ if err != nil {
+ b.Fatal(err)
+ }
+
+ if num != b.N {
+ b.Fatalf("expected %d items, not %d", b.N, num)
+ }
+}
diff --git a/vendor/github.com/lib/pq/encode.go b/vendor/github.com/lib/pq/encode.go
index e52d39b26..6681bd3e7 100644
--- a/vendor/github.com/lib/pq/encode.go
+++ b/vendor/github.com/lib/pq/encode.go
@@ -23,7 +23,6 @@ func binaryEncode(parameterStatus *parameterStatus, x interface{}) []byte {
default:
return encode(parameterStatus, x, oid.T_unknown)
}
- panic("not reached")
}
func encode(parameterStatus *parameterStatus, x interface{}, pgtypOid oid.Oid) []byte {
@@ -76,7 +75,7 @@ func binaryDecode(parameterStatus *parameterStatus, s []byte, typ oid.Oid) inter
return int64(int16(binary.BigEndian.Uint16(s)))
default:
- errorf("don't know how to decode binary parameter of type %u", uint32(typ))
+ errorf("don't know how to decode binary parameter of type %d", uint32(typ))
}
panic("not reached")
diff --git a/vendor/github.com/lib/pq/encode_test.go b/vendor/github.com/lib/pq/encode_test.go
new file mode 100644
index 000000000..984abbc94
--- /dev/null
+++ b/vendor/github.com/lib/pq/encode_test.go
@@ -0,0 +1,727 @@
+package pq
+
+import (
+ "bytes"
+ "database/sql"
+ "fmt"
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/lib/pq/oid"
+)
+
+func TestScanTimestamp(t *testing.T) {
+ var nt NullTime
+ tn := time.Now()
+ nt.Scan(tn)
+ if !nt.Valid {
+ t.Errorf("Expected Valid=false")
+ }
+ if nt.Time != tn {
+ t.Errorf("Time value mismatch")
+ }
+}
+
+func TestScanNilTimestamp(t *testing.T) {
+ var nt NullTime
+ nt.Scan(nil)
+ if nt.Valid {
+ t.Errorf("Expected Valid=false")
+ }
+}
+
+var timeTests = []struct {
+ str string
+ timeval time.Time
+}{
+ {"22001-02-03", time.Date(22001, time.February, 3, 0, 0, 0, 0, time.FixedZone("", 0))},
+ {"2001-02-03", time.Date(2001, time.February, 3, 0, 0, 0, 0, time.FixedZone("", 0))},
+ {"2001-02-03 04:05:06", time.Date(2001, time.February, 3, 4, 5, 6, 0, time.FixedZone("", 0))},
+ {"2001-02-03 04:05:06.000001", time.Date(2001, time.February, 3, 4, 5, 6, 1000, time.FixedZone("", 0))},
+ {"2001-02-03 04:05:06.00001", time.Date(2001, time.February, 3, 4, 5, 6, 10000, time.FixedZone("", 0))},
+ {"2001-02-03 04:05:06.0001", time.Date(2001, time.February, 3, 4, 5, 6, 100000, time.FixedZone("", 0))},
+ {"2001-02-03 04:05:06.001", time.Date(2001, time.February, 3, 4, 5, 6, 1000000, time.FixedZone("", 0))},
+ {"2001-02-03 04:05:06.01", time.Date(2001, time.February, 3, 4, 5, 6, 10000000, time.FixedZone("", 0))},
+ {"2001-02-03 04:05:06.1", time.Date(2001, time.February, 3, 4, 5, 6, 100000000, time.FixedZone("", 0))},
+ {"2001-02-03 04:05:06.12", time.Date(2001, time.February, 3, 4, 5, 6, 120000000, time.FixedZone("", 0))},
+ {"2001-02-03 04:05:06.123", time.Date(2001, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))},
+ {"2001-02-03 04:05:06.1234", time.Date(2001, time.February, 3, 4, 5, 6, 123400000, time.FixedZone("", 0))},
+ {"2001-02-03 04:05:06.12345", time.Date(2001, time.February, 3, 4, 5, 6, 123450000, time.FixedZone("", 0))},
+ {"2001-02-03 04:05:06.123456", time.Date(2001, time.February, 3, 4, 5, 6, 123456000, time.FixedZone("", 0))},
+ {"2001-02-03 04:05:06.123-07", time.Date(2001, time.February, 3, 4, 5, 6, 123000000,
+ time.FixedZone("", -7*60*60))},
+ {"2001-02-03 04:05:06-07", time.Date(2001, time.February, 3, 4, 5, 6, 0,
+ time.FixedZone("", -7*60*60))},
+ {"2001-02-03 04:05:06-07:42", time.Date(2001, time.February, 3, 4, 5, 6, 0,
+ time.FixedZone("", -(7*60*60+42*60)))},
+ {"2001-02-03 04:05:06-07:30:09", time.Date(2001, time.February, 3, 4, 5, 6, 0,
+ time.FixedZone("", -(7*60*60+30*60+9)))},
+ {"2001-02-03 04:05:06+07", time.Date(2001, time.February, 3, 4, 5, 6, 0,
+ time.FixedZone("", 7*60*60))},
+ {"0011-02-03 04:05:06 BC", time.Date(-10, time.February, 3, 4, 5, 6, 0, time.FixedZone("", 0))},
+ {"0011-02-03 04:05:06.123 BC", time.Date(-10, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))},
+ {"0011-02-03 04:05:06.123-07 BC", time.Date(-10, time.February, 3, 4, 5, 6, 123000000,
+ time.FixedZone("", -7*60*60))},
+ {"0001-02-03 04:05:06.123", time.Date(1, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))},
+ {"0001-02-03 04:05:06.123 BC", time.Date(1, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0)).AddDate(-1, 0, 0)},
+ {"0001-02-03 04:05:06.123 BC", time.Date(0, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))},
+ {"0002-02-03 04:05:06.123 BC", time.Date(0, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0)).AddDate(-1, 0, 0)},
+ {"0002-02-03 04:05:06.123 BC", time.Date(-1, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))},
+ {"12345-02-03 04:05:06.1", time.Date(12345, time.February, 3, 4, 5, 6, 100000000, time.FixedZone("", 0))},
+ {"123456-02-03 04:05:06.1", time.Date(123456, time.February, 3, 4, 5, 6, 100000000, time.FixedZone("", 0))},
+}
+
+// Test that parsing the string results in the expected value.
+func TestParseTs(t *testing.T) {
+ for i, tt := range timeTests {
+ val, err := ParseTimestamp(nil, tt.str)
+ if err != nil {
+ t.Errorf("%d: got error: %v", i, err)
+ } else if val.String() != tt.timeval.String() {
+ t.Errorf("%d: expected to parse %q into %q; got %q",
+ i, tt.str, tt.timeval, val)
+ }
+ }
+}
+
+var timeErrorTests = []string{
+ "2001",
+ "2001-2-03",
+ "2001-02-3",
+ "2001-02-03 ",
+ "2001-02-03 04",
+ "2001-02-03 04:",
+ "2001-02-03 04:05",
+ "2001-02-03 04:05:",
+ "2001-02-03 04:05:6",
+ "2001-02-03 04:05:06.123 B",
+}
+
+// Test that parsing the string results in an error.
+func TestParseTsErrors(t *testing.T) {
+ for i, tt := range timeErrorTests {
+ _, err := ParseTimestamp(nil, tt)
+ if err == nil {
+ t.Errorf("%d: expected an error from parsing: %v", i, tt)
+ }
+ }
+}
+
+// Now test that sending the value into the database and parsing it back
+// returns the same time.Time value.
+func TestEncodeAndParseTs(t *testing.T) {
+ db, err := openTestConnConninfo("timezone='Etc/UTC'")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer db.Close()
+
+ for i, tt := range timeTests {
+ var dbstr string
+ err = db.QueryRow("SELECT ($1::timestamptz)::text", tt.timeval).Scan(&dbstr)
+ if err != nil {
+ t.Errorf("%d: could not send value %q to the database: %s", i, tt.timeval, err)
+ continue
+ }
+
+ val, err := ParseTimestamp(nil, dbstr)
+ if err != nil {
+ t.Errorf("%d: could not parse value %q: %s", i, dbstr, err)
+ continue
+ }
+ val = val.In(tt.timeval.Location())
+ if val.String() != tt.timeval.String() {
+ t.Errorf("%d: expected to parse %q into %q; got %q", i, dbstr, tt.timeval, val)
+ }
+ }
+}
+
+var formatTimeTests = []struct {
+ time time.Time
+ expected string
+}{
+ {time.Time{}, "0001-01-01T00:00:00Z"},
+ {time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "2001-02-03T04:05:06.123456789Z"},
+ {time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "2001-02-03T04:05:06.123456789+02:00"},
+ {time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "2001-02-03T04:05:06.123456789-06:00"},
+ {time.Date(2001, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -(7*60*60+30*60+9))), "2001-02-03T04:05:06-07:30:09"},
+
+ {time.Date(1, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "0001-02-03T04:05:06.123456789Z"},
+ {time.Date(1, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "0001-02-03T04:05:06.123456789+02:00"},
+ {time.Date(1, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "0001-02-03T04:05:06.123456789-06:00"},
+
+ {time.Date(0, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "0001-02-03T04:05:06.123456789Z BC"},
+ {time.Date(0, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "0001-02-03T04:05:06.123456789+02:00 BC"},
+ {time.Date(0, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "0001-02-03T04:05:06.123456789-06:00 BC"},
+
+ {time.Date(1, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -(7*60*60+30*60+9))), "0001-02-03T04:05:06-07:30:09"},
+ {time.Date(0, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -(7*60*60+30*60+9))), "0001-02-03T04:05:06-07:30:09 BC"},
+}
+
+func TestFormatTs(t *testing.T) {
+ for i, tt := range formatTimeTests {
+ val := string(formatTs(tt.time))
+ if val != tt.expected {
+ t.Errorf("%d: incorrect time format %q, want %q", i, val, tt.expected)
+ }
+ }
+}
+
+func TestTimestampWithTimeZone(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ tx, err := db.Begin()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer tx.Rollback()
+
+ // try several different locations, all included in Go's zoneinfo.zip
+ for _, locName := range []string{
+ "UTC",
+ "America/Chicago",
+ "America/New_York",
+ "Australia/Darwin",
+ "Australia/Perth",
+ } {
+ loc, err := time.LoadLocation(locName)
+ if err != nil {
+ t.Logf("Could not load time zone %s - skipping", locName)
+ continue
+ }
+
+ // Postgres timestamps have a resolution of 1 microsecond, so don't
+ // use the full range of the Nanosecond argument
+ refTime := time.Date(2012, 11, 6, 10, 23, 42, 123456000, loc)
+
+ for _, pgTimeZone := range []string{"US/Eastern", "Australia/Darwin"} {
+ // Switch Postgres's timezone to test different output timestamp formats
+ _, err = tx.Exec(fmt.Sprintf("set time zone '%s'", pgTimeZone))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var gotTime time.Time
+ row := tx.QueryRow("select $1::timestamp with time zone", refTime)
+ err = row.Scan(&gotTime)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if !refTime.Equal(gotTime) {
+ t.Errorf("timestamps not equal: %s != %s", refTime, gotTime)
+ }
+
+ // check that the time zone is set correctly based on TimeZone
+ pgLoc, err := time.LoadLocation(pgTimeZone)
+ if err != nil {
+ t.Logf("Could not load time zone %s - skipping", pgLoc)
+ continue
+ }
+ translated := refTime.In(pgLoc)
+ if translated.String() != gotTime.String() {
+ t.Errorf("timestamps not equal: %s != %s", translated, gotTime)
+ }
+ }
+ }
+}
+
+func TestTimestampWithOutTimezone(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ test := func(ts, pgts string) {
+ r, err := db.Query("SELECT $1::timestamp", pgts)
+ if err != nil {
+ t.Fatalf("Could not run query: %v", err)
+ }
+
+ n := r.Next()
+
+ if n != true {
+ t.Fatal("Expected at least one row")
+ }
+
+ var result time.Time
+ err = r.Scan(&result)
+ if err != nil {
+ t.Fatalf("Did not expect error scanning row: %v", err)
+ }
+
+ expected, err := time.Parse(time.RFC3339, ts)
+ if err != nil {
+ t.Fatalf("Could not parse test time literal: %v", err)
+ }
+
+ if !result.Equal(expected) {
+ t.Fatalf("Expected time to match %v: got mismatch %v",
+ expected, result)
+ }
+
+ n = r.Next()
+ if n != false {
+ t.Fatal("Expected only one row")
+ }
+ }
+
+ test("2000-01-01T00:00:00Z", "2000-01-01T00:00:00")
+
+ // Test higher precision time
+ test("2013-01-04T20:14:58.80033Z", "2013-01-04 20:14:58.80033")
+}
+
+func TestInfinityTimestamp(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+ var err error
+ var resultT time.Time
+
+ expectedErrorStrPrefix := `sql: Scan error on column index 0: unsupported`
+ type testCases []struct {
+ Query string
+ Param string
+ ExpectedErrStrPrefix string
+ ExpectedVal interface{}
+ }
+ tc := testCases{
+ {"SELECT $1::timestamp", "-infinity", expectedErrorStrPrefix, "-infinity"},
+ {"SELECT $1::timestamptz", "-infinity", expectedErrorStrPrefix, "-infinity"},
+ {"SELECT $1::timestamp", "infinity", expectedErrorStrPrefix, "infinity"},
+ {"SELECT $1::timestamptz", "infinity", expectedErrorStrPrefix, "infinity"},
+ }
+ // try to assert []byte to time.Time
+ for _, q := range tc {
+ err = db.QueryRow(q.Query, q.Param).Scan(&resultT)
+ if !strings.HasPrefix(err.Error(), q.ExpectedErrStrPrefix) {
+ t.Errorf("Scanning -/+infinity, expected error to have prefix %q, got %q", q.ExpectedErrStrPrefix, err)
+ }
+ }
+ // yield []byte
+ for _, q := range tc {
+ var resultI interface{}
+ err = db.QueryRow(q.Query, q.Param).Scan(&resultI)
+ if err != nil {
+ t.Errorf("Scanning -/+infinity, expected no error, got %q", err)
+ }
+ result, ok := resultI.([]byte)
+ if !ok {
+ t.Errorf("Scanning -/+infinity, expected []byte, got %#v", resultI)
+ }
+ if string(result) != q.ExpectedVal {
+ t.Errorf("Scanning -/+infinity, expected %q, got %q", q.ExpectedVal, result)
+ }
+ }
+
+ y1500 := time.Date(1500, time.January, 1, 0, 0, 0, 0, time.UTC)
+ y2500 := time.Date(2500, time.January, 1, 0, 0, 0, 0, time.UTC)
+ EnableInfinityTs(y1500, y2500)
+
+ err = db.QueryRow("SELECT $1::timestamp", "infinity").Scan(&resultT)
+ if err != nil {
+ t.Errorf("Scanning infinity, expected no error, got %q", err)
+ }
+ if !resultT.Equal(y2500) {
+ t.Errorf("Scanning infinity, expected %q, got %q", y2500, resultT)
+ }
+
+ err = db.QueryRow("SELECT $1::timestamptz", "infinity").Scan(&resultT)
+ if err != nil {
+ t.Errorf("Scanning infinity, expected no error, got %q", err)
+ }
+ if !resultT.Equal(y2500) {
+ t.Errorf("Scanning Infinity, expected time %q, got %q", y2500, resultT.String())
+ }
+
+ err = db.QueryRow("SELECT $1::timestamp", "-infinity").Scan(&resultT)
+ if err != nil {
+ t.Errorf("Scanning -infinity, expected no error, got %q", err)
+ }
+ if !resultT.Equal(y1500) {
+ t.Errorf("Scanning -infinity, expected time %q, got %q", y1500, resultT.String())
+ }
+
+ err = db.QueryRow("SELECT $1::timestamptz", "-infinity").Scan(&resultT)
+ if err != nil {
+ t.Errorf("Scanning -infinity, expected no error, got %q", err)
+ }
+ if !resultT.Equal(y1500) {
+ t.Errorf("Scanning -infinity, expected time %q, got %q", y1500, resultT.String())
+ }
+
+ y_1500 := time.Date(-1500, time.January, 1, 0, 0, 0, 0, time.UTC)
+ y11500 := time.Date(11500, time.January, 1, 0, 0, 0, 0, time.UTC)
+ var s string
+ err = db.QueryRow("SELECT $1::timestamp::text", y_1500).Scan(&s)
+ if err != nil {
+ t.Errorf("Encoding -infinity, expected no error, got %q", err)
+ }
+ if s != "-infinity" {
+ t.Errorf("Encoding -infinity, expected %q, got %q", "-infinity", s)
+ }
+ err = db.QueryRow("SELECT $1::timestamptz::text", y_1500).Scan(&s)
+ if err != nil {
+ t.Errorf("Encoding -infinity, expected no error, got %q", err)
+ }
+ if s != "-infinity" {
+ t.Errorf("Encoding -infinity, expected %q, got %q", "-infinity", s)
+ }
+
+ err = db.QueryRow("SELECT $1::timestamp::text", y11500).Scan(&s)
+ if err != nil {
+ t.Errorf("Encoding infinity, expected no error, got %q", err)
+ }
+ if s != "infinity" {
+ t.Errorf("Encoding infinity, expected %q, got %q", "infinity", s)
+ }
+ err = db.QueryRow("SELECT $1::timestamptz::text", y11500).Scan(&s)
+ if err != nil {
+ t.Errorf("Encoding infinity, expected no error, got %q", err)
+ }
+ if s != "infinity" {
+ t.Errorf("Encoding infinity, expected %q, got %q", "infinity", s)
+ }
+
+ disableInfinityTs()
+
+ var panicErrorString string
+ func() {
+ defer func() {
+ panicErrorString, _ = recover().(string)
+ }()
+ EnableInfinityTs(y2500, y1500)
+ }()
+ if panicErrorString != infinityTsNegativeMustBeSmaller {
+ t.Errorf("Expected error, %q, got %q", infinityTsNegativeMustBeSmaller, panicErrorString)
+ }
+}
+
+func TestStringWithNul(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ hello0world := string("hello\x00world")
+ _, err := db.Query("SELECT $1::text", &hello0world)
+ if err == nil {
+ t.Fatal("Postgres accepts a string with nul in it; " +
+ "injection attacks may be plausible")
+ }
+}
+
+func TestByteSliceToText(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ b := []byte("hello world")
+ row := db.QueryRow("SELECT $1::text", b)
+
+ var result []byte
+ err := row.Scan(&result)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if string(result) != string(b) {
+ t.Fatalf("expected %v but got %v", b, result)
+ }
+}
+
+func TestStringToBytea(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ b := "hello world"
+ row := db.QueryRow("SELECT $1::bytea", b)
+
+ var result []byte
+ err := row.Scan(&result)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if !bytes.Equal(result, []byte(b)) {
+ t.Fatalf("expected %v but got %v", b, result)
+ }
+}
+
+func TestTextByteSliceToUUID(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ b := []byte("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11")
+ row := db.QueryRow("SELECT $1::uuid", b)
+
+ var result string
+ err := row.Scan(&result)
+ if forceBinaryParameters() {
+ pqErr := err.(*Error)
+ if pqErr == nil {
+ t.Errorf("Expected to get error")
+ } else if pqErr.Code != "22P03" {
+ t.Fatalf("Expected to get invalid binary encoding error (22P03), got %s", pqErr.Code)
+ }
+ } else {
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if result != string(b) {
+ t.Fatalf("expected %v but got %v", b, result)
+ }
+ }
+}
+
+func TestBinaryByteSlicetoUUID(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ b := []byte{'\xa0', '\xee', '\xbc', '\x99',
+ '\x9c', '\x0b',
+ '\x4e', '\xf8',
+ '\xbb', '\x00', '\x6b',
+ '\xb9', '\xbd', '\x38', '\x0a', '\x11'}
+ row := db.QueryRow("SELECT $1::uuid", b)
+
+ var result string
+ err := row.Scan(&result)
+ if forceBinaryParameters() {
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if result != string("a0eebc99-9c0b-4ef8-bb00-6bb9bd380a11") {
+ t.Fatalf("expected %v but got %v", b, result)
+ }
+ } else {
+ pqErr := err.(*Error)
+ if pqErr == nil {
+ t.Errorf("Expected to get error")
+ } else if pqErr.Code != "22021" {
+ t.Fatalf("Expected to get invalid byte sequence for encoding error (22021), got %s", pqErr.Code)
+ }
+ }
+}
+
+func TestStringToUUID(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ s := "a0eebc99-9c0b-4ef8-bb00-6bb9bd380a11"
+ row := db.QueryRow("SELECT $1::uuid", s)
+
+ var result string
+ err := row.Scan(&result)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if result != s {
+ t.Fatalf("expected %v but got %v", s, result)
+ }
+}
+
+func TestTextByteSliceToInt(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ expected := 12345678
+ b := []byte(fmt.Sprintf("%d", expected))
+ row := db.QueryRow("SELECT $1::int", b)
+
+ var result int
+ err := row.Scan(&result)
+ if forceBinaryParameters() {
+ pqErr := err.(*Error)
+ if pqErr == nil {
+ t.Errorf("Expected to get error")
+ } else if pqErr.Code != "22P03" {
+ t.Fatalf("Expected to get invalid binary encoding error (22P03), got %s", pqErr.Code)
+ }
+ } else {
+ if err != nil {
+ t.Fatal(err)
+ }
+ if result != expected {
+ t.Fatalf("expected %v but got %v", expected, result)
+ }
+ }
+}
+
+func TestBinaryByteSliceToInt(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ expected := 12345678
+ b := []byte{'\x00', '\xbc', '\x61', '\x4e'}
+ row := db.QueryRow("SELECT $1::int", b)
+
+ var result int
+ err := row.Scan(&result)
+ if forceBinaryParameters() {
+ if err != nil {
+ t.Fatal(err)
+ }
+ if result != expected {
+ t.Fatalf("expected %v but got %v", expected, result)
+ }
+ } else {
+ pqErr := err.(*Error)
+ if pqErr == nil {
+ t.Errorf("Expected to get error")
+ } else if pqErr.Code != "22021" {
+ t.Fatalf("Expected to get invalid byte sequence for encoding error (22021), got %s", pqErr.Code)
+ }
+ }
+}
+
+func TestByteaOutputFormatEncoding(t *testing.T) {
+ input := []byte("\\x\x00\x01\x02\xFF\xFEabcdefg0123")
+ want := []byte("\\x5c78000102fffe6162636465666730313233")
+ got := encode(&parameterStatus{serverVersion: 90000}, input, oid.T_bytea)
+ if !bytes.Equal(want, got) {
+ t.Errorf("invalid hex bytea output, got %v but expected %v", got, want)
+ }
+
+ want = []byte("\\\\x\\000\\001\\002\\377\\376abcdefg0123")
+ got = encode(&parameterStatus{serverVersion: 84000}, input, oid.T_bytea)
+ if !bytes.Equal(want, got) {
+ t.Errorf("invalid escape bytea output, got %v but expected %v", got, want)
+ }
+}
+
+func TestByteaOutputFormats(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ if getServerVersion(t, db) < 90000 {
+ // skip
+ return
+ }
+
+ testByteaOutputFormat := func(f string, usePrepared bool) {
+ expectedData := []byte("\x5c\x78\x00\xff\x61\x62\x63\x01\x08")
+ sqlQuery := "SELECT decode('5c7800ff6162630108', 'hex')"
+
+ var data []byte
+
+ // use a txn to avoid relying on getting the same connection
+ txn, err := db.Begin()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer txn.Rollback()
+
+ _, err = txn.Exec("SET LOCAL bytea_output TO " + f)
+ if err != nil {
+ t.Fatal(err)
+ }
+ var rows *sql.Rows
+ var stmt *sql.Stmt
+ if usePrepared {
+ stmt, err = txn.Prepare(sqlQuery)
+ if err != nil {
+ t.Fatal(err)
+ }
+ rows, err = stmt.Query()
+ } else {
+ // use Query; QueryRow would hide the actual error
+ rows, err = txn.Query(sqlQuery)
+ }
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !rows.Next() {
+ if rows.Err() != nil {
+ t.Fatal(rows.Err())
+ }
+ t.Fatal("shouldn't happen")
+ }
+ err = rows.Scan(&data)
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = rows.Close()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if stmt != nil {
+ err = stmt.Close()
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+ if !bytes.Equal(data, expectedData) {
+ t.Errorf("unexpected bytea value %v for format %s; expected %v", data, f, expectedData)
+ }
+ }
+
+ testByteaOutputFormat("hex", false)
+ testByteaOutputFormat("escape", false)
+ testByteaOutputFormat("hex", true)
+ testByteaOutputFormat("escape", true)
+}
+
+func TestAppendEncodedText(t *testing.T) {
+ var buf []byte
+
+ buf = appendEncodedText(&parameterStatus{serverVersion: 90000}, buf, int64(10))
+ buf = append(buf, '\t')
+ buf = appendEncodedText(&parameterStatus{serverVersion: 90000}, buf, 42.0000000001)
+ buf = append(buf, '\t')
+ buf = appendEncodedText(&parameterStatus{serverVersion: 90000}, buf, "hello\tworld")
+ buf = append(buf, '\t')
+ buf = appendEncodedText(&parameterStatus{serverVersion: 90000}, buf, []byte{0, 128, 255})
+
+ if string(buf) != "10\t42.0000000001\thello\\tworld\t\\\\x0080ff" {
+ t.Fatal(string(buf))
+ }
+}
+
+func TestAppendEscapedText(t *testing.T) {
+ if esc := appendEscapedText(nil, "hallo\tescape"); string(esc) != "hallo\\tescape" {
+ t.Fatal(string(esc))
+ }
+ if esc := appendEscapedText(nil, "hallo\\tescape\n"); string(esc) != "hallo\\\\tescape\\n" {
+ t.Fatal(string(esc))
+ }
+ if esc := appendEscapedText(nil, "\n\r\t\f"); string(esc) != "\\n\\r\\t\f" {
+ t.Fatal(string(esc))
+ }
+}
+
+func TestAppendEscapedTextExistingBuffer(t *testing.T) {
+ var buf []byte
+ buf = []byte("123\t")
+ if esc := appendEscapedText(buf, "hallo\tescape"); string(esc) != "123\thallo\\tescape" {
+ t.Fatal(string(esc))
+ }
+ buf = []byte("123\t")
+ if esc := appendEscapedText(buf, "hallo\\tescape\n"); string(esc) != "123\thallo\\\\tescape\\n" {
+ t.Fatal(string(esc))
+ }
+ buf = []byte("123\t")
+ if esc := appendEscapedText(buf, "\n\r\t\f"); string(esc) != "123\t\\n\\r\\t\f" {
+ t.Fatal(string(esc))
+ }
+}
+
+func BenchmarkAppendEscapedText(b *testing.B) {
+ longString := ""
+ for i := 0; i < 100; i++ {
+ longString += "123456789\n"
+ }
+ for i := 0; i < b.N; i++ {
+ appendEscapedText(nil, longString)
+ }
+}
+
+func BenchmarkAppendEscapedTextNoEscape(b *testing.B) {
+ longString := ""
+ for i := 0; i < 100; i++ {
+ longString += "1234567890"
+ }
+ for i := 0; i < b.N; i++ {
+ appendEscapedText(nil, longString)
+ }
+}
diff --git a/vendor/github.com/lib/pq/hstore/hstore.go b/vendor/github.com/lib/pq/hstore/hstore.go
new file mode 100644
index 000000000..72d5abf51
--- /dev/null
+++ b/vendor/github.com/lib/pq/hstore/hstore.go
@@ -0,0 +1,118 @@
+package hstore
+
+import (
+ "database/sql"
+ "database/sql/driver"
+ "strings"
+)
+
+// A wrapper for transferring Hstore values back and forth easily.
+type Hstore struct {
+ Map map[string]sql.NullString
+}
+
+// escapes and quotes hstore keys/values
+// s should be a sql.NullString or string
+func hQuote(s interface{}) string {
+ var str string
+ switch v := s.(type) {
+ case sql.NullString:
+ if !v.Valid {
+ return "NULL"
+ }
+ str = v.String
+ case string:
+ str = v
+ default:
+ panic("not a string or sql.NullString")
+ }
+
+ str = strings.Replace(str, "\\", "\\\\", -1)
+ return `"` + strings.Replace(str, "\"", "\\\"", -1) + `"`
+}
+
+// Scan implements the Scanner interface.
+//
+// Note h.Map is reallocated before the scan to clear existing values. If the
+// hstore column's database value is NULL, then h.Map is set to nil instead.
+func (h *Hstore) Scan(value interface{}) error {
+ if value == nil {
+ h.Map = nil
+ return nil
+ }
+ h.Map = make(map[string]sql.NullString)
+ var b byte
+ pair := [][]byte{{}, {}}
+ pi := 0
+ inQuote := false
+ didQuote := false
+ sawSlash := false
+ bindex := 0
+ for bindex, b = range value.([]byte) {
+ if sawSlash {
+ pair[pi] = append(pair[pi], b)
+ sawSlash = false
+ continue
+ }
+
+ switch b {
+ case '\\':
+ sawSlash = true
+ continue
+ case '"':
+ inQuote = !inQuote
+ if !didQuote {
+ didQuote = true
+ }
+ continue
+ default:
+ if !inQuote {
+ switch b {
+ case ' ', '\t', '\n', '\r':
+ continue
+ case '=':
+ continue
+ case '>':
+ pi = 1
+ didQuote = false
+ continue
+ case ',':
+ s := string(pair[1])
+ if !didQuote && len(s) == 4 && strings.ToLower(s) == "null" {
+ h.Map[string(pair[0])] = sql.NullString{String: "", Valid: false}
+ } else {
+ h.Map[string(pair[0])] = sql.NullString{String: string(pair[1]), Valid: true}
+ }
+ pair[0] = []byte{}
+ pair[1] = []byte{}
+ pi = 0
+ continue
+ }
+ }
+ }
+ pair[pi] = append(pair[pi], b)
+ }
+ if bindex > 0 {
+ s := string(pair[1])
+ if !didQuote && len(s) == 4 && strings.ToLower(s) == "null" {
+ h.Map[string(pair[0])] = sql.NullString{String: "", Valid: false}
+ } else {
+ h.Map[string(pair[0])] = sql.NullString{String: string(pair[1]), Valid: true}
+ }
+ }
+ return nil
+}
+
+// Value implements the driver Valuer interface. Note if h.Map is nil, the
+// database column value will be set to NULL.
+func (h Hstore) Value() (driver.Value, error) {
+ if h.Map == nil {
+ return nil, nil
+ }
+ parts := []string{}
+ for key, val := range h.Map {
+ thispart := hQuote(key) + "=>" + hQuote(val)
+ parts = append(parts, thispart)
+ }
+ return []byte(strings.Join(parts, ",")), nil
+}
diff --git a/vendor/github.com/lib/pq/hstore/hstore_test.go b/vendor/github.com/lib/pq/hstore/hstore_test.go
new file mode 100644
index 000000000..1c9f2bd49
--- /dev/null
+++ b/vendor/github.com/lib/pq/hstore/hstore_test.go
@@ -0,0 +1,148 @@
+package hstore
+
+import (
+ "database/sql"
+ "os"
+ "testing"
+
+ _ "github.com/lib/pq"
+)
+
+type Fatalistic interface {
+ Fatal(args ...interface{})
+}
+
+func openTestConn(t Fatalistic) *sql.DB {
+ datname := os.Getenv("PGDATABASE")
+ sslmode := os.Getenv("PGSSLMODE")
+
+ if datname == "" {
+ os.Setenv("PGDATABASE", "pqgotest")
+ }
+
+ if sslmode == "" {
+ os.Setenv("PGSSLMODE", "disable")
+ }
+
+ conn, err := sql.Open("postgres", "")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ return conn
+}
+
+func TestHstore(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ // quitely create hstore if it doesn't exist
+ _, err := db.Exec("CREATE EXTENSION IF NOT EXISTS hstore")
+ if err != nil {
+ t.Skipf("Skipping hstore tests - hstore extension create failed: %s", err.Error())
+ }
+
+ hs := Hstore{}
+
+ // test for null-valued hstores
+ err = db.QueryRow("SELECT NULL::hstore").Scan(&hs)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if hs.Map != nil {
+ t.Fatalf("expected null map")
+ }
+
+ err = db.QueryRow("SELECT $1::hstore", hs).Scan(&hs)
+ if err != nil {
+ t.Fatalf("re-query null map failed: %s", err.Error())
+ }
+ if hs.Map != nil {
+ t.Fatalf("expected null map")
+ }
+
+ // test for empty hstores
+ err = db.QueryRow("SELECT ''::hstore").Scan(&hs)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if hs.Map == nil {
+ t.Fatalf("expected empty map, got null map")
+ }
+ if len(hs.Map) != 0 {
+ t.Fatalf("expected empty map, got len(map)=%d", len(hs.Map))
+ }
+
+ err = db.QueryRow("SELECT $1::hstore", hs).Scan(&hs)
+ if err != nil {
+ t.Fatalf("re-query empty map failed: %s", err.Error())
+ }
+ if hs.Map == nil {
+ t.Fatalf("expected empty map, got null map")
+ }
+ if len(hs.Map) != 0 {
+ t.Fatalf("expected empty map, got len(map)=%d", len(hs.Map))
+ }
+
+ // a few example maps to test out
+ hsOnePair := Hstore{
+ Map: map[string]sql.NullString{
+ "key1": {String: "value1", Valid: true},
+ },
+ }
+
+ hsThreePairs := Hstore{
+ Map: map[string]sql.NullString{
+ "key1": {String: "value1", Valid: true},
+ "key2": {String: "value2", Valid: true},
+ "key3": {String: "value3", Valid: true},
+ },
+ }
+
+ hsSmorgasbord := Hstore{
+ Map: map[string]sql.NullString{
+ "nullstring": {String: "NULL", Valid: true},
+ "actuallynull": {String: "", Valid: false},
+ "NULL": {String: "NULL string key", Valid: true},
+ "withbracket": {String: "value>42", Valid: true},
+ "withequal": {String: "value=42", Valid: true},
+ `"withquotes1"`: {String: `this "should" be fine`, Valid: true},
+ `"withquotes"2"`: {String: `this "should\" also be fine`, Valid: true},
+ "embedded1": {String: "value1=>x1", Valid: true},
+ "embedded2": {String: `"value2"=>x2`, Valid: true},
+ "withnewlines": {String: "\n\nvalue\t=>2", Valid: true},
+ "<<all sorts of crazy>>": {String: `this, "should,\" also, => be fine`, Valid: true},
+ },
+ }
+
+ // test encoding in query params, then decoding during Scan
+ testBidirectional := func(h Hstore) {
+ err = db.QueryRow("SELECT $1::hstore", h).Scan(&hs)
+ if err != nil {
+ t.Fatalf("re-query %d-pair map failed: %s", len(h.Map), err.Error())
+ }
+ if hs.Map == nil {
+ t.Fatalf("expected %d-pair map, got null map", len(h.Map))
+ }
+ if len(hs.Map) != len(h.Map) {
+ t.Fatalf("expected %d-pair map, got len(map)=%d", len(h.Map), len(hs.Map))
+ }
+
+ for key, val := range hs.Map {
+ otherval, found := h.Map[key]
+ if !found {
+ t.Fatalf(" key '%v' not found in %d-pair map", key, len(h.Map))
+ }
+ if otherval.Valid != val.Valid {
+ t.Fatalf(" value %v <> %v in %d-pair map", otherval, val, len(h.Map))
+ }
+ if otherval.String != val.String {
+ t.Fatalf(" value '%v' <> '%v' in %d-pair map", otherval.String, val.String, len(h.Map))
+ }
+ }
+ }
+
+ testBidirectional(hsOnePair)
+ testBidirectional(hsThreePairs)
+ testBidirectional(hsSmorgasbord)
+}
diff --git a/vendor/github.com/lib/pq/listen_example/doc.go b/vendor/github.com/lib/pq/listen_example/doc.go
new file mode 100644
index 000000000..5bc99f5c1
--- /dev/null
+++ b/vendor/github.com/lib/pq/listen_example/doc.go
@@ -0,0 +1,102 @@
+/*
+
+Below you will find a self-contained Go program which uses the LISTEN / NOTIFY
+mechanism to avoid polling the database while waiting for more work to arrive.
+
+ //
+ // You can see the program in action by defining a function similar to
+ // the following:
+ //
+ // CREATE OR REPLACE FUNCTION public.get_work()
+ // RETURNS bigint
+ // LANGUAGE sql
+ // AS $$
+ // SELECT CASE WHEN random() >= 0.2 THEN int8 '1' END
+ // $$
+ // ;
+
+ package main
+
+ import (
+ "database/sql"
+ "fmt"
+ "time"
+
+ "github.com/lib/pq"
+ )
+
+ func doWork(db *sql.DB, work int64) {
+ // work here
+ }
+
+ func getWork(db *sql.DB) {
+ for {
+ // get work from the database here
+ var work sql.NullInt64
+ err := db.QueryRow("SELECT get_work()").Scan(&work)
+ if err != nil {
+ fmt.Println("call to get_work() failed: ", err)
+ time.Sleep(10 * time.Second)
+ continue
+ }
+ if !work.Valid {
+ // no more work to do
+ fmt.Println("ran out of work")
+ return
+ }
+
+ fmt.Println("starting work on ", work.Int64)
+ go doWork(db, work.Int64)
+ }
+ }
+
+ func waitForNotification(l *pq.Listener) {
+ for {
+ select {
+ case <-l.Notify:
+ fmt.Println("received notification, new work available")
+ return
+ case <-time.After(90 * time.Second):
+ go func() {
+ l.Ping()
+ }()
+ // Check if there's more work available, just in case it takes
+ // a while for the Listener to notice connection loss and
+ // reconnect.
+ fmt.Println("received no work for 90 seconds, checking for new work")
+ return
+ }
+ }
+ }
+
+ func main() {
+ var conninfo string = ""
+
+ db, err := sql.Open("postgres", conninfo)
+ if err != nil {
+ panic(err)
+ }
+
+ reportProblem := func(ev pq.ListenerEventType, err error) {
+ if err != nil {
+ fmt.Println(err.Error())
+ }
+ }
+
+ listener := pq.NewListener(conninfo, 10 * time.Second, time.Minute, reportProblem)
+ err = listener.Listen("getwork")
+ if err != nil {
+ panic(err)
+ }
+
+ fmt.Println("entering main loop")
+ for {
+ // process all available work before waiting for notifications
+ getWork(db)
+ waitForNotification(listener)
+ }
+ }
+
+
+*/
+package listen_example
diff --git a/vendor/github.com/lib/pq/notify.go b/vendor/github.com/lib/pq/notify.go
index 8cad57815..09f94244b 100644
--- a/vendor/github.com/lib/pq/notify.go
+++ b/vendor/github.com/lib/pq/notify.go
@@ -62,14 +62,18 @@ type ListenerConn struct {
// Creates a new ListenerConn. Use NewListener instead.
func NewListenerConn(name string, notificationChan chan<- *Notification) (*ListenerConn, error) {
- cn, err := Open(name)
+ return newDialListenerConn(defaultDialer{}, name, notificationChan)
+}
+
+func newDialListenerConn(d Dialer, name string, c chan<- *Notification) (*ListenerConn, error) {
+ cn, err := DialOpen(d, name)
if err != nil {
return nil, err
}
l := &ListenerConn{
cn: cn.(*conn),
- notificationChan: notificationChan,
+ notificationChan: c,
connState: connStateIdle,
replyChan: make(chan message, 2),
}
@@ -391,6 +395,7 @@ type Listener struct {
name string
minReconnectInterval time.Duration
maxReconnectInterval time.Duration
+ dialer Dialer
eventCallback EventCallbackType
lock sync.Mutex
@@ -421,10 +426,21 @@ func NewListener(name string,
minReconnectInterval time.Duration,
maxReconnectInterval time.Duration,
eventCallback EventCallbackType) *Listener {
+ return NewDialListener(defaultDialer{}, name, minReconnectInterval, maxReconnectInterval, eventCallback)
+}
+
+// NewDialListener is like NewListener but it takes a Dialer.
+func NewDialListener(d Dialer,
+ name string,
+ minReconnectInterval time.Duration,
+ maxReconnectInterval time.Duration,
+ eventCallback EventCallbackType) *Listener {
+
l := &Listener{
name: name,
minReconnectInterval: minReconnectInterval,
maxReconnectInterval: maxReconnectInterval,
+ dialer: d,
eventCallback: eventCallback,
channels: make(map[string]struct{}),
@@ -660,7 +676,7 @@ func (l *Listener) closed() bool {
func (l *Listener) connect() error {
notificationChan := make(chan *Notification, 32)
- cn, err := NewListenerConn(l.name, notificationChan)
+ cn, err := newDialListenerConn(l.dialer, l.name, notificationChan)
if err != nil {
return err
}
diff --git a/vendor/github.com/lib/pq/notify_test.go b/vendor/github.com/lib/pq/notify_test.go
new file mode 100644
index 000000000..fe8941a4e
--- /dev/null
+++ b/vendor/github.com/lib/pq/notify_test.go
@@ -0,0 +1,574 @@
+package pq
+
+import (
+ "errors"
+ "fmt"
+ "io"
+ "os"
+ "runtime"
+ "sync"
+ "sync/atomic"
+ "testing"
+ "time"
+)
+
+var errNilNotification = errors.New("nil notification")
+
+func expectNotification(t *testing.T, ch <-chan *Notification, relname string, extra string) error {
+ select {
+ case n := <-ch:
+ if n == nil {
+ return errNilNotification
+ }
+ if n.Channel != relname || n.Extra != extra {
+ return fmt.Errorf("unexpected notification %v", n)
+ }
+ return nil
+ case <-time.After(1500 * time.Millisecond):
+ return fmt.Errorf("timeout")
+ }
+}
+
+func expectNoNotification(t *testing.T, ch <-chan *Notification) error {
+ select {
+ case n := <-ch:
+ return fmt.Errorf("unexpected notification %v", n)
+ case <-time.After(100 * time.Millisecond):
+ return nil
+ }
+}
+
+func expectEvent(t *testing.T, eventch <-chan ListenerEventType, et ListenerEventType) error {
+ select {
+ case e := <-eventch:
+ if e != et {
+ return fmt.Errorf("unexpected event %v", e)
+ }
+ return nil
+ case <-time.After(1500 * time.Millisecond):
+ panic("expectEvent timeout")
+ }
+}
+
+func expectNoEvent(t *testing.T, eventch <-chan ListenerEventType) error {
+ select {
+ case e := <-eventch:
+ return fmt.Errorf("unexpected event %v", e)
+ case <-time.After(100 * time.Millisecond):
+ return nil
+ }
+}
+
+func newTestListenerConn(t *testing.T) (*ListenerConn, <-chan *Notification) {
+ datname := os.Getenv("PGDATABASE")
+ sslmode := os.Getenv("PGSSLMODE")
+
+ if datname == "" {
+ os.Setenv("PGDATABASE", "pqgotest")
+ }
+
+ if sslmode == "" {
+ os.Setenv("PGSSLMODE", "disable")
+ }
+
+ notificationChan := make(chan *Notification)
+ l, err := NewListenerConn("", notificationChan)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ return l, notificationChan
+}
+
+func TestNewListenerConn(t *testing.T) {
+ l, _ := newTestListenerConn(t)
+
+ defer l.Close()
+}
+
+func TestConnListen(t *testing.T) {
+ l, channel := newTestListenerConn(t)
+
+ defer l.Close()
+
+ db := openTestConn(t)
+ defer db.Close()
+
+ ok, err := l.Listen("notify_test")
+ if !ok || err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = db.Exec("NOTIFY notify_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = expectNotification(t, channel, "notify_test", "")
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestConnUnlisten(t *testing.T) {
+ l, channel := newTestListenerConn(t)
+
+ defer l.Close()
+
+ db := openTestConn(t)
+ defer db.Close()
+
+ ok, err := l.Listen("notify_test")
+ if !ok || err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = db.Exec("NOTIFY notify_test")
+
+ err = expectNotification(t, channel, "notify_test", "")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ ok, err = l.Unlisten("notify_test")
+ if !ok || err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = db.Exec("NOTIFY notify_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = expectNoNotification(t, channel)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestConnUnlistenAll(t *testing.T) {
+ l, channel := newTestListenerConn(t)
+
+ defer l.Close()
+
+ db := openTestConn(t)
+ defer db.Close()
+
+ ok, err := l.Listen("notify_test")
+ if !ok || err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = db.Exec("NOTIFY notify_test")
+
+ err = expectNotification(t, channel, "notify_test", "")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ ok, err = l.UnlistenAll()
+ if !ok || err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = db.Exec("NOTIFY notify_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = expectNoNotification(t, channel)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestConnClose(t *testing.T) {
+ l, _ := newTestListenerConn(t)
+ defer l.Close()
+
+ err := l.Close()
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = l.Close()
+ if err != errListenerConnClosed {
+ t.Fatalf("expected errListenerConnClosed; got %v", err)
+ }
+}
+
+func TestConnPing(t *testing.T) {
+ l, _ := newTestListenerConn(t)
+ defer l.Close()
+ err := l.Ping()
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = l.Close()
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = l.Ping()
+ if err != errListenerConnClosed {
+ t.Fatalf("expected errListenerConnClosed; got %v", err)
+ }
+}
+
+// Test for deadlock where a query fails while another one is queued
+func TestConnExecDeadlock(t *testing.T) {
+ l, _ := newTestListenerConn(t)
+ defer l.Close()
+
+ var wg sync.WaitGroup
+ wg.Add(2)
+
+ go func() {
+ l.ExecSimpleQuery("SELECT pg_sleep(60)")
+ wg.Done()
+ }()
+ runtime.Gosched()
+ go func() {
+ l.ExecSimpleQuery("SELECT 1")
+ wg.Done()
+ }()
+ // give the two goroutines some time to get into position
+ runtime.Gosched()
+ // calls Close on the net.Conn; equivalent to a network failure
+ l.Close()
+
+ var done int32 = 0
+ go func() {
+ time.Sleep(10 * time.Second)
+ if atomic.LoadInt32(&done) != 1 {
+ panic("timed out")
+ }
+ }()
+ wg.Wait()
+ atomic.StoreInt32(&done, 1)
+}
+
+// Test for ListenerConn being closed while a slow query is executing
+func TestListenerConnCloseWhileQueryIsExecuting(t *testing.T) {
+ l, _ := newTestListenerConn(t)
+ defer l.Close()
+
+ var wg sync.WaitGroup
+ wg.Add(1)
+
+ go func() {
+ sent, err := l.ExecSimpleQuery("SELECT pg_sleep(60)")
+ if sent {
+ panic("expected sent=false")
+ }
+ // could be any of a number of errors
+ if err == nil {
+ panic("expected error")
+ }
+ wg.Done()
+ }()
+ // give the above goroutine some time to get into position
+ runtime.Gosched()
+ err := l.Close()
+ if err != nil {
+ t.Fatal(err)
+ }
+ var done int32 = 0
+ go func() {
+ time.Sleep(10 * time.Second)
+ if atomic.LoadInt32(&done) != 1 {
+ panic("timed out")
+ }
+ }()
+ wg.Wait()
+ atomic.StoreInt32(&done, 1)
+}
+
+func TestNotifyExtra(t *testing.T) {
+ db := openTestConn(t)
+ defer db.Close()
+
+ if getServerVersion(t, db) < 90000 {
+ t.Skip("skipping NOTIFY payload test since the server does not appear to support it")
+ }
+
+ l, channel := newTestListenerConn(t)
+ defer l.Close()
+
+ ok, err := l.Listen("notify_test")
+ if !ok || err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = db.Exec("NOTIFY notify_test, 'something'")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = expectNotification(t, channel, "notify_test", "something")
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+// create a new test listener and also set the timeouts
+func newTestListenerTimeout(t *testing.T, min time.Duration, max time.Duration) (*Listener, <-chan ListenerEventType) {
+ datname := os.Getenv("PGDATABASE")
+ sslmode := os.Getenv("PGSSLMODE")
+
+ if datname == "" {
+ os.Setenv("PGDATABASE", "pqgotest")
+ }
+
+ if sslmode == "" {
+ os.Setenv("PGSSLMODE", "disable")
+ }
+
+ eventch := make(chan ListenerEventType, 16)
+ l := NewListener("", min, max, func(t ListenerEventType, err error) { eventch <- t })
+ err := expectEvent(t, eventch, ListenerEventConnected)
+ if err != nil {
+ t.Fatal(err)
+ }
+ return l, eventch
+}
+
+func newTestListener(t *testing.T) (*Listener, <-chan ListenerEventType) {
+ return newTestListenerTimeout(t, time.Hour, time.Hour)
+}
+
+func TestListenerListen(t *testing.T) {
+ l, _ := newTestListener(t)
+ defer l.Close()
+
+ db := openTestConn(t)
+ defer db.Close()
+
+ err := l.Listen("notify_listen_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = db.Exec("NOTIFY notify_listen_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = expectNotification(t, l.Notify, "notify_listen_test", "")
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestListenerUnlisten(t *testing.T) {
+ l, _ := newTestListener(t)
+ defer l.Close()
+
+ db := openTestConn(t)
+ defer db.Close()
+
+ err := l.Listen("notify_listen_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = db.Exec("NOTIFY notify_listen_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = l.Unlisten("notify_listen_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = expectNotification(t, l.Notify, "notify_listen_test", "")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = db.Exec("NOTIFY notify_listen_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = expectNoNotification(t, l.Notify)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestListenerUnlistenAll(t *testing.T) {
+ l, _ := newTestListener(t)
+ defer l.Close()
+
+ db := openTestConn(t)
+ defer db.Close()
+
+ err := l.Listen("notify_listen_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = db.Exec("NOTIFY notify_listen_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = l.UnlistenAll()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = expectNotification(t, l.Notify, "notify_listen_test", "")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = db.Exec("NOTIFY notify_listen_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = expectNoNotification(t, l.Notify)
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestListenerFailedQuery(t *testing.T) {
+ l, eventch := newTestListener(t)
+ defer l.Close()
+
+ db := openTestConn(t)
+ defer db.Close()
+
+ err := l.Listen("notify_listen_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = db.Exec("NOTIFY notify_listen_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = expectNotification(t, l.Notify, "notify_listen_test", "")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // shouldn't cause a disconnect
+ ok, err := l.cn.ExecSimpleQuery("SELECT error")
+ if !ok {
+ t.Fatalf("could not send query to server: %v", err)
+ }
+ _, ok = err.(PGError)
+ if !ok {
+ t.Fatalf("unexpected error %v", err)
+ }
+ err = expectNoEvent(t, eventch)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // should still work
+ _, err = db.Exec("NOTIFY notify_listen_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = expectNotification(t, l.Notify, "notify_listen_test", "")
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestListenerReconnect(t *testing.T) {
+ l, eventch := newTestListenerTimeout(t, 20*time.Millisecond, time.Hour)
+ defer l.Close()
+
+ db := openTestConn(t)
+ defer db.Close()
+
+ err := l.Listen("notify_listen_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ _, err = db.Exec("NOTIFY notify_listen_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = expectNotification(t, l.Notify, "notify_listen_test", "")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // kill the connection and make sure it comes back up
+ ok, err := l.cn.ExecSimpleQuery("SELECT pg_terminate_backend(pg_backend_pid())")
+ if ok {
+ t.Fatalf("could not kill the connection: %v", err)
+ }
+ if err != io.EOF {
+ t.Fatalf("unexpected error %v", err)
+ }
+ err = expectEvent(t, eventch, ListenerEventDisconnected)
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = expectEvent(t, eventch, ListenerEventReconnected)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // should still work
+ _, err = db.Exec("NOTIFY notify_listen_test")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // should get nil after Reconnected
+ err = expectNotification(t, l.Notify, "", "")
+ if err != errNilNotification {
+ t.Fatal(err)
+ }
+
+ err = expectNotification(t, l.Notify, "notify_listen_test", "")
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func TestListenerClose(t *testing.T) {
+ l, _ := newTestListenerTimeout(t, 20*time.Millisecond, time.Hour)
+ defer l.Close()
+
+ err := l.Close()
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = l.Close()
+ if err != errListenerClosed {
+ t.Fatalf("expected errListenerClosed; got %v", err)
+ }
+}
+
+func TestListenerPing(t *testing.T) {
+ l, _ := newTestListenerTimeout(t, 20*time.Millisecond, time.Hour)
+ defer l.Close()
+
+ err := l.Ping()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = l.Close()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = l.Ping()
+ if err != errListenerClosed {
+ t.Fatalf("expected errListenerClosed; got %v", err)
+ }
+}
diff --git a/vendor/github.com/lib/pq/ssl_test.go b/vendor/github.com/lib/pq/ssl_test.go
new file mode 100644
index 000000000..932b336f5
--- /dev/null
+++ b/vendor/github.com/lib/pq/ssl_test.go
@@ -0,0 +1,226 @@
+package pq
+
+// This file contains SSL tests
+
+import (
+ _ "crypto/sha256"
+ "crypto/x509"
+ "database/sql"
+ "fmt"
+ "os"
+ "path/filepath"
+ "testing"
+)
+
+func maybeSkipSSLTests(t *testing.T) {
+ // Require some special variables for testing certificates
+ if os.Getenv("PQSSLCERTTEST_PATH") == "" {
+ t.Skip("PQSSLCERTTEST_PATH not set, skipping SSL tests")
+ }
+
+ value := os.Getenv("PQGOSSLTESTS")
+ if value == "" || value == "0" {
+ t.Skip("PQGOSSLTESTS not enabled, skipping SSL tests")
+ } else if value != "1" {
+ t.Fatalf("unexpected value %q for PQGOSSLTESTS", value)
+ }
+}
+
+func openSSLConn(t *testing.T, conninfo string) (*sql.DB, error) {
+ db, err := openTestConnConninfo(conninfo)
+ if err != nil {
+ // should never fail
+ t.Fatal(err)
+ }
+ // Do something with the connection to see whether it's working or not.
+ tx, err := db.Begin()
+ if err == nil {
+ return db, tx.Rollback()
+ }
+ _ = db.Close()
+ return nil, err
+}
+
+func checkSSLSetup(t *testing.T, conninfo string) {
+ db, err := openSSLConn(t, conninfo)
+ if err == nil {
+ db.Close()
+ t.Fatalf("expected error with conninfo=%q", conninfo)
+ }
+}
+
+// Connect over SSL and run a simple query to test the basics
+func TestSSLConnection(t *testing.T) {
+ maybeSkipSSLTests(t)
+ // Environment sanity check: should fail without SSL
+ checkSSLSetup(t, "sslmode=disable user=pqgossltest")
+
+ db, err := openSSLConn(t, "sslmode=require user=pqgossltest")
+ if err != nil {
+ t.Fatal(err)
+ }
+ rows, err := db.Query("SELECT 1")
+ if err != nil {
+ t.Fatal(err)
+ }
+ rows.Close()
+}
+
+// Test sslmode=verify-full
+func TestSSLVerifyFull(t *testing.T) {
+ maybeSkipSSLTests(t)
+ // Environment sanity check: should fail without SSL
+ checkSSLSetup(t, "sslmode=disable user=pqgossltest")
+
+ // Not OK according to the system CA
+ _, err := openSSLConn(t, "host=postgres sslmode=verify-full user=pqgossltest")
+ if err == nil {
+ t.Fatal("expected error")
+ }
+ _, ok := err.(x509.UnknownAuthorityError)
+ if !ok {
+ t.Fatalf("expected x509.UnknownAuthorityError, got %#+v", err)
+ }
+
+ rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt")
+ rootCert := "sslrootcert=" + rootCertPath + " "
+ // No match on Common Name
+ _, err = openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=verify-full user=pqgossltest")
+ if err == nil {
+ t.Fatal("expected error")
+ }
+ _, ok = err.(x509.HostnameError)
+ if !ok {
+ t.Fatalf("expected x509.HostnameError, got %#+v", err)
+ }
+ // OK
+ _, err = openSSLConn(t, rootCert+"host=postgres sslmode=verify-full user=pqgossltest")
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+// Test sslmode=verify-ca
+func TestSSLVerifyCA(t *testing.T) {
+ maybeSkipSSLTests(t)
+ // Environment sanity check: should fail without SSL
+ checkSSLSetup(t, "sslmode=disable user=pqgossltest")
+
+ // Not OK according to the system CA
+ _, err := openSSLConn(t, "host=postgres sslmode=verify-ca user=pqgossltest")
+ if err == nil {
+ t.Fatal("expected error")
+ }
+ _, ok := err.(x509.UnknownAuthorityError)
+ if !ok {
+ t.Fatalf("expected x509.UnknownAuthorityError, got %#+v", err)
+ }
+
+ rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt")
+ rootCert := "sslrootcert=" + rootCertPath + " "
+ // No match on Common Name, but that's OK
+ _, err = openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=verify-ca user=pqgossltest")
+ if err != nil {
+ t.Fatal(err)
+ }
+ // Everything OK
+ _, err = openSSLConn(t, rootCert+"host=postgres sslmode=verify-ca user=pqgossltest")
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+func getCertConninfo(t *testing.T, source string) string {
+ var sslkey string
+ var sslcert string
+
+ certpath := os.Getenv("PQSSLCERTTEST_PATH")
+
+ switch source {
+ case "missingkey":
+ sslkey = "/tmp/filedoesnotexist"
+ sslcert = filepath.Join(certpath, "postgresql.crt")
+ case "missingcert":
+ sslkey = filepath.Join(certpath, "postgresql.key")
+ sslcert = "/tmp/filedoesnotexist"
+ case "certtwice":
+ sslkey = filepath.Join(certpath, "postgresql.crt")
+ sslcert = filepath.Join(certpath, "postgresql.crt")
+ case "valid":
+ sslkey = filepath.Join(certpath, "postgresql.key")
+ sslcert = filepath.Join(certpath, "postgresql.crt")
+ default:
+ t.Fatalf("invalid source %q", source)
+ }
+ return fmt.Sprintf("sslmode=require user=pqgosslcert sslkey=%s sslcert=%s", sslkey, sslcert)
+}
+
+// Authenticate over SSL using client certificates
+func TestSSLClientCertificates(t *testing.T) {
+ maybeSkipSSLTests(t)
+ // Environment sanity check: should fail without SSL
+ checkSSLSetup(t, "sslmode=disable user=pqgossltest")
+
+ // Should also fail without a valid certificate
+ db, err := openSSLConn(t, "sslmode=require user=pqgosslcert")
+ if err == nil {
+ db.Close()
+ t.Fatal("expected error")
+ }
+ pge, ok := err.(*Error)
+ if !ok {
+ t.Fatal("expected pq.Error")
+ }
+ if pge.Code.Name() != "invalid_authorization_specification" {
+ t.Fatalf("unexpected error code %q", pge.Code.Name())
+ }
+
+ // Should work
+ db, err = openSSLConn(t, getCertConninfo(t, "valid"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ rows, err := db.Query("SELECT 1")
+ if err != nil {
+ t.Fatal(err)
+ }
+ rows.Close()
+}
+
+// Test errors with ssl certificates
+func TestSSLClientCertificatesMissingFiles(t *testing.T) {
+ maybeSkipSSLTests(t)
+ // Environment sanity check: should fail without SSL
+ checkSSLSetup(t, "sslmode=disable user=pqgossltest")
+
+ // Key missing, should fail
+ _, err := openSSLConn(t, getCertConninfo(t, "missingkey"))
+ if err == nil {
+ t.Fatal("expected error")
+ }
+ // should be a PathError
+ _, ok := err.(*os.PathError)
+ if !ok {
+ t.Fatalf("expected PathError, got %#+v", err)
+ }
+
+ // Cert missing, should fail
+ _, err = openSSLConn(t, getCertConninfo(t, "missingcert"))
+ if err == nil {
+ t.Fatal("expected error")
+ }
+ // should be a PathError
+ _, ok = err.(*os.PathError)
+ if !ok {
+ t.Fatalf("expected PathError, got %#+v", err)
+ }
+
+ // Key has wrong permissions, should fail
+ _, err = openSSLConn(t, getCertConninfo(t, "certtwice"))
+ if err == nil {
+ t.Fatal("expected error")
+ }
+ if err != ErrSSLKeyHasWorldPermissions {
+ t.Fatalf("expected ErrSSLKeyHasWorldPermissions, got %#+v", err)
+ }
+}
diff --git a/vendor/github.com/lib/pq/url_test.go b/vendor/github.com/lib/pq/url_test.go
new file mode 100644
index 000000000..4ff0ce034
--- /dev/null
+++ b/vendor/github.com/lib/pq/url_test.go
@@ -0,0 +1,66 @@
+package pq
+
+import (
+ "testing"
+)
+
+func TestSimpleParseURL(t *testing.T) {
+ expected := "host=hostname.remote"
+ str, err := ParseURL("postgres://hostname.remote")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if str != expected {
+ t.Fatalf("unexpected result from ParseURL:\n+ %v\n- %v", str, expected)
+ }
+}
+
+func TestIPv6LoopbackParseURL(t *testing.T) {
+ expected := "host=::1 port=1234"
+ str, err := ParseURL("postgres://[::1]:1234")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if str != expected {
+ t.Fatalf("unexpected result from ParseURL:\n+ %v\n- %v", str, expected)
+ }
+}
+
+func TestFullParseURL(t *testing.T) {
+ expected := `dbname=database host=hostname.remote password=top\ secret port=1234 user=username`
+ str, err := ParseURL("postgres://username:top%20secret@hostname.remote:1234/database")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if str != expected {
+ t.Fatalf("unexpected result from ParseURL:\n+ %s\n- %s", str, expected)
+ }
+}
+
+func TestInvalidProtocolParseURL(t *testing.T) {
+ _, err := ParseURL("http://hostname.remote")
+ switch err {
+ case nil:
+ t.Fatal("Expected an error from parsing invalid protocol")
+ default:
+ msg := "invalid connection protocol: http"
+ if err.Error() != msg {
+ t.Fatalf("Unexpected error message:\n+ %s\n- %s",
+ err.Error(), msg)
+ }
+ }
+}
+
+func TestMinimalURL(t *testing.T) {
+ cs, err := ParseURL("postgres://")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if cs != "" {
+ t.Fatalf("expected blank connection string, got: %q", cs)
+ }
+}