mirror of
https://github.com/documize/community.git
synced 2025-07-19 21:29:42 +02:00
updated vendored deps
This commit is contained in:
parent
0ee2903608
commit
62b7b149c1
22 changed files with 1696 additions and 177 deletions
17
vendor/github.com/go-sql-driver/mysql/AUTHORS
generated
vendored
17
vendor/github.com/go-sql-driver/mysql/AUTHORS
generated
vendored
|
@ -12,34 +12,49 @@
|
||||||
# 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 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>
|
||||||
|
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>
|
||||||
James Harr <james.harr at gmail.com>
|
James Harr <james.harr at gmail.com>
|
||||||
|
Jeff Hodges <jeff at somethingsimilar.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 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>
|
||||||
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>
|
||||||
|
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>
|
||||||
|
Rebecca Chin <rchin at pivotal.io>
|
||||||
Runrioter Wung <runrioter at gmail.com>
|
Runrioter Wung <runrioter at gmail.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>
|
||||||
|
@ -53,4 +68,6 @@ Zhenye Xie <xiezhenye at gmail.com>
|
||||||
|
|
||||||
Barracuda Networks, Inc.
|
Barracuda Networks, Inc.
|
||||||
Google Inc.
|
Google Inc.
|
||||||
|
Keybase Inc.
|
||||||
|
Pivotal Inc.
|
||||||
Stripe Inc.
|
Stripe Inc.
|
||||||
|
|
79
vendor/github.com/go-sql-driver/mysql/README.md
generated
vendored
79
vendor/github.com/go-sql-driver/mysql/README.md
generated
vendored
|
@ -1,6 +1,6 @@
|
||||||
# Go-MySQL-Driver
|
# Go-MySQL-Driver
|
||||||
|
|
||||||
A MySQL-Driver for Go's [database/sql](http://golang.org/pkg/database/sql) package
|
A MySQL-Driver for Go's [database/sql](https://golang.org/pkg/database/sql/) package
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
@ -15,9 +15,11 @@ A MySQL-Driver for Go's [database/sql](http://golang.org/pkg/database/sql) packa
|
||||||
* [Address](#address)
|
* [Address](#address)
|
||||||
* [Parameters](#parameters)
|
* [Parameters](#parameters)
|
||||||
* [Examples](#examples)
|
* [Examples](#examples)
|
||||||
|
* [Connection pool and timeouts](#connection-pool-and-timeouts)
|
||||||
* [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)
|
||||||
|
* [context.Context Support](#contextcontext-support)
|
||||||
* [Testing / Development](#testing--development)
|
* [Testing / Development](#testing--development)
|
||||||
* [License](#license)
|
* [License](#license)
|
||||||
|
|
||||||
|
@ -26,31 +28,31 @@ A MySQL-Driver for Go's [database/sql](http://golang.org/pkg/database/sql) packa
|
||||||
## Features
|
## Features
|
||||||
* Lightweight and [fast](https://github.com/go-sql-driver/sql-benchmark "golang MySQL-Driver performance")
|
* Lightweight and [fast](https://github.com/go-sql-driver/sql-benchmark "golang MySQL-Driver performance")
|
||||||
* Native Go implementation. No C-bindings, just pure Go
|
* Native Go implementation. No C-bindings, just pure Go
|
||||||
* Connections over TCP/IPv4, TCP/IPv6, Unix domain sockets or [custom protocols](http://godoc.org/github.com/go-sql-driver/mysql#DialFunc)
|
* Connections over TCP/IPv4, TCP/IPv6, Unix domain sockets or [custom protocols](https://godoc.org/github.com/go-sql-driver/mysql#DialFunc)
|
||||||
* Automatic handling of broken connections
|
* Automatic handling of broken connections
|
||||||
* Automatic Connection Pooling *(by database/sql package)*
|
* Automatic Connection Pooling *(by database/sql package)*
|
||||||
* Supports queries larger than 16MB
|
* Supports queries larger than 16MB
|
||||||
* Full [`sql.RawBytes`](http://golang.org/pkg/database/sql/#RawBytes) support.
|
* Full [`sql.RawBytes`](https://golang.org/pkg/database/sql/#RawBytes) support.
|
||||||
* Intelligent `LONG DATA` handling in prepared statements
|
* Intelligent `LONG DATA` handling in prepared statements
|
||||||
* Secure `LOAD DATA LOCAL INFILE` support with file Whitelisting and `io.Reader` support
|
* Secure `LOAD DATA LOCAL INFILE` support with file Whitelisting and `io.Reader` support
|
||||||
* Optional `time.Time` parsing
|
* Optional `time.Time` parsing
|
||||||
* Optional placeholder interpolation
|
* Optional placeholder interpolation
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
* Go 1.2 or higher
|
* Go 1.5 or higher
|
||||||
* 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+)
|
||||||
|
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
Simple install the package to your [$GOPATH](http://code.google.com/p/go-wiki/wiki/GOPATH "GOPATH") with the [go tool](http://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 github.com/go-sql-driver/mysql
|
||||||
```
|
```
|
||||||
Make sure [Git is installed](http://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`.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
_Go MySQL Driver_ is an implementation of Go's `database/sql/driver` interface. You only need to import the driver and can use the full [`database/sql`](http://golang.org/pkg/database/sql) API then.
|
_Go MySQL Driver_ is an implementation of Go's `database/sql/driver` interface. You only need to import the driver and can use the full [`database/sql`](https://golang.org/pkg/database/sql/) API then.
|
||||||
|
|
||||||
Use `mysql` as `driverName` and a valid [DSN](#dsn-data-source-name) as `dataSourceName`:
|
Use `mysql` as `driverName` and a valid [DSN](#dsn-data-source-name) as `dataSourceName`:
|
||||||
```go
|
```go
|
||||||
|
@ -95,13 +97,13 @@ Alternatively, [Config.FormatDSN](https://godoc.org/github.com/go-sql-driver/mys
|
||||||
Passwords can consist of any character. Escaping is **not** necessary.
|
Passwords can consist of any character. Escaping is **not** necessary.
|
||||||
|
|
||||||
#### Protocol
|
#### Protocol
|
||||||
See [net.Dial](http://golang.org/pkg/net/#Dial) for more information which networks are available.
|
See [net.Dial](https://golang.org/pkg/net/#Dial) for more information which networks are available.
|
||||||
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 `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](http://golang.org/pkg/net/#JoinHostPort) and [net.SplitHostPort](http://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.
|
||||||
|
|
||||||
For Unix domain sockets the address is the absolute path to the MySQL-Server-socket, e.g. `/var/run/mysqld/mysqld.sock` or `/tmp/mysql.sock`.
|
For Unix domain sockets the address is the absolute path to the MySQL-Server-socket, e.g. `/var/run/mysqld/mysqld.sock` or `/tmp/mysql.sock`.
|
||||||
|
|
||||||
|
@ -220,11 +222,11 @@ Valid Values: <escaped name>
|
||||||
Default: UTC
|
Default: UTC
|
||||||
```
|
```
|
||||||
|
|
||||||
Sets the location for time.Time values (when using `parseTime=true`). *"Local"* sets the system's location. See [time.LoadLocation](http://golang.org/pkg/time/#LoadLocation) for details.
|
Sets the location for time.Time values (when using `parseTime=true`). *"Local"* sets the system's location. See [time.LoadLocation](https://golang.org/pkg/time/#LoadLocation) for details.
|
||||||
|
|
||||||
Note that this sets the location for time.Time values but does not change MySQL's [time_zone setting](https://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html). For that see the [time_zone system variable](#system-variables), which can also be set as a DSN parameter.
|
Note that this sets the location for time.Time values but does not change MySQL's [time_zone setting](https://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html). For that see the [time_zone system variable](#system-variables), which can also be set as a DSN parameter.
|
||||||
|
|
||||||
Please keep in mind, that param values must be [url.QueryEscape](http://golang.org/pkg/net/url/#QueryEscape)'ed. Alternatively you can manually replace the `/` with `%2F`. For example `US/Pacific` would be `loc=US%2FPacific`.
|
Please keep in mind, that param values must be [url.QueryEscape](https://golang.org/pkg/net/url/#QueryEscape)'ed. Alternatively you can manually replace the `/` with `%2F`. For example `US/Pacific` would be `loc=US%2FPacific`.
|
||||||
|
|
||||||
##### `maxAllowedPacket`
|
##### `maxAllowedPacket`
|
||||||
```
|
```
|
||||||
|
@ -260,11 +262,36 @@ Default: false
|
||||||
##### `readTimeout`
|
##### `readTimeout`
|
||||||
|
|
||||||
```
|
```
|
||||||
Type: decimal number
|
Type: duration
|
||||||
Default: 0
|
Default: 0
|
||||||
```
|
```
|
||||||
|
|
||||||
I/O read timeout. The value must be a decimal number with an 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"*.
|
||||||
|
|
||||||
|
##### `rejectReadOnly`
|
||||||
|
|
||||||
|
```
|
||||||
|
Type: bool
|
||||||
|
Valid Values: true, false
|
||||||
|
Default: false
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
`rejectreadOnly=true` causes the driver to reject read-only connections. This
|
||||||
|
is for a possible race condition during an automatic failover, where the mysql
|
||||||
|
client gets connected to a read-only replica after the failover.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
##### `strict`
|
##### `strict`
|
||||||
|
|
||||||
|
@ -283,11 +310,11 @@ By default MySQL also treats notes as warnings. Use [`sql_notes=false`](http://d
|
||||||
##### `timeout`
|
##### `timeout`
|
||||||
|
|
||||||
```
|
```
|
||||||
Type: decimal number
|
Type: duration
|
||||||
Default: OS default
|
Default: OS default
|
||||||
```
|
```
|
||||||
|
|
||||||
*Driver* side connection timeout. The value must be a decimal number with an unit suffix ( *"ms"*, *"s"*, *"m"*, *"h"* ), such as *"30s"*, *"0.5m"* or *"1m30s"*. To set a server side timeout, use the parameter [`wait_timeout`](http://dev.mysql.com/doc/refman/5.6/en/server-system-variables.html#sysvar_wait_timeout).
|
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`
|
||||||
|
|
||||||
|
@ -297,16 +324,16 @@ Valid Values: true, false, skip-verify, <name>
|
||||||
Default: false
|
Default: false
|
||||||
```
|
```
|
||||||
|
|
||||||
`tls=true` enables TLS / SSL encrypted connection to the server. Use `skip-verify` if you want to use a self-signed or invalid certificate (server side). Use a custom value registered with [`mysql.RegisterTLSConfig`](http://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`
|
||||||
|
|
||||||
```
|
```
|
||||||
Type: decimal number
|
Type: duration
|
||||||
Default: 0
|
Default: 0
|
||||||
```
|
```
|
||||||
|
|
||||||
I/O write timeout. The value must be a decimal number with an unit suffix ( *"ms"*, *"s"*, *"m"*, *"h"* ), such as *"30s"*, *"0.5m"* or *"1m30s"*.
|
I/O write timeout. The value must be a decimal number with a unit suffix (*"ms"*, *"s"*, *"m"*, *"h"*), such as *"30s"*, *"0.5m"* or *"1m30s"*.
|
||||||
|
|
||||||
|
|
||||||
##### System Variables
|
##### System Variables
|
||||||
|
@ -380,6 +407,11 @@ No Database preselected:
|
||||||
user:password@/
|
user:password@/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
|
|
||||||
### `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 `_`):
|
||||||
```go
|
```go
|
||||||
|
@ -390,17 +422,17 @@ Files must be whitelisted by registering them with `mysql.RegisterLocalFile(file
|
||||||
|
|
||||||
To use a `io.Reader` a handler function must be registered with `mysql.RegisterReaderHandler(name, handler)` which returns a `io.Reader` or `io.ReadCloser`. The Reader is available with the filepath `Reader::<name>` then. Choose different names for different handlers and `DeregisterReaderHandler` when you don't need it anymore.
|
To use a `io.Reader` a handler function must be registered with `mysql.RegisterReaderHandler(name, handler)` which returns a `io.Reader` or `io.ReadCloser`. The Reader is available with the filepath `Reader::<name>` then. Choose different names for different handlers and `DeregisterReaderHandler` when you don't need it anymore.
|
||||||
|
|
||||||
See the [godoc of Go-MySQL-Driver](http://godoc.org/github.com/go-sql-driver/mysql "golang mysql driver documentation") for details.
|
See the [godoc of Go-MySQL-Driver](https://godoc.org/github.com/go-sql-driver/mysql "golang mysql driver documentation") for details.
|
||||||
|
|
||||||
|
|
||||||
### `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 programm.
|
||||||
|
|
||||||
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](http://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.
|
||||||
|
|
||||||
**Caution:** As of Go 1.1, this makes `time.Time` the only variable type you can scan `DATE` and `DATETIME` values into. This breaks for example [`sql.RawBytes` support](https://github.com/go-sql-driver/mysql/wiki/Examples#rawbytes).
|
**Caution:** As of Go 1.1, this makes `time.Time` the only variable type you can scan `DATE` and `DATETIME` values into. This breaks for example [`sql.RawBytes` support](https://github.com/go-sql-driver/mysql/wiki/Examples#rawbytes).
|
||||||
|
|
||||||
Alternatively you can use the [`NullTime`](http://godoc.org/github.com/go-sql-driver/mysql#NullTime) type as the scan destination, which works with both `time.Time` and `string` / `[]byte`.
|
Alternatively you can use the [`NullTime`](https://godoc.org/github.com/go-sql-driver/mysql#NullTime) type as the scan destination, which works with both `time.Time` and `string` / `[]byte`.
|
||||||
|
|
||||||
|
|
||||||
### Unicode support
|
### Unicode support
|
||||||
|
@ -412,6 +444,9 @@ 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.
|
||||||
|
|
||||||
|
## `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.
|
||||||
|
|
||||||
## 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.
|
||||||
|
@ -435,7 +470,7 @@ That means:
|
||||||
* 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)
|
* 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 **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](http://www.mozilla.org/MPL/2.0/FAQ.html) 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)
|
||||||
|
|
||||||
|
|
93
vendor/github.com/go-sql-driver/mysql/benchmark_go18_test.go
generated
vendored
Normal file
93
vendor/github.com/go-sql-driver/mysql/benchmark_go18_test.go
generated
vendored
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
// 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 (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func benchmarkQueryContext(b *testing.B, db *sql.DB, p int) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
db.SetMaxIdleConns(p * runtime.GOMAXPROCS(0))
|
||||||
|
|
||||||
|
tb := (*TB)(b)
|
||||||
|
stmt := tb.checkStmt(db.PrepareContext(ctx, "SELECT val FROM foo WHERE id=?"))
|
||||||
|
defer stmt.Close()
|
||||||
|
|
||||||
|
b.SetParallelism(p)
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.ResetTimer()
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
var got string
|
||||||
|
for pb.Next() {
|
||||||
|
tb.check(stmt.QueryRow(1).Scan(&got))
|
||||||
|
if got != "one" {
|
||||||
|
b.Fatalf("query = %q; want one", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkQueryContext(b *testing.B) {
|
||||||
|
db := initDB(b,
|
||||||
|
"DROP TABLE IF EXISTS foo",
|
||||||
|
"CREATE TABLE foo (id INT PRIMARY KEY, val CHAR(50))",
|
||||||
|
`INSERT INTO foo VALUES (1, "one")`,
|
||||||
|
`INSERT INTO foo VALUES (2, "two")`,
|
||||||
|
)
|
||||||
|
defer db.Close()
|
||||||
|
for _, p := range []int{1, 2, 3, 4} {
|
||||||
|
b.Run(fmt.Sprintf("%d", p), func(b *testing.B) {
|
||||||
|
benchmarkQueryContext(b, db, p)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func benchmarkExecContext(b *testing.B, db *sql.DB, p int) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
db.SetMaxIdleConns(p * runtime.GOMAXPROCS(0))
|
||||||
|
|
||||||
|
tb := (*TB)(b)
|
||||||
|
stmt := tb.checkStmt(db.PrepareContext(ctx, "DO 1"))
|
||||||
|
defer stmt.Close()
|
||||||
|
|
||||||
|
b.SetParallelism(p)
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.ResetTimer()
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
if _, err := stmt.ExecContext(ctx); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkExecContext(b *testing.B) {
|
||||||
|
db := initDB(b,
|
||||||
|
"DROP TABLE IF EXISTS foo",
|
||||||
|
"CREATE TABLE foo (id INT PRIMARY KEY, val CHAR(50))",
|
||||||
|
`INSERT INTO foo VALUES (1, "one")`,
|
||||||
|
`INSERT INTO foo VALUES (2, "two")`,
|
||||||
|
)
|
||||||
|
defer db.Close()
|
||||||
|
for _, p := range []int{1, 2, 3, 4} {
|
||||||
|
b.Run(fmt.Sprintf("%d", p), func(b *testing.B) {
|
||||||
|
benchmarkQueryContext(b, db, p)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
105
vendor/github.com/go-sql-driver/mysql/connection.go
generated
vendored
105
vendor/github.com/go-sql-driver/mysql/connection.go
generated
vendored
|
@ -10,12 +10,23 @@ package mysql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql/driver"
|
"database/sql/driver"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"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,6 +41,14 @@ type mysqlConn struct {
|
||||||
sequence uint8
|
sequence uint8
|
||||||
parseTime bool
|
parseTime bool
|
||||||
strict 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,7 +82,7 @@ func (mc *mysqlConn) handleParams() (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mc *mysqlConn) Begin() (driver.Tx, error) {
|
func (mc *mysqlConn) Begin() (driver.Tx, error) {
|
||||||
if mc.netConn == nil {
|
if mc.closed.IsSet() {
|
||||||
errLog.Print(ErrInvalidConn)
|
errLog.Print(ErrInvalidConn)
|
||||||
return nil, driver.ErrBadConn
|
return nil, driver.ErrBadConn
|
||||||
}
|
}
|
||||||
|
@ -77,7 +96,7 @@ func (mc *mysqlConn) Begin() (driver.Tx, error) {
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,19 +110,32 @@ 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 {
|
close(mc.closech)
|
||||||
|
if mc.netConn == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
if err := mc.netConn.Close(); err != nil {
|
if err := mc.netConn.Close(); err != nil {
|
||||||
errLog.Print(err)
|
errLog.Print(err)
|
||||||
}
|
}
|
||||||
mc.netConn = nil
|
|
||||||
}
|
}
|
||||||
mc.cfg = nil
|
|
||||||
mc.buf.nc = nil
|
func (mc *mysqlConn) error() error {
|
||||||
|
if mc.closed.IsSet() {
|
||||||
|
if err := mc.canceled.Value(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return ErrInvalidConn
|
||||||
|
}
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
@ -257,7 +289,7 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) {
|
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
|
||||||
}
|
}
|
||||||
|
@ -271,7 +303,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
|
||||||
|
@ -289,26 +320,37 @@ func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, 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
|
||||||
err := mc.writeCommandPacketStr(comQuery, query)
|
if err := mc.writeCommandPacketStr(comQuery, query); err != nil {
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read Result
|
// Read Result
|
||||||
resLen, err := mc.readResultSetHeaderPacket()
|
resLen, err := mc.readResultSetHeaderPacket()
|
||||||
if err == nil && resLen > 0 {
|
if err != nil {
|
||||||
if err = mc.readUntilEOF(); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = mc.readUntilEOF()
|
if resLen > 0 {
|
||||||
|
// columns
|
||||||
|
if err := mc.readUntilEOF(); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rows
|
||||||
|
if err := mc.readUntilEOF(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mc.discardResults()
|
||||||
|
}
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
@ -322,7 +364,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)
|
||||||
|
@ -335,11 +376,17 @@ func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, erro
|
||||||
rows.mc = mc
|
rows.mc = mc
|
||||||
|
|
||||||
if resLen == 0 {
|
if resLen == 0 {
|
||||||
// no columns, no more data
|
rows.rs.done = true
|
||||||
return emptyRows{}, nil
|
|
||||||
|
switch err := rows.NextResultSet(); err {
|
||||||
|
case nil, io.EOF:
|
||||||
|
return rows, nil
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Columns
|
// Columns
|
||||||
rows.columns, err = mc.readColumns(resLen)
|
rows.rs.columns, err = mc.readColumns(resLen)
|
||||||
return rows, err
|
return rows, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -359,7 +406,7 @@ func (mc *mysqlConn) getSystemVar(name string) ([]byte, error) {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
rows := new(textRows)
|
rows := new(textRows)
|
||||||
rows.mc = mc
|
rows.mc = mc
|
||||||
rows.columns = []mysqlField{{fieldType: fieldTypeVarChar}}
|
rows.rs.columns = []mysqlField{{fieldType: fieldTypeVarChar}}
|
||||||
|
|
||||||
if resLen > 0 {
|
if resLen > 0 {
|
||||||
// Columns
|
// Columns
|
||||||
|
@ -375,3 +422,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:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
204
vendor/github.com/go-sql-driver/mysql/connection_go18.go
generated
vendored
Normal file
204
vendor/github.com/go-sql-driver/mysql/connection_go18.go
generated
vendored
Normal file
|
@ -0,0 +1,204 @@
|
||||||
|
// 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"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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 opts.ReadOnly {
|
||||||
|
// TODO: support read-only transactions
|
||||||
|
return nil, errors.New("mysql: read-only transactions not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
13
vendor/github.com/go-sql-driver/mysql/driver.go
generated
vendored
13
vendor/github.com/go-sql-driver/mysql/driver.go
generated
vendored
|
@ -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,6 +57,7 @@ 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 {
|
||||||
|
@ -81,6 +87,11 @@ func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Call startWatcher for context support (From Go 1.8)
|
||||||
|
if s, ok := interface{}(mc).(watcher); ok {
|
||||||
|
s.startWatcher()
|
||||||
|
}
|
||||||
|
|
||||||
mc.buf = newBuffer(mc.netConn)
|
mc.buf = newBuffer(mc.netConn)
|
||||||
|
|
||||||
// Set I/O timeouts
|
// Set I/O timeouts
|
||||||
|
|
522
vendor/github.com/go-sql-driver/mysql/driver_go18_test.go
generated
vendored
Normal file
522
vendor/github.com/go-sql-driver/mysql/driver_go18_test.go
generated
vendored
Normal file
|
@ -0,0 +1,522 @@
|
||||||
|
// 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 (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"database/sql/driver"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// static interface implementation checks of mysqlConn
|
||||||
|
var (
|
||||||
|
_ driver.ConnBeginTx = &mysqlConn{}
|
||||||
|
_ driver.ConnPrepareContext = &mysqlConn{}
|
||||||
|
_ driver.ExecerContext = &mysqlConn{}
|
||||||
|
_ driver.Pinger = &mysqlConn{}
|
||||||
|
_ driver.QueryerContext = &mysqlConn{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// static interface implementation checks of mysqlStmt
|
||||||
|
var (
|
||||||
|
_ driver.StmtExecContext = &mysqlStmt{}
|
||||||
|
_ driver.StmtQueryContext = &mysqlStmt{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMultiResultSet(t *testing.T) {
|
||||||
|
type result struct {
|
||||||
|
values [][]int
|
||||||
|
columns []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkRows is a helper test function to validate rows containing 3 result
|
||||||
|
// sets with specific values and columns. The basic query would look like this:
|
||||||
|
//
|
||||||
|
// SELECT 1 AS col1, 2 AS col2 UNION SELECT 3, 4;
|
||||||
|
// SELECT 0 UNION SELECT 1;
|
||||||
|
// SELECT 1 AS col1, 2 AS col2, 3 AS col3 UNION SELECT 4, 5, 6;
|
||||||
|
//
|
||||||
|
// to distinguish test cases the first string argument is put in front of
|
||||||
|
// every error or fatal message.
|
||||||
|
checkRows := func(desc string, rows *sql.Rows, dbt *DBTest) {
|
||||||
|
expected := []result{
|
||||||
|
{
|
||||||
|
values: [][]int{{1, 2}, {3, 4}},
|
||||||
|
columns: []string{"col1", "col2"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
values: [][]int{{1, 2, 3}, {4, 5, 6}},
|
||||||
|
columns: []string{"col1", "col2", "col3"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var res1 result
|
||||||
|
for rows.Next() {
|
||||||
|
var res [2]int
|
||||||
|
if err := rows.Scan(&res[0], &res[1]); err != nil {
|
||||||
|
dbt.Fatal(err)
|
||||||
|
}
|
||||||
|
res1.values = append(res1.values, res[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
cols, err := rows.Columns()
|
||||||
|
if err != nil {
|
||||||
|
dbt.Fatal(desc, err)
|
||||||
|
}
|
||||||
|
res1.columns = cols
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(expected[0], res1) {
|
||||||
|
dbt.Error(desc, "want =", expected[0], "got =", res1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !rows.NextResultSet() {
|
||||||
|
dbt.Fatal(desc, "expected next result set")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ignoring one result set
|
||||||
|
|
||||||
|
if !rows.NextResultSet() {
|
||||||
|
dbt.Fatal(desc, "expected next result set")
|
||||||
|
}
|
||||||
|
|
||||||
|
var res2 result
|
||||||
|
cols, err = rows.Columns()
|
||||||
|
if err != nil {
|
||||||
|
dbt.Fatal(desc, err)
|
||||||
|
}
|
||||||
|
res2.columns = cols
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var res [3]int
|
||||||
|
if err := rows.Scan(&res[0], &res[1], &res[2]); err != nil {
|
||||||
|
dbt.Fatal(desc, err)
|
||||||
|
}
|
||||||
|
res2.values = append(res2.values, res[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(expected[1], res2) {
|
||||||
|
dbt.Error(desc, "want =", expected[1], "got =", res2)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rows.NextResultSet() {
|
||||||
|
dbt.Error(desc, "unexpected next result set")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
dbt.Error(desc, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
runTestsWithMultiStatement(t, dsn, func(dbt *DBTest) {
|
||||||
|
rows := dbt.mustQuery(`DO 1;
|
||||||
|
SELECT 1 AS col1, 2 AS col2 UNION SELECT 3, 4;
|
||||||
|
DO 1;
|
||||||
|
SELECT 0 UNION SELECT 1;
|
||||||
|
SELECT 1 AS col1, 2 AS col2, 3 AS col3 UNION SELECT 4, 5, 6;`)
|
||||||
|
defer rows.Close()
|
||||||
|
checkRows("query: ", rows, dbt)
|
||||||
|
})
|
||||||
|
|
||||||
|
runTestsWithMultiStatement(t, dsn, func(dbt *DBTest) {
|
||||||
|
queries := []string{
|
||||||
|
`
|
||||||
|
DROP PROCEDURE IF EXISTS test_mrss;
|
||||||
|
CREATE PROCEDURE test_mrss()
|
||||||
|
BEGIN
|
||||||
|
DO 1;
|
||||||
|
SELECT 1 AS col1, 2 AS col2 UNION SELECT 3, 4;
|
||||||
|
DO 1;
|
||||||
|
SELECT 0 UNION SELECT 1;
|
||||||
|
SELECT 1 AS col1, 2 AS col2, 3 AS col3 UNION SELECT 4, 5, 6;
|
||||||
|
END
|
||||||
|
`,
|
||||||
|
`
|
||||||
|
DROP PROCEDURE IF EXISTS test_mrss;
|
||||||
|
CREATE PROCEDURE test_mrss()
|
||||||
|
BEGIN
|
||||||
|
SELECT 1 AS col1, 2 AS col2 UNION SELECT 3, 4;
|
||||||
|
SELECT 0 UNION SELECT 1;
|
||||||
|
SELECT 1 AS col1, 2 AS col2, 3 AS col3 UNION SELECT 4, 5, 6;
|
||||||
|
END
|
||||||
|
`,
|
||||||
|
}
|
||||||
|
|
||||||
|
defer dbt.mustExec("DROP PROCEDURE IF EXISTS test_mrss")
|
||||||
|
|
||||||
|
for i, query := range queries {
|
||||||
|
dbt.mustExec(query)
|
||||||
|
|
||||||
|
stmt, err := dbt.db.Prepare("CALL test_mrss()")
|
||||||
|
if err != nil {
|
||||||
|
dbt.Fatalf("%v (i=%d)", err, i)
|
||||||
|
}
|
||||||
|
defer stmt.Close()
|
||||||
|
|
||||||
|
for j := 0; j < 2; j++ {
|
||||||
|
rows, err := stmt.Query()
|
||||||
|
if err != nil {
|
||||||
|
dbt.Fatalf("%v (i=%d) (j=%d)", err, i, j)
|
||||||
|
}
|
||||||
|
checkRows(fmt.Sprintf("prepared stmt query (i=%d) (j=%d): ", i, j), rows, dbt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMultiResultSetNoSelect(t *testing.T) {
|
||||||
|
runTestsWithMultiStatement(t, dsn, func(dbt *DBTest) {
|
||||||
|
rows := dbt.mustQuery("DO 1; DO 2;")
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
if rows.Next() {
|
||||||
|
dbt.Error("unexpected row")
|
||||||
|
}
|
||||||
|
|
||||||
|
if rows.NextResultSet() {
|
||||||
|
dbt.Error("unexpected next result set")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
dbt.Error("expected nil; got ", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// tests if rows are set in a proper state if some results were ignored before
|
||||||
|
// calling rows.NextResultSet.
|
||||||
|
func TestSkipResults(t *testing.T) {
|
||||||
|
runTests(t, dsn, func(dbt *DBTest) {
|
||||||
|
rows := dbt.mustQuery("SELECT 1, 2")
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
if !rows.Next() {
|
||||||
|
dbt.Error("expected row")
|
||||||
|
}
|
||||||
|
|
||||||
|
if rows.NextResultSet() {
|
||||||
|
dbt.Error("unexpected next result set")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
dbt.Error("expected nil; got ", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPingContext(t *testing.T) {
|
||||||
|
runTests(t, dsn, func(dbt *DBTest) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
cancel()
|
||||||
|
if err := dbt.db.PingContext(ctx); err != context.Canceled {
|
||||||
|
dbt.Errorf("expected context.Canceled, got %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContextCancelExec(t *testing.T) {
|
||||||
|
runTests(t, dsn, func(dbt *DBTest) {
|
||||||
|
dbt.mustExec("CREATE TABLE test (v INTEGER)")
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
// Delay execution for just a bit until db.ExecContext has begun.
|
||||||
|
defer time.AfterFunc(100*time.Millisecond, cancel).Stop()
|
||||||
|
|
||||||
|
// This query will be canceled.
|
||||||
|
startTime := time.Now()
|
||||||
|
if _, err := dbt.db.ExecContext(ctx, "INSERT INTO test VALUES (SLEEP(1))"); err != context.Canceled {
|
||||||
|
dbt.Errorf("expected context.Canceled, got %v", err)
|
||||||
|
}
|
||||||
|
if d := time.Since(startTime); d > 500*time.Millisecond {
|
||||||
|
dbt.Errorf("too long execution time: %s", d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the INSERT query has done.
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
|
// Check how many times the query is executed.
|
||||||
|
var v int
|
||||||
|
if err := dbt.db.QueryRow("SELECT COUNT(*) FROM test").Scan(&v); err != nil {
|
||||||
|
dbt.Fatalf("%s", err.Error())
|
||||||
|
}
|
||||||
|
if v != 1 { // TODO: need to kill the query, and v should be 0.
|
||||||
|
dbt.Errorf("expected val to be 1, got %d", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context is already canceled, so error should come before execution.
|
||||||
|
if _, err := dbt.db.ExecContext(ctx, "INSERT INTO test VALUES (1)"); err == nil {
|
||||||
|
dbt.Error("expected error")
|
||||||
|
} else if err.Error() != "context canceled" {
|
||||||
|
dbt.Fatalf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The second insert query will fail, so the table has no changes.
|
||||||
|
if err := dbt.db.QueryRow("SELECT COUNT(*) FROM test").Scan(&v); err != nil {
|
||||||
|
dbt.Fatalf("%s", err.Error())
|
||||||
|
}
|
||||||
|
if v != 1 {
|
||||||
|
dbt.Errorf("expected val to be 1, got %d", v)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContextCancelQuery(t *testing.T) {
|
||||||
|
runTests(t, dsn, func(dbt *DBTest) {
|
||||||
|
dbt.mustExec("CREATE TABLE test (v INTEGER)")
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
// Delay execution for just a bit until db.ExecContext has begun.
|
||||||
|
defer time.AfterFunc(100*time.Millisecond, cancel).Stop()
|
||||||
|
|
||||||
|
// This query will be canceled.
|
||||||
|
startTime := time.Now()
|
||||||
|
if _, err := dbt.db.QueryContext(ctx, "INSERT INTO test VALUES (SLEEP(1))"); err != context.Canceled {
|
||||||
|
dbt.Errorf("expected context.Canceled, got %v", err)
|
||||||
|
}
|
||||||
|
if d := time.Since(startTime); d > 500*time.Millisecond {
|
||||||
|
dbt.Errorf("too long execution time: %s", d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the INSERT query has done.
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
|
// Check how many times the query is executed.
|
||||||
|
var v int
|
||||||
|
if err := dbt.db.QueryRow("SELECT COUNT(*) FROM test").Scan(&v); err != nil {
|
||||||
|
dbt.Fatalf("%s", err.Error())
|
||||||
|
}
|
||||||
|
if v != 1 { // TODO: need to kill the query, and v should be 0.
|
||||||
|
dbt.Errorf("expected val to be 1, got %d", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context is already canceled, so error should come before execution.
|
||||||
|
if _, err := dbt.db.QueryContext(ctx, "INSERT INTO test VALUES (1)"); err != context.Canceled {
|
||||||
|
dbt.Errorf("expected context.Canceled, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The second insert query will fail, so the table has no changes.
|
||||||
|
if err := dbt.db.QueryRow("SELECT COUNT(*) FROM test").Scan(&v); err != nil {
|
||||||
|
dbt.Fatalf("%s", err.Error())
|
||||||
|
}
|
||||||
|
if v != 1 {
|
||||||
|
dbt.Errorf("expected val to be 1, got %d", v)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContextCancelQueryRow(t *testing.T) {
|
||||||
|
runTests(t, dsn, func(dbt *DBTest) {
|
||||||
|
dbt.mustExec("CREATE TABLE test (v INTEGER)")
|
||||||
|
dbt.mustExec("INSERT INTO test VALUES (1), (2), (3)")
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
rows, err := dbt.db.QueryContext(ctx, "SELECT v FROM test")
|
||||||
|
if err != nil {
|
||||||
|
dbt.Fatalf("%s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// the first row will be succeed.
|
||||||
|
var v int
|
||||||
|
if !rows.Next() {
|
||||||
|
dbt.Fatalf("unexpected end")
|
||||||
|
}
|
||||||
|
if err := rows.Scan(&v); err != nil {
|
||||||
|
dbt.Fatalf("%s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel()
|
||||||
|
// make sure the driver recieve cancel request.
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
if rows.Next() {
|
||||||
|
dbt.Errorf("expected end, but not")
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != context.Canceled {
|
||||||
|
dbt.Errorf("expected context.Canceled, got %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContextCancelPrepare(t *testing.T) {
|
||||||
|
runTests(t, dsn, func(dbt *DBTest) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
cancel()
|
||||||
|
if _, err := dbt.db.PrepareContext(ctx, "SELECT 1"); err != context.Canceled {
|
||||||
|
dbt.Errorf("expected context.Canceled, got %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContextCancelStmtExec(t *testing.T) {
|
||||||
|
runTests(t, dsn, func(dbt *DBTest) {
|
||||||
|
dbt.mustExec("CREATE TABLE test (v INTEGER)")
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
stmt, err := dbt.db.PrepareContext(ctx, "INSERT INTO test VALUES (SLEEP(1))")
|
||||||
|
if err != nil {
|
||||||
|
dbt.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delay execution for just a bit until db.ExecContext has begun.
|
||||||
|
defer time.AfterFunc(100*time.Millisecond, cancel).Stop()
|
||||||
|
|
||||||
|
// This query will be canceled.
|
||||||
|
startTime := time.Now()
|
||||||
|
if _, err := stmt.ExecContext(ctx); err != context.Canceled {
|
||||||
|
dbt.Errorf("expected context.Canceled, got %v", err)
|
||||||
|
}
|
||||||
|
if d := time.Since(startTime); d > 500*time.Millisecond {
|
||||||
|
dbt.Errorf("too long execution time: %s", d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the INSERT query has done.
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
|
// Check how many times the query is executed.
|
||||||
|
var v int
|
||||||
|
if err := dbt.db.QueryRow("SELECT COUNT(*) FROM test").Scan(&v); err != nil {
|
||||||
|
dbt.Fatalf("%s", err.Error())
|
||||||
|
}
|
||||||
|
if v != 1 { // TODO: need to kill the query, and v should be 0.
|
||||||
|
dbt.Errorf("expected val to be 1, got %d", v)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContextCancelStmtQuery(t *testing.T) {
|
||||||
|
runTests(t, dsn, func(dbt *DBTest) {
|
||||||
|
dbt.mustExec("CREATE TABLE test (v INTEGER)")
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
stmt, err := dbt.db.PrepareContext(ctx, "INSERT INTO test VALUES (SLEEP(1))")
|
||||||
|
if err != nil {
|
||||||
|
dbt.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delay execution for just a bit until db.ExecContext has begun.
|
||||||
|
defer time.AfterFunc(100*time.Millisecond, cancel).Stop()
|
||||||
|
|
||||||
|
// This query will be canceled.
|
||||||
|
startTime := time.Now()
|
||||||
|
if _, err := stmt.QueryContext(ctx); err != context.Canceled {
|
||||||
|
dbt.Errorf("expected context.Canceled, got %v", err)
|
||||||
|
}
|
||||||
|
if d := time.Since(startTime); d > 500*time.Millisecond {
|
||||||
|
dbt.Errorf("too long execution time: %s", d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the INSERT query has done.
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
|
||||||
|
// Check how many times the query is executed.
|
||||||
|
var v int
|
||||||
|
if err := dbt.db.QueryRow("SELECT COUNT(*) FROM test").Scan(&v); err != nil {
|
||||||
|
dbt.Fatalf("%s", err.Error())
|
||||||
|
}
|
||||||
|
if v != 1 { // TODO: need to kill the query, and v should be 0.
|
||||||
|
dbt.Errorf("expected val to be 1, got %d", v)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContextCancelBegin(t *testing.T) {
|
||||||
|
runTests(t, dsn, func(dbt *DBTest) {
|
||||||
|
dbt.mustExec("CREATE TABLE test (v INTEGER)")
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
tx, err := dbt.db.BeginTx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
dbt.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delay execution for just a bit until db.ExecContext has begun.
|
||||||
|
defer time.AfterFunc(100*time.Millisecond, cancel).Stop()
|
||||||
|
|
||||||
|
// This query will be canceled.
|
||||||
|
startTime := time.Now()
|
||||||
|
if _, err := tx.ExecContext(ctx, "INSERT INTO test VALUES (SLEEP(1))"); err != context.Canceled {
|
||||||
|
dbt.Errorf("expected context.Canceled, got %v", err)
|
||||||
|
}
|
||||||
|
if d := time.Since(startTime); d > 500*time.Millisecond {
|
||||||
|
dbt.Errorf("too long execution time: %s", d)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transaction is canceled, so expect an error.
|
||||||
|
switch err := tx.Commit(); err {
|
||||||
|
case sql.ErrTxDone:
|
||||||
|
// because the transaction has already been rollbacked.
|
||||||
|
// the database/sql package watches ctx
|
||||||
|
// and rollbacks when ctx is canceled.
|
||||||
|
case context.Canceled:
|
||||||
|
// the database/sql package rollbacks on another goroutine,
|
||||||
|
// so the transaction may not be rollbacked depending on goroutine scheduling.
|
||||||
|
default:
|
||||||
|
dbt.Errorf("expected sql.ErrTxDone or context.Canceled, got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Context is canceled, so cannot begin a transaction.
|
||||||
|
if _, err := dbt.db.BeginTx(ctx, nil); err != context.Canceled {
|
||||||
|
dbt.Errorf("expected context.Canceled, got %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContextBeginIsolationLevel(t *testing.T) {
|
||||||
|
runTests(t, dsn, func(dbt *DBTest) {
|
||||||
|
dbt.mustExec("CREATE TABLE test (v INTEGER)")
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
tx1, err := dbt.db.BeginTx(ctx, &sql.TxOptions{
|
||||||
|
Isolation: sql.LevelRepeatableRead,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
dbt.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tx2, err := dbt.db.BeginTx(ctx, &sql.TxOptions{
|
||||||
|
Isolation: sql.LevelReadCommitted,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
dbt.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = tx1.ExecContext(ctx, "INSERT INTO test VALUES (1)")
|
||||||
|
if err != nil {
|
||||||
|
dbt.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var v int
|
||||||
|
row := tx2.QueryRowContext(ctx, "SELECT COUNT(*) FROM test")
|
||||||
|
if err := row.Scan(&v); err != nil {
|
||||||
|
dbt.Fatal(err)
|
||||||
|
}
|
||||||
|
// Because writer transaction wasn't commited yet, it should be available
|
||||||
|
if v != 0 {
|
||||||
|
dbt.Errorf("expected val to be 0, got %d", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx1.Commit()
|
||||||
|
if err != nil {
|
||||||
|
dbt.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
row = tx2.QueryRowContext(ctx, "SELECT COUNT(*) FROM test")
|
||||||
|
if err := row.Scan(&v); err != nil {
|
||||||
|
dbt.Fatal(err)
|
||||||
|
}
|
||||||
|
// Data written by writer transaction is already commited, it should be selectable
|
||||||
|
if v != 1 {
|
||||||
|
dbt.Errorf("expected val to be 1, got %d", v)
|
||||||
|
}
|
||||||
|
tx2.Commit()
|
||||||
|
})
|
||||||
|
}
|
129
vendor/github.com/go-sql-driver/mysql/driver_test.go
generated
vendored
129
vendor/github.com/go-sql-driver/mysql/driver_test.go
generated
vendored
|
@ -171,6 +171,17 @@ func (dbt *DBTest) mustQuery(query string, args ...interface{}) (rows *sql.Rows)
|
||||||
return rows
|
return rows
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func maybeSkip(t *testing.T, err error, skipErrno uint16) {
|
||||||
|
mySQLErr, ok := err.(*MySQLError)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if mySQLErr.Number == skipErrno {
|
||||||
|
t.Skipf("skipping test for error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestEmptyQuery(t *testing.T) {
|
func TestEmptyQuery(t *testing.T) {
|
||||||
runTests(t, dsn, func(dbt *DBTest) {
|
runTests(t, dsn, func(dbt *DBTest) {
|
||||||
// just a comment, no query
|
// just a comment, no query
|
||||||
|
@ -684,7 +695,7 @@ func TestDateTime(t *testing.T) {
|
||||||
for _, setup := range setups.tests {
|
for _, setup := range setups.tests {
|
||||||
allowBinTime := true
|
allowBinTime := true
|
||||||
if setup.s == "" {
|
if setup.s == "" {
|
||||||
// fill time string whereever Go can reliable produce it
|
// fill time string wherever Go can reliable produce it
|
||||||
setup.s = setup.t.Format(setups.tlayout)
|
setup.s = setup.t.Format(setups.tlayout)
|
||||||
} else if setup.s[0] == '!' {
|
} else if setup.s[0] == '!' {
|
||||||
// skip tests using setup.t as source in queries
|
// skip tests using setup.t as source in queries
|
||||||
|
@ -856,14 +867,14 @@ func TestNULL(t *testing.T) {
|
||||||
dbt.Fatal(err)
|
dbt.Fatal(err)
|
||||||
}
|
}
|
||||||
if b != nil {
|
if b != nil {
|
||||||
dbt.Error("non-nil []byte wich should be nil")
|
dbt.Error("non-nil []byte which should be nil")
|
||||||
}
|
}
|
||||||
// Read non-nil
|
// Read non-nil
|
||||||
if err = nonNullStmt.QueryRow().Scan(&b); err != nil {
|
if err = nonNullStmt.QueryRow().Scan(&b); err != nil {
|
||||||
dbt.Fatal(err)
|
dbt.Fatal(err)
|
||||||
}
|
}
|
||||||
if b == nil {
|
if b == nil {
|
||||||
dbt.Error("nil []byte wich should be non-nil")
|
dbt.Error("nil []byte which should be non-nil")
|
||||||
}
|
}
|
||||||
// Insert nil
|
// Insert nil
|
||||||
b = nil
|
b = nil
|
||||||
|
@ -1054,22 +1065,36 @@ func TestLoadData(t *testing.T) {
|
||||||
dbt.Fatalf("rows count mismatch. Got %d, want 4", i)
|
dbt.Fatalf("rows count mismatch. Got %d, want 4", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file, err := ioutil.TempFile("", "gotest")
|
|
||||||
defer os.Remove(file.Name())
|
|
||||||
if err != nil {
|
|
||||||
dbt.Fatal(err)
|
|
||||||
}
|
|
||||||
file.WriteString("1\ta string\n2\ta string containing a \\t\n3\ta string containing a \\n\n4\ta string containing both \\t\\n\n")
|
|
||||||
file.Close()
|
|
||||||
|
|
||||||
dbt.db.Exec("DROP TABLE IF EXISTS test")
|
dbt.db.Exec("DROP TABLE IF EXISTS test")
|
||||||
dbt.mustExec("CREATE TABLE test (id INT NOT NULL PRIMARY KEY, value TEXT NOT NULL) CHARACTER SET utf8")
|
dbt.mustExec("CREATE TABLE test (id INT NOT NULL PRIMARY KEY, value TEXT NOT NULL) CHARACTER SET utf8")
|
||||||
|
|
||||||
// Local File
|
// Local File
|
||||||
|
file, err := ioutil.TempFile("", "gotest")
|
||||||
|
defer os.Remove(file.Name())
|
||||||
|
if err != nil {
|
||||||
|
dbt.Fatal(err)
|
||||||
|
}
|
||||||
RegisterLocalFile(file.Name())
|
RegisterLocalFile(file.Name())
|
||||||
|
|
||||||
|
// Try first with empty file
|
||||||
|
dbt.mustExec(fmt.Sprintf("LOAD DATA LOCAL INFILE %q INTO TABLE test", file.Name()))
|
||||||
|
var count int
|
||||||
|
err = dbt.db.QueryRow("SELECT COUNT(*) FROM test").Scan(&count)
|
||||||
|
if err != nil {
|
||||||
|
dbt.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
if count != 0 {
|
||||||
|
dbt.Fatalf("unexpected row count: got %d, want 0", count)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then fille File with data and try to load it
|
||||||
|
file.WriteString("1\ta string\n2\ta string containing a \\t\n3\ta string containing a \\n\n4\ta string containing both \\t\\n\n")
|
||||||
|
file.Close()
|
||||||
dbt.mustExec(fmt.Sprintf("LOAD DATA LOCAL INFILE %q INTO TABLE test", file.Name()))
|
dbt.mustExec(fmt.Sprintf("LOAD DATA LOCAL INFILE %q INTO TABLE test", file.Name()))
|
||||||
verifyLoadDataResult()
|
verifyLoadDataResult()
|
||||||
// negative test
|
|
||||||
|
// Try with non-existing file
|
||||||
_, err = dbt.db.Exec("LOAD DATA LOCAL INFILE 'doesnotexist' INTO TABLE test")
|
_, err = dbt.db.Exec("LOAD DATA LOCAL INFILE 'doesnotexist' INTO TABLE test")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
dbt.Fatal("load non-existent file didn't fail")
|
dbt.Fatal("load non-existent file didn't fail")
|
||||||
|
@ -1154,11 +1179,9 @@ func TestStrict(t *testing.T) {
|
||||||
if conn != nil {
|
if conn != nil {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
}
|
}
|
||||||
if me, ok := err.(*MySQLError); ok && me.Number == 1231 {
|
// Error 1231: Variable 'sql_mode' can't be set to the value of
|
||||||
// Error 1231: Variable 'sql_mode' can't be set to the value of 'ALLOW_INVALID_DATES'
|
// 'ALLOW_INVALID_DATES' => skip test, MySQL server version is too old
|
||||||
// => skip test, MySQL server version is too old
|
maybeSkip(t, err, 1231)
|
||||||
return
|
|
||||||
}
|
|
||||||
runTests(t, relaxedDsn, func(dbt *DBTest) {
|
runTests(t, relaxedDsn, func(dbt *DBTest) {
|
||||||
dbt.mustExec("CREATE TABLE test (a TINYINT NOT NULL, b CHAR(4))")
|
dbt.mustExec("CREATE TABLE test (a TINYINT NOT NULL, b CHAR(4))")
|
||||||
|
|
||||||
|
@ -1902,3 +1925,77 @@ func TestInterruptBySignal(t *testing.T) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestColumnsReusesSlice(t *testing.T) {
|
||||||
|
rows := mysqlRows{
|
||||||
|
rs: resultSet{
|
||||||
|
columns: []mysqlField{
|
||||||
|
{
|
||||||
|
tableName: "test",
|
||||||
|
name: "A",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tableName: "test",
|
||||||
|
name: "B",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
allocs := testing.AllocsPerRun(1, func() {
|
||||||
|
cols := rows.Columns()
|
||||||
|
|
||||||
|
if len(cols) != 2 {
|
||||||
|
t.Fatalf("expected 2 columns, got %d", len(cols))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if allocs != 0 {
|
||||||
|
t.Fatalf("expected 0 allocations, got %d", int(allocs))
|
||||||
|
}
|
||||||
|
|
||||||
|
if rows.rs.columnNames == nil {
|
||||||
|
t.Fatalf("expected columnNames to be set, got nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRejectReadOnly(t *testing.T) {
|
||||||
|
runTests(t, dsn, func(dbt *DBTest) {
|
||||||
|
// Create Table
|
||||||
|
dbt.mustExec("CREATE TABLE test (value BOOL)")
|
||||||
|
// Set the session to read-only. We didn't set the `rejectReadOnly`
|
||||||
|
// option, so any writes after this should fail.
|
||||||
|
_, err := dbt.db.Exec("SET SESSION TRANSACTION READ ONLY")
|
||||||
|
// Error 1193: Unknown system variable 'TRANSACTION' => skip test,
|
||||||
|
// MySQL server version is too old
|
||||||
|
maybeSkip(t, err, 1193)
|
||||||
|
if _, err := dbt.db.Exec("DROP TABLE test"); err == nil {
|
||||||
|
t.Fatalf("writing to DB in read-only session without " +
|
||||||
|
"rejectReadOnly did not error")
|
||||||
|
}
|
||||||
|
// Set the session back to read-write so runTests() can properly clean
|
||||||
|
// up the table `test`.
|
||||||
|
dbt.mustExec("SET SESSION TRANSACTION READ WRITE")
|
||||||
|
})
|
||||||
|
|
||||||
|
// Enable the `rejectReadOnly` option.
|
||||||
|
runTests(t, dsn+"&rejectReadOnly=true", func(dbt *DBTest) {
|
||||||
|
// Create Table
|
||||||
|
dbt.mustExec("CREATE TABLE test (value BOOL)")
|
||||||
|
// Set the session to read only. Any writes after this should error on
|
||||||
|
// a driver.ErrBadConn, and cause `database/sql` to initiate a new
|
||||||
|
// connection.
|
||||||
|
dbt.mustExec("SET SESSION TRANSACTION READ ONLY")
|
||||||
|
// This would error, but `database/sql` should automatically retry on a
|
||||||
|
// new connection which is not read-only, and eventually succeed.
|
||||||
|
dbt.mustExec("DROP TABLE test")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPing(t *testing.T) {
|
||||||
|
runTests(t, dsn, func(dbt *DBTest) {
|
||||||
|
if err := dbt.db.Ping(); err != nil {
|
||||||
|
dbt.fail("Ping", "Ping", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
34
vendor/github.com/go-sql-driver/mysql/dsn.go
generated
vendored
34
vendor/github.com/go-sql-driver/mysql/dsn.go
generated
vendored
|
@ -15,6 +15,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -53,6 +54,7 @@ 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
|
||||||
|
RejectReadOnly bool // Reject read-only connections
|
||||||
Strict bool // Return warnings as errors
|
Strict bool // Return warnings as errors
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,6 +197,15 @@ func (cfg *Config) FormatDSN() string {
|
||||||
buf.WriteString(cfg.ReadTimeout.String())
|
buf.WriteString(cfg.ReadTimeout.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cfg.RejectReadOnly {
|
||||||
|
if hasParam {
|
||||||
|
buf.WriteString("&rejectReadOnly=true")
|
||||||
|
} else {
|
||||||
|
hasParam = true
|
||||||
|
buf.WriteString("?rejectReadOnly=true")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if cfg.Strict {
|
if cfg.Strict {
|
||||||
if hasParam {
|
if hasParam {
|
||||||
buf.WriteString("&strict=true")
|
buf.WriteString("&strict=true")
|
||||||
|
@ -247,7 +258,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 +273,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]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -472,6 +488,14 @@ func parseDSNParams(cfg *Config, params string) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reject read-only connections
|
||||||
|
case "rejectReadOnly":
|
||||||
|
var isBool bool
|
||||||
|
cfg.RejectReadOnly, isBool = readBool(value)
|
||||||
|
if !isBool {
|
||||||
|
return errors.New("invalid bool value: " + value)
|
||||||
|
}
|
||||||
|
|
||||||
// Strict mode
|
// Strict mode
|
||||||
case "strict":
|
case "strict":
|
||||||
var isBool bool
|
var isBool bool
|
||||||
|
@ -494,6 +518,10 @@ func parseDSNParams(cfg *Config, params string) (err error) {
|
||||||
if boolValue {
|
if boolValue {
|
||||||
cfg.TLSConfig = "true"
|
cfg.TLSConfig = "true"
|
||||||
cfg.tls = &tls.Config{}
|
cfg.tls = &tls.Config{}
|
||||||
|
host, _, err := net.SplitHostPort(cfg.Addr)
|
||||||
|
if err == nil {
|
||||||
|
cfg.tls.ServerName = host
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
cfg.TLSConfig = "false"
|
cfg.TLSConfig = "false"
|
||||||
}
|
}
|
||||||
|
@ -506,7 +534,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 tlsConfig := getTLSConfigClone(name); tlsConfig != nil {
|
||||||
if len(tlsConfig.ServerName) == 0 && !tlsConfig.InsecureSkipVerify {
|
if len(tlsConfig.ServerName) == 0 && !tlsConfig.InsecureSkipVerify {
|
||||||
host, _, err := net.SplitHostPort(cfg.Addr)
|
host, _, err := net.SplitHostPort(cfg.Addr)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
18
vendor/github.com/go-sql-driver/mysql/dsn_test.go
generated
vendored
18
vendor/github.com/go-sql-driver/mysql/dsn_test.go
generated
vendored
|
@ -159,6 +159,8 @@ func TestDSNWithCustomTLS(t *testing.T) {
|
||||||
t.Error(err.Error())
|
t.Error(err.Error())
|
||||||
} else if cfg.tls.ServerName != name {
|
} else if cfg.tls.ServerName != name {
|
||||||
t.Errorf("did not get the correct ServerName (%s) parsing DSN (%s).", name, tst)
|
t.Errorf("did not get the correct ServerName (%s) parsing DSN (%s).", name, tst)
|
||||||
|
} else if tlsCfg.ServerName != "" {
|
||||||
|
t.Errorf("tlsCfg was mutated ServerName (%s) should be empty parsing DSN (%s).", name, tst)
|
||||||
}
|
}
|
||||||
|
|
||||||
DeregisterTLSConfig("utils_test")
|
DeregisterTLSConfig("utils_test")
|
||||||
|
@ -218,6 +220,22 @@ func TestDSNUnsafeCollation(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParamsAreSorted(t *testing.T) {
|
||||||
|
expected := "/dbname?interpolateParams=true&foobar=baz&quux=loo"
|
||||||
|
dsn := &Config{
|
||||||
|
DBName: "dbname",
|
||||||
|
InterpolateParams: true,
|
||||||
|
Params: map[string]string{
|
||||||
|
"quux": "loo",
|
||||||
|
"foobar": "baz",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
actual := dsn.FormatDSN()
|
||||||
|
if actual != expected {
|
||||||
|
t.Errorf("generic Config.Params were not sorted: want %#v, got %#v", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkParseDSN(b *testing.B) {
|
func BenchmarkParseDSN(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
|
|
||||||
|
|
3
vendor/github.com/go-sql-driver/mysql/infile.go
generated
vendored
3
vendor/github.com/go-sql-driver/mysql/infile.go
generated
vendored
|
@ -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 {
|
||||||
|
|
113
vendor/github.com/go-sql-driver/mysql/packets.go
generated
vendored
113
vendor/github.com/go-sql-driver/mysql/packets.go
generated
vendored
|
@ -30,6 +30,9 @@ 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, driver.ErrBadConn
|
||||||
|
@ -63,6 +66,9 @@ 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, driver.ErrBadConn
|
||||||
|
@ -125,8 +131,13 @@ 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
|
||||||
|
}
|
||||||
|
mc.cleanup()
|
||||||
errLog.Print(err)
|
errLog.Print(err)
|
||||||
}
|
}
|
||||||
return driver.ErrBadConn
|
return driver.ErrBadConn
|
||||||
|
@ -486,22 +497,23 @@ func (mc *mysqlConn) readResultOK() ([]byte, error) {
|
||||||
plugin := string(data[1:pluginEndIndex])
|
plugin := string(data[1:pluginEndIndex])
|
||||||
cipher := data[pluginEndIndex+1 : len(data)-1]
|
cipher := data[pluginEndIndex+1 : len(data)-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
|
// https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::OldAuthSwitchRequest
|
||||||
return nil, ErrOldPassword
|
return nil, ErrOldPassword
|
||||||
}
|
|
||||||
|
|
||||||
default: // Error otherwise
|
default: // Error otherwise
|
||||||
return nil, mc.handleErrorPacket(data)
|
return nil, mc.handleErrorPacket(data)
|
||||||
|
@ -550,6 +562,21 @@ 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
|
||||||
|
if errno == 1792 && 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]
|
||||||
|
@ -584,8 +611,8 @@ func (mc *mysqlConn) handleOkPacket(data []byte) error {
|
||||||
|
|
||||||
// server_status [2 bytes]
|
// server_status [2 bytes]
|
||||||
mc.status = readStatus(data[1+n+m : 1+n+m+2])
|
mc.status = readStatus(data[1+n+m : 1+n+m+2])
|
||||||
if err := mc.discardResults(); err != nil {
|
if mc.status&statusMoreResultsExists != 0 {
|
||||||
return err
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// warning count [2 bytes]
|
// warning count [2 bytes]
|
||||||
|
@ -698,6 +725,10 @@ func (mc *mysqlConn) readColumns(count int) ([]mysqlField, error) {
|
||||||
func (rows *textRows) readRow(dest []driver.Value) error {
|
func (rows *textRows) readRow(dest []driver.Value) error {
|
||||||
mc := rows.mc
|
mc := rows.mc
|
||||||
|
|
||||||
|
if rows.rs.done {
|
||||||
|
return io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
data, err := mc.readPacket()
|
data, err := mc.readPacket()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -707,15 +738,11 @@ func (rows *textRows) readRow(dest []driver.Value) error {
|
||||||
if data[0] == iEOF && len(data) == 5 {
|
if data[0] == iEOF && len(data) == 5 {
|
||||||
// server_status [2 bytes]
|
// server_status [2 bytes]
|
||||||
rows.mc.status = readStatus(data[3:])
|
rows.mc.status = readStatus(data[3:])
|
||||||
err = rows.mc.discardResults()
|
rows.rs.done = true
|
||||||
if err == nil {
|
if !rows.HasNextResultSet() {
|
||||||
err = io.EOF
|
|
||||||
} else {
|
|
||||||
// connection unusable
|
|
||||||
rows.mc.Close()
|
|
||||||
}
|
|
||||||
rows.mc = nil
|
rows.mc = nil
|
||||||
return err
|
}
|
||||||
|
return io.EOF
|
||||||
}
|
}
|
||||||
if data[0] == iERR {
|
if data[0] == iERR {
|
||||||
rows.mc = nil
|
rows.mc = nil
|
||||||
|
@ -736,7 +763,7 @@ func (rows *textRows) readRow(dest []driver.Value) error {
|
||||||
if !mc.parseTime {
|
if !mc.parseTime {
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
switch rows.columns[i].fieldType {
|
switch rows.rs.columns[i].fieldType {
|
||||||
case fieldTypeTimestamp, fieldTypeDateTime,
|
case fieldTypeTimestamp, fieldTypeDateTime,
|
||||||
fieldTypeDate, fieldTypeNewDate:
|
fieldTypeDate, fieldTypeNewDate:
|
||||||
dest[i], err = parseDateTime(
|
dest[i], err = parseDateTime(
|
||||||
|
@ -1051,17 +1078,19 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
|
||||||
paramTypes[i+i] = fieldTypeString
|
paramTypes[i+i] = 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)
|
||||||
|
@ -1097,8 +1126,6 @@ func (mc *mysqlConn) discardResults() error {
|
||||||
if err := mc.readUntilEOF(); err != nil {
|
if err := mc.readUntilEOF(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
mc.status &^= statusMoreResultsExists
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -1116,15 +1143,11 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
|
||||||
// EOF Packet
|
// EOF Packet
|
||||||
if data[0] == iEOF && len(data) == 5 {
|
if data[0] == iEOF && len(data) == 5 {
|
||||||
rows.mc.status = readStatus(data[3:])
|
rows.mc.status = readStatus(data[3:])
|
||||||
err = rows.mc.discardResults()
|
rows.rs.done = true
|
||||||
if err == nil {
|
if !rows.HasNextResultSet() {
|
||||||
err = io.EOF
|
|
||||||
} else {
|
|
||||||
// connection unusable
|
|
||||||
rows.mc.Close()
|
|
||||||
}
|
|
||||||
rows.mc = nil
|
rows.mc = nil
|
||||||
return err
|
}
|
||||||
|
return io.EOF
|
||||||
}
|
}
|
||||||
rows.mc = nil
|
rows.mc = nil
|
||||||
|
|
||||||
|
@ -1145,14 +1168,14 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert to byte-coded string
|
// Convert to byte-coded string
|
||||||
switch rows.columns[i].fieldType {
|
switch rows.rs.columns[i].fieldType {
|
||||||
case fieldTypeNULL:
|
case fieldTypeNULL:
|
||||||
dest[i] = nil
|
dest[i] = nil
|
||||||
continue
|
continue
|
||||||
|
|
||||||
// Numeric Types
|
// Numeric Types
|
||||||
case fieldTypeTiny:
|
case fieldTypeTiny:
|
||||||
if rows.columns[i].flags&flagUnsigned != 0 {
|
if rows.rs.columns[i].flags&flagUnsigned != 0 {
|
||||||
dest[i] = int64(data[pos])
|
dest[i] = int64(data[pos])
|
||||||
} else {
|
} else {
|
||||||
dest[i] = int64(int8(data[pos]))
|
dest[i] = int64(int8(data[pos]))
|
||||||
|
@ -1161,7 +1184,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
|
||||||
continue
|
continue
|
||||||
|
|
||||||
case fieldTypeShort, fieldTypeYear:
|
case fieldTypeShort, fieldTypeYear:
|
||||||
if rows.columns[i].flags&flagUnsigned != 0 {
|
if rows.rs.columns[i].flags&flagUnsigned != 0 {
|
||||||
dest[i] = int64(binary.LittleEndian.Uint16(data[pos : pos+2]))
|
dest[i] = int64(binary.LittleEndian.Uint16(data[pos : pos+2]))
|
||||||
} else {
|
} else {
|
||||||
dest[i] = int64(int16(binary.LittleEndian.Uint16(data[pos : pos+2])))
|
dest[i] = int64(int16(binary.LittleEndian.Uint16(data[pos : pos+2])))
|
||||||
|
@ -1170,7 +1193,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
|
||||||
continue
|
continue
|
||||||
|
|
||||||
case fieldTypeInt24, fieldTypeLong:
|
case fieldTypeInt24, fieldTypeLong:
|
||||||
if rows.columns[i].flags&flagUnsigned != 0 {
|
if rows.rs.columns[i].flags&flagUnsigned != 0 {
|
||||||
dest[i] = int64(binary.LittleEndian.Uint32(data[pos : pos+4]))
|
dest[i] = int64(binary.LittleEndian.Uint32(data[pos : pos+4]))
|
||||||
} else {
|
} else {
|
||||||
dest[i] = int64(int32(binary.LittleEndian.Uint32(data[pos : pos+4])))
|
dest[i] = int64(int32(binary.LittleEndian.Uint32(data[pos : pos+4])))
|
||||||
|
@ -1179,7 +1202,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
|
||||||
continue
|
continue
|
||||||
|
|
||||||
case fieldTypeLongLong:
|
case fieldTypeLongLong:
|
||||||
if rows.columns[i].flags&flagUnsigned != 0 {
|
if rows.rs.columns[i].flags&flagUnsigned != 0 {
|
||||||
val := binary.LittleEndian.Uint64(data[pos : pos+8])
|
val := binary.LittleEndian.Uint64(data[pos : pos+8])
|
||||||
if val > math.MaxInt64 {
|
if val > math.MaxInt64 {
|
||||||
dest[i] = uint64ToString(val)
|
dest[i] = uint64ToString(val)
|
||||||
|
@ -1193,7 +1216,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
|
||||||
|
|
||||||
|
@ -1233,10 +1256,10 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
|
||||||
case isNull:
|
case isNull:
|
||||||
dest[i] = nil
|
dest[i] = nil
|
||||||
continue
|
continue
|
||||||
case rows.columns[i].fieldType == fieldTypeTime:
|
case rows.rs.columns[i].fieldType == fieldTypeTime:
|
||||||
// database/sql does not support an equivalent to TIME, return a string
|
// database/sql does not support an equivalent to TIME, return a string
|
||||||
var dstlen uint8
|
var dstlen uint8
|
||||||
switch decimals := rows.columns[i].decimals; decimals {
|
switch decimals := rows.rs.columns[i].decimals; decimals {
|
||||||
case 0x00, 0x1f:
|
case 0x00, 0x1f:
|
||||||
dstlen = 8
|
dstlen = 8
|
||||||
case 1, 2, 3, 4, 5, 6:
|
case 1, 2, 3, 4, 5, 6:
|
||||||
|
@ -1244,7 +1267,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"protocol error, illegal decimals value %d",
|
"protocol error, illegal decimals value %d",
|
||||||
rows.columns[i].decimals,
|
rows.rs.columns[i].decimals,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
dest[i], err = formatBinaryDateTime(data[pos:pos+int(num)], dstlen, true)
|
dest[i], err = formatBinaryDateTime(data[pos:pos+int(num)], dstlen, true)
|
||||||
|
@ -1252,10 +1275,10 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
|
||||||
dest[i], err = parseBinaryDateTime(num, data[pos:], rows.mc.cfg.Loc)
|
dest[i], err = parseBinaryDateTime(num, data[pos:], rows.mc.cfg.Loc)
|
||||||
default:
|
default:
|
||||||
var dstlen uint8
|
var dstlen uint8
|
||||||
if rows.columns[i].fieldType == fieldTypeDate {
|
if rows.rs.columns[i].fieldType == fieldTypeDate {
|
||||||
dstlen = 10
|
dstlen = 10
|
||||||
} else {
|
} else {
|
||||||
switch decimals := rows.columns[i].decimals; decimals {
|
switch decimals := rows.rs.columns[i].decimals; decimals {
|
||||||
case 0x00, 0x1f:
|
case 0x00, 0x1f:
|
||||||
dstlen = 19
|
dstlen = 19
|
||||||
case 1, 2, 3, 4, 5, 6:
|
case 1, 2, 3, 4, 5, 6:
|
||||||
|
@ -1263,7 +1286,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf(
|
return fmt.Errorf(
|
||||||
"protocol error, illegal decimals value %d",
|
"protocol error, illegal decimals value %d",
|
||||||
rows.columns[i].decimals,
|
rows.rs.columns[i].decimals,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1279,7 +1302,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
|
||||||
|
|
||||||
// Please report if this happens!
|
// Please report if this happens!
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unknown field type %d", rows.columns[i].fieldType)
|
return fmt.Errorf("unknown field type %d", rows.rs.columns[i].fieldType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
9
vendor/github.com/go-sql-driver/mysql/packets_test.go
generated
vendored
9
vendor/github.com/go-sql-driver/mysql/packets_test.go
generated
vendored
|
@ -101,7 +101,7 @@ func TestReadPacketSingleByte(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if len(packet) != 1 {
|
if len(packet) != 1 {
|
||||||
t.Fatalf("unexpected packet lenght: expected %d, got %d", 1, len(packet))
|
t.Fatalf("unexpected packet length: expected %d, got %d", 1, len(packet))
|
||||||
}
|
}
|
||||||
if packet[0] != 0xff {
|
if packet[0] != 0xff {
|
||||||
t.Fatalf("unexpected packet content: expected %x, got %x", 0xff, packet[0])
|
t.Fatalf("unexpected packet content: expected %x, got %x", 0xff, packet[0])
|
||||||
|
@ -171,7 +171,7 @@ func TestReadPacketSplit(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if len(packet) != maxPacketSize {
|
if len(packet) != maxPacketSize {
|
||||||
t.Fatalf("unexpected packet lenght: expected %d, got %d", maxPacketSize, len(packet))
|
t.Fatalf("unexpected packet length: expected %d, got %d", maxPacketSize, len(packet))
|
||||||
}
|
}
|
||||||
if packet[0] != 0x11 {
|
if packet[0] != 0x11 {
|
||||||
t.Fatalf("unexpected payload start: expected %x, got %x", 0x11, packet[0])
|
t.Fatalf("unexpected payload start: expected %x, got %x", 0x11, packet[0])
|
||||||
|
@ -205,7 +205,7 @@ func TestReadPacketSplit(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if len(packet) != 2*maxPacketSize {
|
if len(packet) != 2*maxPacketSize {
|
||||||
t.Fatalf("unexpected packet lenght: expected %d, got %d", 2*maxPacketSize, len(packet))
|
t.Fatalf("unexpected packet length: expected %d, got %d", 2*maxPacketSize, len(packet))
|
||||||
}
|
}
|
||||||
if packet[0] != 0x11 {
|
if packet[0] != 0x11 {
|
||||||
t.Fatalf("unexpected payload start: expected %x, got %x", 0x11, packet[0])
|
t.Fatalf("unexpected payload start: expected %x, got %x", 0x11, packet[0])
|
||||||
|
@ -231,7 +231,7 @@ func TestReadPacketSplit(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if len(packet) != maxPacketSize+42 {
|
if len(packet) != maxPacketSize+42 {
|
||||||
t.Fatalf("unexpected packet lenght: expected %d, got %d", maxPacketSize+42, len(packet))
|
t.Fatalf("unexpected packet length: expected %d, got %d", maxPacketSize+42, len(packet))
|
||||||
}
|
}
|
||||||
if packet[0] != 0x11 {
|
if packet[0] != 0x11 {
|
||||||
t.Fatalf("unexpected payload start: expected %x, got %x", 0x11, packet[0])
|
t.Fatalf("unexpected payload start: expected %x, got %x", 0x11, packet[0])
|
||||||
|
@ -245,6 +245,7 @@ func TestReadPacketFail(t *testing.T) {
|
||||||
conn := new(mockConn)
|
conn := new(mockConn)
|
||||||
mc := &mysqlConn{
|
mc := &mysqlConn{
|
||||||
buf: newBuffer(conn),
|
buf: newBuffer(conn),
|
||||||
|
closech: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
// illegal empty (stand-alone) packet
|
// illegal empty (stand-alone) packet
|
||||||
|
|
128
vendor/github.com/go-sql-driver/mysql/rows.go
generated
vendored
128
vendor/github.com/go-sql-driver/mysql/rows.go
generated
vendored
|
@ -21,9 +21,16 @@ type mysqlField struct {
|
||||||
decimals byte
|
decimals byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type resultSet struct {
|
||||||
|
columns []mysqlField
|
||||||
|
columnNames []string
|
||||||
|
done bool
|
||||||
|
}
|
||||||
|
|
||||||
type mysqlRows struct {
|
type mysqlRows struct {
|
||||||
mc *mysqlConn
|
mc *mysqlConn
|
||||||
columns []mysqlField
|
rs resultSet
|
||||||
|
finish func()
|
||||||
}
|
}
|
||||||
|
|
||||||
type binaryRows struct {
|
type binaryRows struct {
|
||||||
|
@ -34,37 +41,48 @@ type textRows struct {
|
||||||
mysqlRows
|
mysqlRows
|
||||||
}
|
}
|
||||||
|
|
||||||
type emptyRows struct{}
|
|
||||||
|
|
||||||
func (rows *mysqlRows) Columns() []string {
|
func (rows *mysqlRows) Columns() []string {
|
||||||
columns := make([]string, len(rows.columns))
|
if rows.rs.columnNames != nil {
|
||||||
|
return rows.rs.columnNames
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
if tableName := rows.columns[i].tableName; len(tableName) > 0 {
|
if tableName := rows.rs.columns[i].tableName; len(tableName) > 0 {
|
||||||
columns[i] = tableName + "." + rows.columns[i].name
|
columns[i] = tableName + "." + rows.rs.columns[i].name
|
||||||
} else {
|
} else {
|
||||||
columns[i] = rows.columns[i].name
|
columns[i] = rows.rs.columns[i].name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for i := range columns {
|
for i := range columns {
|
||||||
columns[i] = rows.columns[i].name
|
columns[i] = rows.rs.columns[i].name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rows.rs.columnNames = columns
|
||||||
return columns
|
return columns
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rows *mysqlRows) Close() 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 {
|
if err := mc.error(); err != nil {
|
||||||
return ErrInvalidConn
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove unread packets from stream
|
// Remove unread packets from stream
|
||||||
err := mc.readUntilEOF()
|
if !rows.rs.done {
|
||||||
|
err = mc.readUntilEOF()
|
||||||
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if err = mc.discardResults(); err != nil {
|
if err = mc.discardResults(); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -75,10 +93,66 @@ func (rows *mysqlRows) Close() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rows *mysqlRows) HasNextResultSet() (b bool) {
|
||||||
|
if rows.mc == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return rows.mc.status&statusMoreResultsExists != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rows *mysqlRows) nextResultSet() (int, error) {
|
||||||
|
if rows.mc == nil {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
if err := rows.mc.error(); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove unread packets from stream
|
||||||
|
if !rows.rs.done {
|
||||||
|
if err := rows.mc.readUntilEOF(); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
rows.rs.done = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !rows.HasNextResultSet() {
|
||||||
|
rows.mc = nil
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
rows.rs = resultSet{}
|
||||||
|
return rows.mc.readResultSetHeaderPacket()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rows *mysqlRows) nextNotEmptyResultSet() (int, error) {
|
||||||
|
for {
|
||||||
|
resLen, err := rows.nextResultSet()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resLen > 0 {
|
||||||
|
return resLen, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rows.rs.done = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rows *binaryRows) NextResultSet() error {
|
||||||
|
resLen, err := rows.nextNotEmptyResultSet()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
if err := mc.error(); err != nil {
|
||||||
return ErrInvalidConn
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch next row from stream
|
// Fetch next row from stream
|
||||||
|
@ -87,10 +161,20 @@ func (rows *binaryRows) Next(dest []driver.Value) error {
|
||||||
return io.EOF
|
return io.EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (rows *textRows) NextResultSet() (err error) {
|
||||||
|
resLen, err := rows.nextNotEmptyResultSet()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
rows.rs.columns, err = rows.mc.readColumns(resLen)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
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 {
|
if err := mc.error(); err != nil {
|
||||||
return ErrInvalidConn
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch next row from stream
|
// Fetch next row from stream
|
||||||
|
@ -98,15 +182,3 @@ func (rows *textRows) Next(dest []driver.Value) error {
|
||||||
}
|
}
|
||||||
return io.EOF
|
return io.EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rows emptyRows) Columns() []string {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rows emptyRows) Close() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rows emptyRows) Next(dest []driver.Value) error {
|
|
||||||
return io.EOF
|
|
||||||
}
|
|
||||||
|
|
53
vendor/github.com/go-sql-driver/mysql/statement.go
generated
vendored
53
vendor/github.com/go-sql-driver/mysql/statement.go
generated
vendored
|
@ -11,6 +11,7 @@ package mysql
|
||||||
import (
|
import (
|
||||||
"database/sql/driver"
|
"database/sql/driver"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
@ -19,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.
|
||||||
|
@ -45,7 +45,7 @@ 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
|
||||||
}
|
}
|
||||||
|
@ -62,30 +62,38 @@ func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) {
|
||||||
|
|
||||||
// Read Result
|
// Read Result
|
||||||
resLen, err := mc.readResultSetHeaderPacket()
|
resLen, err := mc.readResultSetHeaderPacket()
|
||||||
if err == nil {
|
|
||||||
if resLen > 0 {
|
|
||||||
// Columns
|
|
||||||
err = mc.readUntilEOF()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rows
|
if resLen > 0 {
|
||||||
err = mc.readUntilEOF()
|
// Columns
|
||||||
|
if err = mc.readUntilEOF(); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
if err == nil {
|
|
||||||
|
// Rows
|
||||||
|
if err := mc.readUntilEOF(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := mc.discardResults(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return &mysqlResult{
|
return &mysqlResult{
|
||||||
affectedRows: int64(mc.affectedRows),
|
affectedRows: int64(mc.affectedRows),
|
||||||
insertId: int64(mc.insertId),
|
insertId: int64(mc.insertId),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
@ -107,14 +115,15 @@ func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) {
|
||||||
|
|
||||||
if resLen > 0 {
|
if resLen > 0 {
|
||||||
rows.mc = mc
|
rows.mc = mc
|
||||||
// Columns
|
rows.rs.columns, err = mc.readColumns(resLen)
|
||||||
// If not cached, read them and cache them
|
|
||||||
if stmt.columns == nil {
|
|
||||||
rows.columns, err = mc.readColumns(resLen)
|
|
||||||
stmt.columns = rows.columns
|
|
||||||
} else {
|
} else {
|
||||||
rows.columns = stmt.columns
|
rows.rs.done = true
|
||||||
err = mc.readUntilEOF()
|
|
||||||
|
switch err := rows.NextResultSet(); err {
|
||||||
|
case nil, io.EOF:
|
||||||
|
return rows, nil
|
||||||
|
default:
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
4
vendor/github.com/go-sql-driver/mysql/transaction.go
generated
vendored
4
vendor/github.com/go-sql-driver/mysql/transaction.go
generated
vendored
|
@ -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")
|
||||||
|
|
82
vendor/github.com/go-sql-driver/mysql/utils.go
generated
vendored
82
vendor/github.com/go-sql-driver/mysql/utils.go
generated
vendored
|
@ -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.
|
||||||
|
@ -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
vendor/github.com/go-sql-driver/mysql/utils_go17.go
generated
vendored
Normal file
40
vendor/github.com/go-sql-driver/mysql/utils_go17.go
generated
vendored
Normal file
|
@ -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
vendor/github.com/go-sql-driver/mysql/utils_go18.go
generated
vendored
Normal file
49
vendor/github.com/go-sql-driver/mysql/utils_go18.go
generated
vendored
Normal file
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
54
vendor/github.com/go-sql-driver/mysql/utils_go18_test.go
generated
vendored
Normal file
54
vendor/github.com/go-sql-driver/mysql/utils_go18_test.go
generated
vendored
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||||
|
//
|
||||||
|
// Copyright 2017 The Go-MySQL-Driver Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||||
|
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
// +build go1.8
|
||||||
|
|
||||||
|
package mysql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"database/sql/driver"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIsolationLevelMapping(t *testing.T) {
|
||||||
|
|
||||||
|
data := []struct {
|
||||||
|
level driver.IsolationLevel
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
level: driver.IsolationLevel(sql.LevelReadCommitted),
|
||||||
|
expected: "READ COMMITTED",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
level: driver.IsolationLevel(sql.LevelRepeatableRead),
|
||||||
|
expected: "REPEATABLE READ",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
level: driver.IsolationLevel(sql.LevelReadUncommitted),
|
||||||
|
expected: "READ UNCOMMITTED",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
level: driver.IsolationLevel(sql.LevelSerializable),
|
||||||
|
expected: "SERIALIZABLE",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, td := range data {
|
||||||
|
if actual, err := mapIsolationLevel(td.level); actual != td.expected || err != nil {
|
||||||
|
t.Fatal(i, td.expected, actual, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check unsupported mapping
|
||||||
|
if actual, err := mapIsolationLevel(driver.IsolationLevel(sql.LevelLinearizable)); actual != "" || err == nil {
|
||||||
|
t.Fatal("Expected error on unsupported isolation level")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
18
vendor/github.com/go-sql-driver/mysql/utils_legacy.go
generated
vendored
Normal file
18
vendor/github.com/go-sql-driver/mysql/utils_legacy.go
generated
vendored
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
// 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
|
||||||
|
|
||||||
|
package mysql
|
||||||
|
|
||||||
|
import "crypto/tls"
|
||||||
|
|
||||||
|
func cloneTLSConfig(c *tls.Config) *tls.Config {
|
||||||
|
clone := *c
|
||||||
|
return &clone
|
||||||
|
}
|
80
vendor/github.com/go-sql-driver/mysql/utils_test.go
generated
vendored
80
vendor/github.com/go-sql-driver/mysql/utils_test.go
generated
vendored
|
@ -195,3 +195,83 @@ func TestEscapeQuotes(t *testing.T) {
|
||||||
expect("foo''bar", "foo'bar") // affected
|
expect("foo''bar", "foo'bar") // affected
|
||||||
expect("foo\"bar", "foo\"bar") // not affected
|
expect("foo\"bar", "foo\"bar") // not affected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAtomicBool(t *testing.T) {
|
||||||
|
var ab atomicBool
|
||||||
|
if ab.IsSet() {
|
||||||
|
t.Fatal("Expected value to be false")
|
||||||
|
}
|
||||||
|
|
||||||
|
ab.Set(true)
|
||||||
|
if ab.value != 1 {
|
||||||
|
t.Fatal("Set(true) did not set value to 1")
|
||||||
|
}
|
||||||
|
if !ab.IsSet() {
|
||||||
|
t.Fatal("Expected value to be true")
|
||||||
|
}
|
||||||
|
|
||||||
|
ab.Set(true)
|
||||||
|
if !ab.IsSet() {
|
||||||
|
t.Fatal("Expected value to be true")
|
||||||
|
}
|
||||||
|
|
||||||
|
ab.Set(false)
|
||||||
|
if ab.value != 0 {
|
||||||
|
t.Fatal("Set(false) did not set value to 0")
|
||||||
|
}
|
||||||
|
if ab.IsSet() {
|
||||||
|
t.Fatal("Expected value to be false")
|
||||||
|
}
|
||||||
|
|
||||||
|
ab.Set(false)
|
||||||
|
if ab.IsSet() {
|
||||||
|
t.Fatal("Expected value to be false")
|
||||||
|
}
|
||||||
|
if ab.TrySet(false) {
|
||||||
|
t.Fatal("Expected TrySet(false) to fail")
|
||||||
|
}
|
||||||
|
if !ab.TrySet(true) {
|
||||||
|
t.Fatal("Expected TrySet(true) to succeed")
|
||||||
|
}
|
||||||
|
if !ab.IsSet() {
|
||||||
|
t.Fatal("Expected value to be true")
|
||||||
|
}
|
||||||
|
|
||||||
|
ab.Set(true)
|
||||||
|
if !ab.IsSet() {
|
||||||
|
t.Fatal("Expected value to be true")
|
||||||
|
}
|
||||||
|
if ab.TrySet(true) {
|
||||||
|
t.Fatal("Expected TrySet(true) to fail")
|
||||||
|
}
|
||||||
|
if !ab.TrySet(false) {
|
||||||
|
t.Fatal("Expected TrySet(false) to succeed")
|
||||||
|
}
|
||||||
|
if ab.IsSet() {
|
||||||
|
t.Fatal("Expected value to be false")
|
||||||
|
}
|
||||||
|
|
||||||
|
ab._noCopy.Lock() // we've "tested" it ¯\_(ツ)_/¯
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAtomicError(t *testing.T) {
|
||||||
|
var ae atomicError
|
||||||
|
if ae.Value() != nil {
|
||||||
|
t.Fatal("Expected value to be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
ae.Set(ErrMalformPkt)
|
||||||
|
if v := ae.Value(); v != ErrMalformPkt {
|
||||||
|
if v == nil {
|
||||||
|
t.Fatal("Value is still nil")
|
||||||
|
}
|
||||||
|
t.Fatal("Error did not match")
|
||||||
|
}
|
||||||
|
ae.Set(ErrPktSync)
|
||||||
|
if ae.Value() == ErrMalformPkt {
|
||||||
|
t.Fatal("Error still matches old error")
|
||||||
|
}
|
||||||
|
if v := ae.Value(); v != ErrPktSync {
|
||||||
|
t.Fatal("Error did not match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue