Просмотр исходного кода

mysql: update mysql driver to latest master

The latest master implements the ColumnType interface.

See https://github.com/go-sql-driver/mysql/issues/495

This removes the custom code in the vendor folder that
did a ColumnType mapping.
Daniel Lee 8 лет назад
Родитель
Сommit
7bc7af6c39

+ 23 - 0
vendor/github.com/go-sql-driver/mysql/AUTHORS

@@ -12,38 +12,57 @@
 # Individual Persons
 # Individual Persons
 
 
 Aaron Hopkins <go-sql-driver at die.net>
 Aaron Hopkins <go-sql-driver at die.net>
+Achille Roussel <achille.roussel at gmail.com>
 Arne Hormann <arnehormann at gmail.com>
 Arne Hormann <arnehormann at gmail.com>
+Asta Xie <xiemengjun at gmail.com>
+Bulat Gaifullin <gaifullinbf at gmail.com>
 Carlos Nieto <jose.carlos at menteslibres.net>
 Carlos Nieto <jose.carlos at menteslibres.net>
 Chris Moos <chris at tech9computers.com>
 Chris Moos <chris at tech9computers.com>
+Daniel Montoya <dsmontoyam at gmail.com>
 Daniel Nichter <nil at codenode.com>
 Daniel Nichter <nil at codenode.com>
 Daniël van Eeden <git at myname.nl>
 Daniël van Eeden <git at myname.nl>
+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>
+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>
 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>
+ICHINOSE Shogo <shogo82148 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>
+Jeffrey Charles <jeffreycharles 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>
 Julien Schmidt <go-sql-driver at julienschmidt.com>
 Julien Schmidt <go-sql-driver at julienschmidt.com>
+Justin Li <jli at j-li.net>
+Justin Nuß <nuss.justin at gmail.com>
 Kamil Dziedzic <kamil at klecza.pl>
 Kamil Dziedzic <kamil at klecza.pl>
 Kevin Malachowski <kevin at chowski.com>
 Kevin Malachowski <kevin at chowski.com>
+Kieron Woodhouse <kieron.woodhouse at infosum.com>
 Lennart Rudolph <lrudolph at hmc.edu>
 Lennart Rudolph <lrudolph at hmc.edu>
 Leonardo YongUk Kim <dalinaum at gmail.com>
 Leonardo YongUk Kim <dalinaum at gmail.com>
+Linh Tran Tuan <linhduonggnu at gmail.com>
+Lion Yang <lion at aosc.xyz>
 Luca Looz <luca.looz92 at gmail.com>
 Luca Looz <luca.looz92 at gmail.com>
 Lucas Liu <extrafliu at gmail.com>
 Lucas Liu <extrafliu at gmail.com>
 Luke Scott <luke at webconnex.com>
 Luke Scott <luke at webconnex.com>
+Maciej Zimnoch <maciej.zimnoch@codilime.com>
 Michael Woolnough <michael.woolnough at gmail.com>
 Michael Woolnough <michael.woolnough at gmail.com>
 Nicola Peduzzi <thenikso at gmail.com>
 Nicola Peduzzi <thenikso at gmail.com>
 Olivier Mengué <dolmen at cpan.org>
 Olivier Mengué <dolmen at cpan.org>
+oscarzhao <oscarzhaosl at gmail.com>
 Paul Bonser <misterpib at gmail.com>
 Paul Bonser <misterpib at gmail.com>
 Peter Schultz <peter.schultz at classmarkets.com>
 Peter Schultz <peter.schultz at classmarkets.com>
+Rebecca Chin <rchin at pivotal.io>
 Runrioter Wung <runrioter at gmail.com>
 Runrioter Wung <runrioter at gmail.com>
+Robert Russell <robert at rrbrussell.com>
+Shuode Li <elemount at qq.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>
@@ -55,5 +74,9 @@ Zhenye Xie <xiezhenye at gmail.com>
 # Organizations
 # Organizations
 
 
 Barracuda Networks, Inc.
 Barracuda Networks, Inc.
+Counting Ltd.
 Google Inc.
 Google Inc.
+InfoSum Ltd.
+Keybase Inc.
+Pivotal Inc.
 Stripe Inc.
 Stripe Inc.

+ 46 - 19
vendor/github.com/go-sql-driver/mysql/README.md

@@ -16,6 +16,8 @@ A MySQL-Driver for Go's [database/sql](https://golang.org/pkg/database/sql/) pac
       * [Parameters](#parameters)
       * [Parameters](#parameters)
       * [Examples](#examples)
       * [Examples](#examples)
     * [Connection pool and timeouts](#connection-pool-and-timeouts)
     * [Connection pool and timeouts](#connection-pool-and-timeouts)
+    * [context.Context Support](#contextcontext-support)
+    * [ColumnType Support](#columntype-support)
     * [LOAD DATA LOCAL INFILE support](#load-data-local-infile-support)
     * [LOAD DATA LOCAL INFILE support](#load-data-local-infile-support)
     * [time.Time support](#timetime-support)
     * [time.Time support](#timetime-support)
     * [Unicode support](#unicode-support)
     * [Unicode support](#unicode-support)
@@ -38,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.2 or higher
+  * Go 1.7 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+)
 
 
 ---------------------------------------
 ---------------------------------------
@@ -46,7 +48,7 @@ A MySQL-Driver for Go's [database/sql](https://golang.org/pkg/database/sql/) pac
 ## Installation
 ## Installation
 Simple install the package to your [$GOPATH](https://github.com/golang/go/wiki/GOPATH "GOPATH") with the [go tool](https://golang.org/cmd/go/ "go command") from shell:
 Simple install the package to your [$GOPATH](https://github.com/golang/go/wiki/GOPATH "GOPATH") with the [go tool](https://golang.org/cmd/go/ "go command") from shell:
 ```bash
 ```bash
-$ go get github.com/go-sql-driver/mysql
+$ go get -u github.com/go-sql-driver/mysql
 ```
 ```
 Make sure [Git is installed](https://git-scm.com/downloads) on your machine and in your system's `PATH`.
 Make sure [Git is installed](https://git-scm.com/downloads) on your machine and in your system's `PATH`.
 
 
@@ -100,7 +102,8 @@ See [net.Dial](https://golang.org/pkg/net/#Dial) for more information which netw
 In general you should use an Unix domain socket if available and TCP otherwise for best performance.
 In general you should use an Unix domain socket if available and TCP otherwise for best performance.
 
 
 #### Address
 #### Address
-For TCP and UDP networks, addresses have the form `host:port`.
+For TCP and UDP networks, addresses have the form `host[:port]`.
+If `port` is omitted, the default port will be used.
 If `host` is a literal IPv6 address, it must be enclosed in square brackets.
 If `host` is a literal IPv6 address, it must be enclosed in square brackets.
 The functions [net.JoinHostPort](https://golang.org/pkg/net/#JoinHostPort) and [net.SplitHostPort](https://golang.org/pkg/net/#SplitHostPort) manipulate addresses in this form.
 The functions [net.JoinHostPort](https://golang.org/pkg/net/#JoinHostPort) and [net.SplitHostPort](https://golang.org/pkg/net/#SplitHostPort) manipulate addresses in this form.
 
 
@@ -137,9 +140,9 @@ Default:        false
 ```
 ```
 Type:           bool
 Type:           bool
 Valid Values:   true, false
 Valid Values:   true, false
-Default:        false
+Default:        true
 ```
 ```
-`allowNativePasswords=true` allows the usage of the mysql native password method.
+`allowNativePasswords=false` disallows the usage of MySQL native password method.
 
 
 ##### `allowOldPasswords`
 ##### `allowOldPasswords`
 
 
@@ -230,10 +233,10 @@ Please keep in mind, that param values must be [url.QueryEscape](https://golang.
 ##### `maxAllowedPacket`
 ##### `maxAllowedPacket`
 ```
 ```
 Type:          decimal number
 Type:          decimal number
-Default:       0
+Default:       4194304
 ```
 ```
 
 
-Max packet size allowed in bytes. Use `maxAllowedPacket=0` to automatically fetch the `max_allowed_packet` variable from server.
+Max packet size allowed in bytes. The default value is 4 MiB and should be adjusted to match the server settings. `maxAllowedPacket=0` can be used to automatically fetch the `max_allowed_packet` variable from server *on every connection*.
 
 
 ##### `multiStatements`
 ##### `multiStatements`
 
 
@@ -267,7 +270,7 @@ Default:        0
 
 
 I/O read timeout. The value must be a decimal number with a unit suffix (*"ms"*, *"s"*, *"m"*, *"h"*), such as *"30s"*, *"0.5m"* or *"1m30s"*.
 I/O read timeout. The value must be a decimal number with a unit suffix (*"ms"*, *"s"*, *"m"*, *"h"*), such as *"30s"*, *"0.5m"* or *"1m30s"*.
 
 
-##### `strict`
+##### `rejectReadOnly`
 
 
 ```
 ```
 Type:           bool
 Type:           bool
@@ -275,11 +278,27 @@ Valid Values:   true, false
 Default:        false
 Default:        false
 ```
 ```
 
 
-`strict=true` enables a driver-side strict mode in which MySQL warnings are treated as errors. This mode should not be used in production as it may lead to data corruption in certain situations.
 
 
-A server-side strict mode, which is safe for production use, can be set via the [`sql_mode`](https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html) system variable.
+`rejectReadOnly=true` causes the driver to reject read-only connections. This
+is for a possible race condition during an automatic failover, where the mysql
+client gets connected to a read-only replica after the failover.
+
+Note that this should be a fairly rare case, as an automatic failover normally
+happens when the primary is down, and the race condition shouldn't happen
+unless it comes back up online as soon as the failover is kicked off. On the
+other hand, when this happens, a MySQL application can get stuck on a
+read-only connection until restarted. It is however fairly easy to reproduce,
+for example, using a manual failover on AWS Aurora's MySQL-compatible cluster.
+
+If you are not relying on read-only transactions to reject writes that aren't
+supposed to happen, setting this on some MySQL providers (such as AWS Aurora)
+is safer for failovers.
+
+Note that ERROR 1290 can be returned for a `read-only` server and this option will
+cause a retry for that error. However the same error number is used for some
+other cases. You should ensure your application will never cause an ERROR 1290
+except for `read-only` mode when enabling this option.
 
 
-By default MySQL also treats notes as warnings. Use [`sql_notes=false`](http://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_sql_notes) to ignore notes.
 
 
 ##### `timeout`
 ##### `timeout`
 
 
@@ -290,6 +309,7 @@ Default:        OS default
 
 
 Timeout for establishing connections, aka dial timeout. The value must be a decimal number with a unit suffix (*"ms"*, *"s"*, *"m"*, *"h"*), such as *"30s"*, *"0.5m"* or *"1m30s"*.
 Timeout for establishing connections, aka dial timeout. The value must be a decimal number with a unit suffix (*"ms"*, *"s"*, *"m"*, *"h"*), such as *"30s"*, *"0.5m"* or *"1m30s"*.
 
 
+
 ##### `tls`
 ##### `tls`
 
 
 ```
 ```
@@ -300,6 +320,7 @@ 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). Use a custom value registered with [`mysql.RegisterTLSConfig`](https://godoc.org/github.com/go-sql-driver/mysql#RegisterTLSConfig).
 
 
+
 ##### `writeTimeout`
 ##### `writeTimeout`
 
 
 ```
 ```
@@ -318,9 +339,9 @@ Any other parameters are interpreted as system variables:
   * `<string_var>=%27<value>%27`: `SET <string_var>='<value>'`
   * `<string_var>=%27<value>%27`: `SET <string_var>='<value>'`
 
 
 Rules:
 Rules:
-* The values for string variables must be quoted with '
+* The values for string variables must be quoted with `'`.
 * The values must also be [url.QueryEscape](http://golang.org/pkg/net/url/#QueryEscape)'ed!
 * The values must also be [url.QueryEscape](http://golang.org/pkg/net/url/#QueryEscape)'ed!
- (which implies values of string variables must be wrapped with `%27`)
+ (which implies values of string variables must be wrapped with `%27`).
 
 
 Examples:
 Examples:
   * `autocommit=1`: `SET autocommit=1`
   * `autocommit=1`: `SET autocommit=1`
@@ -385,6 +406,13 @@ user:password@/
 ### Connection pool and timeouts
 ### Connection pool and timeouts
 The connection pool is managed by Go's database/sql package. For details on how to configure the size of the pool and how long connections stay in the pool see `*DB.SetMaxOpenConns`, `*DB.SetMaxIdleConns`, and `*DB.SetConnMaxLifetime` in the [database/sql documentation](https://golang.org/pkg/database/sql/). The read, write, and dial timeouts for each individual connection are configured with the DSN parameters [`readTimeout`](#readtimeout), [`writeTimeout`](#writetimeout), and [`timeout`](#timeout), respectively.
 The connection pool is managed by Go's database/sql package. For details on how to configure the size of the pool and how long connections stay in the pool see `*DB.SetMaxOpenConns`, `*DB.SetMaxIdleConns`, and `*DB.SetConnMaxLifetime` in the [database/sql documentation](https://golang.org/pkg/database/sql/). The read, write, and dial timeouts for each individual connection are configured with the DSN parameters [`readTimeout`](#readtimeout), [`writeTimeout`](#writetimeout), and [`timeout`](#timeout), respectively.
 
 
+## `ColumnType` Support
+This driver supports the [`ColumnType` interface](https://golang.org/pkg/database/sql/#ColumnType) introduced in Go 1.8, with the exception of [`ColumnType.Length()`](https://golang.org/pkg/database/sql/#ColumnType.Length), which is currently not supported.
+
+## `context.Context` Support
+Go 1.8 added `database/sql` support for `context.Context`. This driver supports query timeouts and cancellation via contexts.
+See [context support in the database/sql package](https://golang.org/doc/go1.8#database_sql) for more details.
+
 
 
 ### `LOAD DATA LOCAL INFILE` support
 ### `LOAD DATA LOCAL INFILE` support
 For this feature you need direct access to the package. Therefore you must change the import path (no `_`):
 For this feature you need direct access to the package. Therefore you must change the import path (no `_`):
@@ -400,7 +428,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 programm.
+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 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.
 
 
@@ -418,7 +446,6 @@ Version 1.0 of the driver recommended adding `&charset=utf8` (alias for `SET NAM
 
 
 See http://dev.mysql.com/doc/refman/5.7/en/charset-unicode.html for more details on MySQL's Unicode support.
 See http://dev.mysql.com/doc/refman/5.7/en/charset-unicode.html for more details on MySQL's Unicode support.
 
 
-
 ## Testing / Development
 ## Testing / Development
 To run the driver tests you may need to adjust the configuration. See the [Testing Wiki-Page](https://github.com/go-sql-driver/mysql/wiki/Testing "Testing") for details.
 To run the driver tests you may need to adjust the configuration. See the [Testing Wiki-Page](https://github.com/go-sql-driver/mysql/wiki/Testing "Testing") for details.
 
 
@@ -437,13 +464,13 @@ Mozilla summarizes the license scope as follows:
 
 
 
 
 That means:
 That means:
-  * You can **use** the **unchanged** source code both in private and commercially
-  * When distributing, you **must publish** the source code of any **changed files** licensed under the MPL 2.0 under a) the MPL 2.0 itself or b) a compatible license (e.g. GPL 3.0 or Apache License 2.0)
-  * You **needn't publish** the source code of your library as long as the files licensed under the MPL 2.0 are **unchanged**
+  * You can **use** the **unchanged** source code both in private and commercially.
+  * When distributing, you **must publish** the source code of any **changed files** licensed under the MPL 2.0 under a) the MPL 2.0 itself or b) a compatible license (e.g. GPL 3.0 or Apache License 2.0).
+  * You **needn't publish** the source code of your library as long as the files licensed under the MPL 2.0 are **unchanged**.
 
 
 Please read the [MPL 2.0 FAQ](https://www.mozilla.org/en-US/MPL/2.0/FAQ/) if you have further questions regarding the license.
 Please read the [MPL 2.0 FAQ](https://www.mozilla.org/en-US/MPL/2.0/FAQ/) if you have further questions regarding the license.
 
 
-You can read the full terms here: [LICENSE](https://raw.github.com/go-sql-driver/mysql/master/LICENSE)
+You can read the full terms here: [LICENSE](https://raw.github.com/go-sql-driver/mysql/master/LICENSE).
 
 
 ![Go Gopher and MySQL Dolphin](https://raw.github.com/wiki/go-sql-driver/mysql/go-mysql-driver_m.jpg "Golang Gopher transporting the MySQL Dolphin in a wheelbarrow")
 ![Go Gopher and MySQL Dolphin](https://raw.github.com/wiki/go-sql-driver/mysql/go-mysql-driver_m.jpg "Golang Gopher transporting the MySQL Dolphin in a wheelbarrow")
 
 

+ 1 - 1
vendor/github.com/go-sql-driver/mysql/appengine.go

@@ -11,7 +11,7 @@
 package mysql
 package mysql
 
 
 import (
 import (
-	"appengine/cloudsql"
+	"google.golang.org/appengine/cloudsql"
 )
 )
 
 
 func init() {
 func init() {

+ 1 - 0
vendor/github.com/go-sql-driver/mysql/collations.go

@@ -9,6 +9,7 @@
 package mysql
 package mysql
 
 
 const defaultCollation = "utf8_general_ci"
 const defaultCollation = "utf8_general_ci"
+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:

+ 92 - 22
vendor/github.com/go-sql-driver/mysql/connection.go

@@ -17,6 +17,16 @@ 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
@@ -30,7 +40,14 @@ type mysqlConn struct {
 	status           statusFlag
 	status           statusFlag
 	sequence         uint8
 	sequence         uint8
 	parseTime        bool
 	parseTime        bool
-	strict           bool
+
+	// for context support (Go 1.8+)
+	watching bool
+	watcher  chan<- mysqlContext
+	closech  chan struct{}
+	finished chan<- struct{}
+	canceled atomicError // set non-nil if conn is canceled
+	closed   atomicBool  // set when conn is closed, before closech is closed
 }
 }
 
 
 // Handles parameters set in DSN after the connection is established
 // Handles parameters set in DSN after the connection is established
@@ -63,22 +80,41 @@ func (mc *mysqlConn) handleParams() (err error) {
 	return
 	return
 }
 }
 
 
+func (mc *mysqlConn) markBadConn(err error) error {
+	if mc == nil {
+		return err
+	}
+	if err != errBadConnNoWrite {
+		return err
+	}
+	return driver.ErrBadConn
+}
+
 func (mc *mysqlConn) Begin() (driver.Tx, error) {
 func (mc *mysqlConn) Begin() (driver.Tx, error) {
-	if mc.netConn == nil {
+	return mc.begin(false)
+}
+
+func (mc *mysqlConn) begin(readOnly bool) (driver.Tx, error) {
+	if mc.closed.IsSet() {
 		errLog.Print(ErrInvalidConn)
 		errLog.Print(ErrInvalidConn)
 		return nil, driver.ErrBadConn
 		return nil, driver.ErrBadConn
 	}
 	}
-	err := mc.exec("START TRANSACTION")
+	var q string
+	if readOnly {
+		q = "START TRANSACTION READ ONLY"
+	} else {
+		q = "START TRANSACTION"
+	}
+	err := mc.exec(q)
 	if err == nil {
 	if err == nil {
 		return &mysqlTx{mc}, err
 		return &mysqlTx{mc}, err
 	}
 	}
-
-	return nil, err
+	return nil, mc.markBadConn(err)
 }
 }
 
 
 func (mc *mysqlConn) Close() (err error) {
 func (mc *mysqlConn) Close() (err error) {
 	// Makes Close idempotent
 	// Makes Close idempotent
-	if mc.netConn != nil {
+	if !mc.closed.IsSet() {
 		err = mc.writeCommandPacket(comQuit)
 		err = mc.writeCommandPacket(comQuit)
 	}
 	}
 
 
@@ -92,26 +128,39 @@ func (mc *mysqlConn) Close() (err error) {
 // is called before auth or on auth failure because MySQL will have already
 // is called before auth or on auth failure because MySQL will have already
 // closed the network connection.
 // closed the network connection.
 func (mc *mysqlConn) cleanup() {
 func (mc *mysqlConn) cleanup() {
+	if !mc.closed.TrySet(true) {
+		return
+	}
+
 	// Makes cleanup idempotent
 	// Makes cleanup idempotent
-	if mc.netConn != nil {
-		if err := mc.netConn.Close(); err != nil {
-			errLog.Print(err)
+	close(mc.closech)
+	if mc.netConn == nil {
+		return
+	}
+	if err := mc.netConn.Close(); err != nil {
+		errLog.Print(err)
+	}
+}
+
+func (mc *mysqlConn) error() error {
+	if mc.closed.IsSet() {
+		if err := mc.canceled.Value(); err != nil {
+			return err
 		}
 		}
-		mc.netConn = nil
+		return ErrInvalidConn
 	}
 	}
-	mc.cfg = nil
-	mc.buf.nc = nil
+	return nil
 }
 }
 
 
 func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
 func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
-	if mc.netConn == nil {
+	if mc.closed.IsSet() {
 		errLog.Print(ErrInvalidConn)
 		errLog.Print(ErrInvalidConn)
 		return nil, driver.ErrBadConn
 		return nil, driver.ErrBadConn
 	}
 	}
 	// Send command
 	// Send command
 	err := mc.writeCommandPacketStr(comStmtPrepare, query)
 	err := mc.writeCommandPacketStr(comStmtPrepare, query)
 	if err != nil {
 	if err != nil {
-		return nil, err
+		return nil, mc.markBadConn(err)
 	}
 	}
 
 
 	stmt := &mysqlStmt{
 	stmt := &mysqlStmt{
@@ -145,7 +194,7 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin
 	if buf == nil {
 	if buf == 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(ErrBusyBuffer)
-		return "", driver.ErrBadConn
+		return "", ErrInvalidConn
 	}
 	}
 	buf = buf[:0]
 	buf = buf[:0]
 	argPos := 0
 	argPos := 0
@@ -258,7 +307,7 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin
 }
 }
 
 
 func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) {
 func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) {
-	if mc.netConn == nil {
+	if mc.closed.IsSet() {
 		errLog.Print(ErrInvalidConn)
 		errLog.Print(ErrInvalidConn)
 		return nil, driver.ErrBadConn
 		return nil, driver.ErrBadConn
 	}
 	}
@@ -272,7 +321,6 @@ func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, err
 			return nil, err
 			return nil, err
 		}
 		}
 		query = prepared
 		query = prepared
-		args = nil
 	}
 	}
 	mc.affectedRows = 0
 	mc.affectedRows = 0
 	mc.insertId = 0
 	mc.insertId = 0
@@ -284,14 +332,14 @@ func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, err
 			insertId:     int64(mc.insertId),
 			insertId:     int64(mc.insertId),
 		}, err
 		}, err
 	}
 	}
-	return nil, err
+	return nil, mc.markBadConn(err)
 }
 }
 
 
 // Internal function to execute commands
 // Internal function to execute commands
 func (mc *mysqlConn) exec(query string) error {
 func (mc *mysqlConn) exec(query string) error {
 	// Send command
 	// Send command
 	if err := mc.writeCommandPacketStr(comQuery, query); err != nil {
 	if err := mc.writeCommandPacketStr(comQuery, query); err != nil {
-		return err
+		return mc.markBadConn(err)
 	}
 	}
 
 
 	// Read Result
 	// Read Result
@@ -316,7 +364,11 @@ func (mc *mysqlConn) exec(query string) error {
 }
 }
 
 
 func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, error) {
 func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, error) {
-	if mc.netConn == nil {
+	return mc.query(query, args)
+}
+
+func (mc *mysqlConn) query(query string, args []driver.Value) (*textRows, error) {
+	if mc.closed.IsSet() {
 		errLog.Print(ErrInvalidConn)
 		errLog.Print(ErrInvalidConn)
 		return nil, driver.ErrBadConn
 		return nil, driver.ErrBadConn
 	}
 	}
@@ -330,7 +382,6 @@ func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, erro
 			return nil, err
 			return nil, err
 		}
 		}
 		query = prepared
 		query = prepared
-		args = nil
 	}
 	}
 	// Send command
 	// Send command
 	err := mc.writeCommandPacketStr(comQuery, query)
 	err := mc.writeCommandPacketStr(comQuery, query)
@@ -352,12 +403,13 @@ func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, erro
 					return nil, err
 					return nil, err
 				}
 				}
 			}
 			}
+
 			// Columns
 			// Columns
 			rows.rs.columns, err = mc.readColumns(resLen)
 			rows.rs.columns, err = mc.readColumns(resLen)
 			return rows, err
 			return rows, err
 		}
 		}
 	}
 	}
-	return nil, err
+	return nil, mc.markBadConn(err)
 }
 }
 
 
 // Gets the value of the given MySQL System Variable
 // Gets the value of the given MySQL System Variable
@@ -389,3 +441,21 @@ func (mc *mysqlConn) getSystemVar(name string) ([]byte, error) {
 	}
 	}
 	return nil, err
 	return nil, err
 }
 }
+
+// finish is called when the query has canceled.
+func (mc *mysqlConn) cancel(err error) {
+	mc.canceled.Set(err)
+	mc.cleanup()
+}
+
+// finish is called when the query has succeeded.
+func (mc *mysqlConn) finish() {
+	if !mc.watching || mc.finished == nil {
+		return
+	}
+	select {
+	case mc.finished <- struct{}{}:
+		mc.watching = false
+	case <-mc.closech:
+	}
+}

+ 202 - 0
vendor/github.com/go-sql-driver/mysql/connection_go18.go

@@ -0,0 +1,202 @@
+// 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) error {
+	if mc.closed.IsSet() {
+		errLog.Print(ErrInvalidConn)
+		return driver.ErrBadConn
+	}
+
+	if err := mc.watchCancel(ctx); err != nil {
+		return err
+	}
+	defer mc.finish()
+
+	if err := mc.writeCommandPacket(comPing); err != nil {
+		return err
+	}
+	if _, err := mc.readResultOK(); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// 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
+}

+ 6 - 3
vendor/github.com/go-sql-driver/mysql/const.go

@@ -9,7 +9,8 @@
 package mysql
 package mysql
 
 
 const (
 const (
-	minProtocolVersion byte = 10
+	defaultMaxAllowedPacket = 4 << 20 // 4 MiB
+	minProtocolVersion      = 10
 	maxPacketSize           = 1<<24 - 1
 	maxPacketSize           = 1<<24 - 1
 	timeFormat              = "2006-01-02 15:04:05.999999"
 	timeFormat              = "2006-01-02 15:04:05.999999"
 )
 )
@@ -87,8 +88,10 @@ const (
 )
 )
 
 
 // https://dev.mysql.com/doc/internals/en/com-query-response.html#packet-Protocol::ColumnType
 // https://dev.mysql.com/doc/internals/en/com-query-response.html#packet-Protocol::ColumnType
+type fieldType byte
+
 const (
 const (
-	fieldTypeDecimal byte = iota
+	fieldTypeDecimal fieldType = iota
 	fieldTypeTiny
 	fieldTypeTiny
 	fieldTypeShort
 	fieldTypeShort
 	fieldTypeLong
 	fieldTypeLong
@@ -107,7 +110,7 @@ const (
 	fieldTypeBit
 	fieldTypeBit
 )
 )
 const (
 const (
-	fieldTypeJSON byte = iota + 0xf5
+	fieldTypeJSON fieldType = iota + 0xf5
 	fieldTypeNewDecimal
 	fieldTypeNewDecimal
 	fieldTypeEnum
 	fieldTypeEnum
 	fieldTypeSet
 	fieldTypeSet

+ 12 - 2
vendor/github.com/go-sql-driver/mysql/driver.go

@@ -4,7 +4,7 @@
 // License, v. 2.0. If a copy of the MPL was not distributed with this file,
 // 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/.
 // You can obtain one at http://mozilla.org/MPL/2.0/.
 
 
-// Package mysql provides a MySQL driver for Go's database/sql package
+// Package mysql provides a MySQL driver for Go's database/sql package.
 //
 //
 // The driver should be used via the database/sql package:
 // The driver should be used via the database/sql package:
 //
 //
@@ -22,6 +22,11 @@ import (
 	"net"
 	"net"
 )
 )
 
 
+// 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{}
@@ -52,13 +57,13 @@ func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
 	mc := &mysqlConn{
 	mc := &mysqlConn{
 		maxAllowedPacket: maxPacketSize,
 		maxAllowedPacket: maxPacketSize,
 		maxWriteSize:     maxPacketSize - 1,
 		maxWriteSize:     maxPacketSize - 1,
+		closech:          make(chan struct{}),
 	}
 	}
 	mc.cfg, err = ParseDSN(dsn)
 	mc.cfg, err = ParseDSN(dsn)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 	mc.parseTime = mc.cfg.ParseTime
 	mc.parseTime = mc.cfg.ParseTime
-	mc.strict = mc.cfg.Strict
 
 
 	// Connect to Server
 	// Connect to Server
 	if dial, ok := dials[mc.cfg.Net]; ok {
 	if dial, ok := dials[mc.cfg.Net]; ok {
@@ -81,6 +86,11 @@ func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
 		}
 		}
 	}
 	}
 
 
+	// Call startWatcher for context support (From Go 1.8)
+	if s, ok := interface{}(mc).(watcher); ok {
+		s.startWatcher()
+	}
+
 	mc.buf = newBuffer(mc.netConn)
 	mc.buf = newBuffer(mc.netConn)
 
 
 	// Set I/O timeouts
 	// Set I/O timeouts

+ 84 - 48
vendor/github.com/go-sql-driver/mysql/dsn.go

@@ -15,6 +15,7 @@ import (
 	"fmt"
 	"fmt"
 	"net"
 	"net"
 	"net/url"
 	"net/url"
+	"sort"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
 	"time"
 	"time"
@@ -27,7 +28,9 @@ var (
 	errInvalidDSNUnsafeCollation = errors.New("invalid DSN: interpolateParams can not be used with unsafe collations")
 	errInvalidDSNUnsafeCollation = errors.New("invalid DSN: interpolateParams can not be used with unsafe collations")
 )
 )
 
 
-// Config is a configuration parsed from a DSN string
+// Config is a configuration parsed from a DSN string.
+// If a new Config is created instead of being parsed from a DSN string,
+// the NewConfig function should be used, which sets default values.
 type Config struct {
 type Config struct {
 	User             string            // Username
 	User             string            // Username
 	Passwd           string            // Password (requires User)
 	Passwd           string            // Password (requires User)
@@ -53,7 +56,54 @@ type Config struct {
 	InterpolateParams       bool // Interpolate placeholders into query string
 	InterpolateParams       bool // Interpolate placeholders into query string
 	MultiStatements         bool // Allow multiple statements in one query
 	MultiStatements         bool // Allow multiple statements in one query
 	ParseTime               bool // Parse time values to time.Time
 	ParseTime               bool // Parse time values to time.Time
-	Strict                  bool // Return warnings as errors
+	RejectReadOnly          bool // Reject read-only connections
+}
+
+// NewConfig creates a new Config and sets default values.
+func NewConfig() *Config {
+	return &Config{
+		Collation:            defaultCollation,
+		Loc:                  time.UTC,
+		MaxAllowedPacket:     defaultMaxAllowedPacket,
+		AllowNativePasswords: true,
+	}
+}
+
+func (cfg *Config) normalize() error {
+	if cfg.InterpolateParams && unsafeCollations[cfg.Collation] {
+		return errInvalidDSNUnsafeCollation
+	}
+
+	// Set default network if empty
+	if cfg.Net == "" {
+		cfg.Net = "tcp"
+	}
+
+	// Set default address if empty
+	if cfg.Addr == "" {
+		switch cfg.Net {
+		case "tcp":
+			cfg.Addr = "127.0.0.1:3306"
+		case "unix":
+			cfg.Addr = "/tmp/mysql.sock"
+		default:
+			return errors.New("default addr for network '" + cfg.Net + "' unknown")
+		}
+
+	} else if cfg.Net == "tcp" {
+		cfg.Addr = ensureHavePort(cfg.Addr)
+	}
+
+	if cfg.tls != nil {
+		if cfg.tls.ServerName == "" && !cfg.tls.InsecureSkipVerify {
+			host, _, err := net.SplitHostPort(cfg.Addr)
+			if err == nil {
+				cfg.tls.ServerName = host
+			}
+		}
+	}
+
+	return nil
 }
 }
 
 
 // FormatDSN formats the given Config into a DSN string which can be passed to
 // FormatDSN formats the given Config into a DSN string which can be passed to
@@ -102,12 +152,12 @@ func (cfg *Config) FormatDSN() string {
 		}
 		}
 	}
 	}
 
 
-	if cfg.AllowNativePasswords {
+	if !cfg.AllowNativePasswords {
 		if hasParam {
 		if hasParam {
-			buf.WriteString("&allowNativePasswords=true")
+			buf.WriteString("&allowNativePasswords=false")
 		} else {
 		} else {
 			hasParam = true
 			hasParam = true
-			buf.WriteString("?allowNativePasswords=true")
+			buf.WriteString("?allowNativePasswords=false")
 		}
 		}
 	}
 	}
 
 
@@ -195,12 +245,12 @@ func (cfg *Config) FormatDSN() string {
 		buf.WriteString(cfg.ReadTimeout.String())
 		buf.WriteString(cfg.ReadTimeout.String())
 	}
 	}
 
 
-	if cfg.Strict {
+	if cfg.RejectReadOnly {
 		if hasParam {
 		if hasParam {
-			buf.WriteString("&strict=true")
+			buf.WriteString("&rejectReadOnly=true")
 		} else {
 		} else {
 			hasParam = true
 			hasParam = true
-			buf.WriteString("?strict=true")
+			buf.WriteString("?rejectReadOnly=true")
 		}
 		}
 	}
 	}
 
 
@@ -234,7 +284,7 @@ func (cfg *Config) FormatDSN() string {
 		buf.WriteString(cfg.WriteTimeout.String())
 		buf.WriteString(cfg.WriteTimeout.String())
 	}
 	}
 
 
-	if cfg.MaxAllowedPacket > 0 {
+	if cfg.MaxAllowedPacket != defaultMaxAllowedPacket {
 		if hasParam {
 		if hasParam {
 			buf.WriteString("&maxAllowedPacket=")
 			buf.WriteString("&maxAllowedPacket=")
 		} else {
 		} else {
@@ -247,7 +297,12 @@ func (cfg *Config) FormatDSN() string {
 
 
 	// other params
 	// other params
 	if cfg.Params != nil {
 	if cfg.Params != nil {
-		for param, value := range cfg.Params {
+		var params []string
+		for param := range cfg.Params {
+			params = append(params, param)
+		}
+		sort.Strings(params)
+		for _, param := range params {
 			if hasParam {
 			if hasParam {
 				buf.WriteByte('&')
 				buf.WriteByte('&')
 			} else {
 			} else {
@@ -257,7 +312,7 @@ func (cfg *Config) FormatDSN() string {
 
 
 			buf.WriteString(param)
 			buf.WriteString(param)
 			buf.WriteByte('=')
 			buf.WriteByte('=')
-			buf.WriteString(url.QueryEscape(value))
+			buf.WriteString(url.QueryEscape(cfg.Params[param]))
 		}
 		}
 	}
 	}
 
 
@@ -267,10 +322,7 @@ func (cfg *Config) FormatDSN() string {
 // ParseDSN parses the DSN string to a Config
 // ParseDSN parses the DSN string to a Config
 func ParseDSN(dsn string) (cfg *Config, err error) {
 func ParseDSN(dsn string) (cfg *Config, err error) {
 	// New config with some default values
 	// New config with some default values
-	cfg = &Config{
-		Loc:       time.UTC,
-		Collation: defaultCollation,
-	}
+	cfg = NewConfig()
 
 
 	// [user[:password]@][net[(addr)]]/dbname[?param1=value1&paramN=valueN]
 	// [user[:password]@][net[(addr)]]/dbname[?param1=value1&paramN=valueN]
 	// Find the last '/' (since the password or the net addr might contain a '/')
 	// Find the last '/' (since the password or the net addr might contain a '/')
@@ -338,28 +390,9 @@ func ParseDSN(dsn string) (cfg *Config, err error) {
 		return nil, errInvalidDSNNoSlash
 		return nil, errInvalidDSNNoSlash
 	}
 	}
 
 
-	if cfg.InterpolateParams && unsafeCollations[cfg.Collation] {
-		return nil, errInvalidDSNUnsafeCollation
-	}
-
-	// Set default network if empty
-	if cfg.Net == "" {
-		cfg.Net = "tcp"
+	if err = cfg.normalize(); err != nil {
+		return nil, err
 	}
 	}
-
-	// Set default address if empty
-	if cfg.Addr == "" {
-		switch cfg.Net {
-		case "tcp":
-			cfg.Addr = "127.0.0.1:3306"
-		case "unix":
-			cfg.Addr = "/tmp/mysql.sock"
-		default:
-			return nil, errors.New("default addr for network '" + cfg.Net + "' unknown")
-		}
-
-	}
-
 	return
 	return
 }
 }
 
 
@@ -374,7 +407,6 @@ func parseDSNParams(cfg *Config, params string) (err error) {
 
 
 		// cfg params
 		// cfg params
 		switch value := param[1]; param[0] {
 		switch value := param[1]; param[0] {
-
 		// Disable INFILE whitelist / enable all files
 		// Disable INFILE whitelist / enable all files
 		case "allowAllFiles":
 		case "allowAllFiles":
 			var isBool bool
 			var isBool bool
@@ -472,14 +504,18 @@ func parseDSNParams(cfg *Config, params string) (err error) {
 				return
 				return
 			}
 			}
 
 
-		// Strict mode
-		case "strict":
+		// Reject read-only connections
+		case "rejectReadOnly":
 			var isBool bool
 			var isBool bool
-			cfg.Strict, isBool = readBool(value)
+			cfg.RejectReadOnly, isBool = readBool(value)
 			if !isBool {
 			if !isBool {
 				return errors.New("invalid bool value: " + value)
 				return errors.New("invalid bool value: " + value)
 			}
 			}
 
 
+		// Strict mode
+		case "strict":
+			panic("strict mode has been removed. See https://github.com/go-sql-driver/mysql/wiki/strict-mode")
+
 		// Dial Timeout
 		// Dial Timeout
 		case "timeout":
 		case "timeout":
 			cfg.Timeout, err = time.ParseDuration(value)
 			cfg.Timeout, err = time.ParseDuration(value)
@@ -506,14 +542,7 @@ func parseDSNParams(cfg *Config, params string) (err error) {
 					return fmt.Errorf("invalid value for TLS config name: %v", err)
 					return fmt.Errorf("invalid value for TLS config name: %v", err)
 				}
 				}
 
 
-				if tlsConfig, ok := tlsConfigRegister[name]; ok {
-					if len(tlsConfig.ServerName) == 0 && !tlsConfig.InsecureSkipVerify {
-						host, _, err := net.SplitHostPort(cfg.Addr)
-						if err == nil {
-							tlsConfig.ServerName = host
-						}
-					}
-
+				if tlsConfig := getTLSConfigClone(name); tlsConfig != nil {
 					cfg.TLSConfig = name
 					cfg.TLSConfig = name
 					cfg.tls = tlsConfig
 					cfg.tls = tlsConfig
 				} else {
 				} else {
@@ -546,3 +575,10 @@ func parseDSNParams(cfg *Config, params string) (err error) {
 
 
 	return
 	return
 }
 }
+
+func ensureHavePort(addr string) string {
+	if _, _, err := net.SplitHostPort(addr); err != nil {
+		return net.JoinHostPort(addr, "3306")
+	}
+	return addr
+}

+ 6 - 73
vendor/github.com/go-sql-driver/mysql/errors.go

@@ -9,10 +9,8 @@
 package mysql
 package mysql
 
 
 import (
 import (
-	"database/sql/driver"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
-	"io"
 	"log"
 	"log"
 	"os"
 	"os"
 )
 )
@@ -31,6 +29,12 @@ var (
 	ErrPktSyncMul        = errors.New("commands out of sync. Did you run multiple statements at once?")
 	ErrPktSyncMul        = errors.New("commands out of sync. Did you run multiple statements at once?")
 	ErrPktTooLarge       = errors.New("packet for query is too large. Try adjusting the 'max_allowed_packet' variable on the server")
 	ErrPktTooLarge       = errors.New("packet for query is too large. Try adjusting the 'max_allowed_packet' variable on the server")
 	ErrBusyBuffer        = errors.New("busy buffer")
 	ErrBusyBuffer        = errors.New("busy buffer")
+
+	// errBadConnNoWrite is used for connection errors where nothing was sent to the database yet.
+	// If this happens first in a function starting a database interaction, it should be replaced by driver.ErrBadConn
+	// to trigger a resend.
+	// See https://github.com/go-sql-driver/mysql/pull/302
+	errBadConnNoWrite = errors.New("bad connection")
 )
 )
 
 
 var errLog = Logger(log.New(os.Stderr, "[mysql] ", log.Ldate|log.Ltime|log.Lshortfile))
 var errLog = Logger(log.New(os.Stderr, "[mysql] ", log.Ldate|log.Ltime|log.Lshortfile))
@@ -59,74 +63,3 @@ type MySQLError struct {
 func (me *MySQLError) Error() string {
 func (me *MySQLError) Error() string {
 	return fmt.Sprintf("Error %d: %s", me.Number, me.Message)
 	return fmt.Sprintf("Error %d: %s", me.Number, me.Message)
 }
 }
-
-// MySQLWarnings is an error type which represents a group of one or more MySQL
-// warnings
-type MySQLWarnings []MySQLWarning
-
-func (mws MySQLWarnings) Error() string {
-	var msg string
-	for i, warning := range mws {
-		if i > 0 {
-			msg += "\r\n"
-		}
-		msg += fmt.Sprintf(
-			"%s %s: %s",
-			warning.Level,
-			warning.Code,
-			warning.Message,
-		)
-	}
-	return msg
-}
-
-// MySQLWarning is an error type which represents a single MySQL warning.
-// Warnings are returned in groups only. See MySQLWarnings
-type MySQLWarning struct {
-	Level   string
-	Code    string
-	Message string
-}
-
-func (mc *mysqlConn) getWarnings() (err error) {
-	rows, err := mc.Query("SHOW WARNINGS", nil)
-	if err != nil {
-		return
-	}
-
-	var warnings = MySQLWarnings{}
-	var values = make([]driver.Value, 3)
-
-	for {
-		err = rows.Next(values)
-		switch err {
-		case nil:
-			warning := MySQLWarning{}
-
-			if raw, ok := values[0].([]byte); ok {
-				warning.Level = string(raw)
-			} else {
-				warning.Level = fmt.Sprintf("%s", values[0])
-			}
-			if raw, ok := values[1].([]byte); ok {
-				warning.Code = string(raw)
-			} else {
-				warning.Code = fmt.Sprintf("%s", values[1])
-			}
-			if raw, ok := values[2].([]byte); ok {
-				warning.Message = string(raw)
-			} else {
-				warning.Message = fmt.Sprintf("%s", values[0])
-			}
-
-			warnings = append(warnings, warning)
-
-		case io.EOF:
-			return warnings
-
-		default:
-			rows.Close()
-			return
-		}
-	}
-}

+ 194 - 0
vendor/github.com/go-sql-driver/mysql/fields.go

@@ -0,0 +1,194 @@
+// 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 (
+	"database/sql"
+	"reflect"
+)
+
+func (mf *mysqlField) typeDatabaseName() string {
+	switch mf.fieldType {
+	case fieldTypeBit:
+		return "BIT"
+	case fieldTypeBLOB:
+		if mf.charSet != collations[binaryCollation] {
+			return "TEXT"
+		}
+		return "BLOB"
+	case fieldTypeDate:
+		return "DATE"
+	case fieldTypeDateTime:
+		return "DATETIME"
+	case fieldTypeDecimal:
+		return "DECIMAL"
+	case fieldTypeDouble:
+		return "DOUBLE"
+	case fieldTypeEnum:
+		return "ENUM"
+	case fieldTypeFloat:
+		return "FLOAT"
+	case fieldTypeGeometry:
+		return "GEOMETRY"
+	case fieldTypeInt24:
+		return "MEDIUMINT"
+	case fieldTypeJSON:
+		return "JSON"
+	case fieldTypeLong:
+		return "INT"
+	case fieldTypeLongBLOB:
+		if mf.charSet != collations[binaryCollation] {
+			return "LONGTEXT"
+		}
+		return "LONGBLOB"
+	case fieldTypeLongLong:
+		return "BIGINT"
+	case fieldTypeMediumBLOB:
+		if mf.charSet != collations[binaryCollation] {
+			return "MEDIUMTEXT"
+		}
+		return "MEDIUMBLOB"
+	case fieldTypeNewDate:
+		return "DATE"
+	case fieldTypeNewDecimal:
+		return "DECIMAL"
+	case fieldTypeNULL:
+		return "NULL"
+	case fieldTypeSet:
+		return "SET"
+	case fieldTypeShort:
+		return "SMALLINT"
+	case fieldTypeString:
+		if mf.charSet == collations[binaryCollation] {
+			return "BINARY"
+		}
+		return "CHAR"
+	case fieldTypeTime:
+		return "TIME"
+	case fieldTypeTimestamp:
+		return "TIMESTAMP"
+	case fieldTypeTiny:
+		return "TINYINT"
+	case fieldTypeTinyBLOB:
+		if mf.charSet != collations[binaryCollation] {
+			return "TINYTEXT"
+		}
+		return "TINYBLOB"
+	case fieldTypeVarChar:
+		if mf.charSet == collations[binaryCollation] {
+			return "VARBINARY"
+		}
+		return "VARCHAR"
+	case fieldTypeVarString:
+		if mf.charSet == collations[binaryCollation] {
+			return "VARBINARY"
+		}
+		return "VARCHAR"
+	case fieldTypeYear:
+		return "YEAR"
+	default:
+		return ""
+	}
+}
+
+var (
+	scanTypeFloat32   = reflect.TypeOf(float32(0))
+	scanTypeFloat64   = reflect.TypeOf(float64(0))
+	scanTypeInt8      = reflect.TypeOf(int8(0))
+	scanTypeInt16     = reflect.TypeOf(int16(0))
+	scanTypeInt32     = reflect.TypeOf(int32(0))
+	scanTypeInt64     = reflect.TypeOf(int64(0))
+	scanTypeNullFloat = reflect.TypeOf(sql.NullFloat64{})
+	scanTypeNullInt   = reflect.TypeOf(sql.NullInt64{})
+	scanTypeNullTime  = reflect.TypeOf(NullTime{})
+	scanTypeUint8     = reflect.TypeOf(uint8(0))
+	scanTypeUint16    = reflect.TypeOf(uint16(0))
+	scanTypeUint32    = reflect.TypeOf(uint32(0))
+	scanTypeUint64    = reflect.TypeOf(uint64(0))
+	scanTypeRawBytes  = reflect.TypeOf(sql.RawBytes{})
+	scanTypeUnknown   = reflect.TypeOf(new(interface{}))
+)
+
+type mysqlField struct {
+	tableName string
+	name      string
+	length    uint32
+	flags     fieldFlag
+	fieldType fieldType
+	decimals  byte
+	charSet   uint8
+}
+
+func (mf *mysqlField) scanType() reflect.Type {
+	switch mf.fieldType {
+	case fieldTypeTiny:
+		if mf.flags&flagNotNULL != 0 {
+			if mf.flags&flagUnsigned != 0 {
+				return scanTypeUint8
+			}
+			return scanTypeInt8
+		}
+		return scanTypeNullInt
+
+	case fieldTypeShort, fieldTypeYear:
+		if mf.flags&flagNotNULL != 0 {
+			if mf.flags&flagUnsigned != 0 {
+				return scanTypeUint16
+			}
+			return scanTypeInt16
+		}
+		return scanTypeNullInt
+
+	case fieldTypeInt24, fieldTypeLong:
+		if mf.flags&flagNotNULL != 0 {
+			if mf.flags&flagUnsigned != 0 {
+				return scanTypeUint32
+			}
+			return scanTypeInt32
+		}
+		return scanTypeNullInt
+
+	case fieldTypeLongLong:
+		if mf.flags&flagNotNULL != 0 {
+			if mf.flags&flagUnsigned != 0 {
+				return scanTypeUint64
+			}
+			return scanTypeInt64
+		}
+		return scanTypeNullInt
+
+	case fieldTypeFloat:
+		if mf.flags&flagNotNULL != 0 {
+			return scanTypeFloat32
+		}
+		return scanTypeNullFloat
+
+	case fieldTypeDouble:
+		if mf.flags&flagNotNULL != 0 {
+			return scanTypeFloat64
+		}
+		return scanTypeNullFloat
+
+	case fieldTypeDecimal, fieldTypeNewDecimal, fieldTypeVarChar,
+		fieldTypeBit, fieldTypeEnum, fieldTypeSet, fieldTypeTinyBLOB,
+		fieldTypeMediumBLOB, fieldTypeLongBLOB, fieldTypeBLOB,
+		fieldTypeVarString, fieldTypeString, fieldTypeGeometry, fieldTypeJSON,
+		fieldTypeTime:
+		return scanTypeRawBytes
+
+	case fieldTypeDate, fieldTypeNewDate,
+		fieldTypeTimestamp, fieldTypeDateTime:
+		// NullTime is always returned for more consistent behavior as it can
+		// handle both cases of parseTime regardless if the field is nullable.
+		return scanTypeNullTime
+
+	default:
+		return scanTypeUnknown
+	}
+}

+ 2 - 1
vendor/github.com/go-sql-driver/mysql/infile.go

@@ -147,7 +147,8 @@ func (mc *mysqlConn) handleInFileRequest(name string) (err error) {
 	}
 	}
 
 
 	// send content packets
 	// send content packets
-	if err == nil {
+	// if packetSize == 0, the Reader contains no data
+	if err == nil && packetSize > 0 {
 		data := make([]byte, 4+packetSize)
 		data := make([]byte, 4+packetSize)
 		var n int
 		var n int
 		for err == nil {
 		for err == nil {

+ 93 - 55
vendor/github.com/go-sql-driver/mysql/packets.go

@@ -30,9 +30,12 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
 		// read packet header
 		// read packet header
 		data, err := mc.buf.readNext(4)
 		data, err := mc.buf.readNext(4)
 		if err != nil {
 		if err != nil {
+			if cerr := mc.canceled.Value(); cerr != nil {
+				return nil, cerr
+			}
 			errLog.Print(err)
 			errLog.Print(err)
 			mc.Close()
 			mc.Close()
-			return nil, driver.ErrBadConn
+			return nil, ErrInvalidConn
 		}
 		}
 
 
 		// packet length [24 bit]
 		// packet length [24 bit]
@@ -54,7 +57,7 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
 			if prevData == nil {
 			if prevData == nil {
 				errLog.Print(ErrMalformPkt)
 				errLog.Print(ErrMalformPkt)
 				mc.Close()
 				mc.Close()
-				return nil, driver.ErrBadConn
+				return nil, ErrInvalidConn
 			}
 			}
 
 
 			return prevData, nil
 			return prevData, nil
@@ -63,9 +66,12 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
 		// read packet body [pktLen bytes]
 		// read packet body [pktLen bytes]
 		data, err = mc.buf.readNext(pktLen)
 		data, err = mc.buf.readNext(pktLen)
 		if err != nil {
 		if err != nil {
+			if cerr := mc.canceled.Value(); cerr != nil {
+				return nil, cerr
+			}
 			errLog.Print(err)
 			errLog.Print(err)
 			mc.Close()
 			mc.Close()
-			return nil, driver.ErrBadConn
+			return nil, ErrInvalidConn
 		}
 		}
 
 
 		// return data if this was the last packet
 		// return data if this was the last packet
@@ -125,11 +131,20 @@ func (mc *mysqlConn) writePacket(data []byte) error {
 
 
 		// Handle error
 		// Handle error
 		if err == nil { // n != len(data)
 		if err == nil { // n != len(data)
+			mc.cleanup()
 			errLog.Print(ErrMalformPkt)
 			errLog.Print(ErrMalformPkt)
 		} else {
 		} else {
+			if cerr := mc.canceled.Value(); cerr != nil {
+				return cerr
+			}
+			if n == 0 && pktLen == len(data)-4 {
+				// only for the first loop iteration when nothing was written yet
+				return errBadConnNoWrite
+			}
+			mc.cleanup()
 			errLog.Print(err)
 			errLog.Print(err)
 		}
 		}
-		return driver.ErrBadConn
+		return ErrInvalidConn
 	}
 	}
 }
 }
 
 
@@ -263,7 +278,7 @@ func (mc *mysqlConn) writeAuthPacket(cipher []byte) error {
 	if data == nil {
 	if data == 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(ErrBusyBuffer)
-		return driver.ErrBadConn
+		return errBadConnNoWrite
 	}
 	}
 
 
 	// ClientFlags [32 bit]
 	// ClientFlags [32 bit]
@@ -341,7 +356,9 @@ func (mc *mysqlConn) writeAuthPacket(cipher []byte) error {
 // 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) writeOldAuthPacket(cipher []byte) error {
 func (mc *mysqlConn) writeOldAuthPacket(cipher []byte) error {
 	// User password
 	// User password
-	scrambleBuff := scrambleOldPassword(cipher, []byte(mc.cfg.Passwd))
+	// https://dev.mysql.com/doc/internals/en/old-password-authentication.html
+	// Old password authentication only need and will need 8-byte challenge.
+	scrambleBuff := scrambleOldPassword(cipher[:8], []byte(mc.cfg.Passwd))
 
 
 	// Calculate the packet length and add a tailing 0
 	// Calculate the packet length and add a tailing 0
 	pktLen := len(scrambleBuff) + 1
 	pktLen := len(scrambleBuff) + 1
@@ -349,7 +366,7 @@ func (mc *mysqlConn) writeOldAuthPacket(cipher []byte) error {
 	if data == nil {
 	if data == 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(ErrBusyBuffer)
-		return driver.ErrBadConn
+		return errBadConnNoWrite
 	}
 	}
 
 
 	// Add the scrambled password [null terminated string]
 	// Add the scrambled password [null terminated string]
@@ -368,7 +385,7 @@ func (mc *mysqlConn) writeClearAuthPacket() error {
 	if data == nil {
 	if data == 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(ErrBusyBuffer)
-		return driver.ErrBadConn
+		return errBadConnNoWrite
 	}
 	}
 
 
 	// Add the clear password [null terminated string]
 	// Add the clear password [null terminated string]
@@ -381,7 +398,9 @@ func (mc *mysqlConn) writeClearAuthPacket() error {
 //  Native password authentication method
 //  Native password authentication method
 // 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) writeNativeAuthPacket(cipher []byte) error {
 func (mc *mysqlConn) writeNativeAuthPacket(cipher []byte) error {
-	scrambleBuff := scramblePassword(cipher, []byte(mc.cfg.Passwd))
+	// https://dev.mysql.com/doc/internals/en/secure-password-authentication.html
+	// Native password authentication only need and will need 20-byte challenge.
+	scrambleBuff := scramblePassword(cipher[0:20], []byte(mc.cfg.Passwd))
 
 
 	// Calculate the packet length and add a tailing 0
 	// Calculate the packet length and add a tailing 0
 	pktLen := len(scrambleBuff)
 	pktLen := len(scrambleBuff)
@@ -389,7 +408,7 @@ func (mc *mysqlConn) writeNativeAuthPacket(cipher []byte) error {
 	if data == nil {
 	if data == 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(ErrBusyBuffer)
-		return driver.ErrBadConn
+		return errBadConnNoWrite
 	}
 	}
 
 
 	// Add the scramble
 	// Add the scramble
@@ -410,7 +429,7 @@ func (mc *mysqlConn) writeCommandPacket(command byte) error {
 	if data == nil {
 	if data == 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(ErrBusyBuffer)
-		return driver.ErrBadConn
+		return errBadConnNoWrite
 	}
 	}
 
 
 	// Add command byte
 	// Add command byte
@@ -429,7 +448,7 @@ func (mc *mysqlConn) writeCommandPacketStr(command byte, arg string) error {
 	if data == nil {
 	if data == 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(ErrBusyBuffer)
-		return driver.ErrBadConn
+		return errBadConnNoWrite
 	}
 	}
 
 
 	// Add command byte
 	// Add command byte
@@ -450,7 +469,7 @@ func (mc *mysqlConn) writeCommandPacketUint32(command byte, arg uint32) error {
 	if data == nil {
 	if data == 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(ErrBusyBuffer)
-		return driver.ErrBadConn
+		return errBadConnNoWrite
 	}
 	}
 
 
 	// Add command byte
 	// Add command byte
@@ -484,25 +503,26 @@ func (mc *mysqlConn) readResultOK() ([]byte, error) {
 			if len(data) > 1 {
 			if len(data) > 1 {
 				pluginEndIndex := bytes.IndexByte(data, 0x00)
 				pluginEndIndex := bytes.IndexByte(data, 0x00)
 				plugin := string(data[1:pluginEndIndex])
 				plugin := string(data[1:pluginEndIndex])
-				cipher := data[pluginEndIndex+1 : len(data)-1]
+				cipher := data[pluginEndIndex+1:]
 
 
-				if plugin == "mysql_old_password" {
+				switch plugin {
+				case "mysql_old_password":
 					// using old_passwords
 					// using old_passwords
 					return cipher, ErrOldPassword
 					return cipher, ErrOldPassword
-				} else if plugin == "mysql_clear_password" {
+				case "mysql_clear_password":
 					// using clear text password
 					// using clear text password
 					return cipher, ErrCleartextPassword
 					return cipher, ErrCleartextPassword
-				} else if plugin == "mysql_native_password" {
+				case "mysql_native_password":
 					// using mysql default authentication method
 					// using mysql default authentication method
 					return cipher, ErrNativePassword
 					return cipher, ErrNativePassword
-				} else {
+				default:
 					return cipher, ErrUnknownPlugin
 					return cipher, ErrUnknownPlugin
 				}
 				}
-			} else {
-				// https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::OldAuthSwitchRequest
-				return nil, ErrOldPassword
 			}
 			}
 
 
+			// https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::OldAuthSwitchRequest
+			return nil, ErrOldPassword
+
 		default: // Error otherwise
 		default: // Error otherwise
 			return nil, mc.handleErrorPacket(data)
 			return nil, mc.handleErrorPacket(data)
 		}
 		}
@@ -550,6 +570,22 @@ func (mc *mysqlConn) handleErrorPacket(data []byte) error {
 	// Error Number [16 bit uint]
 	// Error Number [16 bit uint]
 	errno := binary.LittleEndian.Uint16(data[1:3])
 	errno := binary.LittleEndian.Uint16(data[1:3])
 
 
+	// 1792: ER_CANT_EXECUTE_IN_READ_ONLY_TRANSACTION
+	// 1290: ER_OPTION_PREVENTS_STATEMENT (returned by Aurora during failover)
+	if (errno == 1792 || errno == 1290) && mc.cfg.RejectReadOnly {
+		// Oops; we are connected to a read-only connection, and won't be able
+		// to issue any write statements. Since RejectReadOnly is configured,
+		// we throw away this connection hoping this one would have write
+		// permission. This is specifically for a possible race condition
+		// during failover (e.g. on AWS Aurora). See README.md for more.
+		//
+		// We explicitly close the connection before returning
+		// driver.ErrBadConn to ensure that `database/sql` purges this
+		// connection and initiates a new one for next statement next time.
+		mc.Close()
+		return driver.ErrBadConn
+	}
+
 	pos := 3
 	pos := 3
 
 
 	// SQL State [optional: # + 5bytes string]
 	// SQL State [optional: # + 5bytes string]
@@ -589,14 +625,7 @@ func (mc *mysqlConn) handleOkPacket(data []byte) error {
 	}
 	}
 
 
 	// warning count [2 bytes]
 	// warning count [2 bytes]
-	if !mc.strict {
-		return nil
-	}
 
 
-	pos := 1 + n + m + 2
-	if binary.LittleEndian.Uint16(data[pos:pos+2]) > 0 {
-		return mc.getWarnings()
-	}
 	return nil
 	return nil
 }
 }
 
 
@@ -668,14 +697,21 @@ func (mc *mysqlConn) readColumns(count int) ([]mysqlField, error) {
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
+		pos += n
 
 
 		// Filler [uint8]
 		// Filler [uint8]
+		pos++
+
 		// Charset [charset, collation uint8]
 		// Charset [charset, collation uint8]
+		columns[i].charSet = data[pos]
+		pos += 2
+
 		// Length [uint32]
 		// Length [uint32]
-		pos += n + 1 + 2 + 4
+		columns[i].length = binary.LittleEndian.Uint32(data[pos : pos+4])
+		pos += 4
 
 
 		// Field type [uint8]
 		// Field type [uint8]
-		columns[i].fieldType = data[pos]
+		columns[i].fieldType = fieldType(data[pos])
 		pos++
 		pos++
 
 
 		// Flags [uint16]
 		// Flags [uint16]
@@ -808,14 +844,7 @@ func (stmt *mysqlStmt) readPrepareResultPacket() (uint16, error) {
 		// Reserved [8 bit]
 		// Reserved [8 bit]
 
 
 		// Warning count [16 bit uint]
 		// Warning count [16 bit uint]
-		if !stmt.mc.strict {
-			return columnCount, nil
-		}
 
 
-		// Check for warnings count > 0, only available in MySQL > 4.1
-		if len(data) >= 12 && binary.LittleEndian.Uint16(data[10:12]) > 0 {
-			return columnCount, stmt.mc.getWarnings()
-		}
 		return columnCount, nil
 		return columnCount, nil
 	}
 	}
 	return 0, err
 	return 0, err
@@ -887,6 +916,12 @@ 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.
+	longDataSize := mc.maxAllowedPacket / (stmt.paramCount + 1)
+	if longDataSize < 64 {
+		longDataSize = 64
+	}
+
 	// Reset packet-sequence
 	// Reset packet-sequence
 	mc.sequence = 0
 	mc.sequence = 0
 
 
@@ -900,7 +935,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
 	if data == nil {
 	if data == 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(ErrBusyBuffer)
-		return driver.ErrBadConn
+		return errBadConnNoWrite
 	}
 	}
 
 
 	// command [1 byte]
 	// command [1 byte]
@@ -959,7 +994,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
 			// build NULL-bitmap
 			// build NULL-bitmap
 			if arg == nil {
 			if arg == nil {
 				nullMask[i/8] |= 1 << (uint(i) & 7)
 				nullMask[i/8] |= 1 << (uint(i) & 7)
-				paramTypes[i+i] = fieldTypeNULL
+				paramTypes[i+i] = byte(fieldTypeNULL)
 				paramTypes[i+i+1] = 0x00
 				paramTypes[i+i+1] = 0x00
 				continue
 				continue
 			}
 			}
@@ -967,7 +1002,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
 			// cache types and values
 			// cache types and values
 			switch v := arg.(type) {
 			switch v := arg.(type) {
 			case int64:
 			case int64:
-				paramTypes[i+i] = fieldTypeLongLong
+				paramTypes[i+i] = byte(fieldTypeLongLong)
 				paramTypes[i+i+1] = 0x00
 				paramTypes[i+i+1] = 0x00
 
 
 				if cap(paramValues)-len(paramValues)-8 >= 0 {
 				if cap(paramValues)-len(paramValues)-8 >= 0 {
@@ -983,7 +1018,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
 				}
 				}
 
 
 			case float64:
 			case float64:
-				paramTypes[i+i] = fieldTypeDouble
+				paramTypes[i+i] = byte(fieldTypeDouble)
 				paramTypes[i+i+1] = 0x00
 				paramTypes[i+i+1] = 0x00
 
 
 				if cap(paramValues)-len(paramValues)-8 >= 0 {
 				if cap(paramValues)-len(paramValues)-8 >= 0 {
@@ -999,7 +1034,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
 				}
 				}
 
 
 			case bool:
 			case bool:
-				paramTypes[i+i] = fieldTypeTiny
+				paramTypes[i+i] = byte(fieldTypeTiny)
 				paramTypes[i+i+1] = 0x00
 				paramTypes[i+i+1] = 0x00
 
 
 				if v {
 				if v {
@@ -1011,10 +1046,10 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
 			case []byte:
 			case []byte:
 				// Common case (non-nil value) first
 				// Common case (non-nil value) first
 				if v != nil {
 				if v != nil {
-					paramTypes[i+i] = fieldTypeString
+					paramTypes[i+i] = byte(fieldTypeString)
 					paramTypes[i+i+1] = 0x00
 					paramTypes[i+i+1] = 0x00
 
 
-					if len(v) < mc.maxAllowedPacket-pos-len(paramValues)-(len(args)-(i+1))*64 {
+					if len(v) < longDataSize {
 						paramValues = appendLengthEncodedInteger(paramValues,
 						paramValues = appendLengthEncodedInteger(paramValues,
 							uint64(len(v)),
 							uint64(len(v)),
 						)
 						)
@@ -1029,14 +1064,14 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
 
 
 				// Handle []byte(nil) as a NULL value
 				// Handle []byte(nil) as a NULL value
 				nullMask[i/8] |= 1 << (uint(i) & 7)
 				nullMask[i/8] |= 1 << (uint(i) & 7)
-				paramTypes[i+i] = fieldTypeNULL
+				paramTypes[i+i] = byte(fieldTypeNULL)
 				paramTypes[i+i+1] = 0x00
 				paramTypes[i+i+1] = 0x00
 
 
 			case string:
 			case string:
-				paramTypes[i+i] = fieldTypeString
+				paramTypes[i+i] = byte(fieldTypeString)
 				paramTypes[i+i+1] = 0x00
 				paramTypes[i+i+1] = 0x00
 
 
-				if len(v) < mc.maxAllowedPacket-pos-len(paramValues)-(len(args)-(i+1))*64 {
+				if len(v) < longDataSize {
 					paramValues = appendLengthEncodedInteger(paramValues,
 					paramValues = appendLengthEncodedInteger(paramValues,
 						uint64(len(v)),
 						uint64(len(v)),
 					)
 					)
@@ -1048,20 +1083,22 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
 				}
 				}
 
 
 			case time.Time:
 			case time.Time:
-				paramTypes[i+i] = fieldTypeString
+				paramTypes[i+i] = byte(fieldTypeString)
 				paramTypes[i+i+1] = 0x00
 				paramTypes[i+i+1] = 0x00
 
 
-				var val []byte
+				var a [64]byte
+				var b = a[:0]
+
 				if v.IsZero() {
 				if v.IsZero() {
-					val = []byte("0000-00-00")
+					b = append(b, "0000-00-00"...)
 				} else {
 				} else {
-					val = []byte(v.In(mc.cfg.Loc).Format(timeFormat))
+					b = v.In(mc.cfg.Loc).AppendFormat(b, timeFormat)
 				}
 				}
 
 
 				paramValues = appendLengthEncodedInteger(paramValues,
 				paramValues = appendLengthEncodedInteger(paramValues,
-					uint64(len(val)),
+					uint64(len(b)),
 				)
 				)
-				paramValues = append(paramValues, val...)
+				paramValues = append(paramValues, b...)
 
 
 			default:
 			default:
 				return fmt.Errorf("can not convert type: %T", arg)
 				return fmt.Errorf("can not convert type: %T", arg)
@@ -1120,10 +1157,11 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
 			}
 			}
 			return io.EOF
 			return io.EOF
 		}
 		}
+		mc := rows.mc
 		rows.mc = nil
 		rows.mc = nil
 
 
 		// Error otherwise
 		// Error otherwise
-		return rows.mc.handleErrorPacket(data)
+		return mc.handleErrorPacket(data)
 	}
 	}
 
 
 	// NULL-bitmap,  [(column-count + 7 + 2) / 8 bytes]
 	// NULL-bitmap,  [(column-count + 7 + 2) / 8 bytes]
@@ -1187,7 +1225,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
 			continue
 			continue
 
 
 		case fieldTypeFloat:
 		case fieldTypeFloat:
-			dest[i] = float32(math.Float32frombits(binary.LittleEndian.Uint32(data[pos : pos+4])))
+			dest[i] = math.Float32frombits(binary.LittleEndian.Uint32(data[pos : pos+4]))
 			pos += 4
 			pos += 4
 			continue
 			continue
 
 

+ 0 - 78
vendor/github.com/go-sql-driver/mysql/row_columntypes.go

@@ -1,78 +0,0 @@
-package mysql
-
-const (
-	// In case we get something unexpected
-	FieldTypeUnknown = "UNKNOWN"
-
-	// Human-readable names for each distinct type byte
-	FieldTypeNameDecimal    = "DECIMAL"
-	FieldTypeNameTiny       = "TINY"
-	FieldTypeNameShort      = "SHORT"
-	FieldTypeNameLong       = "LONG"
-	FieldTypeNameFloat      = "FLOAT"
-	FieldTypeNameDouble     = "DOUBLE"
-	FieldTypeNameNULL       = "NULL"
-	FieldTypeNameTimestamp  = "TIMESTAMP"
-	FieldTypeNameLongLong   = "LONGLONG"
-	FieldTypeNameInt24      = "INT24"
-	FieldTypeNameDate       = "DATE"
-	FieldTypeNameTime       = "TIME"
-	FieldTypeNameDateTime   = "DATETIME"
-	FieldTypeNameYear       = "YEAR"
-	FieldTypeNameNewDate    = "NEWDATE"
-	FieldTypeNameVarChar    = "VARCHAR"
-	FieldTypeNameBit        = "BIT"
-	FieldTypeNameJSON       = "JSON"
-	FieldTypeNameNewDecimal = "NEWDECIMAL"
-	FieldTypeNameEnum       = "ENUM"
-	FieldTypeNameSet        = "SET"
-	FieldTypeNameTinyBLOB   = "TINYBLOB"
-	FieldTypeNameMediumBLOB = "MEDIUMBLOB"
-	FieldTypeNameLongBLOB   = "LONGBLOB"
-	FieldTypeNameBLOB       = "BLOB"
-	FieldTypeNameVarString  = "VARSTRING"
-	FieldTypeNameString     = "STRING"
-	FieldTypeNameGeometry   = "GEOMETRY"
-)
-
-// mapping from each type identifier to human readable string
-var mysqlTypeMap = map[byte]string{
-	fieldTypeDecimal:    FieldTypeNameDecimal,
-	fieldTypeTiny:       FieldTypeNameTiny,
-	fieldTypeShort:      FieldTypeNameShort,
-	fieldTypeLong:       FieldTypeNameLong,
-	fieldTypeFloat:      FieldTypeNameFloat,
-	fieldTypeDouble:     FieldTypeNameDouble,
-	fieldTypeNULL:       FieldTypeNameNULL,
-	fieldTypeTimestamp:  FieldTypeNameTimestamp,
-	fieldTypeLongLong:   FieldTypeNameLongLong,
-	fieldTypeInt24:      FieldTypeNameInt24,
-	fieldTypeDate:       FieldTypeNameDate,
-	fieldTypeTime:       FieldTypeNameTime,
-	fieldTypeDateTime:   FieldTypeNameDateTime,
-	fieldTypeYear:       FieldTypeNameYear,
-	fieldTypeNewDate:    FieldTypeNameNewDate,
-	fieldTypeVarChar:    FieldTypeNameVarChar,
-	fieldTypeBit:        FieldTypeNameBit,
-	fieldTypeJSON:       FieldTypeNameJSON,
-	fieldTypeNewDecimal: FieldTypeNameNewDecimal,
-	fieldTypeEnum:       FieldTypeNameEnum,
-	fieldTypeSet:        FieldTypeNameSet,
-	fieldTypeTinyBLOB:   FieldTypeNameTinyBLOB,
-	fieldTypeMediumBLOB: FieldTypeNameMediumBLOB,
-	fieldTypeLongBLOB:   FieldTypeNameLongBLOB,
-	fieldTypeBLOB:       FieldTypeNameBLOB,
-	fieldTypeVarString:  FieldTypeNameVarString,
-	fieldTypeString:     FieldTypeNameString,
-	fieldTypeGeometry:   FieldTypeNameGeometry,
-}
-
-// Make Rows implement the optional RowsColumnTypeDatabaseTypeName interface.
-// See https://github.com/golang/go/commit/2a85578b0ecd424e95b29d810b7a414a299fd6a7
-// - (go 1.8 required for this to have any effect)
-func (rows *mysqlRows) ColumnTypeDatabaseTypeName(index int) string {
-	if typeName, ok := mysqlTypeMap[rows.rs.columns[index].fieldType]; ok {
-		return typeName
-	}
-	return FieldTypeUnknown
-}

+ 68 - 40
vendor/github.com/go-sql-driver/mysql/rows.go

@@ -11,34 +11,24 @@ package mysql
 import (
 import (
 	"database/sql/driver"
 	"database/sql/driver"
 	"io"
 	"io"
+	"math"
+	"reflect"
 )
 )
 
 
-type mysqlField struct {
-	tableName string
-	name      string
-	flags     fieldFlag
-	fieldType byte
-	decimals  byte
-}
-
 type resultSet struct {
 type resultSet struct {
-	columns []mysqlField
-	done    bool
+	columns     []mysqlField
+	columnNames []string
+	done        bool
 }
 }
 
 
 type mysqlRows struct {
 type mysqlRows struct {
-	mc *mysqlConn
-	rs resultSet
+	mc     *mysqlConn
+	rs     resultSet
+	finish func()
 }
 }
 
 
 type binaryRows struct {
 type binaryRows struct {
 	mysqlRows
 	mysqlRows
-	// stmtCols is a pointer to the statement's cached columns for different
-	// result sets.
-	stmtCols *[][]mysqlField
-	// i is a number of the current result set. It is used to fetch proper
-	// columns from stmtCols.
-	i int
 }
 }
 
 
 type textRows struct {
 type textRows struct {
@@ -46,6 +36,10 @@ type textRows struct {
 }
 }
 
 
 func (rows *mysqlRows) Columns() []string {
 func (rows *mysqlRows) Columns() []string {
+	if rows.rs.columnNames != nil {
+		return rows.rs.columnNames
+	}
+
 	columns := make([]string, len(rows.rs.columns))
 	columns := make([]string, len(rows.rs.columns))
 	if rows.mc != nil && rows.mc.cfg.ColumnsWithAlias {
 	if rows.mc != nil && rows.mc.cfg.ColumnsWithAlias {
 		for i := range columns {
 		for i := range columns {
@@ -60,16 +54,61 @@ func (rows *mysqlRows) Columns() []string {
 			columns[i] = rows.rs.columns[i].name
 			columns[i] = rows.rs.columns[i].name
 		}
 		}
 	}
 	}
+
+	rows.rs.columnNames = columns
 	return columns
 	return columns
 }
 }
 
 
+func (rows *mysqlRows) ColumnTypeDatabaseTypeName(i int) string {
+	return rows.rs.columns[i].typeDatabaseName()
+}
+
+// func (rows *mysqlRows) ColumnTypeLength(i int) (length int64, ok bool) {
+// 	return int64(rows.rs.columns[i].length), true
+// }
+
+func (rows *mysqlRows) ColumnTypeNullable(i int) (nullable, ok bool) {
+	return rows.rs.columns[i].flags&flagNotNULL == 0, true
+}
+
+func (rows *mysqlRows) ColumnTypePrecisionScale(i int) (int64, int64, bool) {
+	column := rows.rs.columns[i]
+	decimals := int64(column.decimals)
+
+	switch column.fieldType {
+	case fieldTypeDecimal, fieldTypeNewDecimal:
+		if decimals > 0 {
+			return int64(column.length) - 2, decimals, true
+		}
+		return int64(column.length) - 1, decimals, true
+	case fieldTypeTimestamp, fieldTypeDateTime, fieldTypeTime:
+		return decimals, decimals, true
+	case fieldTypeFloat, fieldTypeDouble:
+		if decimals == 0x1f {
+			return math.MaxInt64, math.MaxInt64, true
+		}
+		return math.MaxInt64, decimals, true
+	}
+
+	return 0, 0, false
+}
+
+func (rows *mysqlRows) ColumnTypeScanType(i int) reflect.Type {
+	return rows.rs.columns[i].scanType()
+}
+
 func (rows *mysqlRows) Close() (err error) {
 func (rows *mysqlRows) Close() (err error) {
+	if f := rows.finish; f != nil {
+		f()
+		rows.finish = nil
+	}
+
 	mc := rows.mc
 	mc := rows.mc
 	if mc == nil {
 	if mc == nil {
 		return nil
 		return nil
 	}
 	}
-	if mc.netConn == nil {
-		return ErrInvalidConn
+	if err := mc.error(); err != nil {
+		return err
 	}
 	}
 
 
 	// Remove unread packets from stream
 	// Remove unread packets from stream
@@ -97,8 +136,8 @@ func (rows *mysqlRows) nextResultSet() (int, error) {
 	if rows.mc == nil {
 	if rows.mc == nil {
 		return 0, io.EOF
 		return 0, io.EOF
 	}
 	}
-	if rows.mc.netConn == nil {
-		return 0, ErrInvalidConn
+	if err := rows.mc.error(); err != nil {
+		return 0, err
 	}
 	}
 
 
 	// Remove unread packets from stream
 	// Remove unread packets from stream
@@ -132,31 +171,20 @@ func (rows *mysqlRows) nextNotEmptyResultSet() (int, error) {
 	}
 	}
 }
 }
 
 
-func (rows *binaryRows) NextResultSet() (err error) {
+func (rows *binaryRows) NextResultSet() error {
 	resLen, err := rows.nextNotEmptyResultSet()
 	resLen, err := rows.nextNotEmptyResultSet()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	// get columns, if not cached, read them and cache them.
-	if rows.i >= len(*rows.stmtCols) {
-		rows.rs.columns, err = rows.mc.readColumns(resLen)
-		*rows.stmtCols = append(*rows.stmtCols, rows.rs.columns)
-	} else {
-		rows.rs.columns = (*rows.stmtCols)[rows.i]
-		if err := rows.mc.readUntilEOF(); err != nil {
-			return err
-		}
-	}
-
-	rows.i++
-	return nil
+	rows.rs.columns, err = rows.mc.readColumns(resLen)
+	return err
 }
 }
 
 
 func (rows *binaryRows) Next(dest []driver.Value) error {
 func (rows *binaryRows) Next(dest []driver.Value) error {
 	if mc := rows.mc; mc != nil {
 	if mc := rows.mc; mc != nil {
-		if mc.netConn == nil {
-			return ErrInvalidConn
+		if err := mc.error(); err != nil {
+			return err
 		}
 		}
 
 
 		// Fetch next row from stream
 		// Fetch next row from stream
@@ -177,8 +205,8 @@ func (rows *textRows) NextResultSet() (err error) {
 
 
 func (rows *textRows) Next(dest []driver.Value) error {
 func (rows *textRows) Next(dest []driver.Value) error {
 	if mc := rows.mc; mc != nil {
 	if mc := rows.mc; mc != nil {
-		if mc.netConn == nil {
-			return ErrInvalidConn
+		if err := mc.error(); err != nil {
+			return err
 		}
 		}
 
 
 		// Fetch next row from stream
 		// Fetch next row from stream

+ 26 - 17
vendor/github.com/go-sql-driver/mysql/statement.go

@@ -20,11 +20,10 @@ type mysqlStmt struct {
 	mc         *mysqlConn
 	mc         *mysqlConn
 	id         uint32
 	id         uint32
 	paramCount int
 	paramCount int
-	columns    [][]mysqlField // cached from the first query
 }
 }
 
 
 func (stmt *mysqlStmt) Close() error {
 func (stmt *mysqlStmt) Close() error {
-	if stmt.mc == nil || stmt.mc.netConn == nil {
+	if stmt.mc == nil || stmt.mc.closed.IsSet() {
 		// driver.Stmt.Close can be called more than once, thus this function
 		// driver.Stmt.Close can be called more than once, thus this function
 		// has to be idempotent.
 		// has to be idempotent.
 		// See also Issue #450 and golang/go#16019.
 		// See also Issue #450 and golang/go#16019.
@@ -46,14 +45,14 @@ func (stmt *mysqlStmt) ColumnConverter(idx int) driver.ValueConverter {
 }
 }
 
 
 func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) {
 func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) {
-	if stmt.mc.netConn == nil {
+	if stmt.mc.closed.IsSet() {
 		errLog.Print(ErrInvalidConn)
 		errLog.Print(ErrInvalidConn)
 		return nil, driver.ErrBadConn
 		return nil, driver.ErrBadConn
 	}
 	}
 	// Send command
 	// Send command
 	err := stmt.writeExecutePacket(args)
 	err := stmt.writeExecutePacket(args)
 	if err != nil {
 	if err != nil {
-		return nil, err
+		return nil, stmt.mc.markBadConn(err)
 	}
 	}
 
 
 	mc := stmt.mc
 	mc := stmt.mc
@@ -90,14 +89,18 @@ func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) {
 }
 }
 
 
 func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) {
 func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) {
-	if stmt.mc.netConn == nil {
+	return stmt.query(args)
+}
+
+func (stmt *mysqlStmt) query(args []driver.Value) (*binaryRows, error) {
+	if stmt.mc.closed.IsSet() {
 		errLog.Print(ErrInvalidConn)
 		errLog.Print(ErrInvalidConn)
 		return nil, driver.ErrBadConn
 		return nil, driver.ErrBadConn
 	}
 	}
 	// Send command
 	// Send command
 	err := stmt.writeExecutePacket(args)
 	err := stmt.writeExecutePacket(args)
 	if err != nil {
 	if err != nil {
-		return nil, err
+		return nil, stmt.mc.markBadConn(err)
 	}
 	}
 
 
 	mc := stmt.mc
 	mc := stmt.mc
@@ -109,20 +112,10 @@ func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) {
 	}
 	}
 
 
 	rows := new(binaryRows)
 	rows := new(binaryRows)
-	rows.stmtCols = &stmt.columns
 
 
 	if resLen > 0 {
 	if resLen > 0 {
 		rows.mc = mc
 		rows.mc = mc
-		rows.i++
-		// Columns
-		// If not cached, read them and cache them
-		if len(stmt.columns) == 0 {
-			rows.rs.columns, err = mc.readColumns(resLen)
-			stmt.columns = append(stmt.columns, rows.rs.columns)
-		} else {
-			rows.rs.columns = stmt.columns[0]
-			err = mc.readUntilEOF()
-		}
+		rows.rs.columns, err = mc.readColumns(resLen)
 	} else {
 	} else {
 		rows.rs.done = true
 		rows.rs.done = true
 
 
@@ -144,6 +137,12 @@ func (c converter) ConvertValue(v interface{}) (driver.Value, error) {
 		return v, nil
 		return v, nil
 	}
 	}
 
 
+	if v != nil {
+		if valuer, ok := v.(driver.Valuer); ok {
+			return valuer.Value()
+		}
+	}
+
 	rv := reflect.ValueOf(v)
 	rv := reflect.ValueOf(v)
 	switch rv.Kind() {
 	switch rv.Kind() {
 	case reflect.Ptr:
 	case reflect.Ptr:
@@ -164,6 +163,16 @@ func (c converter) ConvertValue(v interface{}) (driver.Value, error) {
 		return int64(u64), 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:
+		return rv.Bool(), nil
+	case reflect.Slice:
+		ek := rv.Type().Elem().Kind()
+		if ek == reflect.Uint8 {
+			return rv.Bytes(), nil
+		}
+		return nil, fmt.Errorf("unsupported type %T, a slice of %s", v, ek)
+	case reflect.String:
+		return rv.String(), nil
 	}
 	}
 	return nil, fmt.Errorf("unsupported type %T, a %s", v, rv.Kind())
 	return nil, fmt.Errorf("unsupported type %T, a %s", v, rv.Kind())
 }
 }

+ 2 - 2
vendor/github.com/go-sql-driver/mysql/transaction.go

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

+ 83 - 1
vendor/github.com/go-sql-driver/mysql/utils.go

@@ -16,16 +16,21 @@ import (
 	"fmt"
 	"fmt"
 	"io"
 	"io"
 	"strings"
 	"strings"
+	"sync"
+	"sync/atomic"
 	"time"
 	"time"
 )
 )
 
 
 var (
 var (
+	tlsConfigLock     sync.RWMutex
 	tlsConfigRegister map[string]*tls.Config // Register for custom tls.Configs
 	tlsConfigRegister map[string]*tls.Config // Register for custom tls.Configs
 )
 )
 
 
 // RegisterTLSConfig registers a custom tls.Config to be used with sql.Open.
 // RegisterTLSConfig registers a custom tls.Config to be used with sql.Open.
 // Use the key as a value in the DSN where tls=value.
 // Use the key as a value in the DSN where tls=value.
 //
 //
+// Note: The tls.Config provided to needs to be exclusively owned by the driver after registering.
+//
 //  rootCertPool := x509.NewCertPool()
 //  rootCertPool := x509.NewCertPool()
 //  pem, err := ioutil.ReadFile("/path/ca-cert.pem")
 //  pem, err := ioutil.ReadFile("/path/ca-cert.pem")
 //  if err != nil {
 //  if err != nil {
@@ -51,19 +56,32 @@ func RegisterTLSConfig(key string, config *tls.Config) error {
 		return fmt.Errorf("key '%s' is reserved", key)
 		return fmt.Errorf("key '%s' is reserved", key)
 	}
 	}
 
 
+	tlsConfigLock.Lock()
 	if tlsConfigRegister == nil {
 	if tlsConfigRegister == nil {
 		tlsConfigRegister = make(map[string]*tls.Config)
 		tlsConfigRegister = make(map[string]*tls.Config)
 	}
 	}
 
 
 	tlsConfigRegister[key] = config
 	tlsConfigRegister[key] = config
+	tlsConfigLock.Unlock()
 	return nil
 	return nil
 }
 }
 
 
 // DeregisterTLSConfig removes the tls.Config associated with key.
 // DeregisterTLSConfig removes the tls.Config associated with key.
 func DeregisterTLSConfig(key string) {
 func DeregisterTLSConfig(key string) {
+	tlsConfigLock.Lock()
 	if tlsConfigRegister != nil {
 	if tlsConfigRegister != nil {
 		delete(tlsConfigRegister, key)
 		delete(tlsConfigRegister, key)
 	}
 	}
+	tlsConfigLock.Unlock()
+}
+
+func getTLSConfigClone(key string) (config *tls.Config) {
+	tlsConfigLock.RLock()
+	if v, ok := tlsConfigRegister[key]; ok {
+		config = cloneTLSConfig(v)
+	}
+	tlsConfigLock.RUnlock()
+	return
 }
 }
 
 
 // Returns the bool value of the input.
 // Returns the bool value of the input.
@@ -548,8 +566,8 @@ func readLengthEncodedInteger(b []byte) (uint64, bool, int) {
 	if len(b) == 0 {
 	if len(b) == 0 {
 		return 0, true, 1
 		return 0, true, 1
 	}
 	}
-	switch b[0] {
 
 
+	switch b[0] {
 	// 251: NULL
 	// 251: NULL
 	case 0xfb:
 	case 0xfb:
 		return 0, true, 1
 		return 0, true, 1
@@ -738,3 +756,67 @@ func escapeStringQuotes(buf []byte, v string) []byte {
 
 
 	return buf[:pos]
 	return buf[:pos]
 }
 }
+
+/******************************************************************************
+*                               Sync utils                                    *
+******************************************************************************/
+
+// noCopy may be embedded into structs which must not be copied
+// after the first use.
+//
+// See https://github.com/golang/go/issues/8005#issuecomment-190753527
+// for details.
+type noCopy struct{}
+
+// Lock is a no-op used by -copylocks checker from `go vet`.
+func (*noCopy) Lock() {}
+
+// atomicBool is a wrapper around uint32 for usage as a boolean value with
+// atomic access.
+type atomicBool struct {
+	_noCopy noCopy
+	value   uint32
+}
+
+// IsSet returns wether the current boolean value is true
+func (ab *atomicBool) IsSet() bool {
+	return atomic.LoadUint32(&ab.value) > 0
+}
+
+// Set sets the value of the bool regardless of the previous value
+func (ab *atomicBool) Set(value bool) {
+	if value {
+		atomic.StoreUint32(&ab.value, 1)
+	} else {
+		atomic.StoreUint32(&ab.value, 0)
+	}
+}
+
+// TrySet sets the value of the bool and returns wether the value changed
+func (ab *atomicBool) TrySet(value bool) bool {
+	if value {
+		return atomic.SwapUint32(&ab.value, 1) == 0
+	}
+	return atomic.SwapUint32(&ab.value, 0) > 0
+}
+
+// atomicBool is a wrapper for atomically accessed error values
+type atomicError struct {
+	_noCopy noCopy
+	value   atomic.Value
+}
+
+// Set sets the error value regardless of the previous value.
+// The value must not be nil
+func (ae *atomicError) Set(value error) {
+	ae.value.Store(value)
+}
+
+// Value returns the current error value
+func (ae *atomicError) Value() error {
+	if v := ae.value.Load(); v != nil {
+		// this will panic if the value doesn't implement the error interface
+		return v.(error)
+	}
+	return nil
+}

+ 40 - 0
vendor/github.com/go-sql-driver/mysql/utils_go17.go

@@ -0,0 +1,40 @@
+// 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,
+	}
+}

+ 49 - 0
vendor/github.com/go-sql-driver/mysql/utils_go18.go

@@ -0,0 +1,49 @@
+// 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"
+)
+
+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 "", errors.New("mysql: unsupported isolation level: " + string(level))
+	}
+}

+ 11 - 3
vendor/vendor.json

@@ -409,10 +409,12 @@
 			"revisionTime": "2017-03-20T17:22:09Z"
 			"revisionTime": "2017-03-20T17:22:09Z"
 		},
 		},
 		{
 		{
-			"checksumSHA1": "42vkdsxNaLyPu+FktCzZ/8zsNSE=",
+			"checksumSHA1": "178ebwKMX/plldM2EKeT7pj3vs8=",
 			"path": "github.com/go-sql-driver/mysql",
 			"path": "github.com/go-sql-driver/mysql",
-			"revision": "9dee4ca50b83acdf57a35fb9e6fb4be640afa2f3",
-			"revisionTime": "2017-03-27T11:30:21Z"
+			"revision": "2cc627ac8defc45d65066ae98f898166f580f9a4",
+			"revisionTime": "2018-01-13T20:07:44Z",
+			"version": "master",
+			"versionExact": "master"
 		},
 		},
 		{
 		{
 			"checksumSHA1": "luu+ud4XYZhlCoNnSAwCJlbh0Bg=",
 			"checksumSHA1": "luu+ud4XYZhlCoNnSAwCJlbh0Bg=",
@@ -830,6 +832,12 @@
 			"revision": "1cbadb444a806fd9430d14ad08967ed91da4fa0a",
 			"revision": "1cbadb444a806fd9430d14ad08967ed91da4fa0a",
 			"revisionTime": "2017-09-13T19:45:57Z"
 			"revisionTime": "2017-09-13T19:45:57Z"
 		},
 		},
+		{
+			"checksumSHA1": "LiyXfqOzaeQ8vgYZH3t2hUEdVTw=",
+			"path": "google.golang.org/appengine/cloudsql",
+			"revision": "5bee14b453b4c71be47ec1781b0fa61c2ea182db",
+			"revisionTime": "2017-12-12T22:30:47Z"
+		},
 		{
 		{
 			"checksumSHA1": "Tc3BU26zThLzcyqbVtiSEp7EpU8=",
 			"checksumSHA1": "Tc3BU26zThLzcyqbVtiSEp7EpU8=",
 			"path": "google.golang.org/genproto/googleapis/rpc/status",
 			"path": "google.golang.org/genproto/googleapis/rpc/status",