Merge branch 'v4.4.x'

This commit is contained in:
Torkel Ödegaard
2017-08-08 16:18:08 +02:00
31 changed files with 2235 additions and 3886 deletions

View File

@@ -76,8 +76,10 @@ password =
# Example: mysql://user:secret@host:port/database
url =
# Max idle conn setting default is 2
max_idle_conn = 2
# Max conn setting default is 0 (mean not set)
max_idle_conn =
max_open_conn =
# For "postgres", use either "disable", "require" or "verify-full"

View File

@@ -85,8 +85,10 @@
# For "sqlite3" only, path relative to data_path setting
;path = grafana.db
# Max idle conn setting default is 2
;max_idle_conn = 2
# Max conn setting default is 0 (mean not set)
;max_idle_conn =
;max_open_conn =

View File

@@ -6,3 +6,4 @@ postgrestest:
POSTGRES_DATABASE: grafana
ports:
- "5432:5432"
command: postgres -c log_connections=on -c logging_collector=on -c log_destination=stderr -c log_directory=/var/log/postgresql

View File

@@ -1,4 +0,0 @@
.db
*.test
*~
*.swp

61
vendor/github.com/lib/pq/.travis.yml generated vendored
View File

@@ -1,61 +0,0 @@
language: go
go:
- 1.1
- 1.2
- 1.3
- 1.4
- tip
before_install:
- psql --version
- sudo /etc/init.d/postgresql stop
- sudo apt-get -y --purge remove postgresql libpq-dev libpq5 postgresql-client-common postgresql-common
- sudo rm -rf /var/lib/postgresql
- wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
- sudo sh -c "echo deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main $PGVERSION >> /etc/apt/sources.list.d/postgresql.list"
- sudo apt-get update -qq
- sudo apt-get -y -o Dpkg::Options::=--force-confdef -o Dpkg::Options::="--force-confnew" install postgresql-$PGVERSION postgresql-server-dev-$PGVERSION postgresql-contrib-$PGVERSION
- sudo chmod 777 /etc/postgresql/$PGVERSION/main/pg_hba.conf
- echo "local all postgres trust" > /etc/postgresql/$PGVERSION/main/pg_hba.conf
- echo "local all all trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- echo "hostnossl all pqgossltest 127.0.0.1/32 reject" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- echo "hostnossl all pqgosslcert 127.0.0.1/32 reject" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- echo "hostssl all pqgossltest 127.0.0.1/32 trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- echo "hostssl all pqgosslcert 127.0.0.1/32 cert" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- echo "host all all 127.0.0.1/32 trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- echo "hostnossl all pqgossltest ::1/128 reject" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- echo "hostnossl all pqgosslcert ::1/128 reject" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- echo "hostssl all pqgossltest ::1/128 trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- echo "hostssl all pqgosslcert ::1/128 cert" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- echo "host all all ::1/128 trust" >> /etc/postgresql/$PGVERSION/main/pg_hba.conf
- sudo install -o postgres -g postgres -m 600 -t /var/lib/postgresql/$PGVERSION/main/ certs/server.key certs/server.crt certs/root.crt
- sudo bash -c "[[ '${PGVERSION}' < '9.2' ]] || (echo \"ssl_cert_file = 'server.crt'\" >> /etc/postgresql/$PGVERSION/main/postgresql.conf)"
- sudo bash -c "[[ '${PGVERSION}' < '9.2' ]] || (echo \"ssl_key_file = 'server.key'\" >> /etc/postgresql/$PGVERSION/main/postgresql.conf)"
- sudo bash -c "[[ '${PGVERSION}' < '9.2' ]] || (echo \"ssl_ca_file = 'root.crt'\" >> /etc/postgresql/$PGVERSION/main/postgresql.conf)"
- sudo sh -c "echo 127.0.0.1 postgres >> /etc/hosts"
- sudo ls -l /var/lib/postgresql/$PGVERSION/main/
- sudo cat /etc/postgresql/$PGVERSION/main/postgresql.conf
- sudo chmod 600 $PQSSLCERTTEST_PATH/postgresql.key
- sudo /etc/init.d/postgresql restart
env:
global:
- PGUSER=postgres
- PQGOSSLTESTS=1
- PQSSLCERTTEST_PATH=$PWD/certs
matrix:
- PGVERSION=9.4
- PGVERSION=9.3
- PGVERSION=9.2
- PGVERSION=9.1
- PGVERSION=9.0
- PGVERSION=8.4
script:
- go test -v ./...
before_script:
- psql -c 'create database pqgotest' -U postgres
- psql -c 'create user pqgossltest' -U postgres
- psql -c 'create user pqgosslcert' -U postgres

15
vendor/github.com/lib/pq/README.md generated vendored
View File

@@ -1,6 +1,6 @@
# pq - A pure Go postgres driver for Go's database/sql package
[![Build Status](https://travis-ci.org/lib/pq.png?branch=master)](https://travis-ci.org/lib/pq)
[![Build Status](https://travis-ci.org/lib/pq.svg?branch=master)](https://travis-ci.org/lib/pq)
## Install
@@ -20,11 +20,11 @@ variables.
Example:
PGHOST=/var/run/postgresql go test github.com/lib/pq
PGHOST=/run/postgresql go test github.com/lib/pq
Optionally, a benchmark suite can be run as part of the tests:
PGHOST=/var/run/postgresql go test -bench .
PGHOST=/run/postgresql go test -bench .
## Features
@@ -38,6 +38,7 @@ Optionally, a benchmark suite can be run as part of the tests:
* Many libpq compatible environment variables
* Unix socket support
* Notifications: `LISTEN`/`NOTIFY`
* pgpass support
## Future / Things you can help with
@@ -57,13 +58,17 @@ code still exists in here.
* Brad Fitzpatrick (bradfitz)
* Charlie Melbye (cmelbye)
* Chris Bandy (cbandy)
* Chris Gilling (cgilling)
* Chris Walsh (cwds)
* Dan Sosedoff (sosedoff)
* Daniel Farina (fdr)
* Eric Chlebek (echlebek)
* Eric Garrido (minusnine)
* Eric Urban (hydrogen18)
* Everyone at The Go Team
* Evan Shaw (edsrzf)
* Ewan Chou (coocood)
* Fazal Majid (fazalmajid)
* Federico Romero (federomero)
* Fumin (fumin)
* Gary Burd (garyburd)
@@ -80,7 +85,7 @@ code still exists in here.
* Keith Rarick (kr)
* Kir Shatrov (kirs)
* Lann Martin (lann)
* Maciek Sakrejda (deafbybeheading)
* Maciek Sakrejda (uhoh-itsmaciek)
* Marc Brinkmann (mbr)
* Marko Tiikkaja (johto)
* Matt Newberry (MattNewberry)
@@ -94,5 +99,7 @@ code still exists in here.
* Ryan Smith (ryandotsmith)
* Samuel Stauffer (samuel)
* Timothée Peignier (cyberdelia)
* Travis Cline (tmc)
* TruongSinh Tran-Nguyen (truongsinh)
* Yaismel Miranda (ympons)
* notedit (notedit)

756
vendor/github.com/lib/pq/array.go generated vendored Normal file
View File

@@ -0,0 +1,756 @@
package pq
import (
"bytes"
"database/sql"
"database/sql/driver"
"encoding/hex"
"fmt"
"reflect"
"strconv"
"strings"
)
var typeByteSlice = reflect.TypeOf([]byte{})
var typeDriverValuer = reflect.TypeOf((*driver.Valuer)(nil)).Elem()
var typeSqlScanner = reflect.TypeOf((*sql.Scanner)(nil)).Elem()
// Array returns the optimal driver.Valuer and sql.Scanner for an array or
// slice of any dimension.
//
// For example:
// db.Query(`SELECT * FROM t WHERE id = ANY($1)`, pq.Array([]int{235, 401}))
//
// var x []sql.NullInt64
// db.QueryRow('SELECT ARRAY[235, 401]').Scan(pq.Array(&x))
//
// Scanning multi-dimensional arrays is not supported. Arrays where the lower
// bound is not one (such as `[0:0]={1}') are not supported.
func Array(a interface{}) interface {
driver.Valuer
sql.Scanner
} {
switch a := a.(type) {
case []bool:
return (*BoolArray)(&a)
case []float64:
return (*Float64Array)(&a)
case []int64:
return (*Int64Array)(&a)
case []string:
return (*StringArray)(&a)
case *[]bool:
return (*BoolArray)(a)
case *[]float64:
return (*Float64Array)(a)
case *[]int64:
return (*Int64Array)(a)
case *[]string:
return (*StringArray)(a)
}
return GenericArray{a}
}
// ArrayDelimiter may be optionally implemented by driver.Valuer or sql.Scanner
// to override the array delimiter used by GenericArray.
type ArrayDelimiter interface {
// ArrayDelimiter returns the delimiter character(s) for this element's type.
ArrayDelimiter() string
}
// BoolArray represents a one-dimensional array of the PostgreSQL boolean type.
type BoolArray []bool
// Scan implements the sql.Scanner interface.
func (a *BoolArray) Scan(src interface{}) error {
switch src := src.(type) {
case []byte:
return a.scanBytes(src)
case string:
return a.scanBytes([]byte(src))
case nil:
*a = nil
return nil
}
return fmt.Errorf("pq: cannot convert %T to BoolArray", src)
}
func (a *BoolArray) scanBytes(src []byte) error {
elems, err := scanLinearArray(src, []byte{','}, "BoolArray")
if err != nil {
return err
}
if *a != nil && len(elems) == 0 {
*a = (*a)[:0]
} else {
b := make(BoolArray, len(elems))
for i, v := range elems {
if len(v) != 1 {
return fmt.Errorf("pq: could not parse boolean array index %d: invalid boolean %q", i, v)
}
switch v[0] {
case 't':
b[i] = true
case 'f':
b[i] = false
default:
return fmt.Errorf("pq: could not parse boolean array index %d: invalid boolean %q", i, v)
}
}
*a = b
}
return nil
}
// Value implements the driver.Valuer interface.
func (a BoolArray) Value() (driver.Value, error) {
if a == nil {
return nil, nil
}
if n := len(a); n > 0 {
// There will be exactly two curly brackets, N bytes of values,
// and N-1 bytes of delimiters.
b := make([]byte, 1+2*n)
for i := 0; i < n; i++ {
b[2*i] = ','
if a[i] {
b[1+2*i] = 't'
} else {
b[1+2*i] = 'f'
}
}
b[0] = '{'
b[2*n] = '}'
return string(b), nil
}
return "{}", nil
}
// ByteaArray represents a one-dimensional array of the PostgreSQL bytea type.
type ByteaArray [][]byte
// Scan implements the sql.Scanner interface.
func (a *ByteaArray) Scan(src interface{}) error {
switch src := src.(type) {
case []byte:
return a.scanBytes(src)
case string:
return a.scanBytes([]byte(src))
case nil:
*a = nil
return nil
}
return fmt.Errorf("pq: cannot convert %T to ByteaArray", src)
}
func (a *ByteaArray) scanBytes(src []byte) error {
elems, err := scanLinearArray(src, []byte{','}, "ByteaArray")
if err != nil {
return err
}
if *a != nil && len(elems) == 0 {
*a = (*a)[:0]
} else {
b := make(ByteaArray, len(elems))
for i, v := range elems {
b[i], err = parseBytea(v)
if err != nil {
return fmt.Errorf("could not parse bytea array index %d: %s", i, err.Error())
}
}
*a = b
}
return nil
}
// Value implements the driver.Valuer interface. It uses the "hex" format which
// is only supported on PostgreSQL 9.0 or newer.
func (a ByteaArray) Value() (driver.Value, error) {
if a == nil {
return nil, nil
}
if n := len(a); n > 0 {
// There will be at least two curly brackets, 2*N bytes of quotes,
// 3*N bytes of hex formatting, and N-1 bytes of delimiters.
size := 1 + 6*n
for _, x := range a {
size += hex.EncodedLen(len(x))
}
b := make([]byte, size)
for i, s := 0, b; i < n; i++ {
o := copy(s, `,"\\x`)
o += hex.Encode(s[o:], a[i])
s[o] = '"'
s = s[o+1:]
}
b[0] = '{'
b[size-1] = '}'
return string(b), nil
}
return "{}", nil
}
// Float64Array represents a one-dimensional array of the PostgreSQL double
// precision type.
type Float64Array []float64
// Scan implements the sql.Scanner interface.
func (a *Float64Array) Scan(src interface{}) error {
switch src := src.(type) {
case []byte:
return a.scanBytes(src)
case string:
return a.scanBytes([]byte(src))
case nil:
*a = nil
return nil
}
return fmt.Errorf("pq: cannot convert %T to Float64Array", src)
}
func (a *Float64Array) scanBytes(src []byte) error {
elems, err := scanLinearArray(src, []byte{','}, "Float64Array")
if err != nil {
return err
}
if *a != nil && len(elems) == 0 {
*a = (*a)[:0]
} else {
b := make(Float64Array, len(elems))
for i, v := range elems {
if b[i], err = strconv.ParseFloat(string(v), 64); err != nil {
return fmt.Errorf("pq: parsing array element index %d: %v", i, err)
}
}
*a = b
}
return nil
}
// Value implements the driver.Valuer interface.
func (a Float64Array) Value() (driver.Value, error) {
if a == nil {
return nil, nil
}
if n := len(a); n > 0 {
// There will be at least two curly brackets, N bytes of values,
// and N-1 bytes of delimiters.
b := make([]byte, 1, 1+2*n)
b[0] = '{'
b = strconv.AppendFloat(b, a[0], 'f', -1, 64)
for i := 1; i < n; i++ {
b = append(b, ',')
b = strconv.AppendFloat(b, a[i], 'f', -1, 64)
}
return string(append(b, '}')), nil
}
return "{}", nil
}
// GenericArray implements the driver.Valuer and sql.Scanner interfaces for
// an array or slice of any dimension.
type GenericArray struct{ A interface{} }
func (GenericArray) evaluateDestination(rt reflect.Type) (reflect.Type, func([]byte, reflect.Value) error, string) {
var assign func([]byte, reflect.Value) error
var del = ","
// TODO calculate the assign function for other types
// TODO repeat this section on the element type of arrays or slices (multidimensional)
{
if reflect.PtrTo(rt).Implements(typeSqlScanner) {
// dest is always addressable because it is an element of a slice.
assign = func(src []byte, dest reflect.Value) (err error) {
ss := dest.Addr().Interface().(sql.Scanner)
if src == nil {
err = ss.Scan(nil)
} else {
err = ss.Scan(src)
}
return
}
goto FoundType
}
assign = func([]byte, reflect.Value) error {
return fmt.Errorf("pq: scanning to %s is not implemented; only sql.Scanner", rt)
}
}
FoundType:
if ad, ok := reflect.Zero(rt).Interface().(ArrayDelimiter); ok {
del = ad.ArrayDelimiter()
}
return rt, assign, del
}
// Scan implements the sql.Scanner interface.
func (a GenericArray) Scan(src interface{}) error {
dpv := reflect.ValueOf(a.A)
switch {
case dpv.Kind() != reflect.Ptr:
return fmt.Errorf("pq: destination %T is not a pointer to array or slice", a.A)
case dpv.IsNil():
return fmt.Errorf("pq: destination %T is nil", a.A)
}
dv := dpv.Elem()
switch dv.Kind() {
case reflect.Slice:
case reflect.Array:
default:
return fmt.Errorf("pq: destination %T is not a pointer to array or slice", a.A)
}
switch src := src.(type) {
case []byte:
return a.scanBytes(src, dv)
case string:
return a.scanBytes([]byte(src), dv)
case nil:
if dv.Kind() == reflect.Slice {
dv.Set(reflect.Zero(dv.Type()))
return nil
}
}
return fmt.Errorf("pq: cannot convert %T to %s", src, dv.Type())
}
func (a GenericArray) scanBytes(src []byte, dv reflect.Value) error {
dtype, assign, del := a.evaluateDestination(dv.Type().Elem())
dims, elems, err := parseArray(src, []byte(del))
if err != nil {
return err
}
// TODO allow multidimensional
if len(dims) > 1 {
return fmt.Errorf("pq: scanning from multidimensional ARRAY%s is not implemented",
strings.Replace(fmt.Sprint(dims), " ", "][", -1))
}
// Treat a zero-dimensional array like an array with a single dimension of zero.
if len(dims) == 0 {
dims = append(dims, 0)
}
for i, rt := 0, dv.Type(); i < len(dims); i, rt = i+1, rt.Elem() {
switch rt.Kind() {
case reflect.Slice:
case reflect.Array:
if rt.Len() != dims[i] {
return fmt.Errorf("pq: cannot convert ARRAY%s to %s",
strings.Replace(fmt.Sprint(dims), " ", "][", -1), dv.Type())
}
default:
// TODO handle multidimensional
}
}
values := reflect.MakeSlice(reflect.SliceOf(dtype), len(elems), len(elems))
for i, e := range elems {
if err := assign(e, values.Index(i)); err != nil {
return fmt.Errorf("pq: parsing array element index %d: %v", i, err)
}
}
// TODO handle multidimensional
switch dv.Kind() {
case reflect.Slice:
dv.Set(values.Slice(0, dims[0]))
case reflect.Array:
for i := 0; i < dims[0]; i++ {
dv.Index(i).Set(values.Index(i))
}
}
return nil
}
// Value implements the driver.Valuer interface.
func (a GenericArray) Value() (driver.Value, error) {
if a.A == nil {
return nil, nil
}
rv := reflect.ValueOf(a.A)
switch rv.Kind() {
case reflect.Slice:
if rv.IsNil() {
return nil, nil
}
case reflect.Array:
default:
return nil, fmt.Errorf("pq: Unable to convert %T to array", a.A)
}
if n := rv.Len(); n > 0 {
// There will be at least two curly brackets, N bytes of values,
// and N-1 bytes of delimiters.
b := make([]byte, 0, 1+2*n)
b, _, err := appendArray(b, rv, n)
return string(b), err
}
return "{}", nil
}
// Int64Array represents a one-dimensional array of the PostgreSQL integer types.
type Int64Array []int64
// Scan implements the sql.Scanner interface.
func (a *Int64Array) Scan(src interface{}) error {
switch src := src.(type) {
case []byte:
return a.scanBytes(src)
case string:
return a.scanBytes([]byte(src))
case nil:
*a = nil
return nil
}
return fmt.Errorf("pq: cannot convert %T to Int64Array", src)
}
func (a *Int64Array) scanBytes(src []byte) error {
elems, err := scanLinearArray(src, []byte{','}, "Int64Array")
if err != nil {
return err
}
if *a != nil && len(elems) == 0 {
*a = (*a)[:0]
} else {
b := make(Int64Array, len(elems))
for i, v := range elems {
if b[i], err = strconv.ParseInt(string(v), 10, 64); err != nil {
return fmt.Errorf("pq: parsing array element index %d: %v", i, err)
}
}
*a = b
}
return nil
}
// Value implements the driver.Valuer interface.
func (a Int64Array) Value() (driver.Value, error) {
if a == nil {
return nil, nil
}
if n := len(a); n > 0 {
// There will be at least two curly brackets, N bytes of values,
// and N-1 bytes of delimiters.
b := make([]byte, 1, 1+2*n)
b[0] = '{'
b = strconv.AppendInt(b, a[0], 10)
for i := 1; i < n; i++ {
b = append(b, ',')
b = strconv.AppendInt(b, a[i], 10)
}
return string(append(b, '}')), nil
}
return "{}", nil
}
// StringArray represents a one-dimensional array of the PostgreSQL character types.
type StringArray []string
// Scan implements the sql.Scanner interface.
func (a *StringArray) Scan(src interface{}) error {
switch src := src.(type) {
case []byte:
return a.scanBytes(src)
case string:
return a.scanBytes([]byte(src))
case nil:
*a = nil
return nil
}
return fmt.Errorf("pq: cannot convert %T to StringArray", src)
}
func (a *StringArray) scanBytes(src []byte) error {
elems, err := scanLinearArray(src, []byte{','}, "StringArray")
if err != nil {
return err
}
if *a != nil && len(elems) == 0 {
*a = (*a)[:0]
} else {
b := make(StringArray, len(elems))
for i, v := range elems {
if b[i] = string(v); v == nil {
return fmt.Errorf("pq: parsing array element index %d: cannot convert nil to string", i)
}
}
*a = b
}
return nil
}
// Value implements the driver.Valuer interface.
func (a StringArray) Value() (driver.Value, error) {
if a == nil {
return nil, nil
}
if n := len(a); n > 0 {
// There will be at least two curly brackets, 2*N bytes of quotes,
// and N-1 bytes of delimiters.
b := make([]byte, 1, 1+3*n)
b[0] = '{'
b = appendArrayQuotedBytes(b, []byte(a[0]))
for i := 1; i < n; i++ {
b = append(b, ',')
b = appendArrayQuotedBytes(b, []byte(a[i]))
}
return string(append(b, '}')), nil
}
return "{}", nil
}
// appendArray appends rv to the buffer, returning the extended buffer and
// the delimiter used between elements.
//
// It panics when n <= 0 or rv's Kind is not reflect.Array nor reflect.Slice.
func appendArray(b []byte, rv reflect.Value, n int) ([]byte, string, error) {
var del string
var err error
b = append(b, '{')
if b, del, err = appendArrayElement(b, rv.Index(0)); err != nil {
return b, del, err
}
for i := 1; i < n; i++ {
b = append(b, del...)
if b, del, err = appendArrayElement(b, rv.Index(i)); err != nil {
return b, del, err
}
}
return append(b, '}'), del, nil
}
// appendArrayElement appends rv to the buffer, returning the extended buffer
// and the delimiter to use before the next element.
//
// When rv's Kind is neither reflect.Array nor reflect.Slice, it is converted
// using driver.DefaultParameterConverter and the resulting []byte or string
// is double-quoted.
//
// See http://www.postgresql.org/docs/current/static/arrays.html#ARRAYS-IO
func appendArrayElement(b []byte, rv reflect.Value) ([]byte, string, error) {
if k := rv.Kind(); k == reflect.Array || k == reflect.Slice {
if t := rv.Type(); t != typeByteSlice && !t.Implements(typeDriverValuer) {
if n := rv.Len(); n > 0 {
return appendArray(b, rv, n)
}
return b, "", nil
}
}
var del string = ","
var err error
var iv interface{} = rv.Interface()
if ad, ok := iv.(ArrayDelimiter); ok {
del = ad.ArrayDelimiter()
}
if iv, err = driver.DefaultParameterConverter.ConvertValue(iv); err != nil {
return b, del, err
}
switch v := iv.(type) {
case nil:
return append(b, "NULL"...), del, nil
case []byte:
return appendArrayQuotedBytes(b, v), del, nil
case string:
return appendArrayQuotedBytes(b, []byte(v)), del, nil
}
b, err = appendValue(b, iv)
return b, del, err
}
func appendArrayQuotedBytes(b, v []byte) []byte {
b = append(b, '"')
for {
i := bytes.IndexAny(v, `"\`)
if i < 0 {
b = append(b, v...)
break
}
if i > 0 {
b = append(b, v[:i]...)
}
b = append(b, '\\', v[i])
v = v[i+1:]
}
return append(b, '"')
}
func appendValue(b []byte, v driver.Value) ([]byte, error) {
return append(b, encode(nil, v, 0)...), nil
}
// parseArray extracts the dimensions and elements of an array represented in
// text format. Only representations emitted by the backend are supported.
// Notably, whitespace around brackets and delimiters is significant, and NULL
// is case-sensitive.
//
// See http://www.postgresql.org/docs/current/static/arrays.html#ARRAYS-IO
func parseArray(src, del []byte) (dims []int, elems [][]byte, err error) {
var depth, i int
if len(src) < 1 || src[0] != '{' {
return nil, nil, fmt.Errorf("pq: unable to parse array; expected %q at offset %d", '{', 0)
}
Open:
for i < len(src) {
switch src[i] {
case '{':
depth++
i++
case '}':
elems = make([][]byte, 0)
goto Close
default:
break Open
}
}
dims = make([]int, i)
Element:
for i < len(src) {
switch src[i] {
case '{':
if depth == len(dims) {
break Element
}
depth++
dims[depth-1] = 0
i++
case '"':
var elem = []byte{}
var escape bool
for i++; i < len(src); i++ {
if escape {
elem = append(elem, src[i])
escape = false
} else {
switch src[i] {
default:
elem = append(elem, src[i])
case '\\':
escape = true
case '"':
elems = append(elems, elem)
i++
break Element
}
}
}
default:
for start := i; i < len(src); i++ {
if bytes.HasPrefix(src[i:], del) || src[i] == '}' {
elem := src[start:i]
if len(elem) == 0 {
return nil, nil, fmt.Errorf("pq: unable to parse array; unexpected %q at offset %d", src[i], i)
}
if bytes.Equal(elem, []byte("NULL")) {
elem = nil
}
elems = append(elems, elem)
break Element
}
}
}
}
for i < len(src) {
if bytes.HasPrefix(src[i:], del) && depth > 0 {
dims[depth-1]++
i += len(del)
goto Element
} else if src[i] == '}' && depth > 0 {
dims[depth-1]++
depth--
i++
} else {
return nil, nil, fmt.Errorf("pq: unable to parse array; unexpected %q at offset %d", src[i], i)
}
}
Close:
for i < len(src) {
if src[i] == '}' && depth > 0 {
depth--
i++
} else {
return nil, nil, fmt.Errorf("pq: unable to parse array; unexpected %q at offset %d", src[i], i)
}
}
if depth > 0 {
err = fmt.Errorf("pq: unable to parse array; expected %q at offset %d", '}', i)
}
if err == nil {
for _, d := range dims {
if (len(elems) % d) != 0 {
err = fmt.Errorf("pq: multidimensional arrays must have elements with matching dimensions")
}
}
}
return
}
func scanLinearArray(src, del []byte, typ string) (elems [][]byte, err error) {
dims, elems, err := parseArray(src, del)
if err != nil {
return nil, err
}
if len(dims) > 1 {
return nil, fmt.Errorf("pq: cannot convert ARRAY%s to %s", strings.Replace(fmt.Sprint(dims), " ", "][", -1), typ)
}
return elems, err
}

View File

@@ -1,434 +0,0 @@
// +build go1.1
package pq
import (
"bufio"
"bytes"
"database/sql"
"database/sql/driver"
"github.com/lib/pq/oid"
"io"
"math/rand"
"net"
"runtime"
"strconv"
"strings"
"sync"
"testing"
"time"
)
var (
selectStringQuery = "SELECT '" + strings.Repeat("0123456789", 10) + "'"
selectSeriesQuery = "SELECT generate_series(1, 100)"
)
func BenchmarkSelectString(b *testing.B) {
var result string
benchQuery(b, selectStringQuery, &result)
}
func BenchmarkSelectSeries(b *testing.B) {
var result int
benchQuery(b, selectSeriesQuery, &result)
}
func benchQuery(b *testing.B, query string, result interface{}) {
b.StopTimer()
db := openTestConn(b)
defer db.Close()
b.StartTimer()
for i := 0; i < b.N; i++ {
benchQueryLoop(b, db, query, result)
}
}
func benchQueryLoop(b *testing.B, db *sql.DB, query string, result interface{}) {
rows, err := db.Query(query)
if err != nil {
b.Fatal(err)
}
defer rows.Close()
for rows.Next() {
err = rows.Scan(result)
if err != nil {
b.Fatal("failed to scan", err)
}
}
}
// reading from circularConn yields content[:prefixLen] once, followed by
// content[prefixLen:] over and over again. It never returns EOF.
type circularConn struct {
content string
prefixLen int
pos int
net.Conn // for all other net.Conn methods that will never be called
}
func (r *circularConn) Read(b []byte) (n int, err error) {
n = copy(b, r.content[r.pos:])
r.pos += n
if r.pos >= len(r.content) {
r.pos = r.prefixLen
}
return
}
func (r *circularConn) Write(b []byte) (n int, err error) { return len(b), nil }
func (r *circularConn) Close() error { return nil }
func fakeConn(content string, prefixLen int) *conn {
c := &circularConn{content: content, prefixLen: prefixLen}
return &conn{buf: bufio.NewReader(c), c: c}
}
// This benchmark is meant to be the same as BenchmarkSelectString, but takes
// out some of the factors this package can't control. The numbers are less noisy,
// but also the costs of network communication aren't accurately represented.
func BenchmarkMockSelectString(b *testing.B) {
b.StopTimer()
// taken from a recorded run of BenchmarkSelectString
// See: http://www.postgresql.org/docs/current/static/protocol-message-formats.html
const response = "1\x00\x00\x00\x04" +
"t\x00\x00\x00\x06\x00\x00" +
"T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" +
"Z\x00\x00\x00\x05I" +
"2\x00\x00\x00\x04" +
"D\x00\x00\x00n\x00\x01\x00\x00\x00d0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
"C\x00\x00\x00\rSELECT 1\x00" +
"Z\x00\x00\x00\x05I" +
"3\x00\x00\x00\x04" +
"Z\x00\x00\x00\x05I"
c := fakeConn(response, 0)
b.StartTimer()
for i := 0; i < b.N; i++ {
benchMockQuery(b, c, selectStringQuery)
}
}
var seriesRowData = func() string {
var buf bytes.Buffer
for i := 1; i <= 100; i++ {
digits := byte(2)
if i >= 100 {
digits = 3
} else if i < 10 {
digits = 1
}
buf.WriteString("D\x00\x00\x00")
buf.WriteByte(10 + digits)
buf.WriteString("\x00\x01\x00\x00\x00")
buf.WriteByte(digits)
buf.WriteString(strconv.Itoa(i))
}
return buf.String()
}()
func BenchmarkMockSelectSeries(b *testing.B) {
b.StopTimer()
var response = "1\x00\x00\x00\x04" +
"t\x00\x00\x00\x06\x00\x00" +
"T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" +
"Z\x00\x00\x00\x05I" +
"2\x00\x00\x00\x04" +
seriesRowData +
"C\x00\x00\x00\x0fSELECT 100\x00" +
"Z\x00\x00\x00\x05I" +
"3\x00\x00\x00\x04" +
"Z\x00\x00\x00\x05I"
c := fakeConn(response, 0)
b.StartTimer()
for i := 0; i < b.N; i++ {
benchMockQuery(b, c, selectSeriesQuery)
}
}
func benchMockQuery(b *testing.B, c *conn, query string) {
stmt, err := c.Prepare(query)
if err != nil {
b.Fatal(err)
}
defer stmt.Close()
rows, err := stmt.Query(nil)
if err != nil {
b.Fatal(err)
}
defer rows.Close()
var dest [1]driver.Value
for {
if err := rows.Next(dest[:]); err != nil {
if err == io.EOF {
break
}
b.Fatal(err)
}
}
}
func BenchmarkPreparedSelectString(b *testing.B) {
var result string
benchPreparedQuery(b, selectStringQuery, &result)
}
func BenchmarkPreparedSelectSeries(b *testing.B) {
var result int
benchPreparedQuery(b, selectSeriesQuery, &result)
}
func benchPreparedQuery(b *testing.B, query string, result interface{}) {
b.StopTimer()
db := openTestConn(b)
defer db.Close()
stmt, err := db.Prepare(query)
if err != nil {
b.Fatal(err)
}
defer stmt.Close()
b.StartTimer()
for i := 0; i < b.N; i++ {
benchPreparedQueryLoop(b, db, stmt, result)
}
}
func benchPreparedQueryLoop(b *testing.B, db *sql.DB, stmt *sql.Stmt, result interface{}) {
rows, err := stmt.Query()
if err != nil {
b.Fatal(err)
}
if !rows.Next() {
rows.Close()
b.Fatal("no rows")
}
defer rows.Close()
for rows.Next() {
err = rows.Scan(&result)
if err != nil {
b.Fatal("failed to scan")
}
}
}
// See the comment for BenchmarkMockSelectString.
func BenchmarkMockPreparedSelectString(b *testing.B) {
b.StopTimer()
const parseResponse = "1\x00\x00\x00\x04" +
"t\x00\x00\x00\x06\x00\x00" +
"T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" +
"Z\x00\x00\x00\x05I"
const responses = parseResponse +
"2\x00\x00\x00\x04" +
"D\x00\x00\x00n\x00\x01\x00\x00\x00d0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
"C\x00\x00\x00\rSELECT 1\x00" +
"Z\x00\x00\x00\x05I"
c := fakeConn(responses, len(parseResponse))
stmt, err := c.Prepare(selectStringQuery)
if err != nil {
b.Fatal(err)
}
b.StartTimer()
for i := 0; i < b.N; i++ {
benchPreparedMockQuery(b, c, stmt)
}
}
func BenchmarkMockPreparedSelectSeries(b *testing.B) {
b.StopTimer()
const parseResponse = "1\x00\x00\x00\x04" +
"t\x00\x00\x00\x06\x00\x00" +
"T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" +
"Z\x00\x00\x00\x05I"
var responses = parseResponse +
"2\x00\x00\x00\x04" +
seriesRowData +
"C\x00\x00\x00\x0fSELECT 100\x00" +
"Z\x00\x00\x00\x05I"
c := fakeConn(responses, len(parseResponse))
stmt, err := c.Prepare(selectSeriesQuery)
if err != nil {
b.Fatal(err)
}
b.StartTimer()
for i := 0; i < b.N; i++ {
benchPreparedMockQuery(b, c, stmt)
}
}
func benchPreparedMockQuery(b *testing.B, c *conn, stmt driver.Stmt) {
rows, err := stmt.Query(nil)
if err != nil {
b.Fatal(err)
}
defer rows.Close()
var dest [1]driver.Value
for {
if err := rows.Next(dest[:]); err != nil {
if err == io.EOF {
break
}
b.Fatal(err)
}
}
}
func BenchmarkEncodeInt64(b *testing.B) {
for i := 0; i < b.N; i++ {
encode(&parameterStatus{}, int64(1234), oid.T_int8)
}
}
func BenchmarkEncodeFloat64(b *testing.B) {
for i := 0; i < b.N; i++ {
encode(&parameterStatus{}, 3.14159, oid.T_float8)
}
}
var testByteString = []byte("abcdefghijklmnopqrstuvwxyz")
func BenchmarkEncodeByteaHex(b *testing.B) {
for i := 0; i < b.N; i++ {
encode(&parameterStatus{serverVersion: 90000}, testByteString, oid.T_bytea)
}
}
func BenchmarkEncodeByteaEscape(b *testing.B) {
for i := 0; i < b.N; i++ {
encode(&parameterStatus{serverVersion: 84000}, testByteString, oid.T_bytea)
}
}
func BenchmarkEncodeBool(b *testing.B) {
for i := 0; i < b.N; i++ {
encode(&parameterStatus{}, true, oid.T_bool)
}
}
var testTimestamptz = time.Date(2001, time.January, 1, 0, 0, 0, 0, time.Local)
func BenchmarkEncodeTimestamptz(b *testing.B) {
for i := 0; i < b.N; i++ {
encode(&parameterStatus{}, testTimestamptz, oid.T_timestamptz)
}
}
var testIntBytes = []byte("1234")
func BenchmarkDecodeInt64(b *testing.B) {
for i := 0; i < b.N; i++ {
decode(&parameterStatus{}, testIntBytes, oid.T_int8)
}
}
var testFloatBytes = []byte("3.14159")
func BenchmarkDecodeFloat64(b *testing.B) {
for i := 0; i < b.N; i++ {
decode(&parameterStatus{}, testFloatBytes, oid.T_float8)
}
}
var testBoolBytes = []byte{'t'}
func BenchmarkDecodeBool(b *testing.B) {
for i := 0; i < b.N; i++ {
decode(&parameterStatus{}, testBoolBytes, oid.T_bool)
}
}
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)
}
}
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)
}
}
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()
}
}

30
vendor/github.com/lib/pq/buf.go generated vendored
View File

@@ -3,6 +3,7 @@ package pq
import (
"bytes"
"encoding/binary"
"github.com/lib/pq/oid"
)
@@ -20,6 +21,7 @@ func (b *readBuf) oid() (n oid.Oid) {
return
}
// N.B: this is actually an unsigned 16-bit integer, unlike int32
func (b *readBuf) int16() (n int) {
n = int(binary.BigEndian.Uint16(*b))
*b = (*b)[2:]
@@ -46,28 +48,44 @@ func (b *readBuf) byte() byte {
return b.next(1)[0]
}
type writeBuf []byte
type writeBuf struct {
buf []byte
pos int
}
func (b *writeBuf) int32(n int) {
x := make([]byte, 4)
binary.BigEndian.PutUint32(x, uint32(n))
*b = append(*b, x...)
b.buf = append(b.buf, x...)
}
func (b *writeBuf) int16(n int) {
x := make([]byte, 2)
binary.BigEndian.PutUint16(x, uint16(n))
*b = append(*b, x...)
b.buf = append(b.buf, x...)
}
func (b *writeBuf) string(s string) {
*b = append(*b, (s + "\000")...)
b.buf = append(b.buf, (s + "\000")...)
}
func (b *writeBuf) byte(c byte) {
*b = append(*b, c)
b.buf = append(b.buf, c)
}
func (b *writeBuf) bytes(v []byte) {
*b = append(*b, v...)
b.buf = append(b.buf, v...)
}
func (b *writeBuf) wrap() []byte {
p := b.buf[b.pos:]
binary.BigEndian.PutUint32(p, uint32(len(p)))
return b.buf
}
func (b *writeBuf) next(c byte) {
p := b.buf[b.pos:]
binary.BigEndian.PutUint32(p, uint32(len(p)))
b.pos = len(b.buf) + 1
b.buf = append(b.buf, c, 0, 0, 0, 0)
}

1098
vendor/github.com/lib/pq/conn.go generated vendored

File diff suppressed because it is too large Load Diff

128
vendor/github.com/lib/pq/conn_go18.go generated vendored Normal file
View File

@@ -0,0 +1,128 @@
// +build go1.8
package pq
import (
"context"
"database/sql"
"database/sql/driver"
"fmt"
"io"
"io/ioutil"
)
// Implement the "QueryerContext" interface
func (cn *conn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
list := make([]driver.Value, len(args))
for i, nv := range args {
list[i] = nv.Value
}
finish := cn.watchCancel(ctx)
r, err := cn.query(query, list)
if err != nil {
if finish != nil {
finish()
}
return nil, err
}
r.finish = finish
return r, nil
}
// Implement the "ExecerContext" interface
func (cn *conn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
list := make([]driver.Value, len(args))
for i, nv := range args {
list[i] = nv.Value
}
if finish := cn.watchCancel(ctx); finish != nil {
defer finish()
}
return cn.Exec(query, list)
}
// Implement the "ConnBeginTx" interface
func (cn *conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
var mode string
switch sql.IsolationLevel(opts.Isolation) {
case sql.LevelDefault:
// Don't touch mode: use the server's default
case sql.LevelReadUncommitted:
mode = " ISOLATION LEVEL READ UNCOMMITTED"
case sql.LevelReadCommitted:
mode = " ISOLATION LEVEL READ COMMITTED"
case sql.LevelRepeatableRead:
mode = " ISOLATION LEVEL REPEATABLE READ"
case sql.LevelSerializable:
mode = " ISOLATION LEVEL SERIALIZABLE"
default:
return nil, fmt.Errorf("pq: isolation level not supported: %d", opts.Isolation)
}
if opts.ReadOnly {
mode += " READ ONLY"
} else {
mode += " READ WRITE"
}
tx, err := cn.begin(mode)
if err != nil {
return nil, err
}
cn.txnFinish = cn.watchCancel(ctx)
return tx, nil
}
func (cn *conn) watchCancel(ctx context.Context) func() {
if done := ctx.Done(); done != nil {
finished := make(chan struct{})
go func() {
select {
case <-done:
_ = cn.cancel()
finished <- struct{}{}
case <-finished:
}
}()
return func() {
select {
case <-finished:
case finished <- struct{}{}:
}
}
}
return nil
}
func (cn *conn) cancel() error {
c, err := dial(cn.dialer, cn.opts)
if err != nil {
return err
}
defer c.Close()
{
can := conn{
c: c,
}
can.ssl(cn.opts)
w := can.writeBuf(0)
w.int32(80877102) // cancel request code
w.int32(cn.processID)
w.int32(cn.secretKey)
if err := can.sendStartupPacket(w); err != nil {
return err
}
}
// Read until EOF to ensure that the server received the cancel.
{
_, err := io.Copy(ioutil.Discard, c)
return err
}
}

1286
vendor/github.com/lib/pq/conn_test.go generated vendored

File diff suppressed because it is too large Load Diff

40
vendor/github.com/lib/pq/copy.go generated vendored
View File

@@ -13,6 +13,7 @@ var (
errBinaryCopyNotSupported = errors.New("pq: only text format supported for COPY")
errCopyToNotSupported = errors.New("pq: COPY TO is not supported")
errCopyNotSupportedOutsideTxn = errors.New("pq: COPY is only allowed inside a transaction")
errCopyInProgress = errors.New("pq: COPY in progress")
)
// CopyIn creates a COPY FROM statement which can be prepared with
@@ -96,13 +97,13 @@ awaitCopyInResponse:
err = parseError(r)
case 'Z':
if err == nil {
cn.bad = true
ci.setBad()
errorf("unexpected ReadyForQuery in response to COPY")
}
cn.processReadyForQuery(r)
return nil, err
default:
cn.bad = true
ci.setBad()
errorf("unknown response for copy query: %q", t)
}
}
@@ -121,7 +122,7 @@ awaitCopyInResponse:
cn.processReadyForQuery(r)
return nil, err
default:
cn.bad = true
ci.setBad()
errorf("unknown response for CopyFail: %q", t)
}
}
@@ -142,7 +143,7 @@ func (ci *copyin) resploop() {
var r readBuf
t, err := ci.cn.recvMessage(&r)
if err != nil {
ci.cn.bad = true
ci.setBad()
ci.setError(err)
ci.done <- true
return
@@ -150,6 +151,8 @@ func (ci *copyin) resploop() {
switch t {
case 'C':
// complete
case 'N':
// NoticeResponse
case 'Z':
ci.cn.processReadyForQuery(&r)
ci.done <- true
@@ -158,7 +161,7 @@ func (ci *copyin) resploop() {
err := parseError(&r)
ci.setError(err)
default:
ci.cn.bad = true
ci.setBad()
ci.setError(fmt.Errorf("unknown response during CopyIn: %q", t))
ci.done <- true
return
@@ -166,6 +169,19 @@ func (ci *copyin) resploop() {
}
}
func (ci *copyin) setBad() {
ci.Lock()
ci.cn.bad = true
ci.Unlock()
}
func (ci *copyin) isBad() bool {
ci.Lock()
b := ci.cn.bad
ci.Unlock()
return b
}
func (ci *copyin) isErrorSet() bool {
ci.Lock()
isSet := (ci.err != nil)
@@ -203,7 +219,7 @@ func (ci *copyin) Exec(v []driver.Value) (r driver.Result, err error) {
return nil, errCopyInClosed
}
if ci.cn.bad {
if ci.isBad() {
return nil, driver.ErrBadConn
}
defer ci.cn.errRecover(&err)
@@ -213,9 +229,7 @@ func (ci *copyin) Exec(v []driver.Value) (r driver.Result, err error) {
}
if len(v) == 0 {
err = ci.Close()
ci.closed = true
return nil, err
return nil, ci.Close()
}
numValues := len(v)
@@ -238,11 +252,12 @@ func (ci *copyin) Exec(v []driver.Value) (r driver.Result, err error) {
}
func (ci *copyin) Close() (err error) {
if ci.closed {
return errCopyInClosed
if ci.closed { // Don't do anything, we're already closed
return nil
}
ci.closed = true
if ci.cn.bad {
if ci.isBad() {
return driver.ErrBadConn
}
defer ci.cn.errRecover(&err)
@@ -257,6 +272,7 @@ func (ci *copyin) Close() (err error) {
}
<-ci.done
ci.cn.inCopy = false
if ci.isErrorSet() {
err = ci.err

380
vendor/github.com/lib/pq/copy_test.go generated vendored
View File

@@ -1,380 +0,0 @@
package pq
import (
"bytes"
"database/sql"
"strings"
"testing"
)
func TestCopyInStmt(t *testing.T) {
var stmt string
stmt = CopyIn("table name")
if stmt != `COPY "table name" () FROM STDIN` {
t.Fatal(stmt)
}
stmt = CopyIn("table name", "column 1", "column 2")
if stmt != `COPY "table name" ("column 1", "column 2") FROM STDIN` {
t.Fatal(stmt)
}
stmt = CopyIn(`table " name """`, `co"lumn""`)
if stmt != `COPY "table "" name """"""" ("co""lumn""""") FROM STDIN` {
t.Fatal(stmt)
}
}
func TestCopyInSchemaStmt(t *testing.T) {
var stmt string
stmt = CopyInSchema("schema name", "table name")
if stmt != `COPY "schema name"."table name" () FROM STDIN` {
t.Fatal(stmt)
}
stmt = CopyInSchema("schema name", "table name", "column 1", "column 2")
if stmt != `COPY "schema name"."table name" ("column 1", "column 2") FROM STDIN` {
t.Fatal(stmt)
}
stmt = CopyInSchema(`schema " name """`, `table " name """`, `co"lumn""`)
if stmt != `COPY "schema "" name """"""".`+
`"table "" name """"""" ("co""lumn""""") FROM STDIN` {
t.Fatal(stmt)
}
}
func TestCopyInMultipleValues(t *testing.T) {
db := openTestConn(t)
defer db.Close()
txn, err := db.Begin()
if err != nil {
t.Fatal(err)
}
defer txn.Rollback()
_, err = txn.Exec("CREATE TEMP TABLE temp (a int, b varchar)")
if err != nil {
t.Fatal(err)
}
stmt, err := txn.Prepare(CopyIn("temp", "a", "b"))
if err != nil {
t.Fatal(err)
}
longString := strings.Repeat("#", 500)
for i := 0; i < 500; i++ {
_, err = stmt.Exec(int64(i), longString)
if err != nil {
t.Fatal(err)
}
}
_, err = stmt.Exec()
if err != nil {
t.Fatal(err)
}
err = stmt.Close()
if err != nil {
t.Fatal(err)
}
var num int
err = txn.QueryRow("SELECT COUNT(*) FROM temp").Scan(&num)
if err != nil {
t.Fatal(err)
}
if num != 500 {
t.Fatalf("expected 500 items, not %d", num)
}
}
func TestCopyInTypes(t *testing.T) {
db := openTestConn(t)
defer db.Close()
txn, err := db.Begin()
if err != nil {
t.Fatal(err)
}
defer txn.Rollback()
_, err = txn.Exec("CREATE TEMP TABLE temp (num INTEGER, text VARCHAR, blob BYTEA, nothing VARCHAR)")
if err != nil {
t.Fatal(err)
}
stmt, err := txn.Prepare(CopyIn("temp", "num", "text", "blob", "nothing"))
if err != nil {
t.Fatal(err)
}
_, err = stmt.Exec(int64(1234567890), "Héllö\n ☃!\r\t\\", []byte{0, 255, 9, 10, 13}, nil)
if err != nil {
t.Fatal(err)
}
_, err = stmt.Exec()
if err != nil {
t.Fatal(err)
}
err = stmt.Close()
if err != nil {
t.Fatal(err)
}
var num int
var text string
var blob []byte
var nothing sql.NullString
err = txn.QueryRow("SELECT * FROM temp").Scan(&num, &text, &blob, &nothing)
if err != nil {
t.Fatal(err)
}
if num != 1234567890 {
t.Fatal("unexpected result", num)
}
if text != "Héllö\n ☃!\r\t\\" {
t.Fatal("unexpected result", text)
}
if bytes.Compare(blob, []byte{0, 255, 9, 10, 13}) != 0 {
t.Fatal("unexpected result", blob)
}
if nothing.Valid {
t.Fatal("unexpected result", nothing.String)
}
}
func TestCopyInWrongType(t *testing.T) {
db := openTestConn(t)
defer db.Close()
txn, err := db.Begin()
if err != nil {
t.Fatal(err)
}
defer txn.Rollback()
_, err = txn.Exec("CREATE TEMP TABLE temp (num INTEGER)")
if err != nil {
t.Fatal(err)
}
stmt, err := txn.Prepare(CopyIn("temp", "num"))
if err != nil {
t.Fatal(err)
}
defer stmt.Close()
_, err = stmt.Exec("Héllö\n ☃!\r\t\\")
if err != nil {
t.Fatal(err)
}
_, err = stmt.Exec()
if err == nil {
t.Fatal("expected error")
}
if pge := err.(*Error); pge.Code.Name() != "invalid_text_representation" {
t.Fatalf("expected 'invalid input syntax for integer' error, got %s (%+v)", pge.Code.Name(), pge)
}
}
func TestCopyOutsideOfTxnError(t *testing.T) {
db := openTestConn(t)
defer db.Close()
_, err := db.Prepare(CopyIn("temp", "num"))
if err == nil {
t.Fatal("COPY outside of transaction did not return an error")
}
if err != errCopyNotSupportedOutsideTxn {
t.Fatalf("expected %s, got %s", err, err.Error())
}
}
func TestCopyInBinaryError(t *testing.T) {
db := openTestConn(t)
defer db.Close()
txn, err := db.Begin()
if err != nil {
t.Fatal(err)
}
defer txn.Rollback()
_, err = txn.Exec("CREATE TEMP TABLE temp (num INTEGER)")
if err != nil {
t.Fatal(err)
}
_, err = txn.Prepare("COPY temp (num) FROM STDIN WITH binary")
if err != errBinaryCopyNotSupported {
t.Fatalf("expected %s, got %+v", errBinaryCopyNotSupported, err)
}
// check that the protocol is in a valid state
err = txn.Rollback()
if err != nil {
t.Fatal(err)
}
}
func TestCopyFromError(t *testing.T) {
db := openTestConn(t)
defer db.Close()
txn, err := db.Begin()
if err != nil {
t.Fatal(err)
}
defer txn.Rollback()
_, err = txn.Exec("CREATE TEMP TABLE temp (num INTEGER)")
if err != nil {
t.Fatal(err)
}
_, err = txn.Prepare("COPY temp (num) TO STDOUT")
if err != errCopyToNotSupported {
t.Fatalf("expected %s, got %+v", errCopyToNotSupported, err)
}
// check that the protocol is in a valid state
err = txn.Rollback()
if err != nil {
t.Fatal(err)
}
}
func TestCopySyntaxError(t *testing.T) {
db := openTestConn(t)
defer db.Close()
txn, err := db.Begin()
if err != nil {
t.Fatal(err)
}
defer txn.Rollback()
_, err = txn.Prepare("COPY ")
if err == nil {
t.Fatal("expected error")
}
if pge := err.(*Error); pge.Code.Name() != "syntax_error" {
t.Fatalf("expected syntax error, got %s (%+v)", pge.Code.Name(), pge)
}
// check that the protocol is in a valid state
err = txn.Rollback()
if err != nil {
t.Fatal(err)
}
}
// Tests for connection errors in copyin.resploop()
func TestCopyRespLoopConnectionError(t *testing.T) {
db := openTestConn(t)
defer db.Close()
txn, err := db.Begin()
if err != nil {
t.Fatal(err)
}
defer txn.Rollback()
var pid int
err = txn.QueryRow("SELECT pg_backend_pid()").Scan(&pid)
if err != nil {
t.Fatal(err)
}
_, err = txn.Exec("CREATE TEMP TABLE temp (a int)")
if err != nil {
t.Fatal(err)
}
stmt, err := txn.Prepare(CopyIn("temp", "a"))
if err != nil {
t.Fatal(err)
}
_, err = db.Exec("SELECT pg_terminate_backend($1)", pid)
if err != nil {
t.Fatal(err)
}
// We have to try and send something over, since postgres won't process
// SIGTERMs while it's waiting for CopyData/CopyEnd messages; see
// tcop/postgres.c.
_, err = stmt.Exec(1)
if err != nil {
t.Fatal(err)
}
_, err = stmt.Exec()
if err == nil {
t.Fatalf("expected error")
}
pge, ok := err.(*Error)
if !ok {
t.Fatalf("expected *pq.Error, got %+#v", err)
} else if pge.Code.Name() != "admin_shutdown" {
t.Fatalf("expected admin_shutdown, got %s", pge.Code.Name())
}
err = stmt.Close()
if err != nil {
t.Fatal(err)
}
}
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)
}
}

28
vendor/github.com/lib/pq/doc.go generated vendored
View File

@@ -5,8 +5,9 @@ In most cases clients will use the database/sql package instead of
using this package directly. For example:
import (
_ "github.com/lib/pq"
"database/sql"
_ "github.com/lib/pq"
)
func main() {
@@ -85,9 +86,13 @@ variables not supported by pq are set, pq will panic during connection
establishment. Environment variables have a lower precedence than explicitly
provided connection parameters.
The pgpass mechanism as described in http://www.postgresql.org/docs/current/static/libpq-pgpass.html
is supported, but on Windows PGPASSFILE must be specified explicitly.
Queries
database/sql does not dictate any specific format for parameter
markers in query strings, and pq uses the Postgres-native ordinal markers,
as shown above. The same marker can be reused for the same parameter:
@@ -111,8 +116,29 @@ For more details on RETURNING, see the Postgres documentation:
For additional instructions on querying see the documentation for the database/sql package.
Data Types
Parameters pass through driver.DefaultParameterConverter before they are handled
by this package. When the binary_parameters connection option is enabled,
[]byte values are sent directly to the backend as data in binary format.
This package returns the following types for values from the PostgreSQL backend:
- integer types smallint, integer, and bigint are returned as int64
- floating-point types real and double precision are returned as float64
- character types char, varchar, and text are returned as string
- temporal types date, time, timetz, timestamp, and timestamptz are returned as time.Time
- the boolean type is returned as bool
- the bytea type is returned as []byte
All other types are returned directly from the backend as []byte values in text format.
Errors
pq may return errors of type *pq.Error which can be interrogated for error details:
if err, ok := err.(*pq.Error); ok {

306
vendor/github.com/lib/pq/encode.go generated vendored
View File

@@ -3,24 +3,34 @@ package pq
import (
"bytes"
"database/sql/driver"
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"github.com/lib/pq/oid"
"math"
"strconv"
"strings"
"sync"
"time"
"github.com/lib/pq/oid"
)
func binaryEncode(parameterStatus *parameterStatus, x interface{}) []byte {
switch v := x.(type) {
case []byte:
return v
default:
return encode(parameterStatus, x, oid.T_unknown)
}
}
func encode(parameterStatus *parameterStatus, x interface{}, pgtypOid oid.Oid) []byte {
switch v := x.(type) {
case int64:
return []byte(fmt.Sprintf("%d", v))
case float32:
return []byte(fmt.Sprintf("%.9f", v))
return strconv.AppendInt(nil, v, 10)
case float64:
return []byte(fmt.Sprintf("%.17f", v))
return strconv.AppendFloat(nil, v, 'f', -1, 64)
case []byte:
if pgtypOid == oid.T_bytea {
return encodeBytea(parameterStatus.serverVersion, v)
@@ -34,7 +44,7 @@ func encode(parameterStatus *parameterStatus, x interface{}, pgtypOid oid.Oid) [
return []byte(v)
case bool:
return []byte(fmt.Sprintf("%t", v))
return strconv.AppendBool(nil, v)
case time.Time:
return formatTs(v)
@@ -45,10 +55,51 @@ func encode(parameterStatus *parameterStatus, x interface{}, pgtypOid oid.Oid) [
panic("not reached")
}
func decode(parameterStatus *parameterStatus, s []byte, typ oid.Oid) interface{} {
func decode(parameterStatus *parameterStatus, s []byte, typ oid.Oid, f format) interface{} {
switch f {
case formatBinary:
return binaryDecode(parameterStatus, s, typ)
case formatText:
return textDecode(parameterStatus, s, typ)
default:
panic("not reached")
}
}
func binaryDecode(parameterStatus *parameterStatus, s []byte, typ oid.Oid) interface{} {
switch typ {
case oid.T_bytea:
return parseBytea(s)
return s
case oid.T_int8:
return int64(binary.BigEndian.Uint64(s))
case oid.T_int4:
return int64(int32(binary.BigEndian.Uint32(s)))
case oid.T_int2:
return int64(int16(binary.BigEndian.Uint16(s)))
case oid.T_uuid:
b, err := decodeUUIDBinary(s)
if err != nil {
panic(err)
}
return b
default:
errorf("don't know how to decode binary parameter of type %d", uint32(typ))
}
panic("not reached")
}
func textDecode(parameterStatus *parameterStatus, s []byte, typ oid.Oid) interface{} {
switch typ {
case oid.T_char, oid.T_varchar, oid.T_text:
return string(s)
case oid.T_bytea:
b, err := parseBytea(s)
if err != nil {
errorf("%s", err)
}
return b
case oid.T_timestamptz:
return parseTs(parameterStatus.currentLocation, string(s))
case oid.T_timestamp, oid.T_date:
@@ -59,7 +110,7 @@ func decode(parameterStatus *parameterStatus, s []byte, typ oid.Oid) interface{}
return mustParse("15:04:05-07", typ, s)
case oid.T_bool:
return s[0] == 't'
case oid.T_int8, oid.T_int2, oid.T_int4:
case oid.T_int8, oid.T_int4, oid.T_int2:
i, err := strconv.ParseInt(string(s), 10, 64)
if err != nil {
errorf("%s", err)
@@ -86,8 +137,6 @@ func appendEncodedText(parameterStatus *parameterStatus, buf []byte, x interface
switch v := x.(type) {
case int64:
return strconv.AppendInt(buf, v, 10)
case float32:
return strconv.AppendFloat(buf, float64(v), 'f', -1, 32)
case float64:
return strconv.AppendFloat(buf, v, 'f', -1, 64)
case []byte:
@@ -149,12 +198,6 @@ func appendEscapedText(buf []byte, text string) []byte {
func mustParse(f string, typ oid.Oid, s []byte) time.Time {
str := string(s)
// Special case until time.Parse bug is fixed:
// http://code.google.com/p/go/issues/detail?id=3487
if str[len(str)-2] == '.' {
str += "0"
}
// check for a 30-minute-offset timezone
if (typ == oid.T_timestamptz || typ == oid.T_timetz) &&
str[len(str)-3] == ':' {
@@ -167,16 +210,39 @@ func mustParse(f string, typ oid.Oid, s []byte) time.Time {
return t
}
func expect(str, char string, pos int) {
if c := str[pos : pos+1]; c != char {
errorf("expected '%v' at position %v; got '%v'", char, pos, c)
var errInvalidTimestamp = errors.New("invalid timestamp")
type timestampParser struct {
err error
}
func (p *timestampParser) expect(str string, char byte, pos int) {
if p.err != nil {
return
}
if pos+1 > len(str) {
p.err = errInvalidTimestamp
return
}
if c := str[pos]; c != char && p.err == nil {
p.err = fmt.Errorf("expected '%v' at position %v; got '%v'", char, pos, c)
}
}
func mustAtoi(str string) int {
result, err := strconv.Atoi(str)
func (p *timestampParser) mustAtoi(str string, begin int, end int) int {
if p.err != nil {
return 0
}
if begin < 0 || end < 0 || begin > end || end > len(str) {
p.err = errInvalidTimestamp
return 0
}
result, err := strconv.Atoi(str[begin:end])
if err != nil {
errorf("expected number; got '%v'", str)
if p.err == nil {
p.err = fmt.Errorf("expected number; got '%v'", str)
}
return 0
}
return result
}
@@ -191,7 +257,7 @@ type locationCache struct {
// about 5% speed could be gained by putting the cache in the connection and
// losing the mutex, at the cost of a small amount of memory and a somewhat
// significant increase in code complexity.
var globalLocationCache *locationCache = newLocationCache()
var globalLocationCache = newLocationCache()
func newLocationCache() *locationCache {
return &locationCache{cache: make(map[int]*time.Location)}
@@ -212,32 +278,106 @@ func (c *locationCache) getLocation(offset int) *time.Location {
return location
}
var infinityTsEnabled = false
var infinityTsNegative time.Time
var infinityTsPositive time.Time
const (
infinityTsEnabledAlready = "pq: infinity timestamp enabled already"
infinityTsNegativeMustBeSmaller = "pq: infinity timestamp: negative value must be smaller (before) than positive"
)
// EnableInfinityTs controls the handling of Postgres' "-infinity" and
// "infinity" "timestamp"s.
//
// If EnableInfinityTs is not called, "-infinity" and "infinity" will return
// []byte("-infinity") and []byte("infinity") respectively, and potentially
// cause error "sql: Scan error on column index 0: unsupported driver -> Scan
// pair: []uint8 -> *time.Time", when scanning into a time.Time value.
//
// Once EnableInfinityTs has been called, all connections created using this
// driver will decode Postgres' "-infinity" and "infinity" for "timestamp",
// "timestamp with time zone" and "date" types to the predefined minimum and
// maximum times, respectively. When encoding time.Time values, any time which
// equals or precedes the predefined minimum time will be encoded to
// "-infinity". Any values at or past the maximum time will similarly be
// encoded to "infinity".
//
// If EnableInfinityTs is called with negative >= positive, it will panic.
// Calling EnableInfinityTs after a connection has been established results in
// undefined behavior. If EnableInfinityTs is called more than once, it will
// panic.
func EnableInfinityTs(negative time.Time, positive time.Time) {
if infinityTsEnabled {
panic(infinityTsEnabledAlready)
}
if !negative.Before(positive) {
panic(infinityTsNegativeMustBeSmaller)
}
infinityTsEnabled = true
infinityTsNegative = negative
infinityTsPositive = positive
}
/*
* Testing might want to toggle infinityTsEnabled
*/
func disableInfinityTs() {
infinityTsEnabled = false
}
// This is a time function specific to the Postgres default DateStyle
// setting ("ISO, MDY"), the only one we currently support. This
// accounts for the discrepancies between the parsing available with
// time.Parse and the Postgres date formatting quirks.
func parseTs(currentLocation *time.Location, str string) (result time.Time) {
func parseTs(currentLocation *time.Location, str string) interface{} {
switch str {
case "-infinity":
if infinityTsEnabled {
return infinityTsNegative
}
return []byte(str)
case "infinity":
if infinityTsEnabled {
return infinityTsPositive
}
return []byte(str)
}
t, err := ParseTimestamp(currentLocation, str)
if err != nil {
panic(err)
}
return t
}
// ParseTimestamp parses Postgres' text format. It returns a time.Time in
// currentLocation iff that time's offset agrees with the offset sent from the
// Postgres server. Otherwise, ParseTimestamp returns a time.Time with the
// fixed offset offset provided by the Postgres server.
func ParseTimestamp(currentLocation *time.Location, str string) (time.Time, error) {
p := timestampParser{}
monSep := strings.IndexRune(str, '-')
// this is Gregorian year, not ISO Year
// In Gregorian system, the year 1 BC is followed by AD 1
year := mustAtoi(str[:monSep])
year := p.mustAtoi(str, 0, monSep)
daySep := monSep + 3
month := mustAtoi(str[monSep+1 : daySep])
expect(str, "-", daySep)
month := p.mustAtoi(str, monSep+1, daySep)
p.expect(str, '-', daySep)
timeSep := daySep + 3
day := mustAtoi(str[daySep+1 : timeSep])
day := p.mustAtoi(str, daySep+1, timeSep)
var hour, minute, second int
if len(str) > monSep+len("01-01")+1 {
expect(str, " ", timeSep)
p.expect(str, ' ', timeSep)
minSep := timeSep + 3
expect(str, ":", minSep)
hour = mustAtoi(str[timeSep+1 : minSep])
p.expect(str, ':', minSep)
hour = p.mustAtoi(str, timeSep+1, minSep)
secSep := minSep + 3
expect(str, ":", secSep)
minute = mustAtoi(str[minSep+1 : secSep])
p.expect(str, ':', secSep)
minute = p.mustAtoi(str, minSep+1, secSep)
secEnd := secSep + 3
second = mustAtoi(str[secSep+1 : secEnd])
second = p.mustAtoi(str, secSep+1, secEnd)
}
remainderIdx := monSep + len("01-01 00:00:00") + 1
// Three optional (but ordered) sections follow: the
@@ -248,49 +388,50 @@ func parseTs(currentLocation *time.Location, str string) (result time.Time) {
nanoSec := 0
tzOff := 0
if remainderIdx < len(str) && str[remainderIdx:remainderIdx+1] == "." {
if remainderIdx < len(str) && str[remainderIdx] == '.' {
fracStart := remainderIdx + 1
fracOff := strings.IndexAny(str[fracStart:], "-+ ")
if fracOff < 0 {
fracOff = len(str) - fracStart
}
fracSec := mustAtoi(str[fracStart : fracStart+fracOff])
fracSec := p.mustAtoi(str, fracStart, fracStart+fracOff)
nanoSec = fracSec * (1000000000 / int(math.Pow(10, float64(fracOff))))
remainderIdx += fracOff + 1
}
if tzStart := remainderIdx; tzStart < len(str) && (str[tzStart:tzStart+1] == "-" || str[tzStart:tzStart+1] == "+") {
if tzStart := remainderIdx; tzStart < len(str) && (str[tzStart] == '-' || str[tzStart] == '+') {
// time zone separator is always '-' or '+' (UTC is +00)
var tzSign int
if c := str[tzStart : tzStart+1]; c == "-" {
switch c := str[tzStart]; c {
case '-':
tzSign = -1
} else if c == "+" {
case '+':
tzSign = +1
} else {
errorf("expected '-' or '+' at position %v; got %v", tzStart, c)
default:
return time.Time{}, fmt.Errorf("expected '-' or '+' at position %v; got %v", tzStart, c)
}
tzHours := mustAtoi(str[tzStart+1 : tzStart+3])
tzHours := p.mustAtoi(str, tzStart+1, tzStart+3)
remainderIdx += 3
var tzMin, tzSec int
if tzStart+3 < len(str) && str[tzStart+3:tzStart+4] == ":" {
tzMin = mustAtoi(str[tzStart+4 : tzStart+6])
if remainderIdx < len(str) && str[remainderIdx] == ':' {
tzMin = p.mustAtoi(str, remainderIdx+1, remainderIdx+3)
remainderIdx += 3
}
if tzStart+6 < len(str) && str[tzStart+6:tzStart+7] == ":" {
tzSec = mustAtoi(str[tzStart+7 : tzStart+9])
if remainderIdx < len(str) && str[remainderIdx] == ':' {
tzSec = p.mustAtoi(str, remainderIdx+1, remainderIdx+3)
remainderIdx += 3
}
tzOff = tzSign * ((tzHours * 60 * 60) + (tzMin * 60) + tzSec)
}
var isoYear int
if remainderIdx < len(str) && str[remainderIdx:remainderIdx+3] == " BC" {
if remainderIdx+3 <= len(str) && str[remainderIdx:remainderIdx+3] == " BC" {
isoYear = 1 - year
remainderIdx += 3
} else {
isoYear = year
}
if remainderIdx < len(str) {
errorf("expected end of input, got %v", str[remainderIdx:])
return time.Time{}, fmt.Errorf("expected end of input, got %v", str[remainderIdx:])
}
t := time.Date(isoYear, time.Month(month), day,
hour, minute, second, nanoSec,
@@ -307,13 +448,26 @@ func parseTs(currentLocation *time.Location, str string) (result time.Time) {
}
}
return t
return t, p.err
}
// formatTs formats t as time.RFC3339Nano and appends time zone seconds if
// needed.
func formatTs(t time.Time) (b []byte) {
b = []byte(t.Format(time.RFC3339Nano))
// formatTs formats t into a format postgres understands.
func formatTs(t time.Time) []byte {
if infinityTsEnabled {
// t <= -infinity : ! (t > -infinity)
if !t.After(infinityTsNegative) {
return []byte("-infinity")
}
// t >= infinity : ! (!t < infinity)
if !t.Before(infinityTsPositive) {
return []byte("infinity")
}
}
return FormatTimestamp(t)
}
// FormatTimestamp formats t into Postgres' text format for timestamps.
func FormatTimestamp(t time.Time) []byte {
// Need to send dates before 0001 A.D. with " BC" suffix, instead of the
// minus sign preferred by Go.
// Beware, "0000" in ISO is "1 BC", "-0001" is "2 BC" and so on
@@ -323,38 +477,39 @@ func formatTs(t time.Time) (b []byte) {
t = t.AddDate((-t.Year())*2+1, 0, 0)
bc = true
}
b = []byte(t.Format(time.RFC3339Nano))
if bc {
b = append(b, " BC"...)
}
b := []byte(t.Format("2006-01-02 15:04:05.999999999Z07:00"))
_, offset := t.Zone()
offset = offset % 60
if offset == 0 {
return b
if offset != 0 {
// RFC3339Nano already printed the minus sign
if offset < 0 {
offset = -offset
}
b = append(b, ':')
if offset < 10 {
b = append(b, '0')
}
b = strconv.AppendInt(b, int64(offset), 10)
}
if offset < 0 {
offset = -offset
if bc {
b = append(b, " BC"...)
}
b = append(b, ':')
if offset < 10 {
b = append(b, '0')
}
return strconv.AppendInt(b, int64(offset), 10)
return b
}
// Parse a bytea value received from the server. Both "hex" and the legacy
// "escape" format are supported.
func parseBytea(s []byte) (result []byte) {
func parseBytea(s []byte) (result []byte, err error) {
if len(s) >= 2 && bytes.Equal(s[:2], []byte("\\x")) {
// bytea_output = hex
s = s[2:] // trim off leading "\\x"
result = make([]byte, hex.DecodedLen(len(s)))
_, err := hex.Decode(result, s)
if err != nil {
errorf("%s", err)
return nil, err
}
} else {
// bytea_output = escape
@@ -369,11 +524,11 @@ func parseBytea(s []byte) (result []byte) {
// '\\' followed by an octal number
if len(s) < 4 {
errorf("invalid bytea sequence %v", s)
return nil, fmt.Errorf("invalid bytea sequence %v", s)
}
r, err := strconv.ParseInt(string(s[1:4]), 8, 9)
if err != nil {
errorf("could not parse bytea value: %s", err.Error())
return nil, fmt.Errorf("could not parse bytea value: %s", err.Error())
}
result = append(result, byte(r))
s = s[4:]
@@ -391,13 +546,16 @@ func parseBytea(s []byte) (result []byte) {
}
}
return result
return result, nil
}
func encodeBytea(serverVersion int, v []byte) (result []byte) {
if serverVersion >= 90000 {
// Use the hex format if we know that the server supports it
result = []byte(fmt.Sprintf("\\x%x", v))
result = make([]byte, 2+hex.EncodedLen(len(v)))
result[0] = '\\'
result[1] = 'x'
hex.Encode(result[2:], v)
} else {
// .. or resort to "escape"
for _, b := range v {

View File

@@ -1,433 +0,0 @@
package pq
import (
"github.com/lib/pq/oid"
"bytes"
"fmt"
"testing"
"time"
)
func TestScanTimestamp(t *testing.T) {
var nt NullTime
tn := time.Now()
nt.Scan(tn)
if !nt.Valid {
t.Errorf("Expected Valid=false")
}
if nt.Time != tn {
t.Errorf("Time value mismatch")
}
}
func TestScanNilTimestamp(t *testing.T) {
var nt NullTime
nt.Scan(nil)
if nt.Valid {
t.Errorf("Expected Valid=false")
}
}
var timeTests = []struct {
str string
timeval time.Time
}{
{"22001-02-03", time.Date(22001, time.February, 3, 0, 0, 0, 0, time.FixedZone("", 0))},
{"2001-02-03", time.Date(2001, time.February, 3, 0, 0, 0, 0, time.FixedZone("", 0))},
{"2001-02-03 04:05:06", time.Date(2001, time.February, 3, 4, 5, 6, 0, time.FixedZone("", 0))},
{"2001-02-03 04:05:06.000001", time.Date(2001, time.February, 3, 4, 5, 6, 1000, time.FixedZone("", 0))},
{"2001-02-03 04:05:06.00001", time.Date(2001, time.February, 3, 4, 5, 6, 10000, time.FixedZone("", 0))},
{"2001-02-03 04:05:06.0001", time.Date(2001, time.February, 3, 4, 5, 6, 100000, time.FixedZone("", 0))},
{"2001-02-03 04:05:06.001", time.Date(2001, time.February, 3, 4, 5, 6, 1000000, time.FixedZone("", 0))},
{"2001-02-03 04:05:06.01", time.Date(2001, time.February, 3, 4, 5, 6, 10000000, time.FixedZone("", 0))},
{"2001-02-03 04:05:06.1", time.Date(2001, time.February, 3, 4, 5, 6, 100000000, time.FixedZone("", 0))},
{"2001-02-03 04:05:06.12", time.Date(2001, time.February, 3, 4, 5, 6, 120000000, time.FixedZone("", 0))},
{"2001-02-03 04:05:06.123", time.Date(2001, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))},
{"2001-02-03 04:05:06.1234", time.Date(2001, time.February, 3, 4, 5, 6, 123400000, time.FixedZone("", 0))},
{"2001-02-03 04:05:06.12345", time.Date(2001, time.February, 3, 4, 5, 6, 123450000, time.FixedZone("", 0))},
{"2001-02-03 04:05:06.123456", time.Date(2001, time.February, 3, 4, 5, 6, 123456000, time.FixedZone("", 0))},
{"2001-02-03 04:05:06.123-07", time.Date(2001, time.February, 3, 4, 5, 6, 123000000,
time.FixedZone("", -7*60*60))},
{"2001-02-03 04:05:06-07", time.Date(2001, time.February, 3, 4, 5, 6, 0,
time.FixedZone("", -7*60*60))},
{"2001-02-03 04:05:06-07:42", time.Date(2001, time.February, 3, 4, 5, 6, 0,
time.FixedZone("", -(7*60*60+42*60)))},
{"2001-02-03 04:05:06-07:30:09", time.Date(2001, time.February, 3, 4, 5, 6, 0,
time.FixedZone("", -(7*60*60+30*60+9)))},
{"2001-02-03 04:05:06+07", time.Date(2001, time.February, 3, 4, 5, 6, 0,
time.FixedZone("", 7*60*60))},
{"0011-02-03 04:05:06 BC", time.Date(-10, time.February, 3, 4, 5, 6, 0, time.FixedZone("", 0))},
{"0011-02-03 04:05:06.123 BC", time.Date(-10, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))},
{"0011-02-03 04:05:06.123-07 BC", time.Date(-10, time.February, 3, 4, 5, 6, 123000000,
time.FixedZone("", -7*60*60))},
{"0001-02-03 04:05:06.123", time.Date(1, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))},
{"0001-02-03 04:05:06.123 BC", time.Date(1, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0)).AddDate(-1, 0, 0)},
{"0001-02-03 04:05:06.123 BC", time.Date(0, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))},
{"0002-02-03 04:05:06.123 BC", time.Date(0, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0)).AddDate(-1, 0, 0)},
{"0002-02-03 04:05:06.123 BC", time.Date(-1, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))},
{"12345-02-03 04:05:06.1", time.Date(12345, time.February, 3, 4, 5, 6, 100000000, time.FixedZone("", 0))},
{"123456-02-03 04:05:06.1", time.Date(123456, time.February, 3, 4, 5, 6, 100000000, time.FixedZone("", 0))},
}
// Helper function for the two tests below
func tryParse(str string) (t time.Time, err error) {
defer func() {
if p := recover(); p != nil {
err = fmt.Errorf("%v", p)
return
}
}()
t = parseTs(nil, str)
return
}
// Test that parsing the string results in the expected value.
func TestParseTs(t *testing.T) {
for i, tt := range timeTests {
val, err := tryParse(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)
}
}
}
// 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 := tryParse(dbstr)
if err != nil {
t.Errorf("%d: could not parse value %q: %s", i, dbstr, err)
continue
}
val = val.In(tt.timeval.Location())
if val.String() != tt.timeval.String() {
t.Errorf("%d: expected to parse %q into %q; got %q", i, dbstr, tt.timeval, val)
}
}
}
var formatTimeTests = []struct {
time time.Time
expected string
}{
{time.Time{}, "0001-01-01T00:00:00Z"},
{time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "2001-02-03T04:05:06.123456789Z"},
{time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "2001-02-03T04:05:06.123456789+02:00"},
{time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "2001-02-03T04:05:06.123456789-06:00"},
{time.Date(1, time.January, 1, 0, 0, 0, 0, time.FixedZone("", 19*60+32)), "0001-01-01T00:00:00+00:19:32"},
{time.Date(2001, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -(7*60*60+30*60+9))), "2001-02-03T04:05:06-07:30:09"},
}
func TestFormatTs(t *testing.T) {
for i, tt := range formatTimeTests {
val := string(formatTs(tt.time))
if val != tt.expected {
t.Errorf("%d: incorrect time format %q, want %q", i, val, tt.expected)
}
}
}
func TestTimestampWithTimeZone(t *testing.T) {
db := openTestConn(t)
defer db.Close()
tx, err := db.Begin()
if err != nil {
t.Fatal(err)
}
defer tx.Rollback()
// try several different locations, all included in Go's zoneinfo.zip
for _, locName := range []string{
"UTC",
"America/Chicago",
"America/New_York",
"Australia/Darwin",
"Australia/Perth",
} {
loc, err := time.LoadLocation(locName)
if err != nil {
t.Logf("Could not load time zone %s - skipping", locName)
continue
}
// Postgres timestamps have a resolution of 1 microsecond, so don't
// use the full range of the Nanosecond argument
refTime := time.Date(2012, 11, 6, 10, 23, 42, 123456000, loc)
for _, pgTimeZone := range []string{"US/Eastern", "Australia/Darwin"} {
// Switch Postgres's timezone to test different output timestamp formats
_, err = tx.Exec(fmt.Sprintf("set time zone '%s'", pgTimeZone))
if err != nil {
t.Fatal(err)
}
var gotTime time.Time
row := tx.QueryRow("select $1::timestamp with time zone", refTime)
err = row.Scan(&gotTime)
if err != nil {
t.Fatal(err)
}
if !refTime.Equal(gotTime) {
t.Errorf("timestamps not equal: %s != %s", refTime, gotTime)
}
// check that the time zone is set correctly based on TimeZone
pgLoc, err := time.LoadLocation(pgTimeZone)
if err != nil {
t.Logf("Could not load time zone %s - skipping", pgLoc)
continue
}
translated := refTime.In(pgLoc)
if translated.String() != gotTime.String() {
t.Errorf("timestamps not equal: %s != %s", translated, gotTime)
}
}
}
}
func TestTimestampWithOutTimezone(t *testing.T) {
db := openTestConn(t)
defer db.Close()
test := func(ts, pgts string) {
r, err := db.Query("SELECT $1::timestamp", pgts)
if err != nil {
t.Fatalf("Could not run query: %v", err)
}
n := r.Next()
if n != true {
t.Fatal("Expected at least one row")
}
var result time.Time
err = r.Scan(&result)
if err != nil {
t.Fatalf("Did not expect error scanning row: %v", err)
}
expected, err := time.Parse(time.RFC3339, ts)
if err != nil {
t.Fatalf("Could not parse test time literal: %v", err)
}
if !result.Equal(expected) {
t.Fatalf("Expected time to match %v: got mismatch %v",
expected, result)
}
n = r.Next()
if n != false {
t.Fatal("Expected only one row")
}
}
test("2000-01-01T00:00:00Z", "2000-01-01T00:00:00")
// Test higher precision time
test("2013-01-04T20:14:58.80033Z", "2013-01-04 20:14:58.80033")
}
func 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 TestByteaToText(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 TestTextToBytea(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 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) {
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)
}
// 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 !bytes.Equal(data, expectedData) {
t.Errorf("unexpected bytea value %v for format %s; expected %v", data, f, expectedData)
}
}
testByteaOutputFormat("hex")
testByteaOutputFormat("escape")
}
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, float32(42.0000000001))
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\t42.0000000001\thello\\tworld\t\\\\x0080ff" {
t.Fatal(string(buf))
}
}
func TestAppendEscapedText(t *testing.T) {
if esc := appendEscapedText(nil, "hallo\tescape"); string(esc) != "hallo\\tescape" {
t.Fatal(string(esc))
}
if esc := appendEscapedText(nil, "hallo\\tescape\n"); string(esc) != "hallo\\\\tescape\\n" {
t.Fatal(string(esc))
}
if esc := appendEscapedText(nil, "\n\r\t\f"); string(esc) != "\\n\\r\\t\f" {
t.Fatal(string(esc))
}
}
func TestAppendEscapedTextExistingBuffer(t *testing.T) {
var buf []byte
buf = []byte("123\t")
if esc := appendEscapedText(buf, "hallo\tescape"); string(esc) != "123\thallo\\tescape" {
t.Fatal(string(esc))
}
buf = []byte("123\t")
if esc := appendEscapedText(buf, "hallo\\tescape\n"); string(esc) != "123\thallo\\\\tescape\\n" {
t.Fatal(string(esc))
}
buf = []byte("123\t")
if esc := appendEscapedText(buf, "\n\r\t\f"); string(esc) != "123\t\\n\\r\\t\f" {
t.Fatal(string(esc))
}
}
func BenchmarkAppendEscapedText(b *testing.B) {
longString := ""
for i := 0; i < 100; i++ {
longString += "123456789\n"
}
for i := 0; i < b.N; i++ {
appendEscapedText(nil, longString)
}
}
func BenchmarkAppendEscapedTextNoEscape(b *testing.B) {
longString := ""
for i := 0; i < 100; i++ {
longString += "1234567890"
}
for i := 0; i < b.N; i++ {
appendEscapedText(nil, longString)
}
}

13
vendor/github.com/lib/pq/error.go generated vendored
View File

@@ -459,6 +459,19 @@ func errorf(s string, args ...interface{}) {
panic(fmt.Errorf("pq: %s", fmt.Sprintf(s, args...)))
}
func errRecoverNoErrBadConn(err *error) {
e := recover()
if e == nil {
// Do nothing
return
}
var ok bool
*err, ok = e.(error)
if !ok {
*err = fmt.Errorf("pq: unexpected error: %#v", e)
}
}
func (c *conn) errRecover(err *error) {
e := recover()
switch v := e.(type) {

68
vendor/github.com/lib/pq/notify.go generated vendored
View File

@@ -6,7 +6,6 @@ package pq
import (
"errors"
"fmt"
"io"
"sync"
"sync/atomic"
"time"
@@ -63,14 +62,18 @@ type ListenerConn struct {
// Creates a new ListenerConn. Use NewListener instead.
func NewListenerConn(name string, notificationChan chan<- *Notification) (*ListenerConn, error) {
cn, err := Open(name)
return newDialListenerConn(defaultDialer{}, name, notificationChan)
}
func newDialListenerConn(d Dialer, name string, c chan<- *Notification) (*ListenerConn, error) {
cn, err := DialOpen(d, name)
if err != nil {
return nil, err
}
l := &ListenerConn{
cn: cn.(*conn),
notificationChan: notificationChan,
notificationChan: c,
connState: connStateIdle,
replyChan: make(chan message, 2),
}
@@ -87,12 +90,16 @@ func NewListenerConn(name string, notificationChan chan<- *Notification) (*Liste
// Returns an error if an unrecoverable error has occurred and the ListenerConn
// should be abandoned.
func (l *ListenerConn) acquireSenderLock() error {
l.connectionLock.Lock()
defer l.connectionLock.Unlock()
if l.err != nil {
return l.err
}
// we must acquire senderLock first to avoid deadlocks; see ExecSimpleQuery
l.senderLock.Lock()
l.connectionLock.Lock()
err := l.err
l.connectionLock.Unlock()
if err != nil {
l.senderLock.Unlock()
return err
}
return nil
}
@@ -125,7 +132,7 @@ func (l *ListenerConn) setState(newState int32) bool {
// away or should be discarded because we couldn't agree on the state with the
// server backend.
func (l *ListenerConn) listenerConnLoop() (err error) {
defer l.cn.errRecover(&err)
defer errRecoverNoErrBadConn(&err)
r := &readBuf{}
for {
@@ -140,6 +147,9 @@ func (l *ListenerConn) listenerConnLoop() (err error) {
// about the scratch buffer being overwritten.
l.notificationChan <- recvNotification(r)
case 'T', 'D':
// only used by tests; ignore
case 'E':
// We might receive an ErrorResponse even when not in a query; it
// is expected that the server will close the connection after
@@ -238,7 +248,7 @@ func (l *ListenerConn) Ping() error {
// The caller must be holding senderLock (see acquireSenderLock and
// releaseSenderLock).
func (l *ListenerConn) sendSimpleQuery(q string) (err error) {
defer l.cn.errRecover(&err)
defer errRecoverNoErrBadConn(&err)
// must set connection state before sending the query
if !l.setState(connStateExpectResponse) {
@@ -247,8 +257,10 @@ func (l *ListenerConn) sendSimpleQuery(q string) (err error) {
// Can't use l.cn.writeBuf here because it uses the scratch buffer which
// might get overwritten by listenerConnLoop.
data := writeBuf([]byte("Q\x00\x00\x00\x00"))
b := &data
b := &writeBuf{
buf: []byte("Q\x00\x00\x00\x00"),
pos: 1,
}
b.string(q)
l.cn.send(b)
@@ -277,13 +289,13 @@ func (l *ListenerConn) ExecSimpleQuery(q string) (executed bool, err error) {
// We can't know what state the protocol is in, so we need to abandon
// this connection.
l.connectionLock.Lock()
defer l.connectionLock.Unlock()
// Set the error pointer if it hasn't been set already; see
// listenerConnMain.
if l.err == nil {
l.err = err
}
l.cn.Close()
l.connectionLock.Unlock()
l.cn.c.Close()
return false, err
}
@@ -292,8 +304,11 @@ func (l *ListenerConn) ExecSimpleQuery(q string) (executed bool, err error) {
m, ok := <-l.replyChan
if !ok {
// We lost the connection to server, don't bother waiting for a
// a response.
return false, io.EOF
// a response. err should have been set already.
l.connectionLock.Lock()
err := l.err
l.connectionLock.Unlock()
return false, err
}
switch m.typ {
case 'Z':
@@ -320,12 +335,15 @@ func (l *ListenerConn) ExecSimpleQuery(q string) (executed bool, err error) {
func (l *ListenerConn) Close() error {
l.connectionLock.Lock()
defer l.connectionLock.Unlock()
if l.err != nil {
l.connectionLock.Unlock()
return errListenerConnClosed
}
l.err = errListenerConnClosed
return l.cn.Close()
l.connectionLock.Unlock()
// We can't send anything on the connection without holding senderLock.
// Simply close the net.Conn to wake up everyone operating on it.
return l.cn.c.Close()
}
// Err() returns the reason the connection was closed. It is not safe to call
@@ -377,6 +395,7 @@ type Listener struct {
name string
minReconnectInterval time.Duration
maxReconnectInterval time.Duration
dialer Dialer
eventCallback EventCallbackType
lock sync.Mutex
@@ -407,10 +426,21 @@ func NewListener(name string,
minReconnectInterval time.Duration,
maxReconnectInterval time.Duration,
eventCallback EventCallbackType) *Listener {
return NewDialListener(defaultDialer{}, name, minReconnectInterval, maxReconnectInterval, eventCallback)
}
// NewDialListener is like NewListener but it takes a Dialer.
func NewDialListener(d Dialer,
name string,
minReconnectInterval time.Duration,
maxReconnectInterval time.Duration,
eventCallback EventCallbackType) *Listener {
l := &Listener{
name: name,
minReconnectInterval: minReconnectInterval,
maxReconnectInterval: maxReconnectInterval,
dialer: d,
eventCallback: eventCallback,
channels: make(map[string]struct{}),
@@ -646,7 +676,7 @@ func (l *Listener) closed() bool {
func (l *Listener) connect() error {
notificationChan := make(chan *Notification, 32)
cn, err := NewListenerConn(l.name, notificationChan)
cn, err := newDialListenerConn(l.dialer, l.name, notificationChan)
if err != nil {
return err
}

View File

@@ -1,502 +0,0 @@
package pq
import (
"errors"
"fmt"
"io"
"os"
"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):
return fmt.Errorf("timeout")
}
}
func expectNoEvent(t *testing.T, eventch <-chan ListenerEventType) error {
select {
case e := <-eventch:
return fmt.Errorf("unexpected event %v", e)
case <-time.After(100 * time.Millisecond):
return nil
}
}
func newTestListenerConn(t *testing.T) (*ListenerConn, <-chan *Notification) {
datname := os.Getenv("PGDATABASE")
sslmode := os.Getenv("PGSSLMODE")
if datname == "" {
os.Setenv("PGDATABASE", "pqgotest")
}
if sslmode == "" {
os.Setenv("PGSSLMODE", "disable")
}
notificationChan := make(chan *Notification)
l, err := NewListenerConn("", notificationChan)
if err != nil {
t.Fatal(err)
}
return l, notificationChan
}
func TestNewListenerConn(t *testing.T) {
l, _ := newTestListenerConn(t)
defer l.Close()
}
func TestConnListen(t *testing.T) {
l, channel := newTestListenerConn(t)
defer l.Close()
db := openTestConn(t)
defer db.Close()
ok, err := l.Listen("notify_test")
if !ok || err != nil {
t.Fatal(err)
}
_, err = db.Exec("NOTIFY notify_test")
if err != nil {
t.Fatal(err)
}
err = expectNotification(t, channel, "notify_test", "")
if err != nil {
t.Fatal(err)
}
}
func TestConnUnlisten(t *testing.T) {
l, channel := newTestListenerConn(t)
defer l.Close()
db := openTestConn(t)
defer db.Close()
ok, err := l.Listen("notify_test")
if !ok || err != nil {
t.Fatal(err)
}
_, err = db.Exec("NOTIFY notify_test")
err = expectNotification(t, channel, "notify_test", "")
if err != nil {
t.Fatal(err)
}
ok, err = l.Unlisten("notify_test")
if !ok || err != nil {
t.Fatal(err)
}
_, err = db.Exec("NOTIFY notify_test")
if err != nil {
t.Fatal(err)
}
err = expectNoNotification(t, channel)
if err != nil {
t.Fatal(err)
}
}
func TestConnUnlistenAll(t *testing.T) {
l, channel := newTestListenerConn(t)
defer l.Close()
db := openTestConn(t)
defer db.Close()
ok, err := l.Listen("notify_test")
if !ok || err != nil {
t.Fatal(err)
}
_, err = db.Exec("NOTIFY notify_test")
err = expectNotification(t, channel, "notify_test", "")
if err != nil {
t.Fatal(err)
}
ok, err = l.UnlistenAll()
if !ok || err != nil {
t.Fatal(err)
}
_, err = db.Exec("NOTIFY notify_test")
if err != nil {
t.Fatal(err)
}
err = expectNoNotification(t, channel)
if err != nil {
t.Fatal(err)
}
}
func TestConnClose(t *testing.T) {
l, _ := newTestListenerConn(t)
defer l.Close()
err := l.Close()
if err != nil {
t.Fatal(err)
}
err = l.Close()
if err != errListenerConnClosed {
t.Fatalf("expected errListenerConnClosed; got %v", err)
}
}
func TestConnPing(t *testing.T) {
l, _ := newTestListenerConn(t)
defer l.Close()
err := l.Ping()
if err != nil {
t.Fatal(err)
}
err = l.Close()
if err != nil {
t.Fatal(err)
}
err = l.Ping()
if err != errListenerConnClosed {
t.Fatalf("expected errListenerConnClosed; got %v", err)
}
}
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)
}
}

158
vendor/github.com/lib/pq/ssl.go generated vendored Normal file
View File

@@ -0,0 +1,158 @@
package pq
import (
"crypto/tls"
"crypto/x509"
"io/ioutil"
"net"
"os"
"os/user"
"path/filepath"
)
// ssl generates a function to upgrade a net.Conn based on the "sslmode" and
// related settings. The function is nil when no upgrade should take place.
func ssl(o values) func(net.Conn) net.Conn {
verifyCaOnly := false
tlsConf := tls.Config{}
switch mode := o["sslmode"]; mode {
// "require" is the default.
case "", "require":
// We must skip TLS's own verification since it requires full
// verification since Go 1.3.
tlsConf.InsecureSkipVerify = true
// From http://www.postgresql.org/docs/current/static/libpq-ssl.html:
//
// Note: For backwards compatibility with earlier versions of
// PostgreSQL, if a root CA file exists, the behavior of
// sslmode=require will be the same as that of verify-ca, meaning the
// server certificate is validated against the CA. Relying on this
// behavior is discouraged, and applications that need certificate
// validation should always use verify-ca or verify-full.
if sslrootcert, ok := o["sslrootcert"]; ok {
if _, err := os.Stat(sslrootcert); err == nil {
verifyCaOnly = true
} else {
delete(o, "sslrootcert")
}
}
case "verify-ca":
// We must skip TLS's own verification since it requires full
// verification since Go 1.3.
tlsConf.InsecureSkipVerify = true
verifyCaOnly = true
case "verify-full":
tlsConf.ServerName = o["host"]
case "disable":
return nil
default:
errorf(`unsupported sslmode %q; only "require" (default), "verify-full", "verify-ca", and "disable" supported`, mode)
}
sslClientCertificates(&tlsConf, o)
sslCertificateAuthority(&tlsConf, o)
sslRenegotiation(&tlsConf)
return func(conn net.Conn) net.Conn {
client := tls.Client(conn, &tlsConf)
if verifyCaOnly {
sslVerifyCertificateAuthority(client, &tlsConf)
}
return client
}
}
// sslClientCertificates adds the certificate specified in the "sslcert" and
// "sslkey" settings, or if they aren't set, from the .postgresql directory
// in the user's home directory. The configured files must exist and have
// the correct permissions.
func sslClientCertificates(tlsConf *tls.Config, o values) {
// user.Current() might fail when cross-compiling. We have to ignore the
// error and continue without home directory defaults, since we wouldn't
// know from where to load them.
user, _ := user.Current()
// In libpq, the client certificate is only loaded if the setting is not blank.
//
// https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1036-L1037
sslcert := o["sslcert"]
if len(sslcert) == 0 && user != nil {
sslcert = filepath.Join(user.HomeDir, ".postgresql", "postgresql.crt")
}
// https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1045
if len(sslcert) == 0 {
return
}
// https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1050:L1054
if _, err := os.Stat(sslcert); os.IsNotExist(err) {
return
} else if err != nil {
panic(err)
}
// In libpq, the ssl key is only loaded if the setting is not blank.
//
// https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1123-L1222
sslkey := o["sslkey"]
if len(sslkey) == 0 && user != nil {
sslkey = filepath.Join(user.HomeDir, ".postgresql", "postgresql.key")
}
if len(sslkey) > 0 {
if err := sslKeyPermissions(sslkey); err != nil {
panic(err)
}
}
cert, err := tls.LoadX509KeyPair(sslcert, sslkey)
if err != nil {
panic(err)
}
tlsConf.Certificates = []tls.Certificate{cert}
}
// sslCertificateAuthority adds the RootCA specified in the "sslrootcert" setting.
func sslCertificateAuthority(tlsConf *tls.Config, o values) {
// In libpq, the root certificate is only loaded if the setting is not blank.
//
// https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L950-L951
if sslrootcert := o["sslrootcert"]; len(sslrootcert) > 0 {
tlsConf.RootCAs = x509.NewCertPool()
cert, err := ioutil.ReadFile(sslrootcert)
if err != nil {
panic(err)
}
if !tlsConf.RootCAs.AppendCertsFromPEM(cert) {
errorf("couldn't parse pem in sslrootcert")
}
}
}
// sslVerifyCertificateAuthority carries out a TLS handshake to the server and
// verifies the presented certificate against the CA, i.e. the one specified in
// sslrootcert or the system CA if sslrootcert was not specified.
func sslVerifyCertificateAuthority(client *tls.Conn, tlsConf *tls.Config) {
err := client.Handshake()
if err != nil {
panic(err)
}
certs := client.ConnectionState().PeerCertificates
opts := x509.VerifyOptions{
DNSName: client.ConnectionState().ServerName,
Intermediates: x509.NewCertPool(),
Roots: tlsConf.RootCAs,
}
for i, cert := range certs {
if i == 0 {
continue
}
opts.Intermediates.AddCert(cert)
}
_, err = certs[0].Verify(opts)
if err != nil {
panic(err)
}
}

14
vendor/github.com/lib/pq/ssl_go1.7.go generated vendored Normal file
View File

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

20
vendor/github.com/lib/pq/ssl_permissions.go generated vendored Normal file
View File

@@ -0,0 +1,20 @@
// +build !windows
package pq
import "os"
// sslKeyPermissions checks the permissions on user-supplied ssl key files.
// The key file should have very little access.
//
// libpq does not check key file permissions on Windows.
func sslKeyPermissions(sslkey string) error {
info, err := os.Stat(sslkey)
if err != nil {
return err
}
if info.Mode().Perm()&0077 != 0 {
return ErrSSLKeyHasWorldPermissions
}
return nil
}

8
vendor/github.com/lib/pq/ssl_renegotiation.go generated vendored Normal file
View File

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

226
vendor/github.com/lib/pq/ssl_test.go generated vendored
View File

@@ -1,226 +0,0 @@
package pq
// This file contains SSL tests
import (
_ "crypto/sha256"
"crypto/x509"
"database/sql"
"fmt"
"os"
"path/filepath"
"testing"
)
func maybeSkipSSLTests(t *testing.T) {
// Require some special variables for testing certificates
if os.Getenv("PQSSLCERTTEST_PATH") == "" {
t.Skip("PQSSLCERTTEST_PATH not set, skipping SSL tests")
}
value := os.Getenv("PQGOSSLTESTS")
if value == "" || value == "0" {
t.Skip("PQGOSSLTESTS not enabled, skipping SSL tests")
} else if value != "1" {
t.Fatalf("unexpected value %q for PQGOSSLTESTS", value)
}
}
func openSSLConn(t *testing.T, conninfo string) (*sql.DB, error) {
db, err := openTestConnConninfo(conninfo)
if err != nil {
// should never fail
t.Fatal(err)
}
// Do something with the connection to see whether it's working or not.
tx, err := db.Begin()
if err == nil {
return db, tx.Rollback()
}
_ = db.Close()
return nil, err
}
func checkSSLSetup(t *testing.T, conninfo string) {
db, err := openSSLConn(t, conninfo)
if err == nil {
db.Close()
t.Fatalf("expected error with conninfo=%q", conninfo)
}
}
// Connect over SSL and run a simple query to test the basics
func TestSSLConnection(t *testing.T) {
maybeSkipSSLTests(t)
// Environment sanity check: should fail without SSL
checkSSLSetup(t, "sslmode=disable user=pqgossltest")
db, err := openSSLConn(t, "sslmode=require user=pqgossltest")
if err != nil {
t.Fatal(err)
}
rows, err := db.Query("SELECT 1")
if err != nil {
t.Fatal(err)
}
rows.Close()
}
// Test sslmode=verify-full
func TestSSLVerifyFull(t *testing.T) {
maybeSkipSSLTests(t)
// Environment sanity check: should fail without SSL
checkSSLSetup(t, "sslmode=disable user=pqgossltest")
// Not OK according to the system CA
_, err := openSSLConn(t, "host=postgres sslmode=verify-full user=pqgossltest")
if err == nil {
t.Fatal("expected error")
}
_, ok := err.(x509.UnknownAuthorityError)
if !ok {
t.Fatalf("expected x509.UnknownAuthorityError, got %#+v", err)
}
rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt")
rootCert := "sslrootcert=" + rootCertPath + " "
// No match on Common Name
_, err = openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=verify-full user=pqgossltest")
if err == nil {
t.Fatal("expected error")
}
_, ok = err.(x509.HostnameError)
if !ok {
t.Fatalf("expected x509.HostnameError, got %#+v", err)
}
// OK
_, err = openSSLConn(t, rootCert+"host=postgres sslmode=verify-full user=pqgossltest")
if err != nil {
t.Fatal(err)
}
}
// Test sslmode=verify-ca
func TestSSLVerifyCA(t *testing.T) {
maybeSkipSSLTests(t)
// Environment sanity check: should fail without SSL
checkSSLSetup(t, "sslmode=disable user=pqgossltest")
// Not OK according to the system CA
_, err := openSSLConn(t, "host=postgres sslmode=verify-ca user=pqgossltest")
if err == nil {
t.Fatal("expected error")
}
_, ok := err.(x509.UnknownAuthorityError)
if !ok {
t.Fatalf("expected x509.UnknownAuthorityError, got %#+v", err)
}
rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt")
rootCert := "sslrootcert=" + rootCertPath + " "
// No match on Common Name, but that's OK
_, err = openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=verify-ca user=pqgossltest")
if err != nil {
t.Fatal(err)
}
// Everything OK
_, err = openSSLConn(t, rootCert+"host=postgres sslmode=verify-ca user=pqgossltest")
if err != nil {
t.Fatal(err)
}
}
func getCertConninfo(t *testing.T, source string) string {
var sslkey string
var sslcert string
certpath := os.Getenv("PQSSLCERTTEST_PATH")
switch source {
case "missingkey":
sslkey = "/tmp/filedoesnotexist"
sslcert = filepath.Join(certpath, "postgresql.crt")
case "missingcert":
sslkey = filepath.Join(certpath, "postgresql.key")
sslcert = "/tmp/filedoesnotexist"
case "certtwice":
sslkey = filepath.Join(certpath, "postgresql.crt")
sslcert = filepath.Join(certpath, "postgresql.crt")
case "valid":
sslkey = filepath.Join(certpath, "postgresql.key")
sslcert = filepath.Join(certpath, "postgresql.crt")
default:
t.Fatalf("invalid source %q", source)
}
return fmt.Sprintf("sslmode=require user=pqgosslcert sslkey=%s sslcert=%s", sslkey, sslcert)
}
// Authenticate over SSL using client certificates
func TestSSLClientCertificates(t *testing.T) {
maybeSkipSSLTests(t)
// Environment sanity check: should fail without SSL
checkSSLSetup(t, "sslmode=disable user=pqgossltest")
// Should also fail without a valid certificate
db, err := openSSLConn(t, "sslmode=require user=pqgosslcert")
if err == nil {
db.Close()
t.Fatal("expected error")
}
pge, ok := err.(*Error)
if !ok {
t.Fatal("expected pq.Error")
}
if pge.Code.Name() != "invalid_authorization_specification" {
t.Fatalf("unexpected error code %q", pge.Code.Name())
}
// Should work
db, err = openSSLConn(t, getCertConninfo(t, "valid"))
if err != nil {
t.Fatal(err)
}
rows, err := db.Query("SELECT 1")
if err != nil {
t.Fatal(err)
}
rows.Close()
}
// Test errors with ssl certificates
func TestSSLClientCertificatesMissingFiles(t *testing.T) {
maybeSkipSSLTests(t)
// Environment sanity check: should fail without SSL
checkSSLSetup(t, "sslmode=disable user=pqgossltest")
// Key missing, should fail
_, err := openSSLConn(t, getCertConninfo(t, "missingkey"))
if err == nil {
t.Fatal("expected error")
}
// should be a PathError
_, ok := err.(*os.PathError)
if !ok {
t.Fatalf("expected PathError, got %#+v", err)
}
// Cert missing, should fail
_, err = openSSLConn(t, getCertConninfo(t, "missingcert"))
if err == nil {
t.Fatal("expected error")
}
// should be a PathError
_, ok = err.(*os.PathError)
if !ok {
t.Fatalf("expected PathError, got %#+v", err)
}
// Key has wrong permissions, should fail
_, err = openSSLConn(t, getCertConninfo(t, "certtwice"))
if err == nil {
t.Fatal("expected error")
}
if err != ErrSSLKeyHasWorldPermissions {
t.Fatalf("expected ErrSSLKeyHasWorldPermissions, got %#+v", err)
}
}

9
vendor/github.com/lib/pq/ssl_windows.go generated vendored Normal file
View File

@@ -0,0 +1,9 @@
// +build windows
package pq
// sslKeyPermissions checks the permissions on user-supplied ssl key files.
// The key file should have very little access.
//
// libpq does not check key file permissions on Windows.
func sslKeyPermissions(string) error { return nil }

10
vendor/github.com/lib/pq/url.go generated vendored
View File

@@ -2,6 +2,7 @@ package pq
import (
"fmt"
"net"
nurl "net/url"
"sort"
"strings"
@@ -34,7 +35,7 @@ func ParseURL(url string) (string, error) {
return "", err
}
if u.Scheme != "postgres" {
if u.Scheme != "postgres" && u.Scheme != "postgresql" {
return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme)
}
@@ -54,12 +55,11 @@ func ParseURL(url string) (string, error) {
accrue("password", v)
}
i := strings.Index(u.Host, ":")
if i < 0 {
if host, port, err := net.SplitHostPort(u.Host); err != nil {
accrue("host", u.Host)
} else {
accrue("host", u.Host[:i])
accrue("port", u.Host[i+1:])
accrue("host", host)
accrue("port", port)
}
if u.Path != "" {

54
vendor/github.com/lib/pq/url_test.go generated vendored
View File

@@ -1,54 +0,0 @@
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 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)
}
}

View File

@@ -1,6 +1,6 @@
// Package pq is a pure Go Postgres driver for the database/sql package.
// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris rumprun
package pq

23
vendor/github.com/lib/pq/uuid.go generated vendored Normal file
View File

@@ -0,0 +1,23 @@
package pq
import (
"encoding/hex"
"fmt"
)
// decodeUUIDBinary interprets the binary format of a uuid, returning it in text format.
func decodeUUIDBinary(src []byte) ([]byte, error) {
if len(src) != 16 {
return nil, fmt.Errorf("pq: unable to decode uuid; bad length: %d", len(src))
}
dst := make([]byte, 36)
dst[8], dst[13], dst[18], dst[23] = '-', '-', '-', '-'
hex.Encode(dst[0:], src[0:4])
hex.Encode(dst[9:], src[4:6])
hex.Encode(dst[14:], src[6:8])
hex.Encode(dst[19:], src[8:10])
hex.Encode(dst[24:], src[10:16])
return dst, nil
}

6
vendor/vendor.json vendored
View File

@@ -448,6 +448,12 @@
"revision": "1bab8b35b6bb565f92cbc97939610af9369f942a",
"revisionTime": "2017-02-10T14:05:23Z"
},
{
"checksumSHA1": "ZAj/o03zG8Ui4mZ4XmzU4yyKC04=",
"path": "github.com/lib/pq",
"revision": "dd1fe2071026ce53f36a39112e645b4d4f5793a4",
"revisionTime": "2017-07-07T05:36:02Z"
},
{
"checksumSHA1": "8z32QKTSDusa4QQyunKE4kyYXZ8=",
"path": "github.com/patrickmn/go-cache",