Merge branch 'master' into handle_driver_timeout_error
This commit is contained in:
commit
f268259f5e
13
.github/workflows/ci.yml
vendored
13
.github/workflows/ci.yml
vendored
@ -8,13 +8,18 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Go 1.12
|
||||
- name: Set up Go 1.14
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
version: 1.12
|
||||
id: go
|
||||
go-version: 1.14
|
||||
|
||||
- name: Build
|
||||
run: script/cibuild
|
||||
|
||||
- name: Upload gh-ost binary artifact
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: gh-ost
|
||||
path: bin/gh-ost
|
||||
|
7
.github/workflows/replica-tests.yml
vendored
7
.github/workflows/replica-tests.yml
vendored
@ -8,13 +8,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Go 1.12
|
||||
- name: Set up Go 1.14
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
version: 1.12
|
||||
id: go
|
||||
go-version: 1.14
|
||||
|
||||
- name: migration tests
|
||||
run: script/cibuild-gh-ost-replica-tests
|
||||
|
@ -1,6 +1,6 @@
|
||||
#
|
||||
|
||||
FROM golang:1.12.6
|
||||
FROM golang:1.14.7
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y ruby ruby-dev rubygems build-essential
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM golang:1.12.1
|
||||
FROM golang:1.14.7
|
||||
LABEL maintainer="github@github.com"
|
||||
|
||||
RUN apt-get update
|
||||
|
@ -94,7 +94,7 @@ Please see [Coding gh-ost](doc/coding-ghost.md) for a guide to getting started d
|
||||
|
||||
[Download latest release here](https://github.com/github/gh-ost/releases/latest)
|
||||
|
||||
`gh-ost` is a Go project; it is built with Go `1.12` and above. To build on your own, use either:
|
||||
`gh-ost` is a Go project; it is built with Go `1.14` and above. To build on your own, use either:
|
||||
- [script/build](https://github.com/github/gh-ost/blob/master/script/build) - this is the same build script used by CI hence the authoritative; artifact is `./bin/gh-ost` binary.
|
||||
- [build.sh](https://github.com/github/gh-ost/blob/master/build.sh) for building `tar.gz` artifacts in `/tmp/gh-ost`
|
||||
|
||||
@ -109,3 +109,4 @@ Generally speaking, `master` branch is stable, but only [releases](https://githu
|
||||
- [@shlomi-noach](https://github.com/shlomi-noach)
|
||||
- [@jessbreckenridge](https://github.com/jessbreckenridge)
|
||||
- [@gtowey](https://github.com/gtowey)
|
||||
- [@timvaillancourt](https://github.com/timvaillancourt)
|
||||
|
4
build.sh
4
build.sh
@ -18,8 +18,8 @@ function build {
|
||||
GOOS=$3
|
||||
GOARCH=$4
|
||||
|
||||
if ! go version | egrep -q 'go(1\.1[234])' ; then
|
||||
echo "go version must be 1.12 or above"
|
||||
if ! go version | egrep -q 'go(1\.1[456])' ; then
|
||||
echo "go version must be 1.14 or above"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
@ -80,6 +80,7 @@ type MigrationContext struct {
|
||||
DatabaseName string
|
||||
OriginalTableName string
|
||||
AlterStatement string
|
||||
AlterStatementOptions string // anything following the 'ALTER TABLE [schema.]table' from AlterStatement
|
||||
|
||||
CountTableRows bool
|
||||
ConcurrentCountTableRows bool
|
||||
|
@ -14,6 +14,7 @@ import (
|
||||
|
||||
"github.com/github/gh-ost/go/base"
|
||||
"github.com/github/gh-ost/go/logic"
|
||||
"github.com/github/gh-ost/go/sql"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/outbrain/golib/log"
|
||||
|
||||
@ -48,6 +49,7 @@ func main() {
|
||||
flag.StringVar(&migrationContext.InspectorConnectionConfig.Key.Hostname, "host", "127.0.0.1", "MySQL hostname (preferably a replica, not the master)")
|
||||
flag.StringVar(&migrationContext.AssumeMasterHostname, "assume-master-host", "", "(optional) explicitly tell gh-ost the identity of the master. Format: some.host.com[:port] This is useful in master-master setups where you wish to pick an explicit master, or in a tungsten-replicator where gh-ost is unable to determine the master")
|
||||
flag.IntVar(&migrationContext.InspectorConnectionConfig.Key.Port, "port", 3306, "MySQL port (preferably a replica, not the master)")
|
||||
flag.Float64Var(&migrationContext.InspectorConnectionConfig.Timeout, "mysql-timeout", 0.0, "Connect, read and write timeout for MySQL")
|
||||
flag.StringVar(&migrationContext.CliUser, "user", "", "MySQL user")
|
||||
flag.StringVar(&migrationContext.CliPassword, "password", "", "MySQL password")
|
||||
flag.StringVar(&migrationContext.CliMasterUser, "master-user", "", "MySQL user on master, if different from that on replica. Requires --assume-master-host")
|
||||
@ -172,14 +174,25 @@ func main() {
|
||||
migrationContext.Log.SetLevel(log.ERROR)
|
||||
}
|
||||
|
||||
if migrationContext.AlterStatement == "" {
|
||||
log.Fatalf("--alter must be provided and statement must not be empty")
|
||||
}
|
||||
parser := sql.NewParserFromAlterStatement(migrationContext.AlterStatement)
|
||||
migrationContext.AlterStatementOptions = parser.GetAlterStatementOptions()
|
||||
|
||||
if migrationContext.DatabaseName == "" {
|
||||
migrationContext.Log.Fatalf("--database must be provided and database name must not be empty")
|
||||
if parser.HasExplicitSchema() {
|
||||
migrationContext.DatabaseName = parser.GetExplicitSchema()
|
||||
} else {
|
||||
log.Fatalf("--database must be provided and database name must not be empty, or --alter must specify database name")
|
||||
}
|
||||
}
|
||||
if migrationContext.OriginalTableName == "" {
|
||||
migrationContext.Log.Fatalf("--table must be provided and table name must not be empty")
|
||||
if parser.HasExplicitTable() {
|
||||
migrationContext.OriginalTableName = parser.GetExplicitTable()
|
||||
} else {
|
||||
log.Fatalf("--table must be provided and table name must not be empty, or --alter must specify table name")
|
||||
}
|
||||
if migrationContext.AlterStatement == "" {
|
||||
migrationContext.Log.Fatalf("--alter must be provided and statement must not be empty")
|
||||
}
|
||||
migrationContext.Noop = !(*executeFlag)
|
||||
if migrationContext.AllowedRunningOnMaster && migrationContext.TestOnReplica {
|
||||
|
@ -190,7 +190,7 @@ func (this *Applier) AlterGhost() error {
|
||||
query := fmt.Sprintf(`alter /* gh-ost */ table %s.%s %s`,
|
||||
sql.EscapeName(this.migrationContext.DatabaseName),
|
||||
sql.EscapeName(this.migrationContext.GetGhostTableName()),
|
||||
this.migrationContext.AlterStatement,
|
||||
this.migrationContext.AlterStatementOptions,
|
||||
)
|
||||
this.migrationContext.Log.Infof("Altering ghost table %s.%s",
|
||||
sql.EscapeName(this.migrationContext.DatabaseName),
|
||||
|
@ -60,7 +60,7 @@ const (
|
||||
|
||||
// Migrator is the main schema migration flow manager.
|
||||
type Migrator struct {
|
||||
parser *sql.Parser
|
||||
parser *sql.AlterTableParser
|
||||
inspector *Inspector
|
||||
applier *Applier
|
||||
eventsStreamer *EventsStreamer
|
||||
@ -88,7 +88,7 @@ type Migrator struct {
|
||||
func NewMigrator(context *base.MigrationContext) *Migrator {
|
||||
migrator := &Migrator{
|
||||
migrationContext: context,
|
||||
parser: sql.NewParser(),
|
||||
parser: sql.NewAlterTableParser(),
|
||||
ghostTableMigrated: make(chan bool),
|
||||
firstThrottlingCollected: make(chan bool, 3),
|
||||
rowCopyComplete: make(chan error),
|
||||
|
@ -27,6 +27,7 @@ type ConnectionConfig struct {
|
||||
Password string
|
||||
ImpliedKey *InstanceKey
|
||||
tlsConfig *tls.Config
|
||||
Timeout float64
|
||||
}
|
||||
|
||||
func NewConnectionConfig() *ConnectionConfig {
|
||||
@ -44,6 +45,7 @@ func (this *ConnectionConfig) DuplicateCredentials(key InstanceKey) *ConnectionC
|
||||
User: this.User,
|
||||
Password: this.Password,
|
||||
tlsConfig: this.tlsConfig,
|
||||
Timeout: this.Timeout,
|
||||
}
|
||||
config.ImpliedKey = &config.Key
|
||||
return config
|
||||
@ -116,5 +118,5 @@ func (this *ConnectionConfig) GetDBUri(databaseName string) string {
|
||||
if this.tlsConfig != nil {
|
||||
tlsOption = TLS_CONFIG_KEY
|
||||
}
|
||||
return fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?interpolateParams=%t&autocommit=true&charset=utf8mb4,utf8,latin1&tls=%s", this.User, this.Password, hostname, this.Key.Port, databaseName, interpolateParams, tlsOption)
|
||||
return fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?timeout=%fs&readTimeout=%fs&writeTimeout=%fs&interpolateParams=%t&autocommit=true&charset=utf8mb4,utf8,latin1&tls=%s", this.User, this.Password, hostname, this.Key.Port, databaseName, this.Timeout, this.Timeout, this.Timeout, interpolateParams, tlsOption)
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ func TestGetDBUri(t *testing.T) {
|
||||
c.Password = "penguin"
|
||||
|
||||
uri := c.GetDBUri("test")
|
||||
test.S(t).ExpectEquals(uri, "gromit:penguin@tcp(myhost:3306)/test?interpolateParams=true&autocommit=true&charset=utf8mb4,utf8,latin1&tls=false")
|
||||
test.S(t).ExpectEquals(uri, "gromit:penguin@tcp(myhost:3306)/test?timeout=0.000000s&readTimeout=0.000000s&writeTimeout=0.000000s&interpolateParams=true&autocommit=true&charset=utf8mb4,utf8,latin1&tls=false")
|
||||
}
|
||||
|
||||
func TestGetDBUriWithTLSSetup(t *testing.T) {
|
||||
@ -80,5 +80,5 @@ func TestGetDBUriWithTLSSetup(t *testing.T) {
|
||||
c.tlsConfig = &tls.Config{}
|
||||
|
||||
uri := c.GetDBUri("test")
|
||||
test.S(t).ExpectEquals(uri, "gromit:penguin@tcp(myhost:3306)/test?interpolateParams=true&autocommit=true&charset=utf8mb4,utf8,latin1&tls=ghost")
|
||||
test.S(t).ExpectEquals(uri, "gromit:penguin@tcp(myhost:3306)/test?timeout=0.000000s&readTimeout=0.000000s&writeTimeout=0.000000s&interpolateParams=true&autocommit=true&charset=utf8mb4,utf8,latin1&tls=ghost")
|
||||
}
|
||||
|
@ -492,6 +492,9 @@ func BuildDMLUpdateQuery(databaseName, tableName string, tableColumns, sharedCol
|
||||
}
|
||||
|
||||
setClause, err := BuildSetPreparedClause(mappedSharedColumns)
|
||||
if err != nil {
|
||||
return "", sharedArgs, uniqueKeyArgs, err
|
||||
}
|
||||
|
||||
equalsComparison, err := BuildEqualsPreparedComparison(uniqueKeyColumns.Names())
|
||||
result = fmt.Sprintf(`
|
||||
|
@ -16,22 +16,50 @@ var (
|
||||
renameColumnRegexp = regexp.MustCompile(`(?i)\bchange\s+(column\s+|)([\S]+)\s+([\S]+)\s+`)
|
||||
dropColumnRegexp = regexp.MustCompile(`(?i)\bdrop\s+(column\s+|)([\S]+)$`)
|
||||
renameTableRegexp = regexp.MustCompile(`(?i)\brename\s+(to|as)\s+`)
|
||||
alterTableExplicitSchemaTableRegexps = []*regexp.Regexp{
|
||||
// ALTER TABLE `scm`.`tbl` something
|
||||
regexp.MustCompile(`(?i)\balter\s+table\s+` + "`" + `([^` + "`" + `]+)` + "`" + `[.]` + "`" + `([^` + "`" + `]+)` + "`" + `\s+(.*$)`),
|
||||
// ALTER TABLE `scm`.tbl something
|
||||
regexp.MustCompile(`(?i)\balter\s+table\s+` + "`" + `([^` + "`" + `]+)` + "`" + `[.]([\S]+)\s+(.*$)`),
|
||||
// ALTER TABLE scm.`tbl` something
|
||||
regexp.MustCompile(`(?i)\balter\s+table\s+([\S]+)[.]` + "`" + `([^` + "`" + `]+)` + "`" + `\s+(.*$)`),
|
||||
// ALTER TABLE scm.tbl something
|
||||
regexp.MustCompile(`(?i)\balter\s+table\s+([\S]+)[.]([\S]+)\s+(.*$)`),
|
||||
}
|
||||
alterTableExplicitTableRegexps = []*regexp.Regexp{
|
||||
// ALTER TABLE `tbl` something
|
||||
regexp.MustCompile(`(?i)\balter\s+table\s+` + "`" + `([^` + "`" + `]+)` + "`" + `\s+(.*$)`),
|
||||
// ALTER TABLE tbl something
|
||||
regexp.MustCompile(`(?i)\balter\s+table\s+([\S]+)\s+(.*$)`),
|
||||
}
|
||||
)
|
||||
|
||||
type Parser struct {
|
||||
type AlterTableParser struct {
|
||||
columnRenameMap map[string]string
|
||||
droppedColumns map[string]bool
|
||||
isRenameTable bool
|
||||
|
||||
alterStatementOptions string
|
||||
alterTokens []string
|
||||
|
||||
explicitSchema string
|
||||
explicitTable string
|
||||
}
|
||||
|
||||
func NewParser() *Parser {
|
||||
return &Parser{
|
||||
func NewAlterTableParser() *AlterTableParser {
|
||||
return &AlterTableParser{
|
||||
columnRenameMap: make(map[string]string),
|
||||
droppedColumns: make(map[string]bool),
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Parser) tokenizeAlterStatement(alterStatement string) (tokens []string, err error) {
|
||||
func NewParserFromAlterStatement(alterStatement string) *AlterTableParser {
|
||||
parser := NewAlterTableParser()
|
||||
parser.ParseAlterStatement(alterStatement)
|
||||
return parser
|
||||
}
|
||||
|
||||
func (this *AlterTableParser) tokenizeAlterStatement(alterStatement string) (tokens []string, err error) {
|
||||
terminatingQuote := rune(0)
|
||||
f := func(c rune) bool {
|
||||
switch {
|
||||
@ -58,13 +86,13 @@ func (this *Parser) tokenizeAlterStatement(alterStatement string) (tokens []stri
|
||||
return tokens, nil
|
||||
}
|
||||
|
||||
func (this *Parser) sanitizeQuotesFromAlterStatement(alterStatement string) (strippedStatement string) {
|
||||
func (this *AlterTableParser) sanitizeQuotesFromAlterStatement(alterStatement string) (strippedStatement string) {
|
||||
strippedStatement = alterStatement
|
||||
strippedStatement = sanitizeQuotesRegexp.ReplaceAllString(strippedStatement, "''")
|
||||
return strippedStatement
|
||||
}
|
||||
|
||||
func (this *Parser) parseAlterToken(alterToken string) (err error) {
|
||||
func (this *AlterTableParser) parseAlterToken(alterToken string) (err error) {
|
||||
{
|
||||
// rename
|
||||
allStringSubmatch := renameColumnRegexp.FindAllStringSubmatch(alterToken, -1)
|
||||
@ -97,16 +125,34 @@ func (this *Parser) parseAlterToken(alterToken string) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *Parser) ParseAlterStatement(alterStatement string) (err error) {
|
||||
alterTokens, _ := this.tokenizeAlterStatement(alterStatement)
|
||||
func (this *AlterTableParser) ParseAlterStatement(alterStatement string) (err error) {
|
||||
|
||||
this.alterStatementOptions = alterStatement
|
||||
for _, alterTableRegexp := range alterTableExplicitSchemaTableRegexps {
|
||||
if submatch := alterTableRegexp.FindStringSubmatch(this.alterStatementOptions); len(submatch) > 0 {
|
||||
this.explicitSchema = submatch[1]
|
||||
this.explicitTable = submatch[2]
|
||||
this.alterStatementOptions = submatch[3]
|
||||
break
|
||||
}
|
||||
}
|
||||
for _, alterTableRegexp := range alterTableExplicitTableRegexps {
|
||||
if submatch := alterTableRegexp.FindStringSubmatch(this.alterStatementOptions); len(submatch) > 0 {
|
||||
this.explicitTable = submatch[1]
|
||||
this.alterStatementOptions = submatch[2]
|
||||
break
|
||||
}
|
||||
}
|
||||
alterTokens, _ := this.tokenizeAlterStatement(this.alterStatementOptions)
|
||||
for _, alterToken := range alterTokens {
|
||||
alterToken = this.sanitizeQuotesFromAlterStatement(alterToken)
|
||||
this.parseAlterToken(alterToken)
|
||||
this.alterTokens = append(this.alterTokens, alterToken)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *Parser) GetNonTrivialRenames() map[string]string {
|
||||
func (this *AlterTableParser) GetNonTrivialRenames() map[string]string {
|
||||
result := make(map[string]string)
|
||||
for column, renamed := range this.columnRenameMap {
|
||||
if column != renamed {
|
||||
@ -116,14 +162,33 @@ func (this *Parser) GetNonTrivialRenames() map[string]string {
|
||||
return result
|
||||
}
|
||||
|
||||
func (this *Parser) HasNonTrivialRenames() bool {
|
||||
func (this *AlterTableParser) HasNonTrivialRenames() bool {
|
||||
return len(this.GetNonTrivialRenames()) > 0
|
||||
}
|
||||
|
||||
func (this *Parser) DroppedColumnsMap() map[string]bool {
|
||||
func (this *AlterTableParser) DroppedColumnsMap() map[string]bool {
|
||||
return this.droppedColumns
|
||||
}
|
||||
|
||||
func (this *Parser) IsRenameTable() bool {
|
||||
func (this *AlterTableParser) IsRenameTable() bool {
|
||||
return this.isRenameTable
|
||||
}
|
||||
func (this *AlterTableParser) GetExplicitSchema() string {
|
||||
return this.explicitSchema
|
||||
}
|
||||
|
||||
func (this *AlterTableParser) HasExplicitSchema() bool {
|
||||
return this.GetExplicitSchema() != ""
|
||||
}
|
||||
|
||||
func (this *AlterTableParser) GetExplicitTable() string {
|
||||
return this.explicitTable
|
||||
}
|
||||
|
||||
func (this *AlterTableParser) HasExplicitTable() bool {
|
||||
return this.GetExplicitTable() != ""
|
||||
}
|
||||
|
||||
func (this *AlterTableParser) GetAlterStatementOptions() string {
|
||||
return this.alterStatementOptions
|
||||
}
|
||||
|
@ -19,17 +19,19 @@ func init() {
|
||||
|
||||
func TestParseAlterStatement(t *testing.T) {
|
||||
statement := "add column t int, engine=innodb"
|
||||
parser := NewParser()
|
||||
parser := NewAlterTableParser()
|
||||
err := parser.ParseAlterStatement(statement)
|
||||
test.S(t).ExpectNil(err)
|
||||
test.S(t).ExpectEquals(parser.alterStatementOptions, statement)
|
||||
test.S(t).ExpectFalse(parser.HasNonTrivialRenames())
|
||||
}
|
||||
|
||||
func TestParseAlterStatementTrivialRename(t *testing.T) {
|
||||
statement := "add column t int, change ts ts timestamp, engine=innodb"
|
||||
parser := NewParser()
|
||||
parser := NewAlterTableParser()
|
||||
err := parser.ParseAlterStatement(statement)
|
||||
test.S(t).ExpectNil(err)
|
||||
test.S(t).ExpectEquals(parser.alterStatementOptions, statement)
|
||||
test.S(t).ExpectFalse(parser.HasNonTrivialRenames())
|
||||
test.S(t).ExpectEquals(len(parser.columnRenameMap), 1)
|
||||
test.S(t).ExpectEquals(parser.columnRenameMap["ts"], "ts")
|
||||
@ -37,9 +39,10 @@ func TestParseAlterStatementTrivialRename(t *testing.T) {
|
||||
|
||||
func TestParseAlterStatementTrivialRenames(t *testing.T) {
|
||||
statement := "add column t int, change ts ts timestamp, CHANGE f `f` float, engine=innodb"
|
||||
parser := NewParser()
|
||||
parser := NewAlterTableParser()
|
||||
err := parser.ParseAlterStatement(statement)
|
||||
test.S(t).ExpectNil(err)
|
||||
test.S(t).ExpectEquals(parser.alterStatementOptions, statement)
|
||||
test.S(t).ExpectFalse(parser.HasNonTrivialRenames())
|
||||
test.S(t).ExpectEquals(len(parser.columnRenameMap), 2)
|
||||
test.S(t).ExpectEquals(parser.columnRenameMap["ts"], "ts")
|
||||
@ -58,9 +61,10 @@ func TestParseAlterStatementNonTrivial(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, statement := range statements {
|
||||
parser := NewParser()
|
||||
parser := NewAlterTableParser()
|
||||
err := parser.ParseAlterStatement(statement)
|
||||
test.S(t).ExpectNil(err)
|
||||
test.S(t).ExpectEquals(parser.alterStatementOptions, statement)
|
||||
renames := parser.GetNonTrivialRenames()
|
||||
test.S(t).ExpectEquals(len(renames), 2)
|
||||
test.S(t).ExpectEquals(renames["i"], "count")
|
||||
@ -69,7 +73,7 @@ func TestParseAlterStatementNonTrivial(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestTokenizeAlterStatement(t *testing.T) {
|
||||
parser := NewParser()
|
||||
parser := NewAlterTableParser()
|
||||
{
|
||||
alterStatement := "add column t int"
|
||||
tokens, _ := parser.tokenizeAlterStatement(alterStatement)
|
||||
@ -108,7 +112,7 @@ func TestTokenizeAlterStatement(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSanitizeQuotesFromAlterStatement(t *testing.T) {
|
||||
parser := NewParser()
|
||||
parser := NewAlterTableParser()
|
||||
{
|
||||
alterStatement := "add column e enum('a','b','c')"
|
||||
strippedStatement := parser.sanitizeQuotesFromAlterStatement(alterStatement)
|
||||
@ -124,7 +128,7 @@ func TestSanitizeQuotesFromAlterStatement(t *testing.T) {
|
||||
func TestParseAlterStatementDroppedColumns(t *testing.T) {
|
||||
|
||||
{
|
||||
parser := NewParser()
|
||||
parser := NewAlterTableParser()
|
||||
statement := "drop column b"
|
||||
err := parser.ParseAlterStatement(statement)
|
||||
test.S(t).ExpectNil(err)
|
||||
@ -132,16 +136,17 @@ func TestParseAlterStatementDroppedColumns(t *testing.T) {
|
||||
test.S(t).ExpectTrue(parser.droppedColumns["b"])
|
||||
}
|
||||
{
|
||||
parser := NewParser()
|
||||
parser := NewAlterTableParser()
|
||||
statement := "drop column b, drop key c_idx, drop column `d`"
|
||||
err := parser.ParseAlterStatement(statement)
|
||||
test.S(t).ExpectNil(err)
|
||||
test.S(t).ExpectEquals(parser.alterStatementOptions, statement)
|
||||
test.S(t).ExpectEquals(len(parser.droppedColumns), 2)
|
||||
test.S(t).ExpectTrue(parser.droppedColumns["b"])
|
||||
test.S(t).ExpectTrue(parser.droppedColumns["d"])
|
||||
}
|
||||
{
|
||||
parser := NewParser()
|
||||
parser := NewAlterTableParser()
|
||||
statement := "drop column b, drop key c_idx, drop column `d`, drop `e`, drop primary key, drop foreign key fk_1"
|
||||
err := parser.ParseAlterStatement(statement)
|
||||
test.S(t).ExpectNil(err)
|
||||
@ -151,7 +156,7 @@ func TestParseAlterStatementDroppedColumns(t *testing.T) {
|
||||
test.S(t).ExpectTrue(parser.droppedColumns["e"])
|
||||
}
|
||||
{
|
||||
parser := NewParser()
|
||||
parser := NewAlterTableParser()
|
||||
statement := "drop column b, drop bad statement, add column i int"
|
||||
err := parser.ParseAlterStatement(statement)
|
||||
test.S(t).ExpectNil(err)
|
||||
@ -163,38 +168,133 @@ func TestParseAlterStatementDroppedColumns(t *testing.T) {
|
||||
func TestParseAlterStatementRenameTable(t *testing.T) {
|
||||
|
||||
{
|
||||
parser := NewParser()
|
||||
parser := NewAlterTableParser()
|
||||
statement := "drop column b"
|
||||
err := parser.ParseAlterStatement(statement)
|
||||
test.S(t).ExpectNil(err)
|
||||
test.S(t).ExpectFalse(parser.isRenameTable)
|
||||
}
|
||||
{
|
||||
parser := NewParser()
|
||||
parser := NewAlterTableParser()
|
||||
statement := "rename as something_else"
|
||||
err := parser.ParseAlterStatement(statement)
|
||||
test.S(t).ExpectNil(err)
|
||||
test.S(t).ExpectTrue(parser.isRenameTable)
|
||||
}
|
||||
{
|
||||
parser := NewParser()
|
||||
parser := NewAlterTableParser()
|
||||
statement := "drop column b, rename as something_else"
|
||||
err := parser.ParseAlterStatement(statement)
|
||||
test.S(t).ExpectNil(err)
|
||||
test.S(t).ExpectEquals(parser.alterStatementOptions, statement)
|
||||
test.S(t).ExpectTrue(parser.isRenameTable)
|
||||
}
|
||||
{
|
||||
parser := NewParser()
|
||||
parser := NewAlterTableParser()
|
||||
statement := "engine=innodb rename as something_else"
|
||||
err := parser.ParseAlterStatement(statement)
|
||||
test.S(t).ExpectNil(err)
|
||||
test.S(t).ExpectTrue(parser.isRenameTable)
|
||||
}
|
||||
{
|
||||
parser := NewParser()
|
||||
parser := NewAlterTableParser()
|
||||
statement := "rename as something_else, engine=innodb"
|
||||
err := parser.ParseAlterStatement(statement)
|
||||
test.S(t).ExpectNil(err)
|
||||
test.S(t).ExpectTrue(parser.isRenameTable)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseAlterStatementExplicitTable(t *testing.T) {
|
||||
|
||||
{
|
||||
parser := NewAlterTableParser()
|
||||
statement := "drop column b"
|
||||
err := parser.ParseAlterStatement(statement)
|
||||
test.S(t).ExpectNil(err)
|
||||
test.S(t).ExpectEquals(parser.explicitSchema, "")
|
||||
test.S(t).ExpectEquals(parser.explicitTable, "")
|
||||
test.S(t).ExpectEquals(parser.alterStatementOptions, "drop column b")
|
||||
test.S(t).ExpectTrue(reflect.DeepEqual(parser.alterTokens, []string{"drop column b"}))
|
||||
}
|
||||
{
|
||||
parser := NewAlterTableParser()
|
||||
statement := "alter table tbl drop column b"
|
||||
err := parser.ParseAlterStatement(statement)
|
||||
test.S(t).ExpectNil(err)
|
||||
test.S(t).ExpectEquals(parser.explicitSchema, "")
|
||||
test.S(t).ExpectEquals(parser.explicitTable, "tbl")
|
||||
test.S(t).ExpectEquals(parser.alterStatementOptions, "drop column b")
|
||||
test.S(t).ExpectTrue(reflect.DeepEqual(parser.alterTokens, []string{"drop column b"}))
|
||||
}
|
||||
{
|
||||
parser := NewAlterTableParser()
|
||||
statement := "alter table `tbl` drop column b"
|
||||
err := parser.ParseAlterStatement(statement)
|
||||
test.S(t).ExpectNil(err)
|
||||
test.S(t).ExpectEquals(parser.explicitSchema, "")
|
||||
test.S(t).ExpectEquals(parser.explicitTable, "tbl")
|
||||
test.S(t).ExpectEquals(parser.alterStatementOptions, "drop column b")
|
||||
test.S(t).ExpectTrue(reflect.DeepEqual(parser.alterTokens, []string{"drop column b"}))
|
||||
}
|
||||
{
|
||||
parser := NewAlterTableParser()
|
||||
statement := "alter table `scm with spaces`.`tbl` drop column b"
|
||||
err := parser.ParseAlterStatement(statement)
|
||||
test.S(t).ExpectNil(err)
|
||||
test.S(t).ExpectEquals(parser.explicitSchema, "scm with spaces")
|
||||
test.S(t).ExpectEquals(parser.explicitTable, "tbl")
|
||||
test.S(t).ExpectEquals(parser.alterStatementOptions, "drop column b")
|
||||
test.S(t).ExpectTrue(reflect.DeepEqual(parser.alterTokens, []string{"drop column b"}))
|
||||
}
|
||||
{
|
||||
parser := NewAlterTableParser()
|
||||
statement := "alter table `scm`.`tbl with spaces` drop column b"
|
||||
err := parser.ParseAlterStatement(statement)
|
||||
test.S(t).ExpectNil(err)
|
||||
test.S(t).ExpectEquals(parser.explicitSchema, "scm")
|
||||
test.S(t).ExpectEquals(parser.explicitTable, "tbl with spaces")
|
||||
test.S(t).ExpectEquals(parser.alterStatementOptions, "drop column b")
|
||||
test.S(t).ExpectTrue(reflect.DeepEqual(parser.alterTokens, []string{"drop column b"}))
|
||||
}
|
||||
{
|
||||
parser := NewAlterTableParser()
|
||||
statement := "alter table `scm`.tbl drop column b"
|
||||
err := parser.ParseAlterStatement(statement)
|
||||
test.S(t).ExpectNil(err)
|
||||
test.S(t).ExpectEquals(parser.explicitSchema, "scm")
|
||||
test.S(t).ExpectEquals(parser.explicitTable, "tbl")
|
||||
test.S(t).ExpectEquals(parser.alterStatementOptions, "drop column b")
|
||||
test.S(t).ExpectTrue(reflect.DeepEqual(parser.alterTokens, []string{"drop column b"}))
|
||||
}
|
||||
{
|
||||
parser := NewAlterTableParser()
|
||||
statement := "alter table scm.`tbl` drop column b"
|
||||
err := parser.ParseAlterStatement(statement)
|
||||
test.S(t).ExpectNil(err)
|
||||
test.S(t).ExpectEquals(parser.explicitSchema, "scm")
|
||||
test.S(t).ExpectEquals(parser.explicitTable, "tbl")
|
||||
test.S(t).ExpectEquals(parser.alterStatementOptions, "drop column b")
|
||||
test.S(t).ExpectTrue(reflect.DeepEqual(parser.alterTokens, []string{"drop column b"}))
|
||||
}
|
||||
{
|
||||
parser := NewAlterTableParser()
|
||||
statement := "alter table scm.tbl drop column b"
|
||||
err := parser.ParseAlterStatement(statement)
|
||||
test.S(t).ExpectNil(err)
|
||||
test.S(t).ExpectEquals(parser.explicitSchema, "scm")
|
||||
test.S(t).ExpectEquals(parser.explicitTable, "tbl")
|
||||
test.S(t).ExpectEquals(parser.alterStatementOptions, "drop column b")
|
||||
test.S(t).ExpectTrue(reflect.DeepEqual(parser.alterTokens, []string{"drop column b"}))
|
||||
}
|
||||
{
|
||||
parser := NewAlterTableParser()
|
||||
statement := "alter table scm.tbl drop column b, add index idx(i)"
|
||||
err := parser.ParseAlterStatement(statement)
|
||||
test.S(t).ExpectNil(err)
|
||||
test.S(t).ExpectEquals(parser.explicitSchema, "scm")
|
||||
test.S(t).ExpectEquals(parser.explicitTable, "tbl")
|
||||
test.S(t).ExpectEquals(parser.alterStatementOptions, "drop column b, add index idx(i)")
|
||||
test.S(t).ExpectTrue(reflect.DeepEqual(parser.alterTokens, []string{"drop column b", "add index idx(i)"}))
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
PREFERRED_GO_VERSION=go1.12.6
|
||||
SUPPORTED_GO_VERSIONS='go1.1[234]'
|
||||
PREFERRED_GO_VERSION=go1.14.7
|
||||
SUPPORTED_GO_VERSIONS='go1.1[456]'
|
||||
|
||||
GO_PKG_DARWIN=${PREFERRED_GO_VERSION}.darwin-amd64.pkg
|
||||
GO_PKG_DARWIN_SHA=ea78245e43de2996fa0973033064b33f48820cfe39f4f3c6e953040925cc5815
|
||||
GO_PKG_DARWIN_SHA=0f215de06019a054a3da46a0722989986c956d719c7a0a8fc38a5f3c216d6f6b
|
||||
|
||||
GO_PKG_LINUX=${PREFERRED_GO_VERSION}.linux-amd64.tar.gz
|
||||
GO_PKG_LINUX_SHA=dbcf71a3c1ea53b8d54ef1b48c85a39a6c9a935d01fc8291ff2b92028e59913c
|
||||
GO_PKG_LINUX_SHA=4a7fa60f323ee1416a4b1425aefc37ea359e9d64df19c326a58953a97ad41ea5
|
||||
|
||||
export ROOTDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd )"
|
||||
cd $ROOTDIR
|
||||
|
3
vendor/github.com/go-sql-driver/mysql/.travis.yml
generated
vendored
3
vendor/github.com/go-sql-driver/mysql/.travis.yml
generated
vendored
@ -1,10 +1,10 @@
|
||||
sudo: false
|
||||
language: go
|
||||
go:
|
||||
- 1.9.x
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
- 1.13.x
|
||||
- master
|
||||
|
||||
before_install:
|
||||
@ -105,6 +105,7 @@ matrix:
|
||||
homebrew:
|
||||
packages:
|
||||
- mysql
|
||||
update: true
|
||||
go: 1.12.x
|
||||
before_install:
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
|
5
vendor/github.com/go-sql-driver/mysql/AUTHORS
generated
vendored
5
vendor/github.com/go-sql-driver/mysql/AUTHORS
generated
vendored
@ -13,6 +13,7 @@
|
||||
|
||||
Aaron Hopkins <go-sql-driver at die.net>
|
||||
Achille Roussel <achille.roussel at gmail.com>
|
||||
Alex Snast <alexsn at fb.com>
|
||||
Alexey Palazhchenko <alexey.palazhchenko at gmail.com>
|
||||
Andrew Reid <andrew.reid at tixtrack.com>
|
||||
Arne Hormann <arnehormann at gmail.com>
|
||||
@ -44,6 +45,7 @@ James Harr <james.harr at gmail.com>
|
||||
Jeff Hodges <jeff at somethingsimilar.com>
|
||||
Jeffrey Charles <jeffreycharles at gmail.com>
|
||||
Jerome Meyer <jxmeyer at gmail.com>
|
||||
Jiajia Zhong <zhong2plus at gmail.com>
|
||||
Jian Zhen <zhenjl at gmail.com>
|
||||
Joshua Prunier <joshua.prunier at gmail.com>
|
||||
Julien Lefevre <julien.lefevr at gmail.com>
|
||||
@ -62,6 +64,7 @@ Lucas Liu <extrafliu at gmail.com>
|
||||
Luke Scott <luke at webconnex.com>
|
||||
Maciej Zimnoch <maciej.zimnoch at codilime.com>
|
||||
Michael Woolnough <michael.woolnough at gmail.com>
|
||||
Nathanial Murphy <nathanial.murphy at gmail.com>
|
||||
Nicola Peduzzi <thenikso at gmail.com>
|
||||
Olivier Mengué <dolmen at cpan.org>
|
||||
oscarzhao <oscarzhaosl at gmail.com>
|
||||
@ -81,6 +84,7 @@ Steven Hartland <steven.hartland at multiplay.co.uk>
|
||||
Thomas Wodarek <wodarekwebpage at gmail.com>
|
||||
Tim Ruffles <timruffles at gmail.com>
|
||||
Tom Jenkinson <tom at tjenkinson.me>
|
||||
Vladimir Kovpak <cn007b at gmail.com>
|
||||
Xiangyu Hu <xiangyu.hu at outlook.com>
|
||||
Xiaobing Jiang <s7v7nislands at gmail.com>
|
||||
Xiuming Chen <cc at cxm.cc>
|
||||
@ -90,6 +94,7 @@ Zhenye Xie <xiezhenye at gmail.com>
|
||||
|
||||
Barracuda Networks, Inc.
|
||||
Counting Ltd.
|
||||
DigitalOcean Inc.
|
||||
Facebook Inc.
|
||||
GitHub Inc.
|
||||
Google Inc.
|
||||
|
39
vendor/github.com/go-sql-driver/mysql/CHANGELOG.md
generated
vendored
39
vendor/github.com/go-sql-driver/mysql/CHANGELOG.md
generated
vendored
@ -1,3 +1,42 @@
|
||||
## Version 1.5 (2020-01-07)
|
||||
|
||||
Changes:
|
||||
|
||||
- Dropped support Go 1.9 and lower (#823, #829, #886, #1016, #1017)
|
||||
- Improve buffer handling (#890)
|
||||
- Document potentially insecure TLS configs (#901)
|
||||
- Use a double-buffering scheme to prevent data races (#943)
|
||||
- Pass uint64 values without converting them to string (#838, #955)
|
||||
- Update collations and make utf8mb4 default (#877, #1054)
|
||||
- Make NullTime compatible with sql.NullTime in Go 1.13+ (#995)
|
||||
- Removed CloudSQL support (#993, #1007)
|
||||
- Add Go Module support (#1003)
|
||||
|
||||
New Features:
|
||||
|
||||
- Implement support of optional TLS (#900)
|
||||
- Check connection liveness (#934, #964, #997, #1048, #1051, #1052)
|
||||
- Implement Connector Interface (#941, #958, #1020, #1035)
|
||||
|
||||
Bugfixes:
|
||||
|
||||
- Mark connections as bad on error during ping (#875)
|
||||
- Mark connections as bad on error during dial (#867)
|
||||
- Fix connection leak caused by rapid context cancellation (#1024)
|
||||
- Mark connections as bad on error during Conn.Prepare (#1030)
|
||||
|
||||
|
||||
## Version 1.4.1 (2018-11-14)
|
||||
|
||||
Bugfixes:
|
||||
|
||||
- Fix TIME format for binary columns (#818)
|
||||
- Fix handling of empty auth plugin names (#835)
|
||||
- Fix caching_sha2_password with empty password (#826)
|
||||
- Fix canceled context broke mysqlConn (#862)
|
||||
- Fix OldAuthSwitchRequest support (#870)
|
||||
- Fix Auth Response packet for cleartext password (#887)
|
||||
|
||||
## Version 1.4 (2018-06-03)
|
||||
|
||||
Changes:
|
||||
|
26
vendor/github.com/go-sql-driver/mysql/README.md
generated
vendored
26
vendor/github.com/go-sql-driver/mysql/README.md
generated
vendored
@ -40,7 +40,7 @@ A MySQL-Driver for Go's [database/sql](https://golang.org/pkg/database/sql/) pac
|
||||
* Optional placeholder interpolation
|
||||
|
||||
## Requirements
|
||||
* Go 1.9 or higher. We aim to support the 3 latest versions of Go.
|
||||
* Go 1.10 or higher. We aim to support the 3 latest versions of Go.
|
||||
* MySQL (4.1+), MariaDB, Percona Server, Google CloudSQL or Sphinx (2.2.3+)
|
||||
|
||||
---------------------------------------
|
||||
@ -166,6 +166,17 @@ Sets the charset used for client-server interaction (`"SET NAMES <value>"`). If
|
||||
Usage of the `charset` parameter is discouraged because it issues additional queries to the server.
|
||||
Unless you need the fallback behavior, please use `collation` instead.
|
||||
|
||||
##### `checkConnLiveness`
|
||||
|
||||
```
|
||||
Type: bool
|
||||
Valid Values: true, false
|
||||
Default: true
|
||||
```
|
||||
|
||||
On supported platforms connections retrieved from the connection pool are checked for liveness before using them. If the check fails, the respective connection is marked as bad and the query retried with another connection.
|
||||
`checkConnLiveness=false` disables this liveness check of connections.
|
||||
|
||||
##### `collation`
|
||||
|
||||
```
|
||||
@ -396,14 +407,9 @@ TCP on a remote host, e.g. Amazon RDS:
|
||||
id:password@tcp(your-amazonaws-uri.com:3306)/dbname
|
||||
```
|
||||
|
||||
Google Cloud SQL on App Engine (First Generation MySQL Server):
|
||||
Google Cloud SQL on App Engine:
|
||||
```
|
||||
user@cloudsql(project-id:instance-name)/dbname
|
||||
```
|
||||
|
||||
Google Cloud SQL on App Engine (Second Generation MySQL Server):
|
||||
```
|
||||
user@cloudsql(project-id:regionname:instance-name)/dbname
|
||||
user:password@unix(/cloudsql/project-id:region-name:instance-name)/dbname
|
||||
```
|
||||
|
||||
TCP using default port (3306) on localhost:
|
||||
@ -457,13 +463,13 @@ Alternatively you can use the [`NullTime`](https://godoc.org/github.com/go-sql-d
|
||||
|
||||
|
||||
### Unicode support
|
||||
Since version 1.1 Go-MySQL-Driver automatically uses the collation `utf8_general_ci` by default.
|
||||
Since version 1.5 Go-MySQL-Driver automatically uses the collation ` utf8mb4_general_ci` by default.
|
||||
|
||||
Other collations / charsets can be set using the [`collation`](#collation) DSN parameter.
|
||||
|
||||
Version 1.0 of the driver recommended adding `&charset=utf8` (alias for `SET NAMES utf8`) to the DSN to enable proper UTF-8 support. This is not necessary anymore. The [`collation`](#collation) parameter should be preferred to set another collation / charset than the default.
|
||||
|
||||
See http://dev.mysql.com/doc/refman/5.7/en/charset-unicode.html for more details on MySQL's Unicode support.
|
||||
See http://dev.mysql.com/doc/refman/8.0/en/charset-unicode.html for more details on MySQL's Unicode support.
|
||||
|
||||
## 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.
|
||||
|
25
vendor/github.com/go-sql-driver/mysql/appengine.go
generated
vendored
25
vendor/github.com/go-sql-driver/mysql/appengine.go
generated
vendored
@ -1,25 +0,0 @@
|
||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// +build appengine
|
||||
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
|
||||
"google.golang.org/appengine/cloudsql"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterDialContext("cloudsql", func(_ context.Context, instance string) (net.Conn, error) {
|
||||
// XXX: the cloudsql driver still does not export a Context-aware dialer.
|
||||
return cloudsql.Dial(instance)
|
||||
})
|
||||
}
|
43
vendor/github.com/go-sql-driver/mysql/conncheck.go
generated
vendored
43
vendor/github.com/go-sql-driver/mysql/conncheck.go
generated
vendored
@ -6,7 +6,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/.
|
||||
|
||||
// +build !windows,!appengine
|
||||
// +build linux darwin dragonfly freebsd netbsd openbsd solaris illumos
|
||||
|
||||
package mysql
|
||||
|
||||
@ -19,35 +19,36 @@ import (
|
||||
|
||||
var errUnexpectedRead = errors.New("unexpected read from socket")
|
||||
|
||||
func connCheck(c net.Conn) error {
|
||||
var (
|
||||
n int
|
||||
err error
|
||||
buff [1]byte
|
||||
)
|
||||
func connCheck(conn net.Conn) error {
|
||||
var sysErr error
|
||||
|
||||
sconn, ok := c.(syscall.Conn)
|
||||
sysConn, ok := conn.(syscall.Conn)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
rc, err := sconn.SyscallConn()
|
||||
rawConn, err := sysConn.SyscallConn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rerr := rc.Read(func(fd uintptr) bool {
|
||||
n, err = syscall.Read(int(fd), buff[:])
|
||||
|
||||
err = rawConn.Read(func(fd uintptr) bool {
|
||||
var buf [1]byte
|
||||
n, err := syscall.Read(int(fd), buf[:])
|
||||
switch {
|
||||
case n == 0 && err == nil:
|
||||
sysErr = io.EOF
|
||||
case n > 0:
|
||||
sysErr = errUnexpectedRead
|
||||
case err == syscall.EAGAIN || err == syscall.EWOULDBLOCK:
|
||||
sysErr = nil
|
||||
default:
|
||||
sysErr = err
|
||||
}
|
||||
return true
|
||||
})
|
||||
switch {
|
||||
case rerr != nil:
|
||||
return rerr
|
||||
case n == 0 && err == nil:
|
||||
return io.EOF
|
||||
case n > 0:
|
||||
return errUnexpectedRead
|
||||
case err == syscall.EAGAIN || err == syscall.EWOULDBLOCK:
|
||||
return nil
|
||||
default:
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sysErr
|
||||
}
|
||||
|
4
vendor/github.com/go-sql-driver/mysql/conncheck_dummy.go
generated
vendored
4
vendor/github.com/go-sql-driver/mysql/conncheck_dummy.go
generated
vendored
@ -6,12 +6,12 @@
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// +build windows appengine
|
||||
// +build !linux,!darwin,!dragonfly,!freebsd,!netbsd,!openbsd,!solaris,!illumos
|
||||
|
||||
package mysql
|
||||
|
||||
import "net"
|
||||
|
||||
func connCheck(c net.Conn) error {
|
||||
func connCheck(conn net.Conn) error {
|
||||
return nil
|
||||
}
|
||||
|
2
vendor/github.com/go-sql-driver/mysql/conncheck_test.go
generated
vendored
2
vendor/github.com/go-sql-driver/mysql/conncheck_test.go
generated
vendored
@ -6,7 +6,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/.
|
||||
|
||||
// +build go1.10,!windows
|
||||
// +build linux darwin dragonfly freebsd netbsd openbsd solaris illumos
|
||||
|
||||
package mysql
|
||||
|
||||
|
13
vendor/github.com/go-sql-driver/mysql/connection.go
generated
vendored
13
vendor/github.com/go-sql-driver/mysql/connection.go
generated
vendored
@ -12,6 +12,7 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
@ -154,7 +155,9 @@ func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
|
||||
// Send command
|
||||
err := mc.writeCommandPacketStr(comStmtPrepare, query)
|
||||
if err != nil {
|
||||
return nil, mc.markBadConn(err)
|
||||
// STMT_PREPARE is safe to retry. So we can return ErrBadConn here.
|
||||
errLog.Print(err)
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
|
||||
stmt := &mysqlStmt{
|
||||
@ -269,6 +272,14 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin
|
||||
}
|
||||
buf = append(buf, '\'')
|
||||
}
|
||||
case json.RawMessage:
|
||||
buf = append(buf, '\'')
|
||||
if mc.status&statusNoBackslashEscapes == 0 {
|
||||
buf = escapeBytesBackslash(buf, v)
|
||||
} else {
|
||||
buf = escapeBytesQuotes(buf, v)
|
||||
}
|
||||
buf = append(buf, '\'')
|
||||
case []byte:
|
||||
if v == nil {
|
||||
buf = append(buf, "NULL"...)
|
||||
|
28
vendor/github.com/go-sql-driver/mysql/connection_test.go
generated
vendored
28
vendor/github.com/go-sql-driver/mysql/connection_test.go
generated
vendored
@ -11,6 +11,7 @@ package mysql
|
||||
import (
|
||||
"context"
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net"
|
||||
"testing"
|
||||
@ -36,6 +37,33 @@ func TestInterpolateParams(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterpolateParamsJSONRawMessage(t *testing.T) {
|
||||
mc := &mysqlConn{
|
||||
buf: newBuffer(nil),
|
||||
maxAllowedPacket: maxPacketSize,
|
||||
cfg: &Config{
|
||||
InterpolateParams: true,
|
||||
},
|
||||
}
|
||||
|
||||
buf, err := json.Marshal(struct {
|
||||
Value int `json:"value"`
|
||||
}{Value: 42})
|
||||
if err != nil {
|
||||
t.Errorf("Expected err=nil, got %#v", err)
|
||||
return
|
||||
}
|
||||
q, err := mc.interpolateParams("SELECT ?", []driver.Value{json.RawMessage(buf)})
|
||||
if err != nil {
|
||||
t.Errorf("Expected err=nil, got %#v", err)
|
||||
return
|
||||
}
|
||||
expected := `SELECT '{\"value\":42}'`
|
||||
if q != expected {
|
||||
t.Errorf("Expected: %q\nGot: %q", expected, q)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterpolateParamsTooManyPlaceholders(t *testing.T) {
|
||||
mc := &mysqlConn{
|
||||
buf: newBuffer(nil),
|
||||
|
13
vendor/github.com/go-sql-driver/mysql/connector.go
generated
vendored
13
vendor/github.com/go-sql-driver/mysql/connector.go
generated
vendored
@ -37,17 +37,19 @@ func (c *connector) Connect(ctx context.Context) (driver.Conn, error) {
|
||||
dial, ok := dials[mc.cfg.Net]
|
||||
dialsLock.RUnlock()
|
||||
if ok {
|
||||
mc.netConn, err = dial(ctx, mc.cfg.Addr)
|
||||
dctx := ctx
|
||||
if mc.cfg.Timeout > 0 {
|
||||
var cancel context.CancelFunc
|
||||
dctx, cancel = context.WithTimeout(ctx, c.cfg.Timeout)
|
||||
defer cancel()
|
||||
}
|
||||
mc.netConn, err = dial(dctx, mc.cfg.Addr)
|
||||
} else {
|
||||
nd := net.Dialer{Timeout: mc.cfg.Timeout}
|
||||
mc.netConn, err = nd.DialContext(ctx, mc.cfg.Net, mc.cfg.Addr)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
|
||||
errLog.Print("net.Error from Dial()': ", nerr.Error())
|
||||
return nil, driver.ErrBadConn
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -64,6 +66,7 @@ func (c *connector) Connect(ctx context.Context) (driver.Conn, error) {
|
||||
// Call startWatcher for context support (From Go 1.8)
|
||||
mc.startWatcher()
|
||||
if err := mc.watchCancel(ctx); err != nil {
|
||||
mc.cleanup()
|
||||
return nil, err
|
||||
}
|
||||
defer mc.finish()
|
||||
|
30
vendor/github.com/go-sql-driver/mysql/connector_test.go
generated
vendored
Normal file
30
vendor/github.com/go-sql-driver/mysql/connector_test.go
generated
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestConnectorReturnsTimeout(t *testing.T) {
|
||||
connector := &connector{&Config{
|
||||
Net: "tcp",
|
||||
Addr: "1.1.1.1:1234",
|
||||
Timeout: 10 * time.Millisecond,
|
||||
}}
|
||||
|
||||
_, err := connector.Connect(context.Background())
|
||||
if err == nil {
|
||||
t.Fatal("error expected")
|
||||
}
|
||||
|
||||
if nerr, ok := err.(*net.OpError); ok {
|
||||
expected := "dial tcp 1.1.1.1:1234: i/o timeout"
|
||||
if nerr.Error() != expected {
|
||||
t.Fatalf("expected %q, got %q", expected, nerr.Error())
|
||||
}
|
||||
} else {
|
||||
t.Fatalf("expected %T, got %T", nerr, err)
|
||||
}
|
||||
}
|
22
vendor/github.com/go-sql-driver/mysql/driver.go
generated
vendored
22
vendor/github.com/go-sql-driver/mysql/driver.go
generated
vendored
@ -83,3 +83,25 @@ func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
|
||||
func init() {
|
||||
sql.Register("mysql", &MySQLDriver{})
|
||||
}
|
||||
|
||||
// NewConnector returns new driver.Connector.
|
||||
func NewConnector(cfg *Config) (driver.Connector, error) {
|
||||
cfg = cfg.Clone()
|
||||
// normalize the contents of cfg so calls to NewConnector have the same
|
||||
// behavior as MySQLDriver.OpenConnector
|
||||
if err := cfg.normalize(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &connector{cfg: cfg}, nil
|
||||
}
|
||||
|
||||
// OpenConnector implements driver.DriverContext.
|
||||
func (d MySQLDriver) OpenConnector(dsn string) (driver.Connector, error) {
|
||||
cfg, err := ParseDSN(dsn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &connector{
|
||||
cfg: cfg,
|
||||
}, nil
|
||||
}
|
||||
|
37
vendor/github.com/go-sql-driver/mysql/driver_go110.go
generated
vendored
37
vendor/github.com/go-sql-driver/mysql/driver_go110.go
generated
vendored
@ -1,37 +0,0 @@
|
||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2018 The Go-MySQL-Driver Authors. All rights reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// +build go1.10
|
||||
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
)
|
||||
|
||||
// NewConnector returns new driver.Connector.
|
||||
func NewConnector(cfg *Config) (driver.Connector, error) {
|
||||
cfg = cfg.Clone()
|
||||
// normalize the contents of cfg so calls to NewConnector have the same
|
||||
// behavior as MySQLDriver.OpenConnector
|
||||
if err := cfg.normalize(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &connector{cfg: cfg}, nil
|
||||
}
|
||||
|
||||
// OpenConnector implements driver.DriverContext.
|
||||
func (d MySQLDriver) OpenConnector(dsn string) (driver.Connector, error) {
|
||||
cfg, err := ParseDSN(dsn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &connector{
|
||||
cfg: cfg,
|
||||
}, nil
|
||||
}
|
137
vendor/github.com/go-sql-driver/mysql/driver_go110_test.go
generated
vendored
137
vendor/github.com/go-sql-driver/mysql/driver_go110_test.go
generated
vendored
@ -1,137 +0,0 @@
|
||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2018 The Go-MySQL-Driver Authors. All rights reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// +build go1.10
|
||||
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ driver.DriverContext = &MySQLDriver{}
|
||||
|
||||
type dialCtxKey struct{}
|
||||
|
||||
func TestConnectorObeysDialTimeouts(t *testing.T) {
|
||||
if !available {
|
||||
t.Skipf("MySQL server not running on %s", netAddr)
|
||||
}
|
||||
|
||||
RegisterDialContext("dialctxtest", func(ctx context.Context, addr string) (net.Conn, error) {
|
||||
var d net.Dialer
|
||||
if !ctx.Value(dialCtxKey{}).(bool) {
|
||||
return nil, fmt.Errorf("test error: query context is not propagated to our dialer")
|
||||
}
|
||||
return d.DialContext(ctx, prot, addr)
|
||||
})
|
||||
|
||||
db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@dialctxtest(%s)/%s?timeout=30s", user, pass, addr, dbname))
|
||||
if err != nil {
|
||||
t.Fatalf("error connecting: %s", err.Error())
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
ctx := context.WithValue(context.Background(), dialCtxKey{}, true)
|
||||
|
||||
_, err = db.ExecContext(ctx, "DO 1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func configForTests(t *testing.T) *Config {
|
||||
if !available {
|
||||
t.Skipf("MySQL server not running on %s", netAddr)
|
||||
}
|
||||
|
||||
mycnf := NewConfig()
|
||||
mycnf.User = user
|
||||
mycnf.Passwd = pass
|
||||
mycnf.Addr = addr
|
||||
mycnf.Net = prot
|
||||
mycnf.DBName = dbname
|
||||
return mycnf
|
||||
}
|
||||
|
||||
func TestNewConnector(t *testing.T) {
|
||||
mycnf := configForTests(t)
|
||||
conn, err := NewConnector(mycnf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
db := sql.OpenDB(conn)
|
||||
defer db.Close()
|
||||
|
||||
if err := db.Ping(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
type slowConnection struct {
|
||||
net.Conn
|
||||
slowdown time.Duration
|
||||
}
|
||||
|
||||
func (sc *slowConnection) Read(b []byte) (int, error) {
|
||||
time.Sleep(sc.slowdown)
|
||||
return sc.Conn.Read(b)
|
||||
}
|
||||
|
||||
type connectorHijack struct {
|
||||
driver.Connector
|
||||
connErr error
|
||||
}
|
||||
|
||||
func (cw *connectorHijack) Connect(ctx context.Context) (driver.Conn, error) {
|
||||
var conn driver.Conn
|
||||
conn, cw.connErr = cw.Connector.Connect(ctx)
|
||||
return conn, cw.connErr
|
||||
}
|
||||
|
||||
func TestConnectorTimeoutsDuringOpen(t *testing.T) {
|
||||
RegisterDialContext("slowconn", func(ctx context.Context, addr string) (net.Conn, error) {
|
||||
var d net.Dialer
|
||||
conn, err := d.DialContext(ctx, prot, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &slowConnection{Conn: conn, slowdown: 100 * time.Millisecond}, nil
|
||||
})
|
||||
|
||||
mycnf := configForTests(t)
|
||||
mycnf.Net = "slowconn"
|
||||
|
||||
conn, err := NewConnector(mycnf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
hijack := &connectorHijack{Connector: conn}
|
||||
|
||||
db := sql.OpenDB(hijack)
|
||||
defer db.Close()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
_, err = db.ExecContext(ctx, "DO 1")
|
||||
if err != context.DeadlineExceeded {
|
||||
t.Fatalf("ExecContext should have timed out")
|
||||
}
|
||||
if hijack.connErr != context.DeadlineExceeded {
|
||||
t.Fatalf("(*Connector).Connect should have timed out")
|
||||
}
|
||||
}
|
171
vendor/github.com/go-sql-driver/mysql/driver_test.go
generated
vendored
171
vendor/github.com/go-sql-driver/mysql/driver_test.go
generated
vendored
@ -1874,7 +1874,7 @@ func TestDialNonRetryableNetErr(t *testing.T) {
|
||||
|
||||
func TestDialTemporaryNetErr(t *testing.T) {
|
||||
testErr := netErrorMock{temporary: true}
|
||||
testDialError(t, testErr, driver.ErrBadConn)
|
||||
testDialError(t, testErr, testErr)
|
||||
}
|
||||
|
||||
// Tests custom dial functions
|
||||
@ -2994,3 +2994,172 @@ func TestRawBytesAreNotModified(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
var _ driver.DriverContext = &MySQLDriver{}
|
||||
|
||||
type dialCtxKey struct{}
|
||||
|
||||
func TestConnectorObeysDialTimeouts(t *testing.T) {
|
||||
if !available {
|
||||
t.Skipf("MySQL server not running on %s", netAddr)
|
||||
}
|
||||
|
||||
RegisterDialContext("dialctxtest", func(ctx context.Context, addr string) (net.Conn, error) {
|
||||
var d net.Dialer
|
||||
if !ctx.Value(dialCtxKey{}).(bool) {
|
||||
return nil, fmt.Errorf("test error: query context is not propagated to our dialer")
|
||||
}
|
||||
return d.DialContext(ctx, prot, addr)
|
||||
})
|
||||
|
||||
db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@dialctxtest(%s)/%s?timeout=30s", user, pass, addr, dbname))
|
||||
if err != nil {
|
||||
t.Fatalf("error connecting: %s", err.Error())
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
ctx := context.WithValue(context.Background(), dialCtxKey{}, true)
|
||||
|
||||
_, err = db.ExecContext(ctx, "DO 1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func configForTests(t *testing.T) *Config {
|
||||
if !available {
|
||||
t.Skipf("MySQL server not running on %s", netAddr)
|
||||
}
|
||||
|
||||
mycnf := NewConfig()
|
||||
mycnf.User = user
|
||||
mycnf.Passwd = pass
|
||||
mycnf.Addr = addr
|
||||
mycnf.Net = prot
|
||||
mycnf.DBName = dbname
|
||||
return mycnf
|
||||
}
|
||||
|
||||
func TestNewConnector(t *testing.T) {
|
||||
mycnf := configForTests(t)
|
||||
conn, err := NewConnector(mycnf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
db := sql.OpenDB(conn)
|
||||
defer db.Close()
|
||||
|
||||
if err := db.Ping(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
type slowConnection struct {
|
||||
net.Conn
|
||||
slowdown time.Duration
|
||||
}
|
||||
|
||||
func (sc *slowConnection) Read(b []byte) (int, error) {
|
||||
time.Sleep(sc.slowdown)
|
||||
return sc.Conn.Read(b)
|
||||
}
|
||||
|
||||
type connectorHijack struct {
|
||||
driver.Connector
|
||||
connErr error
|
||||
}
|
||||
|
||||
func (cw *connectorHijack) Connect(ctx context.Context) (driver.Conn, error) {
|
||||
var conn driver.Conn
|
||||
conn, cw.connErr = cw.Connector.Connect(ctx)
|
||||
return conn, cw.connErr
|
||||
}
|
||||
|
||||
func TestConnectorTimeoutsDuringOpen(t *testing.T) {
|
||||
RegisterDialContext("slowconn", func(ctx context.Context, addr string) (net.Conn, error) {
|
||||
var d net.Dialer
|
||||
conn, err := d.DialContext(ctx, prot, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &slowConnection{Conn: conn, slowdown: 100 * time.Millisecond}, nil
|
||||
})
|
||||
|
||||
mycnf := configForTests(t)
|
||||
mycnf.Net = "slowconn"
|
||||
|
||||
conn, err := NewConnector(mycnf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
hijack := &connectorHijack{Connector: conn}
|
||||
|
||||
db := sql.OpenDB(hijack)
|
||||
defer db.Close()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
_, err = db.ExecContext(ctx, "DO 1")
|
||||
if err != context.DeadlineExceeded {
|
||||
t.Fatalf("ExecContext should have timed out")
|
||||
}
|
||||
if hijack.connErr != context.DeadlineExceeded {
|
||||
t.Fatalf("(*Connector).Connect should have timed out")
|
||||
}
|
||||
}
|
||||
|
||||
// A connection which can only be closed.
|
||||
type dummyConnection struct {
|
||||
net.Conn
|
||||
closed bool
|
||||
}
|
||||
|
||||
func (d *dummyConnection) Close() error {
|
||||
d.closed = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestConnectorTimeoutsWatchCancel(t *testing.T) {
|
||||
var (
|
||||
cancel func() // Used to cancel the context just after connecting.
|
||||
created *dummyConnection // The created connection.
|
||||
)
|
||||
|
||||
RegisterDialContext("TestConnectorTimeoutsWatchCancel", func(ctx context.Context, addr string) (net.Conn, error) {
|
||||
// Canceling at this time triggers the watchCancel error branch in Connect().
|
||||
cancel()
|
||||
created = &dummyConnection{}
|
||||
return created, nil
|
||||
})
|
||||
|
||||
mycnf := NewConfig()
|
||||
mycnf.User = "root"
|
||||
mycnf.Addr = "foo"
|
||||
mycnf.Net = "TestConnectorTimeoutsWatchCancel"
|
||||
|
||||
conn, err := NewConnector(mycnf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
db := sql.OpenDB(conn)
|
||||
defer db.Close()
|
||||
|
||||
var ctx context.Context
|
||||
ctx, cancel = context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
if _, err := db.Conn(ctx); err != context.Canceled {
|
||||
t.Errorf("got %v, want context.Canceled", err)
|
||||
}
|
||||
|
||||
if created == nil {
|
||||
t.Fatal("no connection created")
|
||||
}
|
||||
if !created.closed {
|
||||
t.Errorf("connection not closed")
|
||||
}
|
||||
}
|
||||
|
164
vendor/github.com/go-sql-driver/mysql/dsn.go
generated
vendored
164
vendor/github.com/go-sql-driver/mysql/dsn.go
generated
vendored
@ -55,6 +55,7 @@ type Config struct {
|
||||
AllowCleartextPasswords bool // Allows the cleartext client side plugin
|
||||
AllowNativePasswords bool // Allows the native password authentication method
|
||||
AllowOldPasswords bool // Allows the old insecure password method
|
||||
CheckConnLiveness bool // Check connections for liveness before using them
|
||||
ClientFoundRows bool // Return number of matching rows instead of rows changed
|
||||
ColumnsWithAlias bool // Prepend table alias to column names
|
||||
InterpolateParams bool // Interpolate placeholders into query string
|
||||
@ -70,6 +71,7 @@ func NewConfig() *Config {
|
||||
Loc: time.UTC,
|
||||
MaxAllowedPacket: defaultMaxAllowedPacket,
|
||||
AllowNativePasswords: true,
|
||||
CheckConnLiveness: true,
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,6 +150,19 @@ func (cfg *Config) normalize() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeDSNParam(buf *bytes.Buffer, hasParam *bool, name, value string) {
|
||||
buf.Grow(1 + len(name) + 1 + len(value))
|
||||
if !*hasParam {
|
||||
*hasParam = true
|
||||
buf.WriteByte('?')
|
||||
} else {
|
||||
buf.WriteByte('&')
|
||||
}
|
||||
buf.WriteString(name)
|
||||
buf.WriteByte('=')
|
||||
buf.WriteString(value)
|
||||
}
|
||||
|
||||
// FormatDSN formats the given Config into a DSN string which can be passed to
|
||||
// the driver.
|
||||
func (cfg *Config) FormatDSN() string {
|
||||
@ -186,165 +201,75 @@ func (cfg *Config) FormatDSN() string {
|
||||
}
|
||||
|
||||
if cfg.AllowCleartextPasswords {
|
||||
if hasParam {
|
||||
buf.WriteString("&allowCleartextPasswords=true")
|
||||
} else {
|
||||
hasParam = true
|
||||
buf.WriteString("?allowCleartextPasswords=true")
|
||||
}
|
||||
writeDSNParam(&buf, &hasParam, "allowCleartextPasswords", "true")
|
||||
}
|
||||
|
||||
if !cfg.AllowNativePasswords {
|
||||
if hasParam {
|
||||
buf.WriteString("&allowNativePasswords=false")
|
||||
} else {
|
||||
hasParam = true
|
||||
buf.WriteString("?allowNativePasswords=false")
|
||||
}
|
||||
writeDSNParam(&buf, &hasParam, "allowNativePasswords", "false")
|
||||
}
|
||||
|
||||
if cfg.AllowOldPasswords {
|
||||
if hasParam {
|
||||
buf.WriteString("&allowOldPasswords=true")
|
||||
} else {
|
||||
hasParam = true
|
||||
buf.WriteString("?allowOldPasswords=true")
|
||||
writeDSNParam(&buf, &hasParam, "allowOldPasswords", "true")
|
||||
}
|
||||
|
||||
if !cfg.CheckConnLiveness {
|
||||
writeDSNParam(&buf, &hasParam, "checkConnLiveness", "false")
|
||||
}
|
||||
|
||||
if cfg.ClientFoundRows {
|
||||
if hasParam {
|
||||
buf.WriteString("&clientFoundRows=true")
|
||||
} else {
|
||||
hasParam = true
|
||||
buf.WriteString("?clientFoundRows=true")
|
||||
}
|
||||
writeDSNParam(&buf, &hasParam, "clientFoundRows", "true")
|
||||
}
|
||||
|
||||
if col := cfg.Collation; col != defaultCollation && len(col) > 0 {
|
||||
if hasParam {
|
||||
buf.WriteString("&collation=")
|
||||
} else {
|
||||
hasParam = true
|
||||
buf.WriteString("?collation=")
|
||||
}
|
||||
buf.WriteString(col)
|
||||
writeDSNParam(&buf, &hasParam, "collation", col)
|
||||
}
|
||||
|
||||
if cfg.ColumnsWithAlias {
|
||||
if hasParam {
|
||||
buf.WriteString("&columnsWithAlias=true")
|
||||
} else {
|
||||
hasParam = true
|
||||
buf.WriteString("?columnsWithAlias=true")
|
||||
}
|
||||
writeDSNParam(&buf, &hasParam, "columnsWithAlias", "true")
|
||||
}
|
||||
|
||||
if cfg.InterpolateParams {
|
||||
if hasParam {
|
||||
buf.WriteString("&interpolateParams=true")
|
||||
} else {
|
||||
hasParam = true
|
||||
buf.WriteString("?interpolateParams=true")
|
||||
}
|
||||
writeDSNParam(&buf, &hasParam, "interpolateParams", "true")
|
||||
}
|
||||
|
||||
if cfg.Loc != time.UTC && cfg.Loc != nil {
|
||||
if hasParam {
|
||||
buf.WriteString("&loc=")
|
||||
} else {
|
||||
hasParam = true
|
||||
buf.WriteString("?loc=")
|
||||
}
|
||||
buf.WriteString(url.QueryEscape(cfg.Loc.String()))
|
||||
writeDSNParam(&buf, &hasParam, "loc", url.QueryEscape(cfg.Loc.String()))
|
||||
}
|
||||
|
||||
if cfg.MultiStatements {
|
||||
if hasParam {
|
||||
buf.WriteString("&multiStatements=true")
|
||||
} else {
|
||||
hasParam = true
|
||||
buf.WriteString("?multiStatements=true")
|
||||
}
|
||||
writeDSNParam(&buf, &hasParam, "multiStatements", "true")
|
||||
}
|
||||
|
||||
if cfg.ParseTime {
|
||||
if hasParam {
|
||||
buf.WriteString("&parseTime=true")
|
||||
} else {
|
||||
hasParam = true
|
||||
buf.WriteString("?parseTime=true")
|
||||
}
|
||||
writeDSNParam(&buf, &hasParam, "parseTime", "true")
|
||||
}
|
||||
|
||||
if cfg.ReadTimeout > 0 {
|
||||
if hasParam {
|
||||
buf.WriteString("&readTimeout=")
|
||||
} else {
|
||||
hasParam = true
|
||||
buf.WriteString("?readTimeout=")
|
||||
}
|
||||
buf.WriteString(cfg.ReadTimeout.String())
|
||||
writeDSNParam(&buf, &hasParam, "readTimeout", cfg.ReadTimeout.String())
|
||||
}
|
||||
|
||||
if cfg.RejectReadOnly {
|
||||
if hasParam {
|
||||
buf.WriteString("&rejectReadOnly=true")
|
||||
} else {
|
||||
hasParam = true
|
||||
buf.WriteString("?rejectReadOnly=true")
|
||||
}
|
||||
writeDSNParam(&buf, &hasParam, "rejectReadOnly", "true")
|
||||
}
|
||||
|
||||
if len(cfg.ServerPubKey) > 0 {
|
||||
if hasParam {
|
||||
buf.WriteString("&serverPubKey=")
|
||||
} else {
|
||||
hasParam = true
|
||||
buf.WriteString("?serverPubKey=")
|
||||
}
|
||||
buf.WriteString(url.QueryEscape(cfg.ServerPubKey))
|
||||
writeDSNParam(&buf, &hasParam, "serverPubKey", url.QueryEscape(cfg.ServerPubKey))
|
||||
}
|
||||
|
||||
if cfg.Timeout > 0 {
|
||||
if hasParam {
|
||||
buf.WriteString("&timeout=")
|
||||
} else {
|
||||
hasParam = true
|
||||
buf.WriteString("?timeout=")
|
||||
}
|
||||
buf.WriteString(cfg.Timeout.String())
|
||||
writeDSNParam(&buf, &hasParam, "timeout", cfg.Timeout.String())
|
||||
}
|
||||
|
||||
if len(cfg.TLSConfig) > 0 {
|
||||
if hasParam {
|
||||
buf.WriteString("&tls=")
|
||||
} else {
|
||||
hasParam = true
|
||||
buf.WriteString("?tls=")
|
||||
}
|
||||
buf.WriteString(url.QueryEscape(cfg.TLSConfig))
|
||||
writeDSNParam(&buf, &hasParam, "tls", url.QueryEscape(cfg.TLSConfig))
|
||||
}
|
||||
|
||||
if cfg.WriteTimeout > 0 {
|
||||
if hasParam {
|
||||
buf.WriteString("&writeTimeout=")
|
||||
} else {
|
||||
hasParam = true
|
||||
buf.WriteString("?writeTimeout=")
|
||||
}
|
||||
buf.WriteString(cfg.WriteTimeout.String())
|
||||
writeDSNParam(&buf, &hasParam, "writeTimeout", cfg.WriteTimeout.String())
|
||||
}
|
||||
|
||||
if cfg.MaxAllowedPacket != defaultMaxAllowedPacket {
|
||||
if hasParam {
|
||||
buf.WriteString("&maxAllowedPacket=")
|
||||
} else {
|
||||
hasParam = true
|
||||
buf.WriteString("?maxAllowedPacket=")
|
||||
}
|
||||
buf.WriteString(strconv.Itoa(cfg.MaxAllowedPacket))
|
||||
|
||||
writeDSNParam(&buf, &hasParam, "maxAllowedPacket", strconv.Itoa(cfg.MaxAllowedPacket))
|
||||
}
|
||||
|
||||
// other params
|
||||
@ -355,16 +280,7 @@ func (cfg *Config) FormatDSN() string {
|
||||
}
|
||||
sort.Strings(params)
|
||||
for _, param := range params {
|
||||
if hasParam {
|
||||
buf.WriteByte('&')
|
||||
} else {
|
||||
hasParam = true
|
||||
buf.WriteByte('?')
|
||||
}
|
||||
|
||||
buf.WriteString(param)
|
||||
buf.WriteByte('=')
|
||||
buf.WriteString(url.QueryEscape(cfg.Params[param]))
|
||||
writeDSNParam(&buf, &hasParam, param, url.QueryEscape(cfg.Params[param]))
|
||||
}
|
||||
}
|
||||
|
||||
@ -491,6 +407,14 @@ func parseDSNParams(cfg *Config, params string) (err error) {
|
||||
return errors.New("invalid bool value: " + value)
|
||||
}
|
||||
|
||||
// Check connections for Liveness before using them
|
||||
case "checkConnLiveness":
|
||||
var isBool bool
|
||||
cfg.CheckConnLiveness, isBool = readBool(value)
|
||||
if !isBool {
|
||||
return errors.New("invalid bool value: " + value)
|
||||
}
|
||||
|
||||
// Switch "rowsAffected" mode
|
||||
case "clientFoundRows":
|
||||
var isBool bool
|
||||
|
36
vendor/github.com/go-sql-driver/mysql/dsn_test.go
generated
vendored
36
vendor/github.com/go-sql-driver/mysql/dsn_test.go
generated
vendored
@ -22,55 +22,55 @@ var testDSNs = []struct {
|
||||
out *Config
|
||||
}{{
|
||||
"username:password@protocol(address)/dbname?param=value",
|
||||
&Config{User: "username", Passwd: "password", Net: "protocol", Addr: "address", DBName: "dbname", Params: map[string]string{"param": "value"}, Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true},
|
||||
&Config{User: "username", Passwd: "password", Net: "protocol", Addr: "address", DBName: "dbname", Params: map[string]string{"param": "value"}, Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true, CheckConnLiveness: true},
|
||||
}, {
|
||||
"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true",
|
||||
&Config{User: "username", Passwd: "password", Net: "protocol", Addr: "address", DBName: "dbname", Params: map[string]string{"param": "value"}, Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true, ColumnsWithAlias: true},
|
||||
&Config{User: "username", Passwd: "password", Net: "protocol", Addr: "address", DBName: "dbname", Params: map[string]string{"param": "value"}, Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true, CheckConnLiveness: true, ColumnsWithAlias: true},
|
||||
}, {
|
||||
"username:password@protocol(address)/dbname?param=value&columnsWithAlias=true&multiStatements=true",
|
||||
&Config{User: "username", Passwd: "password", Net: "protocol", Addr: "address", DBName: "dbname", Params: map[string]string{"param": "value"}, Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true, ColumnsWithAlias: true, MultiStatements: true},
|
||||
&Config{User: "username", Passwd: "password", Net: "protocol", Addr: "address", DBName: "dbname", Params: map[string]string{"param": "value"}, Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true, CheckConnLiveness: true, ColumnsWithAlias: true, MultiStatements: true},
|
||||
}, {
|
||||
"user@unix(/path/to/socket)/dbname?charset=utf8",
|
||||
&Config{User: "user", Net: "unix", Addr: "/path/to/socket", DBName: "dbname", Params: map[string]string{"charset": "utf8"}, Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true},
|
||||
&Config{User: "user", Net: "unix", Addr: "/path/to/socket", DBName: "dbname", Params: map[string]string{"charset": "utf8"}, Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true, CheckConnLiveness: true},
|
||||
}, {
|
||||
"user:password@tcp(localhost:5555)/dbname?charset=utf8&tls=true",
|
||||
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "localhost:5555", DBName: "dbname", Params: map[string]string{"charset": "utf8"}, Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true, TLSConfig: "true"},
|
||||
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "localhost:5555", DBName: "dbname", Params: map[string]string{"charset": "utf8"}, Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true, CheckConnLiveness: true, TLSConfig: "true"},
|
||||
}, {
|
||||
"user:password@tcp(localhost:5555)/dbname?charset=utf8mb4,utf8&tls=skip-verify",
|
||||
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "localhost:5555", DBName: "dbname", Params: map[string]string{"charset": "utf8mb4,utf8"}, Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true, TLSConfig: "skip-verify"},
|
||||
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "localhost:5555", DBName: "dbname", Params: map[string]string{"charset": "utf8mb4,utf8"}, Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true, CheckConnLiveness: true, TLSConfig: "skip-verify"},
|
||||
}, {
|
||||
"user:password@/dbname?loc=UTC&timeout=30s&readTimeout=1s&writeTimeout=1s&allowAllFiles=1&clientFoundRows=true&allowOldPasswords=TRUE&collation=utf8mb4_unicode_ci&maxAllowedPacket=16777216&tls=false&allowCleartextPasswords=true&parseTime=true&rejectReadOnly=true",
|
||||
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Collation: "utf8mb4_unicode_ci", Loc: time.UTC, TLSConfig: "false", AllowCleartextPasswords: true, AllowNativePasswords: true, Timeout: 30 * time.Second, ReadTimeout: time.Second, WriteTimeout: time.Second, AllowAllFiles: true, AllowOldPasswords: true, ClientFoundRows: true, MaxAllowedPacket: 16777216, ParseTime: true, RejectReadOnly: true},
|
||||
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Collation: "utf8mb4_unicode_ci", Loc: time.UTC, TLSConfig: "false", AllowCleartextPasswords: true, AllowNativePasswords: true, Timeout: 30 * time.Second, ReadTimeout: time.Second, WriteTimeout: time.Second, AllowAllFiles: true, AllowOldPasswords: true, CheckConnLiveness: true, ClientFoundRows: true, MaxAllowedPacket: 16777216, ParseTime: true, RejectReadOnly: true},
|
||||
}, {
|
||||
"user:password@/dbname?allowNativePasswords=false&maxAllowedPacket=0",
|
||||
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: 0, AllowNativePasswords: false},
|
||||
"user:password@/dbname?allowNativePasswords=false&checkConnLiveness=false&maxAllowedPacket=0",
|
||||
&Config{User: "user", Passwd: "password", Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: 0, AllowNativePasswords: false, CheckConnLiveness: false},
|
||||
}, {
|
||||
"user:p@ss(word)@tcp([de:ad:be:ef::ca:fe]:80)/dbname?loc=Local",
|
||||
&Config{User: "user", Passwd: "p@ss(word)", Net: "tcp", Addr: "[de:ad:be:ef::ca:fe]:80", DBName: "dbname", Collation: "utf8mb4_general_ci", Loc: time.Local, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true},
|
||||
&Config{User: "user", Passwd: "p@ss(word)", Net: "tcp", Addr: "[de:ad:be:ef::ca:fe]:80", DBName: "dbname", Collation: "utf8mb4_general_ci", Loc: time.Local, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true, CheckConnLiveness: true},
|
||||
}, {
|
||||
"/dbname",
|
||||
&Config{Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true},
|
||||
&Config{Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true, CheckConnLiveness: true},
|
||||
}, {
|
||||
"@/",
|
||||
&Config{Net: "tcp", Addr: "127.0.0.1:3306", Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true},
|
||||
&Config{Net: "tcp", Addr: "127.0.0.1:3306", Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true, CheckConnLiveness: true},
|
||||
}, {
|
||||
"/",
|
||||
&Config{Net: "tcp", Addr: "127.0.0.1:3306", Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true},
|
||||
&Config{Net: "tcp", Addr: "127.0.0.1:3306", Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true, CheckConnLiveness: true},
|
||||
}, {
|
||||
"",
|
||||
&Config{Net: "tcp", Addr: "127.0.0.1:3306", Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true},
|
||||
&Config{Net: "tcp", Addr: "127.0.0.1:3306", Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true, CheckConnLiveness: true},
|
||||
}, {
|
||||
"user:p@/ssword@/",
|
||||
&Config{User: "user", Passwd: "p@/ssword", Net: "tcp", Addr: "127.0.0.1:3306", Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true},
|
||||
&Config{User: "user", Passwd: "p@/ssword", Net: "tcp", Addr: "127.0.0.1:3306", Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true, CheckConnLiveness: true},
|
||||
}, {
|
||||
"unix/?arg=%2Fsome%2Fpath.ext",
|
||||
&Config{Net: "unix", Addr: "/tmp/mysql.sock", Params: map[string]string{"arg": "/some/path.ext"}, Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true},
|
||||
&Config{Net: "unix", Addr: "/tmp/mysql.sock", Params: map[string]string{"arg": "/some/path.ext"}, Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true, CheckConnLiveness: true},
|
||||
}, {
|
||||
"tcp(127.0.0.1)/dbname",
|
||||
&Config{Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true},
|
||||
&Config{Net: "tcp", Addr: "127.0.0.1:3306", DBName: "dbname", Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true, CheckConnLiveness: true},
|
||||
}, {
|
||||
"tcp(de:ad:be:ef::ca:fe)/dbname",
|
||||
&Config{Net: "tcp", Addr: "[de:ad:be:ef::ca:fe]:3306", DBName: "dbname", Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true},
|
||||
&Config{Net: "tcp", Addr: "[de:ad:be:ef::ca:fe]:3306", DBName: "dbname", Collation: "utf8mb4_general_ci", Loc: time.UTC, MaxAllowedPacket: defaultMaxAllowedPacket, AllowNativePasswords: true, CheckConnLiveness: true},
|
||||
},
|
||||
}
|
||||
|
||||
|
3
vendor/github.com/go-sql-driver/mysql/go.mod
generated
vendored
Normal file
3
vendor/github.com/go-sql-driver/mysql/go.mod
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
module github.com/go-sql-driver/mysql
|
||||
|
||||
go 1.10
|
50
vendor/github.com/go-sql-driver/mysql/nulltime.go
generated
vendored
Normal file
50
vendor/github.com/go-sql-driver/mysql/nulltime.go
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
// The value type must be time.Time or string / []byte (formatted time-string),
|
||||
// otherwise Scan fails.
|
||||
func (nt *NullTime) Scan(value interface{}) (err error) {
|
||||
if value == nil {
|
||||
nt.Time, nt.Valid = time.Time{}, false
|
||||
return
|
||||
}
|
||||
|
||||
switch v := value.(type) {
|
||||
case time.Time:
|
||||
nt.Time, nt.Valid = v, true
|
||||
return
|
||||
case []byte:
|
||||
nt.Time, err = parseDateTime(string(v), time.UTC)
|
||||
nt.Valid = (err == nil)
|
||||
return
|
||||
case string:
|
||||
nt.Time, err = parseDateTime(v, time.UTC)
|
||||
nt.Valid = (err == nil)
|
||||
return
|
||||
}
|
||||
|
||||
nt.Valid = false
|
||||
return fmt.Errorf("Can't convert %T to time.Time", value)
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (nt NullTime) Value() (driver.Value, error) {
|
||||
if !nt.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return nt.Time, nil
|
||||
}
|
31
vendor/github.com/go-sql-driver/mysql/nulltime_go113.go
generated
vendored
Normal file
31
vendor/github.com/go-sql-driver/mysql/nulltime_go113.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// +build go1.13
|
||||
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
// NullTime represents a time.Time that may be NULL.
|
||||
// NullTime implements the Scanner interface so
|
||||
// it can be used as a scan destination:
|
||||
//
|
||||
// var nt NullTime
|
||||
// err := db.QueryRow("SELECT time FROM foo WHERE id=?", id).Scan(&nt)
|
||||
// ...
|
||||
// if nt.Valid {
|
||||
// // use nt.Time
|
||||
// } else {
|
||||
// // NULL value
|
||||
// }
|
||||
//
|
||||
// This NullTime implementation is not driver-specific
|
||||
type NullTime sql.NullTime
|
34
vendor/github.com/go-sql-driver/mysql/nulltime_legacy.go
generated
vendored
Normal file
34
vendor/github.com/go-sql-driver/mysql/nulltime_legacy.go
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// +build !go1.13
|
||||
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// NullTime represents a time.Time that may be NULL.
|
||||
// NullTime implements the Scanner interface so
|
||||
// it can be used as a scan destination:
|
||||
//
|
||||
// var nt NullTime
|
||||
// err := db.QueryRow("SELECT time FROM foo WHERE id=?", id).Scan(&nt)
|
||||
// ...
|
||||
// if nt.Valid {
|
||||
// // use nt.Time
|
||||
// } else {
|
||||
// // NULL value
|
||||
// }
|
||||
//
|
||||
// This NullTime implementation is not driver-specific
|
||||
type NullTime struct {
|
||||
Time time.Time
|
||||
Valid bool // Valid is true if Time is not NULL
|
||||
}
|
62
vendor/github.com/go-sql-driver/mysql/nulltime_test.go
generated
vendored
Normal file
62
vendor/github.com/go-sql-driver/mysql/nulltime_test.go
generated
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
||||
//
|
||||
// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
// Check implementation of interfaces
|
||||
_ driver.Valuer = NullTime{}
|
||||
_ sql.Scanner = (*NullTime)(nil)
|
||||
)
|
||||
|
||||
func TestScanNullTime(t *testing.T) {
|
||||
var scanTests = []struct {
|
||||
in interface{}
|
||||
error bool
|
||||
valid bool
|
||||
time time.Time
|
||||
}{
|
||||
{tDate, false, true, tDate},
|
||||
{sDate, false, true, tDate},
|
||||
{[]byte(sDate), false, true, tDate},
|
||||
{tDateTime, false, true, tDateTime},
|
||||
{sDateTime, false, true, tDateTime},
|
||||
{[]byte(sDateTime), false, true, tDateTime},
|
||||
{tDate0, false, true, tDate0},
|
||||
{sDate0, false, true, tDate0},
|
||||
{[]byte(sDate0), false, true, tDate0},
|
||||
{sDateTime0, false, true, tDate0},
|
||||
{[]byte(sDateTime0), false, true, tDate0},
|
||||
{"", true, false, tDate0},
|
||||
{"1234", true, false, tDate0},
|
||||
{0, true, false, tDate0},
|
||||
}
|
||||
|
||||
var nt = NullTime{}
|
||||
var err error
|
||||
|
||||
for _, tst := range scanTests {
|
||||
err = nt.Scan(tst.in)
|
||||
if (err != nil) != tst.error {
|
||||
t.Errorf("%v: expected error status %t, got %t", tst.in, tst.error, (err != nil))
|
||||
}
|
||||
if nt.Valid != tst.valid {
|
||||
t.Errorf("%v: expected valid status %t, got %t", tst.in, tst.valid, nt.Valid)
|
||||
}
|
||||
if nt.Time != tst.time {
|
||||
t.Errorf("%v: expected time %v, got %v", tst.in, tst.time, nt.Time)
|
||||
}
|
||||
}
|
||||
}
|
2
vendor/github.com/go-sql-driver/mysql/packets.go
generated
vendored
2
vendor/github.com/go-sql-driver/mysql/packets.go
generated
vendored
@ -115,7 +115,7 @@ func (mc *mysqlConn) writePacket(data []byte) error {
|
||||
if mc.cfg.ReadTimeout != 0 {
|
||||
err = conn.SetReadDeadline(time.Time{})
|
||||
}
|
||||
if err == nil {
|
||||
if err == nil && mc.cfg.CheckConnLiveness {
|
||||
err = connCheck(conn)
|
||||
}
|
||||
if err != nil {
|
||||
|
54
vendor/github.com/go-sql-driver/mysql/utils.go
generated
vendored
54
vendor/github.com/go-sql-driver/mysql/utils.go
generated
vendored
@ -106,60 +106,6 @@ func readBool(input string) (value bool, valid bool) {
|
||||
* Time related utils *
|
||||
******************************************************************************/
|
||||
|
||||
// NullTime represents a time.Time that may be NULL.
|
||||
// NullTime implements the Scanner interface so
|
||||
// it can be used as a scan destination:
|
||||
//
|
||||
// var nt NullTime
|
||||
// err := db.QueryRow("SELECT time FROM foo WHERE id=?", id).Scan(&nt)
|
||||
// ...
|
||||
// if nt.Valid {
|
||||
// // use nt.Time
|
||||
// } else {
|
||||
// // NULL value
|
||||
// }
|
||||
//
|
||||
// This NullTime implementation is not driver-specific
|
||||
type NullTime struct {
|
||||
Time time.Time
|
||||
Valid bool // Valid is true if Time is not NULL
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
// The value type must be time.Time or string / []byte (formatted time-string),
|
||||
// otherwise Scan fails.
|
||||
func (nt *NullTime) Scan(value interface{}) (err error) {
|
||||
if value == nil {
|
||||
nt.Time, nt.Valid = time.Time{}, false
|
||||
return
|
||||
}
|
||||
|
||||
switch v := value.(type) {
|
||||
case time.Time:
|
||||
nt.Time, nt.Valid = v, true
|
||||
return
|
||||
case []byte:
|
||||
nt.Time, err = parseDateTime(string(v), time.UTC)
|
||||
nt.Valid = (err == nil)
|
||||
return
|
||||
case string:
|
||||
nt.Time, err = parseDateTime(v, time.UTC)
|
||||
nt.Valid = (err == nil)
|
||||
return
|
||||
}
|
||||
|
||||
nt.Valid = false
|
||||
return fmt.Errorf("Can't convert %T to time.Time", value)
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (nt NullTime) Value() (driver.Value, error) {
|
||||
if !nt.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return nt.Time, nil
|
||||
}
|
||||
|
||||
func parseDateTime(str string, loc *time.Location) (t time.Time, err error) {
|
||||
base := "0000-00-00 00:00:00.0000000"
|
||||
switch len(str) {
|
||||
|
41
vendor/github.com/go-sql-driver/mysql/utils_test.go
generated
vendored
41
vendor/github.com/go-sql-driver/mysql/utils_test.go
generated
vendored
@ -14,49 +14,8 @@ import (
|
||||
"database/sql/driver"
|
||||
"encoding/binary"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestScanNullTime(t *testing.T) {
|
||||
var scanTests = []struct {
|
||||
in interface{}
|
||||
error bool
|
||||
valid bool
|
||||
time time.Time
|
||||
}{
|
||||
{tDate, false, true, tDate},
|
||||
{sDate, false, true, tDate},
|
||||
{[]byte(sDate), false, true, tDate},
|
||||
{tDateTime, false, true, tDateTime},
|
||||
{sDateTime, false, true, tDateTime},
|
||||
{[]byte(sDateTime), false, true, tDateTime},
|
||||
{tDate0, false, true, tDate0},
|
||||
{sDate0, false, true, tDate0},
|
||||
{[]byte(sDate0), false, true, tDate0},
|
||||
{sDateTime0, false, true, tDate0},
|
||||
{[]byte(sDateTime0), false, true, tDate0},
|
||||
{"", true, false, tDate0},
|
||||
{"1234", true, false, tDate0},
|
||||
{0, true, false, tDate0},
|
||||
}
|
||||
|
||||
var nt = NullTime{}
|
||||
var err error
|
||||
|
||||
for _, tst := range scanTests {
|
||||
err = nt.Scan(tst.in)
|
||||
if (err != nil) != tst.error {
|
||||
t.Errorf("%v: expected error status %t, got %t", tst.in, tst.error, (err != nil))
|
||||
}
|
||||
if nt.Valid != tst.valid {
|
||||
t.Errorf("%v: expected valid status %t, got %t", tst.in, tst.valid, nt.Valid)
|
||||
}
|
||||
if nt.Time != tst.time {
|
||||
t.Errorf("%v: expected time %v, got %v", tst.in, tst.time, nt.Time)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLengthEncodedInteger(t *testing.T) {
|
||||
var integerTests = []struct {
|
||||
num uint64
|
||||
|
Loading…
Reference in New Issue
Block a user