1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-07-19 21:29:42 +02:00

Update PostgreSQL driver library

This commit is contained in:
HarveyKandola 2019-06-25 15:33:51 +01:00
parent f3df43efe0
commit c538fc9eb1
31 changed files with 7381 additions and 162 deletions

2
vendor/github.com/lib/pq/README.md generated vendored
View file

@ -10,7 +10,7 @@
## Docs ## Docs
For detailed documentation and basic usage examples, please see the package For detailed documentation and basic usage examples, please see the package
documentation at <http://godoc.org/github.com/lib/pq>. documentation at <https://godoc.org/github.com/lib/pq>.
## Tests ## Tests

1311
vendor/github.com/lib/pq/array_test.go generated vendored Normal file

File diff suppressed because it is too large Load diff

434
vendor/github.com/lib/pq/bench_test.go generated vendored Normal file
View file

@ -0,0 +1,434 @@
package pq
import (
"bufio"
"bytes"
"context"
"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.(driver.StmtQueryContext).QueryContext(context.Background(), 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.(driver.StmtQueryContext).QueryContext(context.Background(), 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()
}
}

3
vendor/github.com/lib/pq/certs/README generated vendored Normal file
View file

@ -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.

15
vendor/github.com/lib/pq/certs/postgresql.key generated vendored Normal file
View file

@ -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-----

27
vendor/github.com/lib/pq/certs/server.key generated vendored Normal file
View file

@ -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-----

279
vendor/github.com/lib/pq/conn.go generated vendored
View file

@ -2,7 +2,9 @@ package pq
import ( import (
"bufio" "bufio"
"context"
"crypto/md5" "crypto/md5"
"crypto/sha256"
"database/sql" "database/sql"
"database/sql/driver" "database/sql/driver"
"encoding/binary" "encoding/binary"
@ -20,6 +22,7 @@ import (
"unicode" "unicode"
"github.com/lib/pq/oid" "github.com/lib/pq/oid"
"github.com/lib/pq/scram"
) )
// Common error types // Common error types
@ -89,13 +92,24 @@ type Dialer interface {
DialTimeout(network, address string, timeout time.Duration) (net.Conn, error) DialTimeout(network, address string, timeout time.Duration) (net.Conn, error)
} }
type defaultDialer struct{} type DialerContext interface {
DialContext(ctx context.Context, network, address string) (net.Conn, error)
func (d defaultDialer) Dial(ntw, addr string) (net.Conn, error) {
return net.Dial(ntw, addr)
} }
func (d defaultDialer) DialTimeout(ntw, addr string, timeout time.Duration) (net.Conn, error) {
return net.DialTimeout(ntw, addr, timeout) type defaultDialer struct {
d net.Dialer
}
func (d defaultDialer) Dial(network, address string) (net.Conn, error) {
return d.d.Dial(network, address)
}
func (d defaultDialer) DialTimeout(network, address string, timeout time.Duration) (net.Conn, error) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
return d.DialContext(ctx, network, address)
}
func (d defaultDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
return d.d.DialContext(ctx, network, address)
} }
type conn struct { type conn struct {
@ -244,90 +258,35 @@ func (cn *conn) writeBuf(b byte) *writeBuf {
} }
} }
// Open opens a new connection to the database. name is a connection string. // Open opens a new connection to the database. dsn is a connection string.
// Most users should only use it through database/sql package from the standard // Most users should only use it through database/sql package from the standard
// library. // library.
func Open(name string) (_ driver.Conn, err error) { func Open(dsn string) (_ driver.Conn, err error) {
return DialOpen(defaultDialer{}, name) return DialOpen(defaultDialer{}, dsn)
} }
// DialOpen opens a new connection to the database using a dialer. // DialOpen opens a new connection to the database using a dialer.
func DialOpen(d Dialer, name string) (_ driver.Conn, err error) { func DialOpen(d Dialer, dsn string) (_ driver.Conn, err error) {
c, err := NewConnector(dsn)
if err != nil {
return nil, err
}
c.dialer = d
return c.open(context.Background())
}
func (c *Connector) open(ctx context.Context) (cn *conn, err error) {
// Handle any panics during connection initialization. Note that we // Handle any panics during connection initialization. Note that we
// specifically do *not* want to use errRecover(), as that would turn any // specifically do *not* want to use errRecover(), as that would turn any
// connection errors into ErrBadConns, hiding the real error message from // connection errors into ErrBadConns, hiding the real error message from
// the user. // the user.
defer errRecoverNoErrBadConn(&err) defer errRecoverNoErrBadConn(&err)
o := make(values) o := c.opts
// A number of defaults are applied here, in this order: cn = &conn{
//
// * Very low precedence defaults applied in every situation
// * Environment variables
// * Explicitly passed connection information
o["host"] = "localhost"
o["port"] = "5432"
// N.B.: Extra float digits should be set to 3, but that breaks
// Postgres 8.4 and older, where the max is 2.
o["extra_float_digits"] = "2"
for k, v := range parseEnviron(os.Environ()) {
o[k] = v
}
if strings.HasPrefix(name, "postgres://") || strings.HasPrefix(name, "postgresql://") {
name, err = ParseURL(name)
if err != nil {
return nil, err
}
}
if err := parseOpts(name, o); err != nil {
return nil, err
}
// Use the "fallback" application name if necessary
if fallback, ok := o["fallback_application_name"]; ok {
if _, ok := o["application_name"]; !ok {
o["application_name"] = fallback
}
}
// We can't work with any client_encoding other than UTF-8 currently.
// However, we have historically allowed the user to set it to UTF-8
// explicitly, and there's no reason to break such programs, so allow that.
// Note that the "options" setting could also set client_encoding, but
// parsing its value is not worth it. Instead, we always explicitly send
// client_encoding as a separate run-time parameter, which should override
// anything set in options.
if enc, ok := o["client_encoding"]; ok && !isUTF8(enc) {
return nil, errors.New("client_encoding must be absent or 'UTF8'")
}
o["client_encoding"] = "UTF8"
// DateStyle needs a similar treatment.
if datestyle, ok := o["datestyle"]; ok {
if datestyle != "ISO, MDY" {
panic(fmt.Sprintf("setting datestyle must be absent or %v; got %v",
"ISO, MDY", datestyle))
}
} else {
o["datestyle"] = "ISO, MDY"
}
// If a user is not provided by any other means, the last
// resort is to use the current operating system provided user
// name.
if _, ok := o["user"]; !ok {
u, err := userCurrent()
if err != nil {
return nil, err
}
o["user"] = u
}
cn := &conn{
opts: o, opts: o,
dialer: d, dialer: c.dialer,
} }
err = cn.handleDriverSettings(o) err = cn.handleDriverSettings(o)
if err != nil { if err != nil {
@ -335,13 +294,16 @@ func DialOpen(d Dialer, name string) (_ driver.Conn, err error) {
} }
cn.handlePgpass(o) cn.handlePgpass(o)
cn.c, err = dial(d, o) cn.c, err = dial(ctx, c.dialer, o)
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = cn.ssl(o) err = cn.ssl(o)
if err != nil { if err != nil {
if cn.c != nil {
cn.c.Close()
}
return nil, err return nil, err
} }
@ -364,10 +326,10 @@ func DialOpen(d Dialer, name string) (_ driver.Conn, err error) {
return cn, err return cn, err
} }
func dial(d Dialer, o values) (net.Conn, error) { func dial(ctx context.Context, d Dialer, o values) (net.Conn, error) {
ntw, addr := network(o) network, address := network(o)
// SSL is not necessary or supported over UNIX domain sockets // SSL is not necessary or supported over UNIX domain sockets
if ntw == "unix" { if network == "unix" {
o["sslmode"] = "disable" o["sslmode"] = "disable"
} }
@ -378,19 +340,30 @@ func dial(d Dialer, o values) (net.Conn, error) {
return nil, fmt.Errorf("invalid value for parameter connect_timeout: %s", err) return nil, fmt.Errorf("invalid value for parameter connect_timeout: %s", err)
} }
duration := time.Duration(seconds) * time.Second duration := time.Duration(seconds) * time.Second
// connect_timeout should apply to the entire connection establishment // connect_timeout should apply to the entire connection establishment
// procedure, so we both use a timeout for the TCP connection // procedure, so we both use a timeout for the TCP connection
// establishment and set a deadline for doing the initial handshake. // establishment and set a deadline for doing the initial handshake.
// The deadline is then reset after startup() is done. // The deadline is then reset after startup() is done.
deadline := time.Now().Add(duration) deadline := time.Now().Add(duration)
conn, err := d.DialTimeout(ntw, addr, duration) var conn net.Conn
if dctx, ok := d.(DialerContext); ok {
ctx, cancel := context.WithTimeout(ctx, duration)
defer cancel()
conn, err = dctx.DialContext(ctx, network, address)
} else {
conn, err = d.DialTimeout(network, address, duration)
}
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = conn.SetDeadline(deadline) err = conn.SetDeadline(deadline)
return conn, err return conn, err
} }
return d.Dial(ntw, addr) if dctx, ok := d.(DialerContext); ok {
return dctx.DialContext(ctx, network, address)
}
return d.Dial(network, address)
} }
func network(o values) (string, string) { func network(o values) (string, string) {
@ -704,7 +677,7 @@ func (cn *conn) simpleQuery(q string) (res *rows, err error) {
// res might be non-nil here if we received a previous // res might be non-nil here if we received a previous
// CommandComplete, but that's fine; just overwrite it // CommandComplete, but that's fine; just overwrite it
res = &rows{cn: cn} res = &rows{cn: cn}
res.colNames, res.colFmts, res.colTyps = parsePortalRowDescribe(r) res.rowsHeader = parsePortalRowDescribe(r)
// To work around a bug in QueryRow in Go 1.2 and earlier, wait // To work around a bug in QueryRow in Go 1.2 and earlier, wait
// until the first DataRow has been received. // until the first DataRow has been received.
@ -861,7 +834,7 @@ func (cn *conn) query(query string, args []driver.Value) (_ *rows, err error) {
cn.readParseResponse() cn.readParseResponse()
cn.readBindResponse() cn.readBindResponse()
rows := &rows{cn: cn} rows := &rows{cn: cn}
rows.colNames, rows.colFmts, rows.colTyps = cn.readPortalDescribeResponse() rows.rowsHeader = cn.readPortalDescribeResponse()
cn.postExecuteWorkaround() cn.postExecuteWorkaround()
return rows, nil return rows, nil
} }
@ -869,9 +842,7 @@ func (cn *conn) query(query string, args []driver.Value) (_ *rows, err error) {
st.exec(args) st.exec(args)
return &rows{ return &rows{
cn: cn, cn: cn,
colNames: st.colNames, rowsHeader: st.rowsHeader,
colTyps: st.colTyps,
colFmts: st.colFmts,
}, nil }, nil
} }
@ -992,7 +963,6 @@ func (cn *conn) recv() (t byte, r *readBuf) {
if err != nil { if err != nil {
panic(err) panic(err)
} }
switch t { switch t {
case 'E': case 'E':
panic(parseError(r)) panic(parseError(r))
@ -1163,6 +1133,55 @@ func (cn *conn) auth(r *readBuf, o values) {
if r.int32() != 0 { if r.int32() != 0 {
errorf("unexpected authentication response: %q", t) errorf("unexpected authentication response: %q", t)
} }
case 10:
sc := scram.NewClient(sha256.New, o["user"], o["password"])
sc.Step(nil)
if sc.Err() != nil {
errorf("SCRAM-SHA-256 error: %s", sc.Err().Error())
}
scOut := sc.Out()
w := cn.writeBuf('p')
w.string("SCRAM-SHA-256")
w.int32(len(scOut))
w.bytes(scOut)
cn.send(w)
t, r := cn.recv()
if t != 'R' {
errorf("unexpected password response: %q", t)
}
if r.int32() != 11 {
errorf("unexpected authentication response: %q", t)
}
nextStep := r.next(len(*r))
sc.Step(nextStep)
if sc.Err() != nil {
errorf("SCRAM-SHA-256 error: %s", sc.Err().Error())
}
scOut = sc.Out()
w = cn.writeBuf('p')
w.bytes(scOut)
cn.send(w)
t, r = cn.recv()
if t != 'R' {
errorf("unexpected password response: %q", t)
}
if r.int32() != 12 {
errorf("unexpected authentication response: %q", t)
}
nextStep = r.next(len(*r))
sc.Step(nextStep)
if sc.Err() != nil {
errorf("SCRAM-SHA-256 error: %s", sc.Err().Error())
}
default: default:
errorf("unknown authentication response: %d", code) errorf("unknown authentication response: %d", code)
} }
@ -1182,10 +1201,8 @@ var colFmtDataAllText = []byte{0, 0}
type stmt struct { type stmt struct {
cn *conn cn *conn
name string name string
colNames []string rowsHeader
colFmts []format
colFmtData []byte colFmtData []byte
colTyps []fieldDesc
paramTyps []oid.Oid paramTyps []oid.Oid
closed bool closed bool
} }
@ -1232,9 +1249,7 @@ func (st *stmt) Query(v []driver.Value) (r driver.Rows, err error) {
st.exec(v) st.exec(v)
return &rows{ return &rows{
cn: st.cn, cn: st.cn,
colNames: st.colNames, rowsHeader: st.rowsHeader,
colTyps: st.colTyps,
colFmts: st.colFmts,
}, nil }, nil
} }
@ -1344,16 +1359,22 @@ func (cn *conn) parseComplete(commandTag string) (driver.Result, string) {
return driver.RowsAffected(n), commandTag return driver.RowsAffected(n), commandTag
} }
type rows struct { type rowsHeader struct {
cn *conn
finish func()
colNames []string colNames []string
colTyps []fieldDesc colTyps []fieldDesc
colFmts []format colFmts []format
}
type rows struct {
cn *conn
finish func()
rowsHeader
done bool done bool
rb readBuf rb readBuf
result driver.Result result driver.Result
tag string tag string
next *rowsHeader
} }
func (rs *rows) Close() error { func (rs *rows) Close() error {
@ -1440,7 +1461,8 @@ func (rs *rows) Next(dest []driver.Value) (err error) {
} }
return return
case 'T': case 'T':
rs.colNames, rs.colFmts, rs.colTyps = parsePortalRowDescribe(&rs.rb) next := parsePortalRowDescribe(&rs.rb)
rs.next = &next
return io.EOF return io.EOF
default: default:
errorf("unexpected message after execute: %q", t) errorf("unexpected message after execute: %q", t)
@ -1449,10 +1471,16 @@ func (rs *rows) Next(dest []driver.Value) (err error) {
} }
func (rs *rows) HasNextResultSet() bool { func (rs *rows) HasNextResultSet() bool {
return !rs.done hasNext := rs.next != nil && !rs.done
return hasNext
} }
func (rs *rows) NextResultSet() error { func (rs *rows) NextResultSet() error {
if rs.next == nil {
return io.EOF
}
rs.rowsHeader = *rs.next
rs.next = nil
return nil return nil
} }
@ -1475,6 +1503,39 @@ func QuoteIdentifier(name string) string {
return `"` + strings.Replace(name, `"`, `""`, -1) + `"` return `"` + strings.Replace(name, `"`, `""`, -1) + `"`
} }
// QuoteLiteral quotes a 'literal' (e.g. a parameter, often used to pass literal
// to DDL and other statements that do not accept parameters) to be used as part
// of an SQL statement. For example:
//
// exp_date := pq.QuoteLiteral("2023-01-05 15:00:00Z")
// err := db.Exec(fmt.Sprintf("CREATE ROLE my_user VALID UNTIL %s", exp_date))
//
// Any single quotes in name will be escaped. Any backslashes (i.e. "\") will be
// replaced by two backslashes (i.e. "\\") and the C-style escape identifier
// that PostgreSQL provides ('E') will be prepended to the string.
func QuoteLiteral(literal string) string {
// This follows the PostgreSQL internal algorithm for handling quoted literals
// from libpq, which can be found in the "PQEscapeStringInternal" function,
// which is found in the libpq/fe-exec.c source file:
// https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/interfaces/libpq/fe-exec.c
//
// substitute any single-quotes (') with two single-quotes ('')
literal = strings.Replace(literal, `'`, `''`, -1)
// determine if the string has any backslashes (\) in it.
// if it does, replace any backslashes (\) with two backslashes (\\)
// then, we need to wrap the entire string with a PostgreSQL
// C-style escape. Per how "PQEscapeStringInternal" handles this case, we
// also add a space before the "E"
if strings.Contains(literal, `\`) {
literal = strings.Replace(literal, `\`, `\\`, -1)
literal = ` E'` + literal + `'`
} else {
// otherwise, we can just wrap the literal with a pair of single quotes
literal = `'` + literal + `'`
}
return literal
}
func md5s(s string) string { func md5s(s string) string {
h := md5.New() h := md5.New()
h.Write([]byte(s)) h.Write([]byte(s))
@ -1630,13 +1691,13 @@ func (cn *conn) readStatementDescribeResponse() (paramTyps []oid.Oid, colNames [
} }
} }
func (cn *conn) readPortalDescribeResponse() (colNames []string, colFmts []format, colTyps []fieldDesc) { func (cn *conn) readPortalDescribeResponse() rowsHeader {
t, r := cn.recv1() t, r := cn.recv1()
switch t { switch t {
case 'T': case 'T':
return parsePortalRowDescribe(r) return parsePortalRowDescribe(r)
case 'n': case 'n':
return nil, nil, nil return rowsHeader{}
case 'E': case 'E':
err := parseError(r) err := parseError(r)
cn.readReadyForQuery() cn.readReadyForQuery()
@ -1742,11 +1803,11 @@ func parseStatementRowDescribe(r *readBuf) (colNames []string, colTyps []fieldDe
return return
} }
func parsePortalRowDescribe(r *readBuf) (colNames []string, colFmts []format, colTyps []fieldDesc) { func parsePortalRowDescribe(r *readBuf) rowsHeader {
n := r.int16() n := r.int16()
colNames = make([]string, n) colNames := make([]string, n)
colFmts = make([]format, n) colFmts := make([]format, n)
colTyps = make([]fieldDesc, n) colTyps := make([]fieldDesc, n)
for i := range colNames { for i := range colNames {
colNames[i] = r.string() colNames[i] = r.string()
r.next(6) r.next(6)
@ -1755,7 +1816,11 @@ func parsePortalRowDescribe(r *readBuf) (colNames []string, colFmts []format, co
colTyps[i].Mod = r.int32() colTyps[i].Mod = r.int32()
colFmts[i] = format(r.int16()) colFmts[i] = format(r.int16())
} }
return return rowsHeader{
colNames: colNames,
colFmts: colFmts,
colTyps: colTyps,
}
} }
// parseEnviron tries to mimic some of libpq's environment handling // parseEnviron tries to mimic some of libpq's environment handling

View file

@ -1,5 +1,3 @@
// +build go1.8
package pq package pq
import ( import (
@ -9,6 +7,7 @@ import (
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"time"
) )
// Implement the "QueryerContext" interface // Implement the "QueryerContext" interface
@ -76,13 +75,32 @@ func (cn *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx,
return tx, nil return tx, nil
} }
func (cn *conn) Ping(ctx context.Context) error {
if finish := cn.watchCancel(ctx); finish != nil {
defer finish()
}
rows, err := cn.simpleQuery("SELECT 'lib/pq ping test';")
if err != nil {
return driver.ErrBadConn // https://golang.org/pkg/database/sql/driver/#Pinger
}
rows.Close()
return nil
}
func (cn *conn) watchCancel(ctx context.Context) func() { func (cn *conn) watchCancel(ctx context.Context) func() {
if done := ctx.Done(); done != nil { if done := ctx.Done(); done != nil {
finished := make(chan struct{}) finished := make(chan struct{})
go func() { go func() {
select { select {
case <-done: case <-done:
_ = cn.cancel() // At this point the function level context is canceled,
// so it must not be used for the additional network
// request to cancel the query.
// Create a new context to pass into the dial.
ctxCancel, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
_ = cn.cancel(ctxCancel)
finished <- struct{}{} finished <- struct{}{}
case <-finished: case <-finished:
} }
@ -97,8 +115,8 @@ func (cn *conn) watchCancel(ctx context.Context) func() {
return nil return nil
} }
func (cn *conn) cancel() error { func (cn *conn) cancel(ctx context.Context) error {
c, err := dial(cn.dialer, cn.opts) c, err := dial(ctx, cn.dialer, cn.opts)
if err != nil { if err != nil {
return err return err
} }

1741
vendor/github.com/lib/pq/conn_test.go generated vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,10 +1,12 @@
// +build go1.10
package pq package pq
import ( import (
"context" "context"
"database/sql/driver" "database/sql/driver"
"errors"
"fmt"
"os"
"strings"
) )
// Connector represents a fixed configuration for the pq driver with a given // Connector represents a fixed configuration for the pq driver with a given
@ -14,30 +16,95 @@ import (
// //
// See https://golang.org/pkg/database/sql/driver/#Connector. // See https://golang.org/pkg/database/sql/driver/#Connector.
// See https://golang.org/pkg/database/sql/#OpenDB. // See https://golang.org/pkg/database/sql/#OpenDB.
type connector struct { type Connector struct {
name string opts values
dialer Dialer
} }
// Connect returns a connection to the database using the fixed configuration // Connect returns a connection to the database using the fixed configuration
// of this Connector. Context is not used. // of this Connector. Context is not used.
func (c *connector) Connect(_ context.Context) (driver.Conn, error) { func (c *Connector) Connect(ctx context.Context) (driver.Conn, error) {
return (&Driver{}).Open(c.name) return c.open(ctx)
} }
// Driver returnst the underlying driver of this Connector. // Driver returnst the underlying driver of this Connector.
func (c *connector) Driver() driver.Driver { func (c *Connector) Driver() driver.Driver {
return &Driver{} return &Driver{}
} }
var _ driver.Connector = &connector{}
// NewConnector returns a connector for the pq driver in a fixed configuration // NewConnector returns a connector for the pq driver in a fixed configuration
// with the given name. The returned connector can be used to create any number // with the given dsn. The returned connector can be used to create any number
// of equivalent Conn's. The returned connector is intended to be used with // of equivalent Conn's. The returned connector is intended to be used with
// database/sql.OpenDB. // database/sql.OpenDB.
// //
// See https://golang.org/pkg/database/sql/driver/#Connector. // See https://golang.org/pkg/database/sql/driver/#Connector.
// See https://golang.org/pkg/database/sql/#OpenDB. // See https://golang.org/pkg/database/sql/#OpenDB.
func NewConnector(name string) (driver.Connector, error) { func NewConnector(dsn string) (*Connector, error) {
return &connector{name: name}, nil var err error
o := make(values)
// A number of defaults are applied here, in this order:
//
// * Very low precedence defaults applied in every situation
// * Environment variables
// * Explicitly passed connection information
o["host"] = "localhost"
o["port"] = "5432"
// N.B.: Extra float digits should be set to 3, but that breaks
// Postgres 8.4 and older, where the max is 2.
o["extra_float_digits"] = "2"
for k, v := range parseEnviron(os.Environ()) {
o[k] = v
}
if strings.HasPrefix(dsn, "postgres://") || strings.HasPrefix(dsn, "postgresql://") {
dsn, err = ParseURL(dsn)
if err != nil {
return nil, err
}
}
if err := parseOpts(dsn, o); err != nil {
return nil, err
}
// Use the "fallback" application name if necessary
if fallback, ok := o["fallback_application_name"]; ok {
if _, ok := o["application_name"]; !ok {
o["application_name"] = fallback
}
}
// We can't work with any client_encoding other than UTF-8 currently.
// However, we have historically allowed the user to set it to UTF-8
// explicitly, and there's no reason to break such programs, so allow that.
// Note that the "options" setting could also set client_encoding, but
// parsing its value is not worth it. Instead, we always explicitly send
// client_encoding as a separate run-time parameter, which should override
// anything set in options.
if enc, ok := o["client_encoding"]; ok && !isUTF8(enc) {
return nil, errors.New("client_encoding must be absent or 'UTF8'")
}
o["client_encoding"] = "UTF8"
// DateStyle needs a similar treatment.
if datestyle, ok := o["datestyle"]; ok {
if datestyle != "ISO, MDY" {
return nil, fmt.Errorf("setting datestyle must be absent or %v; got %v", "ISO, MDY", datestyle)
}
} else {
o["datestyle"] = "ISO, MDY"
}
// If a user is not provided by any other means, the last
// resort is to use the current operating system provided user
// name.
if _, ok := o["user"]; !ok {
u, err := userCurrent()
if err != nil {
return nil, err
}
o["user"] = u
}
return &Connector{opts: o, dialer: defaultDialer{}}, nil
} }

33
vendor/github.com/lib/pq/connector_example_test.go generated vendored Normal file
View file

@ -0,0 +1,33 @@
// +build go1.10
package pq_test
import (
"database/sql"
"fmt"
"github.com/lib/pq"
)
func ExampleNewConnector() {
name := ""
connector, err := pq.NewConnector(name)
if err != nil {
fmt.Println(err)
return
}
db := sql.OpenDB(connector)
if err != nil {
fmt.Println(err)
return
}
defer db.Close()
// Use the DB
txn, err := db.Begin()
if err != nil {
fmt.Println(err)
return
}
txn.Rollback()
}

67
vendor/github.com/lib/pq/connector_test.go generated vendored Normal file
View file

@ -0,0 +1,67 @@
// +build go1.10
package pq
import (
"context"
"database/sql"
"database/sql/driver"
"testing"
)
func TestNewConnector_WorksWithOpenDB(t *testing.T) {
name := ""
c, err := NewConnector(name)
if err != nil {
t.Fatal(err)
}
db := sql.OpenDB(c)
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()
}
func TestNewConnector_Connect(t *testing.T) {
name := ""
c, err := NewConnector(name)
if err != nil {
t.Fatal(err)
}
db, err := c.Connect(context.Background())
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.(driver.ConnBeginTx).BeginTx(context.Background(), driver.TxOptions{})
if err != nil {
t.Fatal(err)
}
txn.Rollback()
}
func TestNewConnector_Driver(t *testing.T) {
name := ""
c, err := NewConnector(name)
if err != nil {
t.Fatal(err)
}
db, err := c.Driver().Open(name)
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.(driver.ConnBeginTx).BeginTx(context.Background(), driver.TxOptions{})
if err != nil {
t.Fatal(err)
}
txn.Rollback()
}

468
vendor/github.com/lib/pq/copy_test.go generated vendored Normal file
View file

@ -0,0 +1,468 @@
package pq
import (
"bytes"
"database/sql"
"database/sql/driver"
"net"
"strings"
"testing"
)
func TestCopyInStmt(t *testing.T) {
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) {
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.Equal(blob, []byte{0, 255, 9, 10, 13}) {
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")
}
switch pge := err.(type) {
case *Error:
if pge.Code.Name() != "admin_shutdown" {
t.Fatalf("expected admin_shutdown, got %s", pge.Code.Name())
}
case *net.OpError:
// ignore
default:
if err == driver.ErrBadConn {
// likely an EPIPE
} else {
t.Fatalf("unexpected error, got %+#v", err)
}
}
_ = 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)
}
}

2
vendor/github.com/lib/pq/doc.go generated vendored
View file

@ -239,7 +239,7 @@ for more information). Note that the channel name will be truncated to 63
bytes by the PostgreSQL server. bytes by the PostgreSQL server.
You can find a complete, working example of Listener usage at You can find a complete, working example of Listener usage at
http://godoc.org/github.com/lib/pq/example/listen. https://godoc.org/github.com/lib/pq/example/listen.
*/ */
package pq package pq

9
vendor/github.com/lib/pq/encode.go generated vendored
View file

@ -117,11 +117,10 @@ func textDecode(parameterStatus *parameterStatus, s []byte, typ oid.Oid) interfa
} }
return i return i
case oid.T_float4, oid.T_float8: case oid.T_float4, oid.T_float8:
bits := 64 // We always use 64 bit parsing, regardless of whether the input text is for
if typ == oid.T_float4 { // a float4 or float8, because clients expect float64s for all float datatypes
bits = 32 // and returning a 32-bit parsed float64 produces lossy results.
} f, err := strconv.ParseFloat(string(s), 64)
f, err := strconv.ParseFloat(string(s), bits)
if err != nil { if err != nil {
errorf("%s", err) errorf("%s", err)
} }

766
vendor/github.com/lib/pq/encode_test.go generated vendored Normal file
View file

@ -0,0 +1,766 @@
package pq
import (
"bytes"
"database/sql"
"fmt"
"regexp"
"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))},
{"0001-12-31 BC", time.Date(0, time.December, 31, 0, 0, 0, 0, time.FixedZone("", 0))},
{"2001-02-03 BC", time.Date(-2000, 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{
"BC",
" BC",
"2001",
"2001-2-03",
"2001-02-3",
"2001-02-03 ",
"2001-02-03 B",
"2001-02-03 04",
"2001-02-03 04:",
"2001-02-03 04:05",
"2001-02-03 04:05 B",
"2001-02-03 04:05 BC",
"2001-02-03 04:05:",
"2001-02-03 04:05:6",
"2001-02-03 04:05:06 B",
"2001-02-03 04:05:06BC",
"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-01 00:00:00Z"},
{time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "2001-02-03 04:05:06.123456789Z"},
{time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "2001-02-03 04:05:06.123456789+02:00"},
{time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "2001-02-03 04: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-03 04:05:06-07:30:09"},
{time.Date(1, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "0001-02-03 04:05:06.123456789Z"},
{time.Date(1, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "0001-02-03 04:05:06.123456789+02:00"},
{time.Date(1, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "0001-02-03 04:05:06.123456789-06:00"},
{time.Date(0, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "0001-02-03 04:05:06.123456789Z BC"},
{time.Date(0, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "0001-02-03 04:05:06.123456789+02:00 BC"},
{time.Date(0, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "0001-02-03 04: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-03 04: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-03 04: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 TestFormatTsBackend(t *testing.T) {
db := openTestConn(t)
defer db.Close()
var str string
err := db.QueryRow("SELECT '2001-02-03T04:05:06.007-08:09:10'::time::text").Scan(&str)
if err == nil {
t.Fatalf("PostgreSQL is accepting an ISO timestamp input for time")
}
for i, tt := range formatTimeTests {
for _, typ := range []string{"date", "time", "timetz", "timestamp", "timestamptz"} {
err = db.QueryRow("SELECT $1::"+typ+"::text", tt.time).Scan(&str)
if err != nil {
t.Errorf("%d: incorrect time format for %v on the backend: %v", i, typ, err)
}
}
}
}
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)
}
if !r.Next() {
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)
}
if r.Next() {
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
expectedErrorStrRegexp := regexp.MustCompile(
`^sql: Scan error on column index 0(, name "timestamp(tz)?"|): unsupported`)
type testCases []struct {
Query string
Param string
ExpectedErrorStrRegexp *regexp.Regexp
ExpectedVal interface{}
}
tc := testCases{
{"SELECT $1::timestamp", "-infinity", expectedErrorStrRegexp, "-infinity"},
{"SELECT $1::timestamptz", "-infinity", expectedErrorStrRegexp, "-infinity"},
{"SELECT $1::timestamp", "infinity", expectedErrorStrRegexp, "infinity"},
{"SELECT $1::timestamptz", "infinity", expectedErrorStrRegexp, "infinity"},
}
// try to assert []byte to time.Time
for _, q := range tc {
err = db.QueryRow(q.Query, q.Param).Scan(&resultT)
if !q.ExpectedErrorStrRegexp.MatchString(err.Error()) {
t.Errorf("Scanning -/+infinity, expected error to match regexp %q, got %q",
q.ExpectedErrorStrRegexp, 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())
}
ym1500 := 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", ym1500).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", ym1500).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 TestTextDecodeIntoString(t *testing.T) {
input := []byte("hello world")
want := string(input)
for _, typ := range []oid.Oid{oid.T_char, oid.T_varchar, oid.T_text} {
got := decode(&parameterStatus{}, input, typ, formatText)
if got != want {
t.Errorf("invalid string decoding output for %T(%+v), got %v but expected %v", typ, typ, got, want)
}
}
}
func TestByteaOutputFormatEncoding(t *testing.T) {
input := []byte("\\x\x00\x01\x02\xFF\xFEabcdefg0123")
want := []byte("\\x5c78000102fffe6162636465666730313233")
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) {
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)
}
}

98
vendor/github.com/lib/pq/example/listen/doc.go generated vendored Normal file
View file

@ -0,0 +1,98 @@
/*
Package listen is 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) {
select {
case <-l.Notify:
fmt.Println("received notification, new work available")
case <-time.After(90 * time.Second):
go 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")
}
}
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())
}
}
minReconn := 10 * time.Second
maxReconn := time.Minute
listener := pq.NewListener(conninfo, minReconn, maxReconn, 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

319
vendor/github.com/lib/pq/go18_test.go generated vendored Normal file
View file

@ -0,0 +1,319 @@
package pq
import (
"context"
"database/sql"
"runtime"
"strings"
"testing"
"time"
)
func TestMultipleSimpleQuery(t *testing.T) {
db := openTestConn(t)
defer db.Close()
rows, err := db.Query("select 1; set time zone default; select 2; select 3")
if err != nil {
t.Fatal(err)
}
defer rows.Close()
var i int
for rows.Next() {
if err := rows.Scan(&i); err != nil {
t.Fatal(err)
}
if i != 1 {
t.Fatalf("expected 1, got %d", i)
}
}
if !rows.NextResultSet() {
t.Fatal("expected more result sets", rows.Err())
}
for rows.Next() {
if err := rows.Scan(&i); err != nil {
t.Fatal(err)
}
if i != 2 {
t.Fatalf("expected 2, got %d", i)
}
}
// Make sure that if we ignore a result we can still query.
rows, err = db.Query("select 4; select 5")
if err != nil {
t.Fatal(err)
}
defer rows.Close()
for rows.Next() {
if err := rows.Scan(&i); err != nil {
t.Fatal(err)
}
if i != 4 {
t.Fatalf("expected 4, got %d", i)
}
}
if !rows.NextResultSet() {
t.Fatal("expected more result sets", rows.Err())
}
for rows.Next() {
if err := rows.Scan(&i); err != nil {
t.Fatal(err)
}
if i != 5 {
t.Fatalf("expected 5, got %d", i)
}
}
if rows.NextResultSet() {
t.Fatal("unexpected result set")
}
}
const contextRaceIterations = 100
func TestContextCancelExec(t *testing.T) {
db := openTestConn(t)
defer db.Close()
ctx, cancel := context.WithCancel(context.Background())
// Delay execution for just a bit until db.ExecContext has begun.
defer time.AfterFunc(time.Millisecond*10, cancel).Stop()
// Not canceled until after the exec has started.
if _, err := db.ExecContext(ctx, "select pg_sleep(1)"); err == nil {
t.Fatal("expected error")
} else if err.Error() != "pq: canceling statement due to user request" {
t.Fatalf("unexpected error: %s", err)
}
// Context is already canceled, so error should come before execution.
if _, err := db.ExecContext(ctx, "select pg_sleep(1)"); err == nil {
t.Fatal("expected error")
} else if err.Error() != "context canceled" {
t.Fatalf("unexpected error: %s", err)
}
for i := 0; i < contextRaceIterations; i++ {
func() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
if _, err := db.ExecContext(ctx, "select 1"); err != nil {
t.Fatal(err)
}
}()
if _, err := db.Exec("select 1"); err != nil {
t.Fatal(err)
}
}
}
func TestContextCancelQuery(t *testing.T) {
db := openTestConn(t)
defer db.Close()
ctx, cancel := context.WithCancel(context.Background())
// Delay execution for just a bit until db.QueryContext has begun.
defer time.AfterFunc(time.Millisecond*10, cancel).Stop()
// Not canceled until after the exec has started.
if _, err := db.QueryContext(ctx, "select pg_sleep(1)"); err == nil {
t.Fatal("expected error")
} else if err.Error() != "pq: canceling statement due to user request" {
t.Fatalf("unexpected error: %s", err)
}
// Context is already canceled, so error should come before execution.
if _, err := db.QueryContext(ctx, "select pg_sleep(1)"); err == nil {
t.Fatal("expected error")
} else if err.Error() != "context canceled" {
t.Fatalf("unexpected error: %s", err)
}
for i := 0; i < contextRaceIterations; i++ {
func() {
ctx, cancel := context.WithCancel(context.Background())
rows, err := db.QueryContext(ctx, "select 1")
cancel()
if err != nil {
t.Fatal(err)
} else if err := rows.Close(); err != nil {
t.Fatal(err)
}
}()
if rows, err := db.Query("select 1"); err != nil {
t.Fatal(err)
} else if err := rows.Close(); err != nil {
t.Fatal(err)
}
}
}
// TestIssue617 tests that a failed query in QueryContext doesn't lead to a
// goroutine leak.
func TestIssue617(t *testing.T) {
db := openTestConn(t)
defer db.Close()
const N = 10
numGoroutineStart := runtime.NumGoroutine()
for i := 0; i < N; i++ {
func() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
_, err := db.QueryContext(ctx, `SELECT * FROM DOESNOTEXIST`)
pqErr, _ := err.(*Error)
// Expecting "pq: relation \"doesnotexist\" does not exist" error.
if err == nil || pqErr == nil || pqErr.Code != "42P01" {
t.Fatalf("expected undefined table error, got %v", err)
}
}()
}
numGoroutineFinish := runtime.NumGoroutine()
// We use N/2 and not N because the GC and other actors may increase or
// decrease the number of goroutines.
if numGoroutineFinish-numGoroutineStart >= N/2 {
t.Errorf("goroutine leak detected, was %d, now %d", numGoroutineStart, numGoroutineFinish)
}
}
func TestContextCancelBegin(t *testing.T) {
db := openTestConn(t)
defer db.Close()
ctx, cancel := context.WithCancel(context.Background())
tx, err := db.BeginTx(ctx, nil)
if err != nil {
t.Fatal(err)
}
// Delay execution for just a bit until tx.Exec has begun.
defer time.AfterFunc(time.Millisecond*10, cancel).Stop()
// Not canceled until after the exec has started.
if _, err := tx.Exec("select pg_sleep(1)"); err == nil {
t.Fatal("expected error")
} else if err.Error() != "pq: canceling statement due to user request" {
t.Fatalf("unexpected error: %s", err)
}
// Transaction is canceled, so expect an error.
if _, err := tx.Query("select pg_sleep(1)"); err == nil {
t.Fatal("expected error")
} else if err != sql.ErrTxDone {
t.Fatalf("unexpected error: %s", err)
}
// Context is canceled, so cannot begin a transaction.
if _, err := db.BeginTx(ctx, nil); err == nil {
t.Fatal("expected error")
} else if err.Error() != "context canceled" {
t.Fatalf("unexpected error: %s", err)
}
for i := 0; i < contextRaceIterations; i++ {
func() {
ctx, cancel := context.WithCancel(context.Background())
tx, err := db.BeginTx(ctx, nil)
cancel()
if err != nil {
t.Fatal(err)
} else if err := tx.Rollback(); err != nil &&
err.Error() != "pq: canceling statement due to user request" &&
err != sql.ErrTxDone {
t.Fatal(err)
}
}()
if tx, err := db.Begin(); err != nil {
t.Fatal(err)
} else if err := tx.Rollback(); err != nil {
t.Fatal(err)
}
}
}
func TestTxOptions(t *testing.T) {
db := openTestConn(t)
defer db.Close()
ctx := context.Background()
tests := []struct {
level sql.IsolationLevel
isolation string
}{
{
level: sql.LevelDefault,
isolation: "",
},
{
level: sql.LevelReadUncommitted,
isolation: "read uncommitted",
},
{
level: sql.LevelReadCommitted,
isolation: "read committed",
},
{
level: sql.LevelRepeatableRead,
isolation: "repeatable read",
},
{
level: sql.LevelSerializable,
isolation: "serializable",
},
}
for _, test := range tests {
for _, ro := range []bool{true, false} {
tx, err := db.BeginTx(ctx, &sql.TxOptions{
Isolation: test.level,
ReadOnly: ro,
})
if err != nil {
t.Fatal(err)
}
var isolation string
err = tx.QueryRow("select current_setting('transaction_isolation')").Scan(&isolation)
if err != nil {
t.Fatal(err)
}
if test.isolation != "" && isolation != test.isolation {
t.Errorf("wrong isolation level: %s != %s", isolation, test.isolation)
}
var isRO string
err = tx.QueryRow("select current_setting('transaction_read_only')").Scan(&isRO)
if err != nil {
t.Fatal(err)
}
if ro != (isRO == "on") {
t.Errorf("read/[write,only] not set: %t != %s for level %s",
ro, isRO, test.isolation)
}
tx.Rollback()
}
}
_, err := db.BeginTx(ctx, &sql.TxOptions{
Isolation: sql.LevelLinearizable,
})
if err == nil {
t.Fatal("expected LevelLinearizable to fail")
}
if !strings.Contains(err.Error(), "isolation level not supported") {
t.Errorf("Expected error to mention isolation level, got %q", err)
}
}

69
vendor/github.com/lib/pq/go19_test.go generated vendored Normal file
View file

@ -0,0 +1,69 @@
// +build go1.9
package pq
import (
"context"
"database/sql"
"database/sql/driver"
"reflect"
"testing"
)
func TestPing(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
db := openTestConn(t)
defer db.Close()
if _, ok := reflect.TypeOf(db).MethodByName("Conn"); !ok {
t.Skipf("Conn method undefined on type %T, skipping test (requires at least go1.9)", db)
}
if err := db.PingContext(ctx); err != nil {
t.Fatal("expected Ping to succeed")
}
defer cancel()
// grab a connection
conn, err := db.Conn(ctx)
if err != nil {
t.Fatal(err)
}
// start a transaction and read backend pid of our connection
tx, err := conn.BeginTx(ctx, &sql.TxOptions{
Isolation: sql.LevelDefault,
ReadOnly: true,
})
if err != nil {
t.Fatal(err)
}
rows, err := tx.Query("SELECT pg_backend_pid()")
if err != nil {
t.Fatal(err)
}
defer rows.Close()
// read the pid from result
var pid int
for rows.Next() {
if err := rows.Scan(&pid); err != nil {
t.Fatal(err)
}
}
if rows.Err() != nil {
t.Fatal(err)
}
if err := tx.Rollback(); err != nil {
t.Fatal(err)
}
// kill the process which handles our connection and test if the ping fails
if _, err := db.Exec("SELECT pg_terminate_backend($1)", pid); err != nil {
t.Fatal(err)
}
if err := conn.PingContext(ctx); err != driver.ErrBadConn {
t.Fatalf("expected error %s, instead got %s", driver.ErrBadConn, err)
}
}

118
vendor/github.com/lib/pq/hstore/hstore.go generated vendored Normal file
View file

@ -0,0 +1,118 @@
package hstore
import (
"database/sql"
"database/sql/driver"
"strings"
)
// Hstore is 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
}

148
vendor/github.com/lib/pq/hstore/hstore_test.go generated vendored Normal file
View file

@ -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)
}

26
vendor/github.com/lib/pq/issues_test.go generated vendored Normal file
View file

@ -0,0 +1,26 @@
package pq
import "testing"
func TestIssue494(t *testing.T) {
db := openTestConn(t)
defer db.Close()
query := `CREATE TEMP TABLE t (i INT PRIMARY KEY)`
if _, err := db.Exec(query); err != nil {
t.Fatal(err)
}
txn, err := db.Begin()
if err != nil {
t.Fatal(err)
}
if _, err := txn.Prepare(CopyIn("t", "i")); err != nil {
t.Fatal(err)
}
if _, err := txn.Query("SELECT 1"); err == nil {
t.Fatal("expected error")
}
}

570
vendor/github.com/lib/pq/notify_test.go generated vendored Normal file
View file

@ -0,0 +1,570 @@
package pq
import (
"errors"
"fmt"
"io"
"os"
"runtime"
"sync"
"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")
if err != nil {
t.Fatal(err)
}
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")
if err != nil {
t.Fatal(err)
}
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()
defer time.AfterFunc(10*time.Second, func() {
panic("timed out")
}).Stop()
wg.Wait()
}
// 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)
}
defer time.AfterFunc(10*time.Second, func() {
panic("timed out")
}).Stop()
wg.Wait()
}
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)
}
}

218
vendor/github.com/lib/pq/rows_test.go generated vendored Normal file
View file

@ -0,0 +1,218 @@
package pq
import (
"math"
"reflect"
"testing"
"github.com/lib/pq/oid"
)
func TestDataTypeName(t *testing.T) {
tts := []struct {
typ oid.Oid
name string
}{
{oid.T_int8, "INT8"},
{oid.T_int4, "INT4"},
{oid.T_int2, "INT2"},
{oid.T_varchar, "VARCHAR"},
{oid.T_text, "TEXT"},
{oid.T_bool, "BOOL"},
{oid.T_numeric, "NUMERIC"},
{oid.T_date, "DATE"},
{oid.T_time, "TIME"},
{oid.T_timetz, "TIMETZ"},
{oid.T_timestamp, "TIMESTAMP"},
{oid.T_timestamptz, "TIMESTAMPTZ"},
{oid.T_bytea, "BYTEA"},
}
for i, tt := range tts {
dt := fieldDesc{OID: tt.typ}
if name := dt.Name(); name != tt.name {
t.Errorf("(%d) got: %s want: %s", i, name, tt.name)
}
}
}
func TestDataType(t *testing.T) {
tts := []struct {
typ oid.Oid
kind reflect.Kind
}{
{oid.T_int8, reflect.Int64},
{oid.T_int4, reflect.Int32},
{oid.T_int2, reflect.Int16},
{oid.T_varchar, reflect.String},
{oid.T_text, reflect.String},
{oid.T_bool, reflect.Bool},
{oid.T_date, reflect.Struct},
{oid.T_time, reflect.Struct},
{oid.T_timetz, reflect.Struct},
{oid.T_timestamp, reflect.Struct},
{oid.T_timestamptz, reflect.Struct},
{oid.T_bytea, reflect.Slice},
}
for i, tt := range tts {
dt := fieldDesc{OID: tt.typ}
if kind := dt.Type().Kind(); kind != tt.kind {
t.Errorf("(%d) got: %s want: %s", i, kind, tt.kind)
}
}
}
func TestDataTypeLength(t *testing.T) {
tts := []struct {
typ oid.Oid
len int
mod int
length int64
ok bool
}{
{oid.T_int4, 0, -1, 0, false},
{oid.T_varchar, 65535, 9, 5, true},
{oid.T_text, 65535, -1, math.MaxInt64, true},
{oid.T_bytea, 65535, -1, math.MaxInt64, true},
}
for i, tt := range tts {
dt := fieldDesc{OID: tt.typ, Len: tt.len, Mod: tt.mod}
if l, k := dt.Length(); k != tt.ok || l != tt.length {
t.Errorf("(%d) got: %d, %t want: %d, %t", i, l, k, tt.length, tt.ok)
}
}
}
func TestDataTypePrecisionScale(t *testing.T) {
tts := []struct {
typ oid.Oid
mod int
precision, scale int64
ok bool
}{
{oid.T_int4, -1, 0, 0, false},
{oid.T_numeric, 589830, 9, 2, true},
{oid.T_text, -1, 0, 0, false},
}
for i, tt := range tts {
dt := fieldDesc{OID: tt.typ, Mod: tt.mod}
p, s, k := dt.PrecisionScale()
if k != tt.ok {
t.Errorf("(%d) got: %t want: %t", i, k, tt.ok)
}
if p != tt.precision {
t.Errorf("(%d) wrong precision got: %d want: %d", i, p, tt.precision)
}
if s != tt.scale {
t.Errorf("(%d) wrong scale got: %d want: %d", i, s, tt.scale)
}
}
}
func TestRowsColumnTypes(t *testing.T) {
columnTypesTests := []struct {
Name string
TypeName string
Length struct {
Len int64
OK bool
}
DecimalSize struct {
Precision int64
Scale int64
OK bool
}
ScanType reflect.Type
}{
{
Name: "a",
TypeName: "INT4",
Length: struct {
Len int64
OK bool
}{
Len: 0,
OK: false,
},
DecimalSize: struct {
Precision int64
Scale int64
OK bool
}{
Precision: 0,
Scale: 0,
OK: false,
},
ScanType: reflect.TypeOf(int32(0)),
}, {
Name: "bar",
TypeName: "TEXT",
Length: struct {
Len int64
OK bool
}{
Len: math.MaxInt64,
OK: true,
},
DecimalSize: struct {
Precision int64
Scale int64
OK bool
}{
Precision: 0,
Scale: 0,
OK: false,
},
ScanType: reflect.TypeOf(""),
},
}
db := openTestConn(t)
defer db.Close()
rows, err := db.Query("SELECT 1 AS a, text 'bar' AS bar, 1.28::numeric(9, 2) AS dec")
if err != nil {
t.Fatal(err)
}
columns, err := rows.ColumnTypes()
if err != nil {
t.Fatal(err)
}
if len(columns) != 3 {
t.Errorf("expected 3 columns found %d", len(columns))
}
for i, tt := range columnTypesTests {
c := columns[i]
if c.Name() != tt.Name {
t.Errorf("(%d) got: %s, want: %s", i, c.Name(), tt.Name)
}
if c.DatabaseTypeName() != tt.TypeName {
t.Errorf("(%d) got: %s, want: %s", i, c.DatabaseTypeName(), tt.TypeName)
}
l, ok := c.Length()
if l != tt.Length.Len {
t.Errorf("(%d) got: %d, want: %d", i, l, tt.Length.Len)
}
if ok != tt.Length.OK {
t.Errorf("(%d) got: %t, want: %t", i, ok, tt.Length.OK)
}
p, s, ok := c.DecimalSize()
if p != tt.DecimalSize.Precision {
t.Errorf("(%d) got: %d, want: %d", i, p, tt.DecimalSize.Precision)
}
if s != tt.DecimalSize.Scale {
t.Errorf("(%d) got: %d, want: %d", i, s, tt.DecimalSize.Scale)
}
if ok != tt.DecimalSize.OK {
t.Errorf("(%d) got: %t, want: %t", i, ok, tt.DecimalSize.OK)
}
if c.ScanType() != tt.ScanType {
t.Errorf("(%d) got: %v, want: %v", i, c.ScanType(), tt.ScanType)
}
}
}

264
vendor/github.com/lib/pq/scram/scram.go generated vendored Normal file
View file

@ -0,0 +1,264 @@
// Copyright (c) 2014 - Gustavo Niemeyer <gustavo@niemeyer.net>
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Pacakage scram implements a SCRAM-{SHA-1,etc} client per RFC5802.
//
// http://tools.ietf.org/html/rfc5802
//
package scram
import (
"bytes"
"crypto/hmac"
"crypto/rand"
"encoding/base64"
"fmt"
"hash"
"strconv"
"strings"
)
// Client implements a SCRAM-* client (SCRAM-SHA-1, SCRAM-SHA-256, etc).
//
// A Client may be used within a SASL conversation with logic resembling:
//
// var in []byte
// var client = scram.NewClient(sha1.New, user, pass)
// for client.Step(in) {
// out := client.Out()
// // send out to server
// in := serverOut
// }
// if client.Err() != nil {
// // auth failed
// }
//
type Client struct {
newHash func() hash.Hash
user string
pass string
step int
out bytes.Buffer
err error
clientNonce []byte
serverNonce []byte
saltedPass []byte
authMsg bytes.Buffer
}
// NewClient returns a new SCRAM-* client with the provided hash algorithm.
//
// For SCRAM-SHA-256, for example, use:
//
// client := scram.NewClient(sha256.New, user, pass)
//
func NewClient(newHash func() hash.Hash, user, pass string) *Client {
c := &Client{
newHash: newHash,
user: user,
pass: pass,
}
c.out.Grow(256)
c.authMsg.Grow(256)
return c
}
// Out returns the data to be sent to the server in the current step.
func (c *Client) Out() []byte {
if c.out.Len() == 0 {
return nil
}
return c.out.Bytes()
}
// Err returns the error that ocurred, or nil if there were no errors.
func (c *Client) Err() error {
return c.err
}
// SetNonce sets the client nonce to the provided value.
// If not set, the nonce is generated automatically out of crypto/rand on the first step.
func (c *Client) SetNonce(nonce []byte) {
c.clientNonce = nonce
}
var escaper = strings.NewReplacer("=", "=3D", ",", "=2C")
// Step processes the incoming data from the server and makes the
// next round of data for the server available via Client.Out.
// Step returns false if there are no errors and more data is
// still expected.
func (c *Client) Step(in []byte) bool {
c.out.Reset()
if c.step > 2 || c.err != nil {
return false
}
c.step++
switch c.step {
case 1:
c.err = c.step1(in)
case 2:
c.err = c.step2(in)
case 3:
c.err = c.step3(in)
}
return c.step > 2 || c.err != nil
}
func (c *Client) step1(in []byte) error {
if len(c.clientNonce) == 0 {
const nonceLen = 16
buf := make([]byte, nonceLen+b64.EncodedLen(nonceLen))
if _, err := rand.Read(buf[:nonceLen]); err != nil {
return fmt.Errorf("cannot read random SCRAM-SHA-256 nonce from operating system: %v", err)
}
c.clientNonce = buf[nonceLen:]
b64.Encode(c.clientNonce, buf[:nonceLen])
}
c.authMsg.WriteString("n=")
escaper.WriteString(&c.authMsg, c.user)
c.authMsg.WriteString(",r=")
c.authMsg.Write(c.clientNonce)
c.out.WriteString("n,,")
c.out.Write(c.authMsg.Bytes())
return nil
}
var b64 = base64.StdEncoding
func (c *Client) step2(in []byte) error {
c.authMsg.WriteByte(',')
c.authMsg.Write(in)
fields := bytes.Split(in, []byte(","))
if len(fields) != 3 {
return fmt.Errorf("expected 3 fields in first SCRAM-SHA-256 server message, got %d: %q", len(fields), in)
}
if !bytes.HasPrefix(fields[0], []byte("r=")) || len(fields[0]) < 2 {
return fmt.Errorf("server sent an invalid SCRAM-SHA-256 nonce: %q", fields[0])
}
if !bytes.HasPrefix(fields[1], []byte("s=")) || len(fields[1]) < 6 {
return fmt.Errorf("server sent an invalid SCRAM-SHA-256 salt: %q", fields[1])
}
if !bytes.HasPrefix(fields[2], []byte("i=")) || len(fields[2]) < 6 {
return fmt.Errorf("server sent an invalid SCRAM-SHA-256 iteration count: %q", fields[2])
}
c.serverNonce = fields[0][2:]
if !bytes.HasPrefix(c.serverNonce, c.clientNonce) {
return fmt.Errorf("server SCRAM-SHA-256 nonce is not prefixed by client nonce: got %q, want %q+\"...\"", c.serverNonce, c.clientNonce)
}
salt := make([]byte, b64.DecodedLen(len(fields[1][2:])))
n, err := b64.Decode(salt, fields[1][2:])
if err != nil {
return fmt.Errorf("cannot decode SCRAM-SHA-256 salt sent by server: %q", fields[1])
}
salt = salt[:n]
iterCount, err := strconv.Atoi(string(fields[2][2:]))
if err != nil {
return fmt.Errorf("server sent an invalid SCRAM-SHA-256 iteration count: %q", fields[2])
}
c.saltPassword(salt, iterCount)
c.authMsg.WriteString(",c=biws,r=")
c.authMsg.Write(c.serverNonce)
c.out.WriteString("c=biws,r=")
c.out.Write(c.serverNonce)
c.out.WriteString(",p=")
c.out.Write(c.clientProof())
return nil
}
func (c *Client) step3(in []byte) error {
var isv, ise bool
var fields = bytes.Split(in, []byte(","))
if len(fields) == 1 {
isv = bytes.HasPrefix(fields[0], []byte("v="))
ise = bytes.HasPrefix(fields[0], []byte("e="))
}
if ise {
return fmt.Errorf("SCRAM-SHA-256 authentication error: %s", fields[0][2:])
} else if !isv {
return fmt.Errorf("unsupported SCRAM-SHA-256 final message from server: %q", in)
}
if !bytes.Equal(c.serverSignature(), fields[0][2:]) {
return fmt.Errorf("cannot authenticate SCRAM-SHA-256 server signature: %q", fields[0][2:])
}
return nil
}
func (c *Client) saltPassword(salt []byte, iterCount int) {
mac := hmac.New(c.newHash, []byte(c.pass))
mac.Write(salt)
mac.Write([]byte{0, 0, 0, 1})
ui := mac.Sum(nil)
hi := make([]byte, len(ui))
copy(hi, ui)
for i := 1; i < iterCount; i++ {
mac.Reset()
mac.Write(ui)
mac.Sum(ui[:0])
for j, b := range ui {
hi[j] ^= b
}
}
c.saltedPass = hi
}
func (c *Client) clientProof() []byte {
mac := hmac.New(c.newHash, c.saltedPass)
mac.Write([]byte("Client Key"))
clientKey := mac.Sum(nil)
hash := c.newHash()
hash.Write(clientKey)
storedKey := hash.Sum(nil)
mac = hmac.New(c.newHash, storedKey)
mac.Write(c.authMsg.Bytes())
clientProof := mac.Sum(nil)
for i, b := range clientKey {
clientProof[i] ^= b
}
clientProof64 := make([]byte, b64.EncodedLen(len(clientProof)))
b64.Encode(clientProof64, clientProof)
return clientProof64
}
func (c *Client) serverSignature() []byte {
mac := hmac.New(c.newHash, c.saltedPass)
mac.Write([]byte("Server Key"))
serverKey := mac.Sum(nil)
mac = hmac.New(c.newHash, serverKey)
mac.Write(c.authMsg.Bytes())
serverSignature := mac.Sum(nil)
encoded := make([]byte, b64.EncodedLen(len(serverSignature)))
b64.Encode(encoded, serverSignature)
return encoded
}

8
vendor/github.com/lib/pq/ssl.go generated vendored
View file

@ -58,7 +58,13 @@ func ssl(o values) (func(net.Conn) (net.Conn, error), error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
sslRenegotiation(&tlsConf)
// Accept renegotiation requests initiated by the backend.
//
// Renegotiation was deprecated then removed from PostgreSQL 9.5, but
// the default configuration of older versions has it enabled. Redshift
// also initiates renegotiations and cannot be reconfigured.
tlsConf.Renegotiation = tls.RenegotiateFreelyAsClient
return func(conn net.Conn) (net.Conn, error) { return func(conn net.Conn) (net.Conn, error) {
client := tls.Client(conn, &tlsConf) client := tls.Client(conn, &tlsConf)

View file

@ -1,14 +0,0 @@
// +build go1.7
package pq
import "crypto/tls"
// Accept renegotiation requests initiated by the backend.
//
// Renegotiation was deprecated then removed from PostgreSQL 9.5, but
// the default configuration of older versions has it enabled. Redshift
// also initiates renegotiations and cannot be reconfigured.
func sslRenegotiation(conf *tls.Config) {
conf.Renegotiation = tls.RenegotiateFreelyAsClient
}

View file

@ -1,8 +0,0 @@
// +build !go1.7
package pq
import "crypto/tls"
// Renegotiation is not supported by crypto/tls until Go 1.7.
func sslRenegotiation(*tls.Config) {}

279
vendor/github.com/lib/pq/ssl_test.go generated vendored Normal file
View file

@ -0,0 +1,279 @@
package pq
// This file contains SSL tests
import (
_ "crypto/sha256"
"crypto/x509"
"database/sql"
"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) {
_, err := openSSLConn(t, conninfo)
if pge, ok := err.(*Error); ok {
if pge.Code.Name() != "invalid_authorization_specification" {
t.Fatalf("unexpected error code '%s'", pge.Code.Name())
}
} else {
t.Fatalf("expected %T, got %v", (*Error)(nil), err)
}
}
// 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=require sslrootcert=rootCertPath
func TestSSLRequireWithRootCert(t *testing.T) {
maybeSkipSSLTests(t)
// Environment sanity check: should fail without SSL
checkSSLSetup(t, "sslmode=disable user=pqgossltest")
bogusRootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "bogus_root.crt")
bogusRootCert := "sslrootcert=" + bogusRootCertPath + " "
// Not OK according to the bogus CA
_, err := openSSLConn(t, bogusRootCert+"host=postgres sslmode=require user=pqgossltest")
if err == nil {
t.Fatal("expected error")
}
_, ok := err.(x509.UnknownAuthorityError)
if !ok {
t.Fatalf("expected x509.UnknownAuthorityError, got %s, %#+v", err, err)
}
nonExistentCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "non_existent.crt")
nonExistentCert := "sslrootcert=" + nonExistentCertPath + " "
// No match on Common Name, but that's OK because we're not validating anything.
_, err = openSSLConn(t, nonExistentCert+"host=127.0.0.1 sslmode=require user=pqgossltest")
if err != nil {
t.Fatal(err)
}
rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt")
rootCert := "sslrootcert=" + rootCertPath + " "
// No match on Common Name, but that's OK because we're not validating the CN.
_, err = openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=require user=pqgossltest")
if err != nil {
t.Fatal(err)
}
// Everything OK
_, err = openSSLConn(t, rootCert+"host=postgres sslmode=require user=pqgossltest")
if err != nil {
t.Fatal(err)
}
}
// Test sslmode=verify-ca
func TestSSLVerifyCA(t *testing.T) {
maybeSkipSSLTests(t)
// 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 _, ok := err.(x509.UnknownAuthorityError); !ok {
t.Fatalf("expected %T, got %#+v", x509.UnknownAuthorityError{}, err)
}
}
// Still not OK according to the system CA; empty sslrootcert is treated as unspecified.
{
_, err := openSSLConn(t, "host=postgres sslmode=verify-ca user=pqgossltest sslrootcert=''")
if _, ok := err.(x509.UnknownAuthorityError); !ok {
t.Fatalf("expected %T, got %#+v", x509.UnknownAuthorityError{}, err)
}
}
rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt")
rootCert := "sslrootcert=" + rootCertPath + " "
// No match on Common Name, but that's OK
if _, err := openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=verify-ca user=pqgossltest"); err != nil {
t.Fatal(err)
}
// Everything OK
if _, err := openSSLConn(t, rootCert+"host=postgres sslmode=verify-ca user=pqgossltest"); err != nil {
t.Fatal(err)
}
}
// 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")
const baseinfo = "sslmode=require user=pqgosslcert"
// Certificate not specified, should fail
{
_, err := openSSLConn(t, baseinfo)
if pge, ok := err.(*Error); ok {
if pge.Code.Name() != "invalid_authorization_specification" {
t.Fatalf("unexpected error code '%s'", pge.Code.Name())
}
} else {
t.Fatalf("expected %T, got %v", (*Error)(nil), err)
}
}
// Empty certificate specified, should fail
{
_, err := openSSLConn(t, baseinfo+" sslcert=''")
if pge, ok := err.(*Error); ok {
if pge.Code.Name() != "invalid_authorization_specification" {
t.Fatalf("unexpected error code '%s'", pge.Code.Name())
}
} else {
t.Fatalf("expected %T, got %v", (*Error)(nil), err)
}
}
// Non-existent certificate specified, should fail
{
_, err := openSSLConn(t, baseinfo+" sslcert=/tmp/filedoesnotexist")
if pge, ok := err.(*Error); ok {
if pge.Code.Name() != "invalid_authorization_specification" {
t.Fatalf("unexpected error code '%s'", pge.Code.Name())
}
} else {
t.Fatalf("expected %T, got %v", (*Error)(nil), err)
}
}
certpath, ok := os.LookupEnv("PQSSLCERTTEST_PATH")
if !ok {
t.Fatalf("PQSSLCERTTEST_PATH not present in environment")
}
sslcert := filepath.Join(certpath, "postgresql.crt")
// Cert present, key not specified, should fail
{
_, err := openSSLConn(t, baseinfo+" sslcert="+sslcert)
if _, ok := err.(*os.PathError); !ok {
t.Fatalf("expected %T, got %#+v", (*os.PathError)(nil), err)
}
}
// Cert present, empty key specified, should fail
{
_, err := openSSLConn(t, baseinfo+" sslcert="+sslcert+" sslkey=''")
if _, ok := err.(*os.PathError); !ok {
t.Fatalf("expected %T, got %#+v", (*os.PathError)(nil), err)
}
}
// Cert present, non-existent key, should fail
{
_, err := openSSLConn(t, baseinfo+" sslcert="+sslcert+" sslkey=/tmp/filedoesnotexist")
if _, ok := err.(*os.PathError); !ok {
t.Fatalf("expected %T, got %#+v", (*os.PathError)(nil), err)
}
}
// Key has wrong permissions (passing the cert as the key), should fail
if _, err := openSSLConn(t, baseinfo+" sslcert="+sslcert+" sslkey="+sslcert); err != ErrSSLKeyHasWorldPermissions {
t.Fatalf("expected %s, got %#+v", ErrSSLKeyHasWorldPermissions, err)
}
sslkey := filepath.Join(certpath, "postgresql.key")
// Should work
if db, err := openSSLConn(t, baseinfo+" sslcert="+sslcert+" sslkey="+sslkey); err != nil {
t.Fatal(err)
} else {
rows, err := db.Query("SELECT 1")
if err != nil {
t.Fatal(err)
}
if err := rows.Close(); err != nil {
t.Fatal(err)
}
if err := db.Close(); err != nil {
t.Fatal(err)
}
}
}

66
vendor/github.com/lib/pq/url_test.go generated vendored Normal file
View file

@ -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)
}
}

46
vendor/github.com/lib/pq/uuid_test.go generated vendored Normal file
View file

@ -0,0 +1,46 @@
package pq
import (
"reflect"
"strings"
"testing"
)
func TestDecodeUUIDBinaryError(t *testing.T) {
t.Parallel()
_, err := decodeUUIDBinary([]byte{0x12, 0x34})
if err == nil {
t.Fatal("Expected error, got none")
}
if !strings.HasPrefix(err.Error(), "pq:") {
t.Errorf("Expected error to start with %q, got %q", "pq:", err.Error())
}
if !strings.Contains(err.Error(), "bad length: 2") {
t.Errorf("Expected error to contain length, got %q", err.Error())
}
}
func BenchmarkDecodeUUIDBinary(b *testing.B) {
x := []byte{0x03, 0xa3, 0x52, 0x2f, 0x89, 0x28, 0x49, 0x87, 0x84, 0xd6, 0x93, 0x7b, 0x36, 0xec, 0x27, 0x6f}
for i := 0; i < b.N; i++ {
decodeUUIDBinary(x)
}
}
func TestDecodeUUIDBackend(t *testing.T) {
db := openTestConn(t)
defer db.Close()
var s = "a0ecc91d-a13f-4fe4-9fce-7e09777cc70a"
var scanned interface{}
err := db.QueryRow(`SELECT $1::uuid`, s).Scan(&scanned)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}
if !reflect.DeepEqual(scanned, []byte(s)) {
t.Errorf("Expected []byte(%q), got %T(%q)", s, scanned, scanned)
}
}