pax_global_header00006660000000000000000000000064136550774630014532gustar00rootroot0000000000000052 comment=f3b22b2cad9e4567803b7ffd9853b8acda021438 golang-github-lib-pq-1.5.2/000077500000000000000000000000001365507746300154505ustar00rootroot00000000000000golang-github-lib-pq-1.5.2/.gitignore000066400000000000000000000000241365507746300174340ustar00rootroot00000000000000.db *.test *~ *.swp golang-github-lib-pq-1.5.2/.travis.sh000077500000000000000000000044041365507746300173770ustar00rootroot00000000000000#!/bin/bash set -eu client_configure() { sudo chmod 600 $PQSSLCERTTEST_PATH/postgresql.key } pgdg_repository() { local sourcelist='sources.list.d/postgresql.list' curl -sS 'https://www.postgresql.org/media/keys/ACCC4CF8.asc' | sudo apt-key add - echo deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main $PGVERSION | sudo tee "/etc/apt/$sourcelist" sudo apt-get -o Dir::Etc::sourcelist="$sourcelist" -o Dir::Etc::sourceparts='-' -o APT::Get::List-Cleanup='0' update } postgresql_configure() { sudo tee /etc/postgresql/$PGVERSION/main/pg_hba.conf > /dev/null <<-config local all all trust hostnossl all pqgossltest 127.0.0.1/32 reject hostnossl all pqgosslcert 127.0.0.1/32 reject hostssl all pqgossltest 127.0.0.1/32 trust hostssl all pqgosslcert 127.0.0.1/32 cert host all all 127.0.0.1/32 trust hostnossl all pqgossltest ::1/128 reject hostnossl all pqgosslcert ::1/128 reject hostssl all pqgossltest ::1/128 trust hostssl all pqgosslcert ::1/128 cert host all all ::1/128 trust config xargs sudo install -o postgres -g postgres -m 600 -t /var/lib/postgresql/$PGVERSION/main/ <<-certificates certs/root.crt certs/server.crt certs/server.key certificates sort -VCu <<-versions || $PGVERSION 9.2 versions sudo tee -a /etc/postgresql/$PGVERSION/main/postgresql.conf > /dev/null <<-config ssl_ca_file = 'root.crt' ssl_cert_file = 'server.crt' ssl_key_file = 'server.key' config echo 127.0.0.1 postgres | sudo tee -a /etc/hosts > /dev/null sudo service postgresql restart } postgresql_install() { xargs sudo apt-get -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confnew' install <<-packages postgresql-$PGVERSION postgresql-server-dev-$PGVERSION postgresql-contrib-$PGVERSION packages } postgresql_uninstall() { sudo service postgresql stop xargs sudo apt-get -y --purge remove <<-packages libpq-dev libpq5 postgresql postgresql-client-common postgresql-common packages sudo rm -rf /var/lib/postgresql } $1 golang-github-lib-pq-1.5.2/.travis.yml000066400000000000000000000017461365507746300175710ustar00rootroot00000000000000language: go go: - 1.13.x - 1.14.x - master sudo: true env: global: - PGUSER=postgres - PQGOSSLTESTS=1 - PQSSLCERTTEST_PATH=$PWD/certs - PGHOST=127.0.0.1 matrix: - PGVERSION=10 - PGVERSION=9.6 - PGVERSION=9.5 - PGVERSION=9.4 before_install: - ./.travis.sh postgresql_uninstall - ./.travis.sh pgdg_repository - ./.travis.sh postgresql_install - ./.travis.sh postgresql_configure - ./.travis.sh client_configure - go get golang.org/x/tools/cmd/goimports - go get golang.org/x/lint/golint - GO111MODULE=on go get honnef.co/go/tools/cmd/staticcheck@2020.1.3 before_script: - createdb pqgotest - createuser -DRS pqgossltest - createuser -DRS pqgosslcert script: - > goimports -d -e $(find -name '*.go') | awk '{ print } END { exit NR == 0 ? 0 : 1 }' - go vet ./... - staticcheck -go 1.13 ./... - golint ./... - PQTEST_BINARY_PARAMETERS=no go test -race -v ./... - PQTEST_BINARY_PARAMETERS=yes go test -race -v ./... golang-github-lib-pq-1.5.2/LICENSE.md000066400000000000000000000021261365507746300170550ustar00rootroot00000000000000Copyright (c) 2011-2013, 'pq' Contributors Portions Copyright (C) 2011 Blake Mizerany Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. golang-github-lib-pq-1.5.2/README.md000066400000000000000000000016751365507746300167400ustar00rootroot00000000000000# pq - A pure Go postgres driver for Go's database/sql package [![GoDoc](https://godoc.org/github.com/lib/pq?status.svg)](https://pkg.go.dev/github.com/lib/pq?tab=doc) ## Install go get github.com/lib/pq ## Features * SSL * Handles bad connections for `database/sql` * Scan `time.Time` correctly (i.e. `timestamp[tz]`, `time[tz]`, `date`) * Scan binary blobs correctly (i.e. `bytea`) * Package for `hstore` support * COPY FROM support * pq.ParseURL for converting urls to connection strings for sql.Open. * Many libpq compatible environment variables * Unix socket support * Notifications: `LISTEN`/`NOTIFY` * pgpass support ## Tests `go test` is used for testing. See [TESTS.md](TESTS.md) for more details. ## Status This package is effectively in maintenance mode and is not actively developed. Small patches and features are only rarely reviewed and merged. We recommend using [pgx](https://github.com/jackc/pgx) which is actively maintained. golang-github-lib-pq-1.5.2/TESTS.md000066400000000000000000000012221365507746300166710ustar00rootroot00000000000000# Tests ## Running Tests `go test` is used for testing. A running PostgreSQL server is required, with the ability to log in. The database to connect to test with is "pqgotest," on "localhost" but these can be overridden using [environment variables](https://www.postgresql.org/docs/9.3/static/libpq-envars.html). Example: PGHOST=/run/postgresql go test ## Benchmarks A benchmark suite can be run as part of the tests: go test -bench . ## Example setup (Docker) Run a postgres container: ``` docker run --expose 5432:5432 postgres ``` Run tests: ``` PGHOST=localhost PGPORT=5432 PGUSER=postgres PGSSLMODE=disable PGDATABASE=postgres go test ``` golang-github-lib-pq-1.5.2/array.go000066400000000000000000000422541365507746300171240ustar00rootroot00000000000000package 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 = "," 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 } golang-github-lib-pq-1.5.2/array_test.go000066400000000000000000001022141365507746300201540ustar00rootroot00000000000000package pq import ( "bytes" "database/sql" "database/sql/driver" "math/rand" "reflect" "strings" "testing" ) func TestParseArray(t *testing.T) { for _, tt := range []struct { input string delim string dims []int elems [][]byte }{ {`{}`, `,`, nil, [][]byte{}}, {`{NULL}`, `,`, []int{1}, [][]byte{nil}}, {`{a}`, `,`, []int{1}, [][]byte{{'a'}}}, {`{a,b}`, `,`, []int{2}, [][]byte{{'a'}, {'b'}}}, {`{{a,b}}`, `,`, []int{1, 2}, [][]byte{{'a'}, {'b'}}}, {`{{a},{b}}`, `,`, []int{2, 1}, [][]byte{{'a'}, {'b'}}}, {`{{{a,b},{c,d},{e,f}}}`, `,`, []int{1, 3, 2}, [][]byte{ {'a'}, {'b'}, {'c'}, {'d'}, {'e'}, {'f'}, }}, {`{""}`, `,`, []int{1}, [][]byte{{}}}, {`{","}`, `,`, []int{1}, [][]byte{{','}}}, {`{",",","}`, `,`, []int{2}, [][]byte{{','}, {','}}}, {`{{",",","}}`, `,`, []int{1, 2}, [][]byte{{','}, {','}}}, {`{{","},{","}}`, `,`, []int{2, 1}, [][]byte{{','}, {','}}}, {`{{{",",","},{",",","},{",",","}}}`, `,`, []int{1, 3, 2}, [][]byte{ {','}, {','}, {','}, {','}, {','}, {','}, }}, {`{"\"}"}`, `,`, []int{1}, [][]byte{{'"', '}'}}}, {`{"\"","\""}`, `,`, []int{2}, [][]byte{{'"'}, {'"'}}}, {`{{"\"","\""}}`, `,`, []int{1, 2}, [][]byte{{'"'}, {'"'}}}, {`{{"\""},{"\""}}`, `,`, []int{2, 1}, [][]byte{{'"'}, {'"'}}}, {`{{{"\"","\""},{"\"","\""},{"\"","\""}}}`, `,`, []int{1, 3, 2}, [][]byte{ {'"'}, {'"'}, {'"'}, {'"'}, {'"'}, {'"'}, }}, {`{axyzb}`, `xyz`, []int{2}, [][]byte{{'a'}, {'b'}}}, } { dims, elems, err := parseArray([]byte(tt.input), []byte(tt.delim)) if err != nil { t.Fatalf("Expected no error for %q, got %q", tt.input, err) } if !reflect.DeepEqual(dims, tt.dims) { t.Errorf("Expected %v dimensions for %q, got %v", tt.dims, tt.input, dims) } if !reflect.DeepEqual(elems, tt.elems) { t.Errorf("Expected %v elements for %q, got %v", tt.elems, tt.input, elems) } } } func TestParseArrayError(t *testing.T) { for _, tt := range []struct { input, err string }{ {``, "expected '{' at offset 0"}, {`x`, "expected '{' at offset 0"}, {`}`, "expected '{' at offset 0"}, {`{`, "expected '}' at offset 1"}, {`{{}`, "expected '}' at offset 3"}, {`{}}`, "unexpected '}' at offset 2"}, {`{,}`, "unexpected ',' at offset 1"}, {`{,x}`, "unexpected ',' at offset 1"}, {`{x,}`, "unexpected '}' at offset 3"}, {`{x,{`, "unexpected '{' at offset 3"}, {`{x},`, "unexpected ',' at offset 3"}, {`{x}}`, "unexpected '}' at offset 3"}, {`{{x}`, "expected '}' at offset 4"}, {`{""x}`, "unexpected 'x' at offset 3"}, {`{{a},{b,c}}`, "multidimensional arrays must have elements with matching dimensions"}, } { _, _, err := parseArray([]byte(tt.input), []byte{','}) if err == nil { t.Fatalf("Expected error for %q, got none", tt.input) } if !strings.Contains(err.Error(), tt.err) { t.Errorf("Expected error to contain %q for %q, got %q", tt.err, tt.input, err) } } } func TestArrayScanner(t *testing.T) { var s sql.Scanner = Array(&[]bool{}) if _, ok := s.(*BoolArray); !ok { t.Errorf("Expected *BoolArray, got %T", s) } s = Array(&[]float64{}) if _, ok := s.(*Float64Array); !ok { t.Errorf("Expected *Float64Array, got %T", s) } s = Array(&[]int64{}) if _, ok := s.(*Int64Array); !ok { t.Errorf("Expected *Int64Array, got %T", s) } s = Array(&[]string{}) if _, ok := s.(*StringArray); !ok { t.Errorf("Expected *StringArray, got %T", s) } for _, tt := range []interface{}{ &[]sql.Scanner{}, &[][]bool{}, &[][]float64{}, &[][]int64{}, &[][]string{}, } { s = Array(tt) if _, ok := s.(GenericArray); !ok { t.Errorf("Expected GenericArray for %T, got %T", tt, s) } } } func TestArrayValuer(t *testing.T) { var v driver.Valuer = Array([]bool{}) if _, ok := v.(*BoolArray); !ok { t.Errorf("Expected *BoolArray, got %T", v) } v = Array([]float64{}) if _, ok := v.(*Float64Array); !ok { t.Errorf("Expected *Float64Array, got %T", v) } v = Array([]int64{}) if _, ok := v.(*Int64Array); !ok { t.Errorf("Expected *Int64Array, got %T", v) } v = Array([]string{}) if _, ok := v.(*StringArray); !ok { t.Errorf("Expected *StringArray, got %T", v) } for _, tt := range []interface{}{ nil, []driver.Value{}, [][]bool{}, [][]float64{}, [][]int64{}, [][]string{}, } { v = Array(tt) if _, ok := v.(GenericArray); !ok { t.Errorf("Expected GenericArray for %T, got %T", tt, v) } } } func TestBoolArrayScanUnsupported(t *testing.T) { var arr BoolArray err := arr.Scan(1) if err == nil { t.Fatal("Expected error when scanning from int") } if !strings.Contains(err.Error(), "int to BoolArray") { t.Errorf("Expected type to be mentioned when scanning, got %q", err) } } func TestBoolArrayScanEmpty(t *testing.T) { var arr BoolArray err := arr.Scan(`{}`) if err != nil { t.Fatalf("Expected no error, got %v", err) } if arr == nil || len(arr) != 0 { t.Errorf("Expected empty, got %#v", arr) } } func TestBoolArrayScanNil(t *testing.T) { arr := BoolArray{true, true, true} err := arr.Scan(nil) if err != nil { t.Fatalf("Expected no error, got %v", err) } if arr != nil { t.Errorf("Expected nil, got %+v", arr) } } var BoolArrayStringTests = []struct { str string arr BoolArray }{ {`{}`, BoolArray{}}, {`{t}`, BoolArray{true}}, {`{f,t}`, BoolArray{false, true}}, } func TestBoolArrayScanBytes(t *testing.T) { for _, tt := range BoolArrayStringTests { bytes := []byte(tt.str) arr := BoolArray{true, true, true} err := arr.Scan(bytes) if err != nil { t.Fatalf("Expected no error for %q, got %v", bytes, err) } if !reflect.DeepEqual(arr, tt.arr) { t.Errorf("Expected %+v for %q, got %+v", tt.arr, bytes, arr) } } } func BenchmarkBoolArrayScanBytes(b *testing.B) { var a BoolArray var x interface{} = []byte(`{t,f,t,f,t,f,t,f,t,f}`) for i := 0; i < b.N; i++ { a = BoolArray{} a.Scan(x) } } func TestBoolArrayScanString(t *testing.T) { for _, tt := range BoolArrayStringTests { arr := BoolArray{true, true, true} err := arr.Scan(tt.str) if err != nil { t.Fatalf("Expected no error for %q, got %v", tt.str, err) } if !reflect.DeepEqual(arr, tt.arr) { t.Errorf("Expected %+v for %q, got %+v", tt.arr, tt.str, arr) } } } func TestBoolArrayScanError(t *testing.T) { for _, tt := range []struct { input, err string }{ {``, "unable to parse array"}, {`{`, "unable to parse array"}, {`{{t},{f}}`, "cannot convert ARRAY[2][1] to BoolArray"}, {`{NULL}`, `could not parse boolean array index 0: invalid boolean ""`}, {`{a}`, `could not parse boolean array index 0: invalid boolean "a"`}, {`{t,b}`, `could not parse boolean array index 1: invalid boolean "b"`}, {`{t,f,cd}`, `could not parse boolean array index 2: invalid boolean "cd"`}, } { arr := BoolArray{true, true, true} err := arr.Scan(tt.input) if err == nil { t.Fatalf("Expected error for %q, got none", tt.input) } if !strings.Contains(err.Error(), tt.err) { t.Errorf("Expected error to contain %q for %q, got %q", tt.err, tt.input, err) } if !reflect.DeepEqual(arr, BoolArray{true, true, true}) { t.Errorf("Expected destination not to change for %q, got %+v", tt.input, arr) } } } func TestBoolArrayValue(t *testing.T) { result, err := BoolArray(nil).Value() if err != nil { t.Fatalf("Expected no error for nil, got %v", err) } if result != nil { t.Errorf("Expected nil, got %q", result) } result, err = BoolArray([]bool{}).Value() if err != nil { t.Fatalf("Expected no error for empty, got %v", err) } if expected := `{}`; !reflect.DeepEqual(result, expected) { t.Errorf("Expected empty, got %q", result) } result, err = BoolArray([]bool{false, true, false}).Value() if err != nil { t.Fatalf("Expected no error, got %v", err) } if expected := `{f,t,f}`; !reflect.DeepEqual(result, expected) { t.Errorf("Expected %q, got %q", expected, result) } } func BenchmarkBoolArrayValue(b *testing.B) { rand.Seed(1) x := make([]bool, 10) for i := 0; i < len(x); i++ { x[i] = rand.Intn(2) == 0 } a := BoolArray(x) for i := 0; i < b.N; i++ { a.Value() } } func TestByteaArrayScanUnsupported(t *testing.T) { var arr ByteaArray err := arr.Scan(1) if err == nil { t.Fatal("Expected error when scanning from int") } if !strings.Contains(err.Error(), "int to ByteaArray") { t.Errorf("Expected type to be mentioned when scanning, got %q", err) } } func TestByteaArrayScanEmpty(t *testing.T) { var arr ByteaArray err := arr.Scan(`{}`) if err != nil { t.Fatalf("Expected no error, got %v", err) } if arr == nil || len(arr) != 0 { t.Errorf("Expected empty, got %#v", arr) } } func TestByteaArrayScanNil(t *testing.T) { arr := ByteaArray{{2}, {6}, {0, 0}} err := arr.Scan(nil) if err != nil { t.Fatalf("Expected no error, got %v", err) } if arr != nil { t.Errorf("Expected nil, got %+v", arr) } } var ByteaArrayStringTests = []struct { str string arr ByteaArray }{ {`{}`, ByteaArray{}}, {`{NULL}`, ByteaArray{nil}}, {`{"\\xfeff"}`, ByteaArray{{'\xFE', '\xFF'}}}, {`{"\\xdead","\\xbeef"}`, ByteaArray{{'\xDE', '\xAD'}, {'\xBE', '\xEF'}}}, } func TestByteaArrayScanBytes(t *testing.T) { for _, tt := range ByteaArrayStringTests { bytes := []byte(tt.str) arr := ByteaArray{{2}, {6}, {0, 0}} err := arr.Scan(bytes) if err != nil { t.Fatalf("Expected no error for %q, got %v", bytes, err) } if !reflect.DeepEqual(arr, tt.arr) { t.Errorf("Expected %+v for %q, got %+v", tt.arr, bytes, arr) } } } func BenchmarkByteaArrayScanBytes(b *testing.B) { var a ByteaArray var x interface{} = []byte(`{"\\xfe","\\xff","\\xdead","\\xbeef","\\xfe","\\xff","\\xdead","\\xbeef","\\xfe","\\xff"}`) for i := 0; i < b.N; i++ { a = ByteaArray{} a.Scan(x) } } func TestByteaArrayScanString(t *testing.T) { for _, tt := range ByteaArrayStringTests { arr := ByteaArray{{2}, {6}, {0, 0}} err := arr.Scan(tt.str) if err != nil { t.Fatalf("Expected no error for %q, got %v", tt.str, err) } if !reflect.DeepEqual(arr, tt.arr) { t.Errorf("Expected %+v for %q, got %+v", tt.arr, tt.str, arr) } } } func TestByteaArrayScanError(t *testing.T) { for _, tt := range []struct { input, err string }{ {``, "unable to parse array"}, {`{`, "unable to parse array"}, {`{{"\\xfeff"},{"\\xbeef"}}`, "cannot convert ARRAY[2][1] to ByteaArray"}, {`{"\\abc"}`, "could not parse bytea array index 0: could not parse bytea value"}, } { arr := ByteaArray{{2}, {6}, {0, 0}} err := arr.Scan(tt.input) if err == nil { t.Fatalf("Expected error for %q, got none", tt.input) } if !strings.Contains(err.Error(), tt.err) { t.Errorf("Expected error to contain %q for %q, got %q", tt.err, tt.input, err) } if !reflect.DeepEqual(arr, ByteaArray{{2}, {6}, {0, 0}}) { t.Errorf("Expected destination not to change for %q, got %+v", tt.input, arr) } } } func TestByteaArrayValue(t *testing.T) { result, err := ByteaArray(nil).Value() if err != nil { t.Fatalf("Expected no error for nil, got %v", err) } if result != nil { t.Errorf("Expected nil, got %q", result) } result, err = ByteaArray([][]byte{}).Value() if err != nil { t.Fatalf("Expected no error for empty, got %v", err) } if expected := `{}`; !reflect.DeepEqual(result, expected) { t.Errorf("Expected empty, got %q", result) } result, err = ByteaArray([][]byte{{'\xDE', '\xAD', '\xBE', '\xEF'}, {'\xFE', '\xFF'}, {}}).Value() if err != nil { t.Fatalf("Expected no error, got %v", err) } if expected := `{"\\xdeadbeef","\\xfeff","\\x"}`; !reflect.DeepEqual(result, expected) { t.Errorf("Expected %q, got %q", expected, result) } } func BenchmarkByteaArrayValue(b *testing.B) { rand.Seed(1) x := make([][]byte, 10) for i := 0; i < len(x); i++ { x[i] = make([]byte, len(x)) for j := 0; j < len(x); j++ { x[i][j] = byte(rand.Int()) } } a := ByteaArray(x) for i := 0; i < b.N; i++ { a.Value() } } func TestFloat64ArrayScanUnsupported(t *testing.T) { var arr Float64Array err := arr.Scan(true) if err == nil { t.Fatal("Expected error when scanning from bool") } if !strings.Contains(err.Error(), "bool to Float64Array") { t.Errorf("Expected type to be mentioned when scanning, got %q", err) } } func TestFloat64ArrayScanEmpty(t *testing.T) { var arr Float64Array err := arr.Scan(`{}`) if err != nil { t.Fatalf("Expected no error, got %v", err) } if arr == nil || len(arr) != 0 { t.Errorf("Expected empty, got %#v", arr) } } func TestFloat64ArrayScanNil(t *testing.T) { arr := Float64Array{5, 5, 5} err := arr.Scan(nil) if err != nil { t.Fatalf("Expected no error, got %v", err) } if arr != nil { t.Errorf("Expected nil, got %+v", arr) } } var Float64ArrayStringTests = []struct { str string arr Float64Array }{ {`{}`, Float64Array{}}, {`{1.2}`, Float64Array{1.2}}, {`{3.456,7.89}`, Float64Array{3.456, 7.89}}, {`{3,1,2}`, Float64Array{3, 1, 2}}, } func TestFloat64ArrayScanBytes(t *testing.T) { for _, tt := range Float64ArrayStringTests { bytes := []byte(tt.str) arr := Float64Array{5, 5, 5} err := arr.Scan(bytes) if err != nil { t.Fatalf("Expected no error for %q, got %v", bytes, err) } if !reflect.DeepEqual(arr, tt.arr) { t.Errorf("Expected %+v for %q, got %+v", tt.arr, bytes, arr) } } } func BenchmarkFloat64ArrayScanBytes(b *testing.B) { var a Float64Array var x interface{} = []byte(`{1.2,3.4,5.6,7.8,9.01,2.34,5.67,8.90,1.234,5.678}`) for i := 0; i < b.N; i++ { a = Float64Array{} a.Scan(x) } } func TestFloat64ArrayScanString(t *testing.T) { for _, tt := range Float64ArrayStringTests { arr := Float64Array{5, 5, 5} err := arr.Scan(tt.str) if err != nil { t.Fatalf("Expected no error for %q, got %v", tt.str, err) } if !reflect.DeepEqual(arr, tt.arr) { t.Errorf("Expected %+v for %q, got %+v", tt.arr, tt.str, arr) } } } func TestFloat64ArrayScanError(t *testing.T) { for _, tt := range []struct { input, err string }{ {``, "unable to parse array"}, {`{`, "unable to parse array"}, {`{{5.6},{7.8}}`, "cannot convert ARRAY[2][1] to Float64Array"}, {`{NULL}`, "parsing array element index 0:"}, {`{a}`, "parsing array element index 0:"}, {`{5.6,a}`, "parsing array element index 1:"}, {`{5.6,7.8,a}`, "parsing array element index 2:"}, } { arr := Float64Array{5, 5, 5} err := arr.Scan(tt.input) if err == nil { t.Fatalf("Expected error for %q, got none", tt.input) } if !strings.Contains(err.Error(), tt.err) { t.Errorf("Expected error to contain %q for %q, got %q", tt.err, tt.input, err) } if !reflect.DeepEqual(arr, Float64Array{5, 5, 5}) { t.Errorf("Expected destination not to change for %q, got %+v", tt.input, arr) } } } func TestFloat64ArrayValue(t *testing.T) { result, err := Float64Array(nil).Value() if err != nil { t.Fatalf("Expected no error for nil, got %v", err) } if result != nil { t.Errorf("Expected nil, got %q", result) } result, err = Float64Array([]float64{}).Value() if err != nil { t.Fatalf("Expected no error for empty, got %v", err) } if expected := `{}`; !reflect.DeepEqual(result, expected) { t.Errorf("Expected empty, got %q", result) } result, err = Float64Array([]float64{1.2, 3.4, 5.6}).Value() if err != nil { t.Fatalf("Expected no error, got %v", err) } if expected := `{1.2,3.4,5.6}`; !reflect.DeepEqual(result, expected) { t.Errorf("Expected %q, got %q", expected, result) } } func BenchmarkFloat64ArrayValue(b *testing.B) { rand.Seed(1) x := make([]float64, 10) for i := 0; i < len(x); i++ { x[i] = rand.NormFloat64() } a := Float64Array(x) for i := 0; i < b.N; i++ { a.Value() } } func TestInt64ArrayScanUnsupported(t *testing.T) { var arr Int64Array err := arr.Scan(true) if err == nil { t.Fatal("Expected error when scanning from bool") } if !strings.Contains(err.Error(), "bool to Int64Array") { t.Errorf("Expected type to be mentioned when scanning, got %q", err) } } func TestInt64ArrayScanEmpty(t *testing.T) { var arr Int64Array err := arr.Scan(`{}`) if err != nil { t.Fatalf("Expected no error, got %v", err) } if arr == nil || len(arr) != 0 { t.Errorf("Expected empty, got %#v", arr) } } func TestInt64ArrayScanNil(t *testing.T) { arr := Int64Array{5, 5, 5} err := arr.Scan(nil) if err != nil { t.Fatalf("Expected no error, got %v", err) } if arr != nil { t.Errorf("Expected nil, got %+v", arr) } } var Int64ArrayStringTests = []struct { str string arr Int64Array }{ {`{}`, Int64Array{}}, {`{12}`, Int64Array{12}}, {`{345,678}`, Int64Array{345, 678}}, } func TestInt64ArrayScanBytes(t *testing.T) { for _, tt := range Int64ArrayStringTests { bytes := []byte(tt.str) arr := Int64Array{5, 5, 5} err := arr.Scan(bytes) if err != nil { t.Fatalf("Expected no error for %q, got %v", bytes, err) } if !reflect.DeepEqual(arr, tt.arr) { t.Errorf("Expected %+v for %q, got %+v", tt.arr, bytes, arr) } } } func BenchmarkInt64ArrayScanBytes(b *testing.B) { var a Int64Array var x interface{} = []byte(`{1,2,3,4,5,6,7,8,9,0}`) for i := 0; i < b.N; i++ { a = Int64Array{} a.Scan(x) } } func TestInt64ArrayScanString(t *testing.T) { for _, tt := range Int64ArrayStringTests { arr := Int64Array{5, 5, 5} err := arr.Scan(tt.str) if err != nil { t.Fatalf("Expected no error for %q, got %v", tt.str, err) } if !reflect.DeepEqual(arr, tt.arr) { t.Errorf("Expected %+v for %q, got %+v", tt.arr, tt.str, arr) } } } func TestInt64ArrayScanError(t *testing.T) { for _, tt := range []struct { input, err string }{ {``, "unable to parse array"}, {`{`, "unable to parse array"}, {`{{5},{6}}`, "cannot convert ARRAY[2][1] to Int64Array"}, {`{NULL}`, "parsing array element index 0:"}, {`{a}`, "parsing array element index 0:"}, {`{5,a}`, "parsing array element index 1:"}, {`{5,6,a}`, "parsing array element index 2:"}, } { arr := Int64Array{5, 5, 5} err := arr.Scan(tt.input) if err == nil { t.Fatalf("Expected error for %q, got none", tt.input) } if !strings.Contains(err.Error(), tt.err) { t.Errorf("Expected error to contain %q for %q, got %q", tt.err, tt.input, err) } if !reflect.DeepEqual(arr, Int64Array{5, 5, 5}) { t.Errorf("Expected destination not to change for %q, got %+v", tt.input, arr) } } } func TestInt64ArrayValue(t *testing.T) { result, err := Int64Array(nil).Value() if err != nil { t.Fatalf("Expected no error for nil, got %v", err) } if result != nil { t.Errorf("Expected nil, got %q", result) } result, err = Int64Array([]int64{}).Value() if err != nil { t.Fatalf("Expected no error for empty, got %v", err) } if expected := `{}`; !reflect.DeepEqual(result, expected) { t.Errorf("Expected empty, got %q", result) } result, err = Int64Array([]int64{1, 2, 3}).Value() if err != nil { t.Fatalf("Expected no error, got %v", err) } if expected := `{1,2,3}`; !reflect.DeepEqual(result, expected) { t.Errorf("Expected %q, got %q", expected, result) } } func BenchmarkInt64ArrayValue(b *testing.B) { rand.Seed(1) x := make([]int64, 10) for i := 0; i < len(x); i++ { x[i] = rand.Int63() } a := Int64Array(x) for i := 0; i < b.N; i++ { a.Value() } } func TestStringArrayScanUnsupported(t *testing.T) { var arr StringArray err := arr.Scan(true) if err == nil { t.Fatal("Expected error when scanning from bool") } if !strings.Contains(err.Error(), "bool to StringArray") { t.Errorf("Expected type to be mentioned when scanning, got %q", err) } } func TestStringArrayScanEmpty(t *testing.T) { var arr StringArray err := arr.Scan(`{}`) if err != nil { t.Fatalf("Expected no error, got %v", err) } if arr == nil || len(arr) != 0 { t.Errorf("Expected empty, got %#v", arr) } } func TestStringArrayScanNil(t *testing.T) { arr := StringArray{"x", "x", "x"} err := arr.Scan(nil) if err != nil { t.Fatalf("Expected no error, got %v", err) } if arr != nil { t.Errorf("Expected nil, got %+v", arr) } } var StringArrayStringTests = []struct { str string arr StringArray }{ {`{}`, StringArray{}}, {`{t}`, StringArray{"t"}}, {`{f,1}`, StringArray{"f", "1"}}, {`{"a\\b","c d",","}`, StringArray{"a\\b", "c d", ","}}, } func TestStringArrayScanBytes(t *testing.T) { for _, tt := range StringArrayStringTests { bytes := []byte(tt.str) arr := StringArray{"x", "x", "x"} err := arr.Scan(bytes) if err != nil { t.Fatalf("Expected no error for %q, got %v", bytes, err) } if !reflect.DeepEqual(arr, tt.arr) { t.Errorf("Expected %+v for %q, got %+v", tt.arr, bytes, arr) } } } func BenchmarkStringArrayScanBytes(b *testing.B) { var a StringArray var x interface{} = []byte(`{a,b,c,d,e,f,g,h,i,j}`) var y interface{} = []byte(`{"\a","\b","\c","\d","\e","\f","\g","\h","\i","\j"}`) for i := 0; i < b.N; i++ { a = StringArray{} a.Scan(x) a = StringArray{} a.Scan(y) } } func TestStringArrayScanString(t *testing.T) { for _, tt := range StringArrayStringTests { arr := StringArray{"x", "x", "x"} err := arr.Scan(tt.str) if err != nil { t.Fatalf("Expected no error for %q, got %v", tt.str, err) } if !reflect.DeepEqual(arr, tt.arr) { t.Errorf("Expected %+v for %q, got %+v", tt.arr, tt.str, arr) } } } func TestStringArrayScanError(t *testing.T) { for _, tt := range []struct { input, err string }{ {``, "unable to parse array"}, {`{`, "unable to parse array"}, {`{{a},{b}}`, "cannot convert ARRAY[2][1] to StringArray"}, {`{NULL}`, "parsing array element index 0: cannot convert nil to string"}, {`{a,NULL}`, "parsing array element index 1: cannot convert nil to string"}, {`{a,b,NULL}`, "parsing array element index 2: cannot convert nil to string"}, } { arr := StringArray{"x", "x", "x"} err := arr.Scan(tt.input) if err == nil { t.Fatalf("Expected error for %q, got none", tt.input) } if !strings.Contains(err.Error(), tt.err) { t.Errorf("Expected error to contain %q for %q, got %q", tt.err, tt.input, err) } if !reflect.DeepEqual(arr, StringArray{"x", "x", "x"}) { t.Errorf("Expected destination not to change for %q, got %+v", tt.input, arr) } } } func TestStringArrayValue(t *testing.T) { result, err := StringArray(nil).Value() if err != nil { t.Fatalf("Expected no error for nil, got %v", err) } if result != nil { t.Errorf("Expected nil, got %q", result) } result, err = StringArray([]string{}).Value() if err != nil { t.Fatalf("Expected no error for empty, got %v", err) } if expected := `{}`; !reflect.DeepEqual(result, expected) { t.Errorf("Expected empty, got %q", result) } result, err = StringArray([]string{`a`, `\b`, `c"`, `d,e`}).Value() if err != nil { t.Fatalf("Expected no error, got %v", err) } if expected := `{"a","\\b","c\"","d,e"}`; !reflect.DeepEqual(result, expected) { t.Errorf("Expected %q, got %q", expected, result) } } func BenchmarkStringArrayValue(b *testing.B) { x := make([]string, 10) for i := 0; i < len(x); i++ { x[i] = strings.Repeat(`abc"def\ghi`, 5) } a := StringArray(x) for i := 0; i < b.N; i++ { a.Value() } } func TestGenericArrayScanUnsupported(t *testing.T) { var s string var ss []string var nsa [1]sql.NullString for _, tt := range []struct { src, dest interface{} err string }{ {nil, nil, "destination is not a pointer to array or slice"}, {nil, true, "destination bool is not a pointer to array or slice"}, {nil, &s, "destination *string is not a pointer to array or slice"}, {nil, ss, "destination []string is not a pointer to array or slice"}, {nil, &nsa, " to [1]sql.NullString"}, {true, &ss, "bool to []string"}, {`{{x}}`, &ss, "multidimensional ARRAY[1][1] is not implemented"}, {`{{x},{x}}`, &ss, "multidimensional ARRAY[2][1] is not implemented"}, {`{x}`, &ss, "scanning to string is not implemented"}, } { err := GenericArray{tt.dest}.Scan(tt.src) if err == nil { t.Fatalf("Expected error for [%#v %#v]", tt.src, tt.dest) } if !strings.Contains(err.Error(), tt.err) { t.Errorf("Expected error to contain %q for [%#v %#v], got %q", tt.err, tt.src, tt.dest, err) } } } func TestGenericArrayScanScannerArrayBytes(t *testing.T) { src, expected, nsa := []byte(`{NULL,abc,"\""}`), [3]sql.NullString{{}, {String: `abc`, Valid: true}, {String: `"`, Valid: true}}, [3]sql.NullString{{String: ``, Valid: true}, {}, {}} if err := (GenericArray{&nsa}).Scan(src); err != nil { t.Fatalf("Expected no error, got %v", err) } if !reflect.DeepEqual(nsa, expected) { t.Errorf("Expected %v, got %v", expected, nsa) } } func TestGenericArrayScanScannerArrayString(t *testing.T) { src, expected, nsa := `{NULL,"\"",xyz}`, [3]sql.NullString{{}, {String: `"`, Valid: true}, {String: `xyz`, Valid: true}}, [3]sql.NullString{{String: ``, Valid: true}, {}, {}} if err := (GenericArray{&nsa}).Scan(src); err != nil { t.Fatalf("Expected no error, got %v", err) } if !reflect.DeepEqual(nsa, expected) { t.Errorf("Expected %v, got %v", expected, nsa) } } func TestGenericArrayScanScannerSliceEmpty(t *testing.T) { var nss []sql.NullString if err := (GenericArray{&nss}).Scan(`{}`); err != nil { t.Fatalf("Expected no error, got %v", err) } if nss == nil || len(nss) != 0 { t.Errorf("Expected empty, got %#v", nss) } } func TestGenericArrayScanScannerSliceNil(t *testing.T) { nss := []sql.NullString{{String: ``, Valid: true}, {}} if err := (GenericArray{&nss}).Scan(nil); err != nil { t.Fatalf("Expected no error, got %v", err) } if nss != nil { t.Errorf("Expected nil, got %+v", nss) } } func TestGenericArrayScanScannerSliceBytes(t *testing.T) { src, expected, nss := []byte(`{NULL,abc,"\""}`), []sql.NullString{{}, {String: `abc`, Valid: true}, {String: `"`, Valid: true}}, []sql.NullString{{String: ``, Valid: true}, {}, {}, {}, {}} if err := (GenericArray{&nss}).Scan(src); err != nil { t.Fatalf("Expected no error, got %v", err) } if !reflect.DeepEqual(nss, expected) { t.Errorf("Expected %v, got %v", expected, nss) } } func BenchmarkGenericArrayScanScannerSliceBytes(b *testing.B) { var a GenericArray var x interface{} = []byte(`{a,b,c,d,e,f,g,h,i,j}`) var y interface{} = []byte(`{"\a","\b","\c","\d","\e","\f","\g","\h","\i","\j"}`) for i := 0; i < b.N; i++ { a = GenericArray{new([]sql.NullString)} a.Scan(x) a = GenericArray{new([]sql.NullString)} a.Scan(y) } } func TestGenericArrayScanScannerSliceString(t *testing.T) { src, expected, nss := `{NULL,"\"",xyz}`, []sql.NullString{{}, {String: `"`, Valid: true}, {String: `xyz`, Valid: true}}, []sql.NullString{{String: ``, Valid: true}, {}, {}} if err := (GenericArray{&nss}).Scan(src); err != nil { t.Fatalf("Expected no error, got %v", err) } if !reflect.DeepEqual(nss, expected) { t.Errorf("Expected %v, got %v", expected, nss) } } type TildeNullInt64 struct{ sql.NullInt64 } func (TildeNullInt64) ArrayDelimiter() string { return "~" } func TestGenericArrayScanDelimiter(t *testing.T) { src, expected, tnis := `{12~NULL~76}`, []TildeNullInt64{{sql.NullInt64{Int64: 12, Valid: true}}, {}, {sql.NullInt64{Int64: 76, Valid: true}}}, []TildeNullInt64{{sql.NullInt64{Int64: 0, Valid: true}}, {}} if err := (GenericArray{&tnis}).Scan(src); err != nil { t.Fatalf("Expected no error for %#v, got %v", src, err) } if !reflect.DeepEqual(tnis, expected) { t.Errorf("Expected %v for %#v, got %v", expected, src, tnis) } } func TestGenericArrayScanErrors(t *testing.T) { var sa [1]string var nis []sql.NullInt64 var pss *[]string for _, tt := range []struct { src, dest interface{} err string }{ {nil, pss, "destination *[]string is nil"}, {`{`, &sa, "unable to parse"}, {`{}`, &sa, "cannot convert ARRAY[0] to [1]string"}, {`{x,x}`, &sa, "cannot convert ARRAY[2] to [1]string"}, {`{x}`, &nis, `parsing array element index 0: converting`}, } { err := GenericArray{tt.dest}.Scan(tt.src) if err == nil { t.Fatalf("Expected error for [%#v %#v]", tt.src, tt.dest) } if !strings.Contains(err.Error(), tt.err) { t.Errorf("Expected error to contain %q for [%#v %#v], got %q", tt.err, tt.src, tt.dest, err) } } } func TestGenericArrayValueUnsupported(t *testing.T) { _, err := GenericArray{true}.Value() if err == nil { t.Fatal("Expected error for bool") } if !strings.Contains(err.Error(), "bool to array") { t.Errorf("Expected type to be mentioned, got %q", err) } } type ByteArrayValuer [1]byte type ByteSliceValuer []byte type FuncArrayValuer struct { delimiter func() string value func() (driver.Value, error) } func (a ByteArrayValuer) Value() (driver.Value, error) { return a[:], nil } func (b ByteSliceValuer) Value() (driver.Value, error) { return []byte(b), nil } func (f FuncArrayValuer) ArrayDelimiter() string { return f.delimiter() } func (f FuncArrayValuer) Value() (driver.Value, error) { return f.value() } func TestGenericArrayValue(t *testing.T) { result, err := GenericArray{nil}.Value() if err != nil { t.Fatalf("Expected no error for nil, got %v", err) } if result != nil { t.Errorf("Expected nil, got %q", result) } for _, tt := range []interface{}{ []bool(nil), [][]int(nil), []*int(nil), []sql.NullString(nil), } { result, err := GenericArray{tt}.Value() if err != nil { t.Fatalf("Expected no error for %#v, got %v", tt, err) } if result != nil { t.Errorf("Expected nil for %#v, got %q", tt, result) } } Tilde := func(v driver.Value) FuncArrayValuer { return FuncArrayValuer{ func() string { return "~" }, func() (driver.Value, error) { return v, nil }} } for _, tt := range []struct { result string input interface{} }{ {`{}`, []bool{}}, {`{true}`, []bool{true}}, {`{true,false}`, []bool{true, false}}, {`{true,false}`, [2]bool{true, false}}, {`{}`, [][]int{{}}}, {`{}`, [][]int{{}, {}}}, {`{{1}}`, [][]int{{1}}}, {`{{1},{2}}`, [][]int{{1}, {2}}}, {`{{1,2},{3,4}}`, [][]int{{1, 2}, {3, 4}}}, {`{{1,2},{3,4}}`, [2][2]int{{1, 2}, {3, 4}}}, {`{"a","\\b","c\"","d,e"}`, []string{`a`, `\b`, `c"`, `d,e`}}, {`{"a","\\b","c\"","d,e"}`, [][]byte{{'a'}, {'\\', 'b'}, {'c', '"'}, {'d', ',', 'e'}}}, {`{NULL}`, []*int{nil}}, {`{0,NULL}`, []*int{new(int), nil}}, {`{NULL}`, []sql.NullString{{}}}, {`{"\"",NULL}`, []sql.NullString{{String: `"`, Valid: true}, {}}}, {`{"a","b"}`, []ByteArrayValuer{{'a'}, {'b'}}}, {`{{"a","b"},{"c","d"}}`, [][]ByteArrayValuer{{{'a'}, {'b'}}, {{'c'}, {'d'}}}}, {`{"e","f"}`, []ByteSliceValuer{{'e'}, {'f'}}}, {`{{"e","f"},{"g","h"}}`, [][]ByteSliceValuer{{{'e'}, {'f'}}, {{'g'}, {'h'}}}}, {`{1~2}`, []FuncArrayValuer{Tilde(int64(1)), Tilde(int64(2))}}, {`{{1~2}~{3~4}}`, [][]FuncArrayValuer{{Tilde(int64(1)), Tilde(int64(2))}, {Tilde(int64(3)), Tilde(int64(4))}}}, } { result, err := GenericArray{tt.input}.Value() if err != nil { t.Fatalf("Expected no error for %q, got %v", tt.input, err) } if !reflect.DeepEqual(result, tt.result) { t.Errorf("Expected %q for %q, got %q", tt.result, tt.input, result) } } } func TestGenericArrayValueErrors(t *testing.T) { v := []interface{}{func() {}} if _, err := (GenericArray{v}).Value(); err == nil { t.Errorf("Expected error for %q, got nil", v) } v = []interface{}{nil, func() {}} if _, err := (GenericArray{v}).Value(); err == nil { t.Errorf("Expected error for %q, got nil", v) } } func BenchmarkGenericArrayValueBools(b *testing.B) { rand.Seed(1) x := make([]bool, 10) for i := 0; i < len(x); i++ { x[i] = rand.Intn(2) == 0 } a := GenericArray{x} for i := 0; i < b.N; i++ { a.Value() } } func BenchmarkGenericArrayValueFloat64s(b *testing.B) { rand.Seed(1) x := make([]float64, 10) for i := 0; i < len(x); i++ { x[i] = rand.NormFloat64() } a := GenericArray{x} for i := 0; i < b.N; i++ { a.Value() } } func BenchmarkGenericArrayValueInt64s(b *testing.B) { rand.Seed(1) x := make([]int64, 10) for i := 0; i < len(x); i++ { x[i] = rand.Int63() } a := GenericArray{x} for i := 0; i < b.N; i++ { a.Value() } } func BenchmarkGenericArrayValueByteSlices(b *testing.B) { x := make([][]byte, 10) for i := 0; i < len(x); i++ { x[i] = bytes.Repeat([]byte(`abc"def\ghi`), 5) } a := GenericArray{x} for i := 0; i < b.N; i++ { a.Value() } } func BenchmarkGenericArrayValueStrings(b *testing.B) { x := make([]string, 10) for i := 0; i < len(x); i++ { x[i] = strings.Repeat(`abc"def\ghi`, 5) } a := GenericArray{x} for i := 0; i < b.N; i++ { a.Value() } } func TestArrayScanBackend(t *testing.T) { db := openTestConn(t) defer db.Close() for _, tt := range []struct { s string d sql.Scanner e interface{} }{ {`ARRAY[true, false]`, new(BoolArray), &BoolArray{true, false}}, {`ARRAY[E'\\xdead', E'\\xbeef']`, new(ByteaArray), &ByteaArray{{'\xDE', '\xAD'}, {'\xBE', '\xEF'}}}, {`ARRAY[1.2, 3.4]`, new(Float64Array), &Float64Array{1.2, 3.4}}, {`ARRAY[1, 2, 3]`, new(Int64Array), &Int64Array{1, 2, 3}}, {`ARRAY['a', E'\\b', 'c"', 'd,e']`, new(StringArray), &StringArray{`a`, `\b`, `c"`, `d,e`}}, } { err := db.QueryRow(`SELECT ` + tt.s).Scan(tt.d) if err != nil { t.Errorf("Expected no error when scanning %s into %T, got %v", tt.s, tt.d, err) } if !reflect.DeepEqual(tt.d, tt.e) { t.Errorf("Expected %v when scanning %s into %T, got %v", tt.e, tt.s, tt.d, tt.d) } } } func TestArrayValueBackend(t *testing.T) { db := openTestConn(t) defer db.Close() for _, tt := range []struct { s string v driver.Valuer }{ {`ARRAY[true, false]`, BoolArray{true, false}}, {`ARRAY[E'\\xdead', E'\\xbeef']`, ByteaArray{{'\xDE', '\xAD'}, {'\xBE', '\xEF'}}}, {`ARRAY[1.2, 3.4]`, Float64Array{1.2, 3.4}}, {`ARRAY[1, 2, 3]`, Int64Array{1, 2, 3}}, {`ARRAY['a', E'\\b', 'c"', 'd,e']`, StringArray{`a`, `\b`, `c"`, `d,e`}}, } { var x int err := db.QueryRow(`SELECT 1 WHERE `+tt.s+` <> $1`, tt.v).Scan(&x) if err != sql.ErrNoRows { t.Errorf("Expected %v to equal %s, got %v", tt.v, tt.s, err) } } } golang-github-lib-pq-1.5.2/bench_test.go000066400000000000000000000241141365507746300201170ustar00rootroot00000000000000package pq import ( "bufio" "bytes" "context" "database/sql" "database/sql/driver" "io" "math/rand" "net" "runtime" "strconv" "strings" "sync" "testing" "time" "github.com/lib/pq/oid" ) var ( selectStringQuery = "SELECT '" + strings.Repeat("0123456789", 10) + "'" selectSeriesQuery = "SELECT generate_series(1, 100)" ) func BenchmarkSelectString(b *testing.B) { var result string benchQuery(b, selectStringQuery, &result) } func BenchmarkSelectSeries(b *testing.B) { var result int benchQuery(b, selectSeriesQuery, &result) } func benchQuery(b *testing.B, query string, result interface{}) { b.StopTimer() db := openTestConn(b) defer db.Close() b.StartTimer() for i := 0; i < b.N; i++ { benchQueryLoop(b, db, query, result) } } func benchQueryLoop(b *testing.B, db *sql.DB, query string, result interface{}) { rows, err := db.Query(query) if err != nil { b.Fatal(err) } defer rows.Close() for rows.Next() { err = rows.Scan(result) if err != nil { b.Fatal("failed to scan", err) } } } // reading from circularConn yields content[:prefixLen] once, followed by // content[prefixLen:] over and over again. It never returns EOF. type circularConn struct { content string prefixLen int pos int net.Conn // for all other net.Conn methods that will never be called } func (r *circularConn) Read(b []byte) (n int, err error) { n = copy(b, r.content[r.pos:]) r.pos += n if r.pos >= len(r.content) { r.pos = r.prefixLen } return } func (r *circularConn) Write(b []byte) (n int, err error) { return len(b), nil } func (r *circularConn) Close() error { return nil } func fakeConn(content string, prefixLen int) *conn { c := &circularConn{content: content, prefixLen: prefixLen} return &conn{buf: bufio.NewReader(c), c: c} } // This benchmark is meant to be the same as BenchmarkSelectString, but takes // out some of the factors this package can't control. The numbers are less noisy, // but also the costs of network communication aren't accurately represented. func BenchmarkMockSelectString(b *testing.B) { b.StopTimer() // taken from a recorded run of BenchmarkSelectString // See: http://www.postgresql.org/docs/current/static/protocol-message-formats.html const response = "1\x00\x00\x00\x04" + "t\x00\x00\x00\x06\x00\x00" + "T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" + "Z\x00\x00\x00\x05I" + "2\x00\x00\x00\x04" + "D\x00\x00\x00n\x00\x01\x00\x00\x00d0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" + "C\x00\x00\x00\rSELECT 1\x00" + "Z\x00\x00\x00\x05I" + "3\x00\x00\x00\x04" + "Z\x00\x00\x00\x05I" c := fakeConn(response, 0) b.StartTimer() for i := 0; i < b.N; i++ { benchMockQuery(b, c, selectStringQuery) } } var seriesRowData = func() string { var buf bytes.Buffer for i := 1; i <= 100; i++ { digits := byte(2) if i >= 100 { digits = 3 } else if i < 10 { digits = 1 } buf.WriteString("D\x00\x00\x00") buf.WriteByte(10 + digits) buf.WriteString("\x00\x01\x00\x00\x00") buf.WriteByte(digits) buf.WriteString(strconv.Itoa(i)) } return buf.String() }() func BenchmarkMockSelectSeries(b *testing.B) { b.StopTimer() var response = "1\x00\x00\x00\x04" + "t\x00\x00\x00\x06\x00\x00" + "T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" + "Z\x00\x00\x00\x05I" + "2\x00\x00\x00\x04" + seriesRowData + "C\x00\x00\x00\x0fSELECT 100\x00" + "Z\x00\x00\x00\x05I" + "3\x00\x00\x00\x04" + "Z\x00\x00\x00\x05I" c := fakeConn(response, 0) b.StartTimer() for i := 0; i < b.N; i++ { benchMockQuery(b, c, selectSeriesQuery) } } func benchMockQuery(b *testing.B, c *conn, query string) { stmt, err := c.Prepare(query) if err != nil { b.Fatal(err) } defer stmt.Close() rows, err := stmt.(driver.StmtQueryContext).QueryContext(context.Background(), nil) if err != nil { b.Fatal(err) } defer rows.Close() var dest [1]driver.Value for { if err := rows.Next(dest[:]); err != nil { if err == io.EOF { break } b.Fatal(err) } } } func BenchmarkPreparedSelectString(b *testing.B) { var result string benchPreparedQuery(b, selectStringQuery, &result) } func BenchmarkPreparedSelectSeries(b *testing.B) { var result int benchPreparedQuery(b, selectSeriesQuery, &result) } func benchPreparedQuery(b *testing.B, query string, result interface{}) { b.StopTimer() db := openTestConn(b) defer db.Close() stmt, err := db.Prepare(query) if err != nil { b.Fatal(err) } defer stmt.Close() b.StartTimer() for i := 0; i < b.N; i++ { benchPreparedQueryLoop(b, db, stmt, result) } } func benchPreparedQueryLoop(b *testing.B, db *sql.DB, stmt *sql.Stmt, result interface{}) { rows, err := stmt.Query() if err != nil { b.Fatal(err) } if !rows.Next() { rows.Close() b.Fatal("no rows") } defer rows.Close() for rows.Next() { err = rows.Scan(&result) if err != nil { b.Fatal("failed to scan") } } } // See the comment for BenchmarkMockSelectString. func BenchmarkMockPreparedSelectString(b *testing.B) { b.StopTimer() const parseResponse = "1\x00\x00\x00\x04" + "t\x00\x00\x00\x06\x00\x00" + "T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" + "Z\x00\x00\x00\x05I" const responses = parseResponse + "2\x00\x00\x00\x04" + "D\x00\x00\x00n\x00\x01\x00\x00\x00d0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" + "C\x00\x00\x00\rSELECT 1\x00" + "Z\x00\x00\x00\x05I" c := fakeConn(responses, len(parseResponse)) stmt, err := c.Prepare(selectStringQuery) if err != nil { b.Fatal(err) } b.StartTimer() for i := 0; i < b.N; i++ { benchPreparedMockQuery(b, c, stmt) } } func BenchmarkMockPreparedSelectSeries(b *testing.B) { b.StopTimer() const parseResponse = "1\x00\x00\x00\x04" + "t\x00\x00\x00\x06\x00\x00" + "T\x00\x00\x00!\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\xc1\xff\xfe\xff\xff\xff\xff\x00\x00" + "Z\x00\x00\x00\x05I" var responses = parseResponse + "2\x00\x00\x00\x04" + seriesRowData + "C\x00\x00\x00\x0fSELECT 100\x00" + "Z\x00\x00\x00\x05I" c := fakeConn(responses, len(parseResponse)) stmt, err := c.Prepare(selectSeriesQuery) if err != nil { b.Fatal(err) } b.StartTimer() for i := 0; i < b.N; i++ { benchPreparedMockQuery(b, c, stmt) } } func benchPreparedMockQuery(b *testing.B, c *conn, stmt driver.Stmt) { rows, err := stmt.(driver.StmtQueryContext).QueryContext(context.Background(), nil) if err != nil { b.Fatal(err) } defer rows.Close() var dest [1]driver.Value for { if err := rows.Next(dest[:]); err != nil { if err == io.EOF { break } b.Fatal(err) } } } func BenchmarkEncodeInt64(b *testing.B) { for i := 0; i < b.N; i++ { encode(¶meterStatus{}, int64(1234), oid.T_int8) } } func BenchmarkEncodeFloat64(b *testing.B) { for i := 0; i < b.N; i++ { encode(¶meterStatus{}, 3.14159, oid.T_float8) } } var testByteString = []byte("abcdefghijklmnopqrstuvwxyz") func BenchmarkEncodeByteaHex(b *testing.B) { for i := 0; i < b.N; i++ { encode(¶meterStatus{serverVersion: 90000}, testByteString, oid.T_bytea) } } func BenchmarkEncodeByteaEscape(b *testing.B) { for i := 0; i < b.N; i++ { encode(¶meterStatus{serverVersion: 84000}, testByteString, oid.T_bytea) } } func BenchmarkEncodeBool(b *testing.B) { for i := 0; i < b.N; i++ { encode(¶meterStatus{}, 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(¶meterStatus{}, testTimestamptz, oid.T_timestamptz) } } var testIntBytes = []byte("1234") func BenchmarkDecodeInt64(b *testing.B) { for i := 0; i < b.N; i++ { decode(¶meterStatus{}, testIntBytes, oid.T_int8, formatText) } } var testFloatBytes = []byte("3.14159") func BenchmarkDecodeFloat64(b *testing.B) { for i := 0; i < b.N; i++ { decode(¶meterStatus{}, testFloatBytes, oid.T_float8, formatText) } } var testBoolBytes = []byte{'t'} func BenchmarkDecodeBool(b *testing.B) { for i := 0; i < b.N; i++ { decode(¶meterStatus{}, testBoolBytes, oid.T_bool, formatText) } } func TestDecodeBool(t *testing.T) { db := openTestConn(t) rows, err := db.Query("select true") if err != nil { t.Fatal(err) } rows.Close() } var testTimestamptzBytes = []byte("2013-09-17 22:15:32.360754-07") func BenchmarkDecodeTimestamptz(b *testing.B) { for i := 0; i < b.N; i++ { decode(¶meterStatus{}, testTimestamptzBytes, oid.T_timestamptz, formatText) } } func BenchmarkDecodeTimestamptzMultiThread(b *testing.B) { oldProcs := runtime.GOMAXPROCS(0) defer runtime.GOMAXPROCS(oldProcs) runtime.GOMAXPROCS(runtime.NumCPU()) globalLocationCache = newLocationCache() f := func(wg *sync.WaitGroup, loops int) { defer wg.Done() for i := 0; i < loops; i++ { decode(¶meterStatus{}, testTimestamptzBytes, oid.T_timestamptz, formatText) } } wg := &sync.WaitGroup{} b.ResetTimer() for j := 0; j < 10; j++ { wg.Add(1) go f(wg, b.N/10) } wg.Wait() } func BenchmarkLocationCache(b *testing.B) { globalLocationCache = newLocationCache() for i := 0; i < b.N; i++ { globalLocationCache.getLocation(rand.Intn(10000)) } } func BenchmarkLocationCacheMultiThread(b *testing.B) { oldProcs := runtime.GOMAXPROCS(0) defer runtime.GOMAXPROCS(oldProcs) runtime.GOMAXPROCS(runtime.NumCPU()) globalLocationCache = newLocationCache() f := func(wg *sync.WaitGroup, loops int) { defer wg.Done() for i := 0; i < loops; i++ { globalLocationCache.getLocation(rand.Intn(10000)) } } wg := &sync.WaitGroup{} b.ResetTimer() for j := 0; j < 10; j++ { wg.Add(1) go f(wg, b.N/10) } wg.Wait() } // Stress test the performance of parsing results from the wire. func BenchmarkResultParsing(b *testing.B) { b.StopTimer() db := openTestConn(b) defer db.Close() _, err := db.Exec("BEGIN") if err != nil { b.Fatal(err) } b.StartTimer() for i := 0; i < b.N; i++ { res, err := db.Query("SELECT generate_series(1, 50000)") if err != nil { b.Fatal(err) } res.Close() } } golang-github-lib-pq-1.5.2/buf.go000066400000000000000000000031261365507746300165550ustar00rootroot00000000000000package pq import ( "bytes" "encoding/binary" "github.com/lib/pq/oid" ) type readBuf []byte func (b *readBuf) int32() (n int) { n = int(int32(binary.BigEndian.Uint32(*b))) *b = (*b)[4:] return } func (b *readBuf) oid() (n oid.Oid) { n = oid.Oid(binary.BigEndian.Uint32(*b)) *b = (*b)[4:] 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:] return } func (b *readBuf) string() string { i := bytes.IndexByte(*b, 0) if i < 0 { errorf("invalid message format; expected string terminator") } s := (*b)[:i] *b = (*b)[i+1:] return string(s) } func (b *readBuf) next(n int) (v []byte) { v = (*b)[:n] *b = (*b)[n:] return } func (b *readBuf) byte() byte { return b.next(1)[0] } type writeBuf struct { buf []byte pos int } func (b *writeBuf) int32(n int) { x := make([]byte, 4) binary.BigEndian.PutUint32(x, uint32(n)) b.buf = append(b.buf, x...) } func (b *writeBuf) int16(n int) { x := make([]byte, 2) binary.BigEndian.PutUint16(x, uint16(n)) b.buf = append(b.buf, x...) } func (b *writeBuf) string(s string) { b.buf = append(append(b.buf, s...), '\000') } func (b *writeBuf) byte(c byte) { b.buf = append(b.buf, c) } func (b *writeBuf) bytes(v []byte) { 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) } golang-github-lib-pq-1.5.2/buf_test.go000066400000000000000000000003351365507746300176130ustar00rootroot00000000000000package pq import "testing" func Benchmark_writeBuf_string(b *testing.B) { var buf writeBuf const s = "foo" b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { buf.string(s) buf.buf = buf.buf[:0] } } golang-github-lib-pq-1.5.2/certs/000077500000000000000000000000001365507746300165705ustar00rootroot00000000000000golang-github-lib-pq-1.5.2/certs/README000066400000000000000000000002541365507746300174510ustar00rootroot00000000000000This directory contains certificates and private keys for testing some SSL-related functionality in Travis. Do NOT use these certificates for anything other than testing. golang-github-lib-pq-1.5.2/certs/bogus_root.crt000066400000000000000000000021271365507746300214660ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDBjCCAe6gAwIBAgIQSnDYp/Naet9HOZljF5PuwDANBgkqhkiG9w0BAQsFADAr MRIwEAYDVQQKEwlDb2Nrcm9hY2gxFTATBgNVBAMTDENvY2tyb2FjaCBDQTAeFw0x NjAyMDcxNjQ0MzdaFw0xNzAyMDYxNjQ0MzdaMCsxEjAQBgNVBAoTCUNvY2tyb2Fj aDEVMBMGA1UEAxMMQ29ja3JvYWNoIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A MIIBCgKCAQEAxdln3/UdgP7ayA/G1kT7upjLe4ERwQjYQ25q0e1+vgsB5jhiirxJ e0+WkhhYu/mwoSAXzvlsbZ2PWFyfdanZeD/Lh6SvIeWXVVaPcWVWL1TEcoN2jr5+ E85MMHmbbmaT2he8s6br2tM/UZxyTQ2XRprIzApbDssyw1c0Yufcpu3C6267FLEl IfcWrzDhnluFhthhtGXv3ToD8IuMScMC5qlKBXtKmD1B5x14ngO/ecNJ+OlEi0HU mavK4KWgI2rDXRZ2EnCpyTZdkc3kkRnzKcg653oOjMDRZdrhfIrha+Jq38ACsUmZ Su7Sp5jkIHOCO8Zg+l6GKVSq37dKMapD8wIDAQABoyYwJDAOBgNVHQ8BAf8EBAMC AuQwEgYDVR0TAQH/BAgwBgEB/wIBATANBgkqhkiG9w0BAQsFAAOCAQEAwZ2Tu0Yu rrSVdMdoPEjT1IZd+5OhM/SLzL0ddtvTithRweLHsw2lDQYlXFqr24i3UGZJQ1sp cqSrNwswgLUQT3vWyTjmM51HEb2vMYWKmjZ+sBQYAUP1CadrN/+OTfNGnlF1+B4w IXOzh7EvQmJJnNybLe4a/aRvj1NE2n8Z898B76SVU9WbfKKz8VwLzuIPDqkKcZda lMy5yzthyztV9YjcWs2zVOUGZvGdAhDrvZuUq6mSmxrBEvR2LBOggmVf3tGRT+Ls lW7c9Lrva5zLHuqmoPP07A+vuI9a0D1X44jwGDuPWJ5RnTOQ63Uez12mKNjqleHw DnkwNanuO8dhAA== -----END CERTIFICATE----- golang-github-lib-pq-1.5.2/certs/postgresql.crt000066400000000000000000000071611365507746300215120ustar00rootroot00000000000000Certificate: Data: Version: 3 (0x2) Serial Number: 2 (0x2) Signature Algorithm: sha256WithRSAEncryption Issuer: C=US, ST=Nevada, L=Las Vegas, O=github.com/lib/pq, CN=pq CA Validity Not Before: Oct 11 15:10:11 2014 GMT Not After : Oct 8 15:10:11 2024 GMT Subject: C=US, ST=Nevada, L=Las Vegas, O=github.com/lib/pq, CN=pqgosslcert Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (1024 bit) Modulus (1024 bit): 00:e3:8c:06:9a:70:54:51:d1:34:34:83:39:cd:a2: 59:0f:05:ed:8d:d8:0e:34:d0:92:f4:09:4d:ee:8c: 78:55:49:24:f8:3c:e0:34:58:02:b2:e7:94:58:c1: e8:e5:bb:d1:af:f6:54:c1:40:b1:90:70:79:0d:35: 54:9c:8f:16:e9:c2:f0:92:e6:64:49:38:c1:76:f8: 47:66:c4:5b:4a:b6:a9:43:ce:c8:be:6c:4d:2b:94: 97:3c:55:bc:d1:d0:6e:b7:53:ae:89:5c:4b:6b:86: 40:be:c1:ae:1e:64:ce:9c:ae:87:0a:69:e5:c8:21: 12:be:ae:1d:f6:45:df:16:a7 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Key Identifier: 9B:25:31:63:A2:D8:06:FF:CB:E3:E9:96:FF:0D:BA:DC:12:7D:04:CF X509v3 Authority Key Identifier: keyid:52:93:ED:1E:76:0A:9F:65:4F:DE:19:66:C1:D5:22:40:35:CB:A0:72 X509v3 Basic Constraints: CA:FALSE X509v3 Key Usage: Digital Signature, Non Repudiation, Key Encipherment Signature Algorithm: sha256WithRSAEncryption 3e:f5:f8:0b:4e:11:bd:00:86:1f:ce:dc:97:02:98:91:11:f5: 65:f6:f2:8a:b2:3e:47:92:05:69:28:c9:e9:b4:f7:cf:93:d1: 2d:81:5d:00:3c:23:be:da:70:ea:59:e1:2c:d3:25:49:ae:a6: 95:54:c1:10:df:23:e3:fe:d6:e4:76:c7:6b:73:ad:1b:34:7c: e2:56:cc:c0:37:ae:c5:7a:11:20:6c:3d:05:0e:99:cd:22:6c: cf:59:a1:da:28:d4:65:ba:7d:2f:2b:3d:69:6d:a6:c1:ae:57: bf:56:64:13:79:f8:48:46:65:eb:81:67:28:0b:7b:de:47:10: b3:80:3c:31:d1:58:94:01:51:4a:c7:c8:1a:01:a8:af:c4:cd: bb:84:a5:d9:8b:b4:b9:a1:64:3e:95:d9:90:1d:d5:3f:67:cc: 3b:ba:f5:b4:d1:33:77:ee:c2:d2:3e:7e:c5:66:6e:b7:35:4c: 60:57:b0:b8:be:36:c8:f3:d3:95:8c:28:4a:c9:f7:27:a4:0d: e5:96:99:eb:f5:c8:bd:f3:84:6d:ef:02:f9:8a:36:7d:6b:5f: 36:68:37:41:d9:74:ae:c6:78:2e:44:86:a1:ad:43:ca:fb:b5: 3e:ba:10:23:09:02:ac:62:d1:d0:83:c8:95:b9:e3:5e:30:ff: 5b:2b:38:fa -----BEGIN CERTIFICATE----- MIIDEzCCAfugAwIBAgIBAjANBgkqhkiG9w0BAQsFADBeMQswCQYDVQQGEwJVUzEP MA0GA1UECBMGTmV2YWRhMRIwEAYDVQQHEwlMYXMgVmVnYXMxGjAYBgNVBAoTEWdp dGh1Yi5jb20vbGliL3BxMQ4wDAYDVQQDEwVwcSBDQTAeFw0xNDEwMTExNTEwMTFa Fw0yNDEwMDgxNTEwMTFaMGQxCzAJBgNVBAYTAlVTMQ8wDQYDVQQIEwZOZXZhZGEx EjAQBgNVBAcTCUxhcyBWZWdhczEaMBgGA1UEChMRZ2l0aHViLmNvbS9saWIvcHEx FDASBgNVBAMTC3BxZ29zc2xjZXJ0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB gQDjjAaacFRR0TQ0gznNolkPBe2N2A400JL0CU3ujHhVSST4POA0WAKy55RYwejl u9Gv9lTBQLGQcHkNNVScjxbpwvCS5mRJOMF2+EdmxFtKtqlDzsi+bE0rlJc8VbzR 0G63U66JXEtrhkC+wa4eZM6crocKaeXIIRK+rh32Rd8WpwIDAQABo1owWDAdBgNV HQ4EFgQUmyUxY6LYBv/L4+mW/w263BJ9BM8wHwYDVR0jBBgwFoAUUpPtHnYKn2VP 3hlmwdUiQDXLoHIwCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwDQYJKoZIhvcNAQEL BQADggEBAD71+AtOEb0Ahh/O3JcCmJER9WX28oqyPkeSBWkoyem098+T0S2BXQA8 I77acOpZ4SzTJUmuppVUwRDfI+P+1uR2x2tzrRs0fOJWzMA3rsV6ESBsPQUOmc0i bM9Zodoo1GW6fS8rPWltpsGuV79WZBN5+EhGZeuBZygLe95HELOAPDHRWJQBUUrH yBoBqK/EzbuEpdmLtLmhZD6V2ZAd1T9nzDu69bTRM3fuwtI+fsVmbrc1TGBXsLi+ Nsjz05WMKErJ9yekDeWWmev1yL3zhG3vAvmKNn1rXzZoN0HZdK7GeC5EhqGtQ8r7 tT66ECMJAqxi0dCDyJW5414w/1srOPo= -----END CERTIFICATE----- golang-github-lib-pq-1.5.2/certs/postgresql.key000066400000000000000000000015671365507746300215160ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIICWwIBAAKBgQDjjAaacFRR0TQ0gznNolkPBe2N2A400JL0CU3ujHhVSST4POA0 WAKy55RYwejlu9Gv9lTBQLGQcHkNNVScjxbpwvCS5mRJOMF2+EdmxFtKtqlDzsi+ bE0rlJc8VbzR0G63U66JXEtrhkC+wa4eZM6crocKaeXIIRK+rh32Rd8WpwIDAQAB AoGAM5dM6/kp9P700i8qjOgRPym96Zoh5nGfz/rIE5z/r36NBkdvIg8OVZfR96nH b0b9TOMR5lsPp0sI9yivTWvX6qyvLJRWy2vvx17hXK9NxXUNTAm0PYZUTvCtcPeX RnJpzQKNZQPkFzF0uXBc4CtPK2Vz0+FGvAelrhYAxnw1dIkCQQD+9qaW5QhXjsjb Nl85CmXgxPmGROcgLQCO+omfrjf9UXrituU9Dz6auym5lDGEdMFnkzfr+wpasEy9 mf5ZZOhDAkEA5HjXfVGaCtpydOt6hDon/uZsyssCK2lQ7NSuE3vP+sUsYMzIpEoy t3VWXqKbo+g9KNDTP4WEliqp1aiSIylzzQJANPeqzihQnlgEdD4MdD4rwhFJwVIp Le8Lcais1KaN7StzOwxB/XhgSibd2TbnPpw+3bSg5n5lvUdo+e62/31OHwJAU1jS I+F09KikQIr28u3UUWT2IzTT4cpVv1AHAQyV3sG3YsjSGT0IK20eyP9BEBZU2WL0 7aNjrvR5aHxKc5FXsQJABsFtyGpgI5X4xufkJZVZ+Mklz2n7iXa+XPatMAHFxAtb EEMt60rngwMjXAzBSC6OYuYogRRAY3UCacNC5VhLYQ== -----END RSA PRIVATE KEY----- golang-github-lib-pq-1.5.2/certs/root.crt000066400000000000000000000026541365507746300202740ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIEAzCCAuugAwIBAgIJANmheROCdW1NMA0GCSqGSIb3DQEBBQUAMF4xCzAJBgNV BAYTAlVTMQ8wDQYDVQQIEwZOZXZhZGExEjAQBgNVBAcTCUxhcyBWZWdhczEaMBgG A1UEChMRZ2l0aHViLmNvbS9saWIvcHExDjAMBgNVBAMTBXBxIENBMB4XDTE0MTAx MTE1MDQyOVoXDTI0MTAwODE1MDQyOVowXjELMAkGA1UEBhMCVVMxDzANBgNVBAgT Bk5ldmFkYTESMBAGA1UEBxMJTGFzIFZlZ2FzMRowGAYDVQQKExFnaXRodWIuY29t L2xpYi9wcTEOMAwGA1UEAxMFcHEgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw ggEKAoIBAQCV4PxP7ShzWBzUCThcKk3qZtOLtHmszQVtbqhvgTpm1kTRtKBdVMu0 pLAHQ3JgJCnAYgH0iZxVGoMP16T3irdgsdC48+nNTFM2T0cCdkfDURGIhSFN47cb Pgy306BcDUD2q7ucW33+dlFSRuGVewocoh4BWM/vMtMvvWzdi4Ag/L/jhb+5wZxZ sWymsadOVSDePEMKOvlCa3EdVwVFV40TVyDb+iWBUivDAYsS2a3KajuJrO6MbZiE Sp2RCIkZS2zFmzWxVRi9ZhzIZhh7EVF9JAaNC3T52jhGUdlRq3YpBTMnd89iOh74 6jWXG7wSuPj3haFzyNhmJ0ZUh+2Ynoh1AgMBAAGjgcMwgcAwHQYDVR0OBBYEFFKT 7R52Cp9lT94ZZsHVIkA1y6ByMIGQBgNVHSMEgYgwgYWAFFKT7R52Cp9lT94ZZsHV IkA1y6ByoWKkYDBeMQswCQYDVQQGEwJVUzEPMA0GA1UECBMGTmV2YWRhMRIwEAYD VQQHEwlMYXMgVmVnYXMxGjAYBgNVBAoTEWdpdGh1Yi5jb20vbGliL3BxMQ4wDAYD VQQDEwVwcSBDQYIJANmheROCdW1NMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEF BQADggEBAAEhCLWkqJNMI8b4gkbmj5fqQ/4+oO83bZ3w2Oqf6eZ8I8BC4f2NOyE6 tRUlq5+aU7eqC1cOAvGjO+YHN/bF/DFpwLlzvUSXt+JP/pYcUjL7v+pIvwqec9hD ndvM4iIbkD/H/OYQ3L+N3W+G1x7AcFIX+bGCb3PzYVQAjxreV6//wgKBosMGFbZo HPxT9RPMun61SViF04H5TNs0derVn1+5eiiYENeAhJzQNyZoOOUuX1X/Inx9bEPh C5vFBtSMgIytPgieRJVWAiMLYsfpIAStrHztRAbBs2DU01LmMgRvHdxgFEKinC/d UHZZQDP+6pT+zADrGhQGXe4eThaO6f0= -----END CERTIFICATE----- golang-github-lib-pq-1.5.2/certs/server.crt000066400000000000000000000105321365507746300206110ustar00rootroot00000000000000Certificate: Data: Version: 3 (0x2) Serial Number: 1 (0x1) Signature Algorithm: sha256WithRSAEncryption Issuer: C=US, ST=Nevada, L=Las Vegas, O=github.com/lib/pq, CN=pq CA Validity Not Before: Oct 11 15:05:15 2014 GMT Not After : Oct 8 15:05:15 2024 GMT Subject: C=US, ST=Nevada, L=Las Vegas, O=github.com/lib/pq, CN=postgres Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public Key: (2048 bit) Modulus (2048 bit): 00:d7:8a:4c:85:fb:17:a5:3c:8f:e0:72:11:29:ce: 3f:b0:1f:3f:7d:c6:ee:7f:a7:fc:02:2b:35:47:08: a6:3d:90:df:5c:56:14:94:00:c7:6d:d1:d2:e2:61: 95:77:b8:e3:a6:66:31:f9:1f:21:7d:62:e1:27:da: 94:37:61:4a:ea:63:53:a0:61:b8:9c:bb:a5:e2:e7: b7:a6:d8:0f:05:04:c7:29:e2:ea:49:2b:7f:de:15: 00:a6:18:70:50:c7:0c:de:9a:f9:5a:96:b0:e1:94: 06:c6:6d:4a:21:3b:b4:0f:a5:6d:92:86:34:b2:4e: d7:0e:a7:19:c0:77:0b:7b:87:c8:92:de:42:ff:86: d2:b7:9a:a4:d4:15:23:ca:ad:a5:69:21:b8:ce:7e: 66:cb:85:5d:b9:ed:8b:2d:09:8d:94:e4:04:1e:72: ec:ef:d0:76:90:15:5a:a4:f7:91:4b:e9:ce:4e:9d: 5d:9a:70:17:9c:d8:e9:73:83:ea:3d:61:99:a6:cd: ac:91:40:5a:88:77:e5:4e:2a:8e:3d:13:f3:f9:38: 6f:81:6b:8a:95:ca:0e:07:ab:6f:da:b4:8c:d9:ff: aa:78:03:aa:c7:c2:cf:6f:64:92:d3:d8:83:d5:af: f1:23:18:a7:2e:7b:17:0b:e7:7d:f1:fa:a8:41:a3: 04:57 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Key Identifier: EE:F0:B3:46:DC:C7:09:EB:0E:B6:2F:E5:FE:62:60:45:44:9F:59:CC X509v3 Authority Key Identifier: keyid:52:93:ED:1E:76:0A:9F:65:4F:DE:19:66:C1:D5:22:40:35:CB:A0:72 X509v3 Basic Constraints: CA:FALSE X509v3 Key Usage: Digital Signature, Non Repudiation, Key Encipherment Signature Algorithm: sha256WithRSAEncryption 7e:5a:6e:be:bf:d2:6c:c1:d6:fa:b6:fb:3f:06:53:36:08:87: 9d:95:b1:39:af:9e:f6:47:38:17:39:da:25:7c:f2:ad:0c:e3: ab:74:19:ca:fb:8c:a0:50:c0:1d:19:8a:9c:21:ed:0f:3a:d1: 96:54:2e:10:09:4f:b8:70:f7:2b:99:43:d2:c6:15:bc:3f:24: 7d:28:39:32:3f:8d:a4:4f:40:75:7f:3e:0d:1c:d1:69:f2:4e: 98:83:47:97:d2:25:ac:c9:36:86:2f:04:a6:c4:86:c7:c4:00: 5f:7f:b9:ad:fc:bf:e9:f5:78:d7:82:1a:51:0d:fc:ab:9e:92: 1d:5f:0c:18:d1:82:e0:14:c9:ce:91:89:71:ff:49:49:ff:35: bf:7b:44:78:42:c1:d0:66:65:bb:28:2e:60:ca:9b:20:12:a9: 90:61:b1:96:ec:15:46:c9:37:f7:07:90:8a:89:45:2a:3f:37: ec:dc:e3:e5:8f:c3:3a:57:80:a5:54:60:0c:e1:b2:26:99:2b: 40:7e:36:d1:9a:70:02:ec:63:f4:3b:72:ae:81:fb:30:20:6d: cb:48:46:c6:b5:8f:39:b1:84:05:25:55:8d:f5:62:f6:1b:46: 2e:da:a3:4c:26:12:44:d7:56:b6:b8:a9:ca:d3:ab:71:45:7c: 9f:48:6d:1e -----BEGIN CERTIFICATE----- MIIDlDCCAnygAwIBAgIBATANBgkqhkiG9w0BAQsFADBeMQswCQYDVQQGEwJVUzEP MA0GA1UECBMGTmV2YWRhMRIwEAYDVQQHEwlMYXMgVmVnYXMxGjAYBgNVBAoTEWdp dGh1Yi5jb20vbGliL3BxMQ4wDAYDVQQDEwVwcSBDQTAeFw0xNDEwMTExNTA1MTVa Fw0yNDEwMDgxNTA1MTVaMGExCzAJBgNVBAYTAlVTMQ8wDQYDVQQIEwZOZXZhZGEx EjAQBgNVBAcTCUxhcyBWZWdhczEaMBgGA1UEChMRZ2l0aHViLmNvbS9saWIvcHEx ETAPBgNVBAMTCHBvc3RncmVzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC AQEA14pMhfsXpTyP4HIRKc4/sB8/fcbuf6f8Ais1RwimPZDfXFYUlADHbdHS4mGV d7jjpmYx+R8hfWLhJ9qUN2FK6mNToGG4nLul4ue3ptgPBQTHKeLqSSt/3hUAphhw UMcM3pr5Wpaw4ZQGxm1KITu0D6VtkoY0sk7XDqcZwHcLe4fIkt5C/4bSt5qk1BUj yq2laSG4zn5my4Vdue2LLQmNlOQEHnLs79B2kBVapPeRS+nOTp1dmnAXnNjpc4Pq PWGZps2skUBaiHflTiqOPRPz+ThvgWuKlcoOB6tv2rSM2f+qeAOqx8LPb2SS09iD 1a/xIxinLnsXC+d98fqoQaMEVwIDAQABo1owWDAdBgNVHQ4EFgQU7vCzRtzHCesO ti/l/mJgRUSfWcwwHwYDVR0jBBgwFoAUUpPtHnYKn2VP3hlmwdUiQDXLoHIwCQYD VR0TBAIwADALBgNVHQ8EBAMCBeAwDQYJKoZIhvcNAQELBQADggEBAH5abr6/0mzB 1vq2+z8GUzYIh52VsTmvnvZHOBc52iV88q0M46t0Gcr7jKBQwB0Zipwh7Q860ZZU LhAJT7hw9yuZQ9LGFbw/JH0oOTI/jaRPQHV/Pg0c0WnyTpiDR5fSJazJNoYvBKbE hsfEAF9/ua38v+n1eNeCGlEN/Kuekh1fDBjRguAUyc6RiXH/SUn/Nb97RHhCwdBm ZbsoLmDKmyASqZBhsZbsFUbJN/cHkIqJRSo/N+zc4+WPwzpXgKVUYAzhsiaZK0B+ NtGacALsY/Q7cq6B+zAgbctIRsa1jzmxhAUlVY31YvYbRi7ao0wmEkTXVra4qcrT q3FFfJ9IbR4= -----END CERTIFICATE----- golang-github-lib-pq-1.5.2/certs/server.key000066400000000000000000000032131365507746300206070ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEA14pMhfsXpTyP4HIRKc4/sB8/fcbuf6f8Ais1RwimPZDfXFYU lADHbdHS4mGVd7jjpmYx+R8hfWLhJ9qUN2FK6mNToGG4nLul4ue3ptgPBQTHKeLq SSt/3hUAphhwUMcM3pr5Wpaw4ZQGxm1KITu0D6VtkoY0sk7XDqcZwHcLe4fIkt5C /4bSt5qk1BUjyq2laSG4zn5my4Vdue2LLQmNlOQEHnLs79B2kBVapPeRS+nOTp1d mnAXnNjpc4PqPWGZps2skUBaiHflTiqOPRPz+ThvgWuKlcoOB6tv2rSM2f+qeAOq x8LPb2SS09iD1a/xIxinLnsXC+d98fqoQaMEVwIDAQABAoIBAF3ZoihUhJ82F4+r Gz4QyDpv4L1reT2sb1aiabhcU8ZK5nbWJG+tRyjSS/i2dNaEcttpdCj9HR/zhgZM bm0OuAgG58rVwgS80CZUruq++Qs+YVojq8/gWPTiQD4SNhV2Fmx3HkwLgUk3oxuT SsvdqzGE3okGVrutCIcgy126eA147VPMoej1Bb3fO6npqK0pFPhZfAc0YoqJuM+k obRm5pAnGUipyLCFXjA9HYPKwYZw2RtfdA3CiImHeanSdqS+ctrC9y8BV40Th7gZ haXdKUNdjmIxV695QQ1mkGqpKLZFqhzKioGQ2/Ly2d1iaKN9fZltTusu8unepWJ2 tlT9qMECgYEA9uHaF1t2CqE+AJvWTihHhPIIuLxoOQXYea1qvxfcH/UMtaLKzCNm lQ5pqCGsPvp+10f36yttO1ZehIvlVNXuJsjt0zJmPtIolNuJY76yeussfQ9jHheB 5uPEzCFlHzxYbBUyqgWaF6W74okRGzEGJXjYSP0yHPPdU4ep2q3bGiUCgYEA34Af wBSuQSK7uLxArWHvQhyuvi43ZGXls6oRGl+Ysj54s8BP6XGkq9hEJ6G4yxgyV+BR DUOs5X8/TLT8POuIMYvKTQthQyCk0eLv2FLdESDuuKx0kBVY3s8lK3/z5HhrdOiN VMNZU+xDKgKc3hN9ypkk8vcZe6EtH7Y14e0rVcsCgYBTgxi8F/M5K0wG9rAqphNz VFBA9XKn/2M33cKjO5X5tXIEKzpAjaUQvNxexG04rJGljzG8+mar0M6ONahw5yD1 O7i/XWgazgpuOEkkVYiYbd8RutfDgR4vFVMn3hAP3eDnRtBplRWH9Ec3HTiNIys6 F8PKBOQjyRZQQC7jyzW3hQKBgACe5HeuFwXLSOYsb6mLmhR+6+VPT4wR1F95W27N USk9jyxAnngxfpmTkiziABdgS9N+pfr5cyN4BP77ia/Jn6kzkC5Cl9SN5KdIkA3z vPVtN/x/ThuQU5zaymmig1ThGLtMYggYOslG4LDfLPxY5YKIhle+Y+259twdr2yf Mf2dAoGAaGv3tWMgnIdGRk6EQL/yb9PKHo7ShN+tKNlGaK7WwzBdKs+Fe8jkgcr7 pz4Ne887CmxejdISzOCcdT+Zm9Bx6I/uZwWOtDvWpIgIxVX9a9URj/+D1MxTE/y4 d6H+c89yDY62I2+drMpdjCd3EtCaTlxpTbRS+s1eAHMH7aEkcCE= -----END RSA PRIVATE KEY----- golang-github-lib-pq-1.5.2/conn.go000066400000000000000000001323021365507746300167350ustar00rootroot00000000000000package pq import ( "bufio" "context" "crypto/md5" "crypto/sha256" "database/sql" "database/sql/driver" "encoding/binary" "errors" "fmt" "io" "net" "os" "os/user" "path" "path/filepath" "strconv" "strings" "time" "unicode" "github.com/lib/pq/oid" "github.com/lib/pq/scram" ) // Common error types var ( ErrNotSupported = errors.New("pq: Unsupported command") ErrInFailedTransaction = errors.New("pq: Could not complete operation in a failed transaction") ErrSSLNotSupported = errors.New("pq: SSL is not enabled on the server") ErrSSLKeyHasWorldPermissions = errors.New("pq: Private key file has group or world access. Permissions should be u=rw (0600) or less") ErrCouldNotDetectUsername = errors.New("pq: Could not detect default username. Please provide one explicitly") errUnexpectedReady = errors.New("unexpected ReadyForQuery") errNoRowsAffected = errors.New("no RowsAffected available after the empty statement") errNoLastInsertID = errors.New("no LastInsertId available after the empty statement") ) // Driver is the Postgres database driver. type Driver struct{} // Open opens a new connection to the database. name is a connection string. // Most users should only use it through database/sql package from the standard // library. func (d *Driver) Open(name string) (driver.Conn, error) { return Open(name) } func init() { sql.Register("postgres", &Driver{}) } type parameterStatus struct { // server version in the same format as server_version_num, or 0 if // unavailable serverVersion int // the current location based on the TimeZone value of the session, if // available currentLocation *time.Location } type transactionStatus byte const ( txnStatusIdle transactionStatus = 'I' txnStatusIdleInTransaction transactionStatus = 'T' txnStatusInFailedTransaction transactionStatus = 'E' ) func (s transactionStatus) String() string { switch s { case txnStatusIdle: return "idle" case txnStatusIdleInTransaction: return "idle in transaction" case txnStatusInFailedTransaction: return "in a failed transaction" default: errorf("unknown transactionStatus %d", s) } panic("not reached") } // Dialer is the dialer interface. It can be used to obtain more control over // how pq creates network connections. type Dialer interface { Dial(network, address string) (net.Conn, error) DialTimeout(network, address string, timeout time.Duration) (net.Conn, error) } // DialerContext is the context-aware dialer interface. type DialerContext interface { DialContext(ctx context.Context, network, address string) (net.Conn, error) } type defaultDialer struct { d net.Dialer } func (d defaultDialer) Dial(network, address string) (net.Conn, error) { return d.d.Dial(network, address) } func (d defaultDialer) DialTimeout(network, address string, timeout time.Duration) (net.Conn, error) { ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() return d.DialContext(ctx, network, address) } func (d defaultDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { return d.d.DialContext(ctx, network, address) } type conn struct { c net.Conn buf *bufio.Reader namei int scratch [512]byte txnStatus transactionStatus txnFinish func() // Save connection arguments to use during CancelRequest. dialer Dialer opts values // Cancellation key data for use with CancelRequest messages. processID int secretKey int parameterStatus parameterStatus saveMessageType byte saveMessageBuffer []byte // If true, this connection is bad and all public-facing functions should // return ErrBadConn. bad bool // If set, this connection should never use the binary format when // receiving query results from prepared statements. Only provided for // debugging. disablePreparedBinaryResult bool // Whether to always send []byte parameters over as binary. Enables single // round-trip mode for non-prepared Query calls. binaryParameters bool // If true this connection is in the middle of a COPY inCopy bool // If not nil, notices will be synchronously sent here noticeHandler func(*Error) // If not nil, notifications will be synchronously sent here notificationHandler func(*Notification) } // Handle driver-side settings in parsed connection string. func (cn *conn) handleDriverSettings(o values) (err error) { boolSetting := func(key string, val *bool) error { if value, ok := o[key]; ok { if value == "yes" { *val = true } else if value == "no" { *val = false } else { return fmt.Errorf("unrecognized value %q for %s", value, key) } } return nil } err = boolSetting("disable_prepared_binary_result", &cn.disablePreparedBinaryResult) if err != nil { return err } return boolSetting("binary_parameters", &cn.binaryParameters) } func (cn *conn) handlePgpass(o values) { // if a password was supplied, do not process .pgpass if _, ok := o["password"]; ok { return } filename := os.Getenv("PGPASSFILE") if filename == "" { // XXX this code doesn't work on Windows where the default filename is // XXX %APPDATA%\postgresql\pgpass.conf // Prefer $HOME over user.Current due to glibc bug: golang.org/issue/13470 userHome := os.Getenv("HOME") if userHome == "" { user, err := user.Current() if err != nil { return } userHome = user.HomeDir } filename = filepath.Join(userHome, ".pgpass") } fileinfo, err := os.Stat(filename) if err != nil { return } mode := fileinfo.Mode() if mode&(0x77) != 0 { // XXX should warn about incorrect .pgpass permissions as psql does return } file, err := os.Open(filename) if err != nil { return } defer file.Close() scanner := bufio.NewScanner(io.Reader(file)) hostname := o["host"] ntw, _ := network(o) port := o["port"] db := o["dbname"] username := o["user"] // From: https://github.com/tg/pgpass/blob/master/reader.go getFields := func(s string) []string { fs := make([]string, 0, 5) f := make([]rune, 0, len(s)) var esc bool for _, c := range s { switch { case esc: f = append(f, c) esc = false case c == '\\': esc = true case c == ':': fs = append(fs, string(f)) f = f[:0] default: f = append(f, c) } } return append(fs, string(f)) } for scanner.Scan() { line := scanner.Text() if len(line) == 0 || line[0] == '#' { continue } split := getFields(line) if len(split) != 5 { continue } if (split[0] == "*" || split[0] == hostname || (split[0] == "localhost" && (hostname == "" || ntw == "unix"))) && (split[1] == "*" || split[1] == port) && (split[2] == "*" || split[2] == db) && (split[3] == "*" || split[3] == username) { o["password"] = split[4] return } } } func (cn *conn) writeBuf(b byte) *writeBuf { cn.scratch[0] = b return &writeBuf{ buf: cn.scratch[:5], pos: 1, } } // Open opens a new connection to the database. dsn is a connection string. // Most users should only use it through database/sql package from the standard // library. func Open(dsn string) (_ driver.Conn, err error) { return DialOpen(defaultDialer{}, dsn) } // DialOpen opens a new connection to the database using a dialer. func DialOpen(d Dialer, dsn string) (_ driver.Conn, err error) { c, err := NewConnector(dsn) if err != nil { return nil, err } c.dialer = d return c.open(context.Background()) } func (c *Connector) open(ctx context.Context) (cn *conn, err error) { // Handle any panics during connection initialization. Note that we // specifically do *not* want to use errRecover(), as that would turn any // connection errors into ErrBadConns, hiding the real error message from // the user. defer errRecoverNoErrBadConn(&err) o := c.opts cn = &conn{ opts: o, dialer: c.dialer, } err = cn.handleDriverSettings(o) if err != nil { return nil, err } cn.handlePgpass(o) cn.c, err = dial(ctx, c.dialer, o) if err != nil { return nil, err } err = cn.ssl(o) if err != nil { if cn.c != nil { cn.c.Close() } return nil, err } // cn.startup panics on error. Make sure we don't leak cn.c. panicking := true defer func() { if panicking { cn.c.Close() } }() cn.buf = bufio.NewReader(cn.c) cn.startup(o) // reset the deadline, in case one was set (see dial) if timeout, ok := o["connect_timeout"]; ok && timeout != "0" { err = cn.c.SetDeadline(time.Time{}) } panicking = false return cn, err } func dial(ctx context.Context, d Dialer, o values) (net.Conn, error) { network, address := network(o) // SSL is not necessary or supported over UNIX domain sockets if network == "unix" { o["sslmode"] = "disable" } // Zero or not specified means wait indefinitely. if timeout, ok := o["connect_timeout"]; ok && timeout != "0" { seconds, err := strconv.ParseInt(timeout, 10, 0) if err != nil { return nil, fmt.Errorf("invalid value for parameter connect_timeout: %s", err) } duration := time.Duration(seconds) * time.Second // connect_timeout should apply to the entire connection establishment // procedure, so we both use a timeout for the TCP connection // establishment and set a deadline for doing the initial handshake. // The deadline is then reset after startup() is done. deadline := time.Now().Add(duration) var conn net.Conn if dctx, ok := d.(DialerContext); ok { ctx, cancel := context.WithTimeout(ctx, duration) defer cancel() conn, err = dctx.DialContext(ctx, network, address) } else { conn, err = d.DialTimeout(network, address, duration) } if err != nil { return nil, err } err = conn.SetDeadline(deadline) return conn, err } if dctx, ok := d.(DialerContext); ok { return dctx.DialContext(ctx, network, address) } return d.Dial(network, address) } func network(o values) (string, string) { host := o["host"] if strings.HasPrefix(host, "/") { sockPath := path.Join(host, ".s.PGSQL."+o["port"]) return "unix", sockPath } return "tcp", net.JoinHostPort(host, o["port"]) } type values map[string]string // scanner implements a tokenizer for libpq-style option strings. type scanner struct { s []rune i int } // newScanner returns a new scanner initialized with the option string s. func newScanner(s string) *scanner { return &scanner{[]rune(s), 0} } // Next returns the next rune. // It returns 0, false if the end of the text has been reached. func (s *scanner) Next() (rune, bool) { if s.i >= len(s.s) { return 0, false } r := s.s[s.i] s.i++ return r, true } // SkipSpaces returns the next non-whitespace rune. // It returns 0, false if the end of the text has been reached. func (s *scanner) SkipSpaces() (rune, bool) { r, ok := s.Next() for unicode.IsSpace(r) && ok { r, ok = s.Next() } return r, ok } // parseOpts parses the options from name and adds them to the values. // // The parsing code is based on conninfo_parse from libpq's fe-connect.c func parseOpts(name string, o values) error { s := newScanner(name) for { var ( keyRunes, valRunes []rune r rune ok bool ) if r, ok = s.SkipSpaces(); !ok { break } // Scan the key for !unicode.IsSpace(r) && r != '=' { keyRunes = append(keyRunes, r) if r, ok = s.Next(); !ok { break } } // Skip any whitespace if we're not at the = yet if r != '=' { r, ok = s.SkipSpaces() } // The current character should be = if r != '=' || !ok { return fmt.Errorf(`missing "=" after %q in connection info string"`, string(keyRunes)) } // Skip any whitespace after the = if r, ok = s.SkipSpaces(); !ok { // If we reach the end here, the last value is just an empty string as per libpq. o[string(keyRunes)] = "" break } if r != '\'' { for !unicode.IsSpace(r) { if r == '\\' { if r, ok = s.Next(); !ok { return fmt.Errorf(`missing character after backslash`) } } valRunes = append(valRunes, r) if r, ok = s.Next(); !ok { break } } } else { quote: for { if r, ok = s.Next(); !ok { return fmt.Errorf(`unterminated quoted string literal in connection string`) } switch r { case '\'': break quote case '\\': r, _ = s.Next() fallthrough default: valRunes = append(valRunes, r) } } } o[string(keyRunes)] = string(valRunes) } return nil } func (cn *conn) isInTransaction() bool { return cn.txnStatus == txnStatusIdleInTransaction || cn.txnStatus == txnStatusInFailedTransaction } func (cn *conn) checkIsInTransaction(intxn bool) { if cn.isInTransaction() != intxn { cn.bad = true errorf("unexpected transaction status %v", cn.txnStatus) } } func (cn *conn) Begin() (_ driver.Tx, err error) { return cn.begin("") } func (cn *conn) begin(mode string) (_ driver.Tx, err error) { if cn.bad { return nil, driver.ErrBadConn } defer cn.errRecover(&err) cn.checkIsInTransaction(false) _, commandTag, err := cn.simpleExec("BEGIN" + mode) if err != nil { return nil, err } if commandTag != "BEGIN" { cn.bad = true return nil, fmt.Errorf("unexpected command tag %s", commandTag) } if cn.txnStatus != txnStatusIdleInTransaction { cn.bad = true return nil, fmt.Errorf("unexpected transaction status %v", cn.txnStatus) } return cn, nil } func (cn *conn) closeTxn() { if finish := cn.txnFinish; finish != nil { finish() } } func (cn *conn) Commit() (err error) { defer cn.closeTxn() if cn.bad { return driver.ErrBadConn } defer cn.errRecover(&err) cn.checkIsInTransaction(true) // We don't want the client to think that everything is okay if it tries // to commit a failed transaction. However, no matter what we return, // database/sql will release this connection back into the free connection // pool so we have to abort the current transaction here. Note that you // would get the same behaviour if you issued a COMMIT in a failed // transaction, so it's also the least surprising thing to do here. if cn.txnStatus == txnStatusInFailedTransaction { if err := cn.rollback(); err != nil { return err } return ErrInFailedTransaction } _, commandTag, err := cn.simpleExec("COMMIT") if err != nil { if cn.isInTransaction() { cn.bad = true } return err } if commandTag != "COMMIT" { cn.bad = true return fmt.Errorf("unexpected command tag %s", commandTag) } cn.checkIsInTransaction(false) return nil } func (cn *conn) Rollback() (err error) { defer cn.closeTxn() if cn.bad { return driver.ErrBadConn } defer cn.errRecover(&err) return cn.rollback() } func (cn *conn) rollback() (err error) { cn.checkIsInTransaction(true) _, commandTag, err := cn.simpleExec("ROLLBACK") if err != nil { if cn.isInTransaction() { cn.bad = true } return err } if commandTag != "ROLLBACK" { return fmt.Errorf("unexpected command tag %s", commandTag) } cn.checkIsInTransaction(false) return nil } func (cn *conn) gname() string { cn.namei++ return strconv.FormatInt(int64(cn.namei), 10) } func (cn *conn) simpleExec(q string) (res driver.Result, commandTag string, err error) { b := cn.writeBuf('Q') b.string(q) cn.send(b) for { t, r := cn.recv1() switch t { case 'C': res, commandTag = cn.parseComplete(r.string()) case 'Z': cn.processReadyForQuery(r) if res == nil && err == nil { err = errUnexpectedReady } // done return case 'E': err = parseError(r) case 'I': res = emptyRows case 'T', 'D': // ignore any results default: cn.bad = true errorf("unknown response for simple query: %q", t) } } } func (cn *conn) simpleQuery(q string) (res *rows, err error) { defer cn.errRecover(&err) b := cn.writeBuf('Q') b.string(q) cn.send(b) for { t, r := cn.recv1() switch t { case 'C', 'I': // We allow queries which don't return any results through Query as // well as Exec. We still have to give database/sql a rows object // the user can close, though, to avoid connections from being // leaked. A "rows" with done=true works fine for that purpose. if err != nil { cn.bad = true errorf("unexpected message %q in simple query execution", t) } if res == nil { res = &rows{ cn: cn, } } // Set the result and tag to the last command complete if there wasn't a // query already run. Although queries usually return from here and cede // control to Next, a query with zero results does not. if t == 'C' && res.colNames == nil { res.result, res.tag = cn.parseComplete(r.string()) } res.done = true case 'Z': cn.processReadyForQuery(r) // done return case 'E': res = nil err = parseError(r) case 'D': if res == nil { cn.bad = true errorf("unexpected DataRow in simple query execution") } // the query didn't fail; kick off to Next cn.saveMessage(t, r) return case 'T': // res might be non-nil here if we received a previous // CommandComplete, but that's fine; just overwrite it res = &rows{cn: cn} res.rowsHeader = parsePortalRowDescribe(r) // To work around a bug in QueryRow in Go 1.2 and earlier, wait // until the first DataRow has been received. default: cn.bad = true errorf("unknown response for simple query: %q", t) } } } type noRows struct{} var emptyRows noRows var _ driver.Result = noRows{} func (noRows) LastInsertId() (int64, error) { return 0, errNoLastInsertID } func (noRows) RowsAffected() (int64, error) { return 0, errNoRowsAffected } // Decides which column formats to use for a prepared statement. The input is // an array of type oids, one element per result column. func decideColumnFormats(colTyps []fieldDesc, forceText bool) (colFmts []format, colFmtData []byte) { if len(colTyps) == 0 { return nil, colFmtDataAllText } colFmts = make([]format, len(colTyps)) if forceText { return colFmts, colFmtDataAllText } allBinary := true allText := true for i, t := range colTyps { switch t.OID { // This is the list of types to use binary mode for when receiving them // through a prepared statement. If a type appears in this list, it // must also be implemented in binaryDecode in encode.go. case oid.T_bytea: fallthrough case oid.T_int8: fallthrough case oid.T_int4: fallthrough case oid.T_int2: fallthrough case oid.T_uuid: colFmts[i] = formatBinary allText = false default: allBinary = false } } if allBinary { return colFmts, colFmtDataAllBinary } else if allText { return colFmts, colFmtDataAllText } else { colFmtData = make([]byte, 2+len(colFmts)*2) binary.BigEndian.PutUint16(colFmtData, uint16(len(colFmts))) for i, v := range colFmts { binary.BigEndian.PutUint16(colFmtData[2+i*2:], uint16(v)) } return colFmts, colFmtData } } func (cn *conn) prepareTo(q, stmtName string) *stmt { st := &stmt{cn: cn, name: stmtName} b := cn.writeBuf('P') b.string(st.name) b.string(q) b.int16(0) b.next('D') b.byte('S') b.string(st.name) b.next('S') cn.send(b) cn.readParseResponse() st.paramTyps, st.colNames, st.colTyps = cn.readStatementDescribeResponse() st.colFmts, st.colFmtData = decideColumnFormats(st.colTyps, cn.disablePreparedBinaryResult) cn.readReadyForQuery() return st } func (cn *conn) Prepare(q string) (_ driver.Stmt, err error) { if cn.bad { return nil, driver.ErrBadConn } defer cn.errRecover(&err) if len(q) >= 4 && strings.EqualFold(q[:4], "COPY") { s, err := cn.prepareCopyIn(q) if err == nil { cn.inCopy = true } return s, err } return cn.prepareTo(q, cn.gname()), nil } func (cn *conn) Close() (err error) { // Skip cn.bad return here because we always want to close a connection. defer cn.errRecover(&err) // Ensure that cn.c.Close is always run. Since error handling is done with // panics and cn.errRecover, the Close must be in a defer. defer func() { cerr := cn.c.Close() if err == nil { err = cerr } }() // Don't go through send(); ListenerConn relies on us not scribbling on the // scratch buffer of this connection. return cn.sendSimpleMessage('X') } // Implement the "Queryer" interface func (cn *conn) Query(query string, args []driver.Value) (driver.Rows, error) { return cn.query(query, args) } func (cn *conn) query(query string, args []driver.Value) (_ *rows, err error) { if cn.bad { return nil, driver.ErrBadConn } if cn.inCopy { return nil, errCopyInProgress } defer cn.errRecover(&err) // Check to see if we can use the "simpleQuery" interface, which is // *much* faster than going through prepare/exec if len(args) == 0 { return cn.simpleQuery(query) } if cn.binaryParameters { cn.sendBinaryModeQuery(query, args) cn.readParseResponse() cn.readBindResponse() rows := &rows{cn: cn} rows.rowsHeader = cn.readPortalDescribeResponse() cn.postExecuteWorkaround() return rows, nil } st := cn.prepareTo(query, "") st.exec(args) return &rows{ cn: cn, rowsHeader: st.rowsHeader, }, nil } // Implement the optional "Execer" interface for one-shot queries func (cn *conn) Exec(query string, args []driver.Value) (res driver.Result, err error) { if cn.bad { return nil, driver.ErrBadConn } defer cn.errRecover(&err) // Check to see if we can use the "simpleExec" interface, which is // *much* faster than going through prepare/exec if len(args) == 0 { // ignore commandTag, our caller doesn't care r, _, err := cn.simpleExec(query) return r, err } if cn.binaryParameters { cn.sendBinaryModeQuery(query, args) cn.readParseResponse() cn.readBindResponse() cn.readPortalDescribeResponse() cn.postExecuteWorkaround() res, _, err = cn.readExecuteResponse("Execute") return res, err } // Use the unnamed statement to defer planning until bind // time, or else value-based selectivity estimates cannot be // used. st := cn.prepareTo(query, "") r, err := st.Exec(args) if err != nil { panic(err) } return r, err } func (cn *conn) send(m *writeBuf) { _, err := cn.c.Write(m.wrap()) if err != nil { panic(err) } } func (cn *conn) sendStartupPacket(m *writeBuf) error { _, err := cn.c.Write((m.wrap())[1:]) return err } // Send a message of type typ to the server on the other end of cn. The // message should have no payload. This method does not use the scratch // buffer. func (cn *conn) sendSimpleMessage(typ byte) (err error) { _, err = cn.c.Write([]byte{typ, '\x00', '\x00', '\x00', '\x04'}) return err } // saveMessage memorizes a message and its buffer in the conn struct. // recvMessage will then return these values on the next call to it. This // method is useful in cases where you have to see what the next message is // going to be (e.g. to see whether it's an error or not) but you can't handle // the message yourself. func (cn *conn) saveMessage(typ byte, buf *readBuf) { if cn.saveMessageType != 0 { cn.bad = true errorf("unexpected saveMessageType %d", cn.saveMessageType) } cn.saveMessageType = typ cn.saveMessageBuffer = *buf } // recvMessage receives any message from the backend, or returns an error if // a problem occurred while reading the message. func (cn *conn) recvMessage(r *readBuf) (byte, error) { // workaround for a QueryRow bug, see exec if cn.saveMessageType != 0 { t := cn.saveMessageType *r = cn.saveMessageBuffer cn.saveMessageType = 0 cn.saveMessageBuffer = nil return t, nil } x := cn.scratch[:5] _, err := io.ReadFull(cn.buf, x) if err != nil { return 0, err } // read the type and length of the message that follows t := x[0] n := int(binary.BigEndian.Uint32(x[1:])) - 4 var y []byte if n <= len(cn.scratch) { y = cn.scratch[:n] } else { y = make([]byte, n) } _, err = io.ReadFull(cn.buf, y) if err != nil { return 0, err } *r = y return t, nil } // recv receives a message from the backend, but if an error happened while // reading the message or the received message was an ErrorResponse, it panics. // NoticeResponses are ignored. This function should generally be used only // during the startup sequence. func (cn *conn) recv() (t byte, r *readBuf) { for { var err error r = &readBuf{} t, err = cn.recvMessage(r) if err != nil { panic(err) } switch t { case 'E': panic(parseError(r)) case 'N': if n := cn.noticeHandler; n != nil { n(parseError(r)) } case 'A': if n := cn.notificationHandler; n != nil { n(recvNotification(r)) } default: return } } } // recv1Buf is exactly equivalent to recv1, except it uses a buffer supplied by // the caller to avoid an allocation. func (cn *conn) recv1Buf(r *readBuf) byte { for { t, err := cn.recvMessage(r) if err != nil { panic(err) } switch t { case 'A': if n := cn.notificationHandler; n != nil { n(recvNotification(r)) } case 'N': if n := cn.noticeHandler; n != nil { n(parseError(r)) } case 'S': cn.processParameterStatus(r) default: return t } } } // recv1 receives a message from the backend, panicking if an error occurs // while attempting to read it. All asynchronous messages are ignored, with // the exception of ErrorResponse. func (cn *conn) recv1() (t byte, r *readBuf) { r = &readBuf{} t = cn.recv1Buf(r) return t, r } func (cn *conn) ssl(o values) error { upgrade, err := ssl(o) if err != nil { return err } if upgrade == nil { // Nothing to do return nil } w := cn.writeBuf(0) w.int32(80877103) if err = cn.sendStartupPacket(w); err != nil { return err } b := cn.scratch[:1] _, err = io.ReadFull(cn.c, b) if err != nil { return err } if b[0] != 'S' { return ErrSSLNotSupported } cn.c, err = upgrade(cn.c) return err } // isDriverSetting returns true iff a setting is purely for configuring the // driver's options and should not be sent to the server in the connection // startup packet. func isDriverSetting(key string) bool { switch key { case "host", "port": return true case "password": return true case "sslmode", "sslcert", "sslkey", "sslrootcert": return true case "fallback_application_name": return true case "connect_timeout": return true case "disable_prepared_binary_result": return true case "binary_parameters": return true default: return false } } func (cn *conn) startup(o values) { w := cn.writeBuf(0) w.int32(196608) // Send the backend the name of the database we want to connect to, and the // user we want to connect as. Additionally, we send over any run-time // parameters potentially included in the connection string. If the server // doesn't recognize any of them, it will reply with an error. for k, v := range o { if isDriverSetting(k) { // skip options which can't be run-time parameters continue } // The protocol requires us to supply the database name as "database" // instead of "dbname". if k == "dbname" { k = "database" } w.string(k) w.string(v) } w.string("") if err := cn.sendStartupPacket(w); err != nil { panic(err) } for { t, r := cn.recv() switch t { case 'K': cn.processBackendKeyData(r) case 'S': cn.processParameterStatus(r) case 'R': cn.auth(r, o) case 'Z': cn.processReadyForQuery(r) return default: errorf("unknown response for startup: %q", t) } } } func (cn *conn) auth(r *readBuf, o values) { switch code := r.int32(); code { case 0: // OK case 3: w := cn.writeBuf('p') w.string(o["password"]) cn.send(w) t, r := cn.recv() if t != 'R' { errorf("unexpected password response: %q", t) } if r.int32() != 0 { errorf("unexpected authentication response: %q", t) } case 5: s := string(r.next(4)) w := cn.writeBuf('p') w.string("md5" + md5s(md5s(o["password"]+o["user"])+s)) cn.send(w) t, r := cn.recv() if t != 'R' { errorf("unexpected password response: %q", t) } if r.int32() != 0 { errorf("unexpected authentication response: %q", t) } case 10: sc := scram.NewClient(sha256.New, o["user"], o["password"]) sc.Step(nil) if sc.Err() != nil { errorf("SCRAM-SHA-256 error: %s", sc.Err().Error()) } scOut := sc.Out() w := cn.writeBuf('p') w.string("SCRAM-SHA-256") w.int32(len(scOut)) w.bytes(scOut) cn.send(w) t, r := cn.recv() if t != 'R' { errorf("unexpected password response: %q", t) } if r.int32() != 11 { errorf("unexpected authentication response: %q", t) } nextStep := r.next(len(*r)) sc.Step(nextStep) if sc.Err() != nil { errorf("SCRAM-SHA-256 error: %s", sc.Err().Error()) } scOut = sc.Out() w = cn.writeBuf('p') w.bytes(scOut) cn.send(w) t, r = cn.recv() if t != 'R' { errorf("unexpected password response: %q", t) } if r.int32() != 12 { errorf("unexpected authentication response: %q", t) } nextStep = r.next(len(*r)) sc.Step(nextStep) if sc.Err() != nil { errorf("SCRAM-SHA-256 error: %s", sc.Err().Error()) } default: errorf("unknown authentication response: %d", code) } } type format int const formatText format = 0 const formatBinary format = 1 // One result-column format code with the value 1 (i.e. all binary). var colFmtDataAllBinary = []byte{0, 1, 0, 1} // No result-column format codes (i.e. all text). var colFmtDataAllText = []byte{0, 0} type stmt struct { cn *conn name string rowsHeader colFmtData []byte paramTyps []oid.Oid closed bool } func (st *stmt) Close() (err error) { if st.closed { return nil } if st.cn.bad { return driver.ErrBadConn } defer st.cn.errRecover(&err) w := st.cn.writeBuf('C') w.byte('S') w.string(st.name) st.cn.send(w) st.cn.send(st.cn.writeBuf('S')) t, _ := st.cn.recv1() if t != '3' { st.cn.bad = true errorf("unexpected close response: %q", t) } st.closed = true t, r := st.cn.recv1() if t != 'Z' { st.cn.bad = true errorf("expected ready for query, but got: %q", t) } st.cn.processReadyForQuery(r) return nil } func (st *stmt) Query(v []driver.Value) (r driver.Rows, err error) { if st.cn.bad { return nil, driver.ErrBadConn } defer st.cn.errRecover(&err) st.exec(v) return &rows{ cn: st.cn, rowsHeader: st.rowsHeader, }, nil } func (st *stmt) Exec(v []driver.Value) (res driver.Result, err error) { if st.cn.bad { return nil, driver.ErrBadConn } defer st.cn.errRecover(&err) st.exec(v) res, _, err = st.cn.readExecuteResponse("simple query") return res, err } func (st *stmt) exec(v []driver.Value) { if len(v) >= 65536 { errorf("got %d parameters but PostgreSQL only supports 65535 parameters", len(v)) } if len(v) != len(st.paramTyps) { errorf("got %d parameters but the statement requires %d", len(v), len(st.paramTyps)) } cn := st.cn w := cn.writeBuf('B') w.byte(0) // unnamed portal w.string(st.name) if cn.binaryParameters { cn.sendBinaryParameters(w, v) } else { w.int16(0) w.int16(len(v)) for i, x := range v { if x == nil { w.int32(-1) } else { b := encode(&cn.parameterStatus, x, st.paramTyps[i]) w.int32(len(b)) w.bytes(b) } } } w.bytes(st.colFmtData) w.next('E') w.byte(0) w.int32(0) w.next('S') cn.send(w) cn.readBindResponse() cn.postExecuteWorkaround() } func (st *stmt) NumInput() int { return len(st.paramTyps) } // parseComplete parses the "command tag" from a CommandComplete message, and // returns the number of rows affected (if applicable) and a string // identifying only the command that was executed, e.g. "ALTER TABLE". If the // command tag could not be parsed, parseComplete panics. func (cn *conn) parseComplete(commandTag string) (driver.Result, string) { commandsWithAffectedRows := []string{ "SELECT ", // INSERT is handled below "UPDATE ", "DELETE ", "FETCH ", "MOVE ", "COPY ", } var affectedRows *string for _, tag := range commandsWithAffectedRows { if strings.HasPrefix(commandTag, tag) { t := commandTag[len(tag):] affectedRows = &t commandTag = tag[:len(tag)-1] break } } // INSERT also includes the oid of the inserted row in its command tag. // Oids in user tables are deprecated, and the oid is only returned when // exactly one row is inserted, so it's unlikely to be of value to any // real-world application and we can ignore it. if affectedRows == nil && strings.HasPrefix(commandTag, "INSERT ") { parts := strings.Split(commandTag, " ") if len(parts) != 3 { cn.bad = true errorf("unexpected INSERT command tag %s", commandTag) } affectedRows = &parts[len(parts)-1] commandTag = "INSERT" } // There should be no affected rows attached to the tag, just return it if affectedRows == nil { return driver.RowsAffected(0), commandTag } n, err := strconv.ParseInt(*affectedRows, 10, 64) if err != nil { cn.bad = true errorf("could not parse commandTag: %s", err) } return driver.RowsAffected(n), commandTag } type rowsHeader struct { colNames []string colTyps []fieldDesc colFmts []format } type rows struct { cn *conn finish func() rowsHeader done bool rb readBuf result driver.Result tag string next *rowsHeader } func (rs *rows) Close() error { if finish := rs.finish; finish != nil { defer finish() } // no need to look at cn.bad as Next() will for { err := rs.Next(nil) switch err { case nil: case io.EOF: // rs.Next can return io.EOF on both 'Z' (ready for query) and 'T' (row // description, used with HasNextResultSet). We need to fetch messages until // we hit a 'Z', which is done by waiting for done to be set. if rs.done { return nil } default: return err } } } func (rs *rows) Columns() []string { return rs.colNames } func (rs *rows) Result() driver.Result { if rs.result == nil { return emptyRows } return rs.result } func (rs *rows) Tag() string { return rs.tag } func (rs *rows) Next(dest []driver.Value) (err error) { if rs.done { return io.EOF } conn := rs.cn if conn.bad { return driver.ErrBadConn } defer conn.errRecover(&err) for { t := conn.recv1Buf(&rs.rb) switch t { case 'E': err = parseError(&rs.rb) case 'C', 'I': if t == 'C' { rs.result, rs.tag = conn.parseComplete(rs.rb.string()) } continue case 'Z': conn.processReadyForQuery(&rs.rb) rs.done = true if err != nil { return err } return io.EOF case 'D': n := rs.rb.int16() if err != nil { conn.bad = true errorf("unexpected DataRow after error %s", err) } if n < len(dest) { dest = dest[:n] } for i := range dest { l := rs.rb.int32() if l == -1 { dest[i] = nil continue } dest[i] = decode(&conn.parameterStatus, rs.rb.next(l), rs.colTyps[i].OID, rs.colFmts[i]) } return case 'T': next := parsePortalRowDescribe(&rs.rb) rs.next = &next return io.EOF default: errorf("unexpected message after execute: %q", t) } } } func (rs *rows) HasNextResultSet() bool { hasNext := rs.next != nil && !rs.done return hasNext } func (rs *rows) NextResultSet() error { if rs.next == nil { return io.EOF } rs.rowsHeader = *rs.next rs.next = nil return nil } // QuoteIdentifier quotes an "identifier" (e.g. a table or a column name) to be // used as part of an SQL statement. For example: // // tblname := "my_table" // data := "my_data" // quoted := pq.QuoteIdentifier(tblname) // err := db.Exec(fmt.Sprintf("INSERT INTO %s VALUES ($1)", quoted), data) // // Any double quotes in name will be escaped. The quoted identifier will be // case sensitive when used in a query. If the input string contains a zero // byte, the result will be truncated immediately before it. func QuoteIdentifier(name string) string { end := strings.IndexRune(name, 0) if end > -1 { name = name[:end] } return `"` + strings.Replace(name, `"`, `""`, -1) + `"` } // QuoteLiteral quotes a 'literal' (e.g. a parameter, often used to pass literal // to DDL and other statements that do not accept parameters) to be used as part // of an SQL statement. For example: // // exp_date := pq.QuoteLiteral("2023-01-05 15:00:00Z") // err := db.Exec(fmt.Sprintf("CREATE ROLE my_user VALID UNTIL %s", exp_date)) // // Any single quotes in name will be escaped. Any backslashes (i.e. "\") will be // replaced by two backslashes (i.e. "\\") and the C-style escape identifier // that PostgreSQL provides ('E') will be prepended to the string. func QuoteLiteral(literal string) string { // This follows the PostgreSQL internal algorithm for handling quoted literals // from libpq, which can be found in the "PQEscapeStringInternal" function, // which is found in the libpq/fe-exec.c source file: // https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/interfaces/libpq/fe-exec.c // // substitute any single-quotes (') with two single-quotes ('') literal = strings.Replace(literal, `'`, `''`, -1) // determine if the string has any backslashes (\) in it. // if it does, replace any backslashes (\) with two backslashes (\\) // then, we need to wrap the entire string with a PostgreSQL // C-style escape. Per how "PQEscapeStringInternal" handles this case, we // also add a space before the "E" if strings.Contains(literal, `\`) { literal = strings.Replace(literal, `\`, `\\`, -1) literal = ` E'` + literal + `'` } else { // otherwise, we can just wrap the literal with a pair of single quotes literal = `'` + literal + `'` } return literal } func md5s(s string) string { h := md5.New() h.Write([]byte(s)) return fmt.Sprintf("%x", h.Sum(nil)) } func (cn *conn) sendBinaryParameters(b *writeBuf, args []driver.Value) { // Do one pass over the parameters to see if we're going to send any of // them over in binary. If we are, create a paramFormats array at the // same time. var paramFormats []int for i, x := range args { _, ok := x.([]byte) if ok { if paramFormats == nil { paramFormats = make([]int, len(args)) } paramFormats[i] = 1 } } if paramFormats == nil { b.int16(0) } else { b.int16(len(paramFormats)) for _, x := range paramFormats { b.int16(x) } } b.int16(len(args)) for _, x := range args { if x == nil { b.int32(-1) } else { datum := binaryEncode(&cn.parameterStatus, x) b.int32(len(datum)) b.bytes(datum) } } } func (cn *conn) sendBinaryModeQuery(query string, args []driver.Value) { if len(args) >= 65536 { errorf("got %d parameters but PostgreSQL only supports 65535 parameters", len(args)) } b := cn.writeBuf('P') b.byte(0) // unnamed statement b.string(query) b.int16(0) b.next('B') b.int16(0) // unnamed portal and statement cn.sendBinaryParameters(b, args) b.bytes(colFmtDataAllText) b.next('D') b.byte('P') b.byte(0) // unnamed portal b.next('E') b.byte(0) b.int32(0) b.next('S') cn.send(b) } func (cn *conn) processParameterStatus(r *readBuf) { var err error param := r.string() switch param { case "server_version": var major1 int var major2 int var minor int _, err = fmt.Sscanf(r.string(), "%d.%d.%d", &major1, &major2, &minor) if err == nil { cn.parameterStatus.serverVersion = major1*10000 + major2*100 + minor } case "TimeZone": cn.parameterStatus.currentLocation, err = time.LoadLocation(r.string()) if err != nil { cn.parameterStatus.currentLocation = nil } default: // ignore } } func (cn *conn) processReadyForQuery(r *readBuf) { cn.txnStatus = transactionStatus(r.byte()) } func (cn *conn) readReadyForQuery() { t, r := cn.recv1() switch t { case 'Z': cn.processReadyForQuery(r) return default: cn.bad = true errorf("unexpected message %q; expected ReadyForQuery", t) } } func (cn *conn) processBackendKeyData(r *readBuf) { cn.processID = r.int32() cn.secretKey = r.int32() } func (cn *conn) readParseResponse() { t, r := cn.recv1() switch t { case '1': return case 'E': err := parseError(r) cn.readReadyForQuery() panic(err) default: cn.bad = true errorf("unexpected Parse response %q", t) } } func (cn *conn) readStatementDescribeResponse() (paramTyps []oid.Oid, colNames []string, colTyps []fieldDesc) { for { t, r := cn.recv1() switch t { case 't': nparams := r.int16() paramTyps = make([]oid.Oid, nparams) for i := range paramTyps { paramTyps[i] = r.oid() } case 'n': return paramTyps, nil, nil case 'T': colNames, colTyps = parseStatementRowDescribe(r) return paramTyps, colNames, colTyps case 'E': err := parseError(r) cn.readReadyForQuery() panic(err) default: cn.bad = true errorf("unexpected Describe statement response %q", t) } } } func (cn *conn) readPortalDescribeResponse() rowsHeader { t, r := cn.recv1() switch t { case 'T': return parsePortalRowDescribe(r) case 'n': return rowsHeader{} case 'E': err := parseError(r) cn.readReadyForQuery() panic(err) default: cn.bad = true errorf("unexpected Describe response %q", t) } panic("not reached") } func (cn *conn) readBindResponse() { t, r := cn.recv1() switch t { case '2': return case 'E': err := parseError(r) cn.readReadyForQuery() panic(err) default: cn.bad = true errorf("unexpected Bind response %q", t) } } func (cn *conn) postExecuteWorkaround() { // Work around a bug in sql.DB.QueryRow: in Go 1.2 and earlier it ignores // any errors from rows.Next, which masks errors that happened during the // execution of the query. To avoid the problem in common cases, we wait // here for one more message from the database. If it's not an error the // query will likely succeed (or perhaps has already, if it's a // CommandComplete), so we push the message into the conn struct; recv1 // will return it as the next message for rows.Next or rows.Close. // However, if it's an error, we wait until ReadyForQuery and then return // the error to our caller. for { t, r := cn.recv1() switch t { case 'E': err := parseError(r) cn.readReadyForQuery() panic(err) case 'C', 'D', 'I': // the query didn't fail, but we can't process this message cn.saveMessage(t, r) return default: cn.bad = true errorf("unexpected message during extended query execution: %q", t) } } } // Only for Exec(), since we ignore the returned data func (cn *conn) readExecuteResponse(protocolState string) (res driver.Result, commandTag string, err error) { for { t, r := cn.recv1() switch t { case 'C': if err != nil { cn.bad = true errorf("unexpected CommandComplete after error %s", err) } res, commandTag = cn.parseComplete(r.string()) case 'Z': cn.processReadyForQuery(r) if res == nil && err == nil { err = errUnexpectedReady } return res, commandTag, err case 'E': err = parseError(r) case 'T', 'D', 'I': if err != nil { cn.bad = true errorf("unexpected %q after error %s", t, err) } if t == 'I' { res = emptyRows } // ignore any results default: cn.bad = true errorf("unknown %s response: %q", protocolState, t) } } } func parseStatementRowDescribe(r *readBuf) (colNames []string, colTyps []fieldDesc) { n := r.int16() colNames = make([]string, n) colTyps = make([]fieldDesc, n) for i := range colNames { colNames[i] = r.string() r.next(6) colTyps[i].OID = r.oid() colTyps[i].Len = r.int16() colTyps[i].Mod = r.int32() // format code not known when describing a statement; always 0 r.next(2) } return } func parsePortalRowDescribe(r *readBuf) rowsHeader { n := r.int16() colNames := make([]string, n) colFmts := make([]format, n) colTyps := make([]fieldDesc, n) for i := range colNames { colNames[i] = r.string() r.next(6) colTyps[i].OID = r.oid() colTyps[i].Len = r.int16() colTyps[i].Mod = r.int32() colFmts[i] = format(r.int16()) } return rowsHeader{ colNames: colNames, colFmts: colFmts, colTyps: colTyps, } } // parseEnviron tries to mimic some of libpq's environment handling // // To ease testing, it does not directly reference os.Environ, but is // designed to accept its output. // // Environment-set connection information is intended to have a higher // precedence than a library default but lower than any explicitly // passed information (such as in the URL or connection string). func parseEnviron(env []string) (out map[string]string) { out = make(map[string]string) for _, v := range env { parts := strings.SplitN(v, "=", 2) accrue := func(keyname string) { out[keyname] = parts[1] } unsupported := func() { panic(fmt.Sprintf("setting %v not supported", parts[0])) } // The order of these is the same as is seen in the // PostgreSQL 9.1 manual. Unsupported but well-defined // keys cause a panic; these should be unset prior to // execution. Options which pq expects to be set to a // certain value are allowed, but must be set to that // value if present (they can, of course, be absent). switch parts[0] { case "PGHOST": accrue("host") case "PGHOSTADDR": unsupported() case "PGPORT": accrue("port") case "PGDATABASE": accrue("dbname") case "PGUSER": accrue("user") case "PGPASSWORD": accrue("password") case "PGSERVICE", "PGSERVICEFILE", "PGREALM": unsupported() case "PGOPTIONS": accrue("options") case "PGAPPNAME": accrue("application_name") case "PGSSLMODE": accrue("sslmode") case "PGSSLCERT": accrue("sslcert") case "PGSSLKEY": accrue("sslkey") case "PGSSLROOTCERT": accrue("sslrootcert") case "PGREQUIRESSL", "PGSSLCRL": unsupported() case "PGREQUIREPEER": unsupported() case "PGKRBSRVNAME", "PGGSSLIB": unsupported() case "PGCONNECT_TIMEOUT": accrue("connect_timeout") case "PGCLIENTENCODING": accrue("client_encoding") case "PGDATESTYLE": accrue("datestyle") case "PGTZ": accrue("timezone") case "PGGEQO": accrue("geqo") case "PGSYSCONFDIR", "PGLOCALEDIR": unsupported() } } return out } // isUTF8 returns whether name is a fuzzy variation of the string "UTF-8". func isUTF8(name string) bool { // Recognize all sorts of silly things as "UTF-8", like Postgres does s := strings.Map(alnumLowerASCII, name) return s == "utf8" || s == "unicode" } func alnumLowerASCII(ch rune) rune { if 'A' <= ch && ch <= 'Z' { return ch + ('a' - 'A') } if 'a' <= ch && ch <= 'z' || '0' <= ch && ch <= '9' { return ch } return -1 // discard } golang-github-lib-pq-1.5.2/conn_go18.go000066400000000000000000000062701365507746300175770ustar00rootroot00000000000000package pq import ( "context" "database/sql" "database/sql/driver" "fmt" "io" "io/ioutil" "time" ) // 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) Ping(ctx context.Context) error { if finish := cn.watchCancel(ctx); finish != nil { defer finish() } rows, err := cn.simpleQuery(";") if err != nil { return driver.ErrBadConn // https://golang.org/pkg/database/sql/driver/#Pinger } rows.Close() return nil } func (cn *conn) watchCancel(ctx context.Context) func() { if done := ctx.Done(); done != nil { finished := make(chan struct{}) go func() { select { case <-done: // At this point the function level context is canceled, // so it must not be used for the additional network // request to cancel the query. // Create a new context to pass into the dial. ctxCancel, cancel := context.WithTimeout(context.Background(), time.Second*10) defer cancel() _ = cn.cancel(ctxCancel) finished <- struct{}{} case <-finished: } }() return func() { select { case <-finished: case finished <- struct{}{}: } } } return nil } func (cn *conn) cancel(ctx context.Context) error { c, err := dial(ctx, cn.dialer, cn.opts) if err != nil { return err } defer c.Close() { can := conn{ c: c, } err = can.ssl(cn.opts) if err != nil { return err } 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 } } golang-github-lib-pq-1.5.2/conn_test.go000066400000000000000000001157141365507746300200040ustar00rootroot00000000000000package pq import ( "context" "database/sql" "database/sql/driver" "fmt" "io" "net" "os" "reflect" "strings" "testing" "time" ) type Fatalistic interface { Fatal(args ...interface{}) } func forceBinaryParameters() bool { bp := os.Getenv("PQTEST_BINARY_PARAMETERS") if bp == "yes" { return true } else if bp == "" || bp == "no" { return false } else { panic("unexpected value for PQTEST_BINARY_PARAMETERS") } } func testConninfo(conninfo string) string { defaultTo := func(envvar string, value string) { if os.Getenv(envvar) == "" { os.Setenv(envvar, value) } } defaultTo("PGDATABASE", "pqgotest") defaultTo("PGSSLMODE", "disable") defaultTo("PGCONNECT_TIMEOUT", "20") if forceBinaryParameters() && !strings.HasPrefix(conninfo, "postgres://") && !strings.HasPrefix(conninfo, "postgresql://") { conninfo += " binary_parameters=yes" } return conninfo } func openTestConnConninfo(conninfo string) (*sql.DB, error) { return sql.Open("postgres", testConninfo(conninfo)) } func openTestConn(t Fatalistic) *sql.DB { conn, err := openTestConnConninfo("") if err != nil { t.Fatal(err) } return conn } func getServerVersion(t *testing.T, db *sql.DB) int { var version int err := db.QueryRow("SHOW server_version_num").Scan(&version) if err != nil { t.Fatal(err) } return version } func TestReconnect(t *testing.T) { db1 := openTestConn(t) defer db1.Close() tx, err := db1.Begin() if err != nil { t.Fatal(err) } var pid1 int err = tx.QueryRow("SELECT pg_backend_pid()").Scan(&pid1) if err != nil { t.Fatal(err) } db2 := openTestConn(t) defer db2.Close() _, err = db2.Exec("SELECT pg_terminate_backend($1)", pid1) if err != nil { t.Fatal(err) } // The rollback will probably "fail" because we just killed // its connection above _ = tx.Rollback() const expected int = 42 var result int err = db1.QueryRow(fmt.Sprintf("SELECT %d", expected)).Scan(&result) if err != nil { t.Fatal(err) } if result != expected { t.Errorf("got %v; expected %v", result, expected) } } func TestCommitInFailedTransaction(t *testing.T) { db := openTestConn(t) defer db.Close() txn, err := db.Begin() if err != nil { t.Fatal(err) } rows, err := txn.Query("SELECT error") if err == nil { rows.Close() t.Fatal("expected failure") } err = txn.Commit() if err != ErrInFailedTransaction { t.Fatalf("expected ErrInFailedTransaction; got %#v", err) } } func TestOpenURL(t *testing.T) { testURL := func(url string) { db, err := openTestConnConninfo(url) if err != nil { t.Fatal(err) } defer db.Close() // database/sql might not call our Open at all unless we do something with // the connection txn, err := db.Begin() if err != nil { t.Fatal(err) } txn.Rollback() } testURL("postgres://") testURL("postgresql://") } const pgpassFile = "/tmp/pqgotest_pgpass" func TestPgpass(t *testing.T) { if os.Getenv("TRAVIS") != "true" { t.Skip("not running under Travis, skipping pgpass tests") } testAssert := func(conninfo string, expected string, reason string) { conn, err := openTestConnConninfo(conninfo) if err != nil { t.Fatal(err) } defer conn.Close() txn, err := conn.Begin() if err != nil { if expected != "fail" { t.Fatalf(reason, err) } return } rows, err := txn.Query("SELECT USER") if err != nil { txn.Rollback() if expected != "fail" { t.Fatalf(reason, err) } } else { rows.Close() if expected != "ok" { t.Fatalf(reason, err) } } txn.Rollback() } testAssert("", "ok", "missing .pgpass, unexpected error %#v") os.Setenv("PGPASSFILE", pgpassFile) testAssert("host=/tmp", "fail", ", unexpected error %#v") os.Remove(pgpassFile) pgpass, err := os.OpenFile(pgpassFile, os.O_RDWR|os.O_CREATE, 0644) if err != nil { t.Fatalf("Unexpected error writing pgpass file %#v", err) } _, err = pgpass.WriteString(`# comment server:5432:some_db:some_user:pass_A *:5432:some_db:some_user:pass_B localhost:*:*:*:pass_C *:*:*:*:pass_fallback `) if err != nil { t.Fatalf("Unexpected error writing pgpass file %#v", err) } pgpass.Close() assertPassword := func(extra values, expected string) { o := values{ "host": "localhost", "sslmode": "disable", "connect_timeout": "20", "user": "majid", "port": "5432", "extra_float_digits": "2", "dbname": "pqgotest", "client_encoding": "UTF8", "datestyle": "ISO, MDY", } for k, v := range extra { o[k] = v } (&conn{}).handlePgpass(o) if pw := o["password"]; pw != expected { t.Fatalf("For %v expected %s got %s", extra, expected, pw) } } // wrong permissions for the pgpass file means it should be ignored assertPassword(values{"host": "example.com", "user": "foo"}, "") // fix the permissions and check if it has taken effect os.Chmod(pgpassFile, 0600) assertPassword(values{"host": "server", "dbname": "some_db", "user": "some_user"}, "pass_A") assertPassword(values{"host": "example.com", "user": "foo"}, "pass_fallback") assertPassword(values{"host": "example.com", "dbname": "some_db", "user": "some_user"}, "pass_B") // localhost also matches the default "" and UNIX sockets assertPassword(values{"host": "", "user": "some_user"}, "pass_C") assertPassword(values{"host": "/tmp", "user": "some_user"}, "pass_C") // cleanup os.Remove(pgpassFile) os.Setenv("PGPASSFILE", "") } func TestExec(t *testing.T) { db := openTestConn(t) defer db.Close() _, err := db.Exec("CREATE TEMP TABLE temp (a int)") if err != nil { t.Fatal(err) } r, err := db.Exec("INSERT INTO temp VALUES (1)") if err != nil { t.Fatal(err) } if n, _ := r.RowsAffected(); n != 1 { t.Fatalf("expected 1 row affected, not %d", n) } r, err = db.Exec("INSERT INTO temp VALUES ($1), ($2), ($3)", 1, 2, 3) if err != nil { t.Fatal(err) } if n, _ := r.RowsAffected(); n != 3 { t.Fatalf("expected 3 rows affected, not %d", n) } // SELECT doesn't send the number of returned rows in the command tag // before 9.0 if getServerVersion(t, db) >= 90000 { r, err = db.Exec("SELECT g FROM generate_series(1, 2) g") if err != nil { t.Fatal(err) } if n, _ := r.RowsAffected(); n != 2 { t.Fatalf("expected 2 rows affected, not %d", n) } r, err = db.Exec("SELECT g FROM generate_series(1, $1) g", 3) if err != nil { t.Fatal(err) } if n, _ := r.RowsAffected(); n != 3 { t.Fatalf("expected 3 rows affected, not %d", n) } } } func TestStatment(t *testing.T) { db := openTestConn(t) defer db.Close() st, err := db.Prepare("SELECT 1") if err != nil { t.Fatal(err) } st1, err := db.Prepare("SELECT 2") if err != nil { t.Fatal(err) } r, err := st.Query() if err != nil { t.Fatal(err) } defer r.Close() if !r.Next() { t.Fatal("expected row") } var i int err = r.Scan(&i) if err != nil { t.Fatal(err) } if i != 1 { t.Fatalf("expected 1, got %d", i) } // st1 r1, err := st1.Query() if err != nil { t.Fatal(err) } defer r1.Close() if !r1.Next() { if r.Err() != nil { t.Fatal(r1.Err()) } t.Fatal("expected row") } err = r1.Scan(&i) if err != nil { t.Fatal(err) } if i != 2 { t.Fatalf("expected 2, got %d", i) } } func TestRowsCloseBeforeDone(t *testing.T) { db := openTestConn(t) defer db.Close() r, err := db.Query("SELECT 1") if err != nil { t.Fatal(err) } err = r.Close() if err != nil { t.Fatal(err) } if r.Next() { t.Fatal("unexpected row") } if r.Err() != nil { t.Fatal(r.Err()) } } func TestParameterCountMismatch(t *testing.T) { db := openTestConn(t) defer db.Close() var notused int err := db.QueryRow("SELECT false", 1).Scan(¬used) if err == nil { t.Fatal("expected err") } // make sure we clean up correctly err = db.QueryRow("SELECT 1").Scan(¬used) if err != nil { t.Fatal(err) } err = db.QueryRow("SELECT $1").Scan(¬used) if err == nil { t.Fatal("expected err") } // make sure we clean up correctly err = db.QueryRow("SELECT 1").Scan(¬used) if err != nil { t.Fatal(err) } } // Test that EmptyQueryResponses are handled correctly. func TestEmptyQuery(t *testing.T) { db := openTestConn(t) defer db.Close() res, err := db.Exec("") if err != nil { t.Fatal(err) } if _, err := res.RowsAffected(); err != errNoRowsAffected { t.Fatalf("expected %s, got %v", errNoRowsAffected, err) } if _, err := res.LastInsertId(); err != errNoLastInsertID { t.Fatalf("expected %s, got %v", errNoLastInsertID, err) } rows, err := db.Query("") if err != nil { t.Fatal(err) } cols, err := rows.Columns() if err != nil { t.Fatal(err) } if len(cols) != 0 { t.Fatalf("unexpected number of columns %d in response to an empty query", len(cols)) } if rows.Next() { t.Fatal("unexpected row") } if rows.Err() != nil { t.Fatal(rows.Err()) } stmt, err := db.Prepare("") if err != nil { t.Fatal(err) } res, err = stmt.Exec() if err != nil { t.Fatal(err) } if _, err := res.RowsAffected(); err != errNoRowsAffected { t.Fatalf("expected %s, got %v", errNoRowsAffected, err) } if _, err := res.LastInsertId(); err != errNoLastInsertID { t.Fatalf("expected %s, got %v", errNoLastInsertID, err) } rows, err = stmt.Query() if err != nil { t.Fatal(err) } cols, err = rows.Columns() if err != nil { t.Fatal(err) } if len(cols) != 0 { t.Fatalf("unexpected number of columns %d in response to an empty query", len(cols)) } if rows.Next() { t.Fatal("unexpected row") } if rows.Err() != nil { t.Fatal(rows.Err()) } } // Test that rows.Columns() is correct even if there are no result rows. func TestEmptyResultSetColumns(t *testing.T) { db := openTestConn(t) defer db.Close() rows, err := db.Query("SELECT 1 AS a, text 'bar' AS bar WHERE FALSE") if err != nil { t.Fatal(err) } cols, err := rows.Columns() if err != nil { t.Fatal(err) } if len(cols) != 2 { t.Fatalf("unexpected number of columns %d in response to an empty query", len(cols)) } if rows.Next() { t.Fatal("unexpected row") } if rows.Err() != nil { t.Fatal(rows.Err()) } if cols[0] != "a" || cols[1] != "bar" { t.Fatalf("unexpected Columns result %v", cols) } stmt, err := db.Prepare("SELECT $1::int AS a, text 'bar' AS bar WHERE FALSE") if err != nil { t.Fatal(err) } rows, err = stmt.Query(1) if err != nil { t.Fatal(err) } cols, err = rows.Columns() if err != nil { t.Fatal(err) } if len(cols) != 2 { t.Fatalf("unexpected number of columns %d in response to an empty query", len(cols)) } if rows.Next() { t.Fatal("unexpected row") } if rows.Err() != nil { t.Fatal(rows.Err()) } if cols[0] != "a" || cols[1] != "bar" { t.Fatalf("unexpected Columns result %v", cols) } } func TestEncodeDecode(t *testing.T) { db := openTestConn(t) defer db.Close() q := ` SELECT E'\\000\\001\\002'::bytea, 'foobar'::text, NULL::integer, '2000-1-1 01:02:03.04-7'::timestamptz, 0::boolean, 123, -321, 3.14::float8 WHERE E'\\000\\001\\002'::bytea = $1 AND 'foobar'::text = $2 AND $3::integer is NULL ` // AND '2000-1-1 12:00:00.000000-7'::timestamp = $3 exp1 := []byte{0, 1, 2} exp2 := "foobar" r, err := db.Query(q, exp1, exp2, nil) if err != nil { t.Fatal(err) } defer r.Close() if !r.Next() { if r.Err() != nil { t.Fatal(r.Err()) } t.Fatal("expected row") } var got1 []byte var got2 string var got3 = sql.NullInt64{Valid: true} var got4 time.Time var got5, got6, got7, got8 interface{} err = r.Scan(&got1, &got2, &got3, &got4, &got5, &got6, &got7, &got8) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(exp1, got1) { t.Errorf("expected %q byte: %q", exp1, got1) } if !reflect.DeepEqual(exp2, got2) { t.Errorf("expected %q byte: %q", exp2, got2) } if got3.Valid { t.Fatal("expected invalid") } if got4.Year() != 2000 { t.Fatal("wrong year") } if got5 != false { t.Fatalf("expected false, got %q", got5) } if got6 != int64(123) { t.Fatalf("expected 123, got %d", got6) } if got7 != int64(-321) { t.Fatalf("expected -321, got %d", got7) } if got8 != float64(3.14) { t.Fatalf("expected 3.14, got %f", got8) } } func TestNoData(t *testing.T) { db := openTestConn(t) defer db.Close() st, err := db.Prepare("SELECT 1 WHERE true = false") if err != nil { t.Fatal(err) } defer st.Close() r, err := st.Query() if err != nil { t.Fatal(err) } defer r.Close() if r.Next() { if r.Err() != nil { t.Fatal(r.Err()) } t.Fatal("unexpected row") } _, err = db.Query("SELECT * FROM nonexistenttable WHERE age=$1", 20) if err == nil { t.Fatal("Should have raised an error on non existent table") } _, err = db.Query("SELECT * FROM nonexistenttable") if err == nil { t.Fatal("Should have raised an error on non existent table") } } func TestErrorDuringStartup(t *testing.T) { // Don't use the normal connection setup, this is intended to // blow up in the startup packet from a non-existent user. db, err := openTestConnConninfo("user=thisuserreallydoesntexist") if err != nil { t.Fatal(err) } defer db.Close() _, err = db.Begin() if err == nil { t.Fatal("expected error") } e, ok := err.(*Error) if !ok { t.Fatalf("expected Error, got %#v", err) } else if e.Code.Name() != "invalid_authorization_specification" && e.Code.Name() != "invalid_password" { t.Fatalf("expected invalid_authorization_specification or invalid_password, got %s (%+v)", e.Code.Name(), err) } } type testConn struct { closed bool net.Conn } func (c *testConn) Close() error { c.closed = true return c.Conn.Close() } type testDialer struct { conns []*testConn } func (d *testDialer) Dial(ntw, addr string) (net.Conn, error) { c, err := net.Dial(ntw, addr) if err != nil { return nil, err } tc := &testConn{Conn: c} d.conns = append(d.conns, tc) return tc, nil } func (d *testDialer) DialTimeout(ntw, addr string, timeout time.Duration) (net.Conn, error) { c, err := net.DialTimeout(ntw, addr, timeout) if err != nil { return nil, err } tc := &testConn{Conn: c} d.conns = append(d.conns, tc) return tc, nil } func TestErrorDuringStartupClosesConn(t *testing.T) { // Don't use the normal connection setup, this is intended to // blow up in the startup packet from a non-existent user. var d testDialer c, err := DialOpen(&d, testConninfo("user=thisuserreallydoesntexist")) if err == nil { c.Close() t.Fatal("expected dial error") } if len(d.conns) != 1 { t.Fatalf("got len(d.conns) = %d, want = %d", len(d.conns), 1) } if !d.conns[0].closed { t.Error("connection leaked") } } func TestBadConn(t *testing.T) { var err error cn := conn{} func() { defer cn.errRecover(&err) panic(io.EOF) }() if err != driver.ErrBadConn { t.Fatalf("expected driver.ErrBadConn, got: %#v", err) } if !cn.bad { t.Fatalf("expected cn.bad") } cn = conn{} func() { defer cn.errRecover(&err) e := &Error{Severity: Efatal} panic(e) }() if err != driver.ErrBadConn { t.Fatalf("expected driver.ErrBadConn, got: %#v", err) } if !cn.bad { t.Fatalf("expected cn.bad") } } // TestCloseBadConn tests that the underlying connection can be closed with // Close after an error. func TestCloseBadConn(t *testing.T) { host := os.Getenv("PGHOST") if host == "" { host = "localhost" } port := os.Getenv("PGPORT") if port == "" { port = "5432" } nc, err := net.Dial("tcp", host+":"+port) if err != nil { t.Fatal(err) } cn := conn{c: nc} func() { defer cn.errRecover(&err) panic(io.EOF) }() // Verify we can write before closing. if _, err := nc.Write(nil); err != nil { t.Fatal(err) } // First close should close the connection. if err := cn.Close(); err != nil { t.Fatal(err) } // During the Go 1.9 cycle, https://github.com/golang/go/commit/3792db5 // changed this error from // // net.errClosing = errors.New("use of closed network connection") // // to // // internal/poll.ErrClosing = errors.New("use of closed file or network connection") const errClosing = "use of closed" // Verify write after closing fails. if _, err := nc.Write(nil); err == nil { t.Fatal("expected error") } else if !strings.Contains(err.Error(), errClosing) { t.Fatalf("expected %s error, got %s", errClosing, err) } // Verify second close fails. if err := cn.Close(); err == nil { t.Fatal("expected error") } else if !strings.Contains(err.Error(), errClosing) { t.Fatalf("expected %s error, got %s", errClosing, err) } } func TestErrorOnExec(t *testing.T) { db := openTestConn(t) defer db.Close() txn, err := db.Begin() if err != nil { t.Fatal(err) } defer txn.Rollback() _, err = txn.Exec("CREATE TEMPORARY TABLE foo(f1 int PRIMARY KEY)") if err != nil { t.Fatal(err) } _, err = txn.Exec("INSERT INTO foo VALUES (0), (0)") if err == nil { t.Fatal("Should have raised error") } e, ok := err.(*Error) if !ok { t.Fatalf("expected Error, got %#v", err) } else if e.Code.Name() != "unique_violation" { t.Fatalf("expected unique_violation, got %s (%+v)", e.Code.Name(), err) } } func TestErrorOnQuery(t *testing.T) { db := openTestConn(t) defer db.Close() txn, err := db.Begin() if err != nil { t.Fatal(err) } defer txn.Rollback() _, err = txn.Exec("CREATE TEMPORARY TABLE foo(f1 int PRIMARY KEY)") if err != nil { t.Fatal(err) } _, err = txn.Query("INSERT INTO foo VALUES (0), (0)") if err == nil { t.Fatal("Should have raised error") } e, ok := err.(*Error) if !ok { t.Fatalf("expected Error, got %#v", err) } else if e.Code.Name() != "unique_violation" { t.Fatalf("expected unique_violation, got %s (%+v)", e.Code.Name(), err) } } func TestErrorOnQueryRowSimpleQuery(t *testing.T) { db := openTestConn(t) defer db.Close() txn, err := db.Begin() if err != nil { t.Fatal(err) } defer txn.Rollback() _, err = txn.Exec("CREATE TEMPORARY TABLE foo(f1 int PRIMARY KEY)") if err != nil { t.Fatal(err) } var v int err = txn.QueryRow("INSERT INTO foo VALUES (0), (0)").Scan(&v) if err == nil { t.Fatal("Should have raised error") } e, ok := err.(*Error) if !ok { t.Fatalf("expected Error, got %#v", err) } else if e.Code.Name() != "unique_violation" { t.Fatalf("expected unique_violation, got %s (%+v)", e.Code.Name(), err) } } // Test the QueryRow bug workarounds in stmt.exec() and simpleQuery() func TestQueryRowBugWorkaround(t *testing.T) { db := openTestConn(t) defer db.Close() // stmt.exec() _, err := db.Exec("CREATE TEMP TABLE notnulltemp (a varchar(10) not null)") if err != nil { t.Fatal(err) } var a string err = db.QueryRow("INSERT INTO notnulltemp(a) values($1) RETURNING a", nil).Scan(&a) if err == sql.ErrNoRows { t.Fatalf("expected constraint violation error; got: %v", err) } pge, ok := err.(*Error) if !ok { t.Fatalf("expected *Error; got: %#v", err) } if pge.Code.Name() != "not_null_violation" { t.Fatalf("expected not_null_violation; got: %s (%+v)", pge.Code.Name(), err) } // Test workaround in simpleQuery() tx, err := db.Begin() if err != nil { t.Fatalf("unexpected error %s in Begin", err) } defer tx.Rollback() _, err = tx.Exec("SET LOCAL check_function_bodies TO FALSE") if err != nil { t.Fatalf("could not disable check_function_bodies: %s", err) } _, err = tx.Exec(` CREATE OR REPLACE FUNCTION bad_function() RETURNS integer -- hack to prevent the function from being inlined SET check_function_bodies TO TRUE AS $$ SELECT text 'bad' $$ LANGUAGE sql`) if err != nil { t.Fatalf("could not create function: %s", err) } err = tx.QueryRow("SELECT * FROM bad_function()").Scan(&a) if err == nil { t.Fatalf("expected error") } pge, ok = err.(*Error) if !ok { t.Fatalf("expected *Error; got: %#v", err) } if pge.Code.Name() != "invalid_function_definition" { t.Fatalf("expected invalid_function_definition; got: %s (%+v)", pge.Code.Name(), err) } err = tx.Rollback() if err != nil { t.Fatalf("unexpected error %s in Rollback", err) } // Also test that simpleQuery()'s workaround works when the query fails // after a row has been received. rows, err := db.Query(` select (select generate_series(1, ss.i)) from (select gs.i from generate_series(1, 2) gs(i) order by gs.i limit 2) ss`) if err != nil { t.Fatalf("query failed: %s", err) } if !rows.Next() { t.Fatalf("expected at least one result row; got %s", rows.Err()) } var i int err = rows.Scan(&i) if err != nil { t.Fatalf("rows.Scan() failed: %s", err) } if i != 1 { t.Fatalf("unexpected value for i: %d", i) } if rows.Next() { t.Fatalf("unexpected row") } pge, ok = rows.Err().(*Error) if !ok { t.Fatalf("expected *Error; got: %#v", err) } if pge.Code.Name() != "cardinality_violation" { t.Fatalf("expected cardinality_violation; got: %s (%+v)", pge.Code.Name(), rows.Err()) } } func TestSimpleQuery(t *testing.T) { db := openTestConn(t) defer db.Close() r, err := db.Query("select 1") if err != nil { t.Fatal(err) } defer r.Close() if !r.Next() { t.Fatal("expected row") } } func TestBindError(t *testing.T) { db := openTestConn(t) defer db.Close() _, err := db.Exec("create temp table test (i integer)") if err != nil { t.Fatal(err) } _, err = db.Query("select * from test where i=$1", "hhh") if err == nil { t.Fatal("expected an error") } // Should not get error here r, err := db.Query("select * from test where i=$1", 1) if err != nil { t.Fatal(err) } defer r.Close() } func TestParseErrorInExtendedQuery(t *testing.T) { db := openTestConn(t) defer db.Close() _, err := db.Query("PARSE_ERROR $1", 1) pqErr, _ := err.(*Error) // Expecting a syntax error. if err == nil || pqErr == nil || pqErr.Code != "42601" { t.Fatalf("expected syntax error, got %s", err) } rows, err := db.Query("SELECT 1") if err != nil { t.Fatal(err) } rows.Close() } // TestReturning tests that an INSERT query using the RETURNING clause returns a row. func TestReturning(t *testing.T) { db := openTestConn(t) defer db.Close() _, err := db.Exec("CREATE TEMP TABLE distributors (did integer default 0, dname text)") if err != nil { t.Fatal(err) } rows, err := db.Query("INSERT INTO distributors (did, dname) VALUES (DEFAULT, 'XYZ Widgets') " + "RETURNING did;") if err != nil { t.Fatal(err) } if !rows.Next() { t.Fatal("no rows") } var did int err = rows.Scan(&did) if err != nil { t.Fatal(err) } if did != 0 { t.Fatalf("bad value for did: got %d, want %d", did, 0) } if rows.Next() { t.Fatal("unexpected next row") } err = rows.Err() if err != nil { t.Fatal(err) } } func TestIssue186(t *testing.T) { db := openTestConn(t) defer db.Close() // Exec() a query which returns results _, err := db.Exec("VALUES (1), (2), (3)") if err != nil { t.Fatal(err) } _, err = db.Exec("VALUES ($1), ($2), ($3)", 1, 2, 3) if err != nil { t.Fatal(err) } // Query() a query which doesn't return any results txn, err := db.Begin() if err != nil { t.Fatal(err) } defer txn.Rollback() rows, err := txn.Query("CREATE TEMP TABLE foo(f1 int)") if err != nil { t.Fatal(err) } if err = rows.Close(); err != nil { t.Fatal(err) } // small trick to get NoData from a parameterized query _, err = txn.Exec("CREATE RULE nodata AS ON INSERT TO foo DO INSTEAD NOTHING") if err != nil { t.Fatal(err) } rows, err = txn.Query("INSERT INTO foo VALUES ($1)", 1) if err != nil { t.Fatal(err) } if err = rows.Close(); err != nil { t.Fatal(err) } } func TestIssue196(t *testing.T) { db := openTestConn(t) defer db.Close() row := db.QueryRow("SELECT float4 '0.10000122' = $1, float8 '35.03554004971999' = $2", float32(0.10000122), float64(35.03554004971999)) var float4match, float8match bool err := row.Scan(&float4match, &float8match) if err != nil { t.Fatal(err) } if !float4match { t.Errorf("Expected float4 fidelity to be maintained; got no match") } if !float8match { t.Errorf("Expected float8 fidelity to be maintained; got no match") } } // Test that any CommandComplete messages sent before the query results are // ignored. func TestIssue282(t *testing.T) { db := openTestConn(t) defer db.Close() var searchPath string err := db.QueryRow(` SET LOCAL search_path TO pg_catalog; SET LOCAL search_path TO pg_catalog; SHOW search_path`).Scan(&searchPath) if err != nil { t.Fatal(err) } if searchPath != "pg_catalog" { t.Fatalf("unexpected search_path %s", searchPath) } } func TestReadFloatPrecision(t *testing.T) { db := openTestConn(t) defer db.Close() row := db.QueryRow("SELECT float4 '0.10000122', float8 '35.03554004971999', float4 '1.2'") var float4val float32 var float8val float64 var float4val2 float64 err := row.Scan(&float4val, &float8val, &float4val2) if err != nil { t.Fatal(err) } if float4val != float32(0.10000122) { t.Errorf("Expected float4 fidelity to be maintained; got no match") } if float8val != float64(35.03554004971999) { t.Errorf("Expected float8 fidelity to be maintained; got no match") } if float4val2 != float64(1.2) { t.Errorf("Expected float4 fidelity into a float64 to be maintained; got no match") } } func TestXactMultiStmt(t *testing.T) { // minified test case based on bug reports from // pico303@gmail.com and rangelspam@gmail.com t.Skip("Skipping failing test") db := openTestConn(t) defer db.Close() tx, err := db.Begin() if err != nil { t.Fatal(err) } defer tx.Commit() rows, err := tx.Query("select 1") if err != nil { t.Fatal(err) } if rows.Next() { var val int32 if err = rows.Scan(&val); err != nil { t.Fatal(err) } } else { t.Fatal("Expected at least one row in first query in xact") } rows2, err := tx.Query("select 2") if err != nil { t.Fatal(err) } if rows2.Next() { var val2 int32 if err := rows2.Scan(&val2); err != nil { t.Fatal(err) } } else { t.Fatal("Expected at least one row in second query in xact") } if err = rows.Err(); err != nil { t.Fatal(err) } if err = rows2.Err(); err != nil { t.Fatal(err) } if err = tx.Commit(); err != nil { t.Fatal(err) } } var envParseTests = []struct { Expected map[string]string Env []string }{ { Env: []string{"PGDATABASE=hello", "PGUSER=goodbye"}, Expected: map[string]string{"dbname": "hello", "user": "goodbye"}, }, { Env: []string{"PGDATESTYLE=ISO, MDY"}, Expected: map[string]string{"datestyle": "ISO, MDY"}, }, { Env: []string{"PGCONNECT_TIMEOUT=30"}, Expected: map[string]string{"connect_timeout": "30"}, }, } func TestParseEnviron(t *testing.T) { for i, tt := range envParseTests { results := parseEnviron(tt.Env) if !reflect.DeepEqual(tt.Expected, results) { t.Errorf("%d: Expected: %#v Got: %#v", i, tt.Expected, results) } } } func TestParseComplete(t *testing.T) { tpc := func(commandTag string, command string, affectedRows int64, shouldFail bool) { defer func() { if p := recover(); p != nil { if !shouldFail { t.Error(p) } } }() cn := &conn{} res, c := cn.parseComplete(commandTag) if c != command { t.Errorf("Expected %v, got %v", command, c) } n, err := res.RowsAffected() if err != nil { t.Fatal(err) } if n != affectedRows { t.Errorf("Expected %d, got %d", affectedRows, n) } } tpc("ALTER TABLE", "ALTER TABLE", 0, false) tpc("INSERT 0 1", "INSERT", 1, false) tpc("UPDATE 100", "UPDATE", 100, false) tpc("SELECT 100", "SELECT", 100, false) tpc("FETCH 100", "FETCH", 100, false) // allow COPY (and others) without row count tpc("COPY", "COPY", 0, false) // don't fail on command tags we don't recognize tpc("UNKNOWNCOMMANDTAG", "UNKNOWNCOMMANDTAG", 0, false) // failure cases tpc("INSERT 1", "", 0, true) // missing oid tpc("UPDATE 0 1", "", 0, true) // too many numbers tpc("SELECT foo", "", 0, true) // invalid row count } // Test interface conformance. var ( _ driver.ExecerContext = (*conn)(nil) _ driver.QueryerContext = (*conn)(nil) ) func TestNullAfterNonNull(t *testing.T) { db := openTestConn(t) defer db.Close() r, err := db.Query("SELECT 9::integer UNION SELECT NULL::integer") if err != nil { t.Fatal(err) } var n sql.NullInt64 if !r.Next() { if r.Err() != nil { t.Fatal(err) } t.Fatal("expected row") } if err := r.Scan(&n); err != nil { t.Fatal(err) } if n.Int64 != 9 { t.Fatalf("expected 2, not %d", n.Int64) } if !r.Next() { if r.Err() != nil { t.Fatal(err) } t.Fatal("expected row") } if err := r.Scan(&n); err != nil { t.Fatal(err) } if n.Valid { t.Fatal("expected n to be invalid") } if n.Int64 != 0 { t.Fatalf("expected n to 2, not %d", n.Int64) } } func Test64BitErrorChecking(t *testing.T) { defer func() { if err := recover(); err != nil { t.Fatal("panic due to 0xFFFFFFFF != -1 " + "when int is 64 bits") } }() db := openTestConn(t) defer db.Close() r, err := db.Query(`SELECT * FROM (VALUES (0::integer, NULL::text), (1, 'test string')) AS t;`) if err != nil { t.Fatal(err) } defer r.Close() for r.Next() { } } func TestCommit(t *testing.T) { db := openTestConn(t) defer db.Close() _, err := db.Exec("CREATE TEMP TABLE temp (a int)") if err != nil { t.Fatal(err) } sqlInsert := "INSERT INTO temp VALUES (1)" sqlSelect := "SELECT * FROM temp" tx, err := db.Begin() if err != nil { t.Fatal(err) } _, err = tx.Exec(sqlInsert) if err != nil { t.Fatal(err) } err = tx.Commit() if err != nil { t.Fatal(err) } var i int err = db.QueryRow(sqlSelect).Scan(&i) if err != nil { t.Fatal(err) } if i != 1 { t.Fatalf("expected 1, got %d", i) } } func TestErrorClass(t *testing.T) { db := openTestConn(t) defer db.Close() _, err := db.Query("SELECT int 'notint'") if err == nil { t.Fatal("expected error") } pge, ok := err.(*Error) if !ok { t.Fatalf("expected *pq.Error, got %#+v", err) } if pge.Code.Class() != "22" { t.Fatalf("expected class 28, got %v", pge.Code.Class()) } if pge.Code.Class().Name() != "data_exception" { t.Fatalf("expected data_exception, got %v", pge.Code.Class().Name()) } } func TestParseOpts(t *testing.T) { tests := []struct { in string expected values valid bool }{ {"dbname=hello user=goodbye", values{"dbname": "hello", "user": "goodbye"}, true}, {"dbname=hello user=goodbye ", values{"dbname": "hello", "user": "goodbye"}, true}, {"dbname = hello user=goodbye", values{"dbname": "hello", "user": "goodbye"}, true}, {"dbname=hello user =goodbye", values{"dbname": "hello", "user": "goodbye"}, true}, {"dbname=hello user= goodbye", values{"dbname": "hello", "user": "goodbye"}, true}, {"host=localhost password='correct horse battery staple'", values{"host": "localhost", "password": "correct horse battery staple"}, true}, {"dbname=データベース password=パスワード", values{"dbname": "データベース", "password": "パスワード"}, true}, {"dbname=hello user=''", values{"dbname": "hello", "user": ""}, true}, {"user='' dbname=hello", values{"dbname": "hello", "user": ""}, true}, // The last option value is an empty string if there's no non-whitespace after its = {"dbname=hello user= ", values{"dbname": "hello", "user": ""}, true}, // The parser ignores spaces after = and interprets the next set of non-whitespace characters as the value. {"user= password=foo", values{"user": "password=foo"}, true}, // Backslash escapes next char {`user=a\ \'\\b`, values{"user": `a '\b`}, true}, {`user='a \'b'`, values{"user": `a 'b`}, true}, // Incomplete escape {`user=x\`, values{}, false}, // No '=' after the key {"postgre://marko@internet", values{}, false}, {"dbname user=goodbye", values{}, false}, {"user=foo blah", values{}, false}, {"user=foo blah ", values{}, false}, // Unterminated quoted value {"dbname=hello user='unterminated", values{}, false}, } for _, test := range tests { o := make(values) err := parseOpts(test.in, o) switch { case err != nil && test.valid: t.Errorf("%q got unexpected error: %s", test.in, err) case err == nil && test.valid && !reflect.DeepEqual(test.expected, o): t.Errorf("%q got: %#v want: %#v", test.in, o, test.expected) case err == nil && !test.valid: t.Errorf("%q expected an error", test.in) } } } func TestRuntimeParameters(t *testing.T) { tests := []struct { conninfo string param string expected string success bool }{ // invalid parameter {"DOESNOTEXIST=foo", "", "", false}, // we can only work with a specific value for these two {"client_encoding=SQL_ASCII", "", "", false}, {"datestyle='ISO, YDM'", "", "", false}, // "options" should work exactly as it does in libpq {"options='-c search_path=pqgotest'", "search_path", "pqgotest", true}, // pq should override client_encoding in this case {"options='-c client_encoding=SQL_ASCII'", "client_encoding", "UTF8", true}, // allow client_encoding to be set explicitly {"client_encoding=UTF8", "client_encoding", "UTF8", true}, // test a runtime parameter not supported by libpq {"work_mem='139kB'", "work_mem", "139kB", true}, // test fallback_application_name {"application_name=foo fallback_application_name=bar", "application_name", "foo", true}, {"application_name='' fallback_application_name=bar", "application_name", "", true}, {"fallback_application_name=bar", "application_name", "bar", true}, } for _, test := range tests { db, err := openTestConnConninfo(test.conninfo) if err != nil { t.Fatal(err) } // application_name didn't exist before 9.0 if test.param == "application_name" && getServerVersion(t, db) < 90000 { db.Close() continue } tryGetParameterValue := func() (value string, success bool) { defer db.Close() row := db.QueryRow("SELECT current_setting($1)", test.param) err = row.Scan(&value) if err != nil { return "", false } return value, true } value, success := tryGetParameterValue() if success != test.success && !test.success { t.Fatalf("%v: unexpected error: %v", test.conninfo, err) } if success != test.success { t.Fatalf("unexpected outcome %v (was expecting %v) for conninfo \"%s\"", success, test.success, test.conninfo) } if value != test.expected { t.Fatalf("bad value for %s: got %s, want %s with conninfo \"%s\"", test.param, value, test.expected, test.conninfo) } } } func TestIsUTF8(t *testing.T) { var cases = []struct { name string want bool }{ {"unicode", true}, {"utf-8", true}, {"utf_8", true}, {"UTF-8", true}, {"UTF8", true}, {"utf8", true}, {"u n ic_ode", true}, {"ut_f%8", true}, {"ubf8", false}, {"punycode", false}, } for _, test := range cases { if g := isUTF8(test.name); g != test.want { t.Errorf("isUTF8(%q) = %v want %v", test.name, g, test.want) } } } func TestQuoteIdentifier(t *testing.T) { var cases = []struct { input string want string }{ {`foo`, `"foo"`}, {`foo bar baz`, `"foo bar baz"`}, {`foo"bar`, `"foo""bar"`}, {"foo\x00bar", `"foo"`}, {"\x00foo", `""`}, } for _, test := range cases { got := QuoteIdentifier(test.input) if got != test.want { t.Errorf("QuoteIdentifier(%q) = %v want %v", test.input, got, test.want) } } } func TestQuoteLiteral(t *testing.T) { var cases = []struct { input string want string }{ {`foo`, `'foo'`}, {`foo bar baz`, `'foo bar baz'`}, {`foo'bar`, `'foo''bar'`}, {`foo\bar`, ` E'foo\\bar'`}, {`foo\ba'r`, ` E'foo\\ba''r'`}, {`foo"bar`, `'foo"bar'`}, {`foo\x00bar`, ` E'foo\\x00bar'`}, {`\x00foo`, ` E'\\x00foo'`}, {`'`, `''''`}, {`''`, `''''''`}, {`\`, ` E'\\'`}, {`'abc'; DROP TABLE users;`, `'''abc''; DROP TABLE users;'`}, {`\'`, ` E'\\'''`}, {`E'\''`, ` E'E''\\'''''`}, {`e'\''`, ` E'e''\\'''''`}, {`E'\'abc\'; DROP TABLE users;'`, ` E'E''\\''abc\\''; DROP TABLE users;'''`}, {`e'\'abc\'; DROP TABLE users;'`, ` E'e''\\''abc\\''; DROP TABLE users;'''`}, } for _, test := range cases { got := QuoteLiteral(test.input) if got != test.want { t.Errorf("QuoteLiteral(%q) = %v want %v", test.input, got, test.want) } } } func TestRowsResultTag(t *testing.T) { type ResultTag interface { Result() driver.Result Tag() string } tests := []struct { query string tag string ra int64 }{ { query: "CREATE TEMP TABLE temp (a int)", tag: "CREATE TABLE", }, { query: "INSERT INTO temp VALUES (1), (2)", tag: "INSERT", ra: 2, }, { query: "SELECT 1", }, // A SELECT anywhere should take precedent. { query: "SELECT 1; INSERT INTO temp VALUES (1), (2)", }, { query: "INSERT INTO temp VALUES (1), (2); SELECT 1", }, // Multiple statements that don't return rows should return the last tag. { query: "CREATE TEMP TABLE t (a int); DROP TABLE t", tag: "DROP TABLE", }, // Ensure a rows-returning query in any position among various tags-returing // statements will prefer the rows. { query: "SELECT 1; CREATE TEMP TABLE t (a int); DROP TABLE t", }, { query: "CREATE TEMP TABLE t (a int); SELECT 1; DROP TABLE t", }, { query: "CREATE TEMP TABLE t (a int); DROP TABLE t; SELECT 1", }, // Verify that an no-results query doesn't set the tag. { query: "CREATE TEMP TABLE t (a int); SELECT 1 WHERE FALSE; DROP TABLE t;", }, } // If this is the only test run, this will correct the connection string. openTestConn(t).Close() conn, err := Open("") if err != nil { t.Fatal(err) } defer conn.Close() q := conn.(driver.QueryerContext) for _, test := range tests { if rows, err := q.QueryContext(context.Background(), test.query, nil); err != nil { t.Fatalf("%s: %s", test.query, err) } else { r := rows.(ResultTag) if tag := r.Tag(); tag != test.tag { t.Fatalf("%s: unexpected tag %q", test.query, tag) } res := r.Result() if ra, _ := res.RowsAffected(); ra != test.ra { t.Fatalf("%s: unexpected rows affected: %d", test.query, ra) } rows.Close() } } } // TestQuickClose tests that closing a query early allows a subsequent query to work. func TestQuickClose(t *testing.T) { db := openTestConn(t) defer db.Close() tx, err := db.Begin() if err != nil { t.Fatal(err) } rows, err := tx.Query("SELECT 1; SELECT 2;") if err != nil { t.Fatal(err) } if err := rows.Close(); err != nil { t.Fatal(err) } var id int if err := tx.QueryRow("SELECT 3").Scan(&id); err != nil { t.Fatal(err) } if id != 3 { t.Fatalf("unexpected %d", id) } if err := tx.Commit(); err != nil { t.Fatal(err) } } func TestMultipleResult(t *testing.T) { db := openTestConn(t) defer db.Close() rows, err := db.Query(` begin; select * from information_schema.tables limit 1; select * from information_schema.columns limit 2; commit; `) if err != nil { t.Fatal(err) } type set struct { cols []string rowCount int } buf := []*set{} for { cols, err := rows.Columns() if err != nil { t.Fatal(err) } s := &set{ cols: cols, } buf = append(buf, s) for rows.Next() { s.rowCount++ } if !rows.NextResultSet() { break } } if len(buf) != 2 { t.Fatalf("got %d sets, expected 2", len(buf)) } if len(buf[0].cols) == len(buf[1].cols) || len(buf[1].cols) == 0 { t.Fatal("invalid cols size, expected different column count and greater then zero") } if buf[0].rowCount != 1 || buf[1].rowCount != 2 { t.Fatal("incorrect number of rows returned") } } func TestCopyInStmtAffectedRows(t *testing.T) { db := openTestConn(t) defer db.Close() _, err := db.Exec("CREATE TEMP TABLE temp (a int)") if err != nil { t.Fatal(err) } txn, err := db.BeginTx(context.TODO(), nil) if err != nil { t.Fatal(err) } copyStmt, err := txn.Prepare(CopyIn("temp", "a")) if err != nil { t.Fatal(err) } res, err := copyStmt.Exec() if err != nil { t.Fatal(err) } res.RowsAffected() res.LastInsertId() } golang-github-lib-pq-1.5.2/connector.go000066400000000000000000000064741365507746300200040ustar00rootroot00000000000000package pq import ( "context" "database/sql/driver" "errors" "fmt" "os" "strings" ) // Connector represents a fixed configuration for the pq driver with a given // name. Connector satisfies the database/sql/driver Connector interface and // can be used to create any number of DB Conn's via the database/sql OpenDB // function. // // See https://golang.org/pkg/database/sql/driver/#Connector. // See https://golang.org/pkg/database/sql/#OpenDB. type Connector struct { opts values dialer Dialer } // Connect returns a connection to the database using the fixed configuration // of this Connector. Context is not used. func (c *Connector) Connect(ctx context.Context) (driver.Conn, error) { return c.open(ctx) } // Driver returnst the underlying driver of this Connector. func (c *Connector) Driver() driver.Driver { return &Driver{} } // NewConnector returns a connector for the pq driver in a fixed configuration // with the given dsn. The returned connector can be used to create any number // of equivalent Conn's. The returned connector is intended to be used with // database/sql.OpenDB. // // See https://golang.org/pkg/database/sql/driver/#Connector. // See https://golang.org/pkg/database/sql/#OpenDB. func NewConnector(dsn string) (*Connector, error) { var err error o := make(values) // A number of defaults are applied here, in this order: // // * Very low precedence defaults applied in every situation // * Environment variables // * Explicitly passed connection information o["host"] = "localhost" o["port"] = "5432" // N.B.: Extra float digits should be set to 3, but that breaks // Postgres 8.4 and older, where the max is 2. o["extra_float_digits"] = "2" for k, v := range parseEnviron(os.Environ()) { o[k] = v } if strings.HasPrefix(dsn, "postgres://") || strings.HasPrefix(dsn, "postgresql://") { dsn, err = ParseURL(dsn) if err != nil { return nil, err } } if err := parseOpts(dsn, o); err != nil { return nil, err } // Use the "fallback" application name if necessary if fallback, ok := o["fallback_application_name"]; ok { if _, ok := o["application_name"]; !ok { o["application_name"] = fallback } } // We can't work with any client_encoding other than UTF-8 currently. // However, we have historically allowed the user to set it to UTF-8 // explicitly, and there's no reason to break such programs, so allow that. // Note that the "options" setting could also set client_encoding, but // parsing its value is not worth it. Instead, we always explicitly send // client_encoding as a separate run-time parameter, which should override // anything set in options. if enc, ok := o["client_encoding"]; ok && !isUTF8(enc) { return nil, errors.New("client_encoding must be absent or 'UTF8'") } o["client_encoding"] = "UTF8" // DateStyle needs a similar treatment. if datestyle, ok := o["datestyle"]; ok { if datestyle != "ISO, MDY" { return nil, fmt.Errorf("setting datestyle must be absent or %v; got %v", "ISO, MDY", datestyle) } } else { o["datestyle"] = "ISO, MDY" } // If a user is not provided by any other means, the last // resort is to use the current operating system provided user // name. if _, ok := o["user"]; !ok { u, err := userCurrent() if err != nil { return nil, err } o["user"] = u } return &Connector{opts: o, dialer: defaultDialer{}}, nil } golang-github-lib-pq-1.5.2/connector_example_test.go000066400000000000000000000005671365507746300225530ustar00rootroot00000000000000// +build go1.10 package pq_test import ( "database/sql" "fmt" "github.com/lib/pq" ) func ExampleNewConnector() { name := "" connector, err := pq.NewConnector(name) if err != nil { fmt.Println(err) return } db := sql.OpenDB(connector) defer db.Close() // Use the DB txn, err := db.Begin() if err != nil { fmt.Println(err) return } txn.Rollback() } golang-github-lib-pq-1.5.2/connector_test.go000066400000000000000000000025021365507746300210270ustar00rootroot00000000000000// +build go1.10 package pq import ( "context" "database/sql" "database/sql/driver" "testing" ) func TestNewConnector_WorksWithOpenDB(t *testing.T) { name := "" c, err := NewConnector(name) if err != nil { t.Fatal(err) } db := sql.OpenDB(c) defer db.Close() // database/sql might not call our Open at all unless we do something with // the connection txn, err := db.Begin() if err != nil { t.Fatal(err) } txn.Rollback() } func TestNewConnector_Connect(t *testing.T) { name := "" c, err := NewConnector(name) if err != nil { t.Fatal(err) } db, err := c.Connect(context.Background()) if err != nil { t.Fatal(err) } defer db.Close() // database/sql might not call our Open at all unless we do something with // the connection txn, err := db.(driver.ConnBeginTx).BeginTx(context.Background(), driver.TxOptions{}) if err != nil { t.Fatal(err) } txn.Rollback() } func TestNewConnector_Driver(t *testing.T) { name := "" c, err := NewConnector(name) if err != nil { t.Fatal(err) } db, err := c.Driver().Open(name) if err != nil { t.Fatal(err) } defer db.Close() // database/sql might not call our Open at all unless we do something with // the connection txn, err := db.(driver.ConnBeginTx).BeginTx(context.Background(), driver.TxOptions{}) if err != nil { t.Fatal(err) } txn.Rollback() } golang-github-lib-pq-1.5.2/copy.go000066400000000000000000000133621365507746300167560ustar00rootroot00000000000000package pq import ( "database/sql/driver" "encoding/binary" "errors" "fmt" "sync" ) var ( errCopyInClosed = errors.New("pq: copyin statement has already been closed") 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 // Tx.Prepare(). The target table should be visible in search_path. func CopyIn(table string, columns ...string) string { stmt := "COPY " + QuoteIdentifier(table) + " (" for i, col := range columns { if i != 0 { stmt += ", " } stmt += QuoteIdentifier(col) } stmt += ") FROM STDIN" return stmt } // CopyInSchema creates a COPY FROM statement which can be prepared with // Tx.Prepare(). func CopyInSchema(schema, table string, columns ...string) string { stmt := "COPY " + QuoteIdentifier(schema) + "." + QuoteIdentifier(table) + " (" for i, col := range columns { if i != 0 { stmt += ", " } stmt += QuoteIdentifier(col) } stmt += ") FROM STDIN" return stmt } type copyin struct { cn *conn buffer []byte rowData chan []byte done chan bool closed bool sync.Mutex // guards err err error } const ciBufferSize = 64 * 1024 // flush buffer before the buffer is filled up and needs reallocation const ciBufferFlushSize = 63 * 1024 func (cn *conn) prepareCopyIn(q string) (_ driver.Stmt, err error) { if !cn.isInTransaction() { return nil, errCopyNotSupportedOutsideTxn } ci := ©in{ cn: cn, buffer: make([]byte, 0, ciBufferSize), rowData: make(chan []byte), done: make(chan bool, 1), } // add CopyData identifier + 4 bytes for message length ci.buffer = append(ci.buffer, 'd', 0, 0, 0, 0) b := cn.writeBuf('Q') b.string(q) cn.send(b) awaitCopyInResponse: for { t, r := cn.recv1() switch t { case 'G': if r.byte() != 0 { err = errBinaryCopyNotSupported break awaitCopyInResponse } go ci.resploop() return ci, nil case 'H': err = errCopyToNotSupported break awaitCopyInResponse case 'E': err = parseError(r) case 'Z': if err == nil { ci.setBad() errorf("unexpected ReadyForQuery in response to COPY") } cn.processReadyForQuery(r) return nil, err default: ci.setBad() errorf("unknown response for copy query: %q", t) } } // something went wrong, abort COPY before we return b = cn.writeBuf('f') b.string(err.Error()) cn.send(b) for { t, r := cn.recv1() switch t { case 'c', 'C', 'E': case 'Z': // correctly aborted, we're done cn.processReadyForQuery(r) return nil, err default: ci.setBad() errorf("unknown response for CopyFail: %q", t) } } } func (ci *copyin) flush(buf []byte) { // set message length (without message identifier) binary.BigEndian.PutUint32(buf[1:], uint32(len(buf)-1)) _, err := ci.cn.c.Write(buf) if err != nil { panic(err) } } func (ci *copyin) resploop() { for { var r readBuf t, err := ci.cn.recvMessage(&r) if err != nil { ci.setBad() ci.setError(err) ci.done <- true return } switch t { case 'C': // complete case 'N': if n := ci.cn.noticeHandler; n != nil { n(parseError(&r)) } case 'Z': ci.cn.processReadyForQuery(&r) ci.done <- true return case 'E': err := parseError(&r) ci.setError(err) default: ci.setBad() ci.setError(fmt.Errorf("unknown response during CopyIn: %q", t)) ci.done <- true return } } } 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) ci.Unlock() return isSet } // setError() sets ci.err if one has not been set already. Caller must not be // holding ci.Mutex. func (ci *copyin) setError(err error) { ci.Lock() if ci.err == nil { ci.err = err } ci.Unlock() } func (ci *copyin) NumInput() int { return -1 } func (ci *copyin) Query(v []driver.Value) (r driver.Rows, err error) { return nil, ErrNotSupported } // Exec inserts values into the COPY stream. The insert is asynchronous // and Exec can return errors from previous Exec calls to the same // COPY stmt. // // You need to call Exec(nil) to sync the COPY stream and to get any // errors from pending data, since Stmt.Close() doesn't return errors // to the user. func (ci *copyin) Exec(v []driver.Value) (r driver.Result, err error) { if ci.closed { return nil, errCopyInClosed } if ci.isBad() { return nil, driver.ErrBadConn } defer ci.cn.errRecover(&err) if ci.isErrorSet() { return nil, ci.err } if len(v) == 0 { return driver.RowsAffected(0), ci.Close() } numValues := len(v) for i, value := range v { ci.buffer = appendEncodedText(&ci.cn.parameterStatus, ci.buffer, value) if i < numValues-1 { ci.buffer = append(ci.buffer, '\t') } } ci.buffer = append(ci.buffer, '\n') if len(ci.buffer) > ciBufferFlushSize { ci.flush(ci.buffer) // reset buffer, keep bytes for message identifier and length ci.buffer = ci.buffer[:5] } return driver.RowsAffected(0), nil } func (ci *copyin) Close() (err error) { if ci.closed { // Don't do anything, we're already closed return nil } ci.closed = true if ci.isBad() { return driver.ErrBadConn } defer ci.cn.errRecover(&err) if len(ci.buffer) > 0 { ci.flush(ci.buffer) } // Avoid touching the scratch buffer as resploop could be using it. err = ci.cn.sendSimpleMessage('c') if err != nil { return err } <-ci.done ci.cn.inCopy = false if ci.isErrorSet() { err = ci.err return err } return nil } golang-github-lib-pq-1.5.2/copy_test.go000066400000000000000000000216721365507746300200200ustar00rootroot00000000000000package pq import ( "bytes" "database/sql" "database/sql/driver" "net" "strings" "testing" ) func TestCopyInStmt(t *testing.T) { stmt := CopyIn("table name") if stmt != `COPY "table name" () FROM STDIN` { t.Fatal(stmt) } stmt = CopyIn("table name", "column 1", "column 2") if stmt != `COPY "table name" ("column 1", "column 2") FROM STDIN` { t.Fatal(stmt) } stmt = CopyIn(`table " name """`, `co"lumn""`) if stmt != `COPY "table "" name """"""" ("co""lumn""""") FROM STDIN` { t.Fatal(stmt) } } func TestCopyInSchemaStmt(t *testing.T) { stmt := CopyInSchema("schema name", "table name") if stmt != `COPY "schema name"."table name" () FROM STDIN` { t.Fatal(stmt) } stmt = CopyInSchema("schema name", "table name", "column 1", "column 2") if stmt != `COPY "schema name"."table name" ("column 1", "column 2") FROM STDIN` { t.Fatal(stmt) } stmt = CopyInSchema(`schema " name """`, `table " name """`, `co"lumn""`) if stmt != `COPY "schema "" name """"""".`+ `"table "" name """"""" ("co""lumn""""") FROM STDIN` { t.Fatal(stmt) } } func TestCopyInMultipleValues(t *testing.T) { db := openTestConn(t) defer db.Close() txn, err := db.Begin() if err != nil { t.Fatal(err) } defer txn.Rollback() _, err = txn.Exec("CREATE TEMP TABLE temp (a int, b varchar)") if err != nil { t.Fatal(err) } stmt, err := txn.Prepare(CopyIn("temp", "a", "b")) if err != nil { t.Fatal(err) } longString := strings.Repeat("#", 500) for i := 0; i < 500; i++ { _, err = stmt.Exec(int64(i), longString) if err != nil { t.Fatal(err) } } _, err = stmt.Exec() if err != nil { t.Fatal(err) } err = stmt.Close() if err != nil { t.Fatal(err) } var num int err = txn.QueryRow("SELECT COUNT(*) FROM temp").Scan(&num) if err != nil { t.Fatal(err) } if num != 500 { t.Fatalf("expected 500 items, not %d", num) } } func TestCopyInRaiseStmtTrigger(t *testing.T) { db := openTestConn(t) defer db.Close() if getServerVersion(t, db) < 90000 { var exists int err := db.QueryRow("SELECT 1 FROM pg_language WHERE lanname = 'plpgsql'").Scan(&exists) if err == sql.ErrNoRows { t.Skip("language PL/PgSQL does not exist; skipping TestCopyInRaiseStmtTrigger") } else if err != nil { t.Fatal(err) } } txn, err := db.Begin() if err != nil { t.Fatal(err) } defer txn.Rollback() _, err = txn.Exec("CREATE TEMP TABLE temp (a int, b varchar)") if err != nil { t.Fatal(err) } _, err = txn.Exec(` CREATE OR REPLACE FUNCTION pg_temp.temptest() RETURNS trigger AS $BODY$ begin raise notice 'Hello world'; return new; end $BODY$ LANGUAGE plpgsql`) if err != nil { t.Fatal(err) } _, err = txn.Exec(` CREATE TRIGGER temptest_trigger BEFORE INSERT ON temp FOR EACH ROW EXECUTE PROCEDURE pg_temp.temptest()`) if err != nil { t.Fatal(err) } stmt, err := txn.Prepare(CopyIn("temp", "a", "b")) if err != nil { t.Fatal(err) } longString := strings.Repeat("#", 500) _, err = stmt.Exec(int64(1), longString) if err != nil { t.Fatal(err) } _, err = stmt.Exec() if err != nil { t.Fatal(err) } err = stmt.Close() if err != nil { t.Fatal(err) } var num int err = txn.QueryRow("SELECT COUNT(*) FROM temp").Scan(&num) if err != nil { t.Fatal(err) } if num != 1 { t.Fatalf("expected 1 items, not %d", num) } } func TestCopyInTypes(t *testing.T) { db := openTestConn(t) defer db.Close() txn, err := db.Begin() if err != nil { t.Fatal(err) } defer txn.Rollback() _, err = txn.Exec("CREATE TEMP TABLE temp (num INTEGER, text VARCHAR, blob BYTEA, nothing VARCHAR)") if err != nil { t.Fatal(err) } stmt, err := txn.Prepare(CopyIn("temp", "num", "text", "blob", "nothing")) if err != nil { t.Fatal(err) } _, err = stmt.Exec(int64(1234567890), "Héllö\n ☃!\r\t\\", []byte{0, 255, 9, 10, 13}, nil) if err != nil { t.Fatal(err) } _, err = stmt.Exec() if err != nil { t.Fatal(err) } err = stmt.Close() if err != nil { t.Fatal(err) } var num int var text string var blob []byte var nothing sql.NullString err = txn.QueryRow("SELECT * FROM temp").Scan(&num, &text, &blob, ¬hing) if err != nil { t.Fatal(err) } if num != 1234567890 { t.Fatal("unexpected result", num) } if text != "Héllö\n ☃!\r\t\\" { t.Fatal("unexpected result", text) } if !bytes.Equal(blob, []byte{0, 255, 9, 10, 13}) { t.Fatal("unexpected result", blob) } if nothing.Valid { t.Fatal("unexpected result", nothing.String) } } func TestCopyInWrongType(t *testing.T) { db := openTestConn(t) defer db.Close() txn, err := db.Begin() if err != nil { t.Fatal(err) } defer txn.Rollback() _, err = txn.Exec("CREATE TEMP TABLE temp (num INTEGER)") if err != nil { t.Fatal(err) } stmt, err := txn.Prepare(CopyIn("temp", "num")) if err != nil { t.Fatal(err) } defer stmt.Close() _, err = stmt.Exec("Héllö\n ☃!\r\t\\") if err != nil { t.Fatal(err) } _, err = stmt.Exec() if err == nil { t.Fatal("expected error") } if pge := err.(*Error); pge.Code.Name() != "invalid_text_representation" { t.Fatalf("expected 'invalid input syntax for integer' error, got %s (%+v)", pge.Code.Name(), pge) } } func TestCopyOutsideOfTxnError(t *testing.T) { db := openTestConn(t) defer db.Close() _, err := db.Prepare(CopyIn("temp", "num")) if err == nil { t.Fatal("COPY outside of transaction did not return an error") } if err != errCopyNotSupportedOutsideTxn { t.Fatalf("expected %s, got %s", err, err.Error()) } } func TestCopyInBinaryError(t *testing.T) { db := openTestConn(t) defer db.Close() txn, err := db.Begin() if err != nil { t.Fatal(err) } defer txn.Rollback() _, err = txn.Exec("CREATE TEMP TABLE temp (num INTEGER)") if err != nil { t.Fatal(err) } _, err = txn.Prepare("COPY temp (num) FROM STDIN WITH binary") if err != errBinaryCopyNotSupported { t.Fatalf("expected %s, got %+v", errBinaryCopyNotSupported, err) } // check that the protocol is in a valid state err = txn.Rollback() if err != nil { t.Fatal(err) } } func TestCopyFromError(t *testing.T) { db := openTestConn(t) defer db.Close() txn, err := db.Begin() if err != nil { t.Fatal(err) } defer txn.Rollback() _, err = txn.Exec("CREATE TEMP TABLE temp (num INTEGER)") if err != nil { t.Fatal(err) } _, err = txn.Prepare("COPY temp (num) TO STDOUT") if err != errCopyToNotSupported { t.Fatalf("expected %s, got %+v", errCopyToNotSupported, err) } // check that the protocol is in a valid state err = txn.Rollback() if err != nil { t.Fatal(err) } } func TestCopySyntaxError(t *testing.T) { db := openTestConn(t) defer db.Close() txn, err := db.Begin() if err != nil { t.Fatal(err) } defer txn.Rollback() _, err = txn.Prepare("COPY ") if err == nil { t.Fatal("expected error") } if pge := err.(*Error); pge.Code.Name() != "syntax_error" { t.Fatalf("expected syntax error, got %s (%+v)", pge.Code.Name(), pge) } // check that the protocol is in a valid state err = txn.Rollback() if err != nil { t.Fatal(err) } } // Tests for connection errors in copyin.resploop() func TestCopyRespLoopConnectionError(t *testing.T) { db := openTestConn(t) defer db.Close() txn, err := db.Begin() if err != nil { t.Fatal(err) } defer txn.Rollback() var pid int err = txn.QueryRow("SELECT pg_backend_pid()").Scan(&pid) if err != nil { t.Fatal(err) } _, err = txn.Exec("CREATE TEMP TABLE temp (a int)") if err != nil { t.Fatal(err) } stmt, err := txn.Prepare(CopyIn("temp", "a")) if err != nil { t.Fatal(err) } defer stmt.Close() _, err = db.Exec("SELECT pg_terminate_backend($1)", pid) if err != nil { t.Fatal(err) } if getServerVersion(t, db) < 90500 { // We have to try and send something over, since postgres before // version 9.5 won't process SIGTERMs while it's waiting for // CopyData/CopyEnd messages; see tcop/postgres.c. _, err = stmt.Exec(1) if err != nil { t.Fatal(err) } } _, err = stmt.Exec() if err == nil { t.Fatalf("expected error") } switch pge := err.(type) { case *Error: if pge.Code.Name() != "admin_shutdown" { t.Fatalf("expected admin_shutdown, got %s", pge.Code.Name()) } case *net.OpError: // ignore default: if err == driver.ErrBadConn { // likely an EPIPE } else { t.Fatalf("unexpected error, got %+#v", err) } } _ = stmt.Close() } func BenchmarkCopyIn(b *testing.B) { db := openTestConn(b) defer db.Close() txn, err := db.Begin() if err != nil { b.Fatal(err) } defer txn.Rollback() _, err = txn.Exec("CREATE TEMP TABLE temp (a int, b varchar)") if err != nil { b.Fatal(err) } stmt, err := txn.Prepare(CopyIn("temp", "a", "b")) if err != nil { b.Fatal(err) } for i := 0; i < b.N; i++ { _, err = stmt.Exec(int64(i), "hello world!") if err != nil { b.Fatal(err) } } _, err = stmt.Exec() if err != nil { b.Fatal(err) } err = stmt.Close() if err != nil { b.Fatal(err) } var num int err = txn.QueryRow("SELECT COUNT(*) FROM temp").Scan(&num) if err != nil { b.Fatal(err) } if num != b.N { b.Fatalf("expected %d items, not %d", b.N, num) } } golang-github-lib-pq-1.5.2/doc.go000066400000000000000000000213121365507746300165430ustar00rootroot00000000000000/* Package pq is a pure Go Postgres driver for the database/sql package. In most cases clients will use the database/sql package instead of using this package directly. For example: import ( "database/sql" _ "github.com/lib/pq" ) func main() { connStr := "user=pqgotest dbname=pqgotest sslmode=verify-full" db, err := sql.Open("postgres", connStr) if err != nil { log.Fatal(err) } age := 21 rows, err := db.Query("SELECT name FROM users WHERE age = $1", age) … } You can also connect to a database using a URL. For example: connStr := "postgres://pqgotest:password@localhost/pqgotest?sslmode=verify-full" db, err := sql.Open("postgres", connStr) Connection String Parameters Similarly to libpq, when establishing a connection using pq you are expected to supply a connection string containing zero or more parameters. A subset of the connection parameters supported by libpq are also supported by pq. Additionally, pq also lets you specify run-time parameters (such as search_path or work_mem) directly in the connection string. This is different from libpq, which does not allow run-time parameters in the connection string, instead requiring you to supply them in the options parameter. For compatibility with libpq, the following special connection parameters are supported: * dbname - The name of the database to connect to * user - The user to sign in as * password - The user's password * host - The host to connect to. Values that start with / are for unix domain sockets. (default is localhost) * port - The port to bind to. (default is 5432) * sslmode - Whether or not to use SSL (default is require, this is not the default for libpq) * fallback_application_name - An application_name to fall back to if one isn't provided. * connect_timeout - Maximum wait for connection, in seconds. Zero or not specified means wait indefinitely. * sslcert - Cert file location. The file must contain PEM encoded data. * sslkey - Key file location. The file must contain PEM encoded data. * sslrootcert - The location of the root certificate file. The file must contain PEM encoded data. Valid values for sslmode are: * disable - No SSL * require - Always SSL (skip verification) * verify-ca - Always SSL (verify that the certificate presented by the server was signed by a trusted CA) * verify-full - Always SSL (verify that the certification presented by the server was signed by a trusted CA and the server host name matches the one in the certificate) See http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING for more information about connection string parameters. Use single quotes for values that contain whitespace: "user=pqgotest password='with spaces'" A backslash will escape the next character in values: "user=space\ man password='it\'s valid'" Note that the connection parameter client_encoding (which sets the text encoding for the connection) may be set but must be "UTF8", matching with the same rules as Postgres. It is an error to provide any other value. In addition to the parameters listed above, any run-time parameter that can be set at backend start time can be set in the connection string. For more information, see http://www.postgresql.org/docs/current/static/runtime-config.html. Most environment variables as specified at http://www.postgresql.org/docs/current/static/libpq-envars.html supported by libpq are also supported by pq. If any of the environment 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: rows, err := db.Query(`SELECT name FROM users WHERE favorite_fruit = $1 OR age BETWEEN $2 AND $2 + 3`, "orange", 64) pq does not support the LastInsertId() method of the Result type in database/sql. To return the identifier of an INSERT (or UPDATE or DELETE), use the Postgres RETURNING clause with a standard Query or QueryRow call: var userid int err := db.QueryRow(`INSERT INTO users(name, favorite_fruit, age) VALUES('beatrice', 'starfruit', 93) RETURNING id`).Scan(&userid) For more details on RETURNING, see the Postgres documentation: http://www.postgresql.org/docs/current/static/sql-insert.html http://www.postgresql.org/docs/current/static/sql-update.html http://www.postgresql.org/docs/current/static/sql-delete.html 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 { fmt.Println("pq error:", err.Code.Name()) } See the pq.Error type for details. Bulk imports You can perform bulk imports by preparing a statement returned by pq.CopyIn (or pq.CopyInSchema) in an explicit transaction (sql.Tx). The returned statement handle can then be repeatedly "executed" to copy data into the target table. After all data has been processed you should call Exec() once with no arguments to flush all buffered data. Any call to Exec() might return an error which should be handled appropriately, but because of the internal buffering an error returned by Exec() might not be related to the data passed in the call that failed. CopyIn uses COPY FROM internally. It is not possible to COPY outside of an explicit transaction in pq. Usage example: txn, err := db.Begin() if err != nil { log.Fatal(err) } stmt, err := txn.Prepare(pq.CopyIn("users", "name", "age")) if err != nil { log.Fatal(err) } for _, user := range users { _, err = stmt.Exec(user.Name, int64(user.Age)) if err != nil { log.Fatal(err) } } _, err = stmt.Exec() if err != nil { log.Fatal(err) } err = stmt.Close() if err != nil { log.Fatal(err) } err = txn.Commit() if err != nil { log.Fatal(err) } Notifications PostgreSQL supports a simple publish/subscribe model over database connections. See http://www.postgresql.org/docs/current/static/sql-notify.html for more information about the general mechanism. To start listening for notifications, you first have to open a new connection to the database by calling NewListener. This connection can not be used for anything other than LISTEN / NOTIFY. Calling Listen will open a "notification channel"; once a notification channel is open, a notification generated on that channel will effect a send on the Listener.Notify channel. A notification channel will remain open until Unlisten is called, though connection loss might result in some notifications being lost. To solve this problem, Listener sends a nil pointer over the Notify channel any time the connection is re-established following a connection loss. The application can get information about the state of the underlying connection by setting an event callback in the call to NewListener. A single Listener can safely be used from concurrent goroutines, which means that there is often no need to create more than one Listener in your application. However, a Listener is always connected to a single database, so you will need to create a new Listener instance for every database you want to receive notifications in. The channel name in both Listen and Unlisten is case sensitive, and can contain any characters legal in an identifier (see http://www.postgresql.org/docs/current/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS for more information). Note that the channel name will be truncated to 63 bytes by the PostgreSQL server. You can find a complete, working example of Listener usage at https://godoc.org/github.com/lib/pq/example/listen. */ package pq golang-github-lib-pq-1.5.2/encode.go000066400000000000000000000402351365507746300172400ustar00rootroot00000000000000package pq import ( "bytes" "database/sql/driver" "encoding/binary" "encoding/hex" "errors" "fmt" "math" "regexp" "strconv" "strings" "sync" "time" "github.com/lib/pq/oid" ) var time2400Regex = regexp.MustCompile(`^(24:00(?::00(?:\.0+)?)?)(?:[Z+-].*)?$`) 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 strconv.AppendInt(nil, v, 10) case float64: return strconv.AppendFloat(nil, v, 'f', -1, 64) case []byte: if pgtypOid == oid.T_bytea { return encodeBytea(parameterStatus.serverVersion, v) } return v case string: if pgtypOid == oid.T_bytea { return encodeBytea(parameterStatus.serverVersion, []byte(v)) } return []byte(v) case bool: return strconv.AppendBool(nil, v) case time.Time: return formatTs(v) default: errorf("encode: unknown type for %T", v) } panic("not reached") } 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 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: return parseTs(nil, string(s)) case oid.T_time: return mustParse("15:04:05", typ, s) case oid.T_timetz: return mustParse("15:04:05-07", typ, s) case oid.T_bool: return s[0] == 't' case oid.T_int8, oid.T_int4, oid.T_int2: i, err := strconv.ParseInt(string(s), 10, 64) if err != nil { errorf("%s", err) } return i case oid.T_float4, oid.T_float8: // We always use 64 bit parsing, regardless of whether the input text is for // a float4 or float8, because clients expect float64s for all float datatypes // and returning a 32-bit parsed float64 produces lossy results. f, err := strconv.ParseFloat(string(s), 64) if err != nil { errorf("%s", err) } return f } return s } // appendEncodedText encodes item in text format as required by COPY // and appends to buf func appendEncodedText(parameterStatus *parameterStatus, buf []byte, x interface{}) []byte { switch v := x.(type) { case int64: return strconv.AppendInt(buf, v, 10) case float64: return strconv.AppendFloat(buf, v, 'f', -1, 64) case []byte: encodedBytea := encodeBytea(parameterStatus.serverVersion, v) return appendEscapedText(buf, string(encodedBytea)) case string: return appendEscapedText(buf, v) case bool: return strconv.AppendBool(buf, v) case time.Time: return append(buf, formatTs(v)...) case nil: return append(buf, "\\N"...) default: errorf("encode: unknown type for %T", v) } panic("not reached") } func appendEscapedText(buf []byte, text string) []byte { escapeNeeded := false startPos := 0 var c byte // check if we need to escape for i := 0; i < len(text); i++ { c = text[i] if c == '\\' || c == '\n' || c == '\r' || c == '\t' { escapeNeeded = true startPos = i break } } if !escapeNeeded { return append(buf, text...) } // copy till first char to escape, iterate the rest result := append(buf, text[:startPos]...) for i := startPos; i < len(text); i++ { c = text[i] switch c { case '\\': result = append(result, '\\', '\\') case '\n': result = append(result, '\\', 'n') case '\r': result = append(result, '\\', 'r') case '\t': result = append(result, '\\', 't') default: result = append(result, c) } } return result } func mustParse(f string, typ oid.Oid, s []byte) time.Time { str := string(s) // check for a 30-minute-offset timezone if (typ == oid.T_timestamptz || typ == oid.T_timetz) && str[len(str)-3] == ':' { f += ":00" } // Special case for 24:00 time. // Unfortunately, golang does not parse 24:00 as a proper time. // In this case, we want to try "round to the next day", to differentiate. // As such, we find if the 24:00 time matches at the beginning; if so, // we default it back to 00:00 but add a day later. var is2400Time bool switch typ { case oid.T_timetz, oid.T_time: if matches := time2400Regex.FindStringSubmatch(str); matches != nil { // Concatenate timezone information at the back. str = "00:00:00" + str[len(matches[1]):] is2400Time = true } } t, err := time.Parse(f, str) if err != nil { errorf("decode: %s", err) } if is2400Time { t = t.Add(24 * time.Hour) } return t } 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 (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 { if p.err == nil { p.err = fmt.Errorf("expected number; got '%v'", str) } return 0 } return result } // The location cache caches the time zones typically used by the client. type locationCache struct { cache map[int]*time.Location lock sync.Mutex } // All connections share the same list of timezones. Benchmarking shows that // 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 = newLocationCache() func newLocationCache() *locationCache { return &locationCache{cache: make(map[int]*time.Location)} } // Returns the cached timezone for the specified offset, creating and caching // it if necessary. func (c *locationCache) getLocation(offset int) *time.Location { c.lock.Lock() defer c.lock.Unlock() location, ok := c.cache[offset] if !ok { location = time.FixedZone("", offset) c.cache[offset] = 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) 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 := p.mustAtoi(str, 0, monSep) daySep := monSep + 3 month := p.mustAtoi(str, monSep+1, daySep) p.expect(str, '-', daySep) timeSep := daySep + 3 day := p.mustAtoi(str, daySep+1, timeSep) minLen := monSep + len("01-01") + 1 isBC := strings.HasSuffix(str, " BC") if isBC { minLen += 3 } var hour, minute, second int if len(str) > minLen { p.expect(str, ' ', timeSep) minSep := timeSep + 3 p.expect(str, ':', minSep) hour = p.mustAtoi(str, timeSep+1, minSep) secSep := minSep + 3 p.expect(str, ':', secSep) minute = p.mustAtoi(str, minSep+1, secSep) secEnd := secSep + 3 second = p.mustAtoi(str, secSep+1, secEnd) } remainderIdx := monSep + len("01-01 00:00:00") + 1 // Three optional (but ordered) sections follow: the // fractional seconds, the time zone offset, and the BC // designation. We set them up here and adjust the other // offsets if the preceding sections exist. nanoSec := 0 tzOff := 0 if remainderIdx < len(str) && str[remainderIdx] == '.' { fracStart := remainderIdx + 1 fracOff := strings.IndexAny(str[fracStart:], "-+ ") if fracOff < 0 { fracOff = len(str) - fracStart } 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] == '-' || str[tzStart] == '+') { // time zone separator is always '-' or '+' (UTC is +00) var tzSign int switch c := str[tzStart]; c { case '-': tzSign = -1 case '+': tzSign = +1 default: return time.Time{}, fmt.Errorf("expected '-' or '+' at position %v; got %v", tzStart, c) } tzHours := p.mustAtoi(str, tzStart+1, tzStart+3) remainderIdx += 3 var tzMin, tzSec int if remainderIdx < len(str) && str[remainderIdx] == ':' { tzMin = p.mustAtoi(str, remainderIdx+1, remainderIdx+3) remainderIdx += 3 } 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 isBC { isoYear = 1 - year remainderIdx += 3 } else { isoYear = year } if remainderIdx < len(str) { 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, globalLocationCache.getLocation(tzOff)) if currentLocation != nil { // Set the location of the returned Time based on the session's // TimeZone value, but only if the local time zone database agrees with // the remote database on the offset. lt := t.In(currentLocation) _, newOff := lt.Zone() if newOff == tzOff { t = lt } } return t, p.err } // 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 bc := false if t.Year() <= 0 { // flip year sign, and add 1, e.g: "0" will be "1", and "-10" will be "11" t = t.AddDate((-t.Year())*2+1, 0, 0) bc = true } b := []byte(t.Format("2006-01-02 15:04:05.999999999Z07:00")) _, offset := t.Zone() offset %= 60 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 bc { b = append(b, " BC"...) } 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, 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 { return nil, err } } else { // bytea_output = escape for len(s) > 0 { if s[0] == '\\' { // escaped '\\' if len(s) >= 2 && s[1] == '\\' { result = append(result, '\\') s = s[2:] continue } // '\\' followed by an octal number if len(s) < 4 { return nil, fmt.Errorf("invalid bytea sequence %v", s) } r, err := strconv.ParseInt(string(s[1:4]), 8, 9) if err != nil { return nil, fmt.Errorf("could not parse bytea value: %s", err.Error()) } result = append(result, byte(r)) s = s[4:] } else { // We hit an unescaped, raw byte. Try to read in as many as // possible in one go. i := bytes.IndexByte(s, '\\') if i == -1 { result = append(result, s...) break } result = append(result, s[:i]...) s = s[i:] } } } 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 = 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 { if b == '\\' { result = append(result, '\\', '\\') } else if b < 0x20 || b > 0x7e { result = append(result, []byte(fmt.Sprintf("\\%03o", b))...) } else { result = append(result, b) } } } return result } // NullTime represents a time.Time that may be null. NullTime implements the // sql.Scanner interface so it can be used as a scan destination, similar to // sql.NullString. type NullTime struct { Time time.Time Valid bool // Valid is true if Time is not NULL } // Scan implements the Scanner interface. func (nt *NullTime) Scan(value interface{}) error { nt.Time, nt.Valid = value.(time.Time) return nil } // Value implements the driver Valuer interface. func (nt NullTime) Value() (driver.Value, error) { if !nt.Valid { return nil, nil } return nt.Time, nil } golang-github-lib-pq-1.5.2/encode_test.go000066400000000000000000000607351365507746300203060ustar00rootroot00000000000000package pq import ( "bytes" "database/sql" "fmt" "regexp" "testing" "time" "github.com/lib/pq/oid" ) func TestScanTimestamp(t *testing.T) { var nt NullTime tn := time.Now() nt.Scan(tn) if !nt.Valid { t.Errorf("Expected Valid=false") } if nt.Time != tn { t.Errorf("Time value mismatch") } } func TestScanNilTimestamp(t *testing.T) { var nt NullTime nt.Scan(nil) if nt.Valid { t.Errorf("Expected Valid=false") } } var timeTests = []struct { str string timeval time.Time }{ {"22001-02-03", time.Date(22001, time.February, 3, 0, 0, 0, 0, time.FixedZone("", 0))}, {"2001-02-03", time.Date(2001, time.February, 3, 0, 0, 0, 0, time.FixedZone("", 0))}, {"0001-12-31 BC", time.Date(0, time.December, 31, 0, 0, 0, 0, time.FixedZone("", 0))}, {"2001-02-03 BC", time.Date(-2000, time.February, 3, 0, 0, 0, 0, time.FixedZone("", 0))}, {"2001-02-03 04:05:06", time.Date(2001, time.February, 3, 4, 5, 6, 0, time.FixedZone("", 0))}, {"2001-02-03 04:05:06.000001", time.Date(2001, time.February, 3, 4, 5, 6, 1000, time.FixedZone("", 0))}, {"2001-02-03 04:05:06.00001", time.Date(2001, time.February, 3, 4, 5, 6, 10000, time.FixedZone("", 0))}, {"2001-02-03 04:05:06.0001", time.Date(2001, time.February, 3, 4, 5, 6, 100000, time.FixedZone("", 0))}, {"2001-02-03 04:05:06.001", time.Date(2001, time.February, 3, 4, 5, 6, 1000000, time.FixedZone("", 0))}, {"2001-02-03 04:05:06.01", time.Date(2001, time.February, 3, 4, 5, 6, 10000000, time.FixedZone("", 0))}, {"2001-02-03 04:05:06.1", time.Date(2001, time.February, 3, 4, 5, 6, 100000000, time.FixedZone("", 0))}, {"2001-02-03 04:05:06.12", time.Date(2001, time.February, 3, 4, 5, 6, 120000000, time.FixedZone("", 0))}, {"2001-02-03 04:05:06.123", time.Date(2001, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))}, {"2001-02-03 04:05:06.1234", time.Date(2001, time.February, 3, 4, 5, 6, 123400000, time.FixedZone("", 0))}, {"2001-02-03 04:05:06.12345", time.Date(2001, time.February, 3, 4, 5, 6, 123450000, time.FixedZone("", 0))}, {"2001-02-03 04:05:06.123456", time.Date(2001, time.February, 3, 4, 5, 6, 123456000, time.FixedZone("", 0))}, {"2001-02-03 04:05:06.123-07", time.Date(2001, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", -7*60*60))}, {"2001-02-03 04:05:06-07", time.Date(2001, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -7*60*60))}, {"2001-02-03 04:05:06-07:42", time.Date(2001, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -(7*60*60+42*60)))}, {"2001-02-03 04:05:06-07:30:09", time.Date(2001, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -(7*60*60+30*60+9)))}, {"2001-02-03 04:05:06+07", time.Date(2001, time.February, 3, 4, 5, 6, 0, time.FixedZone("", 7*60*60))}, {"0011-02-03 04:05:06 BC", time.Date(-10, time.February, 3, 4, 5, 6, 0, time.FixedZone("", 0))}, {"0011-02-03 04:05:06.123 BC", time.Date(-10, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))}, {"0011-02-03 04:05:06.123-07 BC", time.Date(-10, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", -7*60*60))}, {"0001-02-03 04:05:06.123", time.Date(1, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))}, {"0001-02-03 04:05:06.123 BC", time.Date(1, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0)).AddDate(-1, 0, 0)}, {"0001-02-03 04:05:06.123 BC", time.Date(0, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))}, {"0002-02-03 04:05:06.123 BC", time.Date(0, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0)).AddDate(-1, 0, 0)}, {"0002-02-03 04:05:06.123 BC", time.Date(-1, time.February, 3, 4, 5, 6, 123000000, time.FixedZone("", 0))}, {"12345-02-03 04:05:06.1", time.Date(12345, time.February, 3, 4, 5, 6, 100000000, time.FixedZone("", 0))}, {"123456-02-03 04:05:06.1", time.Date(123456, time.February, 3, 4, 5, 6, 100000000, time.FixedZone("", 0))}, } // Test that parsing the string results in the expected value. func TestParseTs(t *testing.T) { for i, tt := range timeTests { val, err := ParseTimestamp(nil, tt.str) if err != nil { t.Errorf("%d: got error: %v", i, err) } else if val.String() != tt.timeval.String() { t.Errorf("%d: expected to parse %q into %q; got %q", i, tt.str, tt.timeval, val) } } } var timeErrorTests = []string{ "BC", " BC", "2001", "2001-2-03", "2001-02-3", "2001-02-03 ", "2001-02-03 B", "2001-02-03 04", "2001-02-03 04:", "2001-02-03 04:05", "2001-02-03 04:05 B", "2001-02-03 04:05 BC", "2001-02-03 04:05:", "2001-02-03 04:05:6", "2001-02-03 04:05:06 B", "2001-02-03 04:05:06BC", "2001-02-03 04:05:06.123 B", } // Test that parsing the string results in an error. func TestParseTsErrors(t *testing.T) { for i, tt := range timeErrorTests { _, err := ParseTimestamp(nil, tt) if err == nil { t.Errorf("%d: expected an error from parsing: %v", i, tt) } } } // Now test that sending the value into the database and parsing it back // returns the same time.Time value. func TestEncodeAndParseTs(t *testing.T) { db, err := openTestConnConninfo("timezone='Etc/UTC'") if err != nil { t.Fatal(err) } defer db.Close() for i, tt := range timeTests { var dbstr string err = db.QueryRow("SELECT ($1::timestamptz)::text", tt.timeval).Scan(&dbstr) if err != nil { t.Errorf("%d: could not send value %q to the database: %s", i, tt.timeval, err) continue } val, err := ParseTimestamp(nil, dbstr) if err != nil { t.Errorf("%d: could not parse value %q: %s", i, dbstr, err) continue } val = val.In(tt.timeval.Location()) if val.String() != tt.timeval.String() { t.Errorf("%d: expected to parse %q into %q; got %q", i, dbstr, tt.timeval, val) } } } var formatTimeTests = []struct { time time.Time expected string }{ {time.Time{}, "0001-01-01 00:00:00Z"}, {time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "2001-02-03 04:05:06.123456789Z"}, {time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "2001-02-03 04:05:06.123456789+02:00"}, {time.Date(2001, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "2001-02-03 04:05:06.123456789-06:00"}, {time.Date(2001, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -(7*60*60+30*60+9))), "2001-02-03 04:05:06-07:30:09"}, {time.Date(1, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "0001-02-03 04:05:06.123456789Z"}, {time.Date(1, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "0001-02-03 04:05:06.123456789+02:00"}, {time.Date(1, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "0001-02-03 04:05:06.123456789-06:00"}, {time.Date(0, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 0)), "0001-02-03 04:05:06.123456789Z BC"}, {time.Date(0, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", 2*60*60)), "0001-02-03 04:05:06.123456789+02:00 BC"}, {time.Date(0, time.February, 3, 4, 5, 6, 123456789, time.FixedZone("", -6*60*60)), "0001-02-03 04:05:06.123456789-06:00 BC"}, {time.Date(1, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -(7*60*60+30*60+9))), "0001-02-03 04:05:06-07:30:09"}, {time.Date(0, time.February, 3, 4, 5, 6, 0, time.FixedZone("", -(7*60*60+30*60+9))), "0001-02-03 04:05:06-07:30:09 BC"}, } func TestFormatTs(t *testing.T) { for i, tt := range formatTimeTests { val := string(formatTs(tt.time)) if val != tt.expected { t.Errorf("%d: incorrect time format %q, want %q", i, val, tt.expected) } } } func TestFormatTsBackend(t *testing.T) { db := openTestConn(t) defer db.Close() var str string err := db.QueryRow("SELECT '2001-02-03T04:05:06.007-08:09:10'::time::text").Scan(&str) if err == nil { t.Fatalf("PostgreSQL is accepting an ISO timestamp input for time") } for i, tt := range formatTimeTests { for _, typ := range []string{"date", "time", "timetz", "timestamp", "timestamptz"} { err = db.QueryRow("SELECT $1::"+typ+"::text", tt.time).Scan(&str) if err != nil { t.Errorf("%d: incorrect time format for %v on the backend: %v", i, typ, err) } } } } func TestTimeWithoutTimezone(t *testing.T) { db := openTestConn(t) defer db.Close() tx, err := db.Begin() if err != nil { t.Fatal(err) } defer tx.Rollback() for _, tc := range []struct { refTime string expectedTime time.Time }{ {"11:59:59", time.Date(0, 1, 1, 11, 59, 59, 0, time.UTC)}, {"24:00", time.Date(0, 1, 2, 0, 0, 0, 0, time.UTC)}, {"24:00:00", time.Date(0, 1, 2, 0, 0, 0, 0, time.UTC)}, {"24:00:00.0", time.Date(0, 1, 2, 0, 0, 0, 0, time.UTC)}, {"24:00:00.000000", time.Date(0, 1, 2, 0, 0, 0, 0, time.UTC)}, } { t.Run( fmt.Sprintf("%s => %s", tc.refTime, tc.expectedTime.Format(time.RFC3339)), func(t *testing.T) { var gotTime time.Time row := tx.QueryRow("select $1::time", tc.refTime) err = row.Scan(&gotTime) if err != nil { t.Fatal(err) } if !tc.expectedTime.Equal(gotTime) { t.Errorf("timestamps not equal: %s != %s", tc.expectedTime, gotTime) } }, ) } } func TestTimeWithTimezone(t *testing.T) { db := openTestConn(t) defer db.Close() tx, err := db.Begin() if err != nil { t.Fatal(err) } defer tx.Rollback() for _, tc := range []struct { refTime string expectedTime time.Time }{ {"11:59:59+00:00", time.Date(0, 1, 1, 11, 59, 59, 0, time.UTC)}, {"11:59:59+04:00", time.Date(0, 1, 1, 11, 59, 59, 0, time.FixedZone("+04", 4*60*60))}, {"24:00+00", time.Date(0, 1, 2, 0, 0, 0, 0, time.UTC)}, {"24:00Z", time.Date(0, 1, 2, 0, 0, 0, 0, time.UTC)}, {"24:00-04:00", time.Date(0, 1, 2, 0, 0, 0, 0, time.FixedZone("-04", -4*60*60))}, {"24:00:00+00", time.Date(0, 1, 2, 0, 0, 0, 0, time.UTC)}, {"24:00:00.0+00", time.Date(0, 1, 2, 0, 0, 0, 0, time.UTC)}, {"24:00:00.000000+00", time.Date(0, 1, 2, 0, 0, 0, 0, time.UTC)}, } { t.Run( fmt.Sprintf("%s => %s", tc.refTime, tc.expectedTime.Format(time.RFC3339)), func(t *testing.T) { var gotTime time.Time row := tx.QueryRow("select $1::timetz", tc.refTime) err = row.Scan(&gotTime) if err != nil { t.Fatal(err) } if !tc.expectedTime.Equal(gotTime) { t.Errorf("timestamps not equal: %s != %s", tc.expectedTime, gotTime) } }, ) } } func TestTimestampWithTimeZone(t *testing.T) { db := openTestConn(t) defer db.Close() tx, err := db.Begin() if err != nil { t.Fatal(err) } defer tx.Rollback() // try several different locations, all included in Go's zoneinfo.zip for _, locName := range []string{ "UTC", "America/Chicago", "America/New_York", "Australia/Darwin", "Australia/Perth", } { loc, err := time.LoadLocation(locName) if err != nil { t.Logf("Could not load time zone %s - skipping", locName) continue } // Postgres timestamps have a resolution of 1 microsecond, so don't // use the full range of the Nanosecond argument refTime := time.Date(2012, 11, 6, 10, 23, 42, 123456000, loc) for _, pgTimeZone := range []string{"US/Eastern", "Australia/Darwin"} { // Switch Postgres's timezone to test different output timestamp formats _, err = tx.Exec(fmt.Sprintf("set time zone '%s'", pgTimeZone)) if err != nil { t.Fatal(err) } var gotTime time.Time row := tx.QueryRow("select $1::timestamp with time zone", refTime) err = row.Scan(&gotTime) if err != nil { t.Fatal(err) } if !refTime.Equal(gotTime) { t.Errorf("timestamps not equal: %s != %s", refTime, gotTime) } // check that the time zone is set correctly based on TimeZone pgLoc, err := time.LoadLocation(pgTimeZone) if err != nil { t.Logf("Could not load time zone %s - skipping", pgLoc) continue } translated := refTime.In(pgLoc) if translated.String() != gotTime.String() { t.Errorf("timestamps not equal: %s != %s", translated, gotTime) } } } } func TestTimestampWithOutTimezone(t *testing.T) { db := openTestConn(t) defer db.Close() test := func(ts, pgts string) { r, err := db.Query("SELECT $1::timestamp", pgts) if err != nil { t.Fatalf("Could not run query: %v", err) } if !r.Next() { t.Fatal("Expected at least one row") } var result time.Time err = r.Scan(&result) if err != nil { t.Fatalf("Did not expect error scanning row: %v", err) } expected, err := time.Parse(time.RFC3339, ts) if err != nil { t.Fatalf("Could not parse test time literal: %v", err) } if !result.Equal(expected) { t.Fatalf("Expected time to match %v: got mismatch %v", expected, result) } if r.Next() { t.Fatal("Expected only one row") } } test("2000-01-01T00:00:00Z", "2000-01-01T00:00:00") // Test higher precision time test("2013-01-04T20:14:58.80033Z", "2013-01-04 20:14:58.80033") } func TestInfinityTimestamp(t *testing.T) { db := openTestConn(t) defer db.Close() var err error var resultT time.Time expectedErrorStrRegexp := regexp.MustCompile( `^sql: Scan error on column index 0(, name "timestamp(tz)?"|): unsupported`) type testCases []struct { Query string Param string ExpectedErrorStrRegexp *regexp.Regexp ExpectedVal interface{} } tc := testCases{ {"SELECT $1::timestamp", "-infinity", expectedErrorStrRegexp, "-infinity"}, {"SELECT $1::timestamptz", "-infinity", expectedErrorStrRegexp, "-infinity"}, {"SELECT $1::timestamp", "infinity", expectedErrorStrRegexp, "infinity"}, {"SELECT $1::timestamptz", "infinity", expectedErrorStrRegexp, "infinity"}, } // try to assert []byte to time.Time for _, q := range tc { err = db.QueryRow(q.Query, q.Param).Scan(&resultT) if err == nil || !q.ExpectedErrorStrRegexp.MatchString(err.Error()) { t.Errorf("Scanning -/+infinity, expected error to match regexp %q, got %q", q.ExpectedErrorStrRegexp, err) } } // yield []byte for _, q := range tc { var resultI interface{} err = db.QueryRow(q.Query, q.Param).Scan(&resultI) if err != nil { t.Errorf("Scanning -/+infinity, expected no error, got %q", err) } result, ok := resultI.([]byte) if !ok { t.Errorf("Scanning -/+infinity, expected []byte, got %#v", resultI) } if string(result) != q.ExpectedVal { t.Errorf("Scanning -/+infinity, expected %q, got %q", q.ExpectedVal, result) } } y1500 := time.Date(1500, time.January, 1, 0, 0, 0, 0, time.UTC) y2500 := time.Date(2500, time.January, 1, 0, 0, 0, 0, time.UTC) EnableInfinityTs(y1500, y2500) err = db.QueryRow("SELECT $1::timestamp", "infinity").Scan(&resultT) if err != nil { t.Errorf("Scanning infinity, expected no error, got %q", err) } if !resultT.Equal(y2500) { t.Errorf("Scanning infinity, expected %q, got %q", y2500, resultT) } err = db.QueryRow("SELECT $1::timestamptz", "infinity").Scan(&resultT) if err != nil { t.Errorf("Scanning infinity, expected no error, got %q", err) } if !resultT.Equal(y2500) { t.Errorf("Scanning Infinity, expected time %q, got %q", y2500, resultT.String()) } err = db.QueryRow("SELECT $1::timestamp", "-infinity").Scan(&resultT) if err != nil { t.Errorf("Scanning -infinity, expected no error, got %q", err) } if !resultT.Equal(y1500) { t.Errorf("Scanning -infinity, expected time %q, got %q", y1500, resultT.String()) } err = db.QueryRow("SELECT $1::timestamptz", "-infinity").Scan(&resultT) if err != nil { t.Errorf("Scanning -infinity, expected no error, got %q", err) } if !resultT.Equal(y1500) { t.Errorf("Scanning -infinity, expected time %q, got %q", y1500, resultT.String()) } ym1500 := time.Date(-1500, time.January, 1, 0, 0, 0, 0, time.UTC) y11500 := time.Date(11500, time.January, 1, 0, 0, 0, 0, time.UTC) var s string err = db.QueryRow("SELECT $1::timestamp::text", ym1500).Scan(&s) if err != nil { t.Errorf("Encoding -infinity, expected no error, got %q", err) } if s != "-infinity" { t.Errorf("Encoding -infinity, expected %q, got %q", "-infinity", s) } err = db.QueryRow("SELECT $1::timestamptz::text", ym1500).Scan(&s) if err != nil { t.Errorf("Encoding -infinity, expected no error, got %q", err) } if s != "-infinity" { t.Errorf("Encoding -infinity, expected %q, got %q", "-infinity", s) } err = db.QueryRow("SELECT $1::timestamp::text", y11500).Scan(&s) if err != nil { t.Errorf("Encoding infinity, expected no error, got %q", err) } if s != "infinity" { t.Errorf("Encoding infinity, expected %q, got %q", "infinity", s) } err = db.QueryRow("SELECT $1::timestamptz::text", y11500).Scan(&s) if err != nil { t.Errorf("Encoding infinity, expected no error, got %q", err) } if s != "infinity" { t.Errorf("Encoding infinity, expected %q, got %q", "infinity", s) } disableInfinityTs() var panicErrorString string func() { defer func() { panicErrorString, _ = recover().(string) }() EnableInfinityTs(y2500, y1500) }() if panicErrorString != infinityTsNegativeMustBeSmaller { t.Errorf("Expected error, %q, got %q", infinityTsNegativeMustBeSmaller, panicErrorString) } } func TestStringWithNul(t *testing.T) { db := openTestConn(t) defer db.Close() hello0world := string("hello\x00world") _, err := db.Query("SELECT $1::text", &hello0world) if err == nil { t.Fatal("Postgres accepts a string with nul in it; " + "injection attacks may be plausible") } } func TestByteSliceToText(t *testing.T) { db := openTestConn(t) defer db.Close() b := []byte("hello world") row := db.QueryRow("SELECT $1::text", b) var result []byte err := row.Scan(&result) if err != nil { t.Fatal(err) } if string(result) != string(b) { t.Fatalf("expected %v but got %v", b, result) } } func TestStringToBytea(t *testing.T) { db := openTestConn(t) defer db.Close() b := "hello world" row := db.QueryRow("SELECT $1::bytea", b) var result []byte err := row.Scan(&result) if err != nil { t.Fatal(err) } if !bytes.Equal(result, []byte(b)) { t.Fatalf("expected %v but got %v", b, result) } } func TestTextByteSliceToUUID(t *testing.T) { db := openTestConn(t) defer db.Close() b := []byte("a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11") row := db.QueryRow("SELECT $1::uuid", b) var result string err := row.Scan(&result) if forceBinaryParameters() { pqErr := err.(*Error) if pqErr == nil { t.Errorf("Expected to get error") } else if pqErr.Code != "22P03" { t.Fatalf("Expected to get invalid binary encoding error (22P03), got %s", pqErr.Code) } } else { if err != nil { t.Fatal(err) } if result != string(b) { t.Fatalf("expected %v but got %v", b, result) } } } func TestBinaryByteSlicetoUUID(t *testing.T) { db := openTestConn(t) defer db.Close() b := []byte{'\xa0', '\xee', '\xbc', '\x99', '\x9c', '\x0b', '\x4e', '\xf8', '\xbb', '\x00', '\x6b', '\xb9', '\xbd', '\x38', '\x0a', '\x11'} row := db.QueryRow("SELECT $1::uuid", b) var result string err := row.Scan(&result) if forceBinaryParameters() { if err != nil { t.Fatal(err) } if result != string("a0eebc99-9c0b-4ef8-bb00-6bb9bd380a11") { t.Fatalf("expected %v but got %v", b, result) } } else { pqErr := err.(*Error) if pqErr == nil { t.Errorf("Expected to get error") } else if pqErr.Code != "22021" { t.Fatalf("Expected to get invalid byte sequence for encoding error (22021), got %s", pqErr.Code) } } } func TestStringToUUID(t *testing.T) { db := openTestConn(t) defer db.Close() s := "a0eebc99-9c0b-4ef8-bb00-6bb9bd380a11" row := db.QueryRow("SELECT $1::uuid", s) var result string err := row.Scan(&result) if err != nil { t.Fatal(err) } if result != s { t.Fatalf("expected %v but got %v", s, result) } } func TestTextByteSliceToInt(t *testing.T) { db := openTestConn(t) defer db.Close() expected := 12345678 b := []byte(fmt.Sprintf("%d", expected)) row := db.QueryRow("SELECT $1::int", b) var result int err := row.Scan(&result) if forceBinaryParameters() { pqErr := err.(*Error) if pqErr == nil { t.Errorf("Expected to get error") } else if pqErr.Code != "22P03" { t.Fatalf("Expected to get invalid binary encoding error (22P03), got %s", pqErr.Code) } } else { if err != nil { t.Fatal(err) } if result != expected { t.Fatalf("expected %v but got %v", expected, result) } } } func TestBinaryByteSliceToInt(t *testing.T) { db := openTestConn(t) defer db.Close() expected := 12345678 b := []byte{'\x00', '\xbc', '\x61', '\x4e'} row := db.QueryRow("SELECT $1::int", b) var result int err := row.Scan(&result) if forceBinaryParameters() { if err != nil { t.Fatal(err) } if result != expected { t.Fatalf("expected %v but got %v", expected, result) } } else { pqErr := err.(*Error) if pqErr == nil { t.Errorf("Expected to get error") } else if pqErr.Code != "22021" { t.Fatalf("Expected to get invalid byte sequence for encoding error (22021), got %s", pqErr.Code) } } } func TestTextDecodeIntoString(t *testing.T) { input := []byte("hello world") want := string(input) for _, typ := range []oid.Oid{oid.T_char, oid.T_varchar, oid.T_text} { got := decode(¶meterStatus{}, input, typ, formatText) if got != want { t.Errorf("invalid string decoding output for %T(%+v), got %v but expected %v", typ, typ, got, want) } } } func TestByteaOutputFormatEncoding(t *testing.T) { input := []byte("\\x\x00\x01\x02\xFF\xFEabcdefg0123") want := []byte("\\x5c78000102fffe6162636465666730313233") got := encode(¶meterStatus{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(¶meterStatus{serverVersion: 84000}, input, oid.T_bytea) if !bytes.Equal(want, got) { t.Errorf("invalid escape bytea output, got %v but expected %v", got, want) } } func TestByteaOutputFormats(t *testing.T) { db := openTestConn(t) defer db.Close() if getServerVersion(t, db) < 90000 { // skip return } testByteaOutputFormat := func(f string, usePrepared bool) { expectedData := []byte("\x5c\x78\x00\xff\x61\x62\x63\x01\x08") sqlQuery := "SELECT decode('5c7800ff6162630108', 'hex')" var data []byte // use a txn to avoid relying on getting the same connection txn, err := db.Begin() if err != nil { t.Fatal(err) } defer txn.Rollback() _, err = txn.Exec("SET LOCAL bytea_output TO " + f) if err != nil { t.Fatal(err) } var rows *sql.Rows var stmt *sql.Stmt if usePrepared { stmt, err = txn.Prepare(sqlQuery) if err != nil { t.Fatal(err) } rows, err = stmt.Query() } else { // use Query; QueryRow would hide the actual error rows, err = txn.Query(sqlQuery) } if err != nil { t.Fatal(err) } if !rows.Next() { if rows.Err() != nil { t.Fatal(rows.Err()) } t.Fatal("shouldn't happen") } err = rows.Scan(&data) if err != nil { t.Fatal(err) } err = rows.Close() if err != nil { t.Fatal(err) } if stmt != nil { err = stmt.Close() if err != nil { t.Fatal(err) } } if !bytes.Equal(data, expectedData) { t.Errorf("unexpected bytea value %v for format %s; expected %v", data, f, expectedData) } } testByteaOutputFormat("hex", false) testByteaOutputFormat("escape", false) testByteaOutputFormat("hex", true) testByteaOutputFormat("escape", true) } func TestAppendEncodedText(t *testing.T) { var buf []byte buf = appendEncodedText(¶meterStatus{serverVersion: 90000}, buf, int64(10)) buf = append(buf, '\t') buf = appendEncodedText(¶meterStatus{serverVersion: 90000}, buf, 42.0000000001) buf = append(buf, '\t') buf = appendEncodedText(¶meterStatus{serverVersion: 90000}, buf, "hello\tworld") buf = append(buf, '\t') buf = appendEncodedText(¶meterStatus{serverVersion: 90000}, buf, []byte{0, 128, 255}) if string(buf) != "10\t42.0000000001\thello\\tworld\t\\\\x0080ff" { t.Fatal(string(buf)) } } func TestAppendEscapedText(t *testing.T) { if esc := appendEscapedText(nil, "hallo\tescape"); string(esc) != "hallo\\tescape" { t.Fatal(string(esc)) } if esc := appendEscapedText(nil, "hallo\\tescape\n"); string(esc) != "hallo\\\\tescape\\n" { t.Fatal(string(esc)) } if esc := appendEscapedText(nil, "\n\r\t\f"); string(esc) != "\\n\\r\\t\f" { t.Fatal(string(esc)) } } func TestAppendEscapedTextExistingBuffer(t *testing.T) { buf := []byte("123\t") if esc := appendEscapedText(buf, "hallo\tescape"); string(esc) != "123\thallo\\tescape" { t.Fatal(string(esc)) } buf = []byte("123\t") if esc := appendEscapedText(buf, "hallo\\tescape\n"); string(esc) != "123\thallo\\\\tescape\\n" { t.Fatal(string(esc)) } buf = []byte("123\t") if esc := appendEscapedText(buf, "\n\r\t\f"); string(esc) != "123\t\\n\\r\\t\f" { t.Fatal(string(esc)) } } func BenchmarkAppendEscapedText(b *testing.B) { longString := "" for i := 0; i < 100; i++ { longString += "123456789\n" } for i := 0; i < b.N; i++ { appendEscapedText(nil, longString) } } func BenchmarkAppendEscapedTextNoEscape(b *testing.B) { longString := "" for i := 0; i < 100; i++ { longString += "1234567890" } for i := 0; i < b.N; i++ { appendEscapedText(nil, longString) } } golang-github-lib-pq-1.5.2/error.go000066400000000000000000000363471365507746300171450ustar00rootroot00000000000000package pq import ( "database/sql/driver" "fmt" "io" "net" "runtime" ) // Error severities const ( Efatal = "FATAL" Epanic = "PANIC" Ewarning = "WARNING" Enotice = "NOTICE" Edebug = "DEBUG" Einfo = "INFO" Elog = "LOG" ) // Error represents an error communicating with the server. // // See http://www.postgresql.org/docs/current/static/protocol-error-fields.html for details of the fields type Error struct { Severity string Code ErrorCode Message string Detail string Hint string Position string InternalPosition string InternalQuery string Where string Schema string Table string Column string DataTypeName string Constraint string File string Line string Routine string } // ErrorCode is a five-character error code. type ErrorCode string // Name returns a more human friendly rendering of the error code, namely the // "condition name". // // See http://www.postgresql.org/docs/9.3/static/errcodes-appendix.html for // details. func (ec ErrorCode) Name() string { return errorCodeNames[ec] } // ErrorClass is only the class part of an error code. type ErrorClass string // Name returns the condition name of an error class. It is equivalent to the // condition name of the "standard" error code (i.e. the one having the last // three characters "000"). func (ec ErrorClass) Name() string { return errorCodeNames[ErrorCode(ec+"000")] } // Class returns the error class, e.g. "28". // // See http://www.postgresql.org/docs/9.3/static/errcodes-appendix.html for // details. func (ec ErrorCode) Class() ErrorClass { return ErrorClass(ec[0:2]) } // errorCodeNames is a mapping between the five-character error codes and the // human readable "condition names". It is derived from the list at // http://www.postgresql.org/docs/9.3/static/errcodes-appendix.html var errorCodeNames = map[ErrorCode]string{ // Class 00 - Successful Completion "00000": "successful_completion", // Class 01 - Warning "01000": "warning", "0100C": "dynamic_result_sets_returned", "01008": "implicit_zero_bit_padding", "01003": "null_value_eliminated_in_set_function", "01007": "privilege_not_granted", "01006": "privilege_not_revoked", "01004": "string_data_right_truncation", "01P01": "deprecated_feature", // Class 02 - No Data (this is also a warning class per the SQL standard) "02000": "no_data", "02001": "no_additional_dynamic_result_sets_returned", // Class 03 - SQL Statement Not Yet Complete "03000": "sql_statement_not_yet_complete", // Class 08 - Connection Exception "08000": "connection_exception", "08003": "connection_does_not_exist", "08006": "connection_failure", "08001": "sqlclient_unable_to_establish_sqlconnection", "08004": "sqlserver_rejected_establishment_of_sqlconnection", "08007": "transaction_resolution_unknown", "08P01": "protocol_violation", // Class 09 - Triggered Action Exception "09000": "triggered_action_exception", // Class 0A - Feature Not Supported "0A000": "feature_not_supported", // Class 0B - Invalid Transaction Initiation "0B000": "invalid_transaction_initiation", // Class 0F - Locator Exception "0F000": "locator_exception", "0F001": "invalid_locator_specification", // Class 0L - Invalid Grantor "0L000": "invalid_grantor", "0LP01": "invalid_grant_operation", // Class 0P - Invalid Role Specification "0P000": "invalid_role_specification", // Class 0Z - Diagnostics Exception "0Z000": "diagnostics_exception", "0Z002": "stacked_diagnostics_accessed_without_active_handler", // Class 20 - Case Not Found "20000": "case_not_found", // Class 21 - Cardinality Violation "21000": "cardinality_violation", // Class 22 - Data Exception "22000": "data_exception", "2202E": "array_subscript_error", "22021": "character_not_in_repertoire", "22008": "datetime_field_overflow", "22012": "division_by_zero", "22005": "error_in_assignment", "2200B": "escape_character_conflict", "22022": "indicator_overflow", "22015": "interval_field_overflow", "2201E": "invalid_argument_for_logarithm", "22014": "invalid_argument_for_ntile_function", "22016": "invalid_argument_for_nth_value_function", "2201F": "invalid_argument_for_power_function", "2201G": "invalid_argument_for_width_bucket_function", "22018": "invalid_character_value_for_cast", "22007": "invalid_datetime_format", "22019": "invalid_escape_character", "2200D": "invalid_escape_octet", "22025": "invalid_escape_sequence", "22P06": "nonstandard_use_of_escape_character", "22010": "invalid_indicator_parameter_value", "22023": "invalid_parameter_value", "2201B": "invalid_regular_expression", "2201W": "invalid_row_count_in_limit_clause", "2201X": "invalid_row_count_in_result_offset_clause", "22009": "invalid_time_zone_displacement_value", "2200C": "invalid_use_of_escape_character", "2200G": "most_specific_type_mismatch", "22004": "null_value_not_allowed", "22002": "null_value_no_indicator_parameter", "22003": "numeric_value_out_of_range", "2200H": "sequence_generator_limit_exceeded", "22026": "string_data_length_mismatch", "22001": "string_data_right_truncation", "22011": "substring_error", "22027": "trim_error", "22024": "unterminated_c_string", "2200F": "zero_length_character_string", "22P01": "floating_point_exception", "22P02": "invalid_text_representation", "22P03": "invalid_binary_representation", "22P04": "bad_copy_file_format", "22P05": "untranslatable_character", "2200L": "not_an_xml_document", "2200M": "invalid_xml_document", "2200N": "invalid_xml_content", "2200S": "invalid_xml_comment", "2200T": "invalid_xml_processing_instruction", // Class 23 - Integrity Constraint Violation "23000": "integrity_constraint_violation", "23001": "restrict_violation", "23502": "not_null_violation", "23503": "foreign_key_violation", "23505": "unique_violation", "23514": "check_violation", "23P01": "exclusion_violation", // Class 24 - Invalid Cursor State "24000": "invalid_cursor_state", // Class 25 - Invalid Transaction State "25000": "invalid_transaction_state", "25001": "active_sql_transaction", "25002": "branch_transaction_already_active", "25008": "held_cursor_requires_same_isolation_level", "25003": "inappropriate_access_mode_for_branch_transaction", "25004": "inappropriate_isolation_level_for_branch_transaction", "25005": "no_active_sql_transaction_for_branch_transaction", "25006": "read_only_sql_transaction", "25007": "schema_and_data_statement_mixing_not_supported", "25P01": "no_active_sql_transaction", "25P02": "in_failed_sql_transaction", // Class 26 - Invalid SQL Statement Name "26000": "invalid_sql_statement_name", // Class 27 - Triggered Data Change Violation "27000": "triggered_data_change_violation", // Class 28 - Invalid Authorization Specification "28000": "invalid_authorization_specification", "28P01": "invalid_password", // Class 2B - Dependent Privilege Descriptors Still Exist "2B000": "dependent_privilege_descriptors_still_exist", "2BP01": "dependent_objects_still_exist", // Class 2D - Invalid Transaction Termination "2D000": "invalid_transaction_termination", // Class 2F - SQL Routine Exception "2F000": "sql_routine_exception", "2F005": "function_executed_no_return_statement", "2F002": "modifying_sql_data_not_permitted", "2F003": "prohibited_sql_statement_attempted", "2F004": "reading_sql_data_not_permitted", // Class 34 - Invalid Cursor Name "34000": "invalid_cursor_name", // Class 38 - External Routine Exception "38000": "external_routine_exception", "38001": "containing_sql_not_permitted", "38002": "modifying_sql_data_not_permitted", "38003": "prohibited_sql_statement_attempted", "38004": "reading_sql_data_not_permitted", // Class 39 - External Routine Invocation Exception "39000": "external_routine_invocation_exception", "39001": "invalid_sqlstate_returned", "39004": "null_value_not_allowed", "39P01": "trigger_protocol_violated", "39P02": "srf_protocol_violated", // Class 3B - Savepoint Exception "3B000": "savepoint_exception", "3B001": "invalid_savepoint_specification", // Class 3D - Invalid Catalog Name "3D000": "invalid_catalog_name", // Class 3F - Invalid Schema Name "3F000": "invalid_schema_name", // Class 40 - Transaction Rollback "40000": "transaction_rollback", "40002": "transaction_integrity_constraint_violation", "40001": "serialization_failure", "40003": "statement_completion_unknown", "40P01": "deadlock_detected", // Class 42 - Syntax Error or Access Rule Violation "42000": "syntax_error_or_access_rule_violation", "42601": "syntax_error", "42501": "insufficient_privilege", "42846": "cannot_coerce", "42803": "grouping_error", "42P20": "windowing_error", "42P19": "invalid_recursion", "42830": "invalid_foreign_key", "42602": "invalid_name", "42622": "name_too_long", "42939": "reserved_name", "42804": "datatype_mismatch", "42P18": "indeterminate_datatype", "42P21": "collation_mismatch", "42P22": "indeterminate_collation", "42809": "wrong_object_type", "42703": "undefined_column", "42883": "undefined_function", "42P01": "undefined_table", "42P02": "undefined_parameter", "42704": "undefined_object", "42701": "duplicate_column", "42P03": "duplicate_cursor", "42P04": "duplicate_database", "42723": "duplicate_function", "42P05": "duplicate_prepared_statement", "42P06": "duplicate_schema", "42P07": "duplicate_table", "42712": "duplicate_alias", "42710": "duplicate_object", "42702": "ambiguous_column", "42725": "ambiguous_function", "42P08": "ambiguous_parameter", "42P09": "ambiguous_alias", "42P10": "invalid_column_reference", "42611": "invalid_column_definition", "42P11": "invalid_cursor_definition", "42P12": "invalid_database_definition", "42P13": "invalid_function_definition", "42P14": "invalid_prepared_statement_definition", "42P15": "invalid_schema_definition", "42P16": "invalid_table_definition", "42P17": "invalid_object_definition", // Class 44 - WITH CHECK OPTION Violation "44000": "with_check_option_violation", // Class 53 - Insufficient Resources "53000": "insufficient_resources", "53100": "disk_full", "53200": "out_of_memory", "53300": "too_many_connections", "53400": "configuration_limit_exceeded", // Class 54 - Program Limit Exceeded "54000": "program_limit_exceeded", "54001": "statement_too_complex", "54011": "too_many_columns", "54023": "too_many_arguments", // Class 55 - Object Not In Prerequisite State "55000": "object_not_in_prerequisite_state", "55006": "object_in_use", "55P02": "cant_change_runtime_param", "55P03": "lock_not_available", // Class 57 - Operator Intervention "57000": "operator_intervention", "57014": "query_canceled", "57P01": "admin_shutdown", "57P02": "crash_shutdown", "57P03": "cannot_connect_now", "57P04": "database_dropped", // Class 58 - System Error (errors external to PostgreSQL itself) "58000": "system_error", "58030": "io_error", "58P01": "undefined_file", "58P02": "duplicate_file", // Class F0 - Configuration File Error "F0000": "config_file_error", "F0001": "lock_file_exists", // Class HV - Foreign Data Wrapper Error (SQL/MED) "HV000": "fdw_error", "HV005": "fdw_column_name_not_found", "HV002": "fdw_dynamic_parameter_value_needed", "HV010": "fdw_function_sequence_error", "HV021": "fdw_inconsistent_descriptor_information", "HV024": "fdw_invalid_attribute_value", "HV007": "fdw_invalid_column_name", "HV008": "fdw_invalid_column_number", "HV004": "fdw_invalid_data_type", "HV006": "fdw_invalid_data_type_descriptors", "HV091": "fdw_invalid_descriptor_field_identifier", "HV00B": "fdw_invalid_handle", "HV00C": "fdw_invalid_option_index", "HV00D": "fdw_invalid_option_name", "HV090": "fdw_invalid_string_length_or_buffer_length", "HV00A": "fdw_invalid_string_format", "HV009": "fdw_invalid_use_of_null_pointer", "HV014": "fdw_too_many_handles", "HV001": "fdw_out_of_memory", "HV00P": "fdw_no_schemas", "HV00J": "fdw_option_name_not_found", "HV00K": "fdw_reply_handle", "HV00Q": "fdw_schema_not_found", "HV00R": "fdw_table_not_found", "HV00L": "fdw_unable_to_create_execution", "HV00M": "fdw_unable_to_create_reply", "HV00N": "fdw_unable_to_establish_connection", // Class P0 - PL/pgSQL Error "P0000": "plpgsql_error", "P0001": "raise_exception", "P0002": "no_data_found", "P0003": "too_many_rows", // Class XX - Internal Error "XX000": "internal_error", "XX001": "data_corrupted", "XX002": "index_corrupted", } func parseError(r *readBuf) *Error { err := new(Error) for t := r.byte(); t != 0; t = r.byte() { msg := r.string() switch t { case 'S': err.Severity = msg case 'C': err.Code = ErrorCode(msg) case 'M': err.Message = msg case 'D': err.Detail = msg case 'H': err.Hint = msg case 'P': err.Position = msg case 'p': err.InternalPosition = msg case 'q': err.InternalQuery = msg case 'W': err.Where = msg case 's': err.Schema = msg case 't': err.Table = msg case 'c': err.Column = msg case 'd': err.DataTypeName = msg case 'n': err.Constraint = msg case 'F': err.File = msg case 'L': err.Line = msg case 'R': err.Routine = msg } } return err } // Fatal returns true if the Error Severity is fatal. func (err *Error) Fatal() bool { return err.Severity == Efatal } // Get implements the legacy PGError interface. New code should use the fields // of the Error struct directly. func (err *Error) Get(k byte) (v string) { switch k { case 'S': return err.Severity case 'C': return string(err.Code) case 'M': return err.Message case 'D': return err.Detail case 'H': return err.Hint case 'P': return err.Position case 'p': return err.InternalPosition case 'q': return err.InternalQuery case 'W': return err.Where case 's': return err.Schema case 't': return err.Table case 'c': return err.Column case 'd': return err.DataTypeName case 'n': return err.Constraint case 'F': return err.File case 'L': return err.Line case 'R': return err.Routine } return "" } func (err Error) Error() string { return "pq: " + err.Message } // PGError is an interface used by previous versions of pq. It is provided // only to support legacy code. New code should use the Error type. type PGError interface { Error() string Fatal() bool Get(k byte) (v string) } func errorf(s string, args ...interface{}) { panic(fmt.Errorf("pq: %s", fmt.Sprintf(s, args...))) } // TODO(ainar-g) Rename to errorf after removing panics. func fmterrorf(s string, args ...interface{}) error { return 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 (cn *conn) errRecover(err *error) { e := recover() switch v := e.(type) { case nil: // Do nothing case runtime.Error: cn.bad = true panic(v) case *Error: if v.Fatal() { *err = driver.ErrBadConn } else { *err = v } case *net.OpError: cn.bad = true *err = v case error: if v == io.EOF || v.(error).Error() == "remote error: handshake failure" { *err = driver.ErrBadConn } else { *err = v } default: cn.bad = true panic(fmt.Sprintf("unknown error: %#v", e)) } // Any time we return ErrBadConn, we need to remember it since *Tx doesn't // mark the connection bad in database/sql. if *err == driver.ErrBadConn { cn.bad = true } } golang-github-lib-pq-1.5.2/example/000077500000000000000000000000001365507746300171035ustar00rootroot00000000000000golang-github-lib-pq-1.5.2/example/listen/000077500000000000000000000000001365507746300204015ustar00rootroot00000000000000golang-github-lib-pq-1.5.2/example/listen/doc.go000066400000000000000000000051011365507746300214720ustar00rootroot00000000000000/* Package listen is a self-contained Go program which uses the LISTEN / NOTIFY mechanism to avoid polling the database while waiting for more work to arrive. // // You can see the program in action by defining a function similar to // the following: // // CREATE OR REPLACE FUNCTION public.get_work() // RETURNS bigint // LANGUAGE sql // AS $$ // SELECT CASE WHEN random() >= 0.2 THEN int8 '1' END // $$ // ; package main import ( "database/sql" "fmt" "time" "github.com/lib/pq" ) func doWork(db *sql.DB, work int64) { // work here } func getWork(db *sql.DB) { for { // get work from the database here var work sql.NullInt64 err := db.QueryRow("SELECT get_work()").Scan(&work) if err != nil { fmt.Println("call to get_work() failed: ", err) time.Sleep(10 * time.Second) continue } if !work.Valid { // no more work to do fmt.Println("ran out of work") return } fmt.Println("starting work on ", work.Int64) go doWork(db, work.Int64) } } func waitForNotification(l *pq.Listener) { select { case <-l.Notify: fmt.Println("received notification, new work available") case <-time.After(90 * time.Second): go l.Ping() // Check if there's more work available, just in case it takes // a while for the Listener to notice connection loss and // reconnect. fmt.Println("received no work for 90 seconds, checking for new work") } } func main() { var conninfo string = "" db, err := sql.Open("postgres", conninfo) if err != nil { panic(err) } reportProblem := func(ev pq.ListenerEventType, err error) { if err != nil { fmt.Println(err.Error()) } } minReconn := 10 * time.Second maxReconn := time.Minute listener := pq.NewListener(conninfo, minReconn, maxReconn, reportProblem) err = listener.Listen("getwork") if err != nil { panic(err) } fmt.Println("entering main loop") for { // process all available work before waiting for notifications getWork(db) waitForNotification(listener) } } */ package listen golang-github-lib-pq-1.5.2/go.mod000066400000000000000000000000311365507746300165500ustar00rootroot00000000000000module github.com/lib/pq golang-github-lib-pq-1.5.2/go18_test.go000066400000000000000000000171351365507746300176230ustar00rootroot00000000000000package pq import ( "context" "database/sql" "runtime" "strings" "testing" "time" ) func TestMultipleSimpleQuery(t *testing.T) { db := openTestConn(t) defer db.Close() rows, err := db.Query("select 1; set time zone default; select 2; select 3") if err != nil { t.Fatal(err) } defer rows.Close() var i int for rows.Next() { if err := rows.Scan(&i); err != nil { t.Fatal(err) } if i != 1 { t.Fatalf("expected 1, got %d", i) } } if !rows.NextResultSet() { t.Fatal("expected more result sets", rows.Err()) } for rows.Next() { if err := rows.Scan(&i); err != nil { t.Fatal(err) } if i != 2 { t.Fatalf("expected 2, got %d", i) } } // Make sure that if we ignore a result we can still query. rows, err = db.Query("select 4; select 5") if err != nil { t.Fatal(err) } defer rows.Close() for rows.Next() { if err := rows.Scan(&i); err != nil { t.Fatal(err) } if i != 4 { t.Fatalf("expected 4, got %d", i) } } if !rows.NextResultSet() { t.Fatal("expected more result sets", rows.Err()) } for rows.Next() { if err := rows.Scan(&i); err != nil { t.Fatal(err) } if i != 5 { t.Fatalf("expected 5, got %d", i) } } if rows.NextResultSet() { t.Fatal("unexpected result set") } } const contextRaceIterations = 100 func TestContextCancelExec(t *testing.T) { db := openTestConn(t) defer db.Close() ctx, cancel := context.WithCancel(context.Background()) // Delay execution for just a bit until db.ExecContext has begun. defer time.AfterFunc(time.Millisecond*10, cancel).Stop() // Not canceled until after the exec has started. if _, err := db.ExecContext(ctx, "select pg_sleep(1)"); err == nil { t.Fatal("expected error") } else if err.Error() != "pq: canceling statement due to user request" { t.Fatalf("unexpected error: %s", err) } // Context is already canceled, so error should come before execution. if _, err := db.ExecContext(ctx, "select pg_sleep(1)"); err == nil { t.Fatal("expected error") } else if err.Error() != "context canceled" { t.Fatalf("unexpected error: %s", err) } for i := 0; i < contextRaceIterations; i++ { func() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() if _, err := db.ExecContext(ctx, "select 1"); err != nil { t.Fatal(err) } }() if _, err := db.Exec("select 1"); err != nil { t.Fatal(err) } } } func TestContextCancelQuery(t *testing.T) { db := openTestConn(t) defer db.Close() ctx, cancel := context.WithCancel(context.Background()) // Delay execution for just a bit until db.QueryContext has begun. defer time.AfterFunc(time.Millisecond*10, cancel).Stop() // Not canceled until after the exec has started. if _, err := db.QueryContext(ctx, "select pg_sleep(1)"); err == nil { t.Fatal("expected error") } else if err.Error() != "pq: canceling statement due to user request" { t.Fatalf("unexpected error: %s", err) } // Context is already canceled, so error should come before execution. if _, err := db.QueryContext(ctx, "select pg_sleep(1)"); err == nil { t.Fatal("expected error") } else if err.Error() != "context canceled" { t.Fatalf("unexpected error: %s", err) } for i := 0; i < contextRaceIterations; i++ { func() { ctx, cancel := context.WithCancel(context.Background()) rows, err := db.QueryContext(ctx, "select 1") cancel() if err != nil { t.Fatal(err) } else if err := rows.Close(); err != nil { t.Fatal(err) } }() if rows, err := db.Query("select 1"); err != nil { t.Fatal(err) } else if err := rows.Close(); err != nil { t.Fatal(err) } } } // TestIssue617 tests that a failed query in QueryContext doesn't lead to a // goroutine leak. func TestIssue617(t *testing.T) { db := openTestConn(t) defer db.Close() const N = 10 numGoroutineStart := runtime.NumGoroutine() for i := 0; i < N; i++ { func() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() _, err := db.QueryContext(ctx, `SELECT * FROM DOESNOTEXIST`) pqErr, _ := err.(*Error) // Expecting "pq: relation \"doesnotexist\" does not exist" error. if err == nil || pqErr == nil || pqErr.Code != "42P01" { t.Fatalf("expected undefined table error, got %v", err) } }() } numGoroutineFinish := runtime.NumGoroutine() // We use N/2 and not N because the GC and other actors may increase or // decrease the number of goroutines. if numGoroutineFinish-numGoroutineStart >= N/2 { t.Errorf("goroutine leak detected, was %d, now %d", numGoroutineStart, numGoroutineFinish) } } func TestContextCancelBegin(t *testing.T) { db := openTestConn(t) defer db.Close() ctx, cancel := context.WithCancel(context.Background()) tx, err := db.BeginTx(ctx, nil) if err != nil { t.Fatal(err) } // Delay execution for just a bit until tx.Exec has begun. defer time.AfterFunc(time.Millisecond*10, cancel).Stop() // Not canceled until after the exec has started. if _, err := tx.Exec("select pg_sleep(1)"); err == nil { t.Fatal("expected error") } else if err.Error() != "pq: canceling statement due to user request" { t.Fatalf("unexpected error: %s", err) } // Transaction is canceled, so expect an error. if _, err := tx.Query("select pg_sleep(1)"); err == nil { t.Fatal("expected error") } else if err != sql.ErrTxDone { t.Fatalf("unexpected error: %s", err) } // Context is canceled, so cannot begin a transaction. if _, err := db.BeginTx(ctx, nil); err == nil { t.Fatal("expected error") } else if err.Error() != "context canceled" { t.Fatalf("unexpected error: %s", err) } for i := 0; i < contextRaceIterations; i++ { func() { ctx, cancel := context.WithCancel(context.Background()) tx, err := db.BeginTx(ctx, nil) cancel() if err != nil { t.Fatal(err) } else if err := tx.Rollback(); err != nil && err.Error() != "pq: canceling statement due to user request" && err != sql.ErrTxDone { t.Fatal(err) } }() if tx, err := db.Begin(); err != nil { t.Fatal(err) } else if err := tx.Rollback(); err != nil { t.Fatal(err) } } } func TestTxOptions(t *testing.T) { db := openTestConn(t) defer db.Close() ctx := context.Background() tests := []struct { level sql.IsolationLevel isolation string }{ { level: sql.LevelDefault, isolation: "", }, { level: sql.LevelReadUncommitted, isolation: "read uncommitted", }, { level: sql.LevelReadCommitted, isolation: "read committed", }, { level: sql.LevelRepeatableRead, isolation: "repeatable read", }, { level: sql.LevelSerializable, isolation: "serializable", }, } for _, test := range tests { for _, ro := range []bool{true, false} { tx, err := db.BeginTx(ctx, &sql.TxOptions{ Isolation: test.level, ReadOnly: ro, }) if err != nil { t.Fatal(err) } var isolation string err = tx.QueryRow("select current_setting('transaction_isolation')").Scan(&isolation) if err != nil { t.Fatal(err) } if test.isolation != "" && isolation != test.isolation { t.Errorf("wrong isolation level: %s != %s", isolation, test.isolation) } var isRO string err = tx.QueryRow("select current_setting('transaction_read_only')").Scan(&isRO) if err != nil { t.Fatal(err) } if ro != (isRO == "on") { t.Errorf("read/[write,only] not set: %t != %s for level %s", ro, isRO, test.isolation) } tx.Rollback() } } _, err := db.BeginTx(ctx, &sql.TxOptions{ Isolation: sql.LevelLinearizable, }) if err == nil { t.Fatal("expected LevelLinearizable to fail") } if !strings.Contains(err.Error(), "isolation level not supported") { t.Errorf("Expected error to mention isolation level, got %q", err) } } golang-github-lib-pq-1.5.2/go19_test.go000066400000000000000000000040771365507746300176250ustar00rootroot00000000000000// +build go1.9 package pq import ( "context" "database/sql" "database/sql/driver" "reflect" "testing" ) func TestPing(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) db := openTestConn(t) defer db.Close() if _, ok := reflect.TypeOf(db).MethodByName("Conn"); !ok { t.Skipf("Conn method undefined on type %T, skipping test (requires at least go1.9)", db) } if err := db.PingContext(ctx); err != nil { t.Fatal("expected Ping to succeed") } defer cancel() // grab a connection conn, err := db.Conn(ctx) if err != nil { t.Fatal(err) } // start a transaction and read backend pid of our connection tx, err := conn.BeginTx(ctx, &sql.TxOptions{ Isolation: sql.LevelDefault, ReadOnly: true, }) if err != nil { t.Fatal(err) } rows, err := tx.Query("SELECT pg_backend_pid()") if err != nil { t.Fatal(err) } defer rows.Close() // read the pid from result var pid int for rows.Next() { if err := rows.Scan(&pid); err != nil { t.Fatal(err) } } if rows.Err() != nil { t.Fatal(err) } // Fail the transaction and make sure we can still ping. if _, err := tx.Query("INVALID SQL"); err == nil { t.Fatal("expected error") } if err := conn.PingContext(ctx); err != nil { t.Fatal(err) } if err := tx.Rollback(); err != nil { t.Fatal(err) } // kill the process which handles our connection and test if the ping fails if _, err := db.Exec("SELECT pg_terminate_backend($1)", pid); err != nil { t.Fatal(err) } if err := conn.PingContext(ctx); err != driver.ErrBadConn { t.Fatalf("expected error %s, instead got %s", driver.ErrBadConn, err) } } func TestCommitInFailedTransactionWithCancelContext(t *testing.T) { db := openTestConn(t) defer db.Close() ctx, cancel := context.WithCancel(context.Background()) defer cancel() txn, err := db.BeginTx(ctx, nil) if err != nil { t.Fatal(err) } rows, err := txn.Query("SELECT error") if err == nil { rows.Close() t.Fatal("expected failure") } err = txn.Commit() if err != ErrInFailedTransaction { t.Fatalf("expected ErrInFailedTransaction; got %#v", err) } } golang-github-lib-pq-1.5.2/hstore/000077500000000000000000000000001365507746300167545ustar00rootroot00000000000000golang-github-lib-pq-1.5.2/hstore/hstore.go000066400000000000000000000051051365507746300206100ustar00rootroot00000000000000package hstore import ( "database/sql" "database/sql/driver" "strings" ) // Hstore is a wrapper for transferring Hstore values back and forth easily. type Hstore struct { Map map[string]sql.NullString } // escapes and quotes hstore keys/values // s should be a sql.NullString or string func hQuote(s interface{}) string { var str string switch v := s.(type) { case sql.NullString: if !v.Valid { return "NULL" } str = v.String case string: str = v default: panic("not a string or sql.NullString") } str = strings.Replace(str, "\\", "\\\\", -1) return `"` + strings.Replace(str, "\"", "\\\"", -1) + `"` } // Scan implements the Scanner interface. // // Note h.Map is reallocated before the scan to clear existing values. If the // hstore column's database value is NULL, then h.Map is set to nil instead. func (h *Hstore) Scan(value interface{}) error { if value == nil { h.Map = nil return nil } h.Map = make(map[string]sql.NullString) var b byte pair := [][]byte{{}, {}} pi := 0 inQuote := false didQuote := false sawSlash := false bindex := 0 for bindex, b = range value.([]byte) { if sawSlash { pair[pi] = append(pair[pi], b) sawSlash = false continue } switch b { case '\\': sawSlash = true continue case '"': inQuote = !inQuote if !didQuote { didQuote = true } continue default: if !inQuote { switch b { case ' ', '\t', '\n', '\r': continue case '=': continue case '>': pi = 1 didQuote = false continue case ',': s := string(pair[1]) if !didQuote && len(s) == 4 && strings.ToLower(s) == "null" { h.Map[string(pair[0])] = sql.NullString{String: "", Valid: false} } else { h.Map[string(pair[0])] = sql.NullString{String: string(pair[1]), Valid: true} } pair[0] = []byte{} pair[1] = []byte{} pi = 0 continue } } } pair[pi] = append(pair[pi], b) } if bindex > 0 { s := string(pair[1]) if !didQuote && len(s) == 4 && strings.ToLower(s) == "null" { h.Map[string(pair[0])] = sql.NullString{String: "", Valid: false} } else { h.Map[string(pair[0])] = sql.NullString{String: string(pair[1]), Valid: true} } } return nil } // Value implements the driver Valuer interface. Note if h.Map is nil, the // database column value will be set to NULL. func (h Hstore) Value() (driver.Value, error) { if h.Map == nil { return nil, nil } parts := []string{} for key, val := range h.Map { thispart := hQuote(key) + "=>" + hQuote(val) parts = append(parts, thispart) } return []byte(strings.Join(parts, ",")), nil } golang-github-lib-pq-1.5.2/hstore/hstore_test.go000066400000000000000000000074061365507746300216550ustar00rootroot00000000000000package hstore import ( "database/sql" "os" "testing" _ "github.com/lib/pq" ) type Fatalistic interface { Fatal(args ...interface{}) } func openTestConn(t Fatalistic) *sql.DB { datname := os.Getenv("PGDATABASE") sslmode := os.Getenv("PGSSLMODE") if datname == "" { os.Setenv("PGDATABASE", "pqgotest") } if sslmode == "" { os.Setenv("PGSSLMODE", "disable") } conn, err := sql.Open("postgres", "") if err != nil { t.Fatal(err) } return conn } func TestHstore(t *testing.T) { db := openTestConn(t) defer db.Close() // quietly create hstore if it doesn't exist _, err := db.Exec("CREATE EXTENSION IF NOT EXISTS hstore") if err != nil { t.Skipf("Skipping hstore tests - hstore extension create failed: %s", err.Error()) } hs := Hstore{} // test for null-valued hstores err = db.QueryRow("SELECT NULL::hstore").Scan(&hs) if err != nil { t.Fatal(err) } if hs.Map != nil { t.Fatalf("expected null map") } err = db.QueryRow("SELECT $1::hstore", hs).Scan(&hs) if err != nil { t.Fatalf("re-query null map failed: %s", err.Error()) } if hs.Map != nil { t.Fatalf("expected null map") } // test for empty hstores err = db.QueryRow("SELECT ''::hstore").Scan(&hs) if err != nil { t.Fatal(err) } if hs.Map == nil { t.Fatalf("expected empty map, got null map") } if len(hs.Map) != 0 { t.Fatalf("expected empty map, got len(map)=%d", len(hs.Map)) } err = db.QueryRow("SELECT $1::hstore", hs).Scan(&hs) if err != nil { t.Fatalf("re-query empty map failed: %s", err.Error()) } if hs.Map == nil { t.Fatalf("expected empty map, got null map") } if len(hs.Map) != 0 { t.Fatalf("expected empty map, got len(map)=%d", len(hs.Map)) } // a few example maps to test out hsOnePair := Hstore{ Map: map[string]sql.NullString{ "key1": {String: "value1", Valid: true}, }, } hsThreePairs := Hstore{ Map: map[string]sql.NullString{ "key1": {String: "value1", Valid: true}, "key2": {String: "value2", Valid: true}, "key3": {String: "value3", Valid: true}, }, } hsSmorgasbord := Hstore{ Map: map[string]sql.NullString{ "nullstring": {String: "NULL", Valid: true}, "actuallynull": {String: "", Valid: false}, "NULL": {String: "NULL string key", Valid: true}, "withbracket": {String: "value>42", Valid: true}, "withequal": {String: "value=42", Valid: true}, `"withquotes1"`: {String: `this "should" be fine`, Valid: true}, `"withquotes"2"`: {String: `this "should\" also be fine`, Valid: true}, "embedded1": {String: "value1=>x1", Valid: true}, "embedded2": {String: `"value2"=>x2`, Valid: true}, "withnewlines": {String: "\n\nvalue\t=>2", Valid: true}, "<>": {String: `this, "should,\" also, => be fine`, Valid: true}, }, } // test encoding in query params, then decoding during Scan testBidirectional := func(h Hstore) { err = db.QueryRow("SELECT $1::hstore", h).Scan(&hs) if err != nil { t.Fatalf("re-query %d-pair map failed: %s", len(h.Map), err.Error()) } if hs.Map == nil { t.Fatalf("expected %d-pair map, got null map", len(h.Map)) } if len(hs.Map) != len(h.Map) { t.Fatalf("expected %d-pair map, got len(map)=%d", len(h.Map), len(hs.Map)) } for key, val := range hs.Map { otherval, found := h.Map[key] if !found { t.Fatalf(" key '%v' not found in %d-pair map", key, len(h.Map)) } if otherval.Valid != val.Valid { t.Fatalf(" value %v <> %v in %d-pair map", otherval, val, len(h.Map)) } if otherval.String != val.String { t.Fatalf(" value '%v' <> '%v' in %d-pair map", otherval.String, val.String, len(h.Map)) } } } testBidirectional(hsOnePair) testBidirectional(hsThreePairs) testBidirectional(hsSmorgasbord) } golang-github-lib-pq-1.5.2/issues_test.go000066400000000000000000000006701365507746300203540ustar00rootroot00000000000000package pq import "testing" func TestIssue494(t *testing.T) { db := openTestConn(t) defer db.Close() query := `CREATE TEMP TABLE t (i INT PRIMARY KEY)` if _, err := db.Exec(query); err != nil { t.Fatal(err) } txn, err := db.Begin() if err != nil { t.Fatal(err) } if _, err := txn.Prepare(CopyIn("t", "i")); err != nil { t.Fatal(err) } if _, err := txn.Query("SELECT 1"); err == nil { t.Fatal("expected error") } } golang-github-lib-pq-1.5.2/notice.go000066400000000000000000000047521365507746300172700ustar00rootroot00000000000000// +build go1.10 package pq import ( "context" "database/sql/driver" ) // NoticeHandler returns the notice handler on the given connection, if any. A // runtime panic occurs if c is not a pq connection. This is rarely used // directly, use ConnectorNoticeHandler and ConnectorWithNoticeHandler instead. func NoticeHandler(c driver.Conn) func(*Error) { return c.(*conn).noticeHandler } // SetNoticeHandler sets the given notice handler on the given connection. A // runtime panic occurs if c is not a pq connection. A nil handler may be used // to unset it. This is rarely used directly, use ConnectorNoticeHandler and // ConnectorWithNoticeHandler instead. // // Note: Notice handlers are executed synchronously by pq meaning commands // won't continue to be processed until the handler returns. func SetNoticeHandler(c driver.Conn, handler func(*Error)) { c.(*conn).noticeHandler = handler } // NoticeHandlerConnector wraps a regular connector and sets a notice handler // on it. type NoticeHandlerConnector struct { driver.Connector noticeHandler func(*Error) } // Connect calls the underlying connector's connect method and then sets the // notice handler. func (n *NoticeHandlerConnector) Connect(ctx context.Context) (driver.Conn, error) { c, err := n.Connector.Connect(ctx) if err == nil { SetNoticeHandler(c, n.noticeHandler) } return c, err } // ConnectorNoticeHandler returns the currently set notice handler, if any. If // the given connector is not a result of ConnectorWithNoticeHandler, nil is // returned. func ConnectorNoticeHandler(c driver.Connector) func(*Error) { if c, ok := c.(*NoticeHandlerConnector); ok { return c.noticeHandler } return nil } // ConnectorWithNoticeHandler creates or sets the given handler for the given // connector. If the given connector is a result of calling this function // previously, it is simply set on the given connector and returned. Otherwise, // this returns a new connector wrapping the given one and setting the notice // handler. A nil notice handler may be used to unset it. // // The returned connector is intended to be used with database/sql.OpenDB. // // Note: Notice handlers are executed synchronously by pq meaning commands // won't continue to be processed until the handler returns. func ConnectorWithNoticeHandler(c driver.Connector, handler func(*Error)) *NoticeHandlerConnector { if c, ok := c.(*NoticeHandlerConnector); ok { c.noticeHandler = handler return c } return &NoticeHandlerConnector{Connector: c, noticeHandler: handler} } golang-github-lib-pq-1.5.2/notice_example_test.go000066400000000000000000000012501365507746300220300ustar00rootroot00000000000000// +build go1.10 package pq_test import ( "database/sql" "fmt" "log" "github.com/lib/pq" ) func ExampleConnectorWithNoticeHandler() { name := "" // Base connector to wrap base, err := pq.NewConnector(name) if err != nil { log.Fatal(err) } // Wrap the connector to simply print out the message connector := pq.ConnectorWithNoticeHandler(base, func(notice *pq.Error) { fmt.Println("Notice sent: " + notice.Message) }) db := sql.OpenDB(connector) defer db.Close() // Raise a notice sql := "DO language plpgsql $$ BEGIN RAISE NOTICE 'test notice'; END $$" if _, err := db.Exec(sql); err != nil { log.Fatal(err) } // Output: // Notice sent: test notice } golang-github-lib-pq-1.5.2/notice_test.go000066400000000000000000000026361365507746300203260ustar00rootroot00000000000000// +build go1.10 package pq import ( "database/sql" "database/sql/driver" "testing" ) func TestConnectorWithNoticeHandler_Simple(t *testing.T) { b, err := NewConnector("") if err != nil { t.Fatal(err) } var notice *Error // Make connector w/ handler to set the local var c := ConnectorWithNoticeHandler(b, func(n *Error) { notice = n }) raiseNotice(c, t, "Test notice #1") if notice == nil || notice.Message != "Test notice #1" { t.Fatalf("Expected notice w/ message, got %v", notice) } // Unset the handler on the same connector prevC := c if c = ConnectorWithNoticeHandler(c, nil); c != prevC { t.Fatalf("Expected to not create new connector but did") } raiseNotice(c, t, "Test notice #2") if notice == nil || notice.Message != "Test notice #1" { t.Fatalf("Expected notice to not change, got %v", notice) } // Set it back on the same connector if c = ConnectorWithNoticeHandler(c, func(n *Error) { notice = n }); c != prevC { t.Fatal("Expected to not create new connector but did") } raiseNotice(c, t, "Test notice #3") if notice == nil || notice.Message != "Test notice #3" { t.Fatalf("Expected notice w/ message, got %v", notice) } } func raiseNotice(c driver.Connector, t *testing.T, escapedNotice string) { db := sql.OpenDB(c) defer db.Close() sql := "DO language plpgsql $$ BEGIN RAISE NOTICE '" + escapedNotice + "'; END $$" if _, err := db.Exec(sql); err != nil { t.Fatal(err) } } golang-github-lib-pq-1.5.2/notify.go000066400000000000000000000617721365507746300173240ustar00rootroot00000000000000package pq // Package pq is a pure Go Postgres driver for the database/sql package. // This module contains support for Postgres LISTEN/NOTIFY. import ( "context" "database/sql/driver" "errors" "fmt" "sync" "sync/atomic" "time" ) // Notification represents a single notification from the database. type Notification struct { // Process ID (PID) of the notifying postgres backend. BePid int // Name of the channel the notification was sent on. Channel string // Payload, or the empty string if unspecified. Extra string } func recvNotification(r *readBuf) *Notification { bePid := r.int32() channel := r.string() extra := r.string() return &Notification{bePid, channel, extra} } // SetNotificationHandler sets the given notification handler on the given // connection. A runtime panic occurs if c is not a pq connection. A nil handler // may be used to unset it. // // Note: Notification handlers are executed synchronously by pq meaning commands // won't continue to be processed until the handler returns. func SetNotificationHandler(c driver.Conn, handler func(*Notification)) { c.(*conn).notificationHandler = handler } // NotificationHandlerConnector wraps a regular connector and sets a notification handler // on it. type NotificationHandlerConnector struct { driver.Connector notificationHandler func(*Notification) } // Connect calls the underlying connector's connect method and then sets the // notification handler. func (n *NotificationHandlerConnector) Connect(ctx context.Context) (driver.Conn, error) { c, err := n.Connector.Connect(ctx) if err == nil { SetNotificationHandler(c, n.notificationHandler) } return c, err } // ConnectorNotificationHandler returns the currently set notification handler, if any. If // the given connector is not a result of ConnectorWithNotificationHandler, nil is // returned. func ConnectorNotificationHandler(c driver.Connector) func(*Notification) { if c, ok := c.(*NotificationHandlerConnector); ok { return c.notificationHandler } return nil } // ConnectorWithNotificationHandler creates or sets the given handler for the given // connector. If the given connector is a result of calling this function // previously, it is simply set on the given connector and returned. Otherwise, // this returns a new connector wrapping the given one and setting the notification // handler. A nil notification handler may be used to unset it. // // The returned connector is intended to be used with database/sql.OpenDB. // // Note: Notification handlers are executed synchronously by pq meaning commands // won't continue to be processed until the handler returns. func ConnectorWithNotificationHandler(c driver.Connector, handler func(*Notification)) *NotificationHandlerConnector { if c, ok := c.(*NotificationHandlerConnector); ok { c.notificationHandler = handler return c } return &NotificationHandlerConnector{Connector: c, notificationHandler: handler} } const ( connStateIdle int32 = iota connStateExpectResponse connStateExpectReadyForQuery ) type message struct { typ byte err error } var errListenerConnClosed = errors.New("pq: ListenerConn has been closed") // ListenerConn is a low-level interface for waiting for notifications. You // should use Listener instead. type ListenerConn struct { // guards cn and err connectionLock sync.Mutex cn *conn err error connState int32 // the sending goroutine will be holding this lock senderLock sync.Mutex notificationChan chan<- *Notification replyChan chan message } // NewListenerConn creates a new ListenerConn. Use NewListener instead. func NewListenerConn(name string, notificationChan chan<- *Notification) (*ListenerConn, error) { 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: c, connState: connStateIdle, replyChan: make(chan message, 2), } go l.listenerConnMain() return l, nil } // We can only allow one goroutine at a time to be running a query on the // connection for various reasons, so the goroutine sending on the connection // must be holding senderLock. // // Returns an error if an unrecoverable error has occurred and the ListenerConn // should be abandoned. func (l *ListenerConn) acquireSenderLock() error { // 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 } func (l *ListenerConn) releaseSenderLock() { l.senderLock.Unlock() } // setState advances the protocol state to newState. Returns false if moving // to that state from the current state is not allowed. func (l *ListenerConn) setState(newState int32) bool { var expectedState int32 switch newState { case connStateIdle: expectedState = connStateExpectReadyForQuery case connStateExpectResponse: expectedState = connStateIdle case connStateExpectReadyForQuery: expectedState = connStateExpectResponse default: panic(fmt.Sprintf("unexpected listenerConnState %d", newState)) } return atomic.CompareAndSwapInt32(&l.connState, expectedState, newState) } // Main logic is here: receive messages from the postgres backend, forward // notifications and query replies and keep the internal state in sync with the // protocol state. Returns when the connection has been lost, is about to go // away or should be discarded because we couldn't agree on the state with the // server backend. func (l *ListenerConn) listenerConnLoop() (err error) { defer errRecoverNoErrBadConn(&err) r := &readBuf{} for { t, err := l.cn.recvMessage(r) if err != nil { return err } switch t { case 'A': // recvNotification copies all the data so we don't need to worry // 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 // that, but we should make sure that the error we display is the // one from the stray ErrorResponse, not io.ErrUnexpectedEOF. if !l.setState(connStateExpectReadyForQuery) { return parseError(r) } l.replyChan <- message{t, parseError(r)} case 'C', 'I': if !l.setState(connStateExpectReadyForQuery) { // protocol out of sync return fmt.Errorf("unexpected CommandComplete") } // ExecSimpleQuery doesn't need to know about this message case 'Z': if !l.setState(connStateIdle) { // protocol out of sync return fmt.Errorf("unexpected ReadyForQuery") } l.replyChan <- message{t, nil} case 'S': // ignore case 'N': if n := l.cn.noticeHandler; n != nil { n(parseError(r)) } default: return fmt.Errorf("unexpected message %q from server in listenerConnLoop", t) } } } // This is the main routine for the goroutine receiving on the database // connection. Most of the main logic is in listenerConnLoop. func (l *ListenerConn) listenerConnMain() { err := l.listenerConnLoop() // listenerConnLoop terminated; we're done, but we still have to clean up. // Make sure nobody tries to start any new queries by making sure the err // pointer is set. It is important that we do not overwrite its value; a // connection could be closed by either this goroutine or one sending on // the connection -- whoever closes the connection is assumed to have the // more meaningful error message (as the other one will probably get // net.errClosed), so that goroutine sets the error we expose while the // other error is discarded. If the connection is lost while two // goroutines are operating on the socket, it probably doesn't matter which // error we expose so we don't try to do anything more complex. l.connectionLock.Lock() if l.err == nil { l.err = err } l.cn.Close() l.connectionLock.Unlock() // There might be a query in-flight; make sure nobody's waiting for a // response to it, since there's not going to be one. close(l.replyChan) // let the listener know we're done close(l.notificationChan) // this ListenerConn is done } // Listen sends a LISTEN query to the server. See ExecSimpleQuery. func (l *ListenerConn) Listen(channel string) (bool, error) { return l.ExecSimpleQuery("LISTEN " + QuoteIdentifier(channel)) } // Unlisten sends an UNLISTEN query to the server. See ExecSimpleQuery. func (l *ListenerConn) Unlisten(channel string) (bool, error) { return l.ExecSimpleQuery("UNLISTEN " + QuoteIdentifier(channel)) } // UnlistenAll sends an `UNLISTEN *` query to the server. See ExecSimpleQuery. func (l *ListenerConn) UnlistenAll() (bool, error) { return l.ExecSimpleQuery("UNLISTEN *") } // Ping the remote server to make sure it's alive. Non-nil error means the // connection has failed and should be abandoned. func (l *ListenerConn) Ping() error { sent, err := l.ExecSimpleQuery("") if !sent { return err } if err != nil { // shouldn't happen panic(err) } return nil } // Attempt to send a query on the connection. Returns an error if sending the // query failed, and the caller should initiate closure of this connection. // The caller must be holding senderLock (see acquireSenderLock and // releaseSenderLock). func (l *ListenerConn) sendSimpleQuery(q string) (err error) { defer errRecoverNoErrBadConn(&err) // must set connection state before sending the query if !l.setState(connStateExpectResponse) { panic("two queries running at the same time") } // Can't use l.cn.writeBuf here because it uses the scratch buffer which // might get overwritten by listenerConnLoop. b := &writeBuf{ buf: []byte("Q\x00\x00\x00\x00"), pos: 1, } b.string(q) l.cn.send(b) return nil } // ExecSimpleQuery executes a "simple query" (i.e. one with no bindable // parameters) on the connection. The possible return values are: // 1) "executed" is true; the query was executed to completion on the // database server. If the query failed, err will be set to the error // returned by the database, otherwise err will be nil. // 2) If "executed" is false, the query could not be executed on the remote // server. err will be non-nil. // // After a call to ExecSimpleQuery has returned an executed=false value, the // connection has either been closed or will be closed shortly thereafter, and // all subsequently executed queries will return an error. func (l *ListenerConn) ExecSimpleQuery(q string) (executed bool, err error) { if err = l.acquireSenderLock(); err != nil { return false, err } defer l.releaseSenderLock() err = l.sendSimpleQuery(q) if err != nil { // We can't know what state the protocol is in, so we need to abandon // this connection. l.connectionLock.Lock() // Set the error pointer if it hasn't been set already; see // listenerConnMain. if l.err == nil { l.err = err } l.connectionLock.Unlock() l.cn.c.Close() return false, err } // now we just wait for a reply.. for { m, ok := <-l.replyChan if !ok { // We lost the connection to server, don't bother waiting for a // 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': // sanity check if m.err != nil { panic("m.err != nil") } // done; err might or might not be set return true, err case 'E': // sanity check if m.err == nil { panic("m.err == nil") } // server responded with an error; ReadyForQuery to follow err = m.err default: return false, fmt.Errorf("unknown response for simple query: %q", m.typ) } } } // Close closes the connection. func (l *ListenerConn) Close() error { l.connectionLock.Lock() if l.err != nil { l.connectionLock.Unlock() return errListenerConnClosed } l.err = errListenerConnClosed 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 // this function until l.Notify has been closed. func (l *ListenerConn) Err() error { return l.err } var errListenerClosed = errors.New("pq: Listener has been closed") // ErrChannelAlreadyOpen is returned from Listen when a channel is already // open. var ErrChannelAlreadyOpen = errors.New("pq: channel is already open") // ErrChannelNotOpen is returned from Unlisten when a channel is not open. var ErrChannelNotOpen = errors.New("pq: channel is not open") // ListenerEventType is an enumeration of listener event types. type ListenerEventType int const ( // ListenerEventConnected is emitted only when the database connection // has been initially initialized. The err argument of the callback // will always be nil. ListenerEventConnected ListenerEventType = iota // ListenerEventDisconnected is emitted after a database connection has // been lost, either because of an error or because Close has been // called. The err argument will be set to the reason the database // connection was lost. ListenerEventDisconnected // ListenerEventReconnected is emitted after a database connection has // been re-established after connection loss. The err argument of the // callback will always be nil. After this event has been emitted, a // nil pq.Notification is sent on the Listener.Notify channel. ListenerEventReconnected // ListenerEventConnectionAttemptFailed is emitted after a connection // to the database was attempted, but failed. The err argument will be // set to an error describing why the connection attempt did not // succeed. ListenerEventConnectionAttemptFailed ) // EventCallbackType is the event callback type. See also ListenerEventType // constants' documentation. type EventCallbackType func(event ListenerEventType, err error) // Listener provides an interface for listening to notifications from a // PostgreSQL database. For general usage information, see section // "Notifications". // // Listener can safely be used from concurrently running goroutines. type Listener struct { // Channel for receiving notifications from the database. In some cases a // nil value will be sent. See section "Notifications" above. Notify chan *Notification name string minReconnectInterval time.Duration maxReconnectInterval time.Duration dialer Dialer eventCallback EventCallbackType lock sync.Mutex isClosed bool reconnectCond *sync.Cond cn *ListenerConn connNotificationChan <-chan *Notification channels map[string]struct{} } // NewListener creates a new database connection dedicated to LISTEN / NOTIFY. // // name should be set to a connection string to be used to establish the // database connection (see section "Connection String Parameters" above). // // minReconnectInterval controls the duration to wait before trying to // re-establish the database connection after connection loss. After each // consecutive failure this interval is doubled, until maxReconnectInterval is // reached. Successfully completing the connection establishment procedure // resets the interval back to minReconnectInterval. // // The last parameter eventCallback can be set to a function which will be // called by the Listener when the state of the underlying database connection // changes. This callback will be called by the goroutine which dispatches the // notifications over the Notify channel, so you should try to avoid doing // potentially time-consuming operations from the callback. 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{}), Notify: make(chan *Notification, 32), } l.reconnectCond = sync.NewCond(&l.lock) go l.listenerMain() return l } // NotificationChannel returns the notification channel for this listener. // This is the same channel as Notify, and will not be recreated during the // life time of the Listener. func (l *Listener) NotificationChannel() <-chan *Notification { return l.Notify } // Listen starts listening for notifications on a channel. Calls to this // function will block until an acknowledgement has been received from the // server. Note that Listener automatically re-establishes the connection // after connection loss, so this function may block indefinitely if the // connection can not be re-established. // // Listen will only fail in three conditions: // 1) The channel is already open. The returned error will be // ErrChannelAlreadyOpen. // 2) The query was executed on the remote server, but PostgreSQL returned an // error message in response to the query. The returned error will be a // pq.Error containing the information the server supplied. // 3) Close is called on the Listener before the request could be completed. // // The channel name is case-sensitive. func (l *Listener) Listen(channel string) error { l.lock.Lock() defer l.lock.Unlock() if l.isClosed { return errListenerClosed } // The server allows you to issue a LISTEN on a channel which is already // open, but it seems useful to be able to detect this case to spot for // mistakes in application logic. If the application genuinely does't // care, it can check the exported error and ignore it. _, exists := l.channels[channel] if exists { return ErrChannelAlreadyOpen } if l.cn != nil { // If gotResponse is true but error is set, the query was executed on // the remote server, but resulted in an error. This should be // relatively rare, so it's fine if we just pass the error to our // caller. However, if gotResponse is false, we could not complete the // query on the remote server and our underlying connection is about // to go away, so we only add relname to l.channels, and wait for // resync() to take care of the rest. gotResponse, err := l.cn.Listen(channel) if gotResponse && err != nil { return err } } l.channels[channel] = struct{}{} for l.cn == nil { l.reconnectCond.Wait() // we let go of the mutex for a while if l.isClosed { return errListenerClosed } } return nil } // Unlisten removes a channel from the Listener's channel list. Returns // ErrChannelNotOpen if the Listener is not listening on the specified channel. // Returns immediately with no error if there is no connection. Note that you // might still get notifications for this channel even after Unlisten has // returned. // // The channel name is case-sensitive. func (l *Listener) Unlisten(channel string) error { l.lock.Lock() defer l.lock.Unlock() if l.isClosed { return errListenerClosed } // Similarly to LISTEN, this is not an error in Postgres, but it seems // useful to distinguish from the normal conditions. _, exists := l.channels[channel] if !exists { return ErrChannelNotOpen } if l.cn != nil { // Similarly to Listen (see comment in that function), the caller // should only be bothered with an error if it came from the backend as // a response to our query. gotResponse, err := l.cn.Unlisten(channel) if gotResponse && err != nil { return err } } // Don't bother waiting for resync if there's no connection. delete(l.channels, channel) return nil } // UnlistenAll removes all channels from the Listener's channel list. Returns // immediately with no error if there is no connection. Note that you might // still get notifications for any of the deleted channels even after // UnlistenAll has returned. func (l *Listener) UnlistenAll() error { l.lock.Lock() defer l.lock.Unlock() if l.isClosed { return errListenerClosed } if l.cn != nil { // Similarly to Listen (see comment in that function), the caller // should only be bothered with an error if it came from the backend as // a response to our query. gotResponse, err := l.cn.UnlistenAll() if gotResponse && err != nil { return err } } // Don't bother waiting for resync if there's no connection. l.channels = make(map[string]struct{}) return nil } // Ping the remote server to make sure it's alive. Non-nil return value means // that there is no active connection. func (l *Listener) Ping() error { l.lock.Lock() defer l.lock.Unlock() if l.isClosed { return errListenerClosed } if l.cn == nil { return errors.New("no connection") } return l.cn.Ping() } // Clean up after losing the server connection. Returns l.cn.Err(), which // should have the reason the connection was lost. func (l *Listener) disconnectCleanup() error { l.lock.Lock() defer l.lock.Unlock() // sanity check; can't look at Err() until the channel has been closed select { case _, ok := <-l.connNotificationChan: if ok { panic("connNotificationChan not closed") } default: panic("connNotificationChan not closed") } err := l.cn.Err() l.cn.Close() l.cn = nil return err } // Synchronize the list of channels we want to be listening on with the server // after the connection has been established. func (l *Listener) resync(cn *ListenerConn, notificationChan <-chan *Notification) error { doneChan := make(chan error) go func(notificationChan <-chan *Notification) { for channel := range l.channels { // If we got a response, return that error to our caller as it's // going to be more descriptive than cn.Err(). gotResponse, err := cn.Listen(channel) if gotResponse && err != nil { doneChan <- err return } // If we couldn't reach the server, wait for notificationChan to // close and then return the error message from the connection, as // per ListenerConn's interface. if err != nil { for range notificationChan { } doneChan <- cn.Err() return } } doneChan <- nil }(notificationChan) // Ignore notifications while synchronization is going on to avoid // deadlocks. We have to send a nil notification over Notify anyway as // we can't possibly know which notifications (if any) were lost while // the connection was down, so there's no reason to try and process // these messages at all. for { select { case _, ok := <-notificationChan: if !ok { notificationChan = nil } case err := <-doneChan: return err } } } // caller should NOT be holding l.lock func (l *Listener) closed() bool { l.lock.Lock() defer l.lock.Unlock() return l.isClosed } func (l *Listener) connect() error { notificationChan := make(chan *Notification, 32) cn, err := newDialListenerConn(l.dialer, l.name, notificationChan) if err != nil { return err } l.lock.Lock() defer l.lock.Unlock() err = l.resync(cn, notificationChan) if err != nil { cn.Close() return err } l.cn = cn l.connNotificationChan = notificationChan l.reconnectCond.Broadcast() return nil } // Close disconnects the Listener from the database and shuts it down. // Subsequent calls to its methods will return an error. Close returns an // error if the connection has already been closed. func (l *Listener) Close() error { l.lock.Lock() defer l.lock.Unlock() if l.isClosed { return errListenerClosed } if l.cn != nil { l.cn.Close() } l.isClosed = true // Unblock calls to Listen() l.reconnectCond.Broadcast() return nil } func (l *Listener) emitEvent(event ListenerEventType, err error) { if l.eventCallback != nil { l.eventCallback(event, err) } } // Main logic here: maintain a connection to the server when possible, wait // for notifications and emit events. func (l *Listener) listenerConnLoop() { var nextReconnect time.Time reconnectInterval := l.minReconnectInterval for { for { err := l.connect() if err == nil { break } if l.closed() { return } l.emitEvent(ListenerEventConnectionAttemptFailed, err) time.Sleep(reconnectInterval) reconnectInterval *= 2 if reconnectInterval > l.maxReconnectInterval { reconnectInterval = l.maxReconnectInterval } } if nextReconnect.IsZero() { l.emitEvent(ListenerEventConnected, nil) } else { l.emitEvent(ListenerEventReconnected, nil) l.Notify <- nil } reconnectInterval = l.minReconnectInterval nextReconnect = time.Now().Add(reconnectInterval) for { notification, ok := <-l.connNotificationChan if !ok { // lost connection, loop again break } l.Notify <- notification } err := l.disconnectCleanup() if l.closed() { return } l.emitEvent(ListenerEventDisconnected, err) time.Sleep(time.Until(nextReconnect)) } } func (l *Listener) listenerMain() { l.listenerConnLoop() close(l.Notify) } golang-github-lib-pq-1.5.2/notify_test.go000066400000000000000000000302321365507746300203460ustar00rootroot00000000000000package pq import ( "database/sql" "database/sql/driver" "errors" "fmt" "io" "os" "runtime" "sync" "testing" "time" ) var errNilNotification = errors.New("nil notification") func expectNotification(t *testing.T, ch <-chan *Notification, relname string, extra string) error { select { case n := <-ch: if n == nil { return errNilNotification } if n.Channel != relname || n.Extra != extra { return fmt.Errorf("unexpected notification %v", n) } return nil case <-time.After(1500 * time.Millisecond): return fmt.Errorf("timeout") } } func expectNoNotification(t *testing.T, ch <-chan *Notification) error { select { case n := <-ch: return fmt.Errorf("unexpected notification %v", n) case <-time.After(100 * time.Millisecond): return nil } } func expectEvent(t *testing.T, eventch <-chan ListenerEventType, et ListenerEventType) error { select { case e := <-eventch: if e != et { return fmt.Errorf("unexpected event %v", e) } return nil case <-time.After(1500 * time.Millisecond): panic("expectEvent timeout") } } func expectNoEvent(t *testing.T, eventch <-chan ListenerEventType) error { select { case e := <-eventch: return fmt.Errorf("unexpected event %v", e) case <-time.After(100 * time.Millisecond): return nil } } func newTestListenerConn(t *testing.T) (*ListenerConn, <-chan *Notification) { datname := os.Getenv("PGDATABASE") sslmode := os.Getenv("PGSSLMODE") if datname == "" { os.Setenv("PGDATABASE", "pqgotest") } if sslmode == "" { os.Setenv("PGSSLMODE", "disable") } notificationChan := make(chan *Notification) l, err := NewListenerConn("", notificationChan) if err != nil { t.Fatal(err) } return l, notificationChan } func TestNewListenerConn(t *testing.T) { l, _ := newTestListenerConn(t) defer l.Close() } func TestConnListen(t *testing.T) { l, channel := newTestListenerConn(t) defer l.Close() db := openTestConn(t) defer db.Close() ok, err := l.Listen("notify_test") if !ok || err != nil { t.Fatal(err) } _, err = db.Exec("NOTIFY notify_test") if err != nil { t.Fatal(err) } err = expectNotification(t, channel, "notify_test", "") if err != nil { t.Fatal(err) } } func TestConnUnlisten(t *testing.T) { l, channel := newTestListenerConn(t) defer l.Close() db := openTestConn(t) defer db.Close() ok, err := l.Listen("notify_test") if !ok || err != nil { t.Fatal(err) } _, err = db.Exec("NOTIFY notify_test") if err != nil { t.Fatal(err) } err = expectNotification(t, channel, "notify_test", "") if err != nil { t.Fatal(err) } ok, err = l.Unlisten("notify_test") if !ok || err != nil { t.Fatal(err) } _, err = db.Exec("NOTIFY notify_test") if err != nil { t.Fatal(err) } err = expectNoNotification(t, channel) if err != nil { t.Fatal(err) } } func TestConnUnlistenAll(t *testing.T) { l, channel := newTestListenerConn(t) defer l.Close() db := openTestConn(t) defer db.Close() ok, err := l.Listen("notify_test") if !ok || err != nil { t.Fatal(err) } _, err = db.Exec("NOTIFY notify_test") if err != nil { t.Fatal(err) } err = expectNotification(t, channel, "notify_test", "") if err != nil { t.Fatal(err) } ok, err = l.UnlistenAll() if !ok || err != nil { t.Fatal(err) } _, err = db.Exec("NOTIFY notify_test") if err != nil { t.Fatal(err) } err = expectNoNotification(t, channel) if err != nil { t.Fatal(err) } } func TestConnClose(t *testing.T) { l, _ := newTestListenerConn(t) defer l.Close() err := l.Close() if err != nil { t.Fatal(err) } err = l.Close() if err != errListenerConnClosed { t.Fatalf("expected errListenerConnClosed; got %v", err) } } func TestConnPing(t *testing.T) { l, _ := newTestListenerConn(t) defer l.Close() err := l.Ping() if err != nil { t.Fatal(err) } err = l.Close() if err != nil { t.Fatal(err) } err = l.Ping() if err != errListenerConnClosed { t.Fatalf("expected errListenerConnClosed; got %v", err) } } // Test for deadlock where a query fails while another one is queued func TestConnExecDeadlock(t *testing.T) { l, _ := newTestListenerConn(t) defer l.Close() var wg sync.WaitGroup wg.Add(2) go func() { l.ExecSimpleQuery("SELECT pg_sleep(60)") wg.Done() }() runtime.Gosched() go func() { l.ExecSimpleQuery("SELECT 1") wg.Done() }() // give the two goroutines some time to get into position runtime.Gosched() // calls Close on the net.Conn; equivalent to a network failure l.Close() defer time.AfterFunc(10*time.Second, func() { panic("timed out") }).Stop() wg.Wait() } // Test for ListenerConn being closed while a slow query is executing func TestListenerConnCloseWhileQueryIsExecuting(t *testing.T) { l, _ := newTestListenerConn(t) defer l.Close() var wg sync.WaitGroup wg.Add(1) go func() { sent, err := l.ExecSimpleQuery("SELECT pg_sleep(60)") if sent { panic("expected sent=false") } // could be any of a number of errors if err == nil { panic("expected error") } wg.Done() }() // give the above goroutine some time to get into position runtime.Gosched() err := l.Close() if err != nil { t.Fatal(err) } defer time.AfterFunc(10*time.Second, func() { panic("timed out") }).Stop() wg.Wait() } func TestNotifyExtra(t *testing.T) { db := openTestConn(t) defer db.Close() if getServerVersion(t, db) < 90000 { t.Skip("skipping NOTIFY payload test since the server does not appear to support it") } l, channel := newTestListenerConn(t) defer l.Close() ok, err := l.Listen("notify_test") if !ok || err != nil { t.Fatal(err) } _, err = db.Exec("NOTIFY notify_test, 'something'") if err != nil { t.Fatal(err) } err = expectNotification(t, channel, "notify_test", "something") if err != nil { t.Fatal(err) } } // create a new test listener and also set the timeouts func newTestListenerTimeout(t *testing.T, min time.Duration, max time.Duration) (*Listener, <-chan ListenerEventType) { datname := os.Getenv("PGDATABASE") sslmode := os.Getenv("PGSSLMODE") if datname == "" { os.Setenv("PGDATABASE", "pqgotest") } if sslmode == "" { os.Setenv("PGSSLMODE", "disable") } eventch := make(chan ListenerEventType, 16) l := NewListener("", min, max, func(t ListenerEventType, err error) { eventch <- t }) err := expectEvent(t, eventch, ListenerEventConnected) if err != nil { t.Fatal(err) } return l, eventch } func newTestListener(t *testing.T) (*Listener, <-chan ListenerEventType) { return newTestListenerTimeout(t, time.Hour, time.Hour) } func TestListenerListen(t *testing.T) { l, _ := newTestListener(t) defer l.Close() db := openTestConn(t) defer db.Close() err := l.Listen("notify_listen_test") if err != nil { t.Fatal(err) } _, err = db.Exec("NOTIFY notify_listen_test") if err != nil { t.Fatal(err) } err = expectNotification(t, l.Notify, "notify_listen_test", "") if err != nil { t.Fatal(err) } } func TestListenerUnlisten(t *testing.T) { l, _ := newTestListener(t) defer l.Close() db := openTestConn(t) defer db.Close() err := l.Listen("notify_listen_test") if err != nil { t.Fatal(err) } _, err = db.Exec("NOTIFY notify_listen_test") if err != nil { t.Fatal(err) } err = l.Unlisten("notify_listen_test") if err != nil { t.Fatal(err) } err = expectNotification(t, l.Notify, "notify_listen_test", "") if err != nil { t.Fatal(err) } _, err = db.Exec("NOTIFY notify_listen_test") if err != nil { t.Fatal(err) } err = expectNoNotification(t, l.Notify) if err != nil { t.Fatal(err) } } func TestListenerUnlistenAll(t *testing.T) { l, _ := newTestListener(t) defer l.Close() db := openTestConn(t) defer db.Close() err := l.Listen("notify_listen_test") if err != nil { t.Fatal(err) } _, err = db.Exec("NOTIFY notify_listen_test") if err != nil { t.Fatal(err) } err = l.UnlistenAll() if err != nil { t.Fatal(err) } err = expectNotification(t, l.Notify, "notify_listen_test", "") if err != nil { t.Fatal(err) } _, err = db.Exec("NOTIFY notify_listen_test") if err != nil { t.Fatal(err) } err = expectNoNotification(t, l.Notify) if err != nil { t.Fatal(err) } } func TestListenerFailedQuery(t *testing.T) { l, eventch := newTestListener(t) defer l.Close() db := openTestConn(t) defer db.Close() err := l.Listen("notify_listen_test") if err != nil { t.Fatal(err) } _, err = db.Exec("NOTIFY notify_listen_test") if err != nil { t.Fatal(err) } err = expectNotification(t, l.Notify, "notify_listen_test", "") if err != nil { t.Fatal(err) } // shouldn't cause a disconnect ok, err := l.cn.ExecSimpleQuery("SELECT error") if !ok { t.Fatalf("could not send query to server: %v", err) } _, ok = err.(PGError) if !ok { t.Fatalf("unexpected error %v", err) } err = expectNoEvent(t, eventch) if err != nil { t.Fatal(err) } // should still work _, err = db.Exec("NOTIFY notify_listen_test") if err != nil { t.Fatal(err) } err = expectNotification(t, l.Notify, "notify_listen_test", "") if err != nil { t.Fatal(err) } } func TestListenerReconnect(t *testing.T) { l, eventch := newTestListenerTimeout(t, 20*time.Millisecond, time.Hour) defer l.Close() db := openTestConn(t) defer db.Close() err := l.Listen("notify_listen_test") if err != nil { t.Fatal(err) } _, err = db.Exec("NOTIFY notify_listen_test") if err != nil { t.Fatal(err) } err = expectNotification(t, l.Notify, "notify_listen_test", "") if err != nil { t.Fatal(err) } // kill the connection and make sure it comes back up ok, err := l.cn.ExecSimpleQuery("SELECT pg_terminate_backend(pg_backend_pid())") if ok { t.Fatalf("could not kill the connection: %v", err) } if err != io.EOF { t.Fatalf("unexpected error %v", err) } err = expectEvent(t, eventch, ListenerEventDisconnected) if err != nil { t.Fatal(err) } err = expectEvent(t, eventch, ListenerEventReconnected) if err != nil { t.Fatal(err) } // should still work _, err = db.Exec("NOTIFY notify_listen_test") if err != nil { t.Fatal(err) } // should get nil after Reconnected err = expectNotification(t, l.Notify, "", "") if err != errNilNotification { t.Fatal(err) } err = expectNotification(t, l.Notify, "notify_listen_test", "") if err != nil { t.Fatal(err) } } func TestListenerClose(t *testing.T) { l, _ := newTestListenerTimeout(t, 20*time.Millisecond, time.Hour) defer l.Close() err := l.Close() if err != nil { t.Fatal(err) } err = l.Close() if err != errListenerClosed { t.Fatalf("expected errListenerClosed; got %v", err) } } func TestListenerPing(t *testing.T) { l, _ := newTestListenerTimeout(t, 20*time.Millisecond, time.Hour) defer l.Close() err := l.Ping() if err != nil { t.Fatal(err) } err = l.Close() if err != nil { t.Fatal(err) } err = l.Ping() if err != errListenerClosed { t.Fatalf("expected errListenerClosed; got %v", err) } } func TestConnectorWithNotificationHandler_Simple(t *testing.T) { b, err := NewConnector("") if err != nil { t.Fatal(err) } var notification *Notification // Make connector w/ handler to set the local var c := ConnectorWithNotificationHandler(b, func(n *Notification) { notification = n }) sendNotification(c, t, "Test notification #1") if notification == nil || notification.Extra != "Test notification #1" { t.Fatalf("Expected notification w/ message, got %v", notification) } // Unset the handler on the same connector prevC := c if c = ConnectorWithNotificationHandler(c, nil); c != prevC { t.Fatalf("Expected to not create new connector but did") } sendNotification(c, t, "Test notification #2") if notification == nil || notification.Extra != "Test notification #1" { t.Fatalf("Expected notification to not change, got %v", notification) } // Set it back on the same connector if c = ConnectorWithNotificationHandler(c, func(n *Notification) { notification = n }); c != prevC { t.Fatal("Expected to not create new connector but did") } sendNotification(c, t, "Test notification #3") if notification == nil || notification.Extra != "Test notification #3" { t.Fatalf("Expected notification w/ message, got %v", notification) } } func sendNotification(c driver.Connector, t *testing.T, escapedNotification string) { db := sql.OpenDB(c) defer db.Close() sql := fmt.Sprintf("LISTEN foo; NOTIFY foo, '%s';", escapedNotification) if _, err := db.Exec(sql); err != nil { t.Fatal(err) } } golang-github-lib-pq-1.5.2/oid/000077500000000000000000000000001365507746300162235ustar00rootroot00000000000000golang-github-lib-pq-1.5.2/oid/doc.go000066400000000000000000000002111365507746300173110ustar00rootroot00000000000000// Package oid contains OID constants // as defined by the Postgres server. package oid // Oid is a Postgres Object ID. type Oid uint32 golang-github-lib-pq-1.5.2/oid/gen.go000066400000000000000000000032741365507746300173310ustar00rootroot00000000000000// +build ignore // Generate the table of OID values // Run with 'go run gen.go'. package main import ( "database/sql" "fmt" "log" "os" "os/exec" "strings" _ "github.com/lib/pq" ) // OID represent a postgres Object Identifier Type. type OID struct { ID int Type string } // Name returns an upper case version of the oid type. func (o OID) Name() string { return strings.ToUpper(o.Type) } func main() { datname := os.Getenv("PGDATABASE") sslmode := os.Getenv("PGSSLMODE") if datname == "" { os.Setenv("PGDATABASE", "pqgotest") } if sslmode == "" { os.Setenv("PGSSLMODE", "disable") } db, err := sql.Open("postgres", "") if err != nil { log.Fatal(err) } rows, err := db.Query(` SELECT typname, oid FROM pg_type WHERE oid < 10000 ORDER BY oid; `) if err != nil { log.Fatal(err) } oids := make([]*OID, 0) for rows.Next() { var oid OID if err = rows.Scan(&oid.Type, &oid.ID); err != nil { log.Fatal(err) } oids = append(oids, &oid) } if err = rows.Err(); err != nil { log.Fatal(err) } cmd := exec.Command("gofmt") cmd.Stderr = os.Stderr w, err := cmd.StdinPipe() if err != nil { log.Fatal(err) } f, err := os.Create("types.go") if err != nil { log.Fatal(err) } cmd.Stdout = f err = cmd.Start() if err != nil { log.Fatal(err) } fmt.Fprintln(w, "// Code generated by gen.go. DO NOT EDIT.") fmt.Fprintln(w, "\npackage oid") fmt.Fprintln(w, "const (") for _, oid := range oids { fmt.Fprintf(w, "T_%s Oid = %d\n", oid.Type, oid.ID) } fmt.Fprintln(w, ")") fmt.Fprintln(w, "var TypeName = map[Oid]string{") for _, oid := range oids { fmt.Fprintf(w, "T_%s: \"%s\",\n", oid.Type, oid.Name()) } fmt.Fprintln(w, "}") w.Close() cmd.Wait() } golang-github-lib-pq-1.5.2/oid/types.go000066400000000000000000000245761365507746300177340ustar00rootroot00000000000000// Code generated by gen.go. DO NOT EDIT. package oid const ( T_bool Oid = 16 T_bytea Oid = 17 T_char Oid = 18 T_name Oid = 19 T_int8 Oid = 20 T_int2 Oid = 21 T_int2vector Oid = 22 T_int4 Oid = 23 T_regproc Oid = 24 T_text Oid = 25 T_oid Oid = 26 T_tid Oid = 27 T_xid Oid = 28 T_cid Oid = 29 T_oidvector Oid = 30 T_pg_ddl_command Oid = 32 T_pg_type Oid = 71 T_pg_attribute Oid = 75 T_pg_proc Oid = 81 T_pg_class Oid = 83 T_json Oid = 114 T_xml Oid = 142 T__xml Oid = 143 T_pg_node_tree Oid = 194 T__json Oid = 199 T_smgr Oid = 210 T_index_am_handler Oid = 325 T_point Oid = 600 T_lseg Oid = 601 T_path Oid = 602 T_box Oid = 603 T_polygon Oid = 604 T_line Oid = 628 T__line Oid = 629 T_cidr Oid = 650 T__cidr Oid = 651 T_float4 Oid = 700 T_float8 Oid = 701 T_abstime Oid = 702 T_reltime Oid = 703 T_tinterval Oid = 704 T_unknown Oid = 705 T_circle Oid = 718 T__circle Oid = 719 T_money Oid = 790 T__money Oid = 791 T_macaddr Oid = 829 T_inet Oid = 869 T__bool Oid = 1000 T__bytea Oid = 1001 T__char Oid = 1002 T__name Oid = 1003 T__int2 Oid = 1005 T__int2vector Oid = 1006 T__int4 Oid = 1007 T__regproc Oid = 1008 T__text Oid = 1009 T__tid Oid = 1010 T__xid Oid = 1011 T__cid Oid = 1012 T__oidvector Oid = 1013 T__bpchar Oid = 1014 T__varchar Oid = 1015 T__int8 Oid = 1016 T__point Oid = 1017 T__lseg Oid = 1018 T__path Oid = 1019 T__box Oid = 1020 T__float4 Oid = 1021 T__float8 Oid = 1022 T__abstime Oid = 1023 T__reltime Oid = 1024 T__tinterval Oid = 1025 T__polygon Oid = 1027 T__oid Oid = 1028 T_aclitem Oid = 1033 T__aclitem Oid = 1034 T__macaddr Oid = 1040 T__inet Oid = 1041 T_bpchar Oid = 1042 T_varchar Oid = 1043 T_date Oid = 1082 T_time Oid = 1083 T_timestamp Oid = 1114 T__timestamp Oid = 1115 T__date Oid = 1182 T__time Oid = 1183 T_timestamptz Oid = 1184 T__timestamptz Oid = 1185 T_interval Oid = 1186 T__interval Oid = 1187 T__numeric Oid = 1231 T_pg_database Oid = 1248 T__cstring Oid = 1263 T_timetz Oid = 1266 T__timetz Oid = 1270 T_bit Oid = 1560 T__bit Oid = 1561 T_varbit Oid = 1562 T__varbit Oid = 1563 T_numeric Oid = 1700 T_refcursor Oid = 1790 T__refcursor Oid = 2201 T_regprocedure Oid = 2202 T_regoper Oid = 2203 T_regoperator Oid = 2204 T_regclass Oid = 2205 T_regtype Oid = 2206 T__regprocedure Oid = 2207 T__regoper Oid = 2208 T__regoperator Oid = 2209 T__regclass Oid = 2210 T__regtype Oid = 2211 T_record Oid = 2249 T_cstring Oid = 2275 T_any Oid = 2276 T_anyarray Oid = 2277 T_void Oid = 2278 T_trigger Oid = 2279 T_language_handler Oid = 2280 T_internal Oid = 2281 T_opaque Oid = 2282 T_anyelement Oid = 2283 T__record Oid = 2287 T_anynonarray Oid = 2776 T_pg_authid Oid = 2842 T_pg_auth_members Oid = 2843 T__txid_snapshot Oid = 2949 T_uuid Oid = 2950 T__uuid Oid = 2951 T_txid_snapshot Oid = 2970 T_fdw_handler Oid = 3115 T_pg_lsn Oid = 3220 T__pg_lsn Oid = 3221 T_tsm_handler Oid = 3310 T_anyenum Oid = 3500 T_tsvector Oid = 3614 T_tsquery Oid = 3615 T_gtsvector Oid = 3642 T__tsvector Oid = 3643 T__gtsvector Oid = 3644 T__tsquery Oid = 3645 T_regconfig Oid = 3734 T__regconfig Oid = 3735 T_regdictionary Oid = 3769 T__regdictionary Oid = 3770 T_jsonb Oid = 3802 T__jsonb Oid = 3807 T_anyrange Oid = 3831 T_event_trigger Oid = 3838 T_int4range Oid = 3904 T__int4range Oid = 3905 T_numrange Oid = 3906 T__numrange Oid = 3907 T_tsrange Oid = 3908 T__tsrange Oid = 3909 T_tstzrange Oid = 3910 T__tstzrange Oid = 3911 T_daterange Oid = 3912 T__daterange Oid = 3913 T_int8range Oid = 3926 T__int8range Oid = 3927 T_pg_shseclabel Oid = 4066 T_regnamespace Oid = 4089 T__regnamespace Oid = 4090 T_regrole Oid = 4096 T__regrole Oid = 4097 ) var TypeName = map[Oid]string{ T_bool: "BOOL", T_bytea: "BYTEA", T_char: "CHAR", T_name: "NAME", T_int8: "INT8", T_int2: "INT2", T_int2vector: "INT2VECTOR", T_int4: "INT4", T_regproc: "REGPROC", T_text: "TEXT", T_oid: "OID", T_tid: "TID", T_xid: "XID", T_cid: "CID", T_oidvector: "OIDVECTOR", T_pg_ddl_command: "PG_DDL_COMMAND", T_pg_type: "PG_TYPE", T_pg_attribute: "PG_ATTRIBUTE", T_pg_proc: "PG_PROC", T_pg_class: "PG_CLASS", T_json: "JSON", T_xml: "XML", T__xml: "_XML", T_pg_node_tree: "PG_NODE_TREE", T__json: "_JSON", T_smgr: "SMGR", T_index_am_handler: "INDEX_AM_HANDLER", T_point: "POINT", T_lseg: "LSEG", T_path: "PATH", T_box: "BOX", T_polygon: "POLYGON", T_line: "LINE", T__line: "_LINE", T_cidr: "CIDR", T__cidr: "_CIDR", T_float4: "FLOAT4", T_float8: "FLOAT8", T_abstime: "ABSTIME", T_reltime: "RELTIME", T_tinterval: "TINTERVAL", T_unknown: "UNKNOWN", T_circle: "CIRCLE", T__circle: "_CIRCLE", T_money: "MONEY", T__money: "_MONEY", T_macaddr: "MACADDR", T_inet: "INET", T__bool: "_BOOL", T__bytea: "_BYTEA", T__char: "_CHAR", T__name: "_NAME", T__int2: "_INT2", T__int2vector: "_INT2VECTOR", T__int4: "_INT4", T__regproc: "_REGPROC", T__text: "_TEXT", T__tid: "_TID", T__xid: "_XID", T__cid: "_CID", T__oidvector: "_OIDVECTOR", T__bpchar: "_BPCHAR", T__varchar: "_VARCHAR", T__int8: "_INT8", T__point: "_POINT", T__lseg: "_LSEG", T__path: "_PATH", T__box: "_BOX", T__float4: "_FLOAT4", T__float8: "_FLOAT8", T__abstime: "_ABSTIME", T__reltime: "_RELTIME", T__tinterval: "_TINTERVAL", T__polygon: "_POLYGON", T__oid: "_OID", T_aclitem: "ACLITEM", T__aclitem: "_ACLITEM", T__macaddr: "_MACADDR", T__inet: "_INET", T_bpchar: "BPCHAR", T_varchar: "VARCHAR", T_date: "DATE", T_time: "TIME", T_timestamp: "TIMESTAMP", T__timestamp: "_TIMESTAMP", T__date: "_DATE", T__time: "_TIME", T_timestamptz: "TIMESTAMPTZ", T__timestamptz: "_TIMESTAMPTZ", T_interval: "INTERVAL", T__interval: "_INTERVAL", T__numeric: "_NUMERIC", T_pg_database: "PG_DATABASE", T__cstring: "_CSTRING", T_timetz: "TIMETZ", T__timetz: "_TIMETZ", T_bit: "BIT", T__bit: "_BIT", T_varbit: "VARBIT", T__varbit: "_VARBIT", T_numeric: "NUMERIC", T_refcursor: "REFCURSOR", T__refcursor: "_REFCURSOR", T_regprocedure: "REGPROCEDURE", T_regoper: "REGOPER", T_regoperator: "REGOPERATOR", T_regclass: "REGCLASS", T_regtype: "REGTYPE", T__regprocedure: "_REGPROCEDURE", T__regoper: "_REGOPER", T__regoperator: "_REGOPERATOR", T__regclass: "_REGCLASS", T__regtype: "_REGTYPE", T_record: "RECORD", T_cstring: "CSTRING", T_any: "ANY", T_anyarray: "ANYARRAY", T_void: "VOID", T_trigger: "TRIGGER", T_language_handler: "LANGUAGE_HANDLER", T_internal: "INTERNAL", T_opaque: "OPAQUE", T_anyelement: "ANYELEMENT", T__record: "_RECORD", T_anynonarray: "ANYNONARRAY", T_pg_authid: "PG_AUTHID", T_pg_auth_members: "PG_AUTH_MEMBERS", T__txid_snapshot: "_TXID_SNAPSHOT", T_uuid: "UUID", T__uuid: "_UUID", T_txid_snapshot: "TXID_SNAPSHOT", T_fdw_handler: "FDW_HANDLER", T_pg_lsn: "PG_LSN", T__pg_lsn: "_PG_LSN", T_tsm_handler: "TSM_HANDLER", T_anyenum: "ANYENUM", T_tsvector: "TSVECTOR", T_tsquery: "TSQUERY", T_gtsvector: "GTSVECTOR", T__tsvector: "_TSVECTOR", T__gtsvector: "_GTSVECTOR", T__tsquery: "_TSQUERY", T_regconfig: "REGCONFIG", T__regconfig: "_REGCONFIG", T_regdictionary: "REGDICTIONARY", T__regdictionary: "_REGDICTIONARY", T_jsonb: "JSONB", T__jsonb: "_JSONB", T_anyrange: "ANYRANGE", T_event_trigger: "EVENT_TRIGGER", T_int4range: "INT4RANGE", T__int4range: "_INT4RANGE", T_numrange: "NUMRANGE", T__numrange: "_NUMRANGE", T_tsrange: "TSRANGE", T__tsrange: "_TSRANGE", T_tstzrange: "TSTZRANGE", T__tstzrange: "_TSTZRANGE", T_daterange: "DATERANGE", T__daterange: "_DATERANGE", T_int8range: "INT8RANGE", T__int8range: "_INT8RANGE", T_pg_shseclabel: "PG_SHSECLABEL", T_regnamespace: "REGNAMESPACE", T__regnamespace: "_REGNAMESPACE", T_regrole: "REGROLE", T__regrole: "_REGROLE", } golang-github-lib-pq-1.5.2/rows.go000066400000000000000000000046311365507746300167750ustar00rootroot00000000000000package pq import ( "math" "reflect" "time" "github.com/lib/pq/oid" ) const headerSize = 4 type fieldDesc struct { // The object ID of the data type. OID oid.Oid // The data type size (see pg_type.typlen). // Note that negative values denote variable-width types. Len int // The type modifier (see pg_attribute.atttypmod). // The meaning of the modifier is type-specific. Mod int } func (fd fieldDesc) Type() reflect.Type { switch fd.OID { case oid.T_int8: return reflect.TypeOf(int64(0)) case oid.T_int4: return reflect.TypeOf(int32(0)) case oid.T_int2: return reflect.TypeOf(int16(0)) case oid.T_varchar, oid.T_text: return reflect.TypeOf("") case oid.T_bool: return reflect.TypeOf(false) case oid.T_date, oid.T_time, oid.T_timetz, oid.T_timestamp, oid.T_timestamptz: return reflect.TypeOf(time.Time{}) case oid.T_bytea: return reflect.TypeOf([]byte(nil)) default: return reflect.TypeOf(new(interface{})).Elem() } } func (fd fieldDesc) Name() string { return oid.TypeName[fd.OID] } func (fd fieldDesc) Length() (length int64, ok bool) { switch fd.OID { case oid.T_text, oid.T_bytea: return math.MaxInt64, true case oid.T_varchar, oid.T_bpchar: return int64(fd.Mod - headerSize), true default: return 0, false } } func (fd fieldDesc) PrecisionScale() (precision, scale int64, ok bool) { switch fd.OID { case oid.T_numeric, oid.T__numeric: mod := fd.Mod - headerSize precision = int64((mod >> 16) & 0xffff) scale = int64(mod & 0xffff) return precision, scale, true default: return 0, 0, false } } // ColumnTypeScanType returns the value type that can be used to scan types into. func (rs *rows) ColumnTypeScanType(index int) reflect.Type { return rs.colTyps[index].Type() } // ColumnTypeDatabaseTypeName return the database system type name. func (rs *rows) ColumnTypeDatabaseTypeName(index int) string { return rs.colTyps[index].Name() } // ColumnTypeLength returns the length of the column type if the column is a // variable length type. If the column is not a variable length type ok // should return false. func (rs *rows) ColumnTypeLength(index int) (length int64, ok bool) { return rs.colTyps[index].Length() } // ColumnTypePrecisionScale should return the precision and scale for decimal // types. If not applicable, ok should be false. func (rs *rows) ColumnTypePrecisionScale(index int) (precision, scale int64, ok bool) { return rs.colTyps[index].PrecisionScale() } golang-github-lib-pq-1.5.2/rows_test.go000066400000000000000000000111541365507746300200320ustar00rootroot00000000000000package pq import ( "math" "reflect" "testing" "github.com/lib/pq/oid" ) func TestDataTypeName(t *testing.T) { tts := []struct { typ oid.Oid name string }{ {oid.T_int8, "INT8"}, {oid.T_int4, "INT4"}, {oid.T_int2, "INT2"}, {oid.T_varchar, "VARCHAR"}, {oid.T_text, "TEXT"}, {oid.T_bool, "BOOL"}, {oid.T_numeric, "NUMERIC"}, {oid.T_date, "DATE"}, {oid.T_time, "TIME"}, {oid.T_timetz, "TIMETZ"}, {oid.T_timestamp, "TIMESTAMP"}, {oid.T_timestamptz, "TIMESTAMPTZ"}, {oid.T_bytea, "BYTEA"}, } for i, tt := range tts { dt := fieldDesc{OID: tt.typ} if name := dt.Name(); name != tt.name { t.Errorf("(%d) got: %s want: %s", i, name, tt.name) } } } func TestDataType(t *testing.T) { tts := []struct { typ oid.Oid kind reflect.Kind }{ {oid.T_int8, reflect.Int64}, {oid.T_int4, reflect.Int32}, {oid.T_int2, reflect.Int16}, {oid.T_varchar, reflect.String}, {oid.T_text, reflect.String}, {oid.T_bool, reflect.Bool}, {oid.T_date, reflect.Struct}, {oid.T_time, reflect.Struct}, {oid.T_timetz, reflect.Struct}, {oid.T_timestamp, reflect.Struct}, {oid.T_timestamptz, reflect.Struct}, {oid.T_bytea, reflect.Slice}, } for i, tt := range tts { dt := fieldDesc{OID: tt.typ} if kind := dt.Type().Kind(); kind != tt.kind { t.Errorf("(%d) got: %s want: %s", i, kind, tt.kind) } } } func TestDataTypeLength(t *testing.T) { tts := []struct { typ oid.Oid len int mod int length int64 ok bool }{ {oid.T_int4, 0, -1, 0, false}, {oid.T_varchar, 65535, 9, 5, true}, {oid.T_text, 65535, -1, math.MaxInt64, true}, {oid.T_bytea, 65535, -1, math.MaxInt64, true}, } for i, tt := range tts { dt := fieldDesc{OID: tt.typ, Len: tt.len, Mod: tt.mod} if l, k := dt.Length(); k != tt.ok || l != tt.length { t.Errorf("(%d) got: %d, %t want: %d, %t", i, l, k, tt.length, tt.ok) } } } func TestDataTypePrecisionScale(t *testing.T) { tts := []struct { typ oid.Oid mod int precision, scale int64 ok bool }{ {oid.T_int4, -1, 0, 0, false}, {oid.T_numeric, 589830, 9, 2, true}, {oid.T_text, -1, 0, 0, false}, } for i, tt := range tts { dt := fieldDesc{OID: tt.typ, Mod: tt.mod} p, s, k := dt.PrecisionScale() if k != tt.ok { t.Errorf("(%d) got: %t want: %t", i, k, tt.ok) } if p != tt.precision { t.Errorf("(%d) wrong precision got: %d want: %d", i, p, tt.precision) } if s != tt.scale { t.Errorf("(%d) wrong scale got: %d want: %d", i, s, tt.scale) } } } func TestRowsColumnTypes(t *testing.T) { columnTypesTests := []struct { Name string TypeName string Length struct { Len int64 OK bool } DecimalSize struct { Precision int64 Scale int64 OK bool } ScanType reflect.Type }{ { Name: "a", TypeName: "INT4", Length: struct { Len int64 OK bool }{ Len: 0, OK: false, }, DecimalSize: struct { Precision int64 Scale int64 OK bool }{ Precision: 0, Scale: 0, OK: false, }, ScanType: reflect.TypeOf(int32(0)), }, { Name: "bar", TypeName: "TEXT", Length: struct { Len int64 OK bool }{ Len: math.MaxInt64, OK: true, }, DecimalSize: struct { Precision int64 Scale int64 OK bool }{ Precision: 0, Scale: 0, OK: false, }, ScanType: reflect.TypeOf(""), }, } db := openTestConn(t) defer db.Close() rows, err := db.Query("SELECT 1 AS a, text 'bar' AS bar, 1.28::numeric(9, 2) AS dec") if err != nil { t.Fatal(err) } columns, err := rows.ColumnTypes() if err != nil { t.Fatal(err) } if len(columns) != 3 { t.Errorf("expected 3 columns found %d", len(columns)) } for i, tt := range columnTypesTests { c := columns[i] if c.Name() != tt.Name { t.Errorf("(%d) got: %s, want: %s", i, c.Name(), tt.Name) } if c.DatabaseTypeName() != tt.TypeName { t.Errorf("(%d) got: %s, want: %s", i, c.DatabaseTypeName(), tt.TypeName) } l, ok := c.Length() if l != tt.Length.Len { t.Errorf("(%d) got: %d, want: %d", i, l, tt.Length.Len) } if ok != tt.Length.OK { t.Errorf("(%d) got: %t, want: %t", i, ok, tt.Length.OK) } p, s, ok := c.DecimalSize() if p != tt.DecimalSize.Precision { t.Errorf("(%d) got: %d, want: %d", i, p, tt.DecimalSize.Precision) } if s != tt.DecimalSize.Scale { t.Errorf("(%d) got: %d, want: %d", i, s, tt.DecimalSize.Scale) } if ok != tt.DecimalSize.OK { t.Errorf("(%d) got: %t, want: %t", i, ok, tt.DecimalSize.OK) } if c.ScanType() != tt.ScanType { t.Errorf("(%d) got: %v, want: %v", i, c.ScanType(), tt.ScanType) } } } golang-github-lib-pq-1.5.2/scram/000077500000000000000000000000001365507746300165555ustar00rootroot00000000000000golang-github-lib-pq-1.5.2/scram/scram.go000066400000000000000000000170251365507746300202160ustar00rootroot00000000000000// Copyright (c) 2014 - Gustavo Niemeyer // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this // list of conditions and the following disclaimer. // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR // ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Package scram implements a SCRAM-{SHA-1,etc} client per RFC5802. // // http://tools.ietf.org/html/rfc5802 // package scram import ( "bytes" "crypto/hmac" "crypto/rand" "encoding/base64" "fmt" "hash" "strconv" "strings" ) // Client implements a SCRAM-* client (SCRAM-SHA-1, SCRAM-SHA-256, etc). // // A Client may be used within a SASL conversation with logic resembling: // // var in []byte // var client = scram.NewClient(sha1.New, user, pass) // for client.Step(in) { // out := client.Out() // // send out to server // in := serverOut // } // if client.Err() != nil { // // auth failed // } // type Client struct { newHash func() hash.Hash user string pass string step int out bytes.Buffer err error clientNonce []byte serverNonce []byte saltedPass []byte authMsg bytes.Buffer } // NewClient returns a new SCRAM-* client with the provided hash algorithm. // // For SCRAM-SHA-256, for example, use: // // client := scram.NewClient(sha256.New, user, pass) // func NewClient(newHash func() hash.Hash, user, pass string) *Client { c := &Client{ newHash: newHash, user: user, pass: pass, } c.out.Grow(256) c.authMsg.Grow(256) return c } // Out returns the data to be sent to the server in the current step. func (c *Client) Out() []byte { if c.out.Len() == 0 { return nil } return c.out.Bytes() } // Err returns the error that occurred, or nil if there were no errors. func (c *Client) Err() error { return c.err } // SetNonce sets the client nonce to the provided value. // If not set, the nonce is generated automatically out of crypto/rand on the first step. func (c *Client) SetNonce(nonce []byte) { c.clientNonce = nonce } var escaper = strings.NewReplacer("=", "=3D", ",", "=2C") // Step processes the incoming data from the server and makes the // next round of data for the server available via Client.Out. // Step returns false if there are no errors and more data is // still expected. func (c *Client) Step(in []byte) bool { c.out.Reset() if c.step > 2 || c.err != nil { return false } c.step++ switch c.step { case 1: c.err = c.step1(in) case 2: c.err = c.step2(in) case 3: c.err = c.step3(in) } return c.step > 2 || c.err != nil } func (c *Client) step1(in []byte) error { if len(c.clientNonce) == 0 { const nonceLen = 16 buf := make([]byte, nonceLen+b64.EncodedLen(nonceLen)) if _, err := rand.Read(buf[:nonceLen]); err != nil { return fmt.Errorf("cannot read random SCRAM-SHA-256 nonce from operating system: %v", err) } c.clientNonce = buf[nonceLen:] b64.Encode(c.clientNonce, buf[:nonceLen]) } c.authMsg.WriteString("n=") escaper.WriteString(&c.authMsg, c.user) c.authMsg.WriteString(",r=") c.authMsg.Write(c.clientNonce) c.out.WriteString("n,,") c.out.Write(c.authMsg.Bytes()) return nil } var b64 = base64.StdEncoding func (c *Client) step2(in []byte) error { c.authMsg.WriteByte(',') c.authMsg.Write(in) fields := bytes.Split(in, []byte(",")) if len(fields) != 3 { return fmt.Errorf("expected 3 fields in first SCRAM-SHA-256 server message, got %d: %q", len(fields), in) } if !bytes.HasPrefix(fields[0], []byte("r=")) || len(fields[0]) < 2 { return fmt.Errorf("server sent an invalid SCRAM-SHA-256 nonce: %q", fields[0]) } if !bytes.HasPrefix(fields[1], []byte("s=")) || len(fields[1]) < 6 { return fmt.Errorf("server sent an invalid SCRAM-SHA-256 salt: %q", fields[1]) } if !bytes.HasPrefix(fields[2], []byte("i=")) || len(fields[2]) < 6 { return fmt.Errorf("server sent an invalid SCRAM-SHA-256 iteration count: %q", fields[2]) } c.serverNonce = fields[0][2:] if !bytes.HasPrefix(c.serverNonce, c.clientNonce) { return fmt.Errorf("server SCRAM-SHA-256 nonce is not prefixed by client nonce: got %q, want %q+\"...\"", c.serverNonce, c.clientNonce) } salt := make([]byte, b64.DecodedLen(len(fields[1][2:]))) n, err := b64.Decode(salt, fields[1][2:]) if err != nil { return fmt.Errorf("cannot decode SCRAM-SHA-256 salt sent by server: %q", fields[1]) } salt = salt[:n] iterCount, err := strconv.Atoi(string(fields[2][2:])) if err != nil { return fmt.Errorf("server sent an invalid SCRAM-SHA-256 iteration count: %q", fields[2]) } c.saltPassword(salt, iterCount) c.authMsg.WriteString(",c=biws,r=") c.authMsg.Write(c.serverNonce) c.out.WriteString("c=biws,r=") c.out.Write(c.serverNonce) c.out.WriteString(",p=") c.out.Write(c.clientProof()) return nil } func (c *Client) step3(in []byte) error { var isv, ise bool var fields = bytes.Split(in, []byte(",")) if len(fields) == 1 { isv = bytes.HasPrefix(fields[0], []byte("v=")) ise = bytes.HasPrefix(fields[0], []byte("e=")) } if ise { return fmt.Errorf("SCRAM-SHA-256 authentication error: %s", fields[0][2:]) } else if !isv { return fmt.Errorf("unsupported SCRAM-SHA-256 final message from server: %q", in) } if !bytes.Equal(c.serverSignature(), fields[0][2:]) { return fmt.Errorf("cannot authenticate SCRAM-SHA-256 server signature: %q", fields[0][2:]) } return nil } func (c *Client) saltPassword(salt []byte, iterCount int) { mac := hmac.New(c.newHash, []byte(c.pass)) mac.Write(salt) mac.Write([]byte{0, 0, 0, 1}) ui := mac.Sum(nil) hi := make([]byte, len(ui)) copy(hi, ui) for i := 1; i < iterCount; i++ { mac.Reset() mac.Write(ui) mac.Sum(ui[:0]) for j, b := range ui { hi[j] ^= b } } c.saltedPass = hi } func (c *Client) clientProof() []byte { mac := hmac.New(c.newHash, c.saltedPass) mac.Write([]byte("Client Key")) clientKey := mac.Sum(nil) hash := c.newHash() hash.Write(clientKey) storedKey := hash.Sum(nil) mac = hmac.New(c.newHash, storedKey) mac.Write(c.authMsg.Bytes()) clientProof := mac.Sum(nil) for i, b := range clientKey { clientProof[i] ^= b } clientProof64 := make([]byte, b64.EncodedLen(len(clientProof))) b64.Encode(clientProof64, clientProof) return clientProof64 } func (c *Client) serverSignature() []byte { mac := hmac.New(c.newHash, c.saltedPass) mac.Write([]byte("Server Key")) serverKey := mac.Sum(nil) mac = hmac.New(c.newHash, serverKey) mac.Write(c.authMsg.Bytes()) serverSignature := mac.Sum(nil) encoded := make([]byte, b64.EncodedLen(len(serverSignature))) b64.Encode(encoded, serverSignature) return encoded } golang-github-lib-pq-1.5.2/ssl.go000066400000000000000000000126361365507746300166100ustar00rootroot00000000000000package 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, error), error) { 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, nil default: return nil, fmterrorf(`unsupported sslmode %q; only "require" (default), "verify-full", "verify-ca", and "disable" supported`, mode) } err := sslClientCertificates(&tlsConf, o) if err != nil { return nil, err } err = sslCertificateAuthority(&tlsConf, o) if err != nil { return nil, err } // Accept renegotiation requests initiated by the backend. // // Renegotiation was deprecated then removed from PostgreSQL 9.5, but // the default configuration of older versions has it enabled. Redshift // also initiates renegotiations and cannot be reconfigured. tlsConf.Renegotiation = tls.RenegotiateFreelyAsClient return func(conn net.Conn) (net.Conn, error) { client := tls.Client(conn, &tlsConf) if verifyCaOnly { err := sslVerifyCertificateAuthority(client, &tlsConf) if err != nil { return nil, err } } return client, nil }, nil } // 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) error { // 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 nil } // 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 nil } else if err != nil { return 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 { return err } } cert, err := tls.LoadX509KeyPair(sslcert, sslkey) if err != nil { return err } tlsConf.Certificates = []tls.Certificate{cert} return nil } // sslCertificateAuthority adds the RootCA specified in the "sslrootcert" setting. func sslCertificateAuthority(tlsConf *tls.Config, o values) error { // 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 { return err } if !tlsConf.RootCAs.AppendCertsFromPEM(cert) { return fmterrorf("couldn't parse pem in sslrootcert") } } return nil } // 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) error { err := client.Handshake() if err != nil { return 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) return err } golang-github-lib-pq-1.5.2/ssl_permissions.go000066400000000000000000000006541365507746300212400ustar00rootroot00000000000000// +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 } golang-github-lib-pq-1.5.2/ssl_test.go000066400000000000000000000200361365507746300176400ustar00rootroot00000000000000package pq // This file contains SSL tests import ( _ "crypto/sha256" "crypto/x509" "database/sql" "os" "path/filepath" "testing" ) func maybeSkipSSLTests(t *testing.T) { // Require some special variables for testing certificates if os.Getenv("PQSSLCERTTEST_PATH") == "" { t.Skip("PQSSLCERTTEST_PATH not set, skipping SSL tests") } value := os.Getenv("PQGOSSLTESTS") if value == "" || value == "0" { t.Skip("PQGOSSLTESTS not enabled, skipping SSL tests") } else if value != "1" { t.Fatalf("unexpected value %q for PQGOSSLTESTS", value) } } func openSSLConn(t *testing.T, conninfo string) (*sql.DB, error) { db, err := openTestConnConninfo(conninfo) if err != nil { // should never fail t.Fatal(err) } // Do something with the connection to see whether it's working or not. tx, err := db.Begin() if err == nil { return db, tx.Rollback() } _ = db.Close() return nil, err } func checkSSLSetup(t *testing.T, conninfo string) { _, err := openSSLConn(t, conninfo) if pge, ok := err.(*Error); ok { if pge.Code.Name() != "invalid_authorization_specification" { t.Fatalf("unexpected error code '%s'", pge.Code.Name()) } } else { t.Fatalf("expected %T, got %v", (*Error)(nil), err) } } // Connect over SSL and run a simple query to test the basics func TestSSLConnection(t *testing.T) { maybeSkipSSLTests(t) // Environment sanity check: should fail without SSL checkSSLSetup(t, "sslmode=disable user=pqgossltest") db, err := openSSLConn(t, "sslmode=require user=pqgossltest") if err != nil { t.Fatal(err) } rows, err := db.Query("SELECT 1") if err != nil { t.Fatal(err) } rows.Close() } // Test sslmode=verify-full func TestSSLVerifyFull(t *testing.T) { maybeSkipSSLTests(t) // Environment sanity check: should fail without SSL checkSSLSetup(t, "sslmode=disable user=pqgossltest") // Not OK according to the system CA _, err := openSSLConn(t, "host=postgres sslmode=verify-full user=pqgossltest") if err == nil { t.Fatal("expected error") } _, ok := err.(x509.UnknownAuthorityError) if !ok { t.Fatalf("expected x509.UnknownAuthorityError, got %#+v", err) } rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt") rootCert := "sslrootcert=" + rootCertPath + " " // No match on Common Name _, err = openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=verify-full user=pqgossltest") if err == nil { t.Fatal("expected error") } _, ok = err.(x509.HostnameError) if !ok { t.Fatalf("expected x509.HostnameError, got %#+v", err) } // OK _, err = openSSLConn(t, rootCert+"host=postgres sslmode=verify-full user=pqgossltest") if err != nil { t.Fatal(err) } } // Test sslmode=require sslrootcert=rootCertPath func TestSSLRequireWithRootCert(t *testing.T) { maybeSkipSSLTests(t) // Environment sanity check: should fail without SSL checkSSLSetup(t, "sslmode=disable user=pqgossltest") bogusRootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "bogus_root.crt") bogusRootCert := "sslrootcert=" + bogusRootCertPath + " " // Not OK according to the bogus CA _, err := openSSLConn(t, bogusRootCert+"host=postgres sslmode=require user=pqgossltest") if err == nil { t.Fatal("expected error") } _, ok := err.(x509.UnknownAuthorityError) if !ok { t.Fatalf("expected x509.UnknownAuthorityError, got %s, %#+v", err, err) } nonExistentCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "non_existent.crt") nonExistentCert := "sslrootcert=" + nonExistentCertPath + " " // No match on Common Name, but that's OK because we're not validating anything. _, err = openSSLConn(t, nonExistentCert+"host=127.0.0.1 sslmode=require user=pqgossltest") if err != nil { t.Fatal(err) } rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt") rootCert := "sslrootcert=" + rootCertPath + " " // No match on Common Name, but that's OK because we're not validating the CN. _, err = openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=require user=pqgossltest") if err != nil { t.Fatal(err) } // Everything OK _, err = openSSLConn(t, rootCert+"host=postgres sslmode=require user=pqgossltest") if err != nil { t.Fatal(err) } } // Test sslmode=verify-ca func TestSSLVerifyCA(t *testing.T) { maybeSkipSSLTests(t) // Environment sanity check: should fail without SSL checkSSLSetup(t, "sslmode=disable user=pqgossltest") // Not OK according to the system CA { _, err := openSSLConn(t, "host=postgres sslmode=verify-ca user=pqgossltest") if _, ok := err.(x509.UnknownAuthorityError); !ok { t.Fatalf("expected %T, got %#+v", x509.UnknownAuthorityError{}, err) } } // Still not OK according to the system CA; empty sslrootcert is treated as unspecified. { _, err := openSSLConn(t, "host=postgres sslmode=verify-ca user=pqgossltest sslrootcert=''") if _, ok := err.(x509.UnknownAuthorityError); !ok { t.Fatalf("expected %T, got %#+v", x509.UnknownAuthorityError{}, err) } } rootCertPath := filepath.Join(os.Getenv("PQSSLCERTTEST_PATH"), "root.crt") rootCert := "sslrootcert=" + rootCertPath + " " // No match on Common Name, but that's OK if _, err := openSSLConn(t, rootCert+"host=127.0.0.1 sslmode=verify-ca user=pqgossltest"); err != nil { t.Fatal(err) } // Everything OK if _, err := openSSLConn(t, rootCert+"host=postgres sslmode=verify-ca user=pqgossltest"); err != nil { t.Fatal(err) } } // Authenticate over SSL using client certificates func TestSSLClientCertificates(t *testing.T) { maybeSkipSSLTests(t) // Environment sanity check: should fail without SSL checkSSLSetup(t, "sslmode=disable user=pqgossltest") const baseinfo = "sslmode=require user=pqgosslcert" // Certificate not specified, should fail { _, err := openSSLConn(t, baseinfo) if pge, ok := err.(*Error); ok { if pge.Code.Name() != "invalid_authorization_specification" { t.Fatalf("unexpected error code '%s'", pge.Code.Name()) } } else { t.Fatalf("expected %T, got %v", (*Error)(nil), err) } } // Empty certificate specified, should fail { _, err := openSSLConn(t, baseinfo+" sslcert=''") if pge, ok := err.(*Error); ok { if pge.Code.Name() != "invalid_authorization_specification" { t.Fatalf("unexpected error code '%s'", pge.Code.Name()) } } else { t.Fatalf("expected %T, got %v", (*Error)(nil), err) } } // Non-existent certificate specified, should fail { _, err := openSSLConn(t, baseinfo+" sslcert=/tmp/filedoesnotexist") if pge, ok := err.(*Error); ok { if pge.Code.Name() != "invalid_authorization_specification" { t.Fatalf("unexpected error code '%s'", pge.Code.Name()) } } else { t.Fatalf("expected %T, got %v", (*Error)(nil), err) } } certpath, ok := os.LookupEnv("PQSSLCERTTEST_PATH") if !ok { t.Fatalf("PQSSLCERTTEST_PATH not present in environment") } sslcert := filepath.Join(certpath, "postgresql.crt") // Cert present, key not specified, should fail { _, err := openSSLConn(t, baseinfo+" sslcert="+sslcert) if _, ok := err.(*os.PathError); !ok { t.Fatalf("expected %T, got %#+v", (*os.PathError)(nil), err) } } // Cert present, empty key specified, should fail { _, err := openSSLConn(t, baseinfo+" sslcert="+sslcert+" sslkey=''") if _, ok := err.(*os.PathError); !ok { t.Fatalf("expected %T, got %#+v", (*os.PathError)(nil), err) } } // Cert present, non-existent key, should fail { _, err := openSSLConn(t, baseinfo+" sslcert="+sslcert+" sslkey=/tmp/filedoesnotexist") if _, ok := err.(*os.PathError); !ok { t.Fatalf("expected %T, got %#+v", (*os.PathError)(nil), err) } } // Key has wrong permissions (passing the cert as the key), should fail if _, err := openSSLConn(t, baseinfo+" sslcert="+sslcert+" sslkey="+sslcert); err != ErrSSLKeyHasWorldPermissions { t.Fatalf("expected %s, got %#+v", ErrSSLKeyHasWorldPermissions, err) } sslkey := filepath.Join(certpath, "postgresql.key") // Should work if db, err := openSSLConn(t, baseinfo+" sslcert="+sslcert+" sslkey="+sslkey); err != nil { t.Fatal(err) } else { rows, err := db.Query("SELECT 1") if err != nil { t.Fatal(err) } if err := rows.Close(); err != nil { t.Fatal(err) } if err := db.Close(); err != nil { t.Fatal(err) } } } golang-github-lib-pq-1.5.2/ssl_windows.go000066400000000000000000000004131365507746300203500ustar00rootroot00000000000000// +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 } golang-github-lib-pq-1.5.2/url.go000066400000000000000000000031751365507746300166070ustar00rootroot00000000000000package pq import ( "fmt" "net" nurl "net/url" "sort" "strings" ) // ParseURL no longer needs to be used by clients of this library since supplying a URL as a // connection string to sql.Open() is now supported: // // sql.Open("postgres", "postgres://bob:secret@1.2.3.4:5432/mydb?sslmode=verify-full") // // It remains exported here for backwards-compatibility. // // ParseURL converts a url to a connection string for driver.Open. // Example: // // "postgres://bob:secret@1.2.3.4:5432/mydb?sslmode=verify-full" // // converts to: // // "user=bob password=secret host=1.2.3.4 port=5432 dbname=mydb sslmode=verify-full" // // A minimal example: // // "postgres://" // // This will be blank, causing driver.Open to use all of the defaults func ParseURL(url string) (string, error) { u, err := nurl.Parse(url) if err != nil { return "", err } if u.Scheme != "postgres" && u.Scheme != "postgresql" { return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme) } var kvs []string escaper := strings.NewReplacer(` `, `\ `, `'`, `\'`, `\`, `\\`) accrue := func(k, v string) { if v != "" { kvs = append(kvs, k+"="+escaper.Replace(v)) } } if u.User != nil { v := u.User.Username() accrue("user", v) v, _ = u.User.Password() accrue("password", v) } if host, port, err := net.SplitHostPort(u.Host); err != nil { accrue("host", u.Host) } else { accrue("host", host) accrue("port", port) } if u.Path != "" { accrue("dbname", u.Path[1:]) } q := u.Query() for k := range q { accrue(k, q.Get(k)) } sort.Strings(kvs) // Makes testing easier (not a performance concern) return strings.Join(kvs, " "), nil } golang-github-lib-pq-1.5.2/url_test.go000066400000000000000000000026661365507746300176520ustar00rootroot00000000000000package pq import ( "testing" ) func TestSimpleParseURL(t *testing.T) { expected := "host=hostname.remote" str, err := ParseURL("postgres://hostname.remote") if err != nil { t.Fatal(err) } if str != expected { t.Fatalf("unexpected result from ParseURL:\n+ %v\n- %v", str, expected) } } func TestIPv6LoopbackParseURL(t *testing.T) { expected := "host=::1 port=1234" str, err := ParseURL("postgres://[::1]:1234") if err != nil { t.Fatal(err) } if str != expected { t.Fatalf("unexpected result from ParseURL:\n+ %v\n- %v", str, expected) } } func TestFullParseURL(t *testing.T) { expected := `dbname=database host=hostname.remote password=top\ secret port=1234 user=username` str, err := ParseURL("postgres://username:top%20secret@hostname.remote:1234/database") if err != nil { t.Fatal(err) } if str != expected { t.Fatalf("unexpected result from ParseURL:\n+ %s\n- %s", str, expected) } } func TestInvalidProtocolParseURL(t *testing.T) { _, err := ParseURL("http://hostname.remote") switch err { case nil: t.Fatal("Expected an error from parsing invalid protocol") default: msg := "invalid connection protocol: http" if err.Error() != msg { t.Fatalf("Unexpected error message:\n+ %s\n- %s", err.Error(), msg) } } } func TestMinimalURL(t *testing.T) { cs, err := ParseURL("postgres://") if err != nil { t.Fatal(err) } if cs != "" { t.Fatalf("expected blank connection string, got: %q", cs) } } golang-github-lib-pq-1.5.2/user_posix.go000066400000000000000000000006431365507746300202020ustar00rootroot00000000000000// Package pq is a pure Go Postgres driver for the database/sql package. // +build aix darwin dragonfly freebsd linux nacl netbsd openbsd plan9 solaris rumprun package pq import ( "os" "os/user" ) func userCurrent() (string, error) { u, err := user.Current() if err == nil { return u.Username, nil } name := os.Getenv("USER") if name != "" { return name, nil } return "", ErrCouldNotDetectUsername } golang-github-lib-pq-1.5.2/user_windows.go000066400000000000000000000015471365507746300205360ustar00rootroot00000000000000// Package pq is a pure Go Postgres driver for the database/sql package. package pq import ( "path/filepath" "syscall" ) // Perform Windows user name lookup identically to libpq. // // The PostgreSQL code makes use of the legacy Win32 function // GetUserName, and that function has not been imported into stock Go. // GetUserNameEx is available though, the difference being that a // wider range of names are available. To get the output to be the // same as GetUserName, only the base (or last) component of the // result is returned. func userCurrent() (string, error) { pw_name := make([]uint16, 128) pwname_size := uint32(len(pw_name)) - 1 err := syscall.GetUserNameEx(syscall.NameSamCompatible, &pw_name[0], &pwname_size) if err != nil { return "", ErrCouldNotDetectUsername } s := syscall.UTF16ToString(pw_name) u := filepath.Base(s) return u, nil } golang-github-lib-pq-1.5.2/uuid.go000066400000000000000000000010531365507746300167440ustar00rootroot00000000000000package 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 } golang-github-lib-pq-1.5.2/uuid_test.go000066400000000000000000000020761365507746300200110ustar00rootroot00000000000000package pq import ( "reflect" "strings" "testing" ) func TestDecodeUUIDBinaryError(t *testing.T) { t.Parallel() _, err := decodeUUIDBinary([]byte{0x12, 0x34}) if err == nil { t.Fatal("Expected error, got none") } if !strings.HasPrefix(err.Error(), "pq:") { t.Errorf("Expected error to start with %q, got %q", "pq:", err.Error()) } if !strings.Contains(err.Error(), "bad length: 2") { t.Errorf("Expected error to contain length, got %q", err.Error()) } } func BenchmarkDecodeUUIDBinary(b *testing.B) { x := []byte{0x03, 0xa3, 0x52, 0x2f, 0x89, 0x28, 0x49, 0x87, 0x84, 0xd6, 0x93, 0x7b, 0x36, 0xec, 0x27, 0x6f} for i := 0; i < b.N; i++ { decodeUUIDBinary(x) } } func TestDecodeUUIDBackend(t *testing.T) { db := openTestConn(t) defer db.Close() var s = "a0ecc91d-a13f-4fe4-9fce-7e09777cc70a" var scanned interface{} err := db.QueryRow(`SELECT $1::uuid`, s).Scan(&scanned) if err != nil { t.Fatalf("Expected no error, got %v", err) } if !reflect.DeepEqual(scanned, []byte(s)) { t.Errorf("Expected []byte(%q), got %T(%q)", s, scanned, scanned) } }