mirror of
https://github.com/documize/community.git
synced 2025-07-19 05:09:42 +02:00
Updated Go MySQL driver library
This commit is contained in:
parent
d04becc1a3
commit
f3df43efe0
32 changed files with 7413 additions and 867 deletions
|
@ -206,5 +206,8 @@ func (h *Handler) Restore(w http.ResponseWriter, r *http.Request) {
|
||||||
h.Runtime.Log.Infof("Restore remapped %d UserID values", len(rh.MapUserID))
|
h.Runtime.Log.Infof("Restore remapped %d UserID values", len(rh.MapUserID))
|
||||||
h.Runtime.Log.Info("Restore completed")
|
h.Runtime.Log.Info("Restore completed")
|
||||||
|
|
||||||
|
h.Runtime.Log.Info("Building search index")
|
||||||
|
go h.Indexer.Rebuild(ctx)
|
||||||
|
|
||||||
response.WriteEmpty(w)
|
response.WriteEmpty(w)
|
||||||
}
|
}
|
||||||
|
|
12
vendor/github.com/go-sql-driver/mysql/AUTHORS
generated
vendored
12
vendor/github.com/go-sql-driver/mysql/AUTHORS
generated
vendored
|
@ -27,6 +27,7 @@ Daniël van Eeden <git at myname.nl>
|
||||||
Dave Protasowski <dprotaso at gmail.com>
|
Dave Protasowski <dprotaso at gmail.com>
|
||||||
DisposaBoy <disposaboy at dby.me>
|
DisposaBoy <disposaboy at dby.me>
|
||||||
Egor Smolyakov <egorsmkv at gmail.com>
|
Egor Smolyakov <egorsmkv at gmail.com>
|
||||||
|
Erwan Martin <hello at erwan.io>
|
||||||
Evan Shaw <evan at vendhq.com>
|
Evan Shaw <evan at vendhq.com>
|
||||||
Frederick Mayle <frederickmayle at gmail.com>
|
Frederick Mayle <frederickmayle at gmail.com>
|
||||||
Gustavo Kristic <gkristic at gmail.com>
|
Gustavo Kristic <gkristic at gmail.com>
|
||||||
|
@ -34,12 +35,15 @@ Hajime Nakagami <nakagami at gmail.com>
|
||||||
Hanno Braun <mail at hannobraun.com>
|
Hanno Braun <mail at hannobraun.com>
|
||||||
Henri Yandell <flamefew at gmail.com>
|
Henri Yandell <flamefew at gmail.com>
|
||||||
Hirotaka Yamamoto <ymmt2005 at gmail.com>
|
Hirotaka Yamamoto <ymmt2005 at gmail.com>
|
||||||
|
Huyiguang <hyg at webterren.com>
|
||||||
ICHINOSE Shogo <shogo82148 at gmail.com>
|
ICHINOSE Shogo <shogo82148 at gmail.com>
|
||||||
|
Ilia Cimpoes <ichimpoesh at gmail.com>
|
||||||
INADA Naoki <songofacandy at gmail.com>
|
INADA Naoki <songofacandy at gmail.com>
|
||||||
Jacek Szwec <szwec.jacek at gmail.com>
|
Jacek Szwec <szwec.jacek at gmail.com>
|
||||||
James Harr <james.harr at gmail.com>
|
James Harr <james.harr at gmail.com>
|
||||||
Jeff Hodges <jeff at somethingsimilar.com>
|
Jeff Hodges <jeff at somethingsimilar.com>
|
||||||
Jeffrey Charles <jeffreycharles at gmail.com>
|
Jeffrey Charles <jeffreycharles at gmail.com>
|
||||||
|
Jerome Meyer <jxmeyer at gmail.com>
|
||||||
Jian Zhen <zhenjl at gmail.com>
|
Jian Zhen <zhenjl at gmail.com>
|
||||||
Joshua Prunier <joshua.prunier at gmail.com>
|
Joshua Prunier <joshua.prunier at gmail.com>
|
||||||
Julien Lefevre <julien.lefevr at gmail.com>
|
Julien Lefevre <julien.lefevr at gmail.com>
|
||||||
|
@ -69,9 +73,14 @@ Richard Wilkes <wilkes at me.com>
|
||||||
Robert Russell <robert at rrbrussell.com>
|
Robert Russell <robert at rrbrussell.com>
|
||||||
Runrioter Wung <runrioter at gmail.com>
|
Runrioter Wung <runrioter at gmail.com>
|
||||||
Shuode Li <elemount at qq.com>
|
Shuode Li <elemount at qq.com>
|
||||||
|
Simon J Mudd <sjmudd at pobox.com>
|
||||||
Soroush Pour <me at soroushjp.com>
|
Soroush Pour <me at soroushjp.com>
|
||||||
Stan Putrya <root.vagner at gmail.com>
|
Stan Putrya <root.vagner at gmail.com>
|
||||||
Stanley Gunawan <gunawan.stanley at gmail.com>
|
Stanley Gunawan <gunawan.stanley at gmail.com>
|
||||||
|
Steven Hartland <steven.hartland at multiplay.co.uk>
|
||||||
|
Thomas Wodarek <wodarekwebpage at gmail.com>
|
||||||
|
Tim Ruffles <timruffles at gmail.com>
|
||||||
|
Tom Jenkinson <tom at tjenkinson.me>
|
||||||
Xiangyu Hu <xiangyu.hu at outlook.com>
|
Xiangyu Hu <xiangyu.hu at outlook.com>
|
||||||
Xiaobing Jiang <s7v7nislands at gmail.com>
|
Xiaobing Jiang <s7v7nislands at gmail.com>
|
||||||
Xiuming Chen <cc at cxm.cc>
|
Xiuming Chen <cc at cxm.cc>
|
||||||
|
@ -81,9 +90,12 @@ Zhenye Xie <xiezhenye at gmail.com>
|
||||||
|
|
||||||
Barracuda Networks, Inc.
|
Barracuda Networks, Inc.
|
||||||
Counting Ltd.
|
Counting Ltd.
|
||||||
|
Facebook Inc.
|
||||||
|
GitHub Inc.
|
||||||
Google Inc.
|
Google Inc.
|
||||||
InfoSum Ltd.
|
InfoSum Ltd.
|
||||||
Keybase Inc.
|
Keybase Inc.
|
||||||
|
Multiplay Ltd.
|
||||||
Percona LLC
|
Percona LLC
|
||||||
Pivotal Inc.
|
Pivotal Inc.
|
||||||
Stripe Inc.
|
Stripe Inc.
|
||||||
|
|
15
vendor/github.com/go-sql-driver/mysql/README.md
generated
vendored
15
vendor/github.com/go-sql-driver/mysql/README.md
generated
vendored
|
@ -40,7 +40,7 @@ A MySQL-Driver for Go's [database/sql](https://golang.org/pkg/database/sql/) pac
|
||||||
* Optional placeholder interpolation
|
* Optional placeholder interpolation
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
* Go 1.7 or higher. We aim to support the 3 latest versions of Go.
|
* Go 1.9 or higher. We aim to support the 3 latest versions of Go.
|
||||||
* MySQL (4.1+), MariaDB, Percona Server, Google CloudSQL or Sphinx (2.2.3+)
|
* MySQL (4.1+), MariaDB, Percona Server, Google CloudSQL or Sphinx (2.2.3+)
|
||||||
|
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
@ -171,13 +171,18 @@ Unless you need the fallback behavior, please use `collation` instead.
|
||||||
```
|
```
|
||||||
Type: string
|
Type: string
|
||||||
Valid Values: <name>
|
Valid Values: <name>
|
||||||
Default: utf8_general_ci
|
Default: utf8mb4_general_ci
|
||||||
```
|
```
|
||||||
|
|
||||||
Sets the collation used for client-server interaction on connection. In contrast to `charset`, `collation` does not issue additional queries. If the specified collation is unavailable on the target server, the connection will fail.
|
Sets the collation used for client-server interaction on connection. In contrast to `charset`, `collation` does not issue additional queries. If the specified collation is unavailable on the target server, the connection will fail.
|
||||||
|
|
||||||
A list of valid charsets for a server is retrievable with `SHOW COLLATION`.
|
A list of valid charsets for a server is retrievable with `SHOW COLLATION`.
|
||||||
|
|
||||||
|
The default collation (`utf8mb4_general_ci`) is supported from MySQL 5.5. You should use an older collation (e.g. `utf8_general_ci`) for older MySQL.
|
||||||
|
|
||||||
|
Collations for charset "ucs2", "utf16", "utf16le", and "utf32" can not be used ([ref](https://dev.mysql.com/doc/refman/5.7/en/charset-connection.html#charset-connection-impermissible-client-charset)).
|
||||||
|
|
||||||
|
|
||||||
##### `clientFoundRows`
|
##### `clientFoundRows`
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -328,11 +333,11 @@ Timeout for establishing connections, aka dial timeout. The value must be a deci
|
||||||
|
|
||||||
```
|
```
|
||||||
Type: bool / string
|
Type: bool / string
|
||||||
Valid Values: true, false, skip-verify, <name>
|
Valid Values: true, false, skip-verify, preferred, <name>
|
||||||
Default: false
|
Default: false
|
||||||
```
|
```
|
||||||
|
|
||||||
`tls=true` enables TLS / SSL encrypted connection to the server. Use `skip-verify` if you want to use a self-signed or invalid certificate (server side). Use a custom value registered with [`mysql.RegisterTLSConfig`](https://godoc.org/github.com/go-sql-driver/mysql#RegisterTLSConfig).
|
`tls=true` enables TLS / SSL encrypted connection to the server. Use `skip-verify` if you want to use a self-signed or invalid certificate (server side) or use `preferred` to use TLS only when advertised by the server. This is similar to `skip-verify`, but additionally allows a fallback to a connection which is not encrypted. Neither `skip-verify` nor `preferred` add any reliable security. You can use a custom TLS config after registering it with [`mysql.RegisterTLSConfig`](https://godoc.org/github.com/go-sql-driver/mysql#RegisterTLSConfig).
|
||||||
|
|
||||||
|
|
||||||
##### `writeTimeout`
|
##### `writeTimeout`
|
||||||
|
@ -444,7 +449,7 @@ See the [godoc of Go-MySQL-Driver](https://godoc.org/github.com/go-sql-driver/my
|
||||||
### `time.Time` support
|
### `time.Time` support
|
||||||
The default internal output type of MySQL `DATE` and `DATETIME` values is `[]byte` which allows you to scan the value into a `[]byte`, `string` or `sql.RawBytes` variable in your program.
|
The default internal output type of MySQL `DATE` and `DATETIME` values is `[]byte` which allows you to scan the value into a `[]byte`, `string` or `sql.RawBytes` variable in your program.
|
||||||
|
|
||||||
However, many want to scan MySQL `DATE` and `DATETIME` values into `time.Time` variables, which is the logical opposite in Go to `DATE` and `DATETIME` in MySQL. You can do that by changing the internal output type from `[]byte` to `time.Time` with the DSN parameter `parseTime=true`. You can set the default [`time.Time` location](https://golang.org/pkg/time/#Location) with the `loc` DSN parameter.
|
However, many want to scan MySQL `DATE` and `DATETIME` values into `time.Time` variables, which is the logical equivalent in Go to `DATE` and `DATETIME` in MySQL. You can do that by changing the internal output type from `[]byte` to `time.Time` with the DSN parameter `parseTime=true`. You can set the default [`time.Time` location](https://golang.org/pkg/time/#Location) with the `loc` DSN parameter.
|
||||||
|
|
||||||
**Caution:** As of Go 1.1, this makes `time.Time` the only variable type you can scan `DATE` and `DATETIME` values into. This breaks for example [`sql.RawBytes` support](https://github.com/go-sql-driver/mysql/wiki/Examples#rawbytes).
|
**Caution:** As of Go 1.1, this makes `time.Time` the only variable type you can scan `DATE` and `DATETIME` values into. This breaks for example [`sql.RawBytes` support](https://github.com/go-sql-driver/mysql/wiki/Examples#rawbytes).
|
||||||
|
|
||||||
|
|
8
vendor/github.com/go-sql-driver/mysql/appengine.go
generated
vendored
8
vendor/github.com/go-sql-driver/mysql/appengine.go
generated
vendored
|
@ -11,9 +11,15 @@
|
||||||
package mysql
|
package mysql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
|
||||||
"google.golang.org/appengine/cloudsql"
|
"google.golang.org/appengine/cloudsql"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RegisterDial("cloudsql", cloudsql.Dial)
|
RegisterDialContext("cloudsql", func(_ context.Context, instance string) (net.Conn, error) {
|
||||||
|
// XXX: the cloudsql driver still does not export a Context-aware dialer.
|
||||||
|
return cloudsql.Dial(instance)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
44
vendor/github.com/go-sql-driver/mysql/auth.go
generated
vendored
44
vendor/github.com/go-sql-driver/mysql/auth.go
generated
vendored
|
@ -234,64 +234,64 @@ func (mc *mysqlConn) sendEncryptedPassword(seed []byte, pub *rsa.PublicKey) erro
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return mc.writeAuthSwitchPacket(enc, false)
|
return mc.writeAuthSwitchPacket(enc)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mc *mysqlConn) auth(authData []byte, plugin string) ([]byte, bool, error) {
|
func (mc *mysqlConn) auth(authData []byte, plugin string) ([]byte, error) {
|
||||||
switch plugin {
|
switch plugin {
|
||||||
case "caching_sha2_password":
|
case "caching_sha2_password":
|
||||||
authResp := scrambleSHA256Password(authData, mc.cfg.Passwd)
|
authResp := scrambleSHA256Password(authData, mc.cfg.Passwd)
|
||||||
return authResp, (authResp == nil), nil
|
return authResp, nil
|
||||||
|
|
||||||
case "mysql_old_password":
|
case "mysql_old_password":
|
||||||
if !mc.cfg.AllowOldPasswords {
|
if !mc.cfg.AllowOldPasswords {
|
||||||
return nil, false, ErrOldPassword
|
return nil, ErrOldPassword
|
||||||
}
|
}
|
||||||
// Note: there are edge cases where this should work but doesn't;
|
// Note: there are edge cases where this should work but doesn't;
|
||||||
// this is currently "wontfix":
|
// this is currently "wontfix":
|
||||||
// https://github.com/go-sql-driver/mysql/issues/184
|
// https://github.com/go-sql-driver/mysql/issues/184
|
||||||
authResp := scrambleOldPassword(authData[:8], mc.cfg.Passwd)
|
authResp := append(scrambleOldPassword(authData[:8], mc.cfg.Passwd), 0)
|
||||||
return authResp, true, nil
|
return authResp, nil
|
||||||
|
|
||||||
case "mysql_clear_password":
|
case "mysql_clear_password":
|
||||||
if !mc.cfg.AllowCleartextPasswords {
|
if !mc.cfg.AllowCleartextPasswords {
|
||||||
return nil, false, ErrCleartextPassword
|
return nil, ErrCleartextPassword
|
||||||
}
|
}
|
||||||
// http://dev.mysql.com/doc/refman/5.7/en/cleartext-authentication-plugin.html
|
// http://dev.mysql.com/doc/refman/5.7/en/cleartext-authentication-plugin.html
|
||||||
// http://dev.mysql.com/doc/refman/5.7/en/pam-authentication-plugin.html
|
// http://dev.mysql.com/doc/refman/5.7/en/pam-authentication-plugin.html
|
||||||
return []byte(mc.cfg.Passwd), true, nil
|
return append([]byte(mc.cfg.Passwd), 0), nil
|
||||||
|
|
||||||
case "mysql_native_password":
|
case "mysql_native_password":
|
||||||
if !mc.cfg.AllowNativePasswords {
|
if !mc.cfg.AllowNativePasswords {
|
||||||
return nil, false, ErrNativePassword
|
return nil, ErrNativePassword
|
||||||
}
|
}
|
||||||
// https://dev.mysql.com/doc/internals/en/secure-password-authentication.html
|
// https://dev.mysql.com/doc/internals/en/secure-password-authentication.html
|
||||||
// Native password authentication only need and will need 20-byte challenge.
|
// Native password authentication only need and will need 20-byte challenge.
|
||||||
authResp := scramblePassword(authData[:20], mc.cfg.Passwd)
|
authResp := scramblePassword(authData[:20], mc.cfg.Passwd)
|
||||||
return authResp, false, nil
|
return authResp, nil
|
||||||
|
|
||||||
case "sha256_password":
|
case "sha256_password":
|
||||||
if len(mc.cfg.Passwd) == 0 {
|
if len(mc.cfg.Passwd) == 0 {
|
||||||
return nil, true, nil
|
return []byte{0}, nil
|
||||||
}
|
}
|
||||||
if mc.cfg.tls != nil || mc.cfg.Net == "unix" {
|
if mc.cfg.tls != nil || mc.cfg.Net == "unix" {
|
||||||
// write cleartext auth packet
|
// write cleartext auth packet
|
||||||
return []byte(mc.cfg.Passwd), true, nil
|
return append([]byte(mc.cfg.Passwd), 0), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
pubKey := mc.cfg.pubKey
|
pubKey := mc.cfg.pubKey
|
||||||
if pubKey == nil {
|
if pubKey == nil {
|
||||||
// request public key from server
|
// request public key from server
|
||||||
return []byte{1}, false, nil
|
return []byte{1}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// encrypted password
|
// encrypted password
|
||||||
enc, err := encryptPassword(mc.cfg.Passwd, authData, pubKey)
|
enc, err := encryptPassword(mc.cfg.Passwd, authData, pubKey)
|
||||||
return enc, false, err
|
return enc, err
|
||||||
|
|
||||||
default:
|
default:
|
||||||
errLog.Print("unknown auth plugin:", plugin)
|
errLog.Print("unknown auth plugin:", plugin)
|
||||||
return nil, false, ErrUnknownPlugin
|
return nil, ErrUnknownPlugin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -315,11 +315,11 @@ func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error {
|
||||||
|
|
||||||
plugin = newPlugin
|
plugin = newPlugin
|
||||||
|
|
||||||
authResp, addNUL, err := mc.auth(authData, plugin)
|
authResp, err := mc.auth(authData, plugin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err = mc.writeAuthSwitchPacket(authResp, addNUL); err != nil {
|
if err = mc.writeAuthSwitchPacket(authResp); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,7 +352,7 @@ func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error {
|
||||||
case cachingSha2PasswordPerformFullAuthentication:
|
case cachingSha2PasswordPerformFullAuthentication:
|
||||||
if mc.cfg.tls != nil || mc.cfg.Net == "unix" {
|
if mc.cfg.tls != nil || mc.cfg.Net == "unix" {
|
||||||
// write cleartext auth packet
|
// write cleartext auth packet
|
||||||
err = mc.writeAuthSwitchPacket([]byte(mc.cfg.Passwd), true)
|
err = mc.writeAuthSwitchPacket(append([]byte(mc.cfg.Passwd), 0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -360,13 +360,15 @@ func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error {
|
||||||
pubKey := mc.cfg.pubKey
|
pubKey := mc.cfg.pubKey
|
||||||
if pubKey == nil {
|
if pubKey == nil {
|
||||||
// request public key from server
|
// request public key from server
|
||||||
data := mc.buf.takeSmallBuffer(4 + 1)
|
data, err := mc.buf.takeSmallBuffer(4 + 1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
data[4] = cachingSha2PasswordRequestPublicKey
|
data[4] = cachingSha2PasswordRequestPublicKey
|
||||||
mc.writePacket(data)
|
mc.writePacket(data)
|
||||||
|
|
||||||
// parse public key
|
// parse public key
|
||||||
data, err := mc.readPacket()
|
if data, err = mc.readPacket(); err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
1330
vendor/github.com/go-sql-driver/mysql/auth_test.go
generated
vendored
Normal file
1330
vendor/github.com/go-sql-driver/mysql/auth_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
373
vendor/github.com/go-sql-driver/mysql/benchmark_test.go
generated
vendored
Normal file
373
vendor/github.com/go-sql-driver/mysql/benchmark_test.go
generated
vendored
Normal file
|
@ -0,0 +1,373 @@
|
||||||
|
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||||
|
//
|
||||||
|
// Copyright 2013 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/.
|
||||||
|
|
||||||
|
package mysql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"database/sql/driver"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TB testing.B
|
||||||
|
|
||||||
|
func (tb *TB) check(err error) {
|
||||||
|
if err != nil {
|
||||||
|
tb.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tb *TB) checkDB(db *sql.DB, err error) *sql.DB {
|
||||||
|
tb.check(err)
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tb *TB) checkRows(rows *sql.Rows, err error) *sql.Rows {
|
||||||
|
tb.check(err)
|
||||||
|
return rows
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tb *TB) checkStmt(stmt *sql.Stmt, err error) *sql.Stmt {
|
||||||
|
tb.check(err)
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func initDB(b *testing.B, queries ...string) *sql.DB {
|
||||||
|
tb := (*TB)(b)
|
||||||
|
db := tb.checkDB(sql.Open("mysql", dsn))
|
||||||
|
for _, query := range queries {
|
||||||
|
if _, err := db.Exec(query); err != nil {
|
||||||
|
b.Fatalf("error on %q: %v", query, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return db
|
||||||
|
}
|
||||||
|
|
||||||
|
const concurrencyLevel = 10
|
||||||
|
|
||||||
|
func BenchmarkQuery(b *testing.B) {
|
||||||
|
tb := (*TB)(b)
|
||||||
|
b.StopTimer()
|
||||||
|
b.ReportAllocs()
|
||||||
|
db := initDB(b,
|
||||||
|
"DROP TABLE IF EXISTS foo",
|
||||||
|
"CREATE TABLE foo (id INT PRIMARY KEY, val CHAR(50))",
|
||||||
|
`INSERT INTO foo VALUES (1, "one")`,
|
||||||
|
`INSERT INTO foo VALUES (2, "two")`,
|
||||||
|
)
|
||||||
|
db.SetMaxIdleConns(concurrencyLevel)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
stmt := tb.checkStmt(db.Prepare("SELECT val FROM foo WHERE id=?"))
|
||||||
|
defer stmt.Close()
|
||||||
|
|
||||||
|
remain := int64(b.N)
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(concurrencyLevel)
|
||||||
|
defer wg.Wait()
|
||||||
|
b.StartTimer()
|
||||||
|
|
||||||
|
for i := 0; i < concurrencyLevel; i++ {
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
if atomic.AddInt64(&remain, -1) < 0 {
|
||||||
|
wg.Done()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var got string
|
||||||
|
tb.check(stmt.QueryRow(1).Scan(&got))
|
||||||
|
if got != "one" {
|
||||||
|
b.Errorf("query = %q; want one", got)
|
||||||
|
wg.Done()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkExec(b *testing.B) {
|
||||||
|
tb := (*TB)(b)
|
||||||
|
b.StopTimer()
|
||||||
|
b.ReportAllocs()
|
||||||
|
db := tb.checkDB(sql.Open("mysql", dsn))
|
||||||
|
db.SetMaxIdleConns(concurrencyLevel)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
stmt := tb.checkStmt(db.Prepare("DO 1"))
|
||||||
|
defer stmt.Close()
|
||||||
|
|
||||||
|
remain := int64(b.N)
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(concurrencyLevel)
|
||||||
|
defer wg.Wait()
|
||||||
|
b.StartTimer()
|
||||||
|
|
||||||
|
for i := 0; i < concurrencyLevel; i++ {
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
if atomic.AddInt64(&remain, -1) < 0 {
|
||||||
|
wg.Done()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := stmt.Exec(); err != nil {
|
||||||
|
b.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// data, but no db writes
|
||||||
|
var roundtripSample []byte
|
||||||
|
|
||||||
|
func initRoundtripBenchmarks() ([]byte, int, int) {
|
||||||
|
if roundtripSample == nil {
|
||||||
|
roundtripSample = []byte(strings.Repeat("0123456789abcdef", 1024*1024))
|
||||||
|
}
|
||||||
|
return roundtripSample, 16, len(roundtripSample)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkRoundtripTxt(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
sample, min, max := initRoundtripBenchmarks()
|
||||||
|
sampleString := string(sample)
|
||||||
|
b.ReportAllocs()
|
||||||
|
tb := (*TB)(b)
|
||||||
|
db := tb.checkDB(sql.Open("mysql", dsn))
|
||||||
|
defer db.Close()
|
||||||
|
b.StartTimer()
|
||||||
|
var result string
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
length := min + i
|
||||||
|
if length > max {
|
||||||
|
length = max
|
||||||
|
}
|
||||||
|
test := sampleString[0:length]
|
||||||
|
rows := tb.checkRows(db.Query(`SELECT "` + test + `"`))
|
||||||
|
if !rows.Next() {
|
||||||
|
rows.Close()
|
||||||
|
b.Fatalf("crashed")
|
||||||
|
}
|
||||||
|
err := rows.Scan(&result)
|
||||||
|
if err != nil {
|
||||||
|
rows.Close()
|
||||||
|
b.Fatalf("crashed")
|
||||||
|
}
|
||||||
|
if result != test {
|
||||||
|
rows.Close()
|
||||||
|
b.Errorf("mismatch")
|
||||||
|
}
|
||||||
|
rows.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkRoundtripBin(b *testing.B) {
|
||||||
|
b.StopTimer()
|
||||||
|
sample, min, max := initRoundtripBenchmarks()
|
||||||
|
b.ReportAllocs()
|
||||||
|
tb := (*TB)(b)
|
||||||
|
db := tb.checkDB(sql.Open("mysql", dsn))
|
||||||
|
defer db.Close()
|
||||||
|
stmt := tb.checkStmt(db.Prepare("SELECT ?"))
|
||||||
|
defer stmt.Close()
|
||||||
|
b.StartTimer()
|
||||||
|
var result sql.RawBytes
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
length := min + i
|
||||||
|
if length > max {
|
||||||
|
length = max
|
||||||
|
}
|
||||||
|
test := sample[0:length]
|
||||||
|
rows := tb.checkRows(stmt.Query(test))
|
||||||
|
if !rows.Next() {
|
||||||
|
rows.Close()
|
||||||
|
b.Fatalf("crashed")
|
||||||
|
}
|
||||||
|
err := rows.Scan(&result)
|
||||||
|
if err != nil {
|
||||||
|
rows.Close()
|
||||||
|
b.Fatalf("crashed")
|
||||||
|
}
|
||||||
|
if !bytes.Equal(result, test) {
|
||||||
|
rows.Close()
|
||||||
|
b.Errorf("mismatch")
|
||||||
|
}
|
||||||
|
rows.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkInterpolation(b *testing.B) {
|
||||||
|
mc := &mysqlConn{
|
||||||
|
cfg: &Config{
|
||||||
|
InterpolateParams: true,
|
||||||
|
Loc: time.UTC,
|
||||||
|
},
|
||||||
|
maxAllowedPacket: maxPacketSize,
|
||||||
|
maxWriteSize: maxPacketSize - 1,
|
||||||
|
buf: newBuffer(nil),
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []driver.Value{
|
||||||
|
int64(42424242),
|
||||||
|
float64(math.Pi),
|
||||||
|
false,
|
||||||
|
time.Unix(1423411542, 807015000),
|
||||||
|
[]byte("bytes containing special chars ' \" \a \x00"),
|
||||||
|
"string containing special chars ' \" \a \x00",
|
||||||
|
}
|
||||||
|
q := "SELECT ?, ?, ?, ?, ?, ?"
|
||||||
|
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
_, err := mc.interpolateParams(q, args)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func benchmarkQueryContext(b *testing.B, db *sql.DB, p int) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
db.SetMaxIdleConns(p * runtime.GOMAXPROCS(0))
|
||||||
|
|
||||||
|
tb := (*TB)(b)
|
||||||
|
stmt := tb.checkStmt(db.PrepareContext(ctx, "SELECT val FROM foo WHERE id=?"))
|
||||||
|
defer stmt.Close()
|
||||||
|
|
||||||
|
b.SetParallelism(p)
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.ResetTimer()
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
var got string
|
||||||
|
for pb.Next() {
|
||||||
|
tb.check(stmt.QueryRow(1).Scan(&got))
|
||||||
|
if got != "one" {
|
||||||
|
b.Fatalf("query = %q; want one", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkQueryContext(b *testing.B) {
|
||||||
|
db := initDB(b,
|
||||||
|
"DROP TABLE IF EXISTS foo",
|
||||||
|
"CREATE TABLE foo (id INT PRIMARY KEY, val CHAR(50))",
|
||||||
|
`INSERT INTO foo VALUES (1, "one")`,
|
||||||
|
`INSERT INTO foo VALUES (2, "two")`,
|
||||||
|
)
|
||||||
|
defer db.Close()
|
||||||
|
for _, p := range []int{1, 2, 3, 4} {
|
||||||
|
b.Run(fmt.Sprintf("%d", p), func(b *testing.B) {
|
||||||
|
benchmarkQueryContext(b, db, p)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func benchmarkExecContext(b *testing.B, db *sql.DB, p int) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
db.SetMaxIdleConns(p * runtime.GOMAXPROCS(0))
|
||||||
|
|
||||||
|
tb := (*TB)(b)
|
||||||
|
stmt := tb.checkStmt(db.PrepareContext(ctx, "DO 1"))
|
||||||
|
defer stmt.Close()
|
||||||
|
|
||||||
|
b.SetParallelism(p)
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.ResetTimer()
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
if _, err := stmt.ExecContext(ctx); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkExecContext(b *testing.B) {
|
||||||
|
db := initDB(b,
|
||||||
|
"DROP TABLE IF EXISTS foo",
|
||||||
|
"CREATE TABLE foo (id INT PRIMARY KEY, val CHAR(50))",
|
||||||
|
`INSERT INTO foo VALUES (1, "one")`,
|
||||||
|
`INSERT INTO foo VALUES (2, "two")`,
|
||||||
|
)
|
||||||
|
defer db.Close()
|
||||||
|
for _, p := range []int{1, 2, 3, 4} {
|
||||||
|
b.Run(fmt.Sprintf("%d", p), func(b *testing.B) {
|
||||||
|
benchmarkQueryContext(b, db, p)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkQueryRawBytes benchmarks fetching 100 blobs using sql.RawBytes.
|
||||||
|
// "size=" means size of each blobs.
|
||||||
|
func BenchmarkQueryRawBytes(b *testing.B) {
|
||||||
|
var sizes []int = []int{100, 1000, 2000, 4000, 8000, 12000, 16000, 32000, 64000, 256000}
|
||||||
|
db := initDB(b,
|
||||||
|
"DROP TABLE IF EXISTS bench_rawbytes",
|
||||||
|
"CREATE TABLE bench_rawbytes (id INT PRIMARY KEY, val LONGBLOB)",
|
||||||
|
)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
blob := make([]byte, sizes[len(sizes)-1])
|
||||||
|
for i := range blob {
|
||||||
|
blob[i] = 42
|
||||||
|
}
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
_, err := db.Exec("INSERT INTO bench_rawbytes VALUES (?, ?)", i, blob)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range sizes {
|
||||||
|
b.Run(fmt.Sprintf("size=%v", s), func(b *testing.B) {
|
||||||
|
db.SetMaxIdleConns(0)
|
||||||
|
db.SetMaxIdleConns(1)
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
for j := 0; j < b.N; j++ {
|
||||||
|
rows, err := db.Query("SELECT LEFT(val, ?) as v FROM bench_rawbytes", s)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
nrows := 0
|
||||||
|
for rows.Next() {
|
||||||
|
var buf sql.RawBytes
|
||||||
|
err := rows.Scan(&buf)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(buf) != s {
|
||||||
|
b.Fatalf("size mismatch: expected %v, got %v", s, len(buf))
|
||||||
|
}
|
||||||
|
nrows++
|
||||||
|
}
|
||||||
|
rows.Close()
|
||||||
|
if nrows != 100 {
|
||||||
|
b.Fatalf("numbers of rows mismatch: expected %v, got %v", 100, nrows)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
97
vendor/github.com/go-sql-driver/mysql/buffer.go
generated
vendored
97
vendor/github.com/go-sql-driver/mysql/buffer.go
generated
vendored
|
@ -15,47 +15,69 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultBufSize = 4096
|
const defaultBufSize = 4096
|
||||||
|
const maxCachedBufSize = 256 * 1024
|
||||||
|
|
||||||
// A buffer which is used for both reading and writing.
|
// A buffer which is used for both reading and writing.
|
||||||
// This is possible since communication on each connection is synchronous.
|
// This is possible since communication on each connection is synchronous.
|
||||||
// In other words, we can't write and read simultaneously on the same connection.
|
// In other words, we can't write and read simultaneously on the same connection.
|
||||||
// The buffer is similar to bufio.Reader / Writer but zero-copy-ish
|
// The buffer is similar to bufio.Reader / Writer but zero-copy-ish
|
||||||
// Also highly optimized for this particular use case.
|
// Also highly optimized for this particular use case.
|
||||||
|
// This buffer is backed by two byte slices in a double-buffering scheme
|
||||||
type buffer struct {
|
type buffer struct {
|
||||||
buf []byte
|
buf []byte // buf is a byte buffer who's length and capacity are equal.
|
||||||
nc net.Conn
|
nc net.Conn
|
||||||
idx int
|
idx int
|
||||||
length int
|
length int
|
||||||
timeout time.Duration
|
timeout time.Duration
|
||||||
|
dbuf [2][]byte // dbuf is an array with the two byte slices that back this buffer
|
||||||
|
flipcnt uint // flipccnt is the current buffer counter for double-buffering
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newBuffer allocates and returns a new buffer.
|
||||||
func newBuffer(nc net.Conn) buffer {
|
func newBuffer(nc net.Conn) buffer {
|
||||||
var b [defaultBufSize]byte
|
fg := make([]byte, defaultBufSize)
|
||||||
return buffer{
|
return buffer{
|
||||||
buf: b[:],
|
buf: fg,
|
||||||
nc: nc,
|
nc: nc,
|
||||||
|
dbuf: [2][]byte{fg, nil},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// flip replaces the active buffer with the background buffer
|
||||||
|
// this is a delayed flip that simply increases the buffer counter;
|
||||||
|
// the actual flip will be performed the next time we call `buffer.fill`
|
||||||
|
func (b *buffer) flip() {
|
||||||
|
b.flipcnt += 1
|
||||||
|
}
|
||||||
|
|
||||||
// fill reads into the buffer until at least _need_ bytes are in it
|
// fill reads into the buffer until at least _need_ bytes are in it
|
||||||
func (b *buffer) fill(need int) error {
|
func (b *buffer) fill(need int) error {
|
||||||
n := b.length
|
n := b.length
|
||||||
|
// fill data into its double-buffering target: if we've called
|
||||||
|
// flip on this buffer, we'll be copying to the background buffer,
|
||||||
|
// and then filling it with network data; otherwise we'll just move
|
||||||
|
// the contents of the current buffer to the front before filling it
|
||||||
|
dest := b.dbuf[b.flipcnt&1]
|
||||||
|
|
||||||
// move existing data to the beginning
|
// grow buffer if necessary to fit the whole packet.
|
||||||
if n > 0 && b.idx > 0 {
|
if need > len(dest) {
|
||||||
copy(b.buf[0:n], b.buf[b.idx:])
|
|
||||||
}
|
|
||||||
|
|
||||||
// grow buffer if necessary
|
|
||||||
// TODO: let the buffer shrink again at some point
|
|
||||||
// Maybe keep the org buf slice and swap back?
|
|
||||||
if need > len(b.buf) {
|
|
||||||
// Round up to the next multiple of the default size
|
// Round up to the next multiple of the default size
|
||||||
newBuf := make([]byte, ((need/defaultBufSize)+1)*defaultBufSize)
|
dest = make([]byte, ((need/defaultBufSize)+1)*defaultBufSize)
|
||||||
copy(newBuf, b.buf)
|
|
||||||
b.buf = newBuf
|
// if the allocated buffer is not too large, move it to backing storage
|
||||||
|
// to prevent extra allocations on applications that perform large reads
|
||||||
|
if len(dest) <= maxCachedBufSize {
|
||||||
|
b.dbuf[b.flipcnt&1] = dest
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if we're filling the fg buffer, move the existing data to the start of it.
|
||||||
|
// if we're filling the bg buffer, copy over the data
|
||||||
|
if n > 0 {
|
||||||
|
copy(dest[:n], b.buf[b.idx:])
|
||||||
|
}
|
||||||
|
|
||||||
|
b.buf = dest
|
||||||
b.idx = 0
|
b.idx = 0
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
@ -105,43 +127,56 @@ func (b *buffer) readNext(need int) ([]byte, error) {
|
||||||
return b.buf[offset:b.idx], nil
|
return b.buf[offset:b.idx], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns a buffer with the requested size.
|
// takeBuffer returns a buffer with the requested size.
|
||||||
// If possible, a slice from the existing buffer is returned.
|
// If possible, a slice from the existing buffer is returned.
|
||||||
// Otherwise a bigger buffer is made.
|
// Otherwise a bigger buffer is made.
|
||||||
// Only one buffer (total) can be used at a time.
|
// Only one buffer (total) can be used at a time.
|
||||||
func (b *buffer) takeBuffer(length int) []byte {
|
func (b *buffer) takeBuffer(length int) ([]byte, error) {
|
||||||
if b.length > 0 {
|
if b.length > 0 {
|
||||||
return nil
|
return nil, ErrBusyBuffer
|
||||||
}
|
}
|
||||||
|
|
||||||
// test (cheap) general case first
|
// test (cheap) general case first
|
||||||
if length <= defaultBufSize || length <= cap(b.buf) {
|
if length <= cap(b.buf) {
|
||||||
return b.buf[:length]
|
return b.buf[:length], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if length < maxPacketSize {
|
if length < maxPacketSize {
|
||||||
b.buf = make([]byte, length)
|
b.buf = make([]byte, length)
|
||||||
return b.buf
|
return b.buf, nil
|
||||||
}
|
}
|
||||||
return make([]byte, length)
|
|
||||||
|
// buffer is larger than we want to store.
|
||||||
|
return make([]byte, length), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// shortcut which can be used if the requested buffer is guaranteed to be
|
// takeSmallBuffer is shortcut which can be used if length is
|
||||||
// smaller than defaultBufSize
|
// known to be smaller than defaultBufSize.
|
||||||
// Only one buffer (total) can be used at a time.
|
// Only one buffer (total) can be used at a time.
|
||||||
func (b *buffer) takeSmallBuffer(length int) []byte {
|
func (b *buffer) takeSmallBuffer(length int) ([]byte, error) {
|
||||||
if b.length > 0 {
|
if b.length > 0 {
|
||||||
return nil
|
return nil, ErrBusyBuffer
|
||||||
}
|
}
|
||||||
return b.buf[:length]
|
return b.buf[:length], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// takeCompleteBuffer returns the complete existing buffer.
|
// takeCompleteBuffer returns the complete existing buffer.
|
||||||
// This can be used if the necessary buffer size is unknown.
|
// This can be used if the necessary buffer size is unknown.
|
||||||
|
// cap and len of the returned buffer will be equal.
|
||||||
// Only one buffer (total) can be used at a time.
|
// Only one buffer (total) can be used at a time.
|
||||||
func (b *buffer) takeCompleteBuffer() []byte {
|
func (b *buffer) takeCompleteBuffer() ([]byte, error) {
|
||||||
if b.length > 0 {
|
if b.length > 0 {
|
||||||
return nil
|
return nil, ErrBusyBuffer
|
||||||
}
|
}
|
||||||
return b.buf
|
return b.buf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// store stores buf, an updated buffer, if its suitable to do so.
|
||||||
|
func (b *buffer) store(buf []byte) error {
|
||||||
|
if b.length > 0 {
|
||||||
|
return ErrBusyBuffer
|
||||||
|
} else if cap(buf) <= maxPacketSize && cap(buf) > cap(b.buf) {
|
||||||
|
b.buf = buf[:cap(buf)]
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
376
vendor/github.com/go-sql-driver/mysql/collations.go
generated
vendored
376
vendor/github.com/go-sql-driver/mysql/collations.go
generated
vendored
|
@ -8,183 +8,190 @@
|
||||||
|
|
||||||
package mysql
|
package mysql
|
||||||
|
|
||||||
const defaultCollation = "utf8_general_ci"
|
const defaultCollation = "utf8mb4_general_ci"
|
||||||
const binaryCollation = "binary"
|
const binaryCollation = "binary"
|
||||||
|
|
||||||
// A list of available collations mapped to the internal ID.
|
// A list of available collations mapped to the internal ID.
|
||||||
// To update this map use the following MySQL query:
|
// To update this map use the following MySQL query:
|
||||||
// SELECT COLLATION_NAME, ID FROM information_schema.COLLATIONS
|
// SELECT COLLATION_NAME, ID FROM information_schema.COLLATIONS WHERE ID<256 ORDER BY ID
|
||||||
|
//
|
||||||
|
// Handshake packet have only 1 byte for collation_id. So we can't use collations with ID > 255.
|
||||||
|
//
|
||||||
|
// ucs2, utf16, and utf32 can't be used for connection charset.
|
||||||
|
// https://dev.mysql.com/doc/refman/5.7/en/charset-connection.html#charset-connection-impermissible-client-charset
|
||||||
|
// They are commented out to reduce this map.
|
||||||
var collations = map[string]byte{
|
var collations = map[string]byte{
|
||||||
"big5_chinese_ci": 1,
|
"big5_chinese_ci": 1,
|
||||||
"latin2_czech_cs": 2,
|
"latin2_czech_cs": 2,
|
||||||
"dec8_swedish_ci": 3,
|
"dec8_swedish_ci": 3,
|
||||||
"cp850_general_ci": 4,
|
"cp850_general_ci": 4,
|
||||||
"latin1_german1_ci": 5,
|
"latin1_german1_ci": 5,
|
||||||
"hp8_english_ci": 6,
|
"hp8_english_ci": 6,
|
||||||
"koi8r_general_ci": 7,
|
"koi8r_general_ci": 7,
|
||||||
"latin1_swedish_ci": 8,
|
"latin1_swedish_ci": 8,
|
||||||
"latin2_general_ci": 9,
|
"latin2_general_ci": 9,
|
||||||
"swe7_swedish_ci": 10,
|
"swe7_swedish_ci": 10,
|
||||||
"ascii_general_ci": 11,
|
"ascii_general_ci": 11,
|
||||||
"ujis_japanese_ci": 12,
|
"ujis_japanese_ci": 12,
|
||||||
"sjis_japanese_ci": 13,
|
"sjis_japanese_ci": 13,
|
||||||
"cp1251_bulgarian_ci": 14,
|
"cp1251_bulgarian_ci": 14,
|
||||||
"latin1_danish_ci": 15,
|
"latin1_danish_ci": 15,
|
||||||
"hebrew_general_ci": 16,
|
"hebrew_general_ci": 16,
|
||||||
"tis620_thai_ci": 18,
|
"tis620_thai_ci": 18,
|
||||||
"euckr_korean_ci": 19,
|
"euckr_korean_ci": 19,
|
||||||
"latin7_estonian_cs": 20,
|
"latin7_estonian_cs": 20,
|
||||||
"latin2_hungarian_ci": 21,
|
"latin2_hungarian_ci": 21,
|
||||||
"koi8u_general_ci": 22,
|
"koi8u_general_ci": 22,
|
||||||
"cp1251_ukrainian_ci": 23,
|
"cp1251_ukrainian_ci": 23,
|
||||||
"gb2312_chinese_ci": 24,
|
"gb2312_chinese_ci": 24,
|
||||||
"greek_general_ci": 25,
|
"greek_general_ci": 25,
|
||||||
"cp1250_general_ci": 26,
|
"cp1250_general_ci": 26,
|
||||||
"latin2_croatian_ci": 27,
|
"latin2_croatian_ci": 27,
|
||||||
"gbk_chinese_ci": 28,
|
"gbk_chinese_ci": 28,
|
||||||
"cp1257_lithuanian_ci": 29,
|
"cp1257_lithuanian_ci": 29,
|
||||||
"latin5_turkish_ci": 30,
|
"latin5_turkish_ci": 30,
|
||||||
"latin1_german2_ci": 31,
|
"latin1_german2_ci": 31,
|
||||||
"armscii8_general_ci": 32,
|
"armscii8_general_ci": 32,
|
||||||
"utf8_general_ci": 33,
|
"utf8_general_ci": 33,
|
||||||
"cp1250_czech_cs": 34,
|
"cp1250_czech_cs": 34,
|
||||||
"ucs2_general_ci": 35,
|
//"ucs2_general_ci": 35,
|
||||||
"cp866_general_ci": 36,
|
"cp866_general_ci": 36,
|
||||||
"keybcs2_general_ci": 37,
|
"keybcs2_general_ci": 37,
|
||||||
"macce_general_ci": 38,
|
"macce_general_ci": 38,
|
||||||
"macroman_general_ci": 39,
|
"macroman_general_ci": 39,
|
||||||
"cp852_general_ci": 40,
|
"cp852_general_ci": 40,
|
||||||
"latin7_general_ci": 41,
|
"latin7_general_ci": 41,
|
||||||
"latin7_general_cs": 42,
|
"latin7_general_cs": 42,
|
||||||
"macce_bin": 43,
|
"macce_bin": 43,
|
||||||
"cp1250_croatian_ci": 44,
|
"cp1250_croatian_ci": 44,
|
||||||
"utf8mb4_general_ci": 45,
|
"utf8mb4_general_ci": 45,
|
||||||
"utf8mb4_bin": 46,
|
"utf8mb4_bin": 46,
|
||||||
"latin1_bin": 47,
|
"latin1_bin": 47,
|
||||||
"latin1_general_ci": 48,
|
"latin1_general_ci": 48,
|
||||||
"latin1_general_cs": 49,
|
"latin1_general_cs": 49,
|
||||||
"cp1251_bin": 50,
|
"cp1251_bin": 50,
|
||||||
"cp1251_general_ci": 51,
|
"cp1251_general_ci": 51,
|
||||||
"cp1251_general_cs": 52,
|
"cp1251_general_cs": 52,
|
||||||
"macroman_bin": 53,
|
"macroman_bin": 53,
|
||||||
"utf16_general_ci": 54,
|
//"utf16_general_ci": 54,
|
||||||
"utf16_bin": 55,
|
//"utf16_bin": 55,
|
||||||
"utf16le_general_ci": 56,
|
//"utf16le_general_ci": 56,
|
||||||
"cp1256_general_ci": 57,
|
"cp1256_general_ci": 57,
|
||||||
"cp1257_bin": 58,
|
"cp1257_bin": 58,
|
||||||
"cp1257_general_ci": 59,
|
"cp1257_general_ci": 59,
|
||||||
"utf32_general_ci": 60,
|
//"utf32_general_ci": 60,
|
||||||
"utf32_bin": 61,
|
//"utf32_bin": 61,
|
||||||
"utf16le_bin": 62,
|
//"utf16le_bin": 62,
|
||||||
"binary": 63,
|
"binary": 63,
|
||||||
"armscii8_bin": 64,
|
"armscii8_bin": 64,
|
||||||
"ascii_bin": 65,
|
"ascii_bin": 65,
|
||||||
"cp1250_bin": 66,
|
"cp1250_bin": 66,
|
||||||
"cp1256_bin": 67,
|
"cp1256_bin": 67,
|
||||||
"cp866_bin": 68,
|
"cp866_bin": 68,
|
||||||
"dec8_bin": 69,
|
"dec8_bin": 69,
|
||||||
"greek_bin": 70,
|
"greek_bin": 70,
|
||||||
"hebrew_bin": 71,
|
"hebrew_bin": 71,
|
||||||
"hp8_bin": 72,
|
"hp8_bin": 72,
|
||||||
"keybcs2_bin": 73,
|
"keybcs2_bin": 73,
|
||||||
"koi8r_bin": 74,
|
"koi8r_bin": 74,
|
||||||
"koi8u_bin": 75,
|
"koi8u_bin": 75,
|
||||||
"latin2_bin": 77,
|
"utf8_tolower_ci": 76,
|
||||||
"latin5_bin": 78,
|
"latin2_bin": 77,
|
||||||
"latin7_bin": 79,
|
"latin5_bin": 78,
|
||||||
"cp850_bin": 80,
|
"latin7_bin": 79,
|
||||||
"cp852_bin": 81,
|
"cp850_bin": 80,
|
||||||
"swe7_bin": 82,
|
"cp852_bin": 81,
|
||||||
"utf8_bin": 83,
|
"swe7_bin": 82,
|
||||||
"big5_bin": 84,
|
"utf8_bin": 83,
|
||||||
"euckr_bin": 85,
|
"big5_bin": 84,
|
||||||
"gb2312_bin": 86,
|
"euckr_bin": 85,
|
||||||
"gbk_bin": 87,
|
"gb2312_bin": 86,
|
||||||
"sjis_bin": 88,
|
"gbk_bin": 87,
|
||||||
"tis620_bin": 89,
|
"sjis_bin": 88,
|
||||||
"ucs2_bin": 90,
|
"tis620_bin": 89,
|
||||||
"ujis_bin": 91,
|
//"ucs2_bin": 90,
|
||||||
"geostd8_general_ci": 92,
|
"ujis_bin": 91,
|
||||||
"geostd8_bin": 93,
|
"geostd8_general_ci": 92,
|
||||||
"latin1_spanish_ci": 94,
|
"geostd8_bin": 93,
|
||||||
"cp932_japanese_ci": 95,
|
"latin1_spanish_ci": 94,
|
||||||
"cp932_bin": 96,
|
"cp932_japanese_ci": 95,
|
||||||
"eucjpms_japanese_ci": 97,
|
"cp932_bin": 96,
|
||||||
"eucjpms_bin": 98,
|
"eucjpms_japanese_ci": 97,
|
||||||
"cp1250_polish_ci": 99,
|
"eucjpms_bin": 98,
|
||||||
"utf16_unicode_ci": 101,
|
"cp1250_polish_ci": 99,
|
||||||
"utf16_icelandic_ci": 102,
|
//"utf16_unicode_ci": 101,
|
||||||
"utf16_latvian_ci": 103,
|
//"utf16_icelandic_ci": 102,
|
||||||
"utf16_romanian_ci": 104,
|
//"utf16_latvian_ci": 103,
|
||||||
"utf16_slovenian_ci": 105,
|
//"utf16_romanian_ci": 104,
|
||||||
"utf16_polish_ci": 106,
|
//"utf16_slovenian_ci": 105,
|
||||||
"utf16_estonian_ci": 107,
|
//"utf16_polish_ci": 106,
|
||||||
"utf16_spanish_ci": 108,
|
//"utf16_estonian_ci": 107,
|
||||||
"utf16_swedish_ci": 109,
|
//"utf16_spanish_ci": 108,
|
||||||
"utf16_turkish_ci": 110,
|
//"utf16_swedish_ci": 109,
|
||||||
"utf16_czech_ci": 111,
|
//"utf16_turkish_ci": 110,
|
||||||
"utf16_danish_ci": 112,
|
//"utf16_czech_ci": 111,
|
||||||
"utf16_lithuanian_ci": 113,
|
//"utf16_danish_ci": 112,
|
||||||
"utf16_slovak_ci": 114,
|
//"utf16_lithuanian_ci": 113,
|
||||||
"utf16_spanish2_ci": 115,
|
//"utf16_slovak_ci": 114,
|
||||||
"utf16_roman_ci": 116,
|
//"utf16_spanish2_ci": 115,
|
||||||
"utf16_persian_ci": 117,
|
//"utf16_roman_ci": 116,
|
||||||
"utf16_esperanto_ci": 118,
|
//"utf16_persian_ci": 117,
|
||||||
"utf16_hungarian_ci": 119,
|
//"utf16_esperanto_ci": 118,
|
||||||
"utf16_sinhala_ci": 120,
|
//"utf16_hungarian_ci": 119,
|
||||||
"utf16_german2_ci": 121,
|
//"utf16_sinhala_ci": 120,
|
||||||
"utf16_croatian_ci": 122,
|
//"utf16_german2_ci": 121,
|
||||||
"utf16_unicode_520_ci": 123,
|
//"utf16_croatian_ci": 122,
|
||||||
"utf16_vietnamese_ci": 124,
|
//"utf16_unicode_520_ci": 123,
|
||||||
"ucs2_unicode_ci": 128,
|
//"utf16_vietnamese_ci": 124,
|
||||||
"ucs2_icelandic_ci": 129,
|
//"ucs2_unicode_ci": 128,
|
||||||
"ucs2_latvian_ci": 130,
|
//"ucs2_icelandic_ci": 129,
|
||||||
"ucs2_romanian_ci": 131,
|
//"ucs2_latvian_ci": 130,
|
||||||
"ucs2_slovenian_ci": 132,
|
//"ucs2_romanian_ci": 131,
|
||||||
"ucs2_polish_ci": 133,
|
//"ucs2_slovenian_ci": 132,
|
||||||
"ucs2_estonian_ci": 134,
|
//"ucs2_polish_ci": 133,
|
||||||
"ucs2_spanish_ci": 135,
|
//"ucs2_estonian_ci": 134,
|
||||||
"ucs2_swedish_ci": 136,
|
//"ucs2_spanish_ci": 135,
|
||||||
"ucs2_turkish_ci": 137,
|
//"ucs2_swedish_ci": 136,
|
||||||
"ucs2_czech_ci": 138,
|
//"ucs2_turkish_ci": 137,
|
||||||
"ucs2_danish_ci": 139,
|
//"ucs2_czech_ci": 138,
|
||||||
"ucs2_lithuanian_ci": 140,
|
//"ucs2_danish_ci": 139,
|
||||||
"ucs2_slovak_ci": 141,
|
//"ucs2_lithuanian_ci": 140,
|
||||||
"ucs2_spanish2_ci": 142,
|
//"ucs2_slovak_ci": 141,
|
||||||
"ucs2_roman_ci": 143,
|
//"ucs2_spanish2_ci": 142,
|
||||||
"ucs2_persian_ci": 144,
|
//"ucs2_roman_ci": 143,
|
||||||
"ucs2_esperanto_ci": 145,
|
//"ucs2_persian_ci": 144,
|
||||||
"ucs2_hungarian_ci": 146,
|
//"ucs2_esperanto_ci": 145,
|
||||||
"ucs2_sinhala_ci": 147,
|
//"ucs2_hungarian_ci": 146,
|
||||||
"ucs2_german2_ci": 148,
|
//"ucs2_sinhala_ci": 147,
|
||||||
"ucs2_croatian_ci": 149,
|
//"ucs2_german2_ci": 148,
|
||||||
"ucs2_unicode_520_ci": 150,
|
//"ucs2_croatian_ci": 149,
|
||||||
"ucs2_vietnamese_ci": 151,
|
//"ucs2_unicode_520_ci": 150,
|
||||||
"ucs2_general_mysql500_ci": 159,
|
//"ucs2_vietnamese_ci": 151,
|
||||||
"utf32_unicode_ci": 160,
|
//"ucs2_general_mysql500_ci": 159,
|
||||||
"utf32_icelandic_ci": 161,
|
//"utf32_unicode_ci": 160,
|
||||||
"utf32_latvian_ci": 162,
|
//"utf32_icelandic_ci": 161,
|
||||||
"utf32_romanian_ci": 163,
|
//"utf32_latvian_ci": 162,
|
||||||
"utf32_slovenian_ci": 164,
|
//"utf32_romanian_ci": 163,
|
||||||
"utf32_polish_ci": 165,
|
//"utf32_slovenian_ci": 164,
|
||||||
"utf32_estonian_ci": 166,
|
//"utf32_polish_ci": 165,
|
||||||
"utf32_spanish_ci": 167,
|
//"utf32_estonian_ci": 166,
|
||||||
"utf32_swedish_ci": 168,
|
//"utf32_spanish_ci": 167,
|
||||||
"utf32_turkish_ci": 169,
|
//"utf32_swedish_ci": 168,
|
||||||
"utf32_czech_ci": 170,
|
//"utf32_turkish_ci": 169,
|
||||||
"utf32_danish_ci": 171,
|
//"utf32_czech_ci": 170,
|
||||||
"utf32_lithuanian_ci": 172,
|
//"utf32_danish_ci": 171,
|
||||||
"utf32_slovak_ci": 173,
|
//"utf32_lithuanian_ci": 172,
|
||||||
"utf32_spanish2_ci": 174,
|
//"utf32_slovak_ci": 173,
|
||||||
"utf32_roman_ci": 175,
|
//"utf32_spanish2_ci": 174,
|
||||||
"utf32_persian_ci": 176,
|
//"utf32_roman_ci": 175,
|
||||||
"utf32_esperanto_ci": 177,
|
//"utf32_persian_ci": 176,
|
||||||
"utf32_hungarian_ci": 178,
|
//"utf32_esperanto_ci": 177,
|
||||||
"utf32_sinhala_ci": 179,
|
//"utf32_hungarian_ci": 178,
|
||||||
"utf32_german2_ci": 180,
|
//"utf32_sinhala_ci": 179,
|
||||||
"utf32_croatian_ci": 181,
|
//"utf32_german2_ci": 180,
|
||||||
"utf32_unicode_520_ci": 182,
|
//"utf32_croatian_ci": 181,
|
||||||
"utf32_vietnamese_ci": 183,
|
//"utf32_unicode_520_ci": 182,
|
||||||
|
//"utf32_vietnamese_ci": 183,
|
||||||
"utf8_unicode_ci": 192,
|
"utf8_unicode_ci": 192,
|
||||||
"utf8_icelandic_ci": 193,
|
"utf8_icelandic_ci": 193,
|
||||||
"utf8_latvian_ci": 194,
|
"utf8_latvian_ci": 194,
|
||||||
|
@ -234,18 +241,25 @@ var collations = map[string]byte{
|
||||||
"utf8mb4_croatian_ci": 245,
|
"utf8mb4_croatian_ci": 245,
|
||||||
"utf8mb4_unicode_520_ci": 246,
|
"utf8mb4_unicode_520_ci": 246,
|
||||||
"utf8mb4_vietnamese_ci": 247,
|
"utf8mb4_vietnamese_ci": 247,
|
||||||
|
"gb18030_chinese_ci": 248,
|
||||||
|
"gb18030_bin": 249,
|
||||||
|
"gb18030_unicode_520_ci": 250,
|
||||||
|
"utf8mb4_0900_ai_ci": 255,
|
||||||
}
|
}
|
||||||
|
|
||||||
// A blacklist of collations which is unsafe to interpolate parameters.
|
// A blacklist of collations which is unsafe to interpolate parameters.
|
||||||
// These multibyte encodings may contains 0x5c (`\`) in their trailing bytes.
|
// These multibyte encodings may contains 0x5c (`\`) in their trailing bytes.
|
||||||
var unsafeCollations = map[string]bool{
|
var unsafeCollations = map[string]bool{
|
||||||
"big5_chinese_ci": true,
|
"big5_chinese_ci": true,
|
||||||
"sjis_japanese_ci": true,
|
"sjis_japanese_ci": true,
|
||||||
"gbk_chinese_ci": true,
|
"gbk_chinese_ci": true,
|
||||||
"big5_bin": true,
|
"big5_bin": true,
|
||||||
"gb2312_bin": true,
|
"gb2312_bin": true,
|
||||||
"gbk_bin": true,
|
"gbk_bin": true,
|
||||||
"sjis_bin": true,
|
"sjis_bin": true,
|
||||||
"cp932_japanese_ci": true,
|
"cp932_japanese_ci": true,
|
||||||
"cp932_bin": true,
|
"cp932_bin": true,
|
||||||
|
"gb18030_chinese_ci": true,
|
||||||
|
"gb18030_bin": true,
|
||||||
|
"gb18030_unicode_520_ci": true,
|
||||||
}
|
}
|
||||||
|
|
53
vendor/github.com/go-sql-driver/mysql/conncheck.go
generated
vendored
Normal file
53
vendor/github.com/go-sql-driver/mysql/conncheck.go
generated
vendored
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||||
|
//
|
||||||
|
// Copyright 2019 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 !windows,!appengine
|
||||||
|
|
||||||
|
package mysql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errUnexpectedRead = errors.New("unexpected read from socket")
|
||||||
|
|
||||||
|
func connCheck(c net.Conn) error {
|
||||||
|
var (
|
||||||
|
n int
|
||||||
|
err error
|
||||||
|
buff [1]byte
|
||||||
|
)
|
||||||
|
|
||||||
|
sconn, ok := c.(syscall.Conn)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
rc, err := sconn.SyscallConn()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
rerr := rc.Read(func(fd uintptr) bool {
|
||||||
|
n, err = syscall.Read(int(fd), buff[:])
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
switch {
|
||||||
|
case rerr != nil:
|
||||||
|
return rerr
|
||||||
|
case n == 0 && err == nil:
|
||||||
|
return io.EOF
|
||||||
|
case n > 0:
|
||||||
|
return errUnexpectedRead
|
||||||
|
case err == syscall.EAGAIN || err == syscall.EWOULDBLOCK:
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
17
vendor/github.com/go-sql-driver/mysql/conncheck_dummy.go
generated
vendored
Normal file
17
vendor/github.com/go-sql-driver/mysql/conncheck_dummy.go
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||||
|
//
|
||||||
|
// Copyright 2019 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 windows appengine
|
||||||
|
|
||||||
|
package mysql
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
|
func connCheck(c net.Conn) error {
|
||||||
|
return nil
|
||||||
|
}
|
38
vendor/github.com/go-sql-driver/mysql/conncheck_test.go
generated
vendored
Normal file
38
vendor/github.com/go-sql-driver/mysql/conncheck_test.go
generated
vendored
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||||
|
//
|
||||||
|
// Copyright 2013 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.10,!windows
|
||||||
|
|
||||||
|
package mysql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStaleConnectionChecks(t *testing.T) {
|
||||||
|
runTests(t, dsn, func(dbt *DBTest) {
|
||||||
|
dbt.mustExec("SET @@SESSION.wait_timeout = 2")
|
||||||
|
|
||||||
|
if err := dbt.db.Ping(); err != nil {
|
||||||
|
dbt.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for MySQL to close our connection
|
||||||
|
time.Sleep(3 * time.Second)
|
||||||
|
|
||||||
|
tx, err := dbt.db.Begin()
|
||||||
|
if err != nil {
|
||||||
|
dbt.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Rollback(); err != nil {
|
||||||
|
dbt.Fatal(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
216
vendor/github.com/go-sql-driver/mysql/connection.go
generated
vendored
216
vendor/github.com/go-sql-driver/mysql/connection.go
generated
vendored
|
@ -9,6 +9,8 @@
|
||||||
package mysql
|
package mysql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
"database/sql/driver"
|
"database/sql/driver"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
@ -17,19 +19,10 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// a copy of context.Context for Go 1.7 and earlier
|
|
||||||
type mysqlContext interface {
|
|
||||||
Done() <-chan struct{}
|
|
||||||
Err() error
|
|
||||||
|
|
||||||
// defined in context.Context, but not used in this driver:
|
|
||||||
// Deadline() (deadline time.Time, ok bool)
|
|
||||||
// Value(key interface{}) interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
type mysqlConn struct {
|
type mysqlConn struct {
|
||||||
buf buffer
|
buf buffer
|
||||||
netConn net.Conn
|
netConn net.Conn
|
||||||
|
rawConn net.Conn // underlying connection when netConn is TLS connection.
|
||||||
affectedRows uint64
|
affectedRows uint64
|
||||||
insertId uint64
|
insertId uint64
|
||||||
cfg *Config
|
cfg *Config
|
||||||
|
@ -40,10 +33,11 @@ type mysqlConn struct {
|
||||||
status statusFlag
|
status statusFlag
|
||||||
sequence uint8
|
sequence uint8
|
||||||
parseTime bool
|
parseTime bool
|
||||||
|
reset bool // set when the Go SQL package calls ResetSession
|
||||||
|
|
||||||
// for context support (Go 1.8+)
|
// for context support (Go 1.8+)
|
||||||
watching bool
|
watching bool
|
||||||
watcher chan<- mysqlContext
|
watcher chan<- context.Context
|
||||||
closech chan struct{}
|
closech chan struct{}
|
||||||
finished chan<- struct{}
|
finished chan<- struct{}
|
||||||
canceled atomicError // set non-nil if conn is canceled
|
canceled atomicError // set non-nil if conn is canceled
|
||||||
|
@ -190,10 +184,10 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin
|
||||||
return "", driver.ErrSkip
|
return "", driver.ErrSkip
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := mc.buf.takeCompleteBuffer()
|
buf, err := mc.buf.takeCompleteBuffer()
|
||||||
if buf == nil {
|
if err != nil {
|
||||||
// can not take the buffer. Something must be wrong with the connection
|
// can not take the buffer. Something must be wrong with the connection
|
||||||
errLog.Print(ErrBusyBuffer)
|
errLog.Print(err)
|
||||||
return "", ErrInvalidConn
|
return "", ErrInvalidConn
|
||||||
}
|
}
|
||||||
buf = buf[:0]
|
buf = buf[:0]
|
||||||
|
@ -219,6 +213,9 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin
|
||||||
switch v := arg.(type) {
|
switch v := arg.(type) {
|
||||||
case int64:
|
case int64:
|
||||||
buf = strconv.AppendInt(buf, v, 10)
|
buf = strconv.AppendInt(buf, v, 10)
|
||||||
|
case uint64:
|
||||||
|
// Handle uint64 explicitly because our custom ConvertValue emits unsigned values
|
||||||
|
buf = strconv.AppendUint(buf, v, 10)
|
||||||
case float64:
|
case float64:
|
||||||
buf = strconv.AppendFloat(buf, v, 'g', -1, 64)
|
buf = strconv.AppendFloat(buf, v, 'g', -1, 64)
|
||||||
case bool:
|
case bool:
|
||||||
|
@ -459,3 +456,194 @@ func (mc *mysqlConn) finish() {
|
||||||
case <-mc.closech:
|
case <-mc.closech:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ping implements driver.Pinger interface
|
||||||
|
func (mc *mysqlConn) Ping(ctx context.Context) (err error) {
|
||||||
|
if mc.closed.IsSet() {
|
||||||
|
errLog.Print(ErrInvalidConn)
|
||||||
|
return driver.ErrBadConn
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = mc.watchCancel(ctx); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer mc.finish()
|
||||||
|
|
||||||
|
if err = mc.writeCommandPacket(comPing); err != nil {
|
||||||
|
return mc.markBadConn(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return mc.readResultOK()
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeginTx implements driver.ConnBeginTx interface
|
||||||
|
func (mc *mysqlConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
|
||||||
|
if err := mc.watchCancel(ctx); 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mc.begin(opts.ReadOnly)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc *mysqlConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
|
||||||
|
dargs, err := namedValueToValue(args)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mc.watchCancel(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := mc.query(query, dargs)
|
||||||
|
if err != nil {
|
||||||
|
mc.finish()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rows.finish = mc.finish
|
||||||
|
return rows, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc *mysqlConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
|
||||||
|
dargs, err := namedValueToValue(args)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mc.watchCancel(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer mc.finish()
|
||||||
|
|
||||||
|
return mc.Exec(query, dargs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc *mysqlConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
|
||||||
|
if err := mc.watchCancel(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
stmt, err := mc.Prepare(query)
|
||||||
|
mc.finish()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
default:
|
||||||
|
case <-ctx.Done():
|
||||||
|
stmt.Close()
|
||||||
|
return nil, ctx.Err()
|
||||||
|
}
|
||||||
|
return stmt, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (stmt *mysqlStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
|
||||||
|
dargs, err := namedValueToValue(args)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := stmt.mc.watchCancel(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := stmt.query(dargs)
|
||||||
|
if err != nil {
|
||||||
|
stmt.mc.finish()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rows.finish = stmt.mc.finish
|
||||||
|
return rows, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (stmt *mysqlStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
|
||||||
|
dargs, err := namedValueToValue(args)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := stmt.mc.watchCancel(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer stmt.mc.finish()
|
||||||
|
|
||||||
|
return stmt.Exec(dargs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc *mysqlConn) watchCancel(ctx context.Context) error {
|
||||||
|
if mc.watching {
|
||||||
|
// Reach here if canceled,
|
||||||
|
// so the connection is already invalid
|
||||||
|
mc.cleanup()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// When ctx is already cancelled, don't watch it.
|
||||||
|
if err := ctx.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// When ctx is not cancellable, don't watch it.
|
||||||
|
if ctx.Done() == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// When watcher is not alive, can't watch it.
|
||||||
|
if mc.watcher == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
mc.watching = true
|
||||||
|
mc.watcher <- ctx
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc *mysqlConn) startWatcher() {
|
||||||
|
watcher := make(chan context.Context, 1)
|
||||||
|
mc.watcher = watcher
|
||||||
|
finished := make(chan struct{})
|
||||||
|
mc.finished = finished
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
var ctx context.Context
|
||||||
|
select {
|
||||||
|
case ctx = <-watcher:
|
||||||
|
case <-mc.closech:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
mc.cancel(ctx.Err())
|
||||||
|
case <-finished:
|
||||||
|
case <-mc.closech:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mc *mysqlConn) CheckNamedValue(nv *driver.NamedValue) (err error) {
|
||||||
|
nv.Value, err = converter{}.ConvertValue(nv.Value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetSession implements driver.SessionResetter.
|
||||||
|
// (From Go 1.10)
|
||||||
|
func (mc *mysqlConn) ResetSession(ctx context.Context) error {
|
||||||
|
if mc.closed.IsSet() {
|
||||||
|
return driver.ErrBadConn
|
||||||
|
}
|
||||||
|
mc.reset = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
208
vendor/github.com/go-sql-driver/mysql/connection_go18.go
generated
vendored
208
vendor/github.com/go-sql-driver/mysql/connection_go18.go
generated
vendored
|
@ -1,208 +0,0 @@
|
||||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
|
||||||
//
|
|
||||||
// Copyright 2012 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 (
|
|
||||||
"context"
|
|
||||||
"database/sql"
|
|
||||||
"database/sql/driver"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Ping implements driver.Pinger interface
|
|
||||||
func (mc *mysqlConn) Ping(ctx context.Context) (err error) {
|
|
||||||
if mc.closed.IsSet() {
|
|
||||||
errLog.Print(ErrInvalidConn)
|
|
||||||
return driver.ErrBadConn
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = mc.watchCancel(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer mc.finish()
|
|
||||||
|
|
||||||
if err = mc.writeCommandPacket(comPing); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return mc.readResultOK()
|
|
||||||
}
|
|
||||||
|
|
||||||
// BeginTx implements driver.ConnBeginTx interface
|
|
||||||
func (mc *mysqlConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
|
|
||||||
if err := mc.watchCancel(ctx); 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return mc.begin(opts.ReadOnly)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *mysqlConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
|
|
||||||
dargs, err := namedValueToValue(args)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := mc.watchCancel(ctx); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
rows, err := mc.query(query, dargs)
|
|
||||||
if err != nil {
|
|
||||||
mc.finish()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rows.finish = mc.finish
|
|
||||||
return rows, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *mysqlConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
|
|
||||||
dargs, err := namedValueToValue(args)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := mc.watchCancel(ctx); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer mc.finish()
|
|
||||||
|
|
||||||
return mc.Exec(query, dargs)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *mysqlConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
|
|
||||||
if err := mc.watchCancel(ctx); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt, err := mc.Prepare(query)
|
|
||||||
mc.finish()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
default:
|
|
||||||
case <-ctx.Done():
|
|
||||||
stmt.Close()
|
|
||||||
return nil, ctx.Err()
|
|
||||||
}
|
|
||||||
return stmt, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (stmt *mysqlStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
|
|
||||||
dargs, err := namedValueToValue(args)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := stmt.mc.watchCancel(ctx); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
rows, err := stmt.query(dargs)
|
|
||||||
if err != nil {
|
|
||||||
stmt.mc.finish()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rows.finish = stmt.mc.finish
|
|
||||||
return rows, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (stmt *mysqlStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
|
|
||||||
dargs, err := namedValueToValue(args)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := stmt.mc.watchCancel(ctx); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer stmt.mc.finish()
|
|
||||||
|
|
||||||
return stmt.Exec(dargs)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *mysqlConn) watchCancel(ctx context.Context) error {
|
|
||||||
if mc.watching {
|
|
||||||
// Reach here if canceled,
|
|
||||||
// so the connection is already invalid
|
|
||||||
mc.cleanup()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if ctx.Done() == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
mc.watching = true
|
|
||||||
select {
|
|
||||||
default:
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
}
|
|
||||||
if mc.watcher == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
mc.watcher <- ctx
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *mysqlConn) startWatcher() {
|
|
||||||
watcher := make(chan mysqlContext, 1)
|
|
||||||
mc.watcher = watcher
|
|
||||||
finished := make(chan struct{})
|
|
||||||
mc.finished = finished
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
var ctx mysqlContext
|
|
||||||
select {
|
|
||||||
case ctx = <-watcher:
|
|
||||||
case <-mc.closech:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
mc.cancel(ctx.Err())
|
|
||||||
case <-finished:
|
|
||||||
case <-mc.closech:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *mysqlConn) CheckNamedValue(nv *driver.NamedValue) (err error) {
|
|
||||||
nv.Value, err = converter{}.ConvertValue(nv.Value)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResetSession implements driver.SessionResetter.
|
|
||||||
// (From Go 1.10)
|
|
||||||
func (mc *mysqlConn) ResetSession(ctx context.Context) error {
|
|
||||||
if mc.closed.IsSet() {
|
|
||||||
return driver.ErrBadConn
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
175
vendor/github.com/go-sql-driver/mysql/connection_test.go
generated
vendored
Normal file
175
vendor/github.com/go-sql-driver/mysql/connection_test.go
generated
vendored
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||||
|
//
|
||||||
|
// Copyright 2016 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/.
|
||||||
|
|
||||||
|
package mysql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql/driver"
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestInterpolateParams(t *testing.T) {
|
||||||
|
mc := &mysqlConn{
|
||||||
|
buf: newBuffer(nil),
|
||||||
|
maxAllowedPacket: maxPacketSize,
|
||||||
|
cfg: &Config{
|
||||||
|
InterpolateParams: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
q, err := mc.interpolateParams("SELECT ?+?", []driver.Value{int64(42), "gopher"})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Expected err=nil, got %#v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
expected := `SELECT 42+'gopher'`
|
||||||
|
if q != expected {
|
||||||
|
t.Errorf("Expected: %q\nGot: %q", expected, q)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInterpolateParamsTooManyPlaceholders(t *testing.T) {
|
||||||
|
mc := &mysqlConn{
|
||||||
|
buf: newBuffer(nil),
|
||||||
|
maxAllowedPacket: maxPacketSize,
|
||||||
|
cfg: &Config{
|
||||||
|
InterpolateParams: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
q, err := mc.interpolateParams("SELECT ?+?", []driver.Value{int64(42)})
|
||||||
|
if err != driver.ErrSkip {
|
||||||
|
t.Errorf("Expected err=driver.ErrSkip, got err=%#v, q=%#v", err, q)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't support placeholder in string literal for now.
|
||||||
|
// https://github.com/go-sql-driver/mysql/pull/490
|
||||||
|
func TestInterpolateParamsPlaceholderInString(t *testing.T) {
|
||||||
|
mc := &mysqlConn{
|
||||||
|
buf: newBuffer(nil),
|
||||||
|
maxAllowedPacket: maxPacketSize,
|
||||||
|
cfg: &Config{
|
||||||
|
InterpolateParams: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
q, err := mc.interpolateParams("SELECT 'abc?xyz',?", []driver.Value{int64(42)})
|
||||||
|
// When InterpolateParams support string literal, this should return `"SELECT 'abc?xyz', 42`
|
||||||
|
if err != driver.ErrSkip {
|
||||||
|
t.Errorf("Expected err=driver.ErrSkip, got err=%#v, q=%#v", err, q)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInterpolateParamsUint64(t *testing.T) {
|
||||||
|
mc := &mysqlConn{
|
||||||
|
buf: newBuffer(nil),
|
||||||
|
maxAllowedPacket: maxPacketSize,
|
||||||
|
cfg: &Config{
|
||||||
|
InterpolateParams: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
q, err := mc.interpolateParams("SELECT ?", []driver.Value{uint64(42)})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Expected err=nil, got err=%#v, q=%#v", err, q)
|
||||||
|
}
|
||||||
|
if q != "SELECT 42" {
|
||||||
|
t.Errorf("Expected uint64 interpolation to work, got q=%#v", q)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckNamedValue(t *testing.T) {
|
||||||
|
value := driver.NamedValue{Value: ^uint64(0)}
|
||||||
|
x := &mysqlConn{}
|
||||||
|
err := x.CheckNamedValue(&value)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("uint64 high-bit not convertible", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if value.Value != ^uint64(0) {
|
||||||
|
t.Fatalf("uint64 high-bit converted, got %#v %T", value.Value, value.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCleanCancel tests passed context is cancelled at start.
|
||||||
|
// No packet should be sent. Connection should keep current status.
|
||||||
|
func TestCleanCancel(t *testing.T) {
|
||||||
|
mc := &mysqlConn{
|
||||||
|
closech: make(chan struct{}),
|
||||||
|
}
|
||||||
|
mc.startWatcher()
|
||||||
|
defer mc.cleanup()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
for i := 0; i < 3; i++ { // Repeat same behavior
|
||||||
|
err := mc.Ping(ctx)
|
||||||
|
if err != context.Canceled {
|
||||||
|
t.Errorf("expected context.Canceled, got %#v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if mc.closed.IsSet() {
|
||||||
|
t.Error("expected mc is not closed, closed actually")
|
||||||
|
}
|
||||||
|
|
||||||
|
if mc.watching {
|
||||||
|
t.Error("expected watching is false, but true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPingMarkBadConnection(t *testing.T) {
|
||||||
|
nc := badConnection{err: errors.New("boom")}
|
||||||
|
ms := &mysqlConn{
|
||||||
|
netConn: nc,
|
||||||
|
buf: newBuffer(nc),
|
||||||
|
maxAllowedPacket: defaultMaxAllowedPacket,
|
||||||
|
}
|
||||||
|
|
||||||
|
err := ms.Ping(context.Background())
|
||||||
|
|
||||||
|
if err != driver.ErrBadConn {
|
||||||
|
t.Errorf("expected driver.ErrBadConn, got %#v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPingErrInvalidConn(t *testing.T) {
|
||||||
|
nc := badConnection{err: errors.New("failed to write"), n: 10}
|
||||||
|
ms := &mysqlConn{
|
||||||
|
netConn: nc,
|
||||||
|
buf: newBuffer(nc),
|
||||||
|
maxAllowedPacket: defaultMaxAllowedPacket,
|
||||||
|
closech: make(chan struct{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
err := ms.Ping(context.Background())
|
||||||
|
|
||||||
|
if err != ErrInvalidConn {
|
||||||
|
t.Errorf("expected ErrInvalidConn, got %#v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type badConnection struct {
|
||||||
|
n int
|
||||||
|
err error
|
||||||
|
net.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bc badConnection) Write(b []byte) (n int, err error) {
|
||||||
|
return bc.n, bc.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bc badConnection) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
143
vendor/github.com/go-sql-driver/mysql/connector.go
generated
vendored
Normal file
143
vendor/github.com/go-sql-driver/mysql/connector.go
generated
vendored
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||||
|
//
|
||||||
|
// Copyright 2018 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/.
|
||||||
|
|
||||||
|
package mysql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql/driver"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type connector struct {
|
||||||
|
cfg *Config // immutable private copy.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect implements driver.Connector interface.
|
||||||
|
// Connect returns a connection to the database.
|
||||||
|
func (c *connector) Connect(ctx context.Context) (driver.Conn, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// New mysqlConn
|
||||||
|
mc := &mysqlConn{
|
||||||
|
maxAllowedPacket: maxPacketSize,
|
||||||
|
maxWriteSize: maxPacketSize - 1,
|
||||||
|
closech: make(chan struct{}),
|
||||||
|
cfg: c.cfg,
|
||||||
|
}
|
||||||
|
mc.parseTime = mc.cfg.ParseTime
|
||||||
|
|
||||||
|
// Connect to Server
|
||||||
|
dialsLock.RLock()
|
||||||
|
dial, ok := dials[mc.cfg.Net]
|
||||||
|
dialsLock.RUnlock()
|
||||||
|
if ok {
|
||||||
|
mc.netConn, err = dial(ctx, mc.cfg.Addr)
|
||||||
|
} else {
|
||||||
|
nd := net.Dialer{Timeout: mc.cfg.Timeout}
|
||||||
|
mc.netConn, err = nd.DialContext(ctx, mc.cfg.Net, mc.cfg.Addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
|
||||||
|
errLog.Print("net.Error from Dial()': ", nerr.Error())
|
||||||
|
return nil, driver.ErrBadConn
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable TCP Keepalives on TCP connections
|
||||||
|
if tc, ok := mc.netConn.(*net.TCPConn); ok {
|
||||||
|
if err := tc.SetKeepAlive(true); err != nil {
|
||||||
|
// Don't send COM_QUIT before handshake.
|
||||||
|
mc.netConn.Close()
|
||||||
|
mc.netConn = nil
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call startWatcher for context support (From Go 1.8)
|
||||||
|
mc.startWatcher()
|
||||||
|
if err := mc.watchCancel(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer mc.finish()
|
||||||
|
|
||||||
|
mc.buf = newBuffer(mc.netConn)
|
||||||
|
|
||||||
|
// Set I/O timeouts
|
||||||
|
mc.buf.timeout = mc.cfg.ReadTimeout
|
||||||
|
mc.writeTimeout = mc.cfg.WriteTimeout
|
||||||
|
|
||||||
|
// Reading Handshake Initialization Packet
|
||||||
|
authData, plugin, err := mc.readHandshakePacket()
|
||||||
|
if err != nil {
|
||||||
|
mc.cleanup()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if plugin == "" {
|
||||||
|
plugin = defaultAuthPlugin
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send Client Authentication Packet
|
||||||
|
authResp, err := mc.auth(authData, plugin)
|
||||||
|
if err != nil {
|
||||||
|
// try the default auth plugin, if using the requested plugin failed
|
||||||
|
errLog.Print("could not use requested auth plugin '"+plugin+"': ", err.Error())
|
||||||
|
plugin = defaultAuthPlugin
|
||||||
|
authResp, err = mc.auth(authData, plugin)
|
||||||
|
if err != nil {
|
||||||
|
mc.cleanup()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err = mc.writeHandshakeResponsePacket(authResp, plugin); err != nil {
|
||||||
|
mc.cleanup()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle response to auth packet, switch methods if possible
|
||||||
|
if err = mc.handleAuthResult(authData, plugin); err != nil {
|
||||||
|
// Authentication failed and MySQL has already closed the connection
|
||||||
|
// (https://dev.mysql.com/doc/internals/en/authentication-fails.html).
|
||||||
|
// Do not send COM_QUIT, just cleanup and return the error.
|
||||||
|
mc.cleanup()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if mc.cfg.MaxAllowedPacket > 0 {
|
||||||
|
mc.maxAllowedPacket = mc.cfg.MaxAllowedPacket
|
||||||
|
} else {
|
||||||
|
// Get max allowed packet size
|
||||||
|
maxap, err := mc.getSystemVar("max_allowed_packet")
|
||||||
|
if err != nil {
|
||||||
|
mc.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mc.maxAllowedPacket = stringToInt(maxap) - 1
|
||||||
|
}
|
||||||
|
if mc.maxAllowedPacket < maxPacketSize {
|
||||||
|
mc.maxWriteSize = mc.maxAllowedPacket
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle DSN Params
|
||||||
|
err = mc.handleParams()
|
||||||
|
if err != nil {
|
||||||
|
mc.Close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return mc, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Driver implements driver.Connector interface.
|
||||||
|
// Driver returns &MySQLDriver{}.
|
||||||
|
func (c *connector) Driver() driver.Driver {
|
||||||
|
return &MySQLDriver{}
|
||||||
|
}
|
140
vendor/github.com/go-sql-driver/mysql/driver.go
generated
vendored
140
vendor/github.com/go-sql-driver/mysql/driver.go
generated
vendored
|
@ -17,151 +17,67 @@
|
||||||
package mysql
|
package mysql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"database/sql/driver"
|
"database/sql/driver"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
// watcher interface is used for context support (From Go 1.8)
|
|
||||||
type watcher interface {
|
|
||||||
startWatcher()
|
|
||||||
}
|
|
||||||
|
|
||||||
// MySQLDriver is exported to make the driver directly accessible.
|
// MySQLDriver is exported to make the driver directly accessible.
|
||||||
// In general the driver is used via the database/sql package.
|
// In general the driver is used via the database/sql package.
|
||||||
type MySQLDriver struct{}
|
type MySQLDriver struct{}
|
||||||
|
|
||||||
// DialFunc is a function which can be used to establish the network connection.
|
// DialFunc is a function which can be used to establish the network connection.
|
||||||
// Custom dial functions must be registered with RegisterDial
|
// Custom dial functions must be registered with RegisterDial
|
||||||
|
//
|
||||||
|
// Deprecated: users should register a DialContextFunc instead
|
||||||
type DialFunc func(addr string) (net.Conn, error)
|
type DialFunc func(addr string) (net.Conn, error)
|
||||||
|
|
||||||
|
// DialContextFunc is a function which can be used to establish the network connection.
|
||||||
|
// Custom dial functions must be registered with RegisterDialContext
|
||||||
|
type DialContextFunc func(ctx context.Context, addr string) (net.Conn, error)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
dialsLock sync.RWMutex
|
dialsLock sync.RWMutex
|
||||||
dials map[string]DialFunc
|
dials map[string]DialContextFunc
|
||||||
)
|
)
|
||||||
|
|
||||||
// RegisterDial registers a custom dial function. It can then be used by the
|
// RegisterDialContext registers a custom dial function. It can then be used by the
|
||||||
// network address mynet(addr), where mynet is the registered new network.
|
// network address mynet(addr), where mynet is the registered new network.
|
||||||
// addr is passed as a parameter to the dial function.
|
// The current context for the connection and its address is passed to the dial function.
|
||||||
func RegisterDial(net string, dial DialFunc) {
|
func RegisterDialContext(net string, dial DialContextFunc) {
|
||||||
dialsLock.Lock()
|
dialsLock.Lock()
|
||||||
defer dialsLock.Unlock()
|
defer dialsLock.Unlock()
|
||||||
if dials == nil {
|
if dials == nil {
|
||||||
dials = make(map[string]DialFunc)
|
dials = make(map[string]DialContextFunc)
|
||||||
}
|
}
|
||||||
dials[net] = dial
|
dials[net] = dial
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RegisterDial registers a custom dial function. It can then be used by the
|
||||||
|
// network address mynet(addr), where mynet is the registered new network.
|
||||||
|
// addr is passed as a parameter to the dial function.
|
||||||
|
//
|
||||||
|
// Deprecated: users should call RegisterDialContext instead
|
||||||
|
func RegisterDial(network string, dial DialFunc) {
|
||||||
|
RegisterDialContext(network, func(_ context.Context, addr string) (net.Conn, error) {
|
||||||
|
return dial(addr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Open new Connection.
|
// Open new Connection.
|
||||||
// See https://github.com/go-sql-driver/mysql#dsn-data-source-name for how
|
// See https://github.com/go-sql-driver/mysql#dsn-data-source-name for how
|
||||||
// the DSN string is formated
|
// the DSN string is formatted
|
||||||
func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
|
func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
|
||||||
var err error
|
cfg, err := ParseDSN(dsn)
|
||||||
|
|
||||||
// New mysqlConn
|
|
||||||
mc := &mysqlConn{
|
|
||||||
maxAllowedPacket: maxPacketSize,
|
|
||||||
maxWriteSize: maxPacketSize - 1,
|
|
||||||
closech: make(chan struct{}),
|
|
||||||
}
|
|
||||||
mc.cfg, err = ParseDSN(dsn)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
mc.parseTime = mc.cfg.ParseTime
|
c := &connector{
|
||||||
|
cfg: cfg,
|
||||||
// Connect to Server
|
|
||||||
dialsLock.RLock()
|
|
||||||
dial, ok := dials[mc.cfg.Net]
|
|
||||||
dialsLock.RUnlock()
|
|
||||||
if ok {
|
|
||||||
mc.netConn, err = dial(mc.cfg.Addr)
|
|
||||||
} else {
|
|
||||||
nd := net.Dialer{Timeout: mc.cfg.Timeout}
|
|
||||||
mc.netConn, err = nd.Dial(mc.cfg.Net, mc.cfg.Addr)
|
|
||||||
}
|
}
|
||||||
if err != nil {
|
return c.Connect(context.Background())
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable TCP Keepalives on TCP connections
|
|
||||||
if tc, ok := mc.netConn.(*net.TCPConn); ok {
|
|
||||||
if err := tc.SetKeepAlive(true); err != nil {
|
|
||||||
// Don't send COM_QUIT before handshake.
|
|
||||||
mc.netConn.Close()
|
|
||||||
mc.netConn = nil
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
mc.buf.timeout = mc.cfg.ReadTimeout
|
|
||||||
mc.writeTimeout = mc.cfg.WriteTimeout
|
|
||||||
|
|
||||||
// Reading Handshake Initialization Packet
|
|
||||||
authData, plugin, err := mc.readHandshakePacket()
|
|
||||||
if err != nil {
|
|
||||||
mc.cleanup()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send Client Authentication Packet
|
|
||||||
authResp, addNUL, err := mc.auth(authData, plugin)
|
|
||||||
if err != nil {
|
|
||||||
// try the default auth plugin, if using the requested plugin failed
|
|
||||||
errLog.Print("could not use requested auth plugin '"+plugin+"': ", err.Error())
|
|
||||||
plugin = defaultAuthPlugin
|
|
||||||
authResp, addNUL, err = mc.auth(authData, plugin)
|
|
||||||
if err != nil {
|
|
||||||
mc.cleanup()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err = mc.writeHandshakeResponsePacket(authResp, addNUL, plugin); err != nil {
|
|
||||||
mc.cleanup()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle response to auth packet, switch methods if possible
|
|
||||||
if err = mc.handleAuthResult(authData, plugin); err != nil {
|
|
||||||
// Authentication failed and MySQL has already closed the connection
|
|
||||||
// (https://dev.mysql.com/doc/internals/en/authentication-fails.html).
|
|
||||||
// Do not send COM_QUIT, just cleanup and return the error.
|
|
||||||
mc.cleanup()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if mc.cfg.MaxAllowedPacket > 0 {
|
|
||||||
mc.maxAllowedPacket = mc.cfg.MaxAllowedPacket
|
|
||||||
} else {
|
|
||||||
// Get max allowed packet size
|
|
||||||
maxap, err := mc.getSystemVar("max_allowed_packet")
|
|
||||||
if err != nil {
|
|
||||||
mc.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
mc.maxAllowedPacket = stringToInt(maxap) - 1
|
|
||||||
}
|
|
||||||
if mc.maxAllowedPacket < maxPacketSize {
|
|
||||||
mc.maxWriteSize = mc.maxAllowedPacket
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle DSN Params
|
|
||||||
err = mc.handleParams()
|
|
||||||
if err != nil {
|
|
||||||
mc.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return mc, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
37
vendor/github.com/go-sql-driver/mysql/driver_go110.go
generated
vendored
Normal file
37
vendor/github.com/go-sql-driver/mysql/driver_go110.go
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||||
|
//
|
||||||
|
// Copyright 2018 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.10
|
||||||
|
|
||||||
|
package mysql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql/driver"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewConnector returns new driver.Connector.
|
||||||
|
func NewConnector(cfg *Config) (driver.Connector, error) {
|
||||||
|
cfg = cfg.Clone()
|
||||||
|
// normalize the contents of cfg so calls to NewConnector have the same
|
||||||
|
// behavior as MySQLDriver.OpenConnector
|
||||||
|
if err := cfg.normalize(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &connector{cfg: cfg}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenConnector implements driver.DriverContext.
|
||||||
|
func (d MySQLDriver) OpenConnector(dsn string) (driver.Connector, error) {
|
||||||
|
cfg, err := ParseDSN(dsn)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &connector{
|
||||||
|
cfg: cfg,
|
||||||
|
}, nil
|
||||||
|
}
|
137
vendor/github.com/go-sql-driver/mysql/driver_go110_test.go
generated
vendored
Normal file
137
vendor/github.com/go-sql-driver/mysql/driver_go110_test.go
generated
vendored
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||||
|
//
|
||||||
|
// Copyright 2018 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.10
|
||||||
|
|
||||||
|
package mysql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"database/sql/driver"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ driver.DriverContext = &MySQLDriver{}
|
||||||
|
|
||||||
|
type dialCtxKey struct{}
|
||||||
|
|
||||||
|
func TestConnectorObeysDialTimeouts(t *testing.T) {
|
||||||
|
if !available {
|
||||||
|
t.Skipf("MySQL server not running on %s", netAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
RegisterDialContext("dialctxtest", func(ctx context.Context, addr string) (net.Conn, error) {
|
||||||
|
var d net.Dialer
|
||||||
|
if !ctx.Value(dialCtxKey{}).(bool) {
|
||||||
|
return nil, fmt.Errorf("test error: query context is not propagated to our dialer")
|
||||||
|
}
|
||||||
|
return d.DialContext(ctx, prot, addr)
|
||||||
|
})
|
||||||
|
|
||||||
|
db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@dialctxtest(%s)/%s?timeout=30s", user, pass, addr, dbname))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error connecting: %s", err.Error())
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
ctx := context.WithValue(context.Background(), dialCtxKey{}, true)
|
||||||
|
|
||||||
|
_, err = db.ExecContext(ctx, "DO 1")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func configForTests(t *testing.T) *Config {
|
||||||
|
if !available {
|
||||||
|
t.Skipf("MySQL server not running on %s", netAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
mycnf := NewConfig()
|
||||||
|
mycnf.User = user
|
||||||
|
mycnf.Passwd = pass
|
||||||
|
mycnf.Addr = addr
|
||||||
|
mycnf.Net = prot
|
||||||
|
mycnf.DBName = dbname
|
||||||
|
return mycnf
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewConnector(t *testing.T) {
|
||||||
|
mycnf := configForTests(t)
|
||||||
|
conn, err := NewConnector(mycnf)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
db := sql.OpenDB(conn)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
if err := db.Ping(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type slowConnection struct {
|
||||||
|
net.Conn
|
||||||
|
slowdown time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc *slowConnection) Read(b []byte) (int, error) {
|
||||||
|
time.Sleep(sc.slowdown)
|
||||||
|
return sc.Conn.Read(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
type connectorHijack struct {
|
||||||
|
driver.Connector
|
||||||
|
connErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cw *connectorHijack) Connect(ctx context.Context) (driver.Conn, error) {
|
||||||
|
var conn driver.Conn
|
||||||
|
conn, cw.connErr = cw.Connector.Connect(ctx)
|
||||||
|
return conn, cw.connErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConnectorTimeoutsDuringOpen(t *testing.T) {
|
||||||
|
RegisterDialContext("slowconn", func(ctx context.Context, addr string) (net.Conn, error) {
|
||||||
|
var d net.Dialer
|
||||||
|
conn, err := d.DialContext(ctx, prot, addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &slowConnection{Conn: conn, slowdown: 100 * time.Millisecond}, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
mycnf := configForTests(t)
|
||||||
|
mycnf.Net = "slowconn"
|
||||||
|
|
||||||
|
conn, err := NewConnector(mycnf)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
hijack := &connectorHijack{Connector: conn}
|
||||||
|
|
||||||
|
db := sql.OpenDB(hijack)
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
_, err = db.ExecContext(ctx, "DO 1")
|
||||||
|
if err != context.DeadlineExceeded {
|
||||||
|
t.Fatalf("ExecContext should have timed out")
|
||||||
|
}
|
||||||
|
if hijack.connErr != context.DeadlineExceeded {
|
||||||
|
t.Fatalf("(*Connector).Connect should have timed out")
|
||||||
|
}
|
||||||
|
}
|
2996
vendor/github.com/go-sql-driver/mysql/driver_test.go
generated
vendored
Normal file
2996
vendor/github.com/go-sql-driver/mysql/driver_test.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
73
vendor/github.com/go-sql-driver/mysql/dsn.go
generated
vendored
73
vendor/github.com/go-sql-driver/mysql/dsn.go
generated
vendored
|
@ -14,6 +14,7 @@ import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"sort"
|
"sort"
|
||||||
|
@ -72,6 +73,26 @@ func NewConfig() *Config {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cfg *Config) Clone() *Config {
|
||||||
|
cp := *cfg
|
||||||
|
if cp.tls != nil {
|
||||||
|
cp.tls = cfg.tls.Clone()
|
||||||
|
}
|
||||||
|
if len(cp.Params) > 0 {
|
||||||
|
cp.Params = make(map[string]string, len(cfg.Params))
|
||||||
|
for k, v := range cfg.Params {
|
||||||
|
cp.Params[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cfg.pubKey != nil {
|
||||||
|
cp.pubKey = &rsa.PublicKey{
|
||||||
|
N: new(big.Int).Set(cfg.pubKey.N),
|
||||||
|
E: cfg.pubKey.E,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &cp
|
||||||
|
}
|
||||||
|
|
||||||
func (cfg *Config) normalize() error {
|
func (cfg *Config) normalize() error {
|
||||||
if cfg.InterpolateParams && unsafeCollations[cfg.Collation] {
|
if cfg.InterpolateParams && unsafeCollations[cfg.Collation] {
|
||||||
return errInvalidDSNUnsafeCollation
|
return errInvalidDSNUnsafeCollation
|
||||||
|
@ -92,17 +113,35 @@ func (cfg *Config) normalize() error {
|
||||||
default:
|
default:
|
||||||
return errors.New("default addr for network '" + cfg.Net + "' unknown")
|
return errors.New("default addr for network '" + cfg.Net + "' unknown")
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if cfg.Net == "tcp" {
|
} else if cfg.Net == "tcp" {
|
||||||
cfg.Addr = ensureHavePort(cfg.Addr)
|
cfg.Addr = ensureHavePort(cfg.Addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.tls != nil {
|
switch cfg.TLSConfig {
|
||||||
if cfg.tls.ServerName == "" && !cfg.tls.InsecureSkipVerify {
|
case "false", "":
|
||||||
host, _, err := net.SplitHostPort(cfg.Addr)
|
// don't set anything
|
||||||
if err == nil {
|
case "true":
|
||||||
cfg.tls.ServerName = host
|
cfg.tls = &tls.Config{}
|
||||||
}
|
case "skip-verify", "preferred":
|
||||||
|
cfg.tls = &tls.Config{InsecureSkipVerify: true}
|
||||||
|
default:
|
||||||
|
cfg.tls = getTLSConfigClone(cfg.TLSConfig)
|
||||||
|
if cfg.tls == nil {
|
||||||
|
return errors.New("invalid value / unknown config name: " + cfg.TLSConfig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.tls != nil && cfg.tls.ServerName == "" && !cfg.tls.InsecureSkipVerify {
|
||||||
|
host, _, err := net.SplitHostPort(cfg.Addr)
|
||||||
|
if err == nil {
|
||||||
|
cfg.tls.ServerName = host
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.ServerPubKey != "" {
|
||||||
|
cfg.pubKey = getServerPubKey(cfg.ServerPubKey)
|
||||||
|
if cfg.pubKey == nil {
|
||||||
|
return errors.New("invalid value / unknown server pub key name: " + cfg.ServerPubKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -531,13 +570,7 @@ func parseDSNParams(cfg *Config, params string) (err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid value for server pub key name: %v", err)
|
return fmt.Errorf("invalid value for server pub key name: %v", err)
|
||||||
}
|
}
|
||||||
|
cfg.ServerPubKey = name
|
||||||
if pubKey := getServerPubKey(name); pubKey != nil {
|
|
||||||
cfg.ServerPubKey = name
|
|
||||||
cfg.pubKey = pubKey
|
|
||||||
} else {
|
|
||||||
return errors.New("invalid value / unknown server pub key name: " + name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Strict mode
|
// Strict mode
|
||||||
case "strict":
|
case "strict":
|
||||||
|
@ -556,25 +589,17 @@ func parseDSNParams(cfg *Config, params string) (err error) {
|
||||||
if isBool {
|
if isBool {
|
||||||
if boolValue {
|
if boolValue {
|
||||||
cfg.TLSConfig = "true"
|
cfg.TLSConfig = "true"
|
||||||
cfg.tls = &tls.Config{}
|
|
||||||
} else {
|
} else {
|
||||||
cfg.TLSConfig = "false"
|
cfg.TLSConfig = "false"
|
||||||
}
|
}
|
||||||
} else if vl := strings.ToLower(value); vl == "skip-verify" {
|
} else if vl := strings.ToLower(value); vl == "skip-verify" || vl == "preferred" {
|
||||||
cfg.TLSConfig = vl
|
cfg.TLSConfig = vl
|
||||||
cfg.tls = &tls.Config{InsecureSkipVerify: true}
|
|
||||||
} else {
|
} else {
|
||||||
name, err := url.QueryUnescape(value)
|
name, err := url.QueryUnescape(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid value for TLS config name: %v", err)
|
return fmt.Errorf("invalid value for TLS config name: %v", err)
|
||||||
}
|
}
|
||||||
|
cfg.TLSConfig = name
|
||||||
if tlsConfig := getTLSConfigClone(name); tlsConfig != nil {
|
|
||||||
cfg.TLSConfig = name
|
|
||||||
cfg.tls = tlsConfig
|
|
||||||
} else {
|
|
||||||
return errors.New("invalid value / unknown config name: " + name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// I/O write Timeout
|
// I/O write Timeout
|
||||||
|
|
415
vendor/github.com/go-sql-driver/mysql/dsn_test.go
generated
vendored
Normal file
415
vendor/github.com/go-sql-driver/mysql/dsn_test.go
generated
vendored
Normal file
|
@ -0,0 +1,415 @@
|
||||||
|
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||||
|
//
|
||||||
|
// Copyright 2016 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/.
|
||||||
|
|
||||||
|
package mysql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var testDSNs = []struct {
|
||||||
|
in string
|
||||||
|
out *Config
|
||||||
|
}{{
|
||||||
|
"username:password@protocol(address)/dbname?param=value",
|
||||||
|
&Config{User: "username", Passwd: "password", Net: "protocol", Addr: "address", DBName: "dbname", Params: map[string]string{"param": "value"}, Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true},
|
||||||
|
}, {
|
||||||
|
"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true",
|
||||||
|
&Config{User: "username", Passwd: "password", Net: "protocol", Addr: "address", DBName: "dbname", Params: map[string]string{"param": "value"}, Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true, ColumnsWithAlias: true},
|
||||||
|
}, {
|
||||||
|
"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true&multiStatements=true",
|
||||||
|
&Config{User: "username", Passwd: "password", Net: "protocol", Addr: "address", DBName: "dbname", Params: map[string]string{"param": "value"}, Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true, ColumnsWithAlias: true, MultiStatements: true},
|
||||||
|
}, {
|
||||||
|
"user@unix(/path/to/socket)/dbname?charset=utf8",
|
||||||
|
&Config{User: "user", Net: "unix", Addr: "/path/to/socket", DBName: "dbname", Params: map[string]string{"charset": "utf8"}, Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true},
|
||||||
|
}, {
|
||||||
|
"user:password@tcp(localhost:5555)/dbname?charset=utf8&tls=true",
|
||||||
|
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "localhost:5555", DBName: "dbname", Params: map[string]string{"charset": "utf8"}, Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true, TLSConfig: "true"},
|
||||||
|
}, {
|
||||||
|
"user:password@tcp(localhost:5555)/dbname?charset=utf8mb4,utf8&tls=skip-verify",
|
||||||
|
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "localhost:5555", DBName: "dbname", Params: map[string]string{"charset": "utf8mb4,utf8"}, Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true, TLSConfig: "skip-verify"},
|
||||||
|
}, {
|
||||||
|
"user:password@/dbname?loc=UTC&timeout=30s&readTimeout=1s&writeTimeout=1s&allowAllFiles=1&clientFoundRows=true&allowOldPasswords=TRUE&collation=utf8mb4_unicode_ci&maxAllowedPacket=16777216&tls=false&allowCleartextPasswords=true&parseTime=true&rejectReadOnly=true",
|
||||||
|
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Collation: "utf8mb4_unicode_ci", Loc: time.UTC, TLSConfig: "false", AllowCleartextPasswords: true, AllowNativePasswords: true, Timeout: 30 * time.Second, ReadTimeout: time.Second, WriteTimeout: time.Second, AllowAllFiles: true, AllowOldPasswords: true, ClientFoundRows: true, MaxAllowedPacket: 16777216, ParseTime: true, RejectReadOnly: true},
|
||||||
|
}, {
|
||||||
|
"user:password@/dbname?allowNativePasswords=false&maxAllowedPacket=0",
|
||||||
|
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: 0, AllowNativePasswords: false},
|
||||||
|
}, {
|
||||||
|
"user:p@ss(word)@tcp([de:ad:be:ef::ca:fe]:80)/dbname?loc=Local",
|
||||||
|
&Config{User: "user", Passwd: "p@ss(word)", Net: "tcp", Addr: "[de:ad:be:ef::ca:fe]:80", DBName: "dbname", Collation: "utf8mb4_general_ci", Loc: time.Local, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true},
|
||||||
|
}, {
|
||||||
|
"/dbname",
|
||||||
|
&Config{Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true},
|
||||||
|
}, {
|
||||||
|
"@/",
|
||||||
|
&Config{Net: "tcp", Addr: "127.0.0.1:3306", Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true},
|
||||||
|
}, {
|
||||||
|
"/",
|
||||||
|
&Config{Net: "tcp", Addr: "127.0.0.1:3306", Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true},
|
||||||
|
}, {
|
||||||
|
"",
|
||||||
|
&Config{Net: "tcp", Addr: "127.0.0.1:3306", Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true},
|
||||||
|
}, {
|
||||||
|
"user:p@/ssword@/",
|
||||||
|
&Config{User: "user", Passwd: "p@/ssword", Net: "tcp", Addr: "127.0.0.1:3306", Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true},
|
||||||
|
}, {
|
||||||
|
"unix/?arg=%2Fsome%2Fpath.ext",
|
||||||
|
&Config{Net: "unix", Addr: "/tmp/mysql.sock", Params: map[string]string{"arg": "/some/path.ext"}, Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true},
|
||||||
|
}, {
|
||||||
|
"tcp(127.0.0.1)/dbname",
|
||||||
|
&Config{Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true},
|
||||||
|
}, {
|
||||||
|
"tcp(de:ad:be:ef::ca:fe)/dbname",
|
||||||
|
&Config{Net: "tcp", Addr: "[de:ad:be:ef::ca:fe]:3306", DBName: "dbname", Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDSNParser(t *testing.T) {
|
||||||
|
for i, tst := range testDSNs {
|
||||||
|
cfg, err := ParseDSN(tst.in)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// pointer not static
|
||||||
|
cfg.tls = nil
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(cfg, tst.out) {
|
||||||
|
t.Errorf("%d. ParseDSN(%q) mismatch:\ngot %+v\nwant %+v", i, tst.in, cfg, tst.out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDSNParserInvalid(t *testing.T) {
|
||||||
|
var invalidDSNs = []string{
|
||||||
|
"@net(addr/", // no closing brace
|
||||||
|
"@tcp(/", // no closing brace
|
||||||
|
"tcp(/", // no closing brace
|
||||||
|
"(/", // no closing brace
|
||||||
|
"net(addr)//", // unescaped
|
||||||
|
"User:pass@tcp(1.2.3.4:3306)", // no trailing slash
|
||||||
|
"net()/", // unknown default addr
|
||||||
|
//"/dbname?arg=/some/unescaped/path",
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tst := range invalidDSNs {
|
||||||
|
if _, err := ParseDSN(tst); err == nil {
|
||||||
|
t.Errorf("invalid DSN #%d. (%s) didn't error!", i, tst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDSNReformat(t *testing.T) {
|
||||||
|
for i, tst := range testDSNs {
|
||||||
|
dsn1 := tst.in
|
||||||
|
cfg1, err := ParseDSN(dsn1)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cfg1.tls = nil // pointer not static
|
||||||
|
res1 := fmt.Sprintf("%+v", cfg1)
|
||||||
|
|
||||||
|
dsn2 := cfg1.FormatDSN()
|
||||||
|
cfg2, err := ParseDSN(dsn2)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err.Error())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cfg2.tls = nil // pointer not static
|
||||||
|
res2 := fmt.Sprintf("%+v", cfg2)
|
||||||
|
|
||||||
|
if res1 != res2 {
|
||||||
|
t.Errorf("%d. %q does not match %q", i, res2, res1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDSNServerPubKey(t *testing.T) {
|
||||||
|
baseDSN := "User:password@tcp(localhost:5555)/dbname?serverPubKey="
|
||||||
|
|
||||||
|
RegisterServerPubKey("testKey", testPubKeyRSA)
|
||||||
|
defer DeregisterServerPubKey("testKey")
|
||||||
|
|
||||||
|
tst := baseDSN + "testKey"
|
||||||
|
cfg, err := ParseDSN(tst)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.ServerPubKey != "testKey" {
|
||||||
|
t.Errorf("unexpected cfg.ServerPubKey value: %v", cfg.ServerPubKey)
|
||||||
|
}
|
||||||
|
if cfg.pubKey != testPubKeyRSA {
|
||||||
|
t.Error("pub key pointer doesn't match")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key is missing
|
||||||
|
tst = baseDSN + "invalid_name"
|
||||||
|
cfg, err = ParseDSN(tst)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("invalid name in DSN (%s) but did not error. Got config: %#v", tst, cfg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDSNServerPubKeyQueryEscape(t *testing.T) {
|
||||||
|
const name = "&%!:"
|
||||||
|
dsn := "User:password@tcp(localhost:5555)/dbname?serverPubKey=" + url.QueryEscape(name)
|
||||||
|
|
||||||
|
RegisterServerPubKey(name, testPubKeyRSA)
|
||||||
|
defer DeregisterServerPubKey(name)
|
||||||
|
|
||||||
|
cfg, err := ParseDSN(dsn)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.pubKey != testPubKeyRSA {
|
||||||
|
t.Error("pub key pointer doesn't match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDSNWithCustomTLS(t *testing.T) {
|
||||||
|
baseDSN := "User:password@tcp(localhost:5555)/dbname?tls="
|
||||||
|
tlsCfg := tls.Config{}
|
||||||
|
|
||||||
|
RegisterTLSConfig("utils_test", &tlsCfg)
|
||||||
|
defer DeregisterTLSConfig("utils_test")
|
||||||
|
|
||||||
|
// Custom TLS is missing
|
||||||
|
tst := baseDSN + "invalid_tls"
|
||||||
|
cfg, err := ParseDSN(tst)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("invalid custom TLS in DSN (%s) but did not error. Got config: %#v", tst, cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
tst = baseDSN + "utils_test"
|
||||||
|
|
||||||
|
// Custom TLS with a server name
|
||||||
|
name := "foohost"
|
||||||
|
tlsCfg.ServerName = name
|
||||||
|
cfg, err = ParseDSN(tst)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err.Error())
|
||||||
|
} else if cfg.tls.ServerName != name {
|
||||||
|
t.Errorf("did not get the correct TLS ServerName (%s) parsing DSN (%s).", name, tst)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom TLS without a server name
|
||||||
|
name = "localhost"
|
||||||
|
tlsCfg.ServerName = ""
|
||||||
|
cfg, err = ParseDSN(tst)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err.Error())
|
||||||
|
} else if cfg.tls.ServerName != name {
|
||||||
|
t.Errorf("did not get the correct ServerName (%s) parsing DSN (%s).", name, tst)
|
||||||
|
} else if tlsCfg.ServerName != "" {
|
||||||
|
t.Errorf("tlsCfg was mutated ServerName (%s) should be empty parsing DSN (%s).", name, tst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDSNTLSConfig(t *testing.T) {
|
||||||
|
expectedServerName := "example.com"
|
||||||
|
dsn := "tcp(example.com:1234)/?tls=true"
|
||||||
|
|
||||||
|
cfg, err := ParseDSN(dsn)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err.Error())
|
||||||
|
}
|
||||||
|
if cfg.tls == nil {
|
||||||
|
t.Error("cfg.tls should not be nil")
|
||||||
|
}
|
||||||
|
if cfg.tls.ServerName != expectedServerName {
|
||||||
|
t.Errorf("cfg.tls.ServerName should be %q, got %q (host with port)", expectedServerName, cfg.tls.ServerName)
|
||||||
|
}
|
||||||
|
|
||||||
|
dsn = "tcp(example.com)/?tls=true"
|
||||||
|
cfg, err = ParseDSN(dsn)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err.Error())
|
||||||
|
}
|
||||||
|
if cfg.tls == nil {
|
||||||
|
t.Error("cfg.tls should not be nil")
|
||||||
|
}
|
||||||
|
if cfg.tls.ServerName != expectedServerName {
|
||||||
|
t.Errorf("cfg.tls.ServerName should be %q, got %q (host without port)", expectedServerName, cfg.tls.ServerName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDSNWithCustomTLSQueryEscape(t *testing.T) {
|
||||||
|
const configKey = "&%!:"
|
||||||
|
dsn := "User:password@tcp(localhost:5555)/dbname?tls=" + url.QueryEscape(configKey)
|
||||||
|
name := "foohost"
|
||||||
|
tlsCfg := tls.Config{ServerName: name}
|
||||||
|
|
||||||
|
RegisterTLSConfig(configKey, &tlsCfg)
|
||||||
|
defer DeregisterTLSConfig(configKey)
|
||||||
|
|
||||||
|
cfg, err := ParseDSN(dsn)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err.Error())
|
||||||
|
} else if cfg.tls.ServerName != name {
|
||||||
|
t.Errorf("did not get the correct TLS ServerName (%s) parsing DSN (%s).", name, dsn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDSNUnsafeCollation(t *testing.T) {
|
||||||
|
_, err := ParseDSN("/dbname?collation=gbk_chinese_ci&interpolateParams=true")
|
||||||
|
if err != errInvalidDSNUnsafeCollation {
|
||||||
|
t.Errorf("expected %v, got %v", errInvalidDSNUnsafeCollation, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = ParseDSN("/dbname?collation=gbk_chinese_ci&interpolateParams=false")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected %v, got %v", nil, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = ParseDSN("/dbname?collation=gbk_chinese_ci")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected %v, got %v", nil, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = ParseDSN("/dbname?collation=ascii_bin&interpolateParams=true")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected %v, got %v", nil, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = ParseDSN("/dbname?collation=latin1_german1_ci&interpolateParams=true")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected %v, got %v", nil, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = ParseDSN("/dbname?collation=utf8_general_ci&interpolateParams=true")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected %v, got %v", nil, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = ParseDSN("/dbname?collation=utf8mb4_general_ci&interpolateParams=true")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("expected %v, got %v", nil, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParamsAreSorted(t *testing.T) {
|
||||||
|
expected := "/dbname?interpolateParams=true&foobar=baz&quux=loo"
|
||||||
|
cfg := NewConfig()
|
||||||
|
cfg.DBName = "dbname"
|
||||||
|
cfg.InterpolateParams = true
|
||||||
|
cfg.Params = map[string]string{
|
||||||
|
"quux": "loo",
|
||||||
|
"foobar": "baz",
|
||||||
|
}
|
||||||
|
actual := cfg.FormatDSN()
|
||||||
|
if actual != expected {
|
||||||
|
t.Errorf("generic Config.Params were not sorted: want %#v, got %#v", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloneConfig(t *testing.T) {
|
||||||
|
RegisterServerPubKey("testKey", testPubKeyRSA)
|
||||||
|
defer DeregisterServerPubKey("testKey")
|
||||||
|
|
||||||
|
expectedServerName := "example.com"
|
||||||
|
dsn := "tcp(example.com:1234)/?tls=true&foobar=baz&serverPubKey=testKey"
|
||||||
|
cfg, err := ParseDSN(dsn)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg2 := cfg.Clone()
|
||||||
|
if cfg == cfg2 {
|
||||||
|
t.Errorf("Config.Clone did not create a separate config struct")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg2.tls.ServerName != expectedServerName {
|
||||||
|
t.Errorf("cfg.tls.ServerName should be %q, got %q (host with port)", expectedServerName, cfg.tls.ServerName)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg2.tls.ServerName = "example2.com"
|
||||||
|
if cfg.tls.ServerName == cfg2.tls.ServerName {
|
||||||
|
t.Errorf("changed cfg.tls.Server name should not propagate to original Config")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := cfg2.Params["foobar"]; !ok {
|
||||||
|
t.Errorf("cloned Config is missing custom params")
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(cfg2.Params, "foobar")
|
||||||
|
|
||||||
|
if _, ok := cfg.Params["foobar"]; !ok {
|
||||||
|
t.Errorf("custom params in cloned Config should not propagate to original Config")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(cfg.pubKey, cfg2.pubKey) {
|
||||||
|
t.Errorf("public key in Config should be identical")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNormalizeTLSConfig(t *testing.T) {
|
||||||
|
tt := []struct {
|
||||||
|
tlsConfig string
|
||||||
|
want *tls.Config
|
||||||
|
}{
|
||||||
|
{"", nil},
|
||||||
|
{"false", nil},
|
||||||
|
{"true", &tls.Config{ServerName: "myserver"}},
|
||||||
|
{"skip-verify", &tls.Config{InsecureSkipVerify: true}},
|
||||||
|
{"preferred", &tls.Config{InsecureSkipVerify: true}},
|
||||||
|
{"test_tls_config", &tls.Config{ServerName: "myServerName"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
RegisterTLSConfig("test_tls_config", &tls.Config{ServerName: "myServerName"})
|
||||||
|
defer func() { DeregisterTLSConfig("test_tls_config") }()
|
||||||
|
|
||||||
|
for _, tc := range tt {
|
||||||
|
t.Run(tc.tlsConfig, func(t *testing.T) {
|
||||||
|
cfg := &Config{
|
||||||
|
Addr: "myserver:3306",
|
||||||
|
TLSConfig: tc.tlsConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.normalize()
|
||||||
|
|
||||||
|
if cfg.tls == nil {
|
||||||
|
if tc.want != nil {
|
||||||
|
t.Fatal("wanted a tls config but got nil instead")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.tls.ServerName != tc.want.ServerName {
|
||||||
|
t.Errorf("tls.ServerName doesn't match (want: '%s', got: '%s')",
|
||||||
|
tc.want.ServerName, cfg.tls.ServerName)
|
||||||
|
}
|
||||||
|
if cfg.tls.InsecureSkipVerify != tc.want.InsecureSkipVerify {
|
||||||
|
t.Errorf("tls.InsecureSkipVerify doesn't match (want: %T, got :%T)",
|
||||||
|
tc.want.InsecureSkipVerify, cfg.tls.InsecureSkipVerify)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkParseDSN(b *testing.B) {
|
||||||
|
b.ReportAllocs()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
for _, tst := range testDSNs {
|
||||||
|
if _, err := ParseDSN(tst.in); err != nil {
|
||||||
|
b.Error(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
42
vendor/github.com/go-sql-driver/mysql/errors_test.go
generated
vendored
Normal file
42
vendor/github.com/go-sql-driver/mysql/errors_test.go
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||||
|
//
|
||||||
|
// Copyright 2013 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/.
|
||||||
|
|
||||||
|
package mysql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"log"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestErrorsSetLogger(t *testing.T) {
|
||||||
|
previous := errLog
|
||||||
|
defer func() {
|
||||||
|
errLog = previous
|
||||||
|
}()
|
||||||
|
|
||||||
|
// set up logger
|
||||||
|
const expected = "prefix: test\n"
|
||||||
|
buffer := bytes.NewBuffer(make([]byte, 0, 64))
|
||||||
|
logger := log.New(buffer, "prefix: ", 0)
|
||||||
|
|
||||||
|
// print
|
||||||
|
SetLogger(logger)
|
||||||
|
errLog.Print("test")
|
||||||
|
|
||||||
|
// check result
|
||||||
|
if actual := buffer.String(); actual != expected {
|
||||||
|
t.Errorf("expected %q, got %q", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorsStrictIgnoreNotes(t *testing.T) {
|
||||||
|
runTests(t, dsn+"&sql_notes=false", func(dbt *DBTest) {
|
||||||
|
dbt.mustExec("DROP TABLE IF EXISTS does_not_exist")
|
||||||
|
})
|
||||||
|
}
|
145
vendor/github.com/go-sql-driver/mysql/packets.go
generated
vendored
145
vendor/github.com/go-sql-driver/mysql/packets.go
generated
vendored
|
@ -51,7 +51,7 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
|
||||||
mc.sequence++
|
mc.sequence++
|
||||||
|
|
||||||
// packets with length 0 terminate a previous packet which is a
|
// packets with length 0 terminate a previous packet which is a
|
||||||
// multiple of (2^24)−1 bytes long
|
// multiple of (2^24)-1 bytes long
|
||||||
if pktLen == 0 {
|
if pktLen == 0 {
|
||||||
// there was no previous packet
|
// there was no previous packet
|
||||||
if prevData == nil {
|
if prevData == nil {
|
||||||
|
@ -96,6 +96,35 @@ func (mc *mysqlConn) writePacket(data []byte) error {
|
||||||
return ErrPktTooLarge
|
return ErrPktTooLarge
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Perform a stale connection check. We only perform this check for
|
||||||
|
// the first query on a connection that has been checked out of the
|
||||||
|
// connection pool: a fresh connection from the pool is more likely
|
||||||
|
// to be stale, and it has not performed any previous writes that
|
||||||
|
// could cause data corruption, so it's safe to return ErrBadConn
|
||||||
|
// if the check fails.
|
||||||
|
if mc.reset {
|
||||||
|
mc.reset = false
|
||||||
|
conn := mc.netConn
|
||||||
|
if mc.rawConn != nil {
|
||||||
|
conn = mc.rawConn
|
||||||
|
}
|
||||||
|
var err error
|
||||||
|
// If this connection has a ReadTimeout which we've been setting on
|
||||||
|
// reads, reset it to its default value before we attempt a non-blocking
|
||||||
|
// read, otherwise the scheduler will just time us out before we can read
|
||||||
|
if mc.cfg.ReadTimeout != 0 {
|
||||||
|
err = conn.SetReadDeadline(time.Time{})
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
err = connCheck(conn)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
errLog.Print("closing bad idle connection: ", err)
|
||||||
|
mc.Close()
|
||||||
|
return driver.ErrBadConn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
var size int
|
var size int
|
||||||
if pktLen >= maxPacketSize {
|
if pktLen >= maxPacketSize {
|
||||||
|
@ -154,15 +183,15 @@ func (mc *mysqlConn) writePacket(data []byte) error {
|
||||||
|
|
||||||
// Handshake Initialization Packet
|
// Handshake Initialization Packet
|
||||||
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::Handshake
|
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::Handshake
|
||||||
func (mc *mysqlConn) readHandshakePacket() ([]byte, string, error) {
|
func (mc *mysqlConn) readHandshakePacket() (data []byte, plugin string, err error) {
|
||||||
data, err := mc.readPacket()
|
data, err = mc.readPacket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// for init we can rewrite this to ErrBadConn for sql.Driver to retry, since
|
// for init we can rewrite this to ErrBadConn for sql.Driver to retry, since
|
||||||
// in connection initialization we don't risk retrying non-idempotent actions.
|
// in connection initialization we don't risk retrying non-idempotent actions.
|
||||||
if err == ErrInvalidConn {
|
if err == ErrInvalidConn {
|
||||||
return nil, "", driver.ErrBadConn
|
return nil, "", driver.ErrBadConn
|
||||||
}
|
}
|
||||||
return nil, "", err
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if data[0] == iERR {
|
if data[0] == iERR {
|
||||||
|
@ -194,11 +223,14 @@ func (mc *mysqlConn) readHandshakePacket() ([]byte, string, error) {
|
||||||
return nil, "", ErrOldProtocol
|
return nil, "", ErrOldProtocol
|
||||||
}
|
}
|
||||||
if mc.flags&clientSSL == 0 && mc.cfg.tls != nil {
|
if mc.flags&clientSSL == 0 && mc.cfg.tls != nil {
|
||||||
return nil, "", ErrNoTLS
|
if mc.cfg.TLSConfig == "preferred" {
|
||||||
|
mc.cfg.tls = nil
|
||||||
|
} else {
|
||||||
|
return nil, "", ErrNoTLS
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pos += 2
|
pos += 2
|
||||||
|
|
||||||
plugin := ""
|
|
||||||
if len(data) > pos {
|
if len(data) > pos {
|
||||||
// character set [1 byte]
|
// character set [1 byte]
|
||||||
// status flags [2 bytes]
|
// status flags [2 bytes]
|
||||||
|
@ -236,8 +268,6 @@ func (mc *mysqlConn) readHandshakePacket() ([]byte, string, error) {
|
||||||
return b[:], plugin, nil
|
return b[:], plugin, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
plugin = defaultAuthPlugin
|
|
||||||
|
|
||||||
// make a memory safe copy of the cipher slice
|
// make a memory safe copy of the cipher slice
|
||||||
var b [8]byte
|
var b [8]byte
|
||||||
copy(b[:], authData)
|
copy(b[:], authData)
|
||||||
|
@ -246,7 +276,7 @@ func (mc *mysqlConn) readHandshakePacket() ([]byte, string, error) {
|
||||||
|
|
||||||
// Client Authentication Packet
|
// Client Authentication Packet
|
||||||
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::HandshakeResponse
|
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::HandshakeResponse
|
||||||
func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, addNUL bool, plugin string) error {
|
func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, plugin string) error {
|
||||||
// Adjust client flags based on server support
|
// Adjust client flags based on server support
|
||||||
clientFlags := clientProtocol41 |
|
clientFlags := clientProtocol41 |
|
||||||
clientSecureConn |
|
clientSecureConn |
|
||||||
|
@ -272,7 +302,8 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, addNUL bool,
|
||||||
|
|
||||||
// encode length of the auth plugin data
|
// encode length of the auth plugin data
|
||||||
var authRespLEIBuf [9]byte
|
var authRespLEIBuf [9]byte
|
||||||
authRespLEI := appendLengthEncodedInteger(authRespLEIBuf[:0], uint64(len(authResp)))
|
authRespLen := len(authResp)
|
||||||
|
authRespLEI := appendLengthEncodedInteger(authRespLEIBuf[:0], uint64(authRespLen))
|
||||||
if len(authRespLEI) > 1 {
|
if len(authRespLEI) > 1 {
|
||||||
// if the length can not be written in 1 byte, it must be written as a
|
// if the length can not be written in 1 byte, it must be written as a
|
||||||
// length encoded integer
|
// length encoded integer
|
||||||
|
@ -280,9 +311,6 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, addNUL bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pktLen := 4 + 4 + 1 + 23 + len(mc.cfg.User) + 1 + len(authRespLEI) + len(authResp) + 21 + 1
|
pktLen := 4 + 4 + 1 + 23 + len(mc.cfg.User) + 1 + len(authRespLEI) + len(authResp) + 21 + 1
|
||||||
if addNUL {
|
|
||||||
pktLen++
|
|
||||||
}
|
|
||||||
|
|
||||||
// To specify a db name
|
// To specify a db name
|
||||||
if n := len(mc.cfg.DBName); n > 0 {
|
if n := len(mc.cfg.DBName); n > 0 {
|
||||||
|
@ -291,10 +319,10 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, addNUL bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate packet length and get buffer with that size
|
// Calculate packet length and get buffer with that size
|
||||||
data := mc.buf.takeSmallBuffer(pktLen + 4)
|
data, err := mc.buf.takeSmallBuffer(pktLen + 4)
|
||||||
if data == nil {
|
if err != nil {
|
||||||
// cannot take the buffer. Something must be wrong with the connection
|
// cannot take the buffer. Something must be wrong with the connection
|
||||||
errLog.Print(ErrBusyBuffer)
|
errLog.Print(err)
|
||||||
return errBadConnNoWrite
|
return errBadConnNoWrite
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,6 +361,7 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, addNUL bool,
|
||||||
if err := tlsConn.Handshake(); err != nil {
|
if err := tlsConn.Handshake(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
mc.rawConn = mc.netConn
|
||||||
mc.netConn = tlsConn
|
mc.netConn = tlsConn
|
||||||
mc.buf.nc = tlsConn
|
mc.buf.nc = tlsConn
|
||||||
}
|
}
|
||||||
|
@ -353,10 +382,6 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, addNUL bool,
|
||||||
// Auth Data [length encoded integer]
|
// Auth Data [length encoded integer]
|
||||||
pos += copy(data[pos:], authRespLEI)
|
pos += copy(data[pos:], authRespLEI)
|
||||||
pos += copy(data[pos:], authResp)
|
pos += copy(data[pos:], authResp)
|
||||||
if addNUL {
|
|
||||||
data[pos] = 0x00
|
|
||||||
pos++
|
|
||||||
}
|
|
||||||
|
|
||||||
// Databasename [null terminated string]
|
// Databasename [null terminated string]
|
||||||
if len(mc.cfg.DBName) > 0 {
|
if len(mc.cfg.DBName) > 0 {
|
||||||
|
@ -367,30 +392,24 @@ func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, addNUL bool,
|
||||||
|
|
||||||
pos += copy(data[pos:], plugin)
|
pos += copy(data[pos:], plugin)
|
||||||
data[pos] = 0x00
|
data[pos] = 0x00
|
||||||
|
pos++
|
||||||
|
|
||||||
// Send Auth packet
|
// Send Auth packet
|
||||||
return mc.writePacket(data)
|
return mc.writePacket(data[:pos])
|
||||||
}
|
}
|
||||||
|
|
||||||
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse
|
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse
|
||||||
func (mc *mysqlConn) writeAuthSwitchPacket(authData []byte, addNUL bool) error {
|
func (mc *mysqlConn) writeAuthSwitchPacket(authData []byte) error {
|
||||||
pktLen := 4 + len(authData)
|
pktLen := 4 + len(authData)
|
||||||
if addNUL {
|
data, err := mc.buf.takeSmallBuffer(pktLen)
|
||||||
pktLen++
|
if err != nil {
|
||||||
}
|
|
||||||
data := mc.buf.takeSmallBuffer(pktLen)
|
|
||||||
if data == nil {
|
|
||||||
// cannot take the buffer. Something must be wrong with the connection
|
// cannot take the buffer. Something must be wrong with the connection
|
||||||
errLog.Print(ErrBusyBuffer)
|
errLog.Print(err)
|
||||||
return errBadConnNoWrite
|
return errBadConnNoWrite
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the auth data [EOF]
|
// Add the auth data [EOF]
|
||||||
copy(data[4:], authData)
|
copy(data[4:], authData)
|
||||||
if addNUL {
|
|
||||||
data[pktLen-1] = 0x00
|
|
||||||
}
|
|
||||||
|
|
||||||
return mc.writePacket(data)
|
return mc.writePacket(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -402,10 +421,10 @@ func (mc *mysqlConn) writeCommandPacket(command byte) error {
|
||||||
// Reset Packet Sequence
|
// Reset Packet Sequence
|
||||||
mc.sequence = 0
|
mc.sequence = 0
|
||||||
|
|
||||||
data := mc.buf.takeSmallBuffer(4 + 1)
|
data, err := mc.buf.takeSmallBuffer(4 + 1)
|
||||||
if data == nil {
|
if err != nil {
|
||||||
// cannot take the buffer. Something must be wrong with the connection
|
// cannot take the buffer. Something must be wrong with the connection
|
||||||
errLog.Print(ErrBusyBuffer)
|
errLog.Print(err)
|
||||||
return errBadConnNoWrite
|
return errBadConnNoWrite
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -421,10 +440,10 @@ func (mc *mysqlConn) writeCommandPacketStr(command byte, arg string) error {
|
||||||
mc.sequence = 0
|
mc.sequence = 0
|
||||||
|
|
||||||
pktLen := 1 + len(arg)
|
pktLen := 1 + len(arg)
|
||||||
data := mc.buf.takeBuffer(pktLen + 4)
|
data, err := mc.buf.takeBuffer(pktLen + 4)
|
||||||
if data == nil {
|
if err != nil {
|
||||||
// cannot take the buffer. Something must be wrong with the connection
|
// cannot take the buffer. Something must be wrong with the connection
|
||||||
errLog.Print(ErrBusyBuffer)
|
errLog.Print(err)
|
||||||
return errBadConnNoWrite
|
return errBadConnNoWrite
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -442,10 +461,10 @@ func (mc *mysqlConn) writeCommandPacketUint32(command byte, arg uint32) error {
|
||||||
// Reset Packet Sequence
|
// Reset Packet Sequence
|
||||||
mc.sequence = 0
|
mc.sequence = 0
|
||||||
|
|
||||||
data := mc.buf.takeSmallBuffer(4 + 1 + 4)
|
data, err := mc.buf.takeSmallBuffer(4 + 1 + 4)
|
||||||
if data == nil {
|
if err != nil {
|
||||||
// cannot take the buffer. Something must be wrong with the connection
|
// cannot take the buffer. Something must be wrong with the connection
|
||||||
errLog.Print(ErrBusyBuffer)
|
errLog.Print(err)
|
||||||
return errBadConnNoWrite
|
return errBadConnNoWrite
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -482,7 +501,7 @@ func (mc *mysqlConn) readAuthResult() ([]byte, string, error) {
|
||||||
return data[1:], "", err
|
return data[1:], "", err
|
||||||
|
|
||||||
case iEOF:
|
case iEOF:
|
||||||
if len(data) < 1 {
|
if len(data) == 1 {
|
||||||
// https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::OldAuthSwitchRequest
|
// https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::OldAuthSwitchRequest
|
||||||
return nil, "mysql_old_password", nil
|
return nil, "mysql_old_password", nil
|
||||||
}
|
}
|
||||||
|
@ -898,7 +917,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
|
||||||
const minPktLen = 4 + 1 + 4 + 1 + 4
|
const minPktLen = 4 + 1 + 4 + 1 + 4
|
||||||
mc := stmt.mc
|
mc := stmt.mc
|
||||||
|
|
||||||
// Determine threshould dynamically to avoid packet size shortage.
|
// Determine threshold dynamically to avoid packet size shortage.
|
||||||
longDataSize := mc.maxAllowedPacket / (stmt.paramCount + 1)
|
longDataSize := mc.maxAllowedPacket / (stmt.paramCount + 1)
|
||||||
if longDataSize < 64 {
|
if longDataSize < 64 {
|
||||||
longDataSize = 64
|
longDataSize = 64
|
||||||
|
@ -908,15 +927,17 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
|
||||||
mc.sequence = 0
|
mc.sequence = 0
|
||||||
|
|
||||||
var data []byte
|
var data []byte
|
||||||
|
var err error
|
||||||
|
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
data = mc.buf.takeBuffer(minPktLen)
|
data, err = mc.buf.takeBuffer(minPktLen)
|
||||||
} else {
|
} else {
|
||||||
data = mc.buf.takeCompleteBuffer()
|
data, err = mc.buf.takeCompleteBuffer()
|
||||||
|
// In this case the len(data) == cap(data) which is used to optimise the flow below.
|
||||||
}
|
}
|
||||||
if data == nil {
|
if err != nil {
|
||||||
// cannot take the buffer. Something must be wrong with the connection
|
// cannot take the buffer. Something must be wrong with the connection
|
||||||
errLog.Print(ErrBusyBuffer)
|
errLog.Print(err)
|
||||||
return errBadConnNoWrite
|
return errBadConnNoWrite
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -942,7 +963,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
|
||||||
pos := minPktLen
|
pos := minPktLen
|
||||||
|
|
||||||
var nullMask []byte
|
var nullMask []byte
|
||||||
if maskLen, typesLen := (len(args)+7)/8, 1+2*len(args); pos+maskLen+typesLen >= len(data) {
|
if maskLen, typesLen := (len(args)+7)/8, 1+2*len(args); pos+maskLen+typesLen >= cap(data) {
|
||||||
// buffer has to be extended but we don't know by how much so
|
// buffer has to be extended but we don't know by how much so
|
||||||
// we depend on append after all data with known sizes fit.
|
// we depend on append after all data with known sizes fit.
|
||||||
// We stop at that because we deal with a lot of columns here
|
// We stop at that because we deal with a lot of columns here
|
||||||
|
@ -951,10 +972,11 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
|
||||||
copy(tmp[:pos], data[:pos])
|
copy(tmp[:pos], data[:pos])
|
||||||
data = tmp
|
data = tmp
|
||||||
nullMask = data[pos : pos+maskLen]
|
nullMask = data[pos : pos+maskLen]
|
||||||
|
// No need to clean nullMask as make ensures that.
|
||||||
pos += maskLen
|
pos += maskLen
|
||||||
} else {
|
} else {
|
||||||
nullMask = data[pos : pos+maskLen]
|
nullMask = data[pos : pos+maskLen]
|
||||||
for i := 0; i < maskLen; i++ {
|
for i := range nullMask {
|
||||||
nullMask[i] = 0
|
nullMask[i] = 0
|
||||||
}
|
}
|
||||||
pos += maskLen
|
pos += maskLen
|
||||||
|
@ -999,6 +1021,22 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case uint64:
|
||||||
|
paramTypes[i+i] = byte(fieldTypeLongLong)
|
||||||
|
paramTypes[i+i+1] = 0x80 // type is unsigned
|
||||||
|
|
||||||
|
if cap(paramValues)-len(paramValues)-8 >= 0 {
|
||||||
|
paramValues = paramValues[:len(paramValues)+8]
|
||||||
|
binary.LittleEndian.PutUint64(
|
||||||
|
paramValues[len(paramValues)-8:],
|
||||||
|
uint64(v),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
paramValues = append(paramValues,
|
||||||
|
uint64ToBytes(uint64(v))...,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
case float64:
|
case float64:
|
||||||
paramTypes[i+i] = byte(fieldTypeDouble)
|
paramTypes[i+i] = byte(fieldTypeDouble)
|
||||||
paramTypes[i+i+1] = 0x00
|
paramTypes[i+i+1] = 0x00
|
||||||
|
@ -1091,7 +1129,10 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
|
||||||
// In that case we must build the data packet with the new values buffer
|
// In that case we must build the data packet with the new values buffer
|
||||||
if valuesCap != cap(paramValues) {
|
if valuesCap != cap(paramValues) {
|
||||||
data = append(data[:pos], paramValues...)
|
data = append(data[:pos], paramValues...)
|
||||||
mc.buf.buf = data
|
if err = mc.buf.store(data); err != nil {
|
||||||
|
errLog.Print(err)
|
||||||
|
return errBadConnNoWrite
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pos += len(paramValues)
|
pos += len(paramValues)
|
||||||
|
@ -1261,7 +1302,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
|
||||||
rows.rs.columns[i].decimals,
|
rows.rs.columns[i].decimals,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
dest[i], err = formatBinaryDateTime(data[pos:pos+int(num)], dstlen, true)
|
dest[i], err = formatBinaryTime(data[pos:pos+int(num)], dstlen)
|
||||||
case rows.mc.parseTime:
|
case rows.mc.parseTime:
|
||||||
dest[i], err = parseBinaryDateTime(num, data[pos:], rows.mc.cfg.Loc)
|
dest[i], err = parseBinaryDateTime(num, data[pos:], rows.mc.cfg.Loc)
|
||||||
default:
|
default:
|
||||||
|
@ -1281,7 +1322,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dest[i], err = formatBinaryDateTime(data[pos:pos+int(num)], dstlen, false)
|
dest[i], err = formatBinaryDateTime(data[pos:pos+int(num)], dstlen)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
336
vendor/github.com/go-sql-driver/mysql/packets_test.go
generated
vendored
Normal file
336
vendor/github.com/go-sql-driver/mysql/packets_test.go
generated
vendored
Normal file
|
@ -0,0 +1,336 @@
|
||||||
|
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||||
|
//
|
||||||
|
// Copyright 2016 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/.
|
||||||
|
|
||||||
|
package mysql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errConnClosed = errors.New("connection is closed")
|
||||||
|
errConnTooManyReads = errors.New("too many reads")
|
||||||
|
errConnTooManyWrites = errors.New("too many writes")
|
||||||
|
)
|
||||||
|
|
||||||
|
// struct to mock a net.Conn for testing purposes
|
||||||
|
type mockConn struct {
|
||||||
|
laddr net.Addr
|
||||||
|
raddr net.Addr
|
||||||
|
data []byte
|
||||||
|
written []byte
|
||||||
|
queuedReplies [][]byte
|
||||||
|
closed bool
|
||||||
|
read int
|
||||||
|
reads int
|
||||||
|
writes int
|
||||||
|
maxReads int
|
||||||
|
maxWrites int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockConn) Read(b []byte) (n int, err error) {
|
||||||
|
if m.closed {
|
||||||
|
return 0, errConnClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
m.reads++
|
||||||
|
if m.maxReads > 0 && m.reads > m.maxReads {
|
||||||
|
return 0, errConnTooManyReads
|
||||||
|
}
|
||||||
|
|
||||||
|
n = copy(b, m.data)
|
||||||
|
m.read += n
|
||||||
|
m.data = m.data[n:]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (m *mockConn) Write(b []byte) (n int, err error) {
|
||||||
|
if m.closed {
|
||||||
|
return 0, errConnClosed
|
||||||
|
}
|
||||||
|
|
||||||
|
m.writes++
|
||||||
|
if m.maxWrites > 0 && m.writes > m.maxWrites {
|
||||||
|
return 0, errConnTooManyWrites
|
||||||
|
}
|
||||||
|
|
||||||
|
n = len(b)
|
||||||
|
m.written = append(m.written, b...)
|
||||||
|
|
||||||
|
if n > 0 && len(m.queuedReplies) > 0 {
|
||||||
|
m.data = m.queuedReplies[0]
|
||||||
|
m.queuedReplies = m.queuedReplies[1:]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (m *mockConn) Close() error {
|
||||||
|
m.closed = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (m *mockConn) LocalAddr() net.Addr {
|
||||||
|
return m.laddr
|
||||||
|
}
|
||||||
|
func (m *mockConn) RemoteAddr() net.Addr {
|
||||||
|
return m.raddr
|
||||||
|
}
|
||||||
|
func (m *mockConn) SetDeadline(t time.Time) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (m *mockConn) SetReadDeadline(t time.Time) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (m *mockConn) SetWriteDeadline(t time.Time) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure mockConn implements the net.Conn interface
|
||||||
|
var _ net.Conn = new(mockConn)
|
||||||
|
|
||||||
|
func newRWMockConn(sequence uint8) (*mockConn, *mysqlConn) {
|
||||||
|
conn := new(mockConn)
|
||||||
|
mc := &mysqlConn{
|
||||||
|
buf: newBuffer(conn),
|
||||||
|
cfg: NewConfig(),
|
||||||
|
netConn: conn,
|
||||||
|
closech: make(chan struct{}),
|
||||||
|
maxAllowedPacket: defaultMaxAllowedPacket,
|
||||||
|
sequence: sequence,
|
||||||
|
}
|
||||||
|
return conn, mc
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadPacketSingleByte(t *testing.T) {
|
||||||
|
conn := new(mockConn)
|
||||||
|
mc := &mysqlConn{
|
||||||
|
buf: newBuffer(conn),
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.data = []byte{0x01, 0x00, 0x00, 0x00, 0xff}
|
||||||
|
conn.maxReads = 1
|
||||||
|
packet, err := mc.readPacket()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(packet) != 1 {
|
||||||
|
t.Fatalf("unexpected packet length: expected %d, got %d", 1, len(packet))
|
||||||
|
}
|
||||||
|
if packet[0] != 0xff {
|
||||||
|
t.Fatalf("unexpected packet content: expected %x, got %x", 0xff, packet[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadPacketWrongSequenceID(t *testing.T) {
|
||||||
|
conn := new(mockConn)
|
||||||
|
mc := &mysqlConn{
|
||||||
|
buf: newBuffer(conn),
|
||||||
|
}
|
||||||
|
|
||||||
|
// too low sequence id
|
||||||
|
conn.data = []byte{0x01, 0x00, 0x00, 0x00, 0xff}
|
||||||
|
conn.maxReads = 1
|
||||||
|
mc.sequence = 1
|
||||||
|
_, err := mc.readPacket()
|
||||||
|
if err != ErrPktSync {
|
||||||
|
t.Errorf("expected ErrPktSync, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset
|
||||||
|
conn.reads = 0
|
||||||
|
mc.sequence = 0
|
||||||
|
mc.buf = newBuffer(conn)
|
||||||
|
|
||||||
|
// too high sequence id
|
||||||
|
conn.data = []byte{0x01, 0x00, 0x00, 0x42, 0xff}
|
||||||
|
_, err = mc.readPacket()
|
||||||
|
if err != ErrPktSyncMul {
|
||||||
|
t.Errorf("expected ErrPktSyncMul, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadPacketSplit(t *testing.T) {
|
||||||
|
conn := new(mockConn)
|
||||||
|
mc := &mysqlConn{
|
||||||
|
buf: newBuffer(conn),
|
||||||
|
}
|
||||||
|
|
||||||
|
data := make([]byte, maxPacketSize*2+4*3)
|
||||||
|
const pkt2ofs = maxPacketSize + 4
|
||||||
|
const pkt3ofs = 2 * (maxPacketSize + 4)
|
||||||
|
|
||||||
|
// case 1: payload has length maxPacketSize
|
||||||
|
data = data[:pkt2ofs+4]
|
||||||
|
|
||||||
|
// 1st packet has maxPacketSize length and sequence id 0
|
||||||
|
// ff ff ff 00 ...
|
||||||
|
data[0] = 0xff
|
||||||
|
data[1] = 0xff
|
||||||
|
data[2] = 0xff
|
||||||
|
|
||||||
|
// mark the payload start and end of 1st packet so that we can check if the
|
||||||
|
// content was correctly appended
|
||||||
|
data[4] = 0x11
|
||||||
|
data[maxPacketSize+3] = 0x22
|
||||||
|
|
||||||
|
// 2nd packet has payload length 0 and squence id 1
|
||||||
|
// 00 00 00 01
|
||||||
|
data[pkt2ofs+3] = 0x01
|
||||||
|
|
||||||
|
conn.data = data
|
||||||
|
conn.maxReads = 3
|
||||||
|
packet, err := mc.readPacket()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(packet) != maxPacketSize {
|
||||||
|
t.Fatalf("unexpected packet length: expected %d, got %d", maxPacketSize, len(packet))
|
||||||
|
}
|
||||||
|
if packet[0] != 0x11 {
|
||||||
|
t.Fatalf("unexpected payload start: expected %x, got %x", 0x11, packet[0])
|
||||||
|
}
|
||||||
|
if packet[maxPacketSize-1] != 0x22 {
|
||||||
|
t.Fatalf("unexpected payload end: expected %x, got %x", 0x22, packet[maxPacketSize-1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// case 2: payload has length which is a multiple of maxPacketSize
|
||||||
|
data = data[:cap(data)]
|
||||||
|
|
||||||
|
// 2nd packet now has maxPacketSize length
|
||||||
|
data[pkt2ofs] = 0xff
|
||||||
|
data[pkt2ofs+1] = 0xff
|
||||||
|
data[pkt2ofs+2] = 0xff
|
||||||
|
|
||||||
|
// mark the payload start and end of the 2nd packet
|
||||||
|
data[pkt2ofs+4] = 0x33
|
||||||
|
data[pkt2ofs+maxPacketSize+3] = 0x44
|
||||||
|
|
||||||
|
// 3rd packet has payload length 0 and squence id 2
|
||||||
|
// 00 00 00 02
|
||||||
|
data[pkt3ofs+3] = 0x02
|
||||||
|
|
||||||
|
conn.data = data
|
||||||
|
conn.reads = 0
|
||||||
|
conn.maxReads = 5
|
||||||
|
mc.sequence = 0
|
||||||
|
packet, err = mc.readPacket()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(packet) != 2*maxPacketSize {
|
||||||
|
t.Fatalf("unexpected packet length: expected %d, got %d", 2*maxPacketSize, len(packet))
|
||||||
|
}
|
||||||
|
if packet[0] != 0x11 {
|
||||||
|
t.Fatalf("unexpected payload start: expected %x, got %x", 0x11, packet[0])
|
||||||
|
}
|
||||||
|
if packet[2*maxPacketSize-1] != 0x44 {
|
||||||
|
t.Fatalf("unexpected payload end: expected %x, got %x", 0x44, packet[2*maxPacketSize-1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// case 3: payload has a length larger maxPacketSize, which is not an exact
|
||||||
|
// multiple of it
|
||||||
|
data = data[:pkt2ofs+4+42]
|
||||||
|
data[pkt2ofs] = 0x2a
|
||||||
|
data[pkt2ofs+1] = 0x00
|
||||||
|
data[pkt2ofs+2] = 0x00
|
||||||
|
data[pkt2ofs+4+41] = 0x44
|
||||||
|
|
||||||
|
conn.data = data
|
||||||
|
conn.reads = 0
|
||||||
|
conn.maxReads = 4
|
||||||
|
mc.sequence = 0
|
||||||
|
packet, err = mc.readPacket()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(packet) != maxPacketSize+42 {
|
||||||
|
t.Fatalf("unexpected packet length: expected %d, got %d", maxPacketSize+42, len(packet))
|
||||||
|
}
|
||||||
|
if packet[0] != 0x11 {
|
||||||
|
t.Fatalf("unexpected payload start: expected %x, got %x", 0x11, packet[0])
|
||||||
|
}
|
||||||
|
if packet[maxPacketSize+41] != 0x44 {
|
||||||
|
t.Fatalf("unexpected payload end: expected %x, got %x", 0x44, packet[maxPacketSize+41])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadPacketFail(t *testing.T) {
|
||||||
|
conn := new(mockConn)
|
||||||
|
mc := &mysqlConn{
|
||||||
|
buf: newBuffer(conn),
|
||||||
|
closech: make(chan struct{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
// illegal empty (stand-alone) packet
|
||||||
|
conn.data = []byte{0x00, 0x00, 0x00, 0x00}
|
||||||
|
conn.maxReads = 1
|
||||||
|
_, err := mc.readPacket()
|
||||||
|
if err != ErrInvalidConn {
|
||||||
|
t.Errorf("expected ErrInvalidConn, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset
|
||||||
|
conn.reads = 0
|
||||||
|
mc.sequence = 0
|
||||||
|
mc.buf = newBuffer(conn)
|
||||||
|
|
||||||
|
// fail to read header
|
||||||
|
conn.closed = true
|
||||||
|
_, err = mc.readPacket()
|
||||||
|
if err != ErrInvalidConn {
|
||||||
|
t.Errorf("expected ErrInvalidConn, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset
|
||||||
|
conn.closed = false
|
||||||
|
conn.reads = 0
|
||||||
|
mc.sequence = 0
|
||||||
|
mc.buf = newBuffer(conn)
|
||||||
|
|
||||||
|
// fail to read body
|
||||||
|
conn.maxReads = 1
|
||||||
|
_, err = mc.readPacket()
|
||||||
|
if err != ErrInvalidConn {
|
||||||
|
t.Errorf("expected ErrInvalidConn, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/go-sql-driver/mysql/pull/801
|
||||||
|
// not-NUL terminated plugin_name in init packet
|
||||||
|
func TestRegression801(t *testing.T) {
|
||||||
|
conn := new(mockConn)
|
||||||
|
mc := &mysqlConn{
|
||||||
|
buf: newBuffer(conn),
|
||||||
|
cfg: new(Config),
|
||||||
|
sequence: 42,
|
||||||
|
closech: make(chan struct{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.data = []byte{72, 0, 0, 42, 10, 53, 46, 53, 46, 56, 0, 165, 0, 0, 0,
|
||||||
|
60, 70, 63, 58, 68, 104, 34, 97, 0, 223, 247, 33, 2, 0, 15, 128, 21, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 98, 120, 114, 47, 85, 75, 109, 99, 51, 77,
|
||||||
|
50, 64, 0, 109, 121, 115, 113, 108, 95, 110, 97, 116, 105, 118, 101, 95,
|
||||||
|
112, 97, 115, 115, 119, 111, 114, 100}
|
||||||
|
conn.maxReads = 1
|
||||||
|
|
||||||
|
authData, pluginName, err := mc.readHandshakePacket()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("got error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if pluginName != "mysql_native_password" {
|
||||||
|
t.Errorf("expected plugin name 'mysql_native_password', got '%s'", pluginName)
|
||||||
|
}
|
||||||
|
|
||||||
|
expectedAuthData := []byte{60, 70, 63, 58, 68, 104, 34, 97, 98, 120, 114,
|
||||||
|
47, 85, 75, 109, 99, 51, 77, 50, 64}
|
||||||
|
if !bytes.Equal(authData, expectedAuthData) {
|
||||||
|
t.Errorf("expected authData '%v', got '%v'", expectedAuthData, authData)
|
||||||
|
}
|
||||||
|
}
|
7
vendor/github.com/go-sql-driver/mysql/rows.go
generated
vendored
7
vendor/github.com/go-sql-driver/mysql/rows.go
generated
vendored
|
@ -111,6 +111,13 @@ func (rows *mysqlRows) Close() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// flip the buffer for this connection if we need to drain it.
|
||||||
|
// note that for a successful query (i.e. one where rows.next()
|
||||||
|
// has been called until it returns false), `rows.mc` will be nil
|
||||||
|
// by the time the user calls `(*Rows).Close`, so we won't reach this
|
||||||
|
// see: https://github.com/golang/go/commit/651ddbdb5056ded455f47f9c494c67b389622a47
|
||||||
|
mc.buf.flip()
|
||||||
|
|
||||||
// Remove unread packets from stream
|
// Remove unread packets from stream
|
||||||
if !rows.rs.done {
|
if !rows.rs.done {
|
||||||
err = mc.readUntilEOF()
|
err = mc.readUntilEOF()
|
||||||
|
|
11
vendor/github.com/go-sql-driver/mysql/statement.go
generated
vendored
11
vendor/github.com/go-sql-driver/mysql/statement.go
generated
vendored
|
@ -13,7 +13,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type mysqlStmt struct {
|
type mysqlStmt struct {
|
||||||
|
@ -164,14 +163,8 @@ func (c converter) ConvertValue(v interface{}) (driver.Value, error) {
|
||||||
}
|
}
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
return rv.Int(), nil
|
return rv.Int(), nil
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32:
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
return int64(rv.Uint()), nil
|
return rv.Uint(), nil
|
||||||
case reflect.Uint64:
|
|
||||||
u64 := rv.Uint()
|
|
||||||
if u64 >= 1<<63 {
|
|
||||||
return strconv.FormatUint(u64, 10), nil
|
|
||||||
}
|
|
||||||
return int64(u64), nil
|
|
||||||
case reflect.Float32, reflect.Float64:
|
case reflect.Float32, reflect.Float64:
|
||||||
return rv.Float(), nil
|
return rv.Float(), nil
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
|
|
126
vendor/github.com/go-sql-driver/mysql/statement_test.go
generated
vendored
Normal file
126
vendor/github.com/go-sql-driver/mysql/statement_test.go
generated
vendored
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
// 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/.
|
||||||
|
|
||||||
|
package mysql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConvertDerivedString(t *testing.T) {
|
||||||
|
type derived string
|
||||||
|
|
||||||
|
output, err := converter{}.ConvertValue(derived("value"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Derived string type not convertible", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if output != "value" {
|
||||||
|
t.Fatalf("Derived string type not converted, got %#v %T", output, output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertDerivedByteSlice(t *testing.T) {
|
||||||
|
type derived []uint8
|
||||||
|
|
||||||
|
output, err := converter{}.ConvertValue(derived("value"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Byte slice not convertible", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if bytes.Compare(output.([]byte), []byte("value")) != 0 {
|
||||||
|
t.Fatalf("Byte slice not converted, got %#v %T", output, output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertDerivedUnsupportedSlice(t *testing.T) {
|
||||||
|
type derived []int
|
||||||
|
|
||||||
|
_, err := converter{}.ConvertValue(derived{1})
|
||||||
|
if err == nil || err.Error() != "unsupported type mysql.derived, a slice of int" {
|
||||||
|
t.Fatal("Unexpected error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertDerivedBool(t *testing.T) {
|
||||||
|
type derived bool
|
||||||
|
|
||||||
|
output, err := converter{}.ConvertValue(derived(true))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Derived bool type not convertible", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if output != true {
|
||||||
|
t.Fatalf("Derived bool type not converted, got %#v %T", output, output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertPointer(t *testing.T) {
|
||||||
|
str := "value"
|
||||||
|
|
||||||
|
output, err := converter{}.ConvertValue(&str)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Pointer type not convertible", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if output != "value" {
|
||||||
|
t.Fatalf("Pointer type not converted, got %#v %T", output, output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertSignedIntegers(t *testing.T) {
|
||||||
|
values := []interface{}{
|
||||||
|
int8(-42),
|
||||||
|
int16(-42),
|
||||||
|
int32(-42),
|
||||||
|
int64(-42),
|
||||||
|
int(-42),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, value := range values {
|
||||||
|
output, err := converter{}.ConvertValue(value)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%T type not convertible %s", value, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if output != int64(-42) {
|
||||||
|
t.Fatalf("%T type not converted, got %#v %T", value, output, output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConvertUnsignedIntegers(t *testing.T) {
|
||||||
|
values := []interface{}{
|
||||||
|
uint8(42),
|
||||||
|
uint16(42),
|
||||||
|
uint32(42),
|
||||||
|
uint64(42),
|
||||||
|
uint(42),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, value := range values {
|
||||||
|
output, err := converter{}.ConvertValue(value)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%T type not convertible %s", value, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if output != uint64(42) {
|
||||||
|
t.Fatalf("%T type not converted, got %#v %T", value, output, output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output, err := converter{}.ConvertValue(^uint64(0))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("uint64 high-bit not convertible", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if output != ^uint64(0) {
|
||||||
|
t.Fatalf("uint64 high-bit converted, got %#v %T", output, output)
|
||||||
|
}
|
||||||
|
}
|
283
vendor/github.com/go-sql-driver/mysql/utils.go
generated
vendored
283
vendor/github.com/go-sql-driver/mysql/utils.go
generated
vendored
|
@ -10,10 +10,13 @@ package mysql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"database/sql"
|
||||||
"database/sql/driver"
|
"database/sql/driver"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
@ -53,7 +56,7 @@ var (
|
||||||
// db, err := sql.Open("mysql", "user@tcp(localhost:3306)/test?tls=custom")
|
// db, err := sql.Open("mysql", "user@tcp(localhost:3306)/test?tls=custom")
|
||||||
//
|
//
|
||||||
func RegisterTLSConfig(key string, config *tls.Config) error {
|
func RegisterTLSConfig(key string, config *tls.Config) error {
|
||||||
if _, isBool := readBool(key); isBool || strings.ToLower(key) == "skip-verify" {
|
if _, isBool := readBool(key); isBool || strings.ToLower(key) == "skip-verify" || strings.ToLower(key) == "preferred" {
|
||||||
return fmt.Errorf("key '%s' is reserved", key)
|
return fmt.Errorf("key '%s' is reserved", key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +82,7 @@ func DeregisterTLSConfig(key string) {
|
||||||
func getTLSConfigClone(key string) (config *tls.Config) {
|
func getTLSConfigClone(key string) (config *tls.Config) {
|
||||||
tlsConfigLock.RLock()
|
tlsConfigLock.RLock()
|
||||||
if v, ok := tlsConfigRegistry[key]; ok {
|
if v, ok := tlsConfigRegistry[key]; ok {
|
||||||
config = cloneTLSConfig(v)
|
config = v.Clone()
|
||||||
}
|
}
|
||||||
tlsConfigLock.RUnlock()
|
tlsConfigLock.RUnlock()
|
||||||
return
|
return
|
||||||
|
@ -227,87 +230,104 @@ var zeroDateTime = []byte("0000-00-00 00:00:00.000000")
|
||||||
const digits01 = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
|
const digits01 = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
|
||||||
const digits10 = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
|
const digits10 = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
|
||||||
|
|
||||||
func formatBinaryDateTime(src []byte, length uint8, justTime bool) (driver.Value, error) {
|
func appendMicrosecs(dst, src []byte, decimals int) []byte {
|
||||||
|
if decimals <= 0 {
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
if len(src) == 0 {
|
||||||
|
return append(dst, ".000000"[:decimals+1]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
microsecs := binary.LittleEndian.Uint32(src[:4])
|
||||||
|
p1 := byte(microsecs / 10000)
|
||||||
|
microsecs -= 10000 * uint32(p1)
|
||||||
|
p2 := byte(microsecs / 100)
|
||||||
|
microsecs -= 100 * uint32(p2)
|
||||||
|
p3 := byte(microsecs)
|
||||||
|
|
||||||
|
switch decimals {
|
||||||
|
default:
|
||||||
|
return append(dst, '.',
|
||||||
|
digits10[p1], digits01[p1],
|
||||||
|
digits10[p2], digits01[p2],
|
||||||
|
digits10[p3], digits01[p3],
|
||||||
|
)
|
||||||
|
case 1:
|
||||||
|
return append(dst, '.',
|
||||||
|
digits10[p1],
|
||||||
|
)
|
||||||
|
case 2:
|
||||||
|
return append(dst, '.',
|
||||||
|
digits10[p1], digits01[p1],
|
||||||
|
)
|
||||||
|
case 3:
|
||||||
|
return append(dst, '.',
|
||||||
|
digits10[p1], digits01[p1],
|
||||||
|
digits10[p2],
|
||||||
|
)
|
||||||
|
case 4:
|
||||||
|
return append(dst, '.',
|
||||||
|
digits10[p1], digits01[p1],
|
||||||
|
digits10[p2], digits01[p2],
|
||||||
|
)
|
||||||
|
case 5:
|
||||||
|
return append(dst, '.',
|
||||||
|
digits10[p1], digits01[p1],
|
||||||
|
digits10[p2], digits01[p2],
|
||||||
|
digits10[p3],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatBinaryDateTime(src []byte, length uint8) (driver.Value, error) {
|
||||||
// length expects the deterministic length of the zero value,
|
// length expects the deterministic length of the zero value,
|
||||||
// negative time and 100+ hours are automatically added if needed
|
// negative time and 100+ hours are automatically added if needed
|
||||||
if len(src) == 0 {
|
if len(src) == 0 {
|
||||||
if justTime {
|
|
||||||
return zeroDateTime[11 : 11+length], nil
|
|
||||||
}
|
|
||||||
return zeroDateTime[:length], nil
|
return zeroDateTime[:length], nil
|
||||||
}
|
}
|
||||||
var dst []byte // return value
|
var dst []byte // return value
|
||||||
var pt, p1, p2, p3 byte // current digit pair
|
var p1, p2, p3 byte // current digit pair
|
||||||
var zOffs byte // offset of value in zeroDateTime
|
|
||||||
if justTime {
|
switch length {
|
||||||
switch length {
|
case 10, 19, 21, 22, 23, 24, 25, 26:
|
||||||
case
|
default:
|
||||||
8, // time (can be up to 10 when negative and 100+ hours)
|
t := "DATE"
|
||||||
10, 11, 12, 13, 14, 15: // time with fractional seconds
|
if length > 10 {
|
||||||
default:
|
t += "TIME"
|
||||||
return nil, fmt.Errorf("illegal TIME length %d", length)
|
|
||||||
}
|
}
|
||||||
switch len(src) {
|
return nil, fmt.Errorf("illegal %s length %d", t, length)
|
||||||
case 8, 12:
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("invalid TIME packet length %d", len(src))
|
|
||||||
}
|
|
||||||
// +2 to enable negative time and 100+ hours
|
|
||||||
dst = make([]byte, 0, length+2)
|
|
||||||
if src[0] == 1 {
|
|
||||||
dst = append(dst, '-')
|
|
||||||
}
|
|
||||||
if src[1] != 0 {
|
|
||||||
hour := uint16(src[1])*24 + uint16(src[5])
|
|
||||||
pt = byte(hour / 100)
|
|
||||||
p1 = byte(hour - 100*uint16(pt))
|
|
||||||
dst = append(dst, digits01[pt])
|
|
||||||
} else {
|
|
||||||
p1 = src[5]
|
|
||||||
}
|
|
||||||
zOffs = 11
|
|
||||||
src = src[6:]
|
|
||||||
} else {
|
|
||||||
switch length {
|
|
||||||
case 10, 19, 21, 22, 23, 24, 25, 26:
|
|
||||||
default:
|
|
||||||
t := "DATE"
|
|
||||||
if length > 10 {
|
|
||||||
t += "TIME"
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("illegal %s length %d", t, length)
|
|
||||||
}
|
|
||||||
switch len(src) {
|
|
||||||
case 4, 7, 11:
|
|
||||||
default:
|
|
||||||
t := "DATE"
|
|
||||||
if length > 10 {
|
|
||||||
t += "TIME"
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("illegal %s packet length %d", t, len(src))
|
|
||||||
}
|
|
||||||
dst = make([]byte, 0, length)
|
|
||||||
// start with the date
|
|
||||||
year := binary.LittleEndian.Uint16(src[:2])
|
|
||||||
pt = byte(year / 100)
|
|
||||||
p1 = byte(year - 100*uint16(pt))
|
|
||||||
p2, p3 = src[2], src[3]
|
|
||||||
dst = append(dst,
|
|
||||||
digits10[pt], digits01[pt],
|
|
||||||
digits10[p1], digits01[p1], '-',
|
|
||||||
digits10[p2], digits01[p2], '-',
|
|
||||||
digits10[p3], digits01[p3],
|
|
||||||
)
|
|
||||||
if length == 10 {
|
|
||||||
return dst, nil
|
|
||||||
}
|
|
||||||
if len(src) == 4 {
|
|
||||||
return append(dst, zeroDateTime[10:length]...), nil
|
|
||||||
}
|
|
||||||
dst = append(dst, ' ')
|
|
||||||
p1 = src[4] // hour
|
|
||||||
src = src[5:]
|
|
||||||
}
|
}
|
||||||
|
switch len(src) {
|
||||||
|
case 4, 7, 11:
|
||||||
|
default:
|
||||||
|
t := "DATE"
|
||||||
|
if length > 10 {
|
||||||
|
t += "TIME"
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("illegal %s packet length %d", t, len(src))
|
||||||
|
}
|
||||||
|
dst = make([]byte, 0, length)
|
||||||
|
// start with the date
|
||||||
|
year := binary.LittleEndian.Uint16(src[:2])
|
||||||
|
pt := year / 100
|
||||||
|
p1 = byte(year - 100*uint16(pt))
|
||||||
|
p2, p3 = src[2], src[3]
|
||||||
|
dst = append(dst,
|
||||||
|
digits10[pt], digits01[pt],
|
||||||
|
digits10[p1], digits01[p1], '-',
|
||||||
|
digits10[p2], digits01[p2], '-',
|
||||||
|
digits10[p3], digits01[p3],
|
||||||
|
)
|
||||||
|
if length == 10 {
|
||||||
|
return dst, nil
|
||||||
|
}
|
||||||
|
if len(src) == 4 {
|
||||||
|
return append(dst, zeroDateTime[10:length]...), nil
|
||||||
|
}
|
||||||
|
dst = append(dst, ' ')
|
||||||
|
p1 = src[4] // hour
|
||||||
|
src = src[5:]
|
||||||
|
|
||||||
// p1 is 2-digit hour, src is after hour
|
// p1 is 2-digit hour, src is after hour
|
||||||
p2, p3 = src[0], src[1]
|
p2, p3 = src[0], src[1]
|
||||||
dst = append(dst,
|
dst = append(dst,
|
||||||
|
@ -315,51 +335,49 @@ func formatBinaryDateTime(src []byte, length uint8, justTime bool) (driver.Value
|
||||||
digits10[p2], digits01[p2], ':',
|
digits10[p2], digits01[p2], ':',
|
||||||
digits10[p3], digits01[p3],
|
digits10[p3], digits01[p3],
|
||||||
)
|
)
|
||||||
if length <= byte(len(dst)) {
|
return appendMicrosecs(dst, src[2:], int(length)-20), nil
|
||||||
return dst, nil
|
}
|
||||||
}
|
|
||||||
src = src[2:]
|
func formatBinaryTime(src []byte, length uint8) (driver.Value, error) {
|
||||||
|
// length expects the deterministic length of the zero value,
|
||||||
|
// negative time and 100+ hours are automatically added if needed
|
||||||
if len(src) == 0 {
|
if len(src) == 0 {
|
||||||
return append(dst, zeroDateTime[19:zOffs+length]...), nil
|
return zeroDateTime[11 : 11+length], nil
|
||||||
}
|
}
|
||||||
microsecs := binary.LittleEndian.Uint32(src[:4])
|
var dst []byte // return value
|
||||||
p1 = byte(microsecs / 10000)
|
|
||||||
microsecs -= 10000 * uint32(p1)
|
switch length {
|
||||||
p2 = byte(microsecs / 100)
|
case
|
||||||
microsecs -= 100 * uint32(p2)
|
8, // time (can be up to 10 when negative and 100+ hours)
|
||||||
p3 = byte(microsecs)
|
10, 11, 12, 13, 14, 15: // time with fractional seconds
|
||||||
switch decimals := zOffs + length - 20; decimals {
|
|
||||||
default:
|
default:
|
||||||
return append(dst, '.',
|
return nil, fmt.Errorf("illegal TIME length %d", length)
|
||||||
digits10[p1], digits01[p1],
|
|
||||||
digits10[p2], digits01[p2],
|
|
||||||
digits10[p3], digits01[p3],
|
|
||||||
), nil
|
|
||||||
case 1:
|
|
||||||
return append(dst, '.',
|
|
||||||
digits10[p1],
|
|
||||||
), nil
|
|
||||||
case 2:
|
|
||||||
return append(dst, '.',
|
|
||||||
digits10[p1], digits01[p1],
|
|
||||||
), nil
|
|
||||||
case 3:
|
|
||||||
return append(dst, '.',
|
|
||||||
digits10[p1], digits01[p1],
|
|
||||||
digits10[p2],
|
|
||||||
), nil
|
|
||||||
case 4:
|
|
||||||
return append(dst, '.',
|
|
||||||
digits10[p1], digits01[p1],
|
|
||||||
digits10[p2], digits01[p2],
|
|
||||||
), nil
|
|
||||||
case 5:
|
|
||||||
return append(dst, '.',
|
|
||||||
digits10[p1], digits01[p1],
|
|
||||||
digits10[p2], digits01[p2],
|
|
||||||
digits10[p3],
|
|
||||||
), nil
|
|
||||||
}
|
}
|
||||||
|
switch len(src) {
|
||||||
|
case 8, 12:
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("invalid TIME packet length %d", len(src))
|
||||||
|
}
|
||||||
|
// +2 to enable negative time and 100+ hours
|
||||||
|
dst = make([]byte, 0, length+2)
|
||||||
|
if src[0] == 1 {
|
||||||
|
dst = append(dst, '-')
|
||||||
|
}
|
||||||
|
days := binary.LittleEndian.Uint32(src[1:5])
|
||||||
|
hours := int64(days)*24 + int64(src[5])
|
||||||
|
|
||||||
|
if hours >= 100 {
|
||||||
|
dst = strconv.AppendInt(dst, hours, 10)
|
||||||
|
} else {
|
||||||
|
dst = append(dst, digits10[hours], digits01[hours])
|
||||||
|
}
|
||||||
|
|
||||||
|
min, sec := src[6], src[7]
|
||||||
|
dst = append(dst, ':',
|
||||||
|
digits10[min], digits01[min], ':',
|
||||||
|
digits10[sec], digits01[sec],
|
||||||
|
)
|
||||||
|
return appendMicrosecs(dst, src[8:], int(length)-9), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
|
@ -666,7 +684,7 @@ type atomicBool struct {
|
||||||
value uint32
|
value uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsSet returns wether the current boolean value is true
|
// IsSet returns whether the current boolean value is true
|
||||||
func (ab *atomicBool) IsSet() bool {
|
func (ab *atomicBool) IsSet() bool {
|
||||||
return atomic.LoadUint32(&ab.value) > 0
|
return atomic.LoadUint32(&ab.value) > 0
|
||||||
}
|
}
|
||||||
|
@ -680,7 +698,7 @@ func (ab *atomicBool) Set(value bool) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TrySet sets the value of the bool and returns wether the value changed
|
// TrySet sets the value of the bool and returns whether the value changed
|
||||||
func (ab *atomicBool) TrySet(value bool) bool {
|
func (ab *atomicBool) TrySet(value bool) bool {
|
||||||
if value {
|
if value {
|
||||||
return atomic.SwapUint32(&ab.value, 1) == 0
|
return atomic.SwapUint32(&ab.value, 1) == 0
|
||||||
|
@ -708,3 +726,30 @@ func (ae *atomicError) Value() error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func namedValueToValue(named []driver.NamedValue) ([]driver.Value, error) {
|
||||||
|
dargs := make([]driver.Value, len(named))
|
||||||
|
for n, param := range named {
|
||||||
|
if len(param.Name) > 0 {
|
||||||
|
// TODO: support the use of Named Parameters #561
|
||||||
|
return nil, errors.New("mysql: driver does not support the use of Named Parameters")
|
||||||
|
}
|
||||||
|
dargs[n] = param.Value
|
||||||
|
}
|
||||||
|
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 "", fmt.Errorf("mysql: unsupported isolation level: %v", level)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
40
vendor/github.com/go-sql-driver/mysql/utils_go17.go
generated
vendored
40
vendor/github.com/go-sql-driver/mysql/utils_go17.go
generated
vendored
|
@ -1,40 +0,0 @@
|
||||||
// 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.7
|
|
||||||
// +build !go1.8
|
|
||||||
|
|
||||||
package mysql
|
|
||||||
|
|
||||||
import "crypto/tls"
|
|
||||||
|
|
||||||
func cloneTLSConfig(c *tls.Config) *tls.Config {
|
|
||||||
return &tls.Config{
|
|
||||||
Rand: c.Rand,
|
|
||||||
Time: c.Time,
|
|
||||||
Certificates: c.Certificates,
|
|
||||||
NameToCertificate: c.NameToCertificate,
|
|
||||||
GetCertificate: c.GetCertificate,
|
|
||||||
RootCAs: c.RootCAs,
|
|
||||||
NextProtos: c.NextProtos,
|
|
||||||
ServerName: c.ServerName,
|
|
||||||
ClientAuth: c.ClientAuth,
|
|
||||||
ClientCAs: c.ClientCAs,
|
|
||||||
InsecureSkipVerify: c.InsecureSkipVerify,
|
|
||||||
CipherSuites: c.CipherSuites,
|
|
||||||
PreferServerCipherSuites: c.PreferServerCipherSuites,
|
|
||||||
SessionTicketsDisabled: c.SessionTicketsDisabled,
|
|
||||||
SessionTicketKey: c.SessionTicketKey,
|
|
||||||
ClientSessionCache: c.ClientSessionCache,
|
|
||||||
MinVersion: c.MinVersion,
|
|
||||||
MaxVersion: c.MaxVersion,
|
|
||||||
CurvePreferences: c.CurvePreferences,
|
|
||||||
DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled,
|
|
||||||
Renegotiation: c.Renegotiation,
|
|
||||||
}
|
|
||||||
}
|
|
50
vendor/github.com/go-sql-driver/mysql/utils_go18.go
generated
vendored
50
vendor/github.com/go-sql-driver/mysql/utils_go18.go
generated
vendored
|
@ -1,50 +0,0 @@
|
||||||
// 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 (
|
|
||||||
"crypto/tls"
|
|
||||||
"database/sql"
|
|
||||||
"database/sql/driver"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func cloneTLSConfig(c *tls.Config) *tls.Config {
|
|
||||||
return c.Clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
func namedValueToValue(named []driver.NamedValue) ([]driver.Value, error) {
|
|
||||||
dargs := make([]driver.Value, len(named))
|
|
||||||
for n, param := range named {
|
|
||||||
if len(param.Name) > 0 {
|
|
||||||
// TODO: support the use of Named Parameters #561
|
|
||||||
return nil, errors.New("mysql: driver does not support the use of Named Parameters")
|
|
||||||
}
|
|
||||||
dargs[n] = param.Value
|
|
||||||
}
|
|
||||||
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 "", fmt.Errorf("mysql: unsupported isolation level: %v", level)
|
|
||||||
}
|
|
||||||
}
|
|
334
vendor/github.com/go-sql-driver/mysql/utils_test.go
generated
vendored
Normal file
334
vendor/github.com/go-sql-driver/mysql/utils_test.go
generated
vendored
Normal file
|
@ -0,0 +1,334 @@
|
||||||
|
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||||
|
//
|
||||||
|
// Copyright 2013 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/.
|
||||||
|
|
||||||
|
package mysql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"database/sql"
|
||||||
|
"database/sql/driver"
|
||||||
|
"encoding/binary"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestScanNullTime(t *testing.T) {
|
||||||
|
var scanTests = []struct {
|
||||||
|
in interface{}
|
||||||
|
error bool
|
||||||
|
valid bool
|
||||||
|
time time.Time
|
||||||
|
}{
|
||||||
|
{tDate, false, true, tDate},
|
||||||
|
{sDate, false, true, tDate},
|
||||||
|
{[]byte(sDate), false, true, tDate},
|
||||||
|
{tDateTime, false, true, tDateTime},
|
||||||
|
{sDateTime, false, true, tDateTime},
|
||||||
|
{[]byte(sDateTime), false, true, tDateTime},
|
||||||
|
{tDate0, false, true, tDate0},
|
||||||
|
{sDate0, false, true, tDate0},
|
||||||
|
{[]byte(sDate0), false, true, tDate0},
|
||||||
|
{sDateTime0, false, true, tDate0},
|
||||||
|
{[]byte(sDateTime0), false, true, tDate0},
|
||||||
|
{"", true, false, tDate0},
|
||||||
|
{"1234", true, false, tDate0},
|
||||||
|
{0, true, false, tDate0},
|
||||||
|
}
|
||||||
|
|
||||||
|
var nt = NullTime{}
|
||||||
|
var err error
|
||||||
|
|
||||||
|
for _, tst := range scanTests {
|
||||||
|
err = nt.Scan(tst.in)
|
||||||
|
if (err != nil) != tst.error {
|
||||||
|
t.Errorf("%v: expected error status %t, got %t", tst.in, tst.error, (err != nil))
|
||||||
|
}
|
||||||
|
if nt.Valid != tst.valid {
|
||||||
|
t.Errorf("%v: expected valid status %t, got %t", tst.in, tst.valid, nt.Valid)
|
||||||
|
}
|
||||||
|
if nt.Time != tst.time {
|
||||||
|
t.Errorf("%v: expected time %v, got %v", tst.in, tst.time, nt.Time)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLengthEncodedInteger(t *testing.T) {
|
||||||
|
var integerTests = []struct {
|
||||||
|
num uint64
|
||||||
|
encoded []byte
|
||||||
|
}{
|
||||||
|
{0x0000000000000000, []byte{0x00}},
|
||||||
|
{0x0000000000000012, []byte{0x12}},
|
||||||
|
{0x00000000000000fa, []byte{0xfa}},
|
||||||
|
{0x0000000000000100, []byte{0xfc, 0x00, 0x01}},
|
||||||
|
{0x0000000000001234, []byte{0xfc, 0x34, 0x12}},
|
||||||
|
{0x000000000000ffff, []byte{0xfc, 0xff, 0xff}},
|
||||||
|
{0x0000000000010000, []byte{0xfd, 0x00, 0x00, 0x01}},
|
||||||
|
{0x0000000000123456, []byte{0xfd, 0x56, 0x34, 0x12}},
|
||||||
|
{0x0000000000ffffff, []byte{0xfd, 0xff, 0xff, 0xff}},
|
||||||
|
{0x0000000001000000, []byte{0xfe, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00}},
|
||||||
|
{0x123456789abcdef0, []byte{0xfe, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12}},
|
||||||
|
{0xffffffffffffffff, []byte{0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tst := range integerTests {
|
||||||
|
num, isNull, numLen := readLengthEncodedInteger(tst.encoded)
|
||||||
|
if isNull {
|
||||||
|
t.Errorf("%x: expected %d, got NULL", tst.encoded, tst.num)
|
||||||
|
}
|
||||||
|
if num != tst.num {
|
||||||
|
t.Errorf("%x: expected %d, got %d", tst.encoded, tst.num, num)
|
||||||
|
}
|
||||||
|
if numLen != len(tst.encoded) {
|
||||||
|
t.Errorf("%x: expected size %d, got %d", tst.encoded, len(tst.encoded), numLen)
|
||||||
|
}
|
||||||
|
encoded := appendLengthEncodedInteger(nil, num)
|
||||||
|
if !bytes.Equal(encoded, tst.encoded) {
|
||||||
|
t.Errorf("%v: expected %x, got %x", num, tst.encoded, encoded)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatBinaryDateTime(t *testing.T) {
|
||||||
|
rawDate := [11]byte{}
|
||||||
|
binary.LittleEndian.PutUint16(rawDate[:2], 1978) // years
|
||||||
|
rawDate[2] = 12 // months
|
||||||
|
rawDate[3] = 30 // days
|
||||||
|
rawDate[4] = 15 // hours
|
||||||
|
rawDate[5] = 46 // minutes
|
||||||
|
rawDate[6] = 23 // seconds
|
||||||
|
binary.LittleEndian.PutUint32(rawDate[7:], 987654) // microseconds
|
||||||
|
expect := func(expected string, inlen, outlen uint8) {
|
||||||
|
actual, _ := formatBinaryDateTime(rawDate[:inlen], outlen)
|
||||||
|
bytes, ok := actual.([]byte)
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("formatBinaryDateTime must return []byte, was %T", actual)
|
||||||
|
}
|
||||||
|
if string(bytes) != expected {
|
||||||
|
t.Errorf(
|
||||||
|
"expected %q, got %q for length in %d, out %d",
|
||||||
|
expected, actual, inlen, outlen,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect("0000-00-00", 0, 10)
|
||||||
|
expect("0000-00-00 00:00:00", 0, 19)
|
||||||
|
expect("1978-12-30", 4, 10)
|
||||||
|
expect("1978-12-30 15:46:23", 7, 19)
|
||||||
|
expect("1978-12-30 15:46:23.987654", 11, 26)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFormatBinaryTime(t *testing.T) {
|
||||||
|
expect := func(expected string, src []byte, outlen uint8) {
|
||||||
|
actual, _ := formatBinaryTime(src, outlen)
|
||||||
|
bytes, ok := actual.([]byte)
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("formatBinaryDateTime must return []byte, was %T", actual)
|
||||||
|
}
|
||||||
|
if string(bytes) != expected {
|
||||||
|
t.Errorf(
|
||||||
|
"expected %q, got %q for src=%q and outlen=%d",
|
||||||
|
expected, actual, src, outlen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// binary format:
|
||||||
|
// sign (0: positive, 1: negative), days(4), hours, minutes, seconds, micro(4)
|
||||||
|
|
||||||
|
// Zeros
|
||||||
|
expect("00:00:00", []byte{}, 8)
|
||||||
|
expect("00:00:00.0", []byte{}, 10)
|
||||||
|
expect("00:00:00.000000", []byte{}, 15)
|
||||||
|
|
||||||
|
// Without micro(4)
|
||||||
|
expect("12:34:56", []byte{0, 0, 0, 0, 0, 12, 34, 56}, 8)
|
||||||
|
expect("-12:34:56", []byte{1, 0, 0, 0, 0, 12, 34, 56}, 8)
|
||||||
|
expect("12:34:56.00", []byte{0, 0, 0, 0, 0, 12, 34, 56}, 11)
|
||||||
|
expect("24:34:56", []byte{0, 1, 0, 0, 0, 0, 34, 56}, 8)
|
||||||
|
expect("-99:34:56", []byte{1, 4, 0, 0, 0, 3, 34, 56}, 8)
|
||||||
|
expect("103079215103:34:56", []byte{0, 255, 255, 255, 255, 23, 34, 56}, 8)
|
||||||
|
|
||||||
|
// With micro(4)
|
||||||
|
expect("12:34:56.00", []byte{0, 0, 0, 0, 0, 12, 34, 56, 99, 0, 0, 0}, 11)
|
||||||
|
expect("12:34:56.000099", []byte{0, 0, 0, 0, 0, 12, 34, 56, 99, 0, 0, 0}, 15)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEscapeBackslash(t *testing.T) {
|
||||||
|
expect := func(expected, value string) {
|
||||||
|
actual := string(escapeBytesBackslash([]byte{}, []byte(value)))
|
||||||
|
if actual != expected {
|
||||||
|
t.Errorf(
|
||||||
|
"expected %s, got %s",
|
||||||
|
expected, actual,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
actual = string(escapeStringBackslash([]byte{}, value))
|
||||||
|
if actual != expected {
|
||||||
|
t.Errorf(
|
||||||
|
"expected %s, got %s",
|
||||||
|
expected, actual,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect("foo\\0bar", "foo\x00bar")
|
||||||
|
expect("foo\\nbar", "foo\nbar")
|
||||||
|
expect("foo\\rbar", "foo\rbar")
|
||||||
|
expect("foo\\Zbar", "foo\x1abar")
|
||||||
|
expect("foo\\\"bar", "foo\"bar")
|
||||||
|
expect("foo\\\\bar", "foo\\bar")
|
||||||
|
expect("foo\\'bar", "foo'bar")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEscapeQuotes(t *testing.T) {
|
||||||
|
expect := func(expected, value string) {
|
||||||
|
actual := string(escapeBytesQuotes([]byte{}, []byte(value)))
|
||||||
|
if actual != expected {
|
||||||
|
t.Errorf(
|
||||||
|
"expected %s, got %s",
|
||||||
|
expected, actual,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
actual = string(escapeStringQuotes([]byte{}, value))
|
||||||
|
if actual != expected {
|
||||||
|
t.Errorf(
|
||||||
|
"expected %s, got %s",
|
||||||
|
expected, actual,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect("foo\x00bar", "foo\x00bar") // not affected
|
||||||
|
expect("foo\nbar", "foo\nbar") // not affected
|
||||||
|
expect("foo\rbar", "foo\rbar") // not affected
|
||||||
|
expect("foo\x1abar", "foo\x1abar") // not affected
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
expectedErr := "mysql: unsupported isolation level: 7"
|
||||||
|
actual, err := mapIsolationLevel(driver.IsolationLevel(sql.LevelLinearizable))
|
||||||
|
if actual != "" || err == nil {
|
||||||
|
t.Fatal("Expected error on unsupported isolation level")
|
||||||
|
}
|
||||||
|
if err.Error() != expectedErr {
|
||||||
|
t.Fatalf("Expected error to be %q, got %q", expectedErr, err)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue