update vendor/ dir to latest w/o heroku, moby

had to lock a lot of things in place
This commit is contained in:
Reed Allman
2017-08-03 02:38:15 -07:00
parent 780791da1c
commit 30f3c45dbc
5637 changed files with 191713 additions and 1133103 deletions

View File

@@ -6,3 +6,4 @@
Icon?
ehthumbs.db
Thumbs.db
.idea

View File

@@ -1,9 +1,6 @@
sudo: false
language: go
go:
- 1.2
- 1.3
- 1.4
- 1.5
- 1.6
- 1.7

View File

@@ -12,6 +12,7 @@
# Individual Persons
Aaron Hopkins <go-sql-driver at die.net>
Achille Roussel <achille.roussel at gmail.com>
Arne Hormann <arnehormann at gmail.com>
Asta Xie <xiemengjun at gmail.com>
Bulat Gaifullin <gaifullinbf at gmail.com>
@@ -32,6 +33,7 @@ ICHINOSE Shogo <shogo82148 at gmail.com>
INADA Naoki <songofacandy at gmail.com>
Jacek Szwec <szwec.jacek at gmail.com>
James Harr <james.harr at gmail.com>
Jeff Hodges <jeff at somethingsimilar.com>
Jian Zhen <zhenjl at gmail.com>
Joshua Prunier <joshua.prunier at gmail.com>
Julien Lefevre <julien.lefevr at gmail.com>
@@ -45,6 +47,7 @@ Lion Yang <lion at aosc.xyz>
Luca Looz <luca.looz92 at gmail.com>
Lucas Liu <extrafliu at gmail.com>
Luke Scott <luke at webconnex.com>
Maciej Zimnoch <maciej.zimnoch@codilime.com>
Michael Woolnough <michael.woolnough at gmail.com>
Nicola Peduzzi <thenikso at gmail.com>
Olivier Mengué <dolmen at cpan.org>

View File

@@ -39,7 +39,7 @@ A MySQL-Driver for Go's [database/sql](https://golang.org/pkg/database/sql/) pac
* Optional placeholder interpolation
## Requirements
* Go 1.2 or higher
* Go 1.5 or higher
* MySQL (4.1+), MariaDB, Percona Server, Google CloudSQL or Sphinx (2.2.3+)
---------------------------------------
@@ -279,7 +279,7 @@ Default: false
`rejectreadOnly=true` causes the driver to reject read-only connections. This
is for a possible race condition during an automatic failover, where the mysql
client gets connected to a read-only replica after the failover.
client gets connected to a read-only replica after the failover.
Note that this should be a fairly rare case, as an automatic failover normally
happens when the primary is down, and the race condition shouldn't happen

View File

@@ -14,17 +14,15 @@ import (
"net"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
)
// a copy of context.Context for Go 1.7 and later.
// a copy of context.Context for Go 1.7 and earlier
type mysqlContext interface {
Done() <-chan struct{}
Err() error
// They are defined in context.Context, but go-mysql-driver does not use them.
// defined in context.Context, but not used in this driver:
// Deadline() (deadline time.Time, ok bool)
// Value(key interface{}) interface{}
}
@@ -44,18 +42,13 @@ type mysqlConn struct {
parseTime bool
strict bool
// for context support (From Go 1.8)
// for context support (Go 1.8+)
watching bool
watcher chan<- mysqlContext
closech chan struct{}
finished chan<- struct{}
// set non-zero when conn is closed, before closech is closed.
// accessed atomically.
closed int32
mu sync.Mutex // guards following fields
canceledErr error // set non-nil if conn is canceled
canceled atomicError // set non-nil if conn is canceled
closed atomicBool // set when conn is closed, before closech is closed
}
// Handles parameters set in DSN after the connection is established
@@ -89,7 +82,7 @@ func (mc *mysqlConn) handleParams() (err error) {
}
func (mc *mysqlConn) Begin() (driver.Tx, error) {
if mc.isBroken() {
if mc.closed.IsSet() {
errLog.Print(ErrInvalidConn)
return nil, driver.ErrBadConn
}
@@ -103,7 +96,7 @@ func (mc *mysqlConn) Begin() (driver.Tx, error) {
func (mc *mysqlConn) Close() (err error) {
// Makes Close idempotent
if !mc.isBroken() {
if !mc.closed.IsSet() {
err = mc.writeCommandPacket(comQuit)
}
@@ -117,7 +110,7 @@ func (mc *mysqlConn) Close() (err error) {
// is called before auth or on auth failure because MySQL will have already
// closed the network connection.
func (mc *mysqlConn) cleanup() {
if atomic.SwapInt32(&mc.closed, 1) != 0 {
if !mc.closed.TrySet(true) {
return
}
@@ -131,13 +124,9 @@ func (mc *mysqlConn) cleanup() {
}
}
func (mc *mysqlConn) isBroken() bool {
return atomic.LoadInt32(&mc.closed) != 0
}
func (mc *mysqlConn) error() error {
if mc.isBroken() {
if err := mc.canceled(); err != nil {
if mc.closed.IsSet() {
if err := mc.canceled.Value(); err != nil {
return err
}
return ErrInvalidConn
@@ -146,7 +135,7 @@ func (mc *mysqlConn) error() error {
}
func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
if mc.isBroken() {
if mc.closed.IsSet() {
errLog.Print(ErrInvalidConn)
return nil, driver.ErrBadConn
}
@@ -300,7 +289,7 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin
}
func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) {
if mc.isBroken() {
if mc.closed.IsSet() {
errLog.Print(ErrInvalidConn)
return nil, driver.ErrBadConn
}
@@ -361,7 +350,7 @@ func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, erro
}
func (mc *mysqlConn) query(query string, args []driver.Value) (*textRows, error) {
if mc.isBroken() {
if mc.closed.IsSet() {
errLog.Print(ErrInvalidConn)
return nil, driver.ErrBadConn
}
@@ -436,19 +425,10 @@ func (mc *mysqlConn) getSystemVar(name string) ([]byte, error) {
// finish is called when the query has canceled.
func (mc *mysqlConn) cancel(err error) {
mc.mu.Lock()
mc.canceledErr = err
mc.mu.Unlock()
mc.canceled.Set(err)
mc.cleanup()
}
// canceled returns non-nil if the connection was closed due to context cancelation.
func (mc *mysqlConn) canceled() error {
mc.mu.Lock()
defer mc.mu.Unlock()
return mc.canceledErr
}
// finish is called when the query has succeeded.
func (mc *mysqlConn) finish() {
if !mc.watching || mc.finished == nil {

View File

@@ -19,7 +19,7 @@ import (
// Ping implements driver.Pinger interface
func (mc *mysqlConn) Ping(ctx context.Context) error {
if mc.isBroken() {
if mc.closed.IsSet() {
errLog.Print(ErrInvalidConn)
return driver.ErrBadConn
}
@@ -41,10 +41,6 @@ func (mc *mysqlConn) Ping(ctx context.Context) error {
// BeginTx implements driver.ConnBeginTx interface
func (mc *mysqlConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
if sql.IsolationLevel(opts.Isolation) != sql.LevelDefault {
// TODO: support isolation levels
return nil, errors.New("mysql: isolation levels not supported")
}
if opts.ReadOnly {
// TODO: support read-only transactions
return nil, errors.New("mysql: read-only transactions not supported")
@@ -54,19 +50,20 @@ func (mc *mysqlConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver
return nil, err
}
tx, err := mc.Begin()
mc.finish()
if err != nil {
return nil, err
defer mc.finish()
if sql.IsolationLevel(opts.Isolation) != sql.LevelDefault {
level, err := mapIsolationLevel(opts.Isolation)
if err != nil {
return nil, err
}
err = mc.exec("SET TRANSACTION ISOLATION LEVEL " + level)
if err != nil {
return nil, err
}
}
select {
default:
case <-ctx.Done():
tx.Rollback()
return nil, ctx.Err()
}
return tx, err
return mc.Begin()
}
func (mc *mysqlConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {

View File

@@ -66,11 +66,6 @@ func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
mc.parseTime = mc.cfg.ParseTime
mc.strict = mc.cfg.Strict
// Call startWatcher for context support (From Go 1.8)
if s, ok := interface{}(mc).(watcher); ok {
s.startWatcher()
}
// Connect to Server
if dial, ok := dials[mc.cfg.Net]; ok {
mc.netConn, err = dial(mc.cfg.Addr)
@@ -92,6 +87,11 @@ func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
}
}
// Call startWatcher for context support (From Go 1.8)
if s, ok := interface{}(mc).(watcher); ok {
s.startWatcher()
}
mc.buf = newBuffer(mc.netConn)
// Set I/O timeouts

View File

@@ -468,3 +468,55 @@ func TestContextCancelBegin(t *testing.T) {
}
})
}
func TestContextBeginIsolationLevel(t *testing.T) {
runTests(t, dsn, func(dbt *DBTest) {
dbt.mustExec("CREATE TABLE test (v INTEGER)")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
tx1, err := dbt.db.BeginTx(ctx, &sql.TxOptions{
Isolation: sql.LevelRepeatableRead,
})
if err != nil {
dbt.Fatal(err)
}
tx2, err := dbt.db.BeginTx(ctx, &sql.TxOptions{
Isolation: sql.LevelReadCommitted,
})
if err != nil {
dbt.Fatal(err)
}
_, err = tx1.ExecContext(ctx, "INSERT INTO test VALUES (1)")
if err != nil {
dbt.Fatal(err)
}
var v int
row := tx2.QueryRowContext(ctx, "SELECT COUNT(*) FROM test")
if err := row.Scan(&v); err != nil {
dbt.Fatal(err)
}
// Because writer transaction wasn't commited yet, it should be available
if v != 0 {
dbt.Errorf("expected val to be 0, got %d", v)
}
err = tx1.Commit()
if err != nil {
dbt.Fatal(err)
}
row = tx2.QueryRowContext(ctx, "SELECT COUNT(*) FROM test")
if err := row.Scan(&v); err != nil {
dbt.Fatal(err)
}
// Data written by writer transaction is already commited, it should be selectable
if v != 1 {
dbt.Errorf("expected val to be 1, got %d", v)
}
tx2.Commit()
})
}

View File

@@ -15,6 +15,7 @@ import (
"fmt"
"net"
"net/url"
"sort"
"strconv"
"strings"
"time"
@@ -257,7 +258,12 @@ func (cfg *Config) FormatDSN() string {
// other params
if cfg.Params != nil {
for param, value := range cfg.Params {
var params []string
for param := range cfg.Params {
params = append(params, param)
}
sort.Strings(params)
for _, param := range params {
if hasParam {
buf.WriteByte('&')
} else {
@@ -267,7 +273,7 @@ func (cfg *Config) FormatDSN() string {
buf.WriteString(param)
buf.WriteByte('=')
buf.WriteString(url.QueryEscape(value))
buf.WriteString(url.QueryEscape(cfg.Params[param]))
}
}
@@ -528,9 +534,7 @@ func parseDSNParams(cfg *Config, params string) (err error) {
return fmt.Errorf("invalid value for TLS config name: %v", err)
}
if tlsConfig, ok := tlsConfigRegister[name]; ok {
tlsConfig = cloneTLSConfig(tlsConfig)
if tlsConfig := getTLSConfigClone(name); tlsConfig != nil {
if len(tlsConfig.ServerName) == 0 && !tlsConfig.InsecureSkipVerify {
host, _, err := net.SplitHostPort(cfg.Addr)
if err == nil {

View File

@@ -220,6 +220,22 @@ func TestDSNUnsafeCollation(t *testing.T) {
}
}
func TestParamsAreSorted(t *testing.T) {
expected := "/dbname?interpolateParams=true&foobar=baz&quux=loo"
dsn := &Config{
DBName: "dbname",
InterpolateParams: true,
Params: map[string]string{
"quux": "loo",
"foobar": "baz",
},
}
actual := dsn.FormatDSN()
if actual != expected {
t.Errorf("generic Config.Params were not sorted: want %#v, got %#v", expected, actual)
}
}
func BenchmarkParseDSN(b *testing.B) {
b.ReportAllocs()

View File

@@ -30,7 +30,7 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
// read packet header
data, err := mc.buf.readNext(4)
if err != nil {
if cerr := mc.canceled(); cerr != nil {
if cerr := mc.canceled.Value(); cerr != nil {
return nil, cerr
}
errLog.Print(err)
@@ -66,7 +66,7 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
// read packet body [pktLen bytes]
data, err = mc.buf.readNext(pktLen)
if err != nil {
if cerr := mc.canceled(); cerr != nil {
if cerr := mc.canceled.Value(); cerr != nil {
return nil, cerr
}
errLog.Print(err)
@@ -134,7 +134,7 @@ func (mc *mysqlConn) writePacket(data []byte) error {
mc.cleanup()
errLog.Print(ErrMalformPkt)
} else {
if cerr := mc.canceled(); cerr != nil {
if cerr := mc.canceled.Value(); cerr != nil {
return cerr
}
mc.cleanup()
@@ -1078,17 +1078,19 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
paramTypes[i+i] = fieldTypeString
paramTypes[i+i+1] = 0x00
var val []byte
var a [64]byte
var b = a[:0]
if v.IsZero() {
val = []byte("0000-00-00")
b = append(b, "0000-00-00"...)
} else {
val = []byte(v.In(mc.cfg.Loc).Format(timeFormat))
b = v.In(mc.cfg.Loc).AppendFormat(b, timeFormat)
}
paramValues = appendLengthEncodedInteger(paramValues,
uint64(len(val)),
uint64(len(b)),
)
paramValues = append(paramValues, val...)
paramValues = append(paramValues, b...)
default:
return fmt.Errorf("can not convert type: %T", arg)

View File

@@ -23,7 +23,7 @@ type mysqlStmt struct {
}
func (stmt *mysqlStmt) Close() error {
if stmt.mc == nil || stmt.mc.isBroken() {
if stmt.mc == nil || stmt.mc.closed.IsSet() {
// driver.Stmt.Close can be called more than once, thus this function
// has to be idempotent.
// See also Issue #450 and golang/go#16019.
@@ -45,7 +45,7 @@ func (stmt *mysqlStmt) ColumnConverter(idx int) driver.ValueConverter {
}
func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) {
if stmt.mc.isBroken() {
if stmt.mc.closed.IsSet() {
errLog.Print(ErrInvalidConn)
return nil, driver.ErrBadConn
}
@@ -93,7 +93,7 @@ func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) {
}
func (stmt *mysqlStmt) query(args []driver.Value) (*binaryRows, error) {
if stmt.mc.isBroken() {
if stmt.mc.closed.IsSet() {
errLog.Print(ErrInvalidConn)
return nil, driver.ErrBadConn
}

View File

@@ -13,7 +13,7 @@ type mysqlTx struct {
}
func (tx *mysqlTx) Commit() (err error) {
if tx.mc == nil || tx.mc.isBroken() {
if tx.mc == nil || tx.mc.closed.IsSet() {
return ErrInvalidConn
}
err = tx.mc.exec("COMMIT")
@@ -22,7 +22,7 @@ func (tx *mysqlTx) Commit() (err error) {
}
func (tx *mysqlTx) Rollback() (err error) {
if tx.mc == nil || tx.mc.isBroken() {
if tx.mc == nil || tx.mc.closed.IsSet() {
return ErrInvalidConn
}
err = tx.mc.exec("ROLLBACK")

View File

@@ -16,10 +16,13 @@ import (
"fmt"
"io"
"strings"
"sync"
"sync/atomic"
"time"
)
var (
tlsConfigLock sync.RWMutex
tlsConfigRegister map[string]*tls.Config // Register for custom tls.Configs
)
@@ -53,19 +56,32 @@ func RegisterTLSConfig(key string, config *tls.Config) error {
return fmt.Errorf("key '%s' is reserved", key)
}
tlsConfigLock.Lock()
if tlsConfigRegister == nil {
tlsConfigRegister = make(map[string]*tls.Config)
}
tlsConfigRegister[key] = config
tlsConfigLock.Unlock()
return nil
}
// DeregisterTLSConfig removes the tls.Config associated with key.
func DeregisterTLSConfig(key string) {
tlsConfigLock.Lock()
if tlsConfigRegister != nil {
delete(tlsConfigRegister, key)
}
tlsConfigLock.Unlock()
}
func getTLSConfigClone(key string) (config *tls.Config) {
tlsConfigLock.RLock()
if v, ok := tlsConfigRegister[key]; ok {
config = cloneTLSConfig(v)
}
tlsConfigLock.RUnlock()
return
}
// Returns the bool value of the input.
@@ -740,3 +756,67 @@ func escapeStringQuotes(buf []byte, v string) []byte {
return buf[:pos]
}
/******************************************************************************
* Sync utils *
******************************************************************************/
// noCopy may be embedded into structs which must not be copied
// after the first use.
//
// See https://github.com/golang/go/issues/8005#issuecomment-190753527
// for details.
type noCopy struct{}
// Lock is a no-op used by -copylocks checker from `go vet`.
func (*noCopy) Lock() {}
// atomicBool is a wrapper around uint32 for usage as a boolean value with
// atomic access.
type atomicBool struct {
_noCopy noCopy
value uint32
}
// IsSet returns wether the current boolean value is true
func (ab *atomicBool) IsSet() bool {
return atomic.LoadUint32(&ab.value) > 0
}
// Set sets the value of the bool regardless of the previous value
func (ab *atomicBool) Set(value bool) {
if value {
atomic.StoreUint32(&ab.value, 1)
} else {
atomic.StoreUint32(&ab.value, 0)
}
}
// TrySet sets the value of the bool and returns wether the value changed
func (ab *atomicBool) TrySet(value bool) bool {
if value {
return atomic.SwapUint32(&ab.value, 1) == 0
}
return atomic.SwapUint32(&ab.value, 0) > 0
}
// atomicBool is a wrapper for atomically accessed error values
type atomicError struct {
_noCopy noCopy
value atomic.Value
}
// Set sets the error value regardless of the previous value.
// The value must not be nil
func (ae *atomicError) Set(value error) {
ae.value.Store(value)
}
// Value returns the current error value
func (ae *atomicError) Value() error {
if v := ae.value.Load(); v != nil {
// this will panic if the value doesn't implement the error interface
return v.(error)
}
return nil
}

View File

@@ -12,6 +12,7 @@ package mysql
import (
"crypto/tls"
"database/sql"
"database/sql/driver"
"errors"
)
@@ -31,3 +32,18 @@ func namedValueToValue(named []driver.NamedValue) ([]driver.Value, error) {
}
return dargs, nil
}
func mapIsolationLevel(level driver.IsolationLevel) (string, error) {
switch sql.IsolationLevel(level) {
case sql.LevelRepeatableRead:
return "REPEATABLE READ", nil
case sql.LevelReadCommitted:
return "READ COMMITTED", nil
case sql.LevelReadUncommitted:
return "READ UNCOMMITTED", nil
case sql.LevelSerializable:
return "SERIALIZABLE", nil
default:
return "", errors.New("mysql: unsupported isolation level: " + string(level))
}
}

View File

@@ -0,0 +1,54 @@
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
//
// Copyright 2017 The Go-MySQL-Driver Authors. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.
// +build go1.8
package mysql
import (
"database/sql"
"database/sql/driver"
"testing"
)
func TestIsolationLevelMapping(t *testing.T) {
data := []struct {
level driver.IsolationLevel
expected string
}{
{
level: driver.IsolationLevel(sql.LevelReadCommitted),
expected: "READ COMMITTED",
},
{
level: driver.IsolationLevel(sql.LevelRepeatableRead),
expected: "REPEATABLE READ",
},
{
level: driver.IsolationLevel(sql.LevelReadUncommitted),
expected: "READ UNCOMMITTED",
},
{
level: driver.IsolationLevel(sql.LevelSerializable),
expected: "SERIALIZABLE",
},
}
for i, td := range data {
if actual, err := mapIsolationLevel(td.level); actual != td.expected || err != nil {
t.Fatal(i, td.expected, actual, err)
}
}
// check unsupported mapping
if actual, err := mapIsolationLevel(driver.IsolationLevel(sql.LevelLinearizable)); actual != "" || err == nil {
t.Fatal("Expected error on unsupported isolation level")
}
}

View File

@@ -195,3 +195,83 @@ func TestEscapeQuotes(t *testing.T) {
expect("foo''bar", "foo'bar") // affected
expect("foo\"bar", "foo\"bar") // not affected
}
func TestAtomicBool(t *testing.T) {
var ab atomicBool
if ab.IsSet() {
t.Fatal("Expected value to be false")
}
ab.Set(true)
if ab.value != 1 {
t.Fatal("Set(true) did not set value to 1")
}
if !ab.IsSet() {
t.Fatal("Expected value to be true")
}
ab.Set(true)
if !ab.IsSet() {
t.Fatal("Expected value to be true")
}
ab.Set(false)
if ab.value != 0 {
t.Fatal("Set(false) did not set value to 0")
}
if ab.IsSet() {
t.Fatal("Expected value to be false")
}
ab.Set(false)
if ab.IsSet() {
t.Fatal("Expected value to be false")
}
if ab.TrySet(false) {
t.Fatal("Expected TrySet(false) to fail")
}
if !ab.TrySet(true) {
t.Fatal("Expected TrySet(true) to succeed")
}
if !ab.IsSet() {
t.Fatal("Expected value to be true")
}
ab.Set(true)
if !ab.IsSet() {
t.Fatal("Expected value to be true")
}
if ab.TrySet(true) {
t.Fatal("Expected TrySet(true) to fail")
}
if !ab.TrySet(false) {
t.Fatal("Expected TrySet(false) to succeed")
}
if ab.IsSet() {
t.Fatal("Expected value to be false")
}
ab._noCopy.Lock() // we've "tested" it ¯\_(ツ)_/¯
}
func TestAtomicError(t *testing.T) {
var ae atomicError
if ae.Value() != nil {
t.Fatal("Expected value to be nil")
}
ae.Set(ErrMalformPkt)
if v := ae.Value(); v != ErrMalformPkt {
if v == nil {
t.Fatal("Value is still nil")
}
t.Fatal("Error did not match")
}
ae.Set(ErrPktSync)
if ae.Value() == ErrMalformPkt {
t.Fatal("Error still matches old error")
}
if v := ae.Value(); v != ErrPktSync {
t.Fatal("Error did not match")
}
}