parent
							
								
									280ebcbf7c
								
							
						
					
					
						commit
						9d4c1ddfa1
					
				
					 22 changed files with 1813 additions and 614 deletions
				
			
		
							
								
								
									
										4
									
								
								Gopkg.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								Gopkg.lock
									
									
									
										generated
									
									
									
								
							|  | @ -294,7 +294,7 @@ | |||
| [[projects]] | ||||
|   name = "github.com/go-sql-driver/mysql" | ||||
|   packages = ["."] | ||||
|   revision = "ce924a41eea897745442daaa1739089b0f3f561d" | ||||
|   revision = "d523deb1b23d913de5bdada721a6071e71283618" | ||||
| 
 | ||||
| [[projects]] | ||||
|   name = "github.com/go-xorm/builder" | ||||
|  | @ -873,6 +873,6 @@ | |||
| [solve-meta] | ||||
|   analyzer-name = "dep" | ||||
|   analyzer-version = 1 | ||||
|   inputs-digest = "036b8c882671cf8d2c5e2fdbe53b1bdfbd39f7ebd7765bd50276c7c4ecf16687" | ||||
|   inputs-digest = "96c83a3502bd50c5ca8e4d9b4145172267630270e587c79b7253156725eeb9b8" | ||||
|   solver-name = "gps-cdcl" | ||||
|   solver-version = 1 | ||||
|  |  | |||
|  | @ -40,6 +40,10 @@ ignored = ["google.golang.org/appengine*"] | |||
|   #version = "0.6.5" | ||||
|   revision = "d4149d1eee0c2c488a74a5863fd9caf13d60fd03" | ||||
| 
 | ||||
| [[override]] | ||||
|   name = "github.com/go-sql-driver/mysql" | ||||
|   revision = "d523deb1b23d913de5bdada721a6071e71283618" | ||||
| 
 | ||||
| [[override]] | ||||
|   name = "github.com/gorilla/mux" | ||||
|   revision = "757bef944d0f21880861c2dd9c871ca543023cba" | ||||
|  |  | |||
							
								
								
									
										34
									
								
								vendor/github.com/go-sql-driver/mysql/AUTHORS
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										34
									
								
								vendor/github.com/go-sql-driver/mysql/AUTHORS
									
									
									
										generated
									
									
										vendored
									
									
								
							|  | @ -12,34 +12,63 @@ | |||
| # Individual Persons | ||||
| 
 | ||||
| Aaron Hopkins <go-sql-driver at die.net> | ||||
| Achille Roussel <achille.roussel at gmail.com> | ||||
| Alexey Palazhchenko <alexey.palazhchenko at gmail.com> | ||||
| Andrew Reid <andrew.reid at tixtrack.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> | ||||
| Craig Wilson <craiggwilson at gmail.com> | ||||
| Daniel Montoya <dsmontoyam at gmail.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> | ||||
| Hajime Nakagami <nakagami 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> | ||||
| Jeffrey Charles <jeffreycharles at gmail.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 Li <jli at j-li.net> | ||||
| Justin Nuß <nuss.justin at gmail.com> | ||||
| Kamil Dziedzic <kamil at klecza.pl> | ||||
| Kevin Malachowski <kevin at chowski.com> | ||||
| Kieron Woodhouse <kieron.woodhouse at infosum.com> | ||||
| Lennart Rudolph <lrudolph at hmc.edu> | ||||
| Leonardo YongUk Kim <dalinaum at gmail.com> | ||||
| Linh Tran Tuan <linhduonggnu at gmail.com> | ||||
| Lion Yang <lion at aosc.xyz> | ||||
| Luca Looz <luca.looz92 at gmail.com> | ||||
| 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> | ||||
| 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> | ||||
| Reed Allman <rdallman10 at gmail.com> | ||||
| Richard Wilkes <wilkes at me.com> | ||||
| Robert Russell <robert at rrbrussell.com> | ||||
| Runrioter Wung <runrioter at gmail.com> | ||||
| Shuode Li <elemount at qq.com> | ||||
| Soroush Pour <me at soroushjp.com> | ||||
| Stan Putrya <root.vagner at gmail.com> | ||||
| Stanley Gunawan <gunawan.stanley at gmail.com> | ||||
|  | @ -51,5 +80,10 @@ Zhenye Xie <xiezhenye at gmail.com> | |||
| # Organizations | ||||
| 
 | ||||
| Barracuda Networks, Inc. | ||||
| Counting Ltd. | ||||
| Google Inc. | ||||
| InfoSum Ltd. | ||||
| Keybase Inc. | ||||
| Percona LLC | ||||
| Pivotal Inc. | ||||
| Stripe Inc. | ||||
|  |  | |||
							
								
								
									
										2
									
								
								vendor/github.com/go-sql-driver/mysql/appengine.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/go-sql-driver/mysql/appengine.go
									
									
									
										generated
									
									
										vendored
									
									
								
							|  | @ -11,7 +11,7 @@ | |||
| package mysql | ||||
| 
 | ||||
| import ( | ||||
| 	"appengine/cloudsql" | ||||
| 	"google.golang.org/appengine/cloudsql" | ||||
| ) | ||||
| 
 | ||||
| func init() { | ||||
|  |  | |||
							
								
								
									
										420
									
								
								vendor/github.com/go-sql-driver/mysql/auth.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										420
									
								
								vendor/github.com/go-sql-driver/mysql/auth.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,420 @@ | |||
| // Go MySQL Driver - A MySQL-Driver for Go's database/sql package
 | ||||
| //
 | ||||
| // Copyright 2018 The Go-MySQL-Driver Authors. All rights reserved.
 | ||||
| //
 | ||||
| // This Source Code Form is subject to the terms of the Mozilla Public
 | ||||
| // License, v. 2.0. If a copy of the MPL was not distributed with this file,
 | ||||
| // You can obtain one at http://mozilla.org/MPL/2.0/.
 | ||||
| 
 | ||||
| package mysql | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto/rand" | ||||
| 	"crypto/rsa" | ||||
| 	"crypto/sha1" | ||||
| 	"crypto/sha256" | ||||
| 	"crypto/x509" | ||||
| 	"encoding/pem" | ||||
| 	"sync" | ||||
| ) | ||||
| 
 | ||||
| // server pub keys registry
 | ||||
| var ( | ||||
| 	serverPubKeyLock     sync.RWMutex | ||||
| 	serverPubKeyRegistry map[string]*rsa.PublicKey | ||||
| ) | ||||
| 
 | ||||
| // RegisterServerPubKey registers a server RSA public key which can be used to
 | ||||
| // send data in a secure manner to the server without receiving the public key
 | ||||
| // in a potentially insecure way from the server first.
 | ||||
| // Registered keys can afterwards be used adding serverPubKey=<name> to the DSN.
 | ||||
| //
 | ||||
| // Note: The provided rsa.PublicKey instance is exclusively owned by the driver
 | ||||
| // after registering it and may not be modified.
 | ||||
| //
 | ||||
| //  data, err := ioutil.ReadFile("mykey.pem")
 | ||||
| //  if err != nil {
 | ||||
| //  	log.Fatal(err)
 | ||||
| //  }
 | ||||
| //
 | ||||
| //  block, _ := pem.Decode(data)
 | ||||
| //  if block == nil || block.Type != "PUBLIC KEY" {
 | ||||
| //  	log.Fatal("failed to decode PEM block containing public key")
 | ||||
| //  }
 | ||||
| //
 | ||||
| //  pub, err := x509.ParsePKIXPublicKey(block.Bytes)
 | ||||
| //  if err != nil {
 | ||||
| //  	log.Fatal(err)
 | ||||
| //  }
 | ||||
| //
 | ||||
| //  if rsaPubKey, ok := pub.(*rsa.PublicKey); ok {
 | ||||
| //  	mysql.RegisterServerPubKey("mykey", rsaPubKey)
 | ||||
| //  } else {
 | ||||
| //  	log.Fatal("not a RSA public key")
 | ||||
| //  }
 | ||||
| //
 | ||||
| func RegisterServerPubKey(name string, pubKey *rsa.PublicKey) { | ||||
| 	serverPubKeyLock.Lock() | ||||
| 	if serverPubKeyRegistry == nil { | ||||
| 		serverPubKeyRegistry = make(map[string]*rsa.PublicKey) | ||||
| 	} | ||||
| 
 | ||||
| 	serverPubKeyRegistry[name] = pubKey | ||||
| 	serverPubKeyLock.Unlock() | ||||
| } | ||||
| 
 | ||||
| // DeregisterServerPubKey removes the public key registered with the given name.
 | ||||
| func DeregisterServerPubKey(name string) { | ||||
| 	serverPubKeyLock.Lock() | ||||
| 	if serverPubKeyRegistry != nil { | ||||
| 		delete(serverPubKeyRegistry, name) | ||||
| 	} | ||||
| 	serverPubKeyLock.Unlock() | ||||
| } | ||||
| 
 | ||||
| func getServerPubKey(name string) (pubKey *rsa.PublicKey) { | ||||
| 	serverPubKeyLock.RLock() | ||||
| 	if v, ok := serverPubKeyRegistry[name]; ok { | ||||
| 		pubKey = v | ||||
| 	} | ||||
| 	serverPubKeyLock.RUnlock() | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // Hash password using pre 4.1 (old password) method
 | ||||
| // https://github.com/atcurtis/mariadb/blob/master/mysys/my_rnd.c
 | ||||
| type myRnd struct { | ||||
| 	seed1, seed2 uint32 | ||||
| } | ||||
| 
 | ||||
| const myRndMaxVal = 0x3FFFFFFF | ||||
| 
 | ||||
| // Pseudo random number generator
 | ||||
| func newMyRnd(seed1, seed2 uint32) *myRnd { | ||||
| 	return &myRnd{ | ||||
| 		seed1: seed1 % myRndMaxVal, | ||||
| 		seed2: seed2 % myRndMaxVal, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Tested to be equivalent to MariaDB's floating point variant
 | ||||
| // http://play.golang.org/p/QHvhd4qved
 | ||||
| // http://play.golang.org/p/RG0q4ElWDx
 | ||||
| func (r *myRnd) NextByte() byte { | ||||
| 	r.seed1 = (r.seed1*3 + r.seed2) % myRndMaxVal | ||||
| 	r.seed2 = (r.seed1 + r.seed2 + 33) % myRndMaxVal | ||||
| 
 | ||||
| 	return byte(uint64(r.seed1) * 31 / myRndMaxVal) | ||||
| } | ||||
| 
 | ||||
| // Generate binary hash from byte string using insecure pre 4.1 method
 | ||||
| func pwHash(password []byte) (result [2]uint32) { | ||||
| 	var add uint32 = 7 | ||||
| 	var tmp uint32 | ||||
| 
 | ||||
| 	result[0] = 1345345333 | ||||
| 	result[1] = 0x12345671 | ||||
| 
 | ||||
| 	for _, c := range password { | ||||
| 		// skip spaces and tabs in password
 | ||||
| 		if c == ' ' || c == '\t' { | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		tmp = uint32(c) | ||||
| 		result[0] ^= (((result[0] & 63) + add) * tmp) + (result[0] << 8) | ||||
| 		result[1] += (result[1] << 8) ^ result[0] | ||||
| 		add += tmp | ||||
| 	} | ||||
| 
 | ||||
| 	// Remove sign bit (1<<31)-1)
 | ||||
| 	result[0] &= 0x7FFFFFFF | ||||
| 	result[1] &= 0x7FFFFFFF | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // Hash password using insecure pre 4.1 method
 | ||||
| func scrambleOldPassword(scramble []byte, password string) []byte { | ||||
| 	if len(password) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	scramble = scramble[:8] | ||||
| 
 | ||||
| 	hashPw := pwHash([]byte(password)) | ||||
| 	hashSc := pwHash(scramble) | ||||
| 
 | ||||
| 	r := newMyRnd(hashPw[0]^hashSc[0], hashPw[1]^hashSc[1]) | ||||
| 
 | ||||
| 	var out [8]byte | ||||
| 	for i := range out { | ||||
| 		out[i] = r.NextByte() + 64 | ||||
| 	} | ||||
| 
 | ||||
| 	mask := r.NextByte() | ||||
| 	for i := range out { | ||||
| 		out[i] ^= mask | ||||
| 	} | ||||
| 
 | ||||
| 	return out[:] | ||||
| } | ||||
| 
 | ||||
| // Hash password using 4.1+ method (SHA1)
 | ||||
| func scramblePassword(scramble []byte, password string) []byte { | ||||
| 	if len(password) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	// stage1Hash = SHA1(password)
 | ||||
| 	crypt := sha1.New() | ||||
| 	crypt.Write([]byte(password)) | ||||
| 	stage1 := crypt.Sum(nil) | ||||
| 
 | ||||
| 	// scrambleHash = SHA1(scramble + SHA1(stage1Hash))
 | ||||
| 	// inner Hash
 | ||||
| 	crypt.Reset() | ||||
| 	crypt.Write(stage1) | ||||
| 	hash := crypt.Sum(nil) | ||||
| 
 | ||||
| 	// outer Hash
 | ||||
| 	crypt.Reset() | ||||
| 	crypt.Write(scramble) | ||||
| 	crypt.Write(hash) | ||||
| 	scramble = crypt.Sum(nil) | ||||
| 
 | ||||
| 	// token = scrambleHash XOR stage1Hash
 | ||||
| 	for i := range scramble { | ||||
| 		scramble[i] ^= stage1[i] | ||||
| 	} | ||||
| 	return scramble | ||||
| } | ||||
| 
 | ||||
| // Hash password using MySQL 8+ method (SHA256)
 | ||||
| func scrambleSHA256Password(scramble []byte, password string) []byte { | ||||
| 	if len(password) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	// XOR(SHA256(password), SHA256(SHA256(SHA256(password)), scramble))
 | ||||
| 
 | ||||
| 	crypt := sha256.New() | ||||
| 	crypt.Write([]byte(password)) | ||||
| 	message1 := crypt.Sum(nil) | ||||
| 
 | ||||
| 	crypt.Reset() | ||||
| 	crypt.Write(message1) | ||||
| 	message1Hash := crypt.Sum(nil) | ||||
| 
 | ||||
| 	crypt.Reset() | ||||
| 	crypt.Write(message1Hash) | ||||
| 	crypt.Write(scramble) | ||||
| 	message2 := crypt.Sum(nil) | ||||
| 
 | ||||
| 	for i := range message1 { | ||||
| 		message1[i] ^= message2[i] | ||||
| 	} | ||||
| 
 | ||||
| 	return message1 | ||||
| } | ||||
| 
 | ||||
| func encryptPassword(password string, seed []byte, pub *rsa.PublicKey) ([]byte, error) { | ||||
| 	plain := make([]byte, len(password)+1) | ||||
| 	copy(plain, password) | ||||
| 	for i := range plain { | ||||
| 		j := i % len(seed) | ||||
| 		plain[i] ^= seed[j] | ||||
| 	} | ||||
| 	sha1 := sha1.New() | ||||
| 	return rsa.EncryptOAEP(sha1, rand.Reader, pub, plain, nil) | ||||
| } | ||||
| 
 | ||||
| func (mc *mysqlConn) sendEncryptedPassword(seed []byte, pub *rsa.PublicKey) error { | ||||
| 	enc, err := encryptPassword(mc.cfg.Passwd, seed, pub) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return mc.writeAuthSwitchPacket(enc, false) | ||||
| } | ||||
| 
 | ||||
| func (mc *mysqlConn) auth(authData []byte, plugin string) ([]byte, bool, error) { | ||||
| 	switch plugin { | ||||
| 	case "caching_sha2_password": | ||||
| 		authResp := scrambleSHA256Password(authData, mc.cfg.Passwd) | ||||
| 		return authResp, (authResp == nil), nil | ||||
| 
 | ||||
| 	case "mysql_old_password": | ||||
| 		if !mc.cfg.AllowOldPasswords { | ||||
| 			return nil, false, ErrOldPassword | ||||
| 		} | ||||
| 		// Note: there are edge cases where this should work but doesn't;
 | ||||
| 		// this is currently "wontfix":
 | ||||
| 		// https://github.com/go-sql-driver/mysql/issues/184
 | ||||
| 		authResp := scrambleOldPassword(authData[:8], mc.cfg.Passwd) | ||||
| 		return authResp, true, nil | ||||
| 
 | ||||
| 	case "mysql_clear_password": | ||||
| 		if !mc.cfg.AllowCleartextPasswords { | ||||
| 			return nil, false, ErrCleartextPassword | ||||
| 		} | ||||
| 		// http://dev.mysql.com/doc/refman/5.7/en/cleartext-authentication-plugin.html
 | ||||
| 		// http://dev.mysql.com/doc/refman/5.7/en/pam-authentication-plugin.html
 | ||||
| 		return []byte(mc.cfg.Passwd), true, nil | ||||
| 
 | ||||
| 	case "mysql_native_password": | ||||
| 		if !mc.cfg.AllowNativePasswords { | ||||
| 			return nil, false, ErrNativePassword | ||||
| 		} | ||||
| 		// https://dev.mysql.com/doc/internals/en/secure-password-authentication.html
 | ||||
| 		// Native password authentication only need and will need 20-byte challenge.
 | ||||
| 		authResp := scramblePassword(authData[:20], mc.cfg.Passwd) | ||||
| 		return authResp, false, nil | ||||
| 
 | ||||
| 	case "sha256_password": | ||||
| 		if len(mc.cfg.Passwd) == 0 { | ||||
| 			return nil, true, nil | ||||
| 		} | ||||
| 		if mc.cfg.tls != nil || mc.cfg.Net == "unix" { | ||||
| 			// write cleartext auth packet
 | ||||
| 			return []byte(mc.cfg.Passwd), true, nil | ||||
| 		} | ||||
| 
 | ||||
| 		pubKey := mc.cfg.pubKey | ||||
| 		if pubKey == nil { | ||||
| 			// request public key from server
 | ||||
| 			return []byte{1}, false, nil | ||||
| 		} | ||||
| 
 | ||||
| 		// encrypted password
 | ||||
| 		enc, err := encryptPassword(mc.cfg.Passwd, authData, pubKey) | ||||
| 		return enc, false, err | ||||
| 
 | ||||
| 	default: | ||||
| 		errLog.Print("unknown auth plugin:", plugin) | ||||
| 		return nil, false, ErrUnknownPlugin | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error { | ||||
| 	// Read Result Packet
 | ||||
| 	authData, newPlugin, err := mc.readAuthResult() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	// handle auth plugin switch, if requested
 | ||||
| 	if newPlugin != "" { | ||||
| 		// If CLIENT_PLUGIN_AUTH capability is not supported, no new cipher is
 | ||||
| 		// sent and we have to keep using the cipher sent in the init packet.
 | ||||
| 		if authData == nil { | ||||
| 			authData = oldAuthData | ||||
| 		} else { | ||||
| 			// copy data from read buffer to owned slice
 | ||||
| 			copy(oldAuthData, authData) | ||||
| 		} | ||||
| 
 | ||||
| 		plugin = newPlugin | ||||
| 
 | ||||
| 		authResp, addNUL, err := mc.auth(authData, plugin) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if err = mc.writeAuthSwitchPacket(authResp, addNUL); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 
 | ||||
| 		// Read Result Packet
 | ||||
| 		authData, newPlugin, err = mc.readAuthResult() | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 
 | ||||
| 		// Do not allow to change the auth plugin more than once
 | ||||
| 		if newPlugin != "" { | ||||
| 			return ErrMalformPkt | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	switch plugin { | ||||
| 
 | ||||
| 	// https://insidemysql.com/preparing-your-community-connector-for-mysql-8-part-2-sha256/
 | ||||
| 	case "caching_sha2_password": | ||||
| 		switch len(authData) { | ||||
| 		case 0: | ||||
| 			return nil // auth successful
 | ||||
| 		case 1: | ||||
| 			switch authData[0] { | ||||
| 			case cachingSha2PasswordFastAuthSuccess: | ||||
| 				if err = mc.readResultOK(); err == nil { | ||||
| 					return nil // auth successful
 | ||||
| 				} | ||||
| 
 | ||||
| 			case cachingSha2PasswordPerformFullAuthentication: | ||||
| 				if mc.cfg.tls != nil || mc.cfg.Net == "unix" { | ||||
| 					// write cleartext auth packet
 | ||||
| 					err = mc.writeAuthSwitchPacket([]byte(mc.cfg.Passwd), true) | ||||
| 					if err != nil { | ||||
| 						return err | ||||
| 					} | ||||
| 				} else { | ||||
| 					pubKey := mc.cfg.pubKey | ||||
| 					if pubKey == nil { | ||||
| 						// request public key from server
 | ||||
| 						data := mc.buf.takeSmallBuffer(4 + 1) | ||||
| 						data[4] = cachingSha2PasswordRequestPublicKey | ||||
| 						mc.writePacket(data) | ||||
| 
 | ||||
| 						// parse public key
 | ||||
| 						data, err := mc.readPacket() | ||||
| 						if err != nil { | ||||
| 							return err | ||||
| 						} | ||||
| 
 | ||||
| 						block, _ := pem.Decode(data[1:]) | ||||
| 						pkix, err := x509.ParsePKIXPublicKey(block.Bytes) | ||||
| 						if err != nil { | ||||
| 							return err | ||||
| 						} | ||||
| 						pubKey = pkix.(*rsa.PublicKey) | ||||
| 					} | ||||
| 
 | ||||
| 					// send encrypted password
 | ||||
| 					err = mc.sendEncryptedPassword(oldAuthData, pubKey) | ||||
| 					if err != nil { | ||||
| 						return err | ||||
| 					} | ||||
| 				} | ||||
| 				return mc.readResultOK() | ||||
| 
 | ||||
| 			default: | ||||
| 				return ErrMalformPkt | ||||
| 			} | ||||
| 		default: | ||||
| 			return ErrMalformPkt | ||||
| 		} | ||||
| 
 | ||||
| 	case "sha256_password": | ||||
| 		switch len(authData) { | ||||
| 		case 0: | ||||
| 			return nil // auth successful
 | ||||
| 		default: | ||||
| 			block, _ := pem.Decode(authData) | ||||
| 			pub, err := x509.ParsePKIXPublicKey(block.Bytes) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 
 | ||||
| 			// send encrypted password
 | ||||
| 			err = mc.sendEncryptedPassword(oldAuthData, pub.(*rsa.PublicKey)) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			return mc.readResultOK() | ||||
| 		} | ||||
| 
 | ||||
| 	default: | ||||
| 		return nil // auth successful
 | ||||
| 	} | ||||
| 
 | ||||
| 	return err | ||||
| } | ||||
							
								
								
									
										12
									
								
								vendor/github.com/go-sql-driver/mysql/buffer.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/go-sql-driver/mysql/buffer.go
									
									
									
										generated
									
									
										vendored
									
									
								
							|  | @ -130,18 +130,18 @@ func (b *buffer) takeBuffer(length int) []byte { | |||
| // smaller than defaultBufSize
 | ||||
| // Only one buffer (total) can be used at a time.
 | ||||
| func (b *buffer) takeSmallBuffer(length int) []byte { | ||||
| 	if b.length == 0 { | ||||
| 		return b.buf[:length] | ||||
| 	if b.length > 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return nil | ||||
| 	return b.buf[:length] | ||||
| } | ||||
| 
 | ||||
| // takeCompleteBuffer returns the complete existing buffer.
 | ||||
| // This can be used if the necessary buffer size is unknown.
 | ||||
| // Only one buffer (total) can be used at a time.
 | ||||
| func (b *buffer) takeCompleteBuffer() []byte { | ||||
| 	if b.length == 0 { | ||||
| 		return b.buf | ||||
| 	if b.length > 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return nil | ||||
| 	return b.buf | ||||
| } | ||||
|  |  | |||
							
								
								
									
										1
									
								
								vendor/github.com/go-sql-driver/mysql/collations.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								vendor/github.com/go-sql-driver/mysql/collations.go
									
									
									
										generated
									
									
										vendored
									
									
								
							|  | @ -9,6 +9,7 @@ | |||
| package mysql | ||||
| 
 | ||||
| const defaultCollation = "utf8_general_ci" | ||||
| const binaryCollation = "binary" | ||||
| 
 | ||||
| // A list of available collations mapped to the internal ID.
 | ||||
| // To update this map use the following MySQL query:
 | ||||
|  |  | |||
							
								
								
									
										152
									
								
								vendor/github.com/go-sql-driver/mysql/connection.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										152
									
								
								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 | ||||
|  | @ -29,7 +40,14 @@ type mysqlConn struct { | |||
| 	status           statusFlag | ||||
| 	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
 | ||||
|  | @ -62,22 +80,41 @@ func (mc *mysqlConn) handleParams() (err error) { | |||
| 	return | ||||
| } | ||||
| 
 | ||||
| func (mc *mysqlConn) markBadConn(err error) error { | ||||
| 	if mc == nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if err != errBadConnNoWrite { | ||||
| 		return err | ||||
| 	} | ||||
| 	return driver.ErrBadConn | ||||
| } | ||||
| 
 | ||||
| func (mc *mysqlConn) Begin() (driver.Tx, error) { | ||||
| 	if mc.netConn == nil { | ||||
| 	return mc.begin(false) | ||||
| } | ||||
| 
 | ||||
| func (mc *mysqlConn) begin(readOnly bool) (driver.Tx, error) { | ||||
| 	if mc.closed.IsSet() { | ||||
| 		errLog.Print(ErrInvalidConn) | ||||
| 		return nil, driver.ErrBadConn | ||||
| 	} | ||||
| 	err := mc.exec("START TRANSACTION") | ||||
| 	var q string | ||||
| 	if readOnly { | ||||
| 		q = "START TRANSACTION READ ONLY" | ||||
| 	} else { | ||||
| 		q = "START TRANSACTION" | ||||
| 	} | ||||
| 	err := mc.exec(q) | ||||
| 	if err == nil { | ||||
| 		return &mysqlTx{mc}, err | ||||
| 	} | ||||
| 
 | ||||
| 	return nil, err | ||||
| 	return nil, mc.markBadConn(err) | ||||
| } | ||||
| 
 | ||||
| func (mc *mysqlConn) Close() (err error) { | ||||
| 	// Makes Close idempotent
 | ||||
| 	if mc.netConn != nil { | ||||
| 	if !mc.closed.IsSet() { | ||||
| 		err = mc.writeCommandPacket(comQuit) | ||||
| 	} | ||||
| 
 | ||||
|  | @ -91,26 +128,39 @@ 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 | ||||
| 	} | ||||
| 	// Send command
 | ||||
| 	err := mc.writeCommandPacketStr(comStmtPrepare, query) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 		return nil, mc.markBadConn(err) | ||||
| 	} | ||||
| 
 | ||||
| 	stmt := &mysqlStmt{ | ||||
|  | @ -144,7 +194,7 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin | |||
| 	if buf == nil { | ||||
| 		// can not take the buffer. Something must be wrong with the connection
 | ||||
| 		errLog.Print(ErrBusyBuffer) | ||||
| 		return "", driver.ErrBadConn | ||||
| 		return "", ErrInvalidConn | ||||
| 	} | ||||
| 	buf = buf[:0] | ||||
| 	argPos := 0 | ||||
|  | @ -257,7 +307,7 @@ func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (strin | |||
| } | ||||
| 
 | ||||
| func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) { | ||||
| 	if mc.netConn == nil { | ||||
| 	if mc.closed.IsSet() { | ||||
| 		errLog.Print(ErrInvalidConn) | ||||
| 		return nil, driver.ErrBadConn | ||||
| 	} | ||||
|  | @ -271,7 +321,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 | ||||
|  | @ -283,32 +332,43 @@ func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, err | |||
| 			insertId:     int64(mc.insertId), | ||||
| 		}, err | ||||
| 	} | ||||
| 	return nil, err | ||||
| 	return nil, mc.markBadConn(err) | ||||
| } | ||||
| 
 | ||||
| // Internal function to execute commands
 | ||||
| func (mc *mysqlConn) exec(query string) error { | ||||
| 	// Send command
 | ||||
| 	err := mc.writeCommandPacketStr(comQuery, query) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	if err := mc.writeCommandPacketStr(comQuery, query); err != nil { | ||||
| 		return mc.markBadConn(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 +382,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,15 +394,22 @@ 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 | ||||
| 		} | ||||
| 	} | ||||
| 	return nil, err | ||||
| 	return nil, mc.markBadConn(err) | ||||
| } | ||||
| 
 | ||||
| // Gets the value of the given MySQL System Variable
 | ||||
|  | @ -359,7 +425,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 +441,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: | ||||
| 	} | ||||
| } | ||||
|  |  | |||
							
								
								
									
										208
									
								
								vendor/github.com/go-sql-driver/mysql/connection_go18.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										208
									
								
								vendor/github.com/go-sql-driver/mysql/connection_go18.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,208 @@ | |||
| // Go MySQL Driver - A MySQL-Driver for Go's database/sql package
 | ||||
| //
 | ||||
| // Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
 | ||||
| //
 | ||||
| // This Source Code Form is subject to the terms of the Mozilla Public
 | ||||
| // License, v. 2.0. If a copy of the MPL was not distributed with this file,
 | ||||
| // You can obtain one at http://mozilla.org/MPL/2.0/.
 | ||||
| 
 | ||||
| // +build go1.8
 | ||||
| 
 | ||||
| package mysql | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"database/sql" | ||||
| 	"database/sql/driver" | ||||
| ) | ||||
| 
 | ||||
| // Ping implements driver.Pinger interface
 | ||||
| func (mc *mysqlConn) Ping(ctx context.Context) (err error) { | ||||
| 	if mc.closed.IsSet() { | ||||
| 		errLog.Print(ErrInvalidConn) | ||||
| 		return driver.ErrBadConn | ||||
| 	} | ||||
| 
 | ||||
| 	if err = mc.watchCancel(ctx); err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 	defer mc.finish() | ||||
| 
 | ||||
| 	if err = mc.writeCommandPacket(comPing); err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	return mc.readResultOK() | ||||
| } | ||||
| 
 | ||||
| // BeginTx implements driver.ConnBeginTx interface
 | ||||
| func (mc *mysqlConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) { | ||||
| 	if err := mc.watchCancel(ctx); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer mc.finish() | ||||
| 
 | ||||
| 	if sql.IsolationLevel(opts.Isolation) != sql.LevelDefault { | ||||
| 		level, err := mapIsolationLevel(opts.Isolation) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		err = mc.exec("SET TRANSACTION ISOLATION LEVEL " + level) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return mc.begin(opts.ReadOnly) | ||||
| } | ||||
| 
 | ||||
| func (mc *mysqlConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) { | ||||
| 	dargs, err := namedValueToValue(args) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	if err := mc.watchCancel(ctx); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	rows, err := mc.query(query, dargs) | ||||
| 	if err != nil { | ||||
| 		mc.finish() | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	rows.finish = mc.finish | ||||
| 	return rows, err | ||||
| } | ||||
| 
 | ||||
| func (mc *mysqlConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) { | ||||
| 	dargs, err := namedValueToValue(args) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	if err := mc.watchCancel(ctx); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer mc.finish() | ||||
| 
 | ||||
| 	return mc.Exec(query, dargs) | ||||
| } | ||||
| 
 | ||||
| func (mc *mysqlConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) { | ||||
| 	if err := mc.watchCancel(ctx); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	stmt, err := mc.Prepare(query) | ||||
| 	mc.finish() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	select { | ||||
| 	default: | ||||
| 	case <-ctx.Done(): | ||||
| 		stmt.Close() | ||||
| 		return nil, ctx.Err() | ||||
| 	} | ||||
| 	return stmt, nil | ||||
| } | ||||
| 
 | ||||
| func (stmt *mysqlStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) { | ||||
| 	dargs, err := namedValueToValue(args) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	if err := stmt.mc.watchCancel(ctx); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	rows, err := stmt.query(dargs) | ||||
| 	if err != nil { | ||||
| 		stmt.mc.finish() | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	rows.finish = stmt.mc.finish | ||||
| 	return rows, err | ||||
| } | ||||
| 
 | ||||
| func (stmt *mysqlStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) { | ||||
| 	dargs, err := namedValueToValue(args) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	if err := stmt.mc.watchCancel(ctx); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer stmt.mc.finish() | ||||
| 
 | ||||
| 	return stmt.Exec(dargs) | ||||
| } | ||||
| 
 | ||||
| func (mc *mysqlConn) watchCancel(ctx context.Context) error { | ||||
| 	if mc.watching { | ||||
| 		// Reach here if canceled,
 | ||||
| 		// so the connection is already invalid
 | ||||
| 		mc.cleanup() | ||||
| 		return nil | ||||
| 	} | ||||
| 	if ctx.Done() == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	mc.watching = true | ||||
| 	select { | ||||
| 	default: | ||||
| 	case <-ctx.Done(): | ||||
| 		return ctx.Err() | ||||
| 	} | ||||
| 	if mc.watcher == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	mc.watcher <- ctx | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (mc *mysqlConn) startWatcher() { | ||||
| 	watcher := make(chan mysqlContext, 1) | ||||
| 	mc.watcher = watcher | ||||
| 	finished := make(chan struct{}) | ||||
| 	mc.finished = finished | ||||
| 	go func() { | ||||
| 		for { | ||||
| 			var ctx mysqlContext | ||||
| 			select { | ||||
| 			case ctx = <-watcher: | ||||
| 			case <-mc.closech: | ||||
| 				return | ||||
| 			} | ||||
| 
 | ||||
| 			select { | ||||
| 			case <-ctx.Done(): | ||||
| 				mc.cancel(ctx.Err()) | ||||
| 			case <-finished: | ||||
| 			case <-mc.closech: | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 	}() | ||||
| } | ||||
| 
 | ||||
| func (mc *mysqlConn) CheckNamedValue(nv *driver.NamedValue) (err error) { | ||||
| 	nv.Value, err = converter{}.ConvertValue(nv.Value) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // ResetSession implements driver.SessionResetter.
 | ||||
| // (From Go 1.10)
 | ||||
| func (mc *mysqlConn) ResetSession(ctx context.Context) error { | ||||
| 	if mc.closed.IsSet() { | ||||
| 		return driver.ErrBadConn | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										25
									
								
								vendor/github.com/go-sql-driver/mysql/const.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										25
									
								
								vendor/github.com/go-sql-driver/mysql/const.go
									
									
									
										generated
									
									
										vendored
									
									
								
							|  | @ -9,7 +9,9 @@ | |||
| package mysql | ||||
| 
 | ||||
| const ( | ||||
| 	minProtocolVersion byte = 10 | ||||
| 	defaultAuthPlugin       = "mysql_native_password" | ||||
| 	defaultMaxAllowedPacket = 4 << 20 // 4 MiB
 | ||||
| 	minProtocolVersion      = 10 | ||||
| 	maxPacketSize           = 1<<24 - 1 | ||||
| 	timeFormat              = "2006-01-02 15:04:05.999999" | ||||
| ) | ||||
|  | @ -18,10 +20,11 @@ const ( | |||
| // http://dev.mysql.com/doc/internals/en/client-server-protocol.html
 | ||||
| 
 | ||||
| const ( | ||||
| 	iOK          byte = 0x00 | ||||
| 	iLocalInFile byte = 0xfb | ||||
| 	iEOF         byte = 0xfe | ||||
| 	iERR         byte = 0xff | ||||
| 	iOK           byte = 0x00 | ||||
| 	iAuthMoreData byte = 0x01 | ||||
| 	iLocalInFile  byte = 0xfb | ||||
| 	iEOF          byte = 0xfe | ||||
| 	iERR          byte = 0xff | ||||
| ) | ||||
| 
 | ||||
| // https://dev.mysql.com/doc/internals/en/capability-flags.html#packet-Protocol::CapabilityFlags
 | ||||
|  | @ -87,8 +90,10 @@ const ( | |||
| ) | ||||
| 
 | ||||
| // https://dev.mysql.com/doc/internals/en/com-query-response.html#packet-Protocol::ColumnType
 | ||||
| type fieldType byte | ||||
| 
 | ||||
| const ( | ||||
| 	fieldTypeDecimal byte = iota | ||||
| 	fieldTypeDecimal fieldType = iota | ||||
| 	fieldTypeTiny | ||||
| 	fieldTypeShort | ||||
| 	fieldTypeLong | ||||
|  | @ -107,7 +112,7 @@ const ( | |||
| 	fieldTypeBit | ||||
| ) | ||||
| const ( | ||||
| 	fieldTypeJSON byte = iota + 0xf5 | ||||
| 	fieldTypeJSON fieldType = iota + 0xf5 | ||||
| 	fieldTypeNewDecimal | ||||
| 	fieldTypeEnum | ||||
| 	fieldTypeSet | ||||
|  | @ -161,3 +166,9 @@ const ( | |||
| 	statusInTransReadonly | ||||
| 	statusSessionStateChanged | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	cachingSha2PasswordRequestPublicKey          = 2 | ||||
| 	cachingSha2PasswordFastAuthSuccess           = 3 | ||||
| 	cachingSha2PasswordPerformFullAuthentication = 4 | ||||
| ) | ||||
|  |  | |||
							
								
								
									
										81
									
								
								vendor/github.com/go-sql-driver/mysql/driver.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										81
									
								
								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:
 | ||||
| //
 | ||||
|  | @ -20,8 +20,14 @@ import ( | |||
| 	"database/sql" | ||||
| 	"database/sql/driver" | ||||
| 	"net" | ||||
| 	"sync" | ||||
| ) | ||||
| 
 | ||||
| // watcher interface is used for context support (From Go 1.8)
 | ||||
| type watcher interface { | ||||
| 	startWatcher() | ||||
| } | ||||
| 
 | ||||
| // MySQLDriver is exported to make the driver directly accessible.
 | ||||
| // In general the driver is used via the database/sql package.
 | ||||
| type MySQLDriver struct{} | ||||
|  | @ -30,12 +36,17 @@ type MySQLDriver struct{} | |||
| // Custom dial functions must be registered with RegisterDial
 | ||||
| type DialFunc func(addr string) (net.Conn, error) | ||||
| 
 | ||||
| var dials map[string]DialFunc | ||||
| var ( | ||||
| 	dialsLock sync.RWMutex | ||||
| 	dials     map[string]DialFunc | ||||
| ) | ||||
| 
 | ||||
| // RegisterDial registers a custom dial function. It can then be used by the
 | ||||
| // network address mynet(addr), where mynet is the registered new network.
 | ||||
| // addr is passed as a parameter to the dial function.
 | ||||
| func RegisterDial(net string, dial DialFunc) { | ||||
| 	dialsLock.Lock() | ||||
| 	defer dialsLock.Unlock() | ||||
| 	if dials == nil { | ||||
| 		dials = make(map[string]DialFunc) | ||||
| 	} | ||||
|  | @ -52,16 +63,19 @@ 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 { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	mc.parseTime = mc.cfg.ParseTime | ||||
| 	mc.strict = mc.cfg.Strict | ||||
| 
 | ||||
| 	// Connect to Server
 | ||||
| 	if dial, ok := dials[mc.cfg.Net]; ok { | ||||
| 	dialsLock.RLock() | ||||
| 	dial, ok := dials[mc.cfg.Net] | ||||
| 	dialsLock.RUnlock() | ||||
| 	if ok { | ||||
| 		mc.netConn, err = dial(mc.cfg.Addr) | ||||
| 	} else { | ||||
| 		nd := net.Dialer{Timeout: mc.cfg.Timeout} | ||||
|  | @ -81,6 +95,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
 | ||||
|  | @ -88,20 +107,31 @@ func (d MySQLDriver) Open(dsn string) (driver.Conn, error) { | |||
| 	mc.writeTimeout = mc.cfg.WriteTimeout | ||||
| 
 | ||||
| 	// Reading Handshake Initialization Packet
 | ||||
| 	cipher, err := mc.readInitPacket() | ||||
| 	authData, plugin, err := mc.readHandshakePacket() | ||||
| 	if err != nil { | ||||
| 		mc.cleanup() | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	// Send Client Authentication Packet
 | ||||
| 	if err = mc.writeAuthPacket(cipher); err != nil { | ||||
| 	authResp, addNUL, err := mc.auth(authData, plugin) | ||||
| 	if err != nil { | ||||
| 		// try the default auth plugin, if using the requested plugin failed
 | ||||
| 		errLog.Print("could not use requested auth plugin '"+plugin+"': ", err.Error()) | ||||
| 		plugin = defaultAuthPlugin | ||||
| 		authResp, addNUL, err = mc.auth(authData, plugin) | ||||
| 		if err != nil { | ||||
| 			mc.cleanup() | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| 	if err = mc.writeHandshakeResponsePacket(authResp, addNUL, plugin); err != nil { | ||||
| 		mc.cleanup() | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	// Handle response to auth packet, switch methods if possible
 | ||||
| 	if err = handleAuthResult(mc); err != nil { | ||||
| 	if err = mc.handleAuthResult(authData, plugin); err != nil { | ||||
| 		// Authentication failed and MySQL has already closed the connection
 | ||||
| 		// (https://dev.mysql.com/doc/internals/en/authentication-fails.html).
 | ||||
| 		// Do not send COM_QUIT, just cleanup and return the error.
 | ||||
|  | @ -134,43 +164,6 @@ func (d MySQLDriver) Open(dsn string) (driver.Conn, error) { | |||
| 	return mc, nil | ||||
| } | ||||
| 
 | ||||
| func handleAuthResult(mc *mysqlConn) error { | ||||
| 	// Read Result Packet
 | ||||
| 	cipher, err := mc.readResultOK() | ||||
| 	if err == nil { | ||||
| 		return nil // auth successful
 | ||||
| 	} | ||||
| 
 | ||||
| 	if mc.cfg == nil { | ||||
| 		return err // auth failed and retry not possible
 | ||||
| 	} | ||||
| 
 | ||||
| 	// Retry auth if configured to do so.
 | ||||
| 	if mc.cfg.AllowOldPasswords && err == ErrOldPassword { | ||||
| 		// Retry with old authentication method. Note: there are edge cases
 | ||||
| 		// where this should work but doesn't; this is currently "wontfix":
 | ||||
| 		// https://github.com/go-sql-driver/mysql/issues/184
 | ||||
| 		if err = mc.writeOldAuthPacket(cipher); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		_, err = mc.readResultOK() | ||||
| 	} else if mc.cfg.AllowCleartextPasswords && err == ErrCleartextPassword { | ||||
| 		// Retry with clear text password for
 | ||||
| 		// http://dev.mysql.com/doc/refman/5.7/en/cleartext-authentication-plugin.html
 | ||||
| 		// http://dev.mysql.com/doc/refman/5.7/en/pam-authentication-plugin.html
 | ||||
| 		if err = mc.writeClearAuthPacket(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		_, err = mc.readResultOK() | ||||
| 	} else if mc.cfg.AllowNativePasswords && err == ErrNativePassword { | ||||
| 		if err = mc.writeNativeAuthPacket(cipher); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		_, err = mc.readResultOK() | ||||
| 	} | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| func init() { | ||||
| 	sql.Register("mysql", &MySQLDriver{}) | ||||
| } | ||||
|  |  | |||
							
								
								
									
										159
									
								
								vendor/github.com/go-sql-driver/mysql/dsn.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										159
									
								
								vendor/github.com/go-sql-driver/mysql/dsn.go
									
									
									
										generated
									
									
										vendored
									
									
								
							|  | @ -10,11 +10,13 @@ package mysql | |||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"crypto/rsa" | ||||
| 	"crypto/tls" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"net" | ||||
| 	"net/url" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | @ -27,7 +29,9 @@ var ( | |||
| 	errInvalidDSNUnsafeCollation = errors.New("invalid DSN: interpolateParams can not be used with unsafe collations") | ||||
| ) | ||||
| 
 | ||||
| // Config is a configuration parsed from a DSN string
 | ||||
| // Config is a configuration parsed from a DSN string.
 | ||||
| // If a new Config is created instead of being parsed from a DSN string,
 | ||||
| // the NewConfig function should be used, which sets default values.
 | ||||
| type Config struct { | ||||
| 	User             string            // Username
 | ||||
| 	Passwd           string            // Password (requires User)
 | ||||
|  | @ -38,6 +42,8 @@ type Config struct { | |||
| 	Collation        string            // Connection collation
 | ||||
| 	Loc              *time.Location    // Location for time.Time values
 | ||||
| 	MaxAllowedPacket int               // Max packet size allowed
 | ||||
| 	ServerPubKey     string            // Server public key name
 | ||||
| 	pubKey           *rsa.PublicKey    // Server public key
 | ||||
| 	TLSConfig        string            // TLS configuration name
 | ||||
| 	tls              *tls.Config       // TLS configuration
 | ||||
| 	Timeout          time.Duration     // Dial timeout
 | ||||
|  | @ -53,7 +59,54 @@ 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
 | ||||
| 	Strict                  bool // Return warnings as errors
 | ||||
| 	RejectReadOnly          bool // Reject read-only connections
 | ||||
| } | ||||
| 
 | ||||
| // NewConfig creates a new Config and sets default values.
 | ||||
| func NewConfig() *Config { | ||||
| 	return &Config{ | ||||
| 		Collation:            defaultCollation, | ||||
| 		Loc:                  time.UTC, | ||||
| 		MaxAllowedPacket:     defaultMaxAllowedPacket, | ||||
| 		AllowNativePasswords: true, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (cfg *Config) normalize() error { | ||||
| 	if cfg.InterpolateParams && unsafeCollations[cfg.Collation] { | ||||
| 		return errInvalidDSNUnsafeCollation | ||||
| 	} | ||||
| 
 | ||||
| 	// Set default network if empty
 | ||||
| 	if cfg.Net == "" { | ||||
| 		cfg.Net = "tcp" | ||||
| 	} | ||||
| 
 | ||||
| 	// Set default address if empty
 | ||||
| 	if cfg.Addr == "" { | ||||
| 		switch cfg.Net { | ||||
| 		case "tcp": | ||||
| 			cfg.Addr = "127.0.0.1:3306" | ||||
| 		case "unix": | ||||
| 			cfg.Addr = "/tmp/mysql.sock" | ||||
| 		default: | ||||
| 			return errors.New("default addr for network '" + cfg.Net + "' unknown") | ||||
| 		} | ||||
| 
 | ||||
| 	} else if cfg.Net == "tcp" { | ||||
| 		cfg.Addr = ensureHavePort(cfg.Addr) | ||||
| 	} | ||||
| 
 | ||||
| 	if cfg.tls != nil { | ||||
| 		if cfg.tls.ServerName == "" && !cfg.tls.InsecureSkipVerify { | ||||
| 			host, _, err := net.SplitHostPort(cfg.Addr) | ||||
| 			if err == nil { | ||||
| 				cfg.tls.ServerName = host | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // FormatDSN formats the given Config into a DSN string which can be passed to
 | ||||
|  | @ -102,12 +155,12 @@ func (cfg *Config) FormatDSN() string { | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if cfg.AllowNativePasswords { | ||||
| 	if !cfg.AllowNativePasswords { | ||||
| 		if hasParam { | ||||
| 			buf.WriteString("&allowNativePasswords=true") | ||||
| 			buf.WriteString("&allowNativePasswords=false") | ||||
| 		} else { | ||||
| 			hasParam = true | ||||
| 			buf.WriteString("?allowNativePasswords=true") | ||||
| 			buf.WriteString("?allowNativePasswords=false") | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  | @ -195,15 +248,25 @@ func (cfg *Config) FormatDSN() string { | |||
| 		buf.WriteString(cfg.ReadTimeout.String()) | ||||
| 	} | ||||
| 
 | ||||
| 	if cfg.Strict { | ||||
| 	if cfg.RejectReadOnly { | ||||
| 		if hasParam { | ||||
| 			buf.WriteString("&strict=true") | ||||
| 			buf.WriteString("&rejectReadOnly=true") | ||||
| 		} else { | ||||
| 			hasParam = true | ||||
| 			buf.WriteString("?strict=true") | ||||
| 			buf.WriteString("?rejectReadOnly=true") | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if len(cfg.ServerPubKey) > 0 { | ||||
| 		if hasParam { | ||||
| 			buf.WriteString("&serverPubKey=") | ||||
| 		} else { | ||||
| 			hasParam = true | ||||
| 			buf.WriteString("?serverPubKey=") | ||||
| 		} | ||||
| 		buf.WriteString(url.QueryEscape(cfg.ServerPubKey)) | ||||
| 	} | ||||
| 
 | ||||
| 	if cfg.Timeout > 0 { | ||||
| 		if hasParam { | ||||
| 			buf.WriteString("&timeout=") | ||||
|  | @ -234,7 +297,7 @@ func (cfg *Config) FormatDSN() string { | |||
| 		buf.WriteString(cfg.WriteTimeout.String()) | ||||
| 	} | ||||
| 
 | ||||
| 	if cfg.MaxAllowedPacket > 0 { | ||||
| 	if cfg.MaxAllowedPacket != defaultMaxAllowedPacket { | ||||
| 		if hasParam { | ||||
| 			buf.WriteString("&maxAllowedPacket=") | ||||
| 		} else { | ||||
|  | @ -247,7 +310,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 +325,7 @@ func (cfg *Config) FormatDSN() string { | |||
| 
 | ||||
| 			buf.WriteString(param) | ||||
| 			buf.WriteByte('=') | ||||
| 			buf.WriteString(url.QueryEscape(value)) | ||||
| 			buf.WriteString(url.QueryEscape(cfg.Params[param])) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  | @ -267,10 +335,7 @@ func (cfg *Config) FormatDSN() string { | |||
| // ParseDSN parses the DSN string to a Config
 | ||||
| func ParseDSN(dsn string) (cfg *Config, err error) { | ||||
| 	// New config with some default values
 | ||||
| 	cfg = &Config{ | ||||
| 		Loc:       time.UTC, | ||||
| 		Collation: defaultCollation, | ||||
| 	} | ||||
| 	cfg = NewConfig() | ||||
| 
 | ||||
| 	// [user[:password]@][net[(addr)]]/dbname[?param1=value1¶mN=valueN]
 | ||||
| 	// Find the last '/' (since the password or the net addr might contain a '/')
 | ||||
|  | @ -338,28 +403,9 @@ func ParseDSN(dsn string) (cfg *Config, err error) { | |||
| 		return nil, errInvalidDSNNoSlash | ||||
| 	} | ||||
| 
 | ||||
| 	if cfg.InterpolateParams && unsafeCollations[cfg.Collation] { | ||||
| 		return nil, errInvalidDSNUnsafeCollation | ||||
| 	if err = cfg.normalize(); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	// Set default network if empty
 | ||||
| 	if cfg.Net == "" { | ||||
| 		cfg.Net = "tcp" | ||||
| 	} | ||||
| 
 | ||||
| 	// Set default address if empty
 | ||||
| 	if cfg.Addr == "" { | ||||
| 		switch cfg.Net { | ||||
| 		case "tcp": | ||||
| 			cfg.Addr = "127.0.0.1:3306" | ||||
| 		case "unix": | ||||
| 			cfg.Addr = "/tmp/mysql.sock" | ||||
| 		default: | ||||
| 			return nil, errors.New("default addr for network '" + cfg.Net + "' unknown") | ||||
| 		} | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
|  | @ -374,7 +420,6 @@ func parseDSNParams(cfg *Config, params string) (err error) { | |||
| 
 | ||||
| 		// cfg params
 | ||||
| 		switch value := param[1]; param[0] { | ||||
| 
 | ||||
| 		// Disable INFILE whitelist / enable all files
 | ||||
| 		case "allowAllFiles": | ||||
| 			var isBool bool | ||||
|  | @ -472,14 +517,32 @@ func parseDSNParams(cfg *Config, params string) (err error) { | |||
| 				return | ||||
| 			} | ||||
| 
 | ||||
| 		// Strict mode
 | ||||
| 		case "strict": | ||||
| 		// Reject read-only connections
 | ||||
| 		case "rejectReadOnly": | ||||
| 			var isBool bool | ||||
| 			cfg.Strict, isBool = readBool(value) | ||||
| 			cfg.RejectReadOnly, isBool = readBool(value) | ||||
| 			if !isBool { | ||||
| 				return errors.New("invalid bool value: " + value) | ||||
| 			} | ||||
| 
 | ||||
| 		// Server public key
 | ||||
| 		case "serverPubKey": | ||||
| 			name, err := url.QueryUnescape(value) | ||||
| 			if err != nil { | ||||
| 				return fmt.Errorf("invalid value for server pub key name: %v", err) | ||||
| 			} | ||||
| 
 | ||||
| 			if pubKey := getServerPubKey(name); pubKey != nil { | ||||
| 				cfg.ServerPubKey = name | ||||
| 				cfg.pubKey = pubKey | ||||
| 			} else { | ||||
| 				return errors.New("invalid value / unknown server pub key name: " + name) | ||||
| 			} | ||||
| 
 | ||||
| 		// Strict mode
 | ||||
| 		case "strict": | ||||
| 			panic("strict mode has been removed. See https://github.com/go-sql-driver/mysql/wiki/strict-mode") | ||||
| 
 | ||||
| 		// Dial Timeout
 | ||||
| 		case "timeout": | ||||
| 			cfg.Timeout, err = time.ParseDuration(value) | ||||
|  | @ -506,14 +569,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 len(tlsConfig.ServerName) == 0 && !tlsConfig.InsecureSkipVerify { | ||||
| 						host, _, err := net.SplitHostPort(cfg.Addr) | ||||
| 						if err == nil { | ||||
| 							tlsConfig.ServerName = host | ||||
| 						} | ||||
| 					} | ||||
| 
 | ||||
| 				if tlsConfig := getTLSConfigClone(name); tlsConfig != nil { | ||||
| 					cfg.TLSConfig = name | ||||
| 					cfg.tls = tlsConfig | ||||
| 				} else { | ||||
|  | @ -546,3 +602,10 @@ func parseDSNParams(cfg *Config, params string) (err error) { | |||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| func ensureHavePort(addr string) string { | ||||
| 	if _, _, err := net.SplitHostPort(addr); err != nil { | ||||
| 		return net.JoinHostPort(addr, "3306") | ||||
| 	} | ||||
| 	return addr | ||||
| } | ||||
|  |  | |||
							
								
								
									
										79
									
								
								vendor/github.com/go-sql-driver/mysql/errors.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										79
									
								
								vendor/github.com/go-sql-driver/mysql/errors.go
									
									
									
										generated
									
									
										vendored
									
									
								
							|  | @ -9,10 +9,8 @@ | |||
| package mysql | ||||
| 
 | ||||
| import ( | ||||
| 	"database/sql/driver" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"log" | ||||
| 	"os" | ||||
| ) | ||||
|  | @ -31,6 +29,12 @@ var ( | |||
| 	ErrPktSyncMul        = errors.New("commands out of sync. Did you run multiple statements at once?") | ||||
| 	ErrPktTooLarge       = errors.New("packet for query is too large. Try adjusting the 'max_allowed_packet' variable on the server") | ||||
| 	ErrBusyBuffer        = errors.New("busy buffer") | ||||
| 
 | ||||
| 	// errBadConnNoWrite is used for connection errors where nothing was sent to the database yet.
 | ||||
| 	// If this happens first in a function starting a database interaction, it should be replaced by driver.ErrBadConn
 | ||||
| 	// to trigger a resend.
 | ||||
| 	// See https://github.com/go-sql-driver/mysql/pull/302
 | ||||
| 	errBadConnNoWrite = errors.New("bad connection") | ||||
| ) | ||||
| 
 | ||||
| var errLog = Logger(log.New(os.Stderr, "[mysql] ", log.Ldate|log.Ltime|log.Lshortfile)) | ||||
|  | @ -59,74 +63,3 @@ type MySQLError struct { | |||
| func (me *MySQLError) Error() string { | ||||
| 	return fmt.Sprintf("Error %d: %s", me.Number, me.Message) | ||||
| } | ||||
| 
 | ||||
| // MySQLWarnings is an error type which represents a group of one or more MySQL
 | ||||
| // warnings
 | ||||
| type MySQLWarnings []MySQLWarning | ||||
| 
 | ||||
| func (mws MySQLWarnings) Error() string { | ||||
| 	var msg string | ||||
| 	for i, warning := range mws { | ||||
| 		if i > 0 { | ||||
| 			msg += "\r\n" | ||||
| 		} | ||||
| 		msg += fmt.Sprintf( | ||||
| 			"%s %s: %s", | ||||
| 			warning.Level, | ||||
| 			warning.Code, | ||||
| 			warning.Message, | ||||
| 		) | ||||
| 	} | ||||
| 	return msg | ||||
| } | ||||
| 
 | ||||
| // MySQLWarning is an error type which represents a single MySQL warning.
 | ||||
| // Warnings are returned in groups only. See MySQLWarnings
 | ||||
| type MySQLWarning struct { | ||||
| 	Level   string | ||||
| 	Code    string | ||||
| 	Message string | ||||
| } | ||||
| 
 | ||||
| func (mc *mysqlConn) getWarnings() (err error) { | ||||
| 	rows, err := mc.Query("SHOW WARNINGS", nil) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	var warnings = MySQLWarnings{} | ||||
| 	var values = make([]driver.Value, 3) | ||||
| 
 | ||||
| 	for { | ||||
| 		err = rows.Next(values) | ||||
| 		switch err { | ||||
| 		case nil: | ||||
| 			warning := MySQLWarning{} | ||||
| 
 | ||||
| 			if raw, ok := values[0].([]byte); ok { | ||||
| 				warning.Level = string(raw) | ||||
| 			} else { | ||||
| 				warning.Level = fmt.Sprintf("%s", values[0]) | ||||
| 			} | ||||
| 			if raw, ok := values[1].([]byte); ok { | ||||
| 				warning.Code = string(raw) | ||||
| 			} else { | ||||
| 				warning.Code = fmt.Sprintf("%s", values[1]) | ||||
| 			} | ||||
| 			if raw, ok := values[2].([]byte); ok { | ||||
| 				warning.Message = string(raw) | ||||
| 			} else { | ||||
| 				warning.Message = fmt.Sprintf("%s", values[0]) | ||||
| 			} | ||||
| 
 | ||||
| 			warnings = append(warnings, warning) | ||||
| 
 | ||||
| 		case io.EOF: | ||||
| 			return warnings | ||||
| 
 | ||||
| 		default: | ||||
| 			rows.Close() | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
							
								
								
									
										194
									
								
								vendor/github.com/go-sql-driver/mysql/fields.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								vendor/github.com/go-sql-driver/mysql/fields.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,194 @@ | |||
| // Go MySQL Driver - A MySQL-Driver for Go's database/sql package
 | ||||
| //
 | ||||
| // Copyright 2017 The Go-MySQL-Driver Authors. All rights reserved.
 | ||||
| //
 | ||||
| // This Source Code Form is subject to the terms of the Mozilla Public
 | ||||
| // License, v. 2.0. If a copy of the MPL was not distributed with this file,
 | ||||
| // You can obtain one at http://mozilla.org/MPL/2.0/.
 | ||||
| 
 | ||||
| package mysql | ||||
| 
 | ||||
| import ( | ||||
| 	"database/sql" | ||||
| 	"reflect" | ||||
| ) | ||||
| 
 | ||||
| func (mf *mysqlField) typeDatabaseName() string { | ||||
| 	switch mf.fieldType { | ||||
| 	case fieldTypeBit: | ||||
| 		return "BIT" | ||||
| 	case fieldTypeBLOB: | ||||
| 		if mf.charSet != collations[binaryCollation] { | ||||
| 			return "TEXT" | ||||
| 		} | ||||
| 		return "BLOB" | ||||
| 	case fieldTypeDate: | ||||
| 		return "DATE" | ||||
| 	case fieldTypeDateTime: | ||||
| 		return "DATETIME" | ||||
| 	case fieldTypeDecimal: | ||||
| 		return "DECIMAL" | ||||
| 	case fieldTypeDouble: | ||||
| 		return "DOUBLE" | ||||
| 	case fieldTypeEnum: | ||||
| 		return "ENUM" | ||||
| 	case fieldTypeFloat: | ||||
| 		return "FLOAT" | ||||
| 	case fieldTypeGeometry: | ||||
| 		return "GEOMETRY" | ||||
| 	case fieldTypeInt24: | ||||
| 		return "MEDIUMINT" | ||||
| 	case fieldTypeJSON: | ||||
| 		return "JSON" | ||||
| 	case fieldTypeLong: | ||||
| 		return "INT" | ||||
| 	case fieldTypeLongBLOB: | ||||
| 		if mf.charSet != collations[binaryCollation] { | ||||
| 			return "LONGTEXT" | ||||
| 		} | ||||
| 		return "LONGBLOB" | ||||
| 	case fieldTypeLongLong: | ||||
| 		return "BIGINT" | ||||
| 	case fieldTypeMediumBLOB: | ||||
| 		if mf.charSet != collations[binaryCollation] { | ||||
| 			return "MEDIUMTEXT" | ||||
| 		} | ||||
| 		return "MEDIUMBLOB" | ||||
| 	case fieldTypeNewDate: | ||||
| 		return "DATE" | ||||
| 	case fieldTypeNewDecimal: | ||||
| 		return "DECIMAL" | ||||
| 	case fieldTypeNULL: | ||||
| 		return "NULL" | ||||
| 	case fieldTypeSet: | ||||
| 		return "SET" | ||||
| 	case fieldTypeShort: | ||||
| 		return "SMALLINT" | ||||
| 	case fieldTypeString: | ||||
| 		if mf.charSet == collations[binaryCollation] { | ||||
| 			return "BINARY" | ||||
| 		} | ||||
| 		return "CHAR" | ||||
| 	case fieldTypeTime: | ||||
| 		return "TIME" | ||||
| 	case fieldTypeTimestamp: | ||||
| 		return "TIMESTAMP" | ||||
| 	case fieldTypeTiny: | ||||
| 		return "TINYINT" | ||||
| 	case fieldTypeTinyBLOB: | ||||
| 		if mf.charSet != collations[binaryCollation] { | ||||
| 			return "TINYTEXT" | ||||
| 		} | ||||
| 		return "TINYBLOB" | ||||
| 	case fieldTypeVarChar: | ||||
| 		if mf.charSet == collations[binaryCollation] { | ||||
| 			return "VARBINARY" | ||||
| 		} | ||||
| 		return "VARCHAR" | ||||
| 	case fieldTypeVarString: | ||||
| 		if mf.charSet == collations[binaryCollation] { | ||||
| 			return "VARBINARY" | ||||
| 		} | ||||
| 		return "VARCHAR" | ||||
| 	case fieldTypeYear: | ||||
| 		return "YEAR" | ||||
| 	default: | ||||
| 		return "" | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| var ( | ||||
| 	scanTypeFloat32   = reflect.TypeOf(float32(0)) | ||||
| 	scanTypeFloat64   = reflect.TypeOf(float64(0)) | ||||
| 	scanTypeInt8      = reflect.TypeOf(int8(0)) | ||||
| 	scanTypeInt16     = reflect.TypeOf(int16(0)) | ||||
| 	scanTypeInt32     = reflect.TypeOf(int32(0)) | ||||
| 	scanTypeInt64     = reflect.TypeOf(int64(0)) | ||||
| 	scanTypeNullFloat = reflect.TypeOf(sql.NullFloat64{}) | ||||
| 	scanTypeNullInt   = reflect.TypeOf(sql.NullInt64{}) | ||||
| 	scanTypeNullTime  = reflect.TypeOf(NullTime{}) | ||||
| 	scanTypeUint8     = reflect.TypeOf(uint8(0)) | ||||
| 	scanTypeUint16    = reflect.TypeOf(uint16(0)) | ||||
| 	scanTypeUint32    = reflect.TypeOf(uint32(0)) | ||||
| 	scanTypeUint64    = reflect.TypeOf(uint64(0)) | ||||
| 	scanTypeRawBytes  = reflect.TypeOf(sql.RawBytes{}) | ||||
| 	scanTypeUnknown   = reflect.TypeOf(new(interface{})) | ||||
| ) | ||||
| 
 | ||||
| type mysqlField struct { | ||||
| 	tableName string | ||||
| 	name      string | ||||
| 	length    uint32 | ||||
| 	flags     fieldFlag | ||||
| 	fieldType fieldType | ||||
| 	decimals  byte | ||||
| 	charSet   uint8 | ||||
| } | ||||
| 
 | ||||
| func (mf *mysqlField) scanType() reflect.Type { | ||||
| 	switch mf.fieldType { | ||||
| 	case fieldTypeTiny: | ||||
| 		if mf.flags&flagNotNULL != 0 { | ||||
| 			if mf.flags&flagUnsigned != 0 { | ||||
| 				return scanTypeUint8 | ||||
| 			} | ||||
| 			return scanTypeInt8 | ||||
| 		} | ||||
| 		return scanTypeNullInt | ||||
| 
 | ||||
| 	case fieldTypeShort, fieldTypeYear: | ||||
| 		if mf.flags&flagNotNULL != 0 { | ||||
| 			if mf.flags&flagUnsigned != 0 { | ||||
| 				return scanTypeUint16 | ||||
| 			} | ||||
| 			return scanTypeInt16 | ||||
| 		} | ||||
| 		return scanTypeNullInt | ||||
| 
 | ||||
| 	case fieldTypeInt24, fieldTypeLong: | ||||
| 		if mf.flags&flagNotNULL != 0 { | ||||
| 			if mf.flags&flagUnsigned != 0 { | ||||
| 				return scanTypeUint32 | ||||
| 			} | ||||
| 			return scanTypeInt32 | ||||
| 		} | ||||
| 		return scanTypeNullInt | ||||
| 
 | ||||
| 	case fieldTypeLongLong: | ||||
| 		if mf.flags&flagNotNULL != 0 { | ||||
| 			if mf.flags&flagUnsigned != 0 { | ||||
| 				return scanTypeUint64 | ||||
| 			} | ||||
| 			return scanTypeInt64 | ||||
| 		} | ||||
| 		return scanTypeNullInt | ||||
| 
 | ||||
| 	case fieldTypeFloat: | ||||
| 		if mf.flags&flagNotNULL != 0 { | ||||
| 			return scanTypeFloat32 | ||||
| 		} | ||||
| 		return scanTypeNullFloat | ||||
| 
 | ||||
| 	case fieldTypeDouble: | ||||
| 		if mf.flags&flagNotNULL != 0 { | ||||
| 			return scanTypeFloat64 | ||||
| 		} | ||||
| 		return scanTypeNullFloat | ||||
| 
 | ||||
| 	case fieldTypeDecimal, fieldTypeNewDecimal, fieldTypeVarChar, | ||||
| 		fieldTypeBit, fieldTypeEnum, fieldTypeSet, fieldTypeTinyBLOB, | ||||
| 		fieldTypeMediumBLOB, fieldTypeLongBLOB, fieldTypeBLOB, | ||||
| 		fieldTypeVarString, fieldTypeString, fieldTypeGeometry, fieldTypeJSON, | ||||
| 		fieldTypeTime: | ||||
| 		return scanTypeRawBytes | ||||
| 
 | ||||
| 	case fieldTypeDate, fieldTypeNewDate, | ||||
| 		fieldTypeTimestamp, fieldTypeDateTime: | ||||
| 		// NullTime is always returned for more consistent behavior as it can
 | ||||
| 		// handle both cases of parseTime regardless if the field is nullable.
 | ||||
| 		return scanTypeNullTime | ||||
| 
 | ||||
| 	default: | ||||
| 		return scanTypeUnknown | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										6
									
								
								vendor/github.com/go-sql-driver/mysql/infile.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								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 { | ||||
|  | @ -173,8 +174,7 @@ func (mc *mysqlConn) handleInFileRequest(name string) (err error) { | |||
| 
 | ||||
| 	// read OK packet
 | ||||
| 	if err == nil { | ||||
| 		_, err = mc.readResultOK() | ||||
| 		return err | ||||
| 		return mc.readResultOK() | ||||
| 	} | ||||
| 
 | ||||
| 	mc.readPacket() | ||||
|  |  | |||
							
								
								
									
										441
									
								
								vendor/github.com/go-sql-driver/mysql/packets.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										441
									
								
								vendor/github.com/go-sql-driver/mysql/packets.go
									
									
									
										generated
									
									
										vendored
									
									
								
							|  | @ -25,26 +25,23 @@ import ( | |||
| 
 | ||||
| // Read packet to buffer 'data'
 | ||||
| func (mc *mysqlConn) readPacket() ([]byte, error) { | ||||
| 	var payload []byte | ||||
| 	var prevData []byte | ||||
| 	for { | ||||
| 		// Read packet header
 | ||||
| 		// 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 | ||||
| 			return nil, ErrInvalidConn | ||||
| 		} | ||||
| 
 | ||||
| 		// Packet Length [24 bit]
 | ||||
| 		// packet length [24 bit]
 | ||||
| 		pktLen := int(uint32(data[0]) | uint32(data[1])<<8 | uint32(data[2])<<16) | ||||
| 
 | ||||
| 		if pktLen < 1 { | ||||
| 			errLog.Print(ErrMalformPkt) | ||||
| 			mc.Close() | ||||
| 			return nil, driver.ErrBadConn | ||||
| 		} | ||||
| 
 | ||||
| 		// Check Packet Sync [8 bit]
 | ||||
| 		// check packet sync [8 bit]
 | ||||
| 		if data[3] != mc.sequence { | ||||
| 			if data[3] > mc.sequence { | ||||
| 				return nil, ErrPktSyncMul | ||||
|  | @ -53,26 +50,41 @@ func (mc *mysqlConn) readPacket() ([]byte, error) { | |||
| 		} | ||||
| 		mc.sequence++ | ||||
| 
 | ||||
| 		// Read packet body [pktLen bytes]
 | ||||
| 		// packets with length 0 terminate a previous packet which is a
 | ||||
| 		// multiple of (2^24)−1 bytes long
 | ||||
| 		if pktLen == 0 { | ||||
| 			// there was no previous packet
 | ||||
| 			if prevData == nil { | ||||
| 				errLog.Print(ErrMalformPkt) | ||||
| 				mc.Close() | ||||
| 				return nil, ErrInvalidConn | ||||
| 			} | ||||
| 
 | ||||
| 			return prevData, nil | ||||
| 		} | ||||
| 
 | ||||
| 		// 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 | ||||
| 			return nil, ErrInvalidConn | ||||
| 		} | ||||
| 
 | ||||
| 		isLastPacket := (pktLen < maxPacketSize) | ||||
| 		// return data if this was the last packet
 | ||||
| 		if pktLen < maxPacketSize { | ||||
| 			// zero allocations for non-split packets
 | ||||
| 			if prevData == nil { | ||||
| 				return data, nil | ||||
| 			} | ||||
| 
 | ||||
| 		// Zero allocations for non-splitting packets
 | ||||
| 		if isLastPacket && payload == nil { | ||||
| 			return data, nil | ||||
| 			return append(prevData, data...), nil | ||||
| 		} | ||||
| 
 | ||||
| 		payload = append(payload, data...) | ||||
| 
 | ||||
| 		if isLastPacket { | ||||
| 			return payload, nil | ||||
| 		} | ||||
| 		prevData = append(prevData, data...) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -119,33 +131,47 @@ 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 | ||||
| 			} | ||||
| 			if n == 0 && pktLen == len(data)-4 { | ||||
| 				// only for the first loop iteration when nothing was written yet
 | ||||
| 				return errBadConnNoWrite | ||||
| 			} | ||||
| 			mc.cleanup() | ||||
| 			errLog.Print(err) | ||||
| 		} | ||||
| 		return driver.ErrBadConn | ||||
| 		return ErrInvalidConn | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /****************************************************************************** | ||||
| *                           Initialisation Process                            * | ||||
| *                           Initialization Process                            * | ||||
| ******************************************************************************/ | ||||
| 
 | ||||
| // Handshake Initialization Packet
 | ||||
| // http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::Handshake
 | ||||
| func (mc *mysqlConn) readInitPacket() ([]byte, error) { | ||||
| func (mc *mysqlConn) readHandshakePacket() ([]byte, string, error) { | ||||
| 	data, err := mc.readPacket() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 		// for init we can rewrite this to ErrBadConn for sql.Driver to retry, since
 | ||||
| 		// in connection initialization we don't risk retrying non-idempotent actions.
 | ||||
| 		if err == ErrInvalidConn { | ||||
| 			return nil, "", driver.ErrBadConn | ||||
| 		} | ||||
| 		return nil, "", err | ||||
| 	} | ||||
| 
 | ||||
| 	if data[0] == iERR { | ||||
| 		return nil, mc.handleErrorPacket(data) | ||||
| 		return nil, "", mc.handleErrorPacket(data) | ||||
| 	} | ||||
| 
 | ||||
| 	// protocol version [1 byte]
 | ||||
| 	if data[0] < minProtocolVersion { | ||||
| 		return nil, fmt.Errorf( | ||||
| 		return nil, "", fmt.Errorf( | ||||
| 			"unsupported protocol version %d. Version %d or higher is required", | ||||
| 			data[0], | ||||
| 			minProtocolVersion, | ||||
|  | @ -157,7 +183,7 @@ func (mc *mysqlConn) readInitPacket() ([]byte, error) { | |||
| 	pos := 1 + bytes.IndexByte(data[1:], 0x00) + 1 + 4 | ||||
| 
 | ||||
| 	// first part of the password cipher [8 bytes]
 | ||||
| 	cipher := data[pos : pos+8] | ||||
| 	authData := data[pos : pos+8] | ||||
| 
 | ||||
| 	// (filler) always 0x00 [1 byte]
 | ||||
| 	pos += 8 + 1 | ||||
|  | @ -165,13 +191,14 @@ func (mc *mysqlConn) readInitPacket() ([]byte, error) { | |||
| 	// capability flags (lower 2 bytes) [2 bytes]
 | ||||
| 	mc.flags = clientFlag(binary.LittleEndian.Uint16(data[pos : pos+2])) | ||||
| 	if mc.flags&clientProtocol41 == 0 { | ||||
| 		return nil, ErrOldProtocol | ||||
| 		return nil, "", ErrOldProtocol | ||||
| 	} | ||||
| 	if mc.flags&clientSSL == 0 && mc.cfg.tls != nil { | ||||
| 		return nil, ErrNoTLS | ||||
| 		return nil, "", ErrNoTLS | ||||
| 	} | ||||
| 	pos += 2 | ||||
| 
 | ||||
| 	plugin := "" | ||||
| 	if len(data) > pos { | ||||
| 		// character set [1 byte]
 | ||||
| 		// status flags [2 bytes]
 | ||||
|  | @ -192,32 +219,34 @@ func (mc *mysqlConn) readInitPacket() ([]byte, error) { | |||
| 		//
 | ||||
| 		// The official Python library uses the fixed length 12
 | ||||
| 		// which seems to work but technically could have a hidden bug.
 | ||||
| 		cipher = append(cipher, data[pos:pos+12]...) | ||||
| 		authData = append(authData, data[pos:pos+12]...) | ||||
| 		pos += 13 | ||||
| 
 | ||||
| 		// TODO: Verify string termination
 | ||||
| 		// EOF if version (>= 5.5.7 and < 5.5.10) or (>= 5.6.0 and < 5.6.2)
 | ||||
| 		// \NUL otherwise
 | ||||
| 		//
 | ||||
| 		//if data[len(data)-1] == 0 {
 | ||||
| 		//	return
 | ||||
| 		//}
 | ||||
| 		//return ErrMalformPkt
 | ||||
| 		if end := bytes.IndexByte(data[pos:], 0x00); end != -1 { | ||||
| 			plugin = string(data[pos : pos+end]) | ||||
| 		} else { | ||||
| 			plugin = string(data[pos:]) | ||||
| 		} | ||||
| 
 | ||||
| 		// make a memory safe copy of the cipher slice
 | ||||
| 		var b [20]byte | ||||
| 		copy(b[:], cipher) | ||||
| 		return b[:], nil | ||||
| 		copy(b[:], authData) | ||||
| 		return b[:], plugin, nil | ||||
| 	} | ||||
| 
 | ||||
| 	plugin = defaultAuthPlugin | ||||
| 
 | ||||
| 	// make a memory safe copy of the cipher slice
 | ||||
| 	var b [8]byte | ||||
| 	copy(b[:], cipher) | ||||
| 	return b[:], nil | ||||
| 	copy(b[:], authData) | ||||
| 	return b[:], plugin, nil | ||||
| } | ||||
| 
 | ||||
| // Client Authentication Packet
 | ||||
| // http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::HandshakeResponse
 | ||||
| func (mc *mysqlConn) writeAuthPacket(cipher []byte) error { | ||||
| func (mc *mysqlConn) writeHandshakeResponsePacket(authResp []byte, addNUL bool, plugin string) error { | ||||
| 	// Adjust client flags based on server support
 | ||||
| 	clientFlags := clientProtocol41 | | ||||
| 		clientSecureConn | | ||||
|  | @ -241,10 +270,19 @@ func (mc *mysqlConn) writeAuthPacket(cipher []byte) error { | |||
| 		clientFlags |= clientMultiStatements | ||||
| 	} | ||||
| 
 | ||||
| 	// User Password
 | ||||
| 	scrambleBuff := scramblePassword(cipher, []byte(mc.cfg.Passwd)) | ||||
| 	// encode length of the auth plugin data
 | ||||
| 	var authRespLEIBuf [9]byte | ||||
| 	authRespLEI := appendLengthEncodedInteger(authRespLEIBuf[:0], uint64(len(authResp))) | ||||
| 	if len(authRespLEI) > 1 { | ||||
| 		// if the length can not be written in 1 byte, it must be written as a
 | ||||
| 		// length encoded integer
 | ||||
| 		clientFlags |= clientPluginAuthLenEncClientData | ||||
| 	} | ||||
| 
 | ||||
| 	pktLen := 4 + 4 + 1 + 23 + len(mc.cfg.User) + 1 + 1 + len(scrambleBuff) + 21 + 1 | ||||
| 	pktLen := 4 + 4 + 1 + 23 + len(mc.cfg.User) + 1 + len(authRespLEI) + len(authResp) + 21 + 1 | ||||
| 	if addNUL { | ||||
| 		pktLen++ | ||||
| 	} | ||||
| 
 | ||||
| 	// To specify a db name
 | ||||
| 	if n := len(mc.cfg.DBName); n > 0 { | ||||
|  | @ -255,9 +293,9 @@ func (mc *mysqlConn) writeAuthPacket(cipher []byte) error { | |||
| 	// Calculate packet length and get buffer with that size
 | ||||
| 	data := mc.buf.takeSmallBuffer(pktLen + 4) | ||||
| 	if data == nil { | ||||
| 		// can not take the buffer. Something must be wrong with the connection
 | ||||
| 		// cannot take the buffer. Something must be wrong with the connection
 | ||||
| 		errLog.Print(ErrBusyBuffer) | ||||
| 		return driver.ErrBadConn | ||||
| 		return errBadConnNoWrite | ||||
| 	} | ||||
| 
 | ||||
| 	// ClientFlags [32 bit]
 | ||||
|  | @ -312,9 +350,13 @@ func (mc *mysqlConn) writeAuthPacket(cipher []byte) error { | |||
| 	data[pos] = 0x00 | ||||
| 	pos++ | ||||
| 
 | ||||
| 	// ScrambleBuffer [length encoded integer]
 | ||||
| 	data[pos] = byte(len(scrambleBuff)) | ||||
| 	pos += 1 + copy(data[pos+1:], scrambleBuff) | ||||
| 	// Auth Data [length encoded integer]
 | ||||
| 	pos += copy(data[pos:], authRespLEI) | ||||
| 	pos += copy(data[pos:], authResp) | ||||
| 	if addNUL { | ||||
| 		data[pos] = 0x00 | ||||
| 		pos++ | ||||
| 	} | ||||
| 
 | ||||
| 	// Databasename [null terminated string]
 | ||||
| 	if len(mc.cfg.DBName) > 0 { | ||||
|  | @ -323,72 +365,32 @@ func (mc *mysqlConn) writeAuthPacket(cipher []byte) error { | |||
| 		pos++ | ||||
| 	} | ||||
| 
 | ||||
| 	// Assume native client during response
 | ||||
| 	pos += copy(data[pos:], "mysql_native_password") | ||||
| 	pos += copy(data[pos:], plugin) | ||||
| 	data[pos] = 0x00 | ||||
| 
 | ||||
| 	// Send Auth packet
 | ||||
| 	return mc.writePacket(data) | ||||
| } | ||||
| 
 | ||||
| //  Client old authentication packet
 | ||||
| // http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse
 | ||||
| func (mc *mysqlConn) writeOldAuthPacket(cipher []byte) error { | ||||
| 	// User password
 | ||||
| 	scrambleBuff := scrambleOldPassword(cipher, []byte(mc.cfg.Passwd)) | ||||
| 
 | ||||
| 	// Calculate the packet length and add a tailing 0
 | ||||
| 	pktLen := len(scrambleBuff) + 1 | ||||
| 	data := mc.buf.takeSmallBuffer(4 + pktLen) | ||||
| func (mc *mysqlConn) writeAuthSwitchPacket(authData []byte, addNUL bool) error { | ||||
| 	pktLen := 4 + len(authData) | ||||
| 	if addNUL { | ||||
| 		pktLen++ | ||||
| 	} | ||||
| 	data := mc.buf.takeSmallBuffer(pktLen) | ||||
| 	if data == nil { | ||||
| 		// can not take the buffer. Something must be wrong with the connection
 | ||||
| 		// cannot take the buffer. Something must be wrong with the connection
 | ||||
| 		errLog.Print(ErrBusyBuffer) | ||||
| 		return driver.ErrBadConn | ||||
| 		return errBadConnNoWrite | ||||
| 	} | ||||
| 
 | ||||
| 	// Add the scrambled password [null terminated string]
 | ||||
| 	copy(data[4:], scrambleBuff) | ||||
| 	data[4+pktLen-1] = 0x00 | ||||
| 
 | ||||
| 	return mc.writePacket(data) | ||||
| } | ||||
| 
 | ||||
| //  Client clear text authentication packet
 | ||||
| // http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse
 | ||||
| func (mc *mysqlConn) writeClearAuthPacket() error { | ||||
| 	// Calculate the packet length and add a tailing 0
 | ||||
| 	pktLen := len(mc.cfg.Passwd) + 1 | ||||
| 	data := mc.buf.takeSmallBuffer(4 + pktLen) | ||||
| 	if data == nil { | ||||
| 		// can not take the buffer. Something must be wrong with the connection
 | ||||
| 		errLog.Print(ErrBusyBuffer) | ||||
| 		return driver.ErrBadConn | ||||
| 	// Add the auth data [EOF]
 | ||||
| 	copy(data[4:], authData) | ||||
| 	if addNUL { | ||||
| 		data[pktLen-1] = 0x00 | ||||
| 	} | ||||
| 
 | ||||
| 	// Add the clear password [null terminated string]
 | ||||
| 	copy(data[4:], mc.cfg.Passwd) | ||||
| 	data[4+pktLen-1] = 0x00 | ||||
| 
 | ||||
| 	return mc.writePacket(data) | ||||
| } | ||||
| 
 | ||||
| //  Native password authentication method
 | ||||
| // http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchResponse
 | ||||
| func (mc *mysqlConn) writeNativeAuthPacket(cipher []byte) error { | ||||
| 	scrambleBuff := scramblePassword(cipher, []byte(mc.cfg.Passwd)) | ||||
| 
 | ||||
| 	// Calculate the packet length and add a tailing 0
 | ||||
| 	pktLen := len(scrambleBuff) | ||||
| 	data := mc.buf.takeSmallBuffer(4 + pktLen) | ||||
| 	if data == nil { | ||||
| 		// can not take the buffer. Something must be wrong with the connection
 | ||||
| 		errLog.Print(ErrBusyBuffer) | ||||
| 		return driver.ErrBadConn | ||||
| 	} | ||||
| 
 | ||||
| 	// Add the scramble
 | ||||
| 	copy(data[4:], scrambleBuff) | ||||
| 
 | ||||
| 	return mc.writePacket(data) | ||||
| } | ||||
| 
 | ||||
|  | @ -402,9 +404,9 @@ func (mc *mysqlConn) writeCommandPacket(command byte) error { | |||
| 
 | ||||
| 	data := mc.buf.takeSmallBuffer(4 + 1) | ||||
| 	if data == nil { | ||||
| 		// can not take the buffer. Something must be wrong with the connection
 | ||||
| 		// cannot take the buffer. Something must be wrong with the connection
 | ||||
| 		errLog.Print(ErrBusyBuffer) | ||||
| 		return driver.ErrBadConn | ||||
| 		return errBadConnNoWrite | ||||
| 	} | ||||
| 
 | ||||
| 	// Add command byte
 | ||||
|  | @ -421,9 +423,9 @@ func (mc *mysqlConn) writeCommandPacketStr(command byte, arg string) error { | |||
| 	pktLen := 1 + len(arg) | ||||
| 	data := mc.buf.takeBuffer(pktLen + 4) | ||||
| 	if data == nil { | ||||
| 		// can not take the buffer. Something must be wrong with the connection
 | ||||
| 		// cannot take the buffer. Something must be wrong with the connection
 | ||||
| 		errLog.Print(ErrBusyBuffer) | ||||
| 		return driver.ErrBadConn | ||||
| 		return errBadConnNoWrite | ||||
| 	} | ||||
| 
 | ||||
| 	// Add command byte
 | ||||
|  | @ -442,9 +444,9 @@ func (mc *mysqlConn) writeCommandPacketUint32(command byte, arg uint32) error { | |||
| 
 | ||||
| 	data := mc.buf.takeSmallBuffer(4 + 1 + 4) | ||||
| 	if data == nil { | ||||
| 		// can not take the buffer. Something must be wrong with the connection
 | ||||
| 		// cannot take the buffer. Something must be wrong with the connection
 | ||||
| 		errLog.Print(ErrBusyBuffer) | ||||
| 		return driver.ErrBadConn | ||||
| 		return errBadConnNoWrite | ||||
| 	} | ||||
| 
 | ||||
| 	// Add command byte
 | ||||
|  | @ -464,43 +466,50 @@ func (mc *mysqlConn) writeCommandPacketUint32(command byte, arg uint32) error { | |||
| *                              Result Packets                                 * | ||||
| ******************************************************************************/ | ||||
| 
 | ||||
| // Returns error if Packet is not an 'Result OK'-Packet
 | ||||
| func (mc *mysqlConn) readResultOK() ([]byte, error) { | ||||
| func (mc *mysqlConn) readAuthResult() ([]byte, string, error) { | ||||
| 	data, err := mc.readPacket() | ||||
| 	if err == nil { | ||||
| 		// packet indicator
 | ||||
| 		switch data[0] { | ||||
| 
 | ||||
| 		case iOK: | ||||
| 			return nil, mc.handleOkPacket(data) | ||||
| 
 | ||||
| 		case iEOF: | ||||
| 			if len(data) > 1 { | ||||
| 				pluginEndIndex := bytes.IndexByte(data, 0x00) | ||||
| 				plugin := string(data[1:pluginEndIndex]) | ||||
| 				cipher := data[pluginEndIndex+1 : len(data)-1] | ||||
| 
 | ||||
| 				if plugin == "mysql_old_password" { | ||||
| 					// using old_passwords
 | ||||
| 					return cipher, ErrOldPassword | ||||
| 				} else if plugin == "mysql_clear_password" { | ||||
| 					// using clear text password
 | ||||
| 					return cipher, ErrCleartextPassword | ||||
| 				} else if plugin == "mysql_native_password" { | ||||
| 					// using mysql default authentication method
 | ||||
| 					return cipher, ErrNativePassword | ||||
| 				} else { | ||||
| 					return cipher, ErrUnknownPlugin | ||||
| 				} | ||||
| 			} else { | ||||
| 				return nil, ErrOldPassword | ||||
| 			} | ||||
| 
 | ||||
| 		default: // Error otherwise
 | ||||
| 			return nil, mc.handleErrorPacket(data) | ||||
| 		} | ||||
| 	if err != nil { | ||||
| 		return nil, "", err | ||||
| 	} | ||||
| 	return nil, err | ||||
| 
 | ||||
| 	// packet indicator
 | ||||
| 	switch data[0] { | ||||
| 
 | ||||
| 	case iOK: | ||||
| 		return nil, "", mc.handleOkPacket(data) | ||||
| 
 | ||||
| 	case iAuthMoreData: | ||||
| 		return data[1:], "", err | ||||
| 
 | ||||
| 	case iEOF: | ||||
| 		if len(data) < 1 { | ||||
| 			// https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::OldAuthSwitchRequest
 | ||||
| 			return nil, "mysql_old_password", nil | ||||
| 		} | ||||
| 		pluginEndIndex := bytes.IndexByte(data, 0x00) | ||||
| 		if pluginEndIndex < 0 { | ||||
| 			return nil, "", ErrMalformPkt | ||||
| 		} | ||||
| 		plugin := string(data[1:pluginEndIndex]) | ||||
| 		authData := data[pluginEndIndex+1:] | ||||
| 		return authData, plugin, nil | ||||
| 
 | ||||
| 	default: // Error otherwise
 | ||||
| 		return nil, "", mc.handleErrorPacket(data) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Returns error if Packet is not an 'Result OK'-Packet
 | ||||
| func (mc *mysqlConn) readResultOK() error { | ||||
| 	data, err := mc.readPacket() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	if data[0] == iOK { | ||||
| 		return mc.handleOkPacket(data) | ||||
| 	} | ||||
| 	return mc.handleErrorPacket(data) | ||||
| } | ||||
| 
 | ||||
| // Result Set Header Packet
 | ||||
|  | @ -543,6 +552,22 @@ 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
 | ||||
| 	// 1290: ER_OPTION_PREVENTS_STATEMENT (returned by Aurora during failover)
 | ||||
| 	if (errno == 1792 || errno == 1290) && mc.cfg.RejectReadOnly { | ||||
| 		// Oops; we are connected to a read-only connection, and won't be able
 | ||||
| 		// to issue any write statements. Since RejectReadOnly is configured,
 | ||||
| 		// we throw away this connection hoping this one would have write
 | ||||
| 		// permission. This is specifically for a possible race condition
 | ||||
| 		// during failover (e.g. on AWS Aurora). See README.md for more.
 | ||||
| 		//
 | ||||
| 		// We explicitly close the connection before returning
 | ||||
| 		// driver.ErrBadConn to ensure that `database/sql` purges this
 | ||||
| 		// connection and initiates a new one for next statement next time.
 | ||||
| 		mc.Close() | ||||
| 		return driver.ErrBadConn | ||||
| 	} | ||||
| 
 | ||||
| 	pos := 3 | ||||
| 
 | ||||
| 	// SQL State [optional: # + 5bytes string]
 | ||||
|  | @ -577,19 +602,12 @@ 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 | ||||
| 	} | ||||
| 
 | ||||
| 	// warning count [2 bytes]
 | ||||
| 	if !mc.strict { | ||||
| 	if mc.status&statusMoreResultsExists != 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	pos := 1 + n + m + 2 | ||||
| 	if binary.LittleEndian.Uint16(data[pos:pos+2]) > 0 { | ||||
| 		return mc.getWarnings() | ||||
| 	} | ||||
| 	// warning count [2 bytes]
 | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
|  | @ -661,14 +679,21 @@ func (mc *mysqlConn) readColumns(count int) ([]mysqlField, error) { | |||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		pos += n | ||||
| 
 | ||||
| 		// Filler [uint8]
 | ||||
| 		pos++ | ||||
| 
 | ||||
| 		// Charset [charset, collation uint8]
 | ||||
| 		columns[i].charSet = data[pos] | ||||
| 		pos += 2 | ||||
| 
 | ||||
| 		// Length [uint32]
 | ||||
| 		pos += n + 1 + 2 + 4 | ||||
| 		columns[i].length = binary.LittleEndian.Uint32(data[pos : pos+4]) | ||||
| 		pos += 4 | ||||
| 
 | ||||
| 		// Field type [uint8]
 | ||||
| 		columns[i].fieldType = data[pos] | ||||
| 		columns[i].fieldType = fieldType(data[pos]) | ||||
| 		pos++ | ||||
| 
 | ||||
| 		// Flags [uint16]
 | ||||
|  | @ -691,6 +716,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 | ||||
|  | @ -700,10 +729,10 @@ 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:]) | ||||
| 		if err := rows.mc.discardResults(); err != nil { | ||||
| 			return err | ||||
| 		rows.rs.done = true | ||||
| 		if !rows.HasNextResultSet() { | ||||
| 			rows.mc = nil | ||||
| 		} | ||||
| 		rows.mc = nil | ||||
| 		return io.EOF | ||||
| 	} | ||||
| 	if data[0] == iERR { | ||||
|  | @ -725,7 +754,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( | ||||
|  | @ -797,14 +826,7 @@ func (stmt *mysqlStmt) readPrepareResultPacket() (uint16, error) { | |||
| 		// Reserved [8 bit]
 | ||||
| 
 | ||||
| 		// Warning count [16 bit uint]
 | ||||
| 		if !stmt.mc.strict { | ||||
| 			return columnCount, nil | ||||
| 		} | ||||
| 
 | ||||
| 		// Check for warnings count > 0, only available in MySQL > 4.1
 | ||||
| 		if len(data) >= 12 && binary.LittleEndian.Uint16(data[10:12]) > 0 { | ||||
| 			return columnCount, stmt.mc.getWarnings() | ||||
| 		} | ||||
| 		return columnCount, nil | ||||
| 	} | ||||
| 	return 0, err | ||||
|  | @ -821,7 +843,7 @@ func (stmt *mysqlStmt) writeCommandLongData(paramID int, arg []byte) error { | |||
| 	// 2 bytes paramID
 | ||||
| 	const dataOffset = 1 + 4 + 2 | ||||
| 
 | ||||
| 	// Can not use the write buffer since
 | ||||
| 	// Cannot use the write buffer since
 | ||||
| 	// a) the buffer is too small
 | ||||
| 	// b) it is in use
 | ||||
| 	data := make([]byte, 4+1+4+2+len(arg)) | ||||
|  | @ -876,6 +898,12 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error { | |||
| 	const minPktLen = 4 + 1 + 4 + 1 + 4 | ||||
| 	mc := stmt.mc | ||||
| 
 | ||||
| 	// Determine threshould dynamically to avoid packet size shortage.
 | ||||
| 	longDataSize := mc.maxAllowedPacket / (stmt.paramCount + 1) | ||||
| 	if longDataSize < 64 { | ||||
| 		longDataSize = 64 | ||||
| 	} | ||||
| 
 | ||||
| 	// Reset packet-sequence
 | ||||
| 	mc.sequence = 0 | ||||
| 
 | ||||
|  | @ -887,9 +915,9 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error { | |||
| 		data = mc.buf.takeCompleteBuffer() | ||||
| 	} | ||||
| 	if data == nil { | ||||
| 		// can not take the buffer. Something must be wrong with the connection
 | ||||
| 		// cannot take the buffer. Something must be wrong with the connection
 | ||||
| 		errLog.Print(ErrBusyBuffer) | ||||
| 		return driver.ErrBadConn | ||||
| 		return errBadConnNoWrite | ||||
| 	} | ||||
| 
 | ||||
| 	// command [1 byte]
 | ||||
|  | @ -948,7 +976,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error { | |||
| 			// build NULL-bitmap
 | ||||
| 			if arg == nil { | ||||
| 				nullMask[i/8] |= 1 << (uint(i) & 7) | ||||
| 				paramTypes[i+i] = fieldTypeNULL | ||||
| 				paramTypes[i+i] = byte(fieldTypeNULL) | ||||
| 				paramTypes[i+i+1] = 0x00 | ||||
| 				continue | ||||
| 			} | ||||
|  | @ -956,7 +984,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error { | |||
| 			// cache types and values
 | ||||
| 			switch v := arg.(type) { | ||||
| 			case int64: | ||||
| 				paramTypes[i+i] = fieldTypeLongLong | ||||
| 				paramTypes[i+i] = byte(fieldTypeLongLong) | ||||
| 				paramTypes[i+i+1] = 0x00 | ||||
| 
 | ||||
| 				if cap(paramValues)-len(paramValues)-8 >= 0 { | ||||
|  | @ -972,7 +1000,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error { | |||
| 				} | ||||
| 
 | ||||
| 			case float64: | ||||
| 				paramTypes[i+i] = fieldTypeDouble | ||||
| 				paramTypes[i+i] = byte(fieldTypeDouble) | ||||
| 				paramTypes[i+i+1] = 0x00 | ||||
| 
 | ||||
| 				if cap(paramValues)-len(paramValues)-8 >= 0 { | ||||
|  | @ -988,7 +1016,7 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error { | |||
| 				} | ||||
| 
 | ||||
| 			case bool: | ||||
| 				paramTypes[i+i] = fieldTypeTiny | ||||
| 				paramTypes[i+i] = byte(fieldTypeTiny) | ||||
| 				paramTypes[i+i+1] = 0x00 | ||||
| 
 | ||||
| 				if v { | ||||
|  | @ -1000,10 +1028,10 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error { | |||
| 			case []byte: | ||||
| 				// Common case (non-nil value) first
 | ||||
| 				if v != nil { | ||||
| 					paramTypes[i+i] = fieldTypeString | ||||
| 					paramTypes[i+i] = byte(fieldTypeString) | ||||
| 					paramTypes[i+i+1] = 0x00 | ||||
| 
 | ||||
| 					if len(v) < mc.maxAllowedPacket-pos-len(paramValues)-(len(args)-(i+1))*64 { | ||||
| 					if len(v) < longDataSize { | ||||
| 						paramValues = appendLengthEncodedInteger(paramValues, | ||||
| 							uint64(len(v)), | ||||
| 						) | ||||
|  | @ -1018,14 +1046,14 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error { | |||
| 
 | ||||
| 				// Handle []byte(nil) as a NULL value
 | ||||
| 				nullMask[i/8] |= 1 << (uint(i) & 7) | ||||
| 				paramTypes[i+i] = fieldTypeNULL | ||||
| 				paramTypes[i+i] = byte(fieldTypeNULL) | ||||
| 				paramTypes[i+i+1] = 0x00 | ||||
| 
 | ||||
| 			case string: | ||||
| 				paramTypes[i+i] = fieldTypeString | ||||
| 				paramTypes[i+i] = byte(fieldTypeString) | ||||
| 				paramTypes[i+i+1] = 0x00 | ||||
| 
 | ||||
| 				if len(v) < mc.maxAllowedPacket-pos-len(paramValues)-(len(args)-(i+1))*64 { | ||||
| 				if len(v) < longDataSize { | ||||
| 					paramValues = appendLengthEncodedInteger(paramValues, | ||||
| 						uint64(len(v)), | ||||
| 					) | ||||
|  | @ -1037,23 +1065,25 @@ func (stmt *mysqlStmt) writeExecutePacket(args []driver.Value) error { | |||
| 				} | ||||
| 
 | ||||
| 			case time.Time: | ||||
| 				paramTypes[i+i] = fieldTypeString | ||||
| 				paramTypes[i+i] = byte(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) | ||||
| 				return fmt.Errorf("cannot convert type: %T", arg) | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
|  | @ -1086,8 +1116,6 @@ func (mc *mysqlConn) discardResults() error { | |||
| 			if err := mc.readUntilEOF(); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		} else { | ||||
| 			mc.status &^= statusMoreResultsExists | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
|  | @ -1105,16 +1133,17 @@ func (rows *binaryRows) readRow(dest []driver.Value) error { | |||
| 		// EOF Packet
 | ||||
| 		if data[0] == iEOF && len(data) == 5 { | ||||
| 			rows.mc.status = readStatus(data[3:]) | ||||
| 			if err := rows.mc.discardResults(); err != nil { | ||||
| 				return err | ||||
| 			rows.rs.done = true | ||||
| 			if !rows.HasNextResultSet() { | ||||
| 				rows.mc = nil | ||||
| 			} | ||||
| 			rows.mc = nil | ||||
| 			return io.EOF | ||||
| 		} | ||||
| 		mc := rows.mc | ||||
| 		rows.mc = nil | ||||
| 
 | ||||
| 		// Error otherwise
 | ||||
| 		return rows.mc.handleErrorPacket(data) | ||||
| 		return mc.handleErrorPacket(data) | ||||
| 	} | ||||
| 
 | ||||
| 	// NULL-bitmap,  [(column-count + 7 + 2) / 8 bytes]
 | ||||
|  | @ -1130,14 +1159,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])) | ||||
|  | @ -1146,7 +1175,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]))) | ||||
|  | @ -1155,7 +1184,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]))) | ||||
|  | @ -1164,7 +1193,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) | ||||
|  | @ -1178,7 +1207,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 | ||||
| 
 | ||||
|  | @ -1218,10 +1247,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: | ||||
|  | @ -1229,7 +1258,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) | ||||
|  | @ -1237,10 +1266,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: | ||||
|  | @ -1248,7 +1277,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, | ||||
| 						) | ||||
| 					} | ||||
| 				} | ||||
|  | @ -1264,7 +1293,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) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										174
									
								
								vendor/github.com/go-sql-driver/mysql/rows.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										174
									
								
								vendor/github.com/go-sql-driver/mysql/rows.go
									
									
									
										generated
									
									
										vendored
									
									
								
							|  | @ -11,19 +11,20 @@ package mysql | |||
| import ( | ||||
| 	"database/sql/driver" | ||||
| 	"io" | ||||
| 	"math" | ||||
| 	"reflect" | ||||
| ) | ||||
| 
 | ||||
| type mysqlField struct { | ||||
| 	tableName string | ||||
| 	name      string | ||||
| 	flags     fieldFlag | ||||
| 	fieldType byte | ||||
| 	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 +35,86 @@ 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) ColumnTypeDatabaseTypeName(i int) string { | ||||
| 	return rows.rs.columns[i].typeDatabaseName() | ||||
| } | ||||
| 
 | ||||
| // func (rows *mysqlRows) ColumnTypeLength(i int) (length int64, ok bool) {
 | ||||
| // 	return int64(rows.rs.columns[i].length), true
 | ||||
| // }
 | ||||
| 
 | ||||
| func (rows *mysqlRows) ColumnTypeNullable(i int) (nullable, ok bool) { | ||||
| 	return rows.rs.columns[i].flags&flagNotNULL == 0, true | ||||
| } | ||||
| 
 | ||||
| func (rows *mysqlRows) ColumnTypePrecisionScale(i int) (int64, int64, bool) { | ||||
| 	column := rows.rs.columns[i] | ||||
| 	decimals := int64(column.decimals) | ||||
| 
 | ||||
| 	switch column.fieldType { | ||||
| 	case fieldTypeDecimal, fieldTypeNewDecimal: | ||||
| 		if decimals > 0 { | ||||
| 			return int64(column.length) - 2, decimals, true | ||||
| 		} | ||||
| 		return int64(column.length) - 1, decimals, true | ||||
| 	case fieldTypeTimestamp, fieldTypeDateTime, fieldTypeTime: | ||||
| 		return decimals, decimals, true | ||||
| 	case fieldTypeFloat, fieldTypeDouble: | ||||
| 		if decimals == 0x1f { | ||||
| 			return math.MaxInt64, math.MaxInt64, true | ||||
| 		} | ||||
| 		return math.MaxInt64, decimals, true | ||||
| 	} | ||||
| 
 | ||||
| 	return 0, 0, false | ||||
| } | ||||
| 
 | ||||
| func (rows *mysqlRows) ColumnTypeScanType(i int) reflect.Type { | ||||
| 	return rows.rs.columns[i].scanType() | ||||
| } | ||||
| 
 | ||||
| func (rows *mysqlRows) Close() (err error) { | ||||
| 	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 +125,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 +193,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 +214,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 | ||||
| } | ||||
|  |  | |||
							
								
								
									
										123
									
								
								vendor/github.com/go-sql-driver/mysql/statement.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										123
									
								
								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,12 +20,14 @@ 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 { | ||||
| 		errLog.Print(ErrInvalidConn) | ||||
| 	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.
 | ||||
| 		//errLog.Print(ErrInvalidConn)
 | ||||
| 		return driver.ErrBadConn | ||||
| 	} | ||||
| 
 | ||||
|  | @ -42,14 +45,14 @@ 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 | ||||
| 	} | ||||
| 	// Send command
 | ||||
| 	err := stmt.writeExecutePacket(args) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 		return nil, stmt.mc.markBadConn(err) | ||||
| 	} | ||||
| 
 | ||||
| 	mc := stmt.mc | ||||
|  | @ -59,37 +62,45 @@ 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 | ||||
| 	} | ||||
| 	// Send command
 | ||||
| 	err := stmt.writeExecutePacket(args) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 		return nil, stmt.mc.markBadConn(err) | ||||
| 	} | ||||
| 
 | ||||
| 	mc := stmt.mc | ||||
|  | @ -104,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 | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  | @ -120,19 +132,36 @@ func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) { | |||
| 
 | ||||
| type converter struct{} | ||||
| 
 | ||||
| // ConvertValue mirrors the reference/default converter in database/sql/driver
 | ||||
| // with _one_ exception.  We support uint64 with their high bit and the default
 | ||||
| // implementation does not.  This function should be kept in sync with
 | ||||
| // database/sql/driver defaultConverter.ConvertValue() except for that
 | ||||
| // deliberate difference.
 | ||||
| func (c converter) ConvertValue(v interface{}) (driver.Value, error) { | ||||
| 	if driver.IsValue(v) { | ||||
| 		return v, nil | ||||
| 	} | ||||
| 
 | ||||
| 	if vr, ok := v.(driver.Valuer); ok { | ||||
| 		sv, err := callValuerValue(vr) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		if !driver.IsValue(sv) { | ||||
| 			return nil, fmt.Errorf("non-Value type %T returned from Value", sv) | ||||
| 		} | ||||
| 		return sv, nil | ||||
| 	} | ||||
| 
 | ||||
| 	rv := reflect.ValueOf(v) | ||||
| 	switch rv.Kind() { | ||||
| 	case reflect.Ptr: | ||||
| 		// indirect pointers
 | ||||
| 		if rv.IsNil() { | ||||
| 			return nil, nil | ||||
| 		} else { | ||||
| 			return c.ConvertValue(rv.Elem().Interface()) | ||||
| 		} | ||||
| 		return c.ConvertValue(rv.Elem().Interface()) | ||||
| 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||||
| 		return rv.Int(), nil | ||||
| 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32: | ||||
|  | @ -145,6 +174,38 @@ func (c converter) ConvertValue(v interface{}) (driver.Value, error) { | |||
| 		return int64(u64), nil | ||||
| 	case reflect.Float32, reflect.Float64: | ||||
| 		return rv.Float(), nil | ||||
| 	case reflect.Bool: | ||||
| 		return rv.Bool(), nil | ||||
| 	case reflect.Slice: | ||||
| 		ek := rv.Type().Elem().Kind() | ||||
| 		if ek == reflect.Uint8 { | ||||
| 			return rv.Bytes(), nil | ||||
| 		} | ||||
| 		return nil, fmt.Errorf("unsupported type %T, a slice of %s", v, ek) | ||||
| 	case reflect.String: | ||||
| 		return rv.String(), nil | ||||
| 	} | ||||
| 	return nil, fmt.Errorf("unsupported type %T, a %s", v, rv.Kind()) | ||||
| } | ||||
| 
 | ||||
| var valuerReflectType = reflect.TypeOf((*driver.Valuer)(nil)).Elem() | ||||
| 
 | ||||
| // callValuerValue returns vr.Value(), with one exception:
 | ||||
| // If vr.Value is an auto-generated method on a pointer type and the
 | ||||
| // pointer is nil, it would panic at runtime in the panicwrap
 | ||||
| // method. Treat it like nil instead.
 | ||||
| //
 | ||||
| // This is so people can implement driver.Value on value types and
 | ||||
| // still use nil pointers to those types to mean nil/NULL, just like
 | ||||
| // string/*string.
 | ||||
| //
 | ||||
| // This is an exact copy of the same-named unexported function from the
 | ||||
| // database/sql package.
 | ||||
| func callValuerValue(vr driver.Valuer) (v driver.Value, err error) { | ||||
| 	if rv := reflect.ValueOf(vr); rv.Kind() == reflect.Ptr && | ||||
| 		rv.IsNil() && | ||||
| 		rv.Type().Elem().Implements(valuerReflectType) { | ||||
| 		return nil, nil | ||||
| 	} | ||||
| 	return vr.Value() | ||||
| } | ||||
|  |  | |||
							
								
								
									
										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") | ||||
|  |  | |||
							
								
								
									
										214
									
								
								vendor/github.com/go-sql-driver/mysql/utils.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										214
									
								
								vendor/github.com/go-sql-driver/mysql/utils.go
									
									
									
										generated
									
									
										vendored
									
									
								
							|  | @ -9,23 +9,29 @@ | |||
| package mysql | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto/sha1" | ||||
| 	"crypto/tls" | ||||
| 	"database/sql/driver" | ||||
| 	"encoding/binary" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"sync/atomic" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // Registry for custom tls.Configs
 | ||||
| var ( | ||||
| 	tlsConfigRegister map[string]*tls.Config // Register for custom tls.Configs
 | ||||
| 	tlsConfigLock     sync.RWMutex | ||||
| 	tlsConfigRegistry map[string]*tls.Config | ||||
| ) | ||||
| 
 | ||||
| // 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 provided tls.Config is exclusively owned by the driver after
 | ||||
| // registering it.
 | ||||
| //
 | ||||
| //  rootCertPool := x509.NewCertPool()
 | ||||
| //  pem, err := ioutil.ReadFile("/path/ca-cert.pem")
 | ||||
| //  if err != nil {
 | ||||
|  | @ -51,19 +57,32 @@ func RegisterTLSConfig(key string, config *tls.Config) error { | |||
| 		return fmt.Errorf("key '%s' is reserved", key) | ||||
| 	} | ||||
| 
 | ||||
| 	if tlsConfigRegister == nil { | ||||
| 		tlsConfigRegister = make(map[string]*tls.Config) | ||||
| 	tlsConfigLock.Lock() | ||||
| 	if tlsConfigRegistry == nil { | ||||
| 		tlsConfigRegistry = make(map[string]*tls.Config) | ||||
| 	} | ||||
| 
 | ||||
| 	tlsConfigRegister[key] = config | ||||
| 	tlsConfigRegistry[key] = config | ||||
| 	tlsConfigLock.Unlock() | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // DeregisterTLSConfig removes the tls.Config associated with key.
 | ||||
| func DeregisterTLSConfig(key string) { | ||||
| 	if tlsConfigRegister != nil { | ||||
| 		delete(tlsConfigRegister, key) | ||||
| 	tlsConfigLock.Lock() | ||||
| 	if tlsConfigRegistry != nil { | ||||
| 		delete(tlsConfigRegistry, key) | ||||
| 	} | ||||
| 	tlsConfigLock.Unlock() | ||||
| } | ||||
| 
 | ||||
| func getTLSConfigClone(key string) (config *tls.Config) { | ||||
| 	tlsConfigLock.RLock() | ||||
| 	if v, ok := tlsConfigRegistry[key]; ok { | ||||
| 		config = cloneTLSConfig(v) | ||||
| 	} | ||||
| 	tlsConfigLock.RUnlock() | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // Returns the bool value of the input.
 | ||||
|  | @ -80,119 +99,6 @@ func readBool(input string) (value bool, valid bool) { | |||
| 	return | ||||
| } | ||||
| 
 | ||||
| /****************************************************************************** | ||||
| *                             Authentication                                  * | ||||
| ******************************************************************************/ | ||||
| 
 | ||||
| // Encrypt password using 4.1+ method
 | ||||
| func scramblePassword(scramble, password []byte) []byte { | ||||
| 	if len(password) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	// stage1Hash = SHA1(password)
 | ||||
| 	crypt := sha1.New() | ||||
| 	crypt.Write(password) | ||||
| 	stage1 := crypt.Sum(nil) | ||||
| 
 | ||||
| 	// scrambleHash = SHA1(scramble + SHA1(stage1Hash))
 | ||||
| 	// inner Hash
 | ||||
| 	crypt.Reset() | ||||
| 	crypt.Write(stage1) | ||||
| 	hash := crypt.Sum(nil) | ||||
| 
 | ||||
| 	// outer Hash
 | ||||
| 	crypt.Reset() | ||||
| 	crypt.Write(scramble) | ||||
| 	crypt.Write(hash) | ||||
| 	scramble = crypt.Sum(nil) | ||||
| 
 | ||||
| 	// token = scrambleHash XOR stage1Hash
 | ||||
| 	for i := range scramble { | ||||
| 		scramble[i] ^= stage1[i] | ||||
| 	} | ||||
| 	return scramble | ||||
| } | ||||
| 
 | ||||
| // Encrypt password using pre 4.1 (old password) method
 | ||||
| // https://github.com/atcurtis/mariadb/blob/master/mysys/my_rnd.c
 | ||||
| type myRnd struct { | ||||
| 	seed1, seed2 uint32 | ||||
| } | ||||
| 
 | ||||
| const myRndMaxVal = 0x3FFFFFFF | ||||
| 
 | ||||
| // Pseudo random number generator
 | ||||
| func newMyRnd(seed1, seed2 uint32) *myRnd { | ||||
| 	return &myRnd{ | ||||
| 		seed1: seed1 % myRndMaxVal, | ||||
| 		seed2: seed2 % myRndMaxVal, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Tested to be equivalent to MariaDB's floating point variant
 | ||||
| // http://play.golang.org/p/QHvhd4qved
 | ||||
| // http://play.golang.org/p/RG0q4ElWDx
 | ||||
| func (r *myRnd) NextByte() byte { | ||||
| 	r.seed1 = (r.seed1*3 + r.seed2) % myRndMaxVal | ||||
| 	r.seed2 = (r.seed1 + r.seed2 + 33) % myRndMaxVal | ||||
| 
 | ||||
| 	return byte(uint64(r.seed1) * 31 / myRndMaxVal) | ||||
| } | ||||
| 
 | ||||
| // Generate binary hash from byte string using insecure pre 4.1 method
 | ||||
| func pwHash(password []byte) (result [2]uint32) { | ||||
| 	var add uint32 = 7 | ||||
| 	var tmp uint32 | ||||
| 
 | ||||
| 	result[0] = 1345345333 | ||||
| 	result[1] = 0x12345671 | ||||
| 
 | ||||
| 	for _, c := range password { | ||||
| 		// skip spaces and tabs in password
 | ||||
| 		if c == ' ' || c == '\t' { | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		tmp = uint32(c) | ||||
| 		result[0] ^= (((result[0] & 63) + add) * tmp) + (result[0] << 8) | ||||
| 		result[1] += (result[1] << 8) ^ result[0] | ||||
| 		add += tmp | ||||
| 	} | ||||
| 
 | ||||
| 	// Remove sign bit (1<<31)-1)
 | ||||
| 	result[0] &= 0x7FFFFFFF | ||||
| 	result[1] &= 0x7FFFFFFF | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| // Encrypt password using insecure pre 4.1 method
 | ||||
| func scrambleOldPassword(scramble, password []byte) []byte { | ||||
| 	if len(password) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	scramble = scramble[:8] | ||||
| 
 | ||||
| 	hashPw := pwHash(password) | ||||
| 	hashSc := pwHash(scramble) | ||||
| 
 | ||||
| 	r := newMyRnd(hashPw[0]^hashSc[0], hashPw[1]^hashSc[1]) | ||||
| 
 | ||||
| 	var out [8]byte | ||||
| 	for i := range out { | ||||
| 		out[i] = r.NextByte() + 64 | ||||
| 	} | ||||
| 
 | ||||
| 	mask := r.NextByte() | ||||
| 	for i := range out { | ||||
| 		out[i] ^= mask | ||||
| 	} | ||||
| 
 | ||||
| 	return out[:] | ||||
| } | ||||
| 
 | ||||
| /****************************************************************************** | ||||
| *                           Time related utils                                * | ||||
| ******************************************************************************/ | ||||
|  | @ -519,7 +425,7 @@ func readLengthEncodedString(b []byte) ([]byte, bool, int, error) { | |||
| 
 | ||||
| 	// Check data length
 | ||||
| 	if len(b) >= n { | ||||
| 		return b[n-int(num) : n], false, n, nil | ||||
| 		return b[n-int(num) : n : n], false, n, nil | ||||
| 	} | ||||
| 	return nil, false, n, io.EOF | ||||
| } | ||||
|  | @ -548,8 +454,8 @@ func readLengthEncodedInteger(b []byte) (uint64, bool, int) { | |||
| 	if len(b) == 0 { | ||||
| 		return 0, true, 1 | ||||
| 	} | ||||
| 	switch b[0] { | ||||
| 
 | ||||
| 	switch b[0] { | ||||
| 	// 251: NULL
 | ||||
| 	case 0xfb: | ||||
| 		return 0, true, 1 | ||||
|  | @ -738,3 +644,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 | ||||
| } | ||||
| 
 | ||||
| // atomicError 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, | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										50
									
								
								vendor/github.com/go-sql-driver/mysql/utils_go18.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								vendor/github.com/go-sql-driver/mysql/utils_go18.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,50 @@ | |||
| // Go MySQL Driver - A MySQL-Driver for Go's database/sql package
 | ||||
| //
 | ||||
| // Copyright 2017 The Go-MySQL-Driver Authors. All rights reserved.
 | ||||
| //
 | ||||
| // This Source Code Form is subject to the terms of the Mozilla Public
 | ||||
| // License, v. 2.0. If a copy of the MPL was not distributed with this file,
 | ||||
| // You can obtain one at http://mozilla.org/MPL/2.0/.
 | ||||
| 
 | ||||
| // +build go1.8
 | ||||
| 
 | ||||
| package mysql | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto/tls" | ||||
| 	"database/sql" | ||||
| 	"database/sql/driver" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| ) | ||||
| 
 | ||||
| func cloneTLSConfig(c *tls.Config) *tls.Config { | ||||
| 	return c.Clone() | ||||
| } | ||||
| 
 | ||||
| func namedValueToValue(named []driver.NamedValue) ([]driver.Value, error) { | ||||
| 	dargs := make([]driver.Value, len(named)) | ||||
| 	for n, param := range named { | ||||
| 		if len(param.Name) > 0 { | ||||
| 			// TODO: support the use of Named Parameters #561
 | ||||
| 			return nil, errors.New("mysql: driver does not support the use of Named Parameters") | ||||
| 		} | ||||
| 		dargs[n] = param.Value | ||||
| 	} | ||||
| 	return dargs, nil | ||||
| } | ||||
| 
 | ||||
| func mapIsolationLevel(level driver.IsolationLevel) (string, error) { | ||||
| 	switch sql.IsolationLevel(level) { | ||||
| 	case sql.LevelRepeatableRead: | ||||
| 		return "REPEATABLE READ", nil | ||||
| 	case sql.LevelReadCommitted: | ||||
| 		return "READ COMMITTED", nil | ||||
| 	case sql.LevelReadUncommitted: | ||||
| 		return "READ UNCOMMITTED", nil | ||||
| 	case sql.LevelSerializable: | ||||
| 		return "SERIALIZABLE", nil | ||||
| 	default: | ||||
| 		return "", fmt.Errorf("mysql: unsupported isolation level: %v", level) | ||||
| 	} | ||||
| } | ||||
		Loading…
	
		Reference in a new issue