parent
80a3745fc8
commit
6a4de37f7e
2
go.mod
2
go.mod
|
@ -61,7 +61,7 @@ require (
|
||||||
github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4
|
github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4
|
||||||
github.com/klauspost/compress v1.10.2
|
github.com/klauspost/compress v1.10.2
|
||||||
github.com/lafriks/xormstore v1.3.2
|
github.com/lafriks/xormstore v1.3.2
|
||||||
github.com/lib/pq v1.2.0
|
github.com/lib/pq v1.7.0
|
||||||
github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
|
github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
|
||||||
github.com/mailru/easyjson v0.7.0 // indirect
|
github.com/mailru/easyjson v0.7.0 // indirect
|
||||||
github.com/markbates/goth v1.61.2
|
github.com/markbates/goth v1.61.2
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -437,6 +437,8 @@ github.com/lestrrat-go/jwx v0.9.0/go.mod h1:iEoxlYfZjvoGpuWwxUz+eR5e6KTJGsaRcy/Y
|
||||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
|
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
|
||||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/lib/pq v1.7.0 h1:h93mCPfUSkaul3Ka/VG8uZdmW1uMHDGxzu0NWHuJmHY=
|
||||||
|
github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||||
github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 h1:uNwtsDp7ci48vBTTxDuwcoTXz4lwtDTe7TjCQ0noaWY=
|
github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 h1:uNwtsDp7ci48vBTTxDuwcoTXz4lwtDTe7TjCQ0noaWY=
|
||||||
github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96/go.mod h1:mmIfjCSQlGYXmJ95jFN84AkQFnVABtKuJL8IrzwvUKQ=
|
github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96/go.mod h1:mmIfjCSQlGYXmJ95jFN84AkQFnVABtKuJL8IrzwvUKQ=
|
||||||
github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de h1:nyxwRdWHAVxpFcDThedEgQ07DbcRc5xgNObtbTp76fk=
|
github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de h1:nyxwRdWHAVxpFcDThedEgQ07DbcRc5xgNObtbTp76fk=
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
language: go
|
language: go
|
||||||
|
|
||||||
go:
|
go:
|
||||||
- 1.11.x
|
- 1.13.x
|
||||||
- 1.12.x
|
- 1.14.x
|
||||||
- master
|
- master
|
||||||
|
|
||||||
sudo: true
|
sudo: true
|
||||||
|
@ -27,7 +27,7 @@ before_install:
|
||||||
- ./.travis.sh client_configure
|
- ./.travis.sh client_configure
|
||||||
- go get golang.org/x/tools/cmd/goimports
|
- go get golang.org/x/tools/cmd/goimports
|
||||||
- go get golang.org/x/lint/golint
|
- go get golang.org/x/lint/golint
|
||||||
- GO111MODULE=on go get honnef.co/go/tools/cmd/staticcheck@2019.2.1
|
- GO111MODULE=on go get honnef.co/go/tools/cmd/staticcheck@2020.1.3
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- createdb pqgotest
|
- createdb pqgotest
|
||||||
|
@ -38,7 +38,7 @@ script:
|
||||||
- >
|
- >
|
||||||
goimports -d -e $(find -name '*.go') | awk '{ print } END { exit NR == 0 ? 0 : 1 }'
|
goimports -d -e $(find -name '*.go') | awk '{ print } END { exit NR == 0 ? 0 : 1 }'
|
||||||
- go vet ./...
|
- go vet ./...
|
||||||
- staticcheck -go 1.11 ./...
|
- staticcheck -go 1.13 ./...
|
||||||
- golint ./...
|
- golint ./...
|
||||||
- PQTEST_BINARY_PARAMETERS=no go test -race -v ./...
|
- PQTEST_BINARY_PARAMETERS=no go test -race -v ./...
|
||||||
- PQTEST_BINARY_PARAMETERS=yes go test -race -v ./...
|
- PQTEST_BINARY_PARAMETERS=yes go test -race -v ./...
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
## Contributing to pq
|
|
||||||
|
|
||||||
`pq` has a backlog of pull requests, but contributions are still very
|
|
||||||
much welcome. You can help with patch review, submitting bug reports,
|
|
||||||
or adding new functionality. There is no formal style guide, but
|
|
||||||
please conform to the style of existing code and general Go formatting
|
|
||||||
conventions when submitting patches.
|
|
||||||
|
|
||||||
### Patch review
|
|
||||||
|
|
||||||
Help review existing open pull requests by commenting on the code or
|
|
||||||
proposed functionality.
|
|
||||||
|
|
||||||
### Bug reports
|
|
||||||
|
|
||||||
We appreciate any bug reports, but especially ones with self-contained
|
|
||||||
(doesn't depend on code outside of pq), minimal (can't be simplified
|
|
||||||
further) test cases. It's especially helpful if you can submit a pull
|
|
||||||
request with just the failing test case (you'll probably want to
|
|
||||||
pattern it after the tests in
|
|
||||||
[conn_test.go](https://github.com/lib/pq/blob/master/conn_test.go).
|
|
||||||
|
|
||||||
### New functionality
|
|
||||||
|
|
||||||
There are a number of pending patches for new functionality, so
|
|
||||||
additional feature patches will take a while to merge. Still, patches
|
|
||||||
are generally reviewed based on usefulness and complexity in addition
|
|
||||||
to time-in-queue, so if you have a knockout idea, take a shot. Feel
|
|
||||||
free to open an issue discussion your proposed patch beforehand.
|
|
|
@ -1,21 +1,11 @@
|
||||||
# pq - A pure Go postgres driver for Go's database/sql package
|
# pq - A pure Go postgres driver for Go's database/sql package
|
||||||
|
|
||||||
[![GoDoc](https://godoc.org/github.com/lib/pq?status.svg)](https://godoc.org/github.com/lib/pq)
|
[![GoDoc](https://godoc.org/github.com/lib/pq?status.svg)](https://pkg.go.dev/github.com/lib/pq?tab=doc)
|
||||||
[![Build Status](https://travis-ci.org/lib/pq.svg?branch=master)](https://travis-ci.org/lib/pq)
|
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
go get github.com/lib/pq
|
go get github.com/lib/pq
|
||||||
|
|
||||||
## Docs
|
|
||||||
|
|
||||||
For detailed documentation and basic usage examples, please see the package
|
|
||||||
documentation at <https://godoc.org/github.com/lib/pq>.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
`go test` is used for testing. See [TESTS.md](TESTS.md) for more details.
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
* SSL
|
* SSL
|
||||||
|
@ -30,66 +20,14 @@ documentation at <https://godoc.org/github.com/lib/pq>.
|
||||||
* Notifications: `LISTEN`/`NOTIFY`
|
* Notifications: `LISTEN`/`NOTIFY`
|
||||||
* pgpass support
|
* pgpass support
|
||||||
|
|
||||||
## Future / Things you can help with
|
## Optional Features
|
||||||
|
|
||||||
* Better COPY FROM / COPY TO (see discussion in #181)
|
* GSS (Kerberos) auth (to use, see GoDoc)
|
||||||
|
|
||||||
## Thank you (alphabetical)
|
## Tests
|
||||||
|
|
||||||
Some of these contributors are from the original library `bmizerany/pq.go` whose
|
`go test` is used for testing. See [TESTS.md](TESTS.md) for more details.
|
||||||
code still exists in here.
|
|
||||||
|
|
||||||
* Andy Balholm (andybalholm)
|
## Status
|
||||||
* Ben Berkert (benburkert)
|
|
||||||
* Benjamin Heatwole (bheatwole)
|
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.
|
||||||
* Bill Mill (llimllib)
|
|
||||||
* Bjørn Madsen (aeons)
|
|
||||||
* Blake Gentry (bgentry)
|
|
||||||
* Brad Fitzpatrick (bradfitz)
|
|
||||||
* Charlie Melbye (cmelbye)
|
|
||||||
* Chris Bandy (cbandy)
|
|
||||||
* Chris Gilling (cgilling)
|
|
||||||
* Chris Walsh (cwds)
|
|
||||||
* Dan Sosedoff (sosedoff)
|
|
||||||
* Daniel Farina (fdr)
|
|
||||||
* Eric Chlebek (echlebek)
|
|
||||||
* Eric Garrido (minusnine)
|
|
||||||
* Eric Urban (hydrogen18)
|
|
||||||
* Everyone at The Go Team
|
|
||||||
* Evan Shaw (edsrzf)
|
|
||||||
* Ewan Chou (coocood)
|
|
||||||
* Fazal Majid (fazalmajid)
|
|
||||||
* Federico Romero (federomero)
|
|
||||||
* Fumin (fumin)
|
|
||||||
* Gary Burd (garyburd)
|
|
||||||
* Heroku (heroku)
|
|
||||||
* James Pozdena (jpoz)
|
|
||||||
* Jason McVetta (jmcvetta)
|
|
||||||
* Jeremy Jay (pbnjay)
|
|
||||||
* Joakim Sernbrant (serbaut)
|
|
||||||
* John Gallagher (jgallagher)
|
|
||||||
* Jonathan Rudenberg (titanous)
|
|
||||||
* Joël Stemmer (jstemmer)
|
|
||||||
* Kamil Kisiel (kisielk)
|
|
||||||
* Kelly Dunn (kellydunn)
|
|
||||||
* Keith Rarick (kr)
|
|
||||||
* Kir Shatrov (kirs)
|
|
||||||
* Lann Martin (lann)
|
|
||||||
* Maciek Sakrejda (uhoh-itsmaciek)
|
|
||||||
* Marc Brinkmann (mbr)
|
|
||||||
* Marko Tiikkaja (johto)
|
|
||||||
* Matt Newberry (MattNewberry)
|
|
||||||
* Matt Robenolt (mattrobenolt)
|
|
||||||
* Martin Olsen (martinolsen)
|
|
||||||
* Mike Lewis (mikelikespie)
|
|
||||||
* Nicolas Patry (Narsil)
|
|
||||||
* Oliver Tonnhofer (olt)
|
|
||||||
* Patrick Hayes (phayes)
|
|
||||||
* Paul Hammond (paulhammond)
|
|
||||||
* Ryan Smith (ryandotsmith)
|
|
||||||
* Samuel Stauffer (samuel)
|
|
||||||
* Timothée Peignier (cyberdelia)
|
|
||||||
* Travis Cline (tmc)
|
|
||||||
* TruongSinh Tran-Nguyen (truongsinh)
|
|
||||||
* Yaismel Miranda (ympons)
|
|
||||||
* notedit (notedit)
|
|
||||||
|
|
|
@ -149,6 +149,15 @@ type conn struct {
|
||||||
|
|
||||||
// If true this connection is in the middle of a COPY
|
// If true this connection is in the middle of a COPY
|
||||||
inCopy bool
|
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)
|
||||||
|
|
||||||
|
// GSSAPI context
|
||||||
|
gss GSS
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle driver-side settings in parsed connection string.
|
// Handle driver-side settings in parsed connection string.
|
||||||
|
@ -329,10 +338,6 @@ func (c *Connector) open(ctx context.Context) (cn *conn, err error) {
|
||||||
|
|
||||||
func dial(ctx context.Context, d Dialer, o values) (net.Conn, error) {
|
func dial(ctx context.Context, d Dialer, o values) (net.Conn, error) {
|
||||||
network, address := network(o)
|
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.
|
// Zero or not specified means wait indefinitely.
|
||||||
if timeout, ok := o["connect_timeout"]; ok && timeout != "0" {
|
if timeout, ok := o["connect_timeout"]; ok && timeout != "0" {
|
||||||
|
@ -971,7 +976,13 @@ func (cn *conn) recv() (t byte, r *readBuf) {
|
||||||
case 'E':
|
case 'E':
|
||||||
panic(parseError(r))
|
panic(parseError(r))
|
||||||
case 'N':
|
case 'N':
|
||||||
// ignore
|
if n := cn.noticeHandler; n != nil {
|
||||||
|
n(parseError(r))
|
||||||
|
}
|
||||||
|
case 'A':
|
||||||
|
if n := cn.notificationHandler; n != nil {
|
||||||
|
n(recvNotification(r))
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -988,8 +999,14 @@ func (cn *conn) recv1Buf(r *readBuf) byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch t {
|
switch t {
|
||||||
case 'A', 'N':
|
case 'A':
|
||||||
// ignore
|
if n := cn.notificationHandler; n != nil {
|
||||||
|
n(recvNotification(r))
|
||||||
|
}
|
||||||
|
case 'N':
|
||||||
|
if n := cn.noticeHandler; n != nil {
|
||||||
|
n(parseError(r))
|
||||||
|
}
|
||||||
case 'S':
|
case 'S':
|
||||||
cn.processParameterStatus(r)
|
cn.processParameterStatus(r)
|
||||||
default:
|
default:
|
||||||
|
@ -1057,7 +1074,10 @@ func isDriverSetting(key string) bool {
|
||||||
return true
|
return true
|
||||||
case "binary_parameters":
|
case "binary_parameters":
|
||||||
return true
|
return true
|
||||||
|
case "service":
|
||||||
|
return true
|
||||||
|
case "spn":
|
||||||
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -1137,6 +1157,59 @@ func (cn *conn) auth(r *readBuf, o values) {
|
||||||
if r.int32() != 0 {
|
if r.int32() != 0 {
|
||||||
errorf("unexpected authentication response: %q", t)
|
errorf("unexpected authentication response: %q", t)
|
||||||
}
|
}
|
||||||
|
case 7: // GSSAPI, startup
|
||||||
|
if newGss == nil {
|
||||||
|
errorf("kerberos error: no GSSAPI provider registered (import github.com/lib/pq/auth/kerberos if you need Kerberos support)")
|
||||||
|
}
|
||||||
|
cli, err := newGss()
|
||||||
|
if err != nil {
|
||||||
|
errorf("kerberos error: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
var token []byte
|
||||||
|
|
||||||
|
if spn, ok := o["spn"]; ok {
|
||||||
|
// Use the supplied SPN if provided..
|
||||||
|
token, err = cli.GetInitTokenFromSpn(spn)
|
||||||
|
} else {
|
||||||
|
// Allow the kerberos service name to be overridden
|
||||||
|
service := "postgres"
|
||||||
|
if val, ok := o["service"]; ok {
|
||||||
|
service = val
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err = cli.GetInitToken(o["host"], service)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
errorf("failed to get Kerberos ticket: %q", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
w := cn.writeBuf('p')
|
||||||
|
w.bytes(token)
|
||||||
|
cn.send(w)
|
||||||
|
|
||||||
|
// Store for GSSAPI continue message
|
||||||
|
cn.gss = cli
|
||||||
|
|
||||||
|
case 8: // GSSAPI continue
|
||||||
|
|
||||||
|
if cn.gss == nil {
|
||||||
|
errorf("GSSAPI protocol error")
|
||||||
|
}
|
||||||
|
|
||||||
|
b := []byte(*r)
|
||||||
|
|
||||||
|
done, tokOut, err := cn.gss.Continue(b)
|
||||||
|
if err == nil && !done {
|
||||||
|
w := cn.writeBuf('p')
|
||||||
|
w.bytes(tokOut)
|
||||||
|
cn.send(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errors fall through and read the more detailed message
|
||||||
|
// from the server..
|
||||||
|
|
||||||
case 10:
|
case 10:
|
||||||
sc := scram.NewClient(sha256.New, o["user"], o["password"])
|
sc := scram.NewClient(sha256.New, o["user"], o["password"])
|
||||||
sc.Step(nil)
|
sc.Step(nil)
|
||||||
|
|
|
@ -79,7 +79,7 @@ func (cn *conn) Ping(ctx context.Context) error {
|
||||||
if finish := cn.watchCancel(ctx); finish != nil {
|
if finish := cn.watchCancel(ctx); finish != nil {
|
||||||
defer finish()
|
defer finish()
|
||||||
}
|
}
|
||||||
rows, err := cn.simpleQuery("SELECT 'lib/pq ping test';")
|
rows, err := cn.simpleQuery(";")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return driver.ErrBadConn // https://golang.org/pkg/database/sql/driver/#Pinger
|
return driver.ErrBadConn // https://golang.org/pkg/database/sql/driver/#Pinger
|
||||||
}
|
}
|
||||||
|
|
|
@ -106,5 +106,10 @@ func NewConnector(dsn string) (*Connector, error) {
|
||||||
o["user"] = u
|
o["user"] = u
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SSL is not necessary or supported over UNIX domain sockets
|
||||||
|
if network, _ := network(o); network == "unix" {
|
||||||
|
o["sslmode"] = "disable"
|
||||||
|
}
|
||||||
|
|
||||||
return &Connector{opts: o, dialer: defaultDialer{}}, nil
|
return &Connector{opts: o, dialer: defaultDialer{}}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -152,7 +152,9 @@ func (ci *copyin) resploop() {
|
||||||
case 'C':
|
case 'C':
|
||||||
// complete
|
// complete
|
||||||
case 'N':
|
case 'N':
|
||||||
// NoticeResponse
|
if n := ci.cn.noticeHandler; n != nil {
|
||||||
|
n(parseError(&r))
|
||||||
|
}
|
||||||
case 'Z':
|
case 'Z':
|
||||||
ci.cn.processReadyForQuery(&r)
|
ci.cn.processReadyForQuery(&r)
|
||||||
ci.done <- true
|
ci.done <- true
|
||||||
|
@ -229,7 +231,7 @@ func (ci *copyin) Exec(v []driver.Value) (r driver.Result, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(v) == 0 {
|
if len(v) == 0 {
|
||||||
return nil, ci.Close()
|
return driver.RowsAffected(0), ci.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
numValues := len(v)
|
numValues := len(v)
|
||||||
|
|
|
@ -57,6 +57,8 @@ supported:
|
||||||
* sslkey - Key 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
|
* sslrootcert - The location of the root certificate file. The file
|
||||||
must contain PEM encoded data.
|
must contain PEM encoded data.
|
||||||
|
* spn - Configures GSS (Kerberos) SPN.
|
||||||
|
* service - GSS (Kerberos) service name to use when constructing the SPN (default is `postgres`).
|
||||||
|
|
||||||
Valid values for sslmode are:
|
Valid values for sslmode are:
|
||||||
|
|
||||||
|
@ -241,5 +243,21 @@ bytes by the PostgreSQL server.
|
||||||
You can find a complete, working example of Listener usage at
|
You can find a complete, working example of Listener usage at
|
||||||
https://godoc.org/github.com/lib/pq/example/listen.
|
https://godoc.org/github.com/lib/pq/example/listen.
|
||||||
|
|
||||||
|
|
||||||
|
Kerberos Support
|
||||||
|
|
||||||
|
|
||||||
|
If you need support for Kerberos authentication, add the following to your main
|
||||||
|
package:
|
||||||
|
|
||||||
|
import "github.com/lib/pq/auth/kerberos"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
pq.RegisterGSSProvider(func() (pq.Gss, error) { return kerberos.NewGSS() })
|
||||||
|
}
|
||||||
|
|
||||||
|
This package is in a separate module so that users who don't need Kerberos
|
||||||
|
don't have to download unnecessary dependencies.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
package pq
|
package pq
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -16,6 +17,8 @@ import (
|
||||||
"github.com/lib/pq/oid"
|
"github.com/lib/pq/oid"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var time2400Regex = regexp.MustCompile(`^(24:00(?::00(?:\.0+)?)?)(?:[Z+-].*)?$`)
|
||||||
|
|
||||||
func binaryEncode(parameterStatus *parameterStatus, x interface{}) []byte {
|
func binaryEncode(parameterStatus *parameterStatus, x interface{}) []byte {
|
||||||
switch v := x.(type) {
|
switch v := x.(type) {
|
||||||
case []byte:
|
case []byte:
|
||||||
|
@ -202,10 +205,27 @@ func mustParse(f string, typ oid.Oid, s []byte) time.Time {
|
||||||
str[len(str)-3] == ':' {
|
str[len(str)-3] == ':' {
|
||||||
f += ":00"
|
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)
|
t, err := time.Parse(f, str)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorf("decode: %s", err)
|
errorf("decode: %s", err)
|
||||||
}
|
}
|
||||||
|
if is2400Time {
|
||||||
|
t = t.Add(24 * time.Hour)
|
||||||
|
}
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -487,7 +507,7 @@ func FormatTimestamp(t time.Time) []byte {
|
||||||
b := []byte(t.Format("2006-01-02 15:04:05.999999999Z07:00"))
|
b := []byte(t.Format("2006-01-02 15:04:05.999999999Z07:00"))
|
||||||
|
|
||||||
_, offset := t.Zone()
|
_, offset := t.Zone()
|
||||||
offset = offset % 60
|
offset %= 60
|
||||||
if offset != 0 {
|
if offset != 0 {
|
||||||
// RFC3339Nano already printed the minus sign
|
// RFC3339Nano already printed the minus sign
|
||||||
if offset < 0 {
|
if offset < 0 {
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
module github.com/lib/pq
|
module github.com/lib/pq
|
||||||
|
|
||||||
|
go 1.13
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
package pq
|
||||||
|
|
||||||
|
// NewGSSFunc creates a GSS authentication provider, for use with
|
||||||
|
// RegisterGSSProvider.
|
||||||
|
type NewGSSFunc func() (GSS, error)
|
||||||
|
|
||||||
|
var newGss NewGSSFunc
|
||||||
|
|
||||||
|
// RegisterGSSProvider registers a GSS authentication provider. For example, if
|
||||||
|
// you need to use Kerberos to authenticate with your server, add this to your
|
||||||
|
// main package:
|
||||||
|
//
|
||||||
|
// import "github.com/lib/pq/auth/kerberos"
|
||||||
|
//
|
||||||
|
// func init() {
|
||||||
|
// pq.RegisterGSSProvider(func() (pq.GSS, error) { return kerberos.NewGSS() })
|
||||||
|
// }
|
||||||
|
func RegisterGSSProvider(newGssArg NewGSSFunc) {
|
||||||
|
newGss = newGssArg
|
||||||
|
}
|
||||||
|
|
||||||
|
// GSS provides GSSAPI authentication (e.g., Kerberos).
|
||||||
|
type GSS interface {
|
||||||
|
GetInitToken(host string, service string) ([]byte, error)
|
||||||
|
GetInitTokenFromSpn(spn string) ([]byte, error)
|
||||||
|
Continue(inToken []byte) (done bool, outToken []byte, err error)
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
// +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}
|
||||||
|
}
|
|
@ -4,6 +4,8 @@ package pq
|
||||||
// This module contains support for Postgres LISTEN/NOTIFY.
|
// This module contains support for Postgres LISTEN/NOTIFY.
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql/driver"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -29,6 +31,61 @@ func recvNotification(r *readBuf) *Notification {
|
||||||
return &Notification{bePid, channel, extra}
|
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 (
|
const (
|
||||||
connStateIdle int32 = iota
|
connStateIdle int32 = iota
|
||||||
connStateExpectResponse
|
connStateExpectResponse
|
||||||
|
@ -174,8 +231,12 @@ func (l *ListenerConn) listenerConnLoop() (err error) {
|
||||||
}
|
}
|
||||||
l.replyChan <- message{t, nil}
|
l.replyChan <- message{t, nil}
|
||||||
|
|
||||||
case 'N', 'S':
|
case 'S':
|
||||||
// ignore
|
// ignore
|
||||||
|
case 'N':
|
||||||
|
if n := l.cn.noticeHandler; n != nil {
|
||||||
|
n(parseError(r))
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unexpected message %q from server in listenerConnLoop", t)
|
return fmt.Errorf("unexpected message %q from server in listenerConnLoop", t)
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,7 +94,7 @@ func (c *Client) Out() []byte {
|
||||||
return c.out.Bytes()
|
return c.out.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Err returns the error that ocurred, or nil if there were no errors.
|
// Err returns the error that occurred, or nil if there were no errors.
|
||||||
func (c *Client) Err() error {
|
func (c *Client) Err() error {
|
||||||
return c.err
|
return c.err
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Package pq is a pure Go Postgres driver for the database/sql package.
|
// Package pq is a pure Go Postgres driver for the database/sql package.
|
||||||
|
|
||||||
// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris rumprun
|
// +build aix darwin dragonfly freebsd linux nacl netbsd openbsd plan9 solaris rumprun
|
||||||
|
|
||||||
package pq
|
package pq
|
||||||
|
|
||||||
|
|
|
@ -451,7 +451,7 @@ github.com/kr/text
|
||||||
## explicit
|
## explicit
|
||||||
github.com/lafriks/xormstore
|
github.com/lafriks/xormstore
|
||||||
github.com/lafriks/xormstore/util
|
github.com/lafriks/xormstore/util
|
||||||
# github.com/lib/pq v1.2.0
|
# github.com/lib/pq v1.7.0
|
||||||
## explicit
|
## explicit
|
||||||
github.com/lib/pq
|
github.com/lib/pq
|
||||||
github.com/lib/pq/oid
|
github.com/lib/pq/oid
|
||||||
|
|
Loading…
Reference in New Issue