fix(go.sum): update ResolveSpec dependency to v1.0.87
This commit is contained in:
+600
@@ -0,0 +1,600 @@
|
||||
package pgdriver
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
sql.Register("pg", NewDriver())
|
||||
}
|
||||
|
||||
type logging interface {
|
||||
Printf(ctx context.Context, format string, v ...interface{})
|
||||
}
|
||||
|
||||
type logger struct {
|
||||
log *log.Logger
|
||||
}
|
||||
|
||||
func (l *logger) Printf(ctx context.Context, format string, v ...interface{}) {
|
||||
_ = l.log.Output(2, fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
var Logger logging = &logger{
|
||||
log: log.New(os.Stderr, "pgdriver: ", log.LstdFlags|log.Lshortfile),
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type Driver struct {
|
||||
connector *Connector
|
||||
}
|
||||
|
||||
var _ driver.DriverContext = (*Driver)(nil)
|
||||
|
||||
func NewDriver() Driver {
|
||||
return Driver{}
|
||||
}
|
||||
|
||||
func (d Driver) OpenConnector(name string) (driver.Connector, error) {
|
||||
opts, err := parseDSN(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewConnector(opts...), nil
|
||||
}
|
||||
|
||||
func (d Driver) Open(name string) (driver.Conn, error) {
|
||||
connector, err := d.OpenConnector(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return connector.Connect(context.TODO())
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type Connector struct {
|
||||
cfg *Config
|
||||
}
|
||||
|
||||
func NewConnector(opts ...Option) *Connector {
|
||||
c := &Connector{cfg: newDefaultConfig()}
|
||||
for _, opt := range opts {
|
||||
opt(c.cfg)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
var _ driver.Connector = (*Connector)(nil)
|
||||
|
||||
func (c *Connector) Connect(ctx context.Context) (driver.Conn, error) {
|
||||
if err := c.cfg.verify(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newConn(ctx, c.cfg)
|
||||
}
|
||||
|
||||
func (c *Connector) Driver() driver.Driver {
|
||||
return Driver{connector: c}
|
||||
}
|
||||
|
||||
func (c *Connector) Config() *Config {
|
||||
return c.cfg
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type Conn struct {
|
||||
cfg *Config
|
||||
|
||||
netConn net.Conn
|
||||
rd *reader
|
||||
|
||||
processID int32
|
||||
secretKey int32
|
||||
|
||||
stmtCount int
|
||||
|
||||
closed int32
|
||||
}
|
||||
|
||||
func newConn(ctx context.Context, cfg *Config) (*Conn, error) {
|
||||
netConn, err := cfg.Dialer(ctx, cfg.Network, cfg.Addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cn := &Conn{
|
||||
cfg: cfg,
|
||||
netConn: netConn,
|
||||
rd: newReader(netConn),
|
||||
}
|
||||
|
||||
if cfg.TLSConfig != nil {
|
||||
if err := enableSSL(ctx, cn, cfg.TLSConfig); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := startup(ctx, cn); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for k, v := range cfg.ConnParams {
|
||||
if v != nil {
|
||||
_, err = cn.ExecContext(ctx, fmt.Sprintf("SET %s TO $1", k), []driver.NamedValue{
|
||||
{Value: v},
|
||||
})
|
||||
} else {
|
||||
_, err = cn.ExecContext(ctx, fmt.Sprintf("SET %s TO DEFAULT", k), nil)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return cn, nil
|
||||
}
|
||||
|
||||
func (cn *Conn) reader(ctx context.Context, timeout time.Duration) *reader {
|
||||
cn.setReadDeadline(ctx, timeout)
|
||||
return cn.rd
|
||||
}
|
||||
|
||||
func (cn *Conn) write(ctx context.Context, wb *writeBuffer) error {
|
||||
cn.setWriteDeadline(ctx, -1)
|
||||
|
||||
n, err := cn.netConn.Write(wb.Bytes)
|
||||
wb.Reset()
|
||||
|
||||
if err != nil {
|
||||
if n == 0 {
|
||||
Logger.Printf(ctx, "pgdriver: Conn.Write failed (zero-length): %s", err)
|
||||
return driver.ErrBadConn
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ driver.Conn = (*Conn)(nil)
|
||||
|
||||
func (cn *Conn) Prepare(query string) (driver.Stmt, error) {
|
||||
if cn.isClosed() {
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
|
||||
ctx := context.TODO()
|
||||
|
||||
name := fmt.Sprintf("pgdriver-%d", cn.stmtCount)
|
||||
cn.stmtCount++
|
||||
|
||||
if err := writeParseDescribeSync(ctx, cn, name, query); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rowDesc, err := readParseDescribeSync(ctx, cn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newStmt(cn, name, rowDesc), nil
|
||||
}
|
||||
|
||||
func (cn *Conn) Close() error {
|
||||
if !atomic.CompareAndSwapInt32(&cn.closed, 0, 1) {
|
||||
return nil
|
||||
}
|
||||
return cn.netConn.Close()
|
||||
}
|
||||
|
||||
func (cn *Conn) isClosed() bool {
|
||||
return atomic.LoadInt32(&cn.closed) == 1
|
||||
}
|
||||
|
||||
func (cn *Conn) Begin() (driver.Tx, error) {
|
||||
return cn.BeginTx(context.Background(), driver.TxOptions{})
|
||||
}
|
||||
|
||||
var _ driver.ConnBeginTx = (*Conn)(nil)
|
||||
|
||||
func (cn *Conn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
|
||||
// No need to check if the conn is closed. ExecContext below handles that.
|
||||
|
||||
if sql.IsolationLevel(opts.Isolation) != sql.LevelDefault {
|
||||
return nil, errors.New("pgdriver: custom IsolationLevel is not supported")
|
||||
}
|
||||
if opts.ReadOnly {
|
||||
return nil, errors.New("pgdriver: ReadOnly transactions are not supported")
|
||||
}
|
||||
|
||||
if _, err := cn.ExecContext(ctx, "BEGIN", nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tx{cn: cn}, nil
|
||||
}
|
||||
|
||||
var _ driver.ExecerContext = (*Conn)(nil)
|
||||
|
||||
func (cn *Conn) ExecContext(
|
||||
ctx context.Context, query string, args []driver.NamedValue,
|
||||
) (driver.Result, error) {
|
||||
if cn.isClosed() {
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
res, err := cn.exec(ctx, query, args)
|
||||
if err != nil {
|
||||
return nil, cn.checkBadConn(err)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (cn *Conn) exec(
|
||||
ctx context.Context, query string, args []driver.NamedValue,
|
||||
) (driver.Result, error) {
|
||||
query, err := formatQuery(query, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := writeQuery(ctx, cn, query); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return readQuery(ctx, cn)
|
||||
}
|
||||
|
||||
var _ driver.QueryerContext = (*Conn)(nil)
|
||||
|
||||
func (cn *Conn) QueryContext(
|
||||
ctx context.Context, query string, args []driver.NamedValue,
|
||||
) (driver.Rows, error) {
|
||||
if cn.isClosed() {
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
rows, err := cn.query(ctx, query, args)
|
||||
if err != nil {
|
||||
return nil, cn.checkBadConn(err)
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
func (cn *Conn) query(
|
||||
ctx context.Context, query string, args []driver.NamedValue,
|
||||
) (driver.Rows, error) {
|
||||
query, err := formatQuery(query, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := writeQuery(ctx, cn, query); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return readQueryData(ctx, cn)
|
||||
}
|
||||
|
||||
var _ driver.Pinger = (*Conn)(nil)
|
||||
|
||||
func (cn *Conn) Ping(ctx context.Context) error {
|
||||
_, err := cn.ExecContext(ctx, "SELECT 1", nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (cn *Conn) setReadDeadline(ctx context.Context, timeout time.Duration) {
|
||||
if timeout == -1 {
|
||||
timeout = cn.cfg.ReadTimeout
|
||||
}
|
||||
_ = cn.netConn.SetReadDeadline(cn.deadline(ctx, timeout))
|
||||
}
|
||||
|
||||
func (cn *Conn) setWriteDeadline(ctx context.Context, timeout time.Duration) {
|
||||
if timeout == -1 {
|
||||
timeout = cn.cfg.WriteTimeout
|
||||
}
|
||||
_ = cn.netConn.SetWriteDeadline(cn.deadline(ctx, timeout))
|
||||
}
|
||||
|
||||
func (cn *Conn) deadline(ctx context.Context, timeout time.Duration) time.Time {
|
||||
deadline, ok := ctx.Deadline()
|
||||
if !ok {
|
||||
if timeout == 0 {
|
||||
return time.Time{}
|
||||
}
|
||||
return time.Now().Add(timeout)
|
||||
}
|
||||
|
||||
if timeout == 0 {
|
||||
return deadline
|
||||
}
|
||||
if tm := time.Now().Add(timeout); tm.Before(deadline) {
|
||||
return tm
|
||||
}
|
||||
return deadline
|
||||
}
|
||||
|
||||
var _ driver.Validator = (*Conn)(nil)
|
||||
|
||||
func (cn *Conn) IsValid() bool {
|
||||
return !cn.isClosed()
|
||||
}
|
||||
|
||||
var _ driver.SessionResetter = (*Conn)(nil)
|
||||
|
||||
func (cn *Conn) ResetSession(ctx context.Context) error {
|
||||
if cn.isClosed() {
|
||||
return driver.ErrBadConn
|
||||
}
|
||||
if cn.cfg.ResetSessionFunc != nil {
|
||||
return cn.cfg.ResetSessionFunc(ctx, cn)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cn *Conn) checkBadConn(err error) error {
|
||||
if isBadConn(err, false) {
|
||||
// Close and return driver.ErrBadConn next time the conn is used.
|
||||
_ = cn.Close()
|
||||
}
|
||||
// Always return the original error.
|
||||
return err
|
||||
}
|
||||
|
||||
func (cn *Conn) Conn() net.Conn { return cn.netConn }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type rows struct {
|
||||
cn *Conn
|
||||
rowDesc *rowDescription
|
||||
reusable bool
|
||||
closed bool
|
||||
}
|
||||
|
||||
var _ driver.Rows = (*rows)(nil)
|
||||
|
||||
func newRows(cn *Conn, rowDesc *rowDescription, reusable bool) *rows {
|
||||
return &rows{
|
||||
cn: cn,
|
||||
rowDesc: rowDesc,
|
||||
reusable: reusable,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *rows) Columns() []string {
|
||||
if r.closed || r.rowDesc == nil {
|
||||
return nil
|
||||
}
|
||||
return r.rowDesc.names
|
||||
}
|
||||
|
||||
func (r *rows) Close() error {
|
||||
if r.closed {
|
||||
return nil
|
||||
}
|
||||
defer r.close()
|
||||
|
||||
for {
|
||||
switch err := r.Next(nil); err {
|
||||
case nil, io.EOF:
|
||||
return nil
|
||||
default: // unexpected error
|
||||
_ = r.cn.Close()
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *rows) close() {
|
||||
r.closed = true
|
||||
|
||||
if r.rowDesc != nil {
|
||||
if r.reusable {
|
||||
rowDescPool.Put(r.rowDesc)
|
||||
}
|
||||
r.rowDesc = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r *rows) Next(dest []driver.Value) error {
|
||||
if r.closed {
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
eof, err := r.next(dest)
|
||||
if err == io.EOF {
|
||||
return io.ErrUnexpectedEOF
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
if eof {
|
||||
return io.EOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *rows) next(dest []driver.Value) (eof bool, _ error) {
|
||||
rd := r.cn.reader(context.TODO(), -1)
|
||||
var firstErr error
|
||||
for {
|
||||
c, msgLen, err := readMessageType(rd)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
switch c {
|
||||
case dataRowMsg:
|
||||
return false, r.readDataRow(rd, dest)
|
||||
case commandCompleteMsg:
|
||||
if err := rd.Discard(msgLen); err != nil {
|
||||
return false, err
|
||||
}
|
||||
case readyForQueryMsg:
|
||||
r.close()
|
||||
|
||||
if err := rd.Discard(msgLen); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if firstErr != nil {
|
||||
return false, firstErr
|
||||
}
|
||||
return true, nil
|
||||
case parameterStatusMsg, noticeResponseMsg:
|
||||
if err := rd.Discard(msgLen); err != nil {
|
||||
return false, err
|
||||
}
|
||||
case errorResponseMsg:
|
||||
e, err := readError(rd)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if firstErr == nil {
|
||||
firstErr = e
|
||||
}
|
||||
default:
|
||||
return false, fmt.Errorf("pgdriver: Next: unexpected message %q", c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *rows) readDataRow(rd *reader, dest []driver.Value) error {
|
||||
numCol, err := readInt16(rd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(dest) != int(numCol) {
|
||||
return fmt.Errorf("pgdriver: query returned %d columns, but Scan dest has %d items",
|
||||
numCol, len(dest))
|
||||
}
|
||||
|
||||
for colIdx := int16(0); colIdx < numCol; colIdx++ {
|
||||
dataLen, err := readInt32(rd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
value, err := readColumnValue(rd, r.rowDesc.types[colIdx], int(dataLen))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if dest != nil {
|
||||
dest[colIdx] = value
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
func parseResult(b []byte) (driver.RowsAffected, error) {
|
||||
i := bytes.LastIndexByte(b, ' ')
|
||||
if i == -1 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
b = b[i+1 : len(b)-1]
|
||||
affected, err := strconv.ParseUint(bytesToString(b), 10, 64)
|
||||
if err != nil {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
return driver.RowsAffected(affected), nil
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type tx struct {
|
||||
cn *Conn
|
||||
}
|
||||
|
||||
var _ driver.Tx = (*tx)(nil)
|
||||
|
||||
func (tx tx) Commit() error {
|
||||
_, err := tx.cn.ExecContext(context.Background(), "COMMIT", nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (tx tx) Rollback() error {
|
||||
_, err := tx.cn.ExecContext(context.Background(), "ROLLBACK", nil)
|
||||
return err
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type stmt struct {
|
||||
cn *Conn
|
||||
name string
|
||||
rowDesc *rowDescription
|
||||
}
|
||||
|
||||
var (
|
||||
_ driver.Stmt = (*stmt)(nil)
|
||||
_ driver.StmtExecContext = (*stmt)(nil)
|
||||
_ driver.StmtQueryContext = (*stmt)(nil)
|
||||
)
|
||||
|
||||
func newStmt(cn *Conn, name string, rowDesc *rowDescription) *stmt {
|
||||
return &stmt{
|
||||
cn: cn,
|
||||
name: name,
|
||||
rowDesc: rowDesc,
|
||||
}
|
||||
}
|
||||
|
||||
func (stmt *stmt) Close() error {
|
||||
if stmt.rowDesc != nil {
|
||||
rowDescPool.Put(stmt.rowDesc)
|
||||
stmt.rowDesc = nil
|
||||
}
|
||||
|
||||
ctx := context.TODO()
|
||||
if err := writeCloseStmt(ctx, stmt.cn, stmt.name); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := readCloseStmtComplete(ctx, stmt.cn); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (stmt *stmt) NumInput() int {
|
||||
if stmt.rowDesc == nil {
|
||||
return -1
|
||||
}
|
||||
return int(stmt.rowDesc.numInput)
|
||||
}
|
||||
|
||||
func (stmt *stmt) Exec(args []driver.Value) (driver.Result, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (stmt *stmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
|
||||
if err := writeBindExecute(ctx, stmt.cn, stmt.name, args); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return readExtQuery(ctx, stmt.cn)
|
||||
}
|
||||
|
||||
func (stmt *stmt) Query(args []driver.Value) (driver.Rows, error) {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (stmt *stmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
|
||||
if err := writeBindExecute(ctx, stmt.cn, stmt.name, args); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return readExtQueryData(ctx, stmt.cn, stmt.rowDesc)
|
||||
}
|
||||
Reference in New Issue
Block a user