mirror of
https://github.com/documize/community.git
synced 2025-07-18 20:59:43 +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
|
||||
|
||||
Aaron Hopkins <go-sql-driver at die.net>
|
||||
Achille Roussel <achille.roussel at gmail.com>
|
||||
Arne Hormann <arnehormann at gmail.com>
|
||||
Asta Xie <xiemengjun at gmail.com>
|
||||
Bulat Gaifullin <gaifullinbf at gmail.com>
|
||||
Carlos Nieto <jose.carlos at menteslibres.net>
|
||||
Chris Moos <chris at tech9computers.com>
|
||||
Daniel Nichter <nil at codenode.com>
|
||||
Daniël van Eeden <git at myname.nl>
|
||||
Dave Protasowski <dprotaso at gmail.com>
|
||||
DisposaBoy <disposaboy at dby.me>
|
||||
Egor Smolyakov <egorsmkv at gmail.com>
|
||||
Evan Shaw <evan at vendhq.com>
|
||||
Frederick Mayle <frederickmayle at gmail.com>
|
||||
Gustavo Kristic <gkristic at gmail.com>
|
||||
Hanno Braun <mail at hannobraun.com>
|
||||
Henri Yandell <flamefew at gmail.com>
|
||||
Hirotaka Yamamoto <ymmt2005 at gmail.com>
|
||||
ICHINOSE Shogo <shogo82148 at gmail.com>
|
||||
INADA Naoki <songofacandy at gmail.com>
|
||||
Jacek Szwec <szwec.jacek at gmail.com>
|
||||
James Harr <james.harr at gmail.com>
|
||||
Jeff Hodges <jeff at somethingsimilar.com>
|
||||
Jian Zhen <zhenjl at gmail.com>
|
||||
Joshua Prunier <joshua.prunier at gmail.com>
|
||||
Julien Lefevre <julien.lefevr at gmail.com>
|
||||
Julien Schmidt <go-sql-driver at julienschmidt.com>
|
||||
Justin Nuß <nuss.justin at gmail.com>
|
||||
Kamil Dziedzic <kamil at klecza.pl>
|
||||
Kevin Malachowski <kevin at chowski.com>
|
||||
Lennart Rudolph <lrudolph at hmc.edu>
|
||||
Leonardo YongUk Kim <dalinaum at gmail.com>
|
||||
Lion Yang <lion at aosc.xyz>
|
||||
Luca Looz <luca.looz92 at gmail.com>
|
||||
Lucas Liu <extrafliu at gmail.com>
|
||||
Luke Scott <luke at webconnex.com>
|
||||
Maciej Zimnoch <maciej.zimnoch@codilime.com>
|
||||
Michael Woolnough <michael.woolnough at gmail.com>
|
||||
Nicola Peduzzi <thenikso at gmail.com>
|
||||
Olivier Mengué <dolmen at cpan.org>
|
||||
oscarzhao <oscarzhaosl 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>
|
||||
Soroush Pour <me at soroushjp.com>
|
||||
Stan Putrya <root.vagner at gmail.com>
|
||||
|
@ -53,4 +68,6 @@ Zhenye Xie <xiezhenye at gmail.com>
|
|||
|
||||
Barracuda Networks, Inc.
|
||||
Google Inc.
|
||||
Keybase Inc.
|
||||
Pivotal 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
|
||||
|
||||
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)
|
||||
* [Parameters](#parameters)
|
||||
* [Examples](#examples)
|
||||
* [Connection pool and timeouts](#connection-pool-and-timeouts)
|
||||
* [LOAD DATA LOCAL INFILE support](#load-data-local-infile-support)
|
||||
* [time.Time support](#timetime-support)
|
||||
* [Unicode support](#unicode-support)
|
||||
* [context.Context Support](#contextcontext-support)
|
||||
* [Testing / Development](#testing--development)
|
||||
* [License](#license)
|
||||
|
||||
|
@ -26,31 +28,31 @@ A MySQL-Driver for Go's [database/sql](http://golang.org/pkg/database/sql) packa
|
|||
## Features
|
||||
* Lightweight and [fast](https://github.com/go-sql-driver/sql-benchmark "golang MySQL-Driver performance")
|
||||
* 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 Connection Pooling *(by database/sql package)*
|
||||
* 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
|
||||
* Secure `LOAD DATA LOCAL INFILE` support with file Whitelisting and `io.Reader` support
|
||||
* Optional `time.Time` parsing
|
||||
* Optional placeholder interpolation
|
||||
|
||||
## Requirements
|
||||
* Go 1.2 or higher
|
||||
* Go 1.5 or higher
|
||||
* MySQL (4.1+), MariaDB, Percona Server, Google CloudSQL or Sphinx (2.2.3+)
|
||||
|
||||
---------------------------------------
|
||||
|
||||
## 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
|
||||
$ 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
|
||||
_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`:
|
||||
```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.
|
||||
|
||||
#### 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.
|
||||
|
||||
#### Address
|
||||
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.
|
||||
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`.
|
||||
|
||||
|
@ -220,11 +222,11 @@ Valid Values: <escaped name>
|
|||
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.
|
||||
|
||||
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`
|
||||
```
|
||||
|
@ -260,11 +262,36 @@ Default: false
|
|||
##### `readTimeout`
|
||||
|
||||
```
|
||||
Type: decimal number
|
||||
Type: duration
|
||||
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`
|
||||
|
||||
|
@ -283,11 +310,11 @@ By default MySQL also treats notes as warnings. Use [`sql_notes=false`](http://d
|
|||
##### `timeout`
|
||||
|
||||
```
|
||||
Type: decimal number
|
||||
Type: duration
|
||||
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`
|
||||
|
||||
|
@ -297,16 +324,16 @@ Valid Values: true, false, skip-verify, <name>
|
|||
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`
|
||||
|
||||
```
|
||||
Type: decimal number
|
||||
Type: duration
|
||||
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
|
||||
|
@ -380,6 +407,11 @@ No Database preselected:
|
|||
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
|
||||
For this feature you need direct access to the package. Therefore you must change the import path (no `_`):
|
||||
```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.
|
||||
|
||||
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
|
||||
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).
|
||||
|
||||
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
|
||||
|
@ -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.
|
||||
|
||||
## `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
|
||||
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)
|
||||
* 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)
|
||||
|
||||
|
|
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)
|
||||
})
|
||||
}
|
||||
}
|
115
vendor/github.com/go-sql-driver/mysql/connection.go
generated
vendored
115
vendor/github.com/go-sql-driver/mysql/connection.go
generated
vendored
|
@ -10,12 +10,23 @@ package mysql
|
|||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"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 {
|
||||
buf buffer
|
||||
netConn net.Conn
|
||||
|
@ -30,6 +41,14 @@ type mysqlConn struct {
|
|||
sequence uint8
|
||||
parseTime bool
|
||||
strict bool
|
||||
|
||||
// for context support (Go 1.8+)
|
||||
watching bool
|
||||
watcher chan<- mysqlContext
|
||||
closech chan struct{}
|
||||
finished chan<- struct{}
|
||||
canceled atomicError // set non-nil if conn is canceled
|
||||
closed atomicBool // set when conn is closed, before closech is closed
|
||||
}
|
||||
|
||||
// Handles parameters set in DSN after the connection is established
|
||||
|
@ -63,7 +82,7 @@ func (mc *mysqlConn) handleParams() (err error) {
|
|||
}
|
||||
|
||||
func (mc *mysqlConn) Begin() (driver.Tx, error) {
|
||||
if mc.netConn == nil {
|
||||
if mc.closed.IsSet() {
|
||||
errLog.Print(ErrInvalidConn)
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
|
@ -77,7 +96,7 @@ func (mc *mysqlConn) Begin() (driver.Tx, error) {
|
|||
|
||||
func (mc *mysqlConn) Close() (err error) {
|
||||
// Makes Close idempotent
|
||||
if mc.netConn != nil {
|
||||
if !mc.closed.IsSet() {
|
||||
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
|
||||
// closed the network connection.
|
||||
func (mc *mysqlConn) cleanup() {
|
||||
// Makes cleanup idempotent
|
||||
if mc.netConn != nil {
|
||||
if err := mc.netConn.Close(); err != nil {
|
||||
errLog.Print(err)
|
||||
}
|
||||
mc.netConn = nil
|
||||
if !mc.closed.TrySet(true) {
|
||||
return
|
||||
}
|
||||
mc.cfg = nil
|
||||
mc.buf.nc = nil
|
||||
|
||||
// Makes cleanup idempotent
|
||||
close(mc.closech)
|
||||
if mc.netConn == nil {
|
||||
return
|
||||
}
|
||||
if err := mc.netConn.Close(); err != nil {
|
||||
errLog.Print(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) error() error {
|
||||
if mc.closed.IsSet() {
|
||||
if err := mc.canceled.Value(); err != nil {
|
||||
return err
|
||||
}
|
||||
return ErrInvalidConn
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
|
||||
if mc.netConn == nil {
|
||||
if mc.closed.IsSet() {
|
||||
errLog.Print(ErrInvalidConn)
|
||||
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) {
|
||||
if mc.netConn == nil {
|
||||
if mc.closed.IsSet() {
|
||||
errLog.Print(ErrInvalidConn)
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
|
@ -271,7 +303,6 @@ func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, err
|
|||
return nil, err
|
||||
}
|
||||
query = prepared
|
||||
args = nil
|
||||
}
|
||||
mc.affectedRows = 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
|
||||
func (mc *mysqlConn) exec(query string) error {
|
||||
// Send command
|
||||
err := mc.writeCommandPacketStr(comQuery, query)
|
||||
if err != nil {
|
||||
if err := mc.writeCommandPacketStr(comQuery, query); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Read Result
|
||||
resLen, err := mc.readResultSetHeaderPacket()
|
||||
if err == nil && resLen > 0 {
|
||||
if err = mc.readUntilEOF(); err != nil {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if resLen > 0 {
|
||||
// columns
|
||||
if err := mc.readUntilEOF(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = mc.readUntilEOF()
|
||||
// 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) {
|
||||
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)
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
|
@ -322,7 +364,6 @@ func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, erro
|
|||
return nil, err
|
||||
}
|
||||
query = prepared
|
||||
args = nil
|
||||
}
|
||||
// Send command
|
||||
err := mc.writeCommandPacketStr(comQuery, query)
|
||||
|
@ -335,11 +376,17 @@ func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, erro
|
|||
rows.mc = mc
|
||||
|
||||
if resLen == 0 {
|
||||
// no columns, no more data
|
||||
return emptyRows{}, nil
|
||||
rows.rs.done = true
|
||||
|
||||
switch err := rows.NextResultSet(); err {
|
||||
case nil, io.EOF:
|
||||
return rows, nil
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// Columns
|
||||
rows.columns, err = mc.readColumns(resLen)
|
||||
rows.rs.columns, err = mc.readColumns(resLen)
|
||||
return rows, err
|
||||
}
|
||||
}
|
||||
|
@ -359,7 +406,7 @@ func (mc *mysqlConn) getSystemVar(name string) ([]byte, error) {
|
|||
if err == nil {
|
||||
rows := new(textRows)
|
||||
rows.mc = mc
|
||||
rows.columns = []mysqlField{{fieldType: fieldTypeVarChar}}
|
||||
rows.rs.columns = []mysqlField{{fieldType: fieldTypeVarChar}}
|
||||
|
||||
if resLen > 0 {
|
||||
// Columns
|
||||
|
@ -375,3 +422,21 @@ func (mc *mysqlConn) getSystemVar(name string) ([]byte, error) {
|
|||
}
|
||||
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,
|
||||
// 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:
|
||||
//
|
||||
|
@ -22,6 +22,11 @@ import (
|
|||
"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.
|
||||
// In general the driver is used via the database/sql package.
|
||||
type MySQLDriver struct{}
|
||||
|
@ -52,6 +57,7 @@ func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
|
|||
mc := &mysqlConn{
|
||||
maxAllowedPacket: maxPacketSize,
|
||||
maxWriteSize: maxPacketSize - 1,
|
||||
closech: make(chan struct{}),
|
||||
}
|
||||
mc.cfg, err = ParseDSN(dsn)
|
||||
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)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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) {
|
||||
runTests(t, dsn, func(dbt *DBTest) {
|
||||
// just a comment, no query
|
||||
|
@ -684,7 +695,7 @@ func TestDateTime(t *testing.T) {
|
|||
for _, setup := range setups.tests {
|
||||
allowBinTime := true
|
||||
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)
|
||||
} else if setup.s[0] == '!' {
|
||||
// skip tests using setup.t as source in queries
|
||||
|
@ -856,14 +867,14 @@ func TestNULL(t *testing.T) {
|
|||
dbt.Fatal(err)
|
||||
}
|
||||
if b != nil {
|
||||
dbt.Error("non-nil []byte wich should be nil")
|
||||
dbt.Error("non-nil []byte which should be nil")
|
||||
}
|
||||
// Read non-nil
|
||||
if err = nonNullStmt.QueryRow().Scan(&b); err != nil {
|
||||
dbt.Fatal(err)
|
||||
}
|
||||
if b == nil {
|
||||
dbt.Error("nil []byte wich should be non-nil")
|
||||
dbt.Error("nil []byte which should be non-nil")
|
||||
}
|
||||
// Insert nil
|
||||
b = nil
|
||||
|
@ -1054,22 +1065,36 @@ func TestLoadData(t *testing.T) {
|
|||
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.mustExec("CREATE TABLE test (id INT NOT NULL PRIMARY KEY, value TEXT NOT NULL) CHARACTER SET utf8")
|
||||
|
||||
// Local File
|
||||
file, err := ioutil.TempFile("", "gotest")
|
||||
defer os.Remove(file.Name())
|
||||
if err != nil {
|
||||
dbt.Fatal(err)
|
||||
}
|
||||
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()))
|
||||
verifyLoadDataResult()
|
||||
// negative test
|
||||
|
||||
// Try with non-existing file
|
||||
_, err = dbt.db.Exec("LOAD DATA LOCAL INFILE 'doesnotexist' INTO TABLE test")
|
||||
if err == nil {
|
||||
dbt.Fatal("load non-existent file didn't fail")
|
||||
|
@ -1154,11 +1179,9 @@ func TestStrict(t *testing.T) {
|
|||
if conn != nil {
|
||||
conn.Close()
|
||||
}
|
||||
if me, ok := err.(*MySQLError); ok && me.Number == 1231 {
|
||||
// Error 1231: Variable 'sql_mode' can't be set to the value of 'ALLOW_INVALID_DATES'
|
||||
// => skip test, MySQL server version is too old
|
||||
return
|
||||
}
|
||||
// Error 1231: Variable 'sql_mode' can't be set to the value of
|
||||
// 'ALLOW_INVALID_DATES' => skip test, MySQL server version is too old
|
||||
maybeSkip(t, err, 1231)
|
||||
runTests(t, relaxedDsn, func(dbt *DBTest) {
|
||||
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"
|
||||
"net"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -53,6 +54,7 @@ type Config struct {
|
|||
InterpolateParams bool // Interpolate placeholders into query string
|
||||
MultiStatements bool // Allow multiple statements in one query
|
||||
ParseTime bool // Parse time values to time.Time
|
||||
RejectReadOnly bool // Reject read-only connections
|
||||
Strict bool // Return warnings as errors
|
||||
}
|
||||
|
||||
|
@ -195,6 +197,15 @@ func (cfg *Config) FormatDSN() 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 hasParam {
|
||||
buf.WriteString("&strict=true")
|
||||
|
@ -247,7 +258,12 @@ func (cfg *Config) FormatDSN() string {
|
|||
|
||||
// other params
|
||||
if cfg.Params != nil {
|
||||
for param, value := range cfg.Params {
|
||||
var params []string
|
||||
for param := range cfg.Params {
|
||||
params = append(params, param)
|
||||
}
|
||||
sort.Strings(params)
|
||||
for _, param := range params {
|
||||
if hasParam {
|
||||
buf.WriteByte('&')
|
||||
} else {
|
||||
|
@ -257,7 +273,7 @@ func (cfg *Config) FormatDSN() string {
|
|||
|
||||
buf.WriteString(param)
|
||||
buf.WriteByte('=')
|
||||
buf.WriteString(url.QueryEscape(value))
|
||||
buf.WriteString(url.QueryEscape(cfg.Params[param]))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -472,6 +488,14 @@ func parseDSNParams(cfg *Config, params string) (err error) {
|
|||
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
|
||||
case "strict":
|
||||
var isBool bool
|
||||
|
@ -494,6 +518,10 @@ func parseDSNParams(cfg *Config, params string) (err error) {
|
|||
if boolValue {
|
||||
cfg.TLSConfig = "true"
|
||||
cfg.tls = &tls.Config{}
|
||||
host, _, err := net.SplitHostPort(cfg.Addr)
|
||||
if err == nil {
|
||||
cfg.tls.ServerName = host
|
||||
}
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
|
||||
if tlsConfig, ok := tlsConfigRegister[name]; ok {
|
||||
if tlsConfig := getTLSConfigClone(name); tlsConfig != nil {
|
||||
if len(tlsConfig.ServerName) == 0 && !tlsConfig.InsecureSkipVerify {
|
||||
host, _, err := net.SplitHostPort(cfg.Addr)
|
||||
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())
|
||||
} else if cfg.tls.ServerName != name {
|
||||
t.Errorf("did not get the correct ServerName (%s) parsing DSN (%s).", name, tst)
|
||||
} else if tlsCfg.ServerName != "" {
|
||||
t.Errorf("tlsCfg was mutated ServerName (%s) should be empty parsing DSN (%s).", name, tst)
|
||||
}
|
||||
|
||||
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) {
|
||||
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
|
||||
if err == nil {
|
||||
// if packetSize == 0, the Reader contains no data
|
||||
if err == nil && packetSize > 0 {
|
||||
data := make([]byte, 4+packetSize)
|
||||
var n int
|
||||
for err == nil {
|
||||
|
|
115
vendor/github.com/go-sql-driver/mysql/packets.go
generated
vendored
115
vendor/github.com/go-sql-driver/mysql/packets.go
generated
vendored
|
@ -30,6 +30,9 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
|
|||
// read packet header
|
||||
data, err := mc.buf.readNext(4)
|
||||
if err != nil {
|
||||
if cerr := mc.canceled.Value(); cerr != nil {
|
||||
return nil, cerr
|
||||
}
|
||||
errLog.Print(err)
|
||||
mc.Close()
|
||||
return nil, driver.ErrBadConn
|
||||
|
@ -63,6 +66,9 @@ func (mc *mysqlConn) readPacket() ([]byte, error) {
|
|||
// read packet body [pktLen bytes]
|
||||
data, err = mc.buf.readNext(pktLen)
|
||||
if err != nil {
|
||||
if cerr := mc.canceled.Value(); cerr != nil {
|
||||
return nil, cerr
|
||||
}
|
||||
errLog.Print(err)
|
||||
mc.Close()
|
||||
return nil, driver.ErrBadConn
|
||||
|
@ -125,8 +131,13 @@ func (mc *mysqlConn) writePacket(data []byte) error {
|
|||
|
||||
// Handle error
|
||||
if err == nil { // n != len(data)
|
||||
mc.cleanup()
|
||||
errLog.Print(ErrMalformPkt)
|
||||
} else {
|
||||
if cerr := mc.canceled.Value(); cerr != nil {
|
||||
return cerr
|
||||
}
|
||||
mc.cleanup()
|
||||
errLog.Print(err)
|
||||
}
|
||||
return driver.ErrBadConn
|
||||
|
@ -486,23 +497,24 @@ func (mc *mysqlConn) readResultOK() ([]byte, error) {
|
|||
plugin := string(data[1:pluginEndIndex])
|
||||
cipher := data[pluginEndIndex+1 : len(data)-1]
|
||||
|
||||
if plugin == "mysql_old_password" {
|
||||
switch plugin {
|
||||
case "mysql_old_password":
|
||||
// using old_passwords
|
||||
return cipher, ErrOldPassword
|
||||
} else if plugin == "mysql_clear_password" {
|
||||
case "mysql_clear_password":
|
||||
// using clear text password
|
||||
return cipher, ErrCleartextPassword
|
||||
} else if plugin == "mysql_native_password" {
|
||||
case "mysql_native_password":
|
||||
// using mysql default authentication method
|
||||
return cipher, ErrNativePassword
|
||||
} else {
|
||||
default:
|
||||
return cipher, ErrUnknownPlugin
|
||||
}
|
||||
} else {
|
||||
// https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::OldAuthSwitchRequest
|
||||
return nil, ErrOldPassword
|
||||
}
|
||||
|
||||
// https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::OldAuthSwitchRequest
|
||||
return nil, ErrOldPassword
|
||||
|
||||
default: // Error otherwise
|
||||
return nil, mc.handleErrorPacket(data)
|
||||
}
|
||||
|
@ -550,6 +562,21 @@ func (mc *mysqlConn) handleErrorPacket(data []byte) error {
|
|||
// Error Number [16 bit uint]
|
||||
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
|
||||
|
||||
// SQL State [optional: # + 5bytes string]
|
||||
|
@ -584,8 +611,8 @@ func (mc *mysqlConn) handleOkPacket(data []byte) error {
|
|||
|
||||
// server_status [2 bytes]
|
||||
mc.status = readStatus(data[1+n+m : 1+n+m+2])
|
||||
if err := mc.discardResults(); err != nil {
|
||||
return err
|
||||
if mc.status&statusMoreResultsExists != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// warning count [2 bytes]
|
||||
|
@ -698,6 +725,10 @@ func (mc *mysqlConn) readColumns(count int) ([]mysqlField, error) {
|
|||
func (rows *textRows) readRow(dest []driver.Value) error {
|
||||
mc := rows.mc
|
||||
|
||||
if rows.rs.done {
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
data, err := mc.readPacket()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -707,15 +738,11 @@ func (rows *textRows) readRow(dest []driver.Value) error {
|
|||
if data[0] == iEOF && len(data) == 5 {
|
||||
// server_status [2 bytes]
|
||||
rows.mc.status = readStatus(data[3:])
|
||||
err = rows.mc.discardResults()
|
||||
if err == nil {
|
||||
err = io.EOF
|
||||
} else {
|
||||
// connection unusable
|
||||
rows.mc.Close()
|
||||
rows.rs.done = true
|
||||
if !rows.HasNextResultSet() {
|
||||
rows.mc = nil
|
||||
}
|
||||
rows.mc = nil
|
||||
return err
|
||||
return io.EOF
|
||||
}
|
||||
if data[0] == iERR {
|
||||
rows.mc = nil
|
||||
|
@ -736,7 +763,7 @@ func (rows *textRows) readRow(dest []driver.Value) error {
|
|||
if !mc.parseTime {
|
||||
continue
|
||||
} else {
|
||||
switch rows.columns[i].fieldType {
|
||||
switch rows.rs.columns[i].fieldType {
|
||||
case fieldTypeTimestamp, fieldTypeDateTime,
|
||||
fieldTypeDate, fieldTypeNewDate:
|
||||
dest[i], err = parseDateTime(
|
||||
|
@ -1051,17 +1078,19 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error {
|
|||
paramTypes[i+i] = fieldTypeString
|
||||
paramTypes[i+i+1] = 0x00
|
||||
|
||||
var val []byte
|
||||
var a [64]byte
|
||||
var b = a[:0]
|
||||
|
||||
if v.IsZero() {
|
||||
val = []byte("0000-00-00")
|
||||
b = append(b, "0000-00-00"...)
|
||||
} else {
|
||||
val = []byte(v.In(mc.cfg.Loc).Format(timeFormat))
|
||||
b = v.In(mc.cfg.Loc).AppendFormat(b, timeFormat)
|
||||
}
|
||||
|
||||
paramValues = appendLengthEncodedInteger(paramValues,
|
||||
uint64(len(val)),
|
||||
uint64(len(b)),
|
||||
)
|
||||
paramValues = append(paramValues, val...)
|
||||
paramValues = append(paramValues, b...)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("can not convert type: %T", arg)
|
||||
|
@ -1097,8 +1126,6 @@ func (mc *mysqlConn) discardResults() error {
|
|||
if err := mc.readUntilEOF(); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
mc.status &^= statusMoreResultsExists
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
@ -1116,15 +1143,11 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
|
|||
// EOF Packet
|
||||
if data[0] == iEOF && len(data) == 5 {
|
||||
rows.mc.status = readStatus(data[3:])
|
||||
err = rows.mc.discardResults()
|
||||
if err == nil {
|
||||
err = io.EOF
|
||||
} else {
|
||||
// connection unusable
|
||||
rows.mc.Close()
|
||||
rows.rs.done = true
|
||||
if !rows.HasNextResultSet() {
|
||||
rows.mc = nil
|
||||
}
|
||||
rows.mc = nil
|
||||
return err
|
||||
return io.EOF
|
||||
}
|
||||
rows.mc = nil
|
||||
|
||||
|
@ -1145,14 +1168,14 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
|
|||
}
|
||||
|
||||
// Convert to byte-coded string
|
||||
switch rows.columns[i].fieldType {
|
||||
switch rows.rs.columns[i].fieldType {
|
||||
case fieldTypeNULL:
|
||||
dest[i] = nil
|
||||
continue
|
||||
|
||||
// Numeric Types
|
||||
case fieldTypeTiny:
|
||||
if rows.columns[i].flags&flagUnsigned != 0 {
|
||||
if rows.rs.columns[i].flags&flagUnsigned != 0 {
|
||||
dest[i] = int64(data[pos])
|
||||
} else {
|
||||
dest[i] = int64(int8(data[pos]))
|
||||
|
@ -1161,7 +1184,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
|
|||
continue
|
||||
|
||||
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]))
|
||||
} else {
|
||||
dest[i] = int64(int16(binary.LittleEndian.Uint16(data[pos : pos+2])))
|
||||
|
@ -1170,7 +1193,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
|
|||
continue
|
||||
|
||||
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]))
|
||||
} else {
|
||||
dest[i] = int64(int32(binary.LittleEndian.Uint32(data[pos : pos+4])))
|
||||
|
@ -1179,7 +1202,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
|
|||
continue
|
||||
|
||||
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])
|
||||
if val > math.MaxInt64 {
|
||||
dest[i] = uint64ToString(val)
|
||||
|
@ -1193,7 +1216,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
|
|||
continue
|
||||
|
||||
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
|
||||
continue
|
||||
|
||||
|
@ -1233,10 +1256,10 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
|
|||
case isNull:
|
||||
dest[i] = nil
|
||||
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
|
||||
var dstlen uint8
|
||||
switch decimals := rows.columns[i].decimals; decimals {
|
||||
switch decimals := rows.rs.columns[i].decimals; decimals {
|
||||
case 0x00, 0x1f:
|
||||
dstlen = 8
|
||||
case 1, 2, 3, 4, 5, 6:
|
||||
|
@ -1244,7 +1267,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
|
|||
default:
|
||||
return fmt.Errorf(
|
||||
"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)
|
||||
|
@ -1252,10 +1275,10 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
|
|||
dest[i], err = parseBinaryDateTime(num, data[pos:], rows.mc.cfg.Loc)
|
||||
default:
|
||||
var dstlen uint8
|
||||
if rows.columns[i].fieldType == fieldTypeDate {
|
||||
if rows.rs.columns[i].fieldType == fieldTypeDate {
|
||||
dstlen = 10
|
||||
} else {
|
||||
switch decimals := rows.columns[i].decimals; decimals {
|
||||
switch decimals := rows.rs.columns[i].decimals; decimals {
|
||||
case 0x00, 0x1f:
|
||||
dstlen = 19
|
||||
case 1, 2, 3, 4, 5, 6:
|
||||
|
@ -1263,7 +1286,7 @@ func (rows *binaryRows) readRow(dest []driver.Value) error {
|
|||
default:
|
||||
return fmt.Errorf(
|
||||
"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!
|
||||
default:
|
||||
return fmt.Errorf("unknown field type %d", rows.columns[i].fieldType)
|
||||
return fmt.Errorf("unknown field type %d", rows.rs.columns[i].fieldType)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
11
vendor/github.com/go-sql-driver/mysql/packets_test.go
generated
vendored
11
vendor/github.com/go-sql-driver/mysql/packets_test.go
generated
vendored
|
@ -101,7 +101,7 @@ func TestReadPacketSingleByte(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
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 {
|
||||
t.Fatalf("unexpected packet content: expected %x, got %x", 0xff, packet[0])
|
||||
|
@ -171,7 +171,7 @@ func TestReadPacketSplit(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
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 {
|
||||
t.Fatalf("unexpected payload start: expected %x, got %x", 0x11, packet[0])
|
||||
|
@ -205,7 +205,7 @@ func TestReadPacketSplit(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
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 {
|
||||
t.Fatalf("unexpected payload start: expected %x, got %x", 0x11, packet[0])
|
||||
|
@ -231,7 +231,7 @@ func TestReadPacketSplit(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
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 {
|
||||
t.Fatalf("unexpected payload start: expected %x, got %x", 0x11, packet[0])
|
||||
|
@ -244,7 +244,8 @@ func TestReadPacketSplit(t *testing.T) {
|
|||
func TestReadPacketFail(t *testing.T) {
|
||||
conn := new(mockConn)
|
||||
mc := &mysqlConn{
|
||||
buf: newBuffer(conn),
|
||||
buf: newBuffer(conn),
|
||||
closech: make(chan struct{}),
|
||||
}
|
||||
|
||||
// illegal empty (stand-alone) packet
|
||||
|
|
130
vendor/github.com/go-sql-driver/mysql/rows.go
generated
vendored
130
vendor/github.com/go-sql-driver/mysql/rows.go
generated
vendored
|
@ -21,9 +21,16 @@ type mysqlField struct {
|
|||
decimals byte
|
||||
}
|
||||
|
||||
type resultSet struct {
|
||||
columns []mysqlField
|
||||
columnNames []string
|
||||
done bool
|
||||
}
|
||||
|
||||
type mysqlRows struct {
|
||||
mc *mysqlConn
|
||||
columns []mysqlField
|
||||
mc *mysqlConn
|
||||
rs resultSet
|
||||
finish func()
|
||||
}
|
||||
|
||||
type binaryRows struct {
|
||||
|
@ -34,37 +41,48 @@ type textRows struct {
|
|||
mysqlRows
|
||||
}
|
||||
|
||||
type emptyRows struct{}
|
||||
|
||||
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 {
|
||||
for i := range columns {
|
||||
if tableName := rows.columns[i].tableName; len(tableName) > 0 {
|
||||
columns[i] = tableName + "." + rows.columns[i].name
|
||||
if tableName := rows.rs.columns[i].tableName; len(tableName) > 0 {
|
||||
columns[i] = tableName + "." + rows.rs.columns[i].name
|
||||
} else {
|
||||
columns[i] = rows.columns[i].name
|
||||
columns[i] = rows.rs.columns[i].name
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for i := range columns {
|
||||
columns[i] = rows.columns[i].name
|
||||
columns[i] = rows.rs.columns[i].name
|
||||
}
|
||||
}
|
||||
|
||||
rows.rs.columnNames = 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
|
||||
if mc == nil {
|
||||
return nil
|
||||
}
|
||||
if mc.netConn == nil {
|
||||
return ErrInvalidConn
|
||||
if err := mc.error(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove unread packets from stream
|
||||
err := mc.readUntilEOF()
|
||||
if !rows.rs.done {
|
||||
err = mc.readUntilEOF()
|
||||
}
|
||||
if err == nil {
|
||||
if err = mc.discardResults(); err != nil {
|
||||
return err
|
||||
|
@ -75,10 +93,66 @@ func (rows *mysqlRows) Close() error {
|
|||
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 {
|
||||
if mc := rows.mc; mc != nil {
|
||||
if mc.netConn == nil {
|
||||
return ErrInvalidConn
|
||||
if err := mc.error(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Fetch next row from stream
|
||||
|
@ -87,10 +161,20 @@ func (rows *binaryRows) Next(dest []driver.Value) error {
|
|||
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 {
|
||||
if mc := rows.mc; mc != nil {
|
||||
if mc.netConn == nil {
|
||||
return ErrInvalidConn
|
||||
if err := mc.error(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Fetch next row from stream
|
||||
|
@ -98,15 +182,3 @@ func (rows *textRows) Next(dest []driver.Value) error {
|
|||
}
|
||||
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
|
||||
}
|
||||
|
|
63
vendor/github.com/go-sql-driver/mysql/statement.go
generated
vendored
63
vendor/github.com/go-sql-driver/mysql/statement.go
generated
vendored
|
@ -11,6 +11,7 @@ package mysql
|
|||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
@ -19,11 +20,10 @@ type mysqlStmt struct {
|
|||
mc *mysqlConn
|
||||
id uint32
|
||||
paramCount int
|
||||
columns []mysqlField // cached from the first query
|
||||
}
|
||||
|
||||
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
|
||||
// has to be idempotent.
|
||||
// See also Issue #450 and golang/go#16019.
|
||||
|
@ -45,7 +45,7 @@ func (stmt *mysqlStmt) ColumnConverter(idx int) driver.ValueConverter {
|
|||
}
|
||||
|
||||
func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) {
|
||||
if stmt.mc.netConn == nil {
|
||||
if stmt.mc.closed.IsSet() {
|
||||
errLog.Print(ErrInvalidConn)
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
|
@ -62,30 +62,38 @@ func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) {
|
|||
|
||||
// Read Result
|
||||
resLen, err := mc.readResultSetHeaderPacket()
|
||||
if err == nil {
|
||||
if resLen > 0 {
|
||||
// Columns
|
||||
err = mc.readUntilEOF()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Rows
|
||||
err = mc.readUntilEOF()
|
||||
if resLen > 0 {
|
||||
// Columns
|
||||
if err = mc.readUntilEOF(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err == nil {
|
||||
return &mysqlResult{
|
||||
affectedRows: int64(mc.affectedRows),
|
||||
insertId: int64(mc.insertId),
|
||||
}, nil
|
||||
|
||||
// Rows
|
||||
if err := mc.readUntilEOF(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return nil, err
|
||||
if err := mc.discardResults(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &mysqlResult{
|
||||
affectedRows: int64(mc.affectedRows),
|
||||
insertId: int64(mc.insertId),
|
||||
}, nil
|
||||
}
|
||||
|
||||
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)
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
|
@ -107,14 +115,15 @@ func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) {
|
|||
|
||||
if resLen > 0 {
|
||||
rows.mc = mc
|
||||
// Columns
|
||||
// If not cached, read them and cache them
|
||||
if stmt.columns == nil {
|
||||
rows.columns, err = mc.readColumns(resLen)
|
||||
stmt.columns = rows.columns
|
||||
} else {
|
||||
rows.columns = stmt.columns
|
||||
err = mc.readUntilEOF()
|
||||
rows.rs.columns, err = mc.readColumns(resLen)
|
||||
} else {
|
||||
rows.rs.done = true
|
||||
|
||||
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) {
|
||||
if tx.mc == nil || tx.mc.netConn == nil {
|
||||
if tx.mc == nil || tx.mc.closed.IsSet() {
|
||||
return ErrInvalidConn
|
||||
}
|
||||
err = tx.mc.exec("COMMIT")
|
||||
|
@ -22,7 +22,7 @@ func (tx *mysqlTx) Commit() (err error) {
|
|||
}
|
||||
|
||||
func (tx *mysqlTx) Rollback() (err error) {
|
||||
if tx.mc == nil || tx.mc.netConn == nil {
|
||||
if tx.mc == nil || tx.mc.closed.IsSet() {
|
||||
return ErrInvalidConn
|
||||
}
|
||||
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"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
tlsConfigLock sync.RWMutex
|
||||
tlsConfigRegister map[string]*tls.Config // Register for custom tls.Configs
|
||||
)
|
||||
|
||||
// RegisterTLSConfig registers a custom tls.Config to be used with sql.Open.
|
||||
// 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()
|
||||
// pem, err := ioutil.ReadFile("/path/ca-cert.pem")
|
||||
// if err != nil {
|
||||
|
@ -51,19 +56,32 @@ func RegisterTLSConfig(key string, config *tls.Config) error {
|
|||
return fmt.Errorf("key '%s' is reserved", key)
|
||||
}
|
||||
|
||||
tlsConfigLock.Lock()
|
||||
if tlsConfigRegister == nil {
|
||||
tlsConfigRegister = make(map[string]*tls.Config)
|
||||
}
|
||||
|
||||
tlsConfigRegister[key] = config
|
||||
tlsConfigLock.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeregisterTLSConfig removes the tls.Config associated with key.
|
||||
func DeregisterTLSConfig(key string) {
|
||||
tlsConfigLock.Lock()
|
||||
if tlsConfigRegister != nil {
|
||||
delete(tlsConfigRegister, key)
|
||||
}
|
||||
tlsConfigLock.Unlock()
|
||||
}
|
||||
|
||||
func getTLSConfigClone(key string) (config *tls.Config) {
|
||||
tlsConfigLock.RLock()
|
||||
if v, ok := tlsConfigRegister[key]; ok {
|
||||
config = cloneTLSConfig(v)
|
||||
}
|
||||
tlsConfigLock.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Returns the bool value of the input.
|
||||
|
@ -738,3 +756,67 @@ func escapeStringQuotes(buf []byte, v string) []byte {
|
|||
|
||||
return buf[:pos]
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* Sync utils *
|
||||
******************************************************************************/
|
||||
|
||||
// noCopy may be embedded into structs which must not be copied
|
||||
// after the first use.
|
||||
//
|
||||
// See https://github.com/golang/go/issues/8005#issuecomment-190753527
|
||||
// for details.
|
||||
type noCopy struct{}
|
||||
|
||||
// Lock is a no-op used by -copylocks checker from `go vet`.
|
||||
func (*noCopy) Lock() {}
|
||||
|
||||
// atomicBool is a wrapper around uint32 for usage as a boolean value with
|
||||
// atomic access.
|
||||
type atomicBool struct {
|
||||
_noCopy noCopy
|
||||
value uint32
|
||||
}
|
||||
|
||||
// IsSet returns wether the current boolean value is true
|
||||
func (ab *atomicBool) IsSet() bool {
|
||||
return atomic.LoadUint32(&ab.value) > 0
|
||||
}
|
||||
|
||||
// Set sets the value of the bool regardless of the previous value
|
||||
func (ab *atomicBool) Set(value bool) {
|
||||
if value {
|
||||
atomic.StoreUint32(&ab.value, 1)
|
||||
} else {
|
||||
atomic.StoreUint32(&ab.value, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// TrySet sets the value of the bool and returns wether the value changed
|
||||
func (ab *atomicBool) TrySet(value bool) bool {
|
||||
if value {
|
||||
return atomic.SwapUint32(&ab.value, 1) == 0
|
||||
}
|
||||
return atomic.SwapUint32(&ab.value, 0) > 0
|
||||
}
|
||||
|
||||
// atomicBool is a wrapper for atomically accessed error values
|
||||
type atomicError struct {
|
||||
_noCopy noCopy
|
||||
value atomic.Value
|
||||
}
|
||||
|
||||
// Set sets the error value regardless of the previous value.
|
||||
// The value must not be nil
|
||||
func (ae *atomicError) Set(value error) {
|
||||
ae.value.Store(value)
|
||||
}
|
||||
|
||||
// Value returns the current error value
|
||||
func (ae *atomicError) Value() error {
|
||||
if v := ae.value.Load(); v != nil {
|
||||
// this will panic if the value doesn't implement the error interface
|
||||
return v.(error)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
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") // 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