summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/go-sql-driver/mysql/utils.go
diff options
context:
space:
mode:
authorChristopher Speller <crspeller@gmail.com>2016-09-23 10:17:51 -0400
committerGitHub <noreply@github.com>2016-09-23 10:17:51 -0400
commit2ca0e8f9a0f9863555a26e984cde15efff9ef8f8 (patch)
treedaae1ee67b14a3d0a84424f2a304885d9e75ce2b /vendor/github.com/go-sql-driver/mysql/utils.go
parent6d62d65b2dc85855aabea036cbd44f6059e19d13 (diff)
downloadchat-2ca0e8f9a0f9863555a26e984cde15efff9ef8f8.tar.gz
chat-2ca0e8f9a0f9863555a26e984cde15efff9ef8f8.tar.bz2
chat-2ca0e8f9a0f9863555a26e984cde15efff9ef8f8.zip
Updating golang dependancies (#4075)
Diffstat (limited to 'vendor/github.com/go-sql-driver/mysql/utils.go')
-rw-r--r--vendor/github.com/go-sql-driver/mysql/utils.go574
1 files changed, 280 insertions, 294 deletions
diff --git a/vendor/github.com/go-sql-driver/mysql/utils.go b/vendor/github.com/go-sql-driver/mysql/utils.go
index d523b7ffd..b6f200389 100644
--- a/vendor/github.com/go-sql-driver/mysql/utils.go
+++ b/vendor/github.com/go-sql-driver/mysql/utils.go
@@ -13,16 +13,26 @@ import (
"crypto/tls"
"database/sql/driver"
"encoding/binary"
+ "errors"
"fmt"
"io"
+ "net/url"
"strings"
"time"
)
var (
tlsConfigRegister map[string]*tls.Config // Register for custom tls.Configs
+
+ errInvalidDSNUnescaped = errors.New("Invalid DSN: Did you forget to escape a param value?")
+ errInvalidDSNAddr = errors.New("Invalid DSN: Network Address not terminated (missing closing brace)")
+ errInvalidDSNNoSlash = errors.New("Invalid DSN: Missing the slash separating the database name")
)
+func init() {
+ tlsConfigRegister = make(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.
//
@@ -48,11 +58,7 @@ var (
//
func RegisterTLSConfig(key string, config *tls.Config) error {
if _, isBool := readBool(key); isBool || strings.ToLower(key) == "skip-verify" {
- return fmt.Errorf("key '%s' is reserved", key)
- }
-
- if tlsConfigRegister == nil {
- tlsConfigRegister = make(map[string]*tls.Config)
+ return fmt.Errorf("Key '%s' is reserved", key)
}
tlsConfigRegister[key] = config
@@ -61,9 +67,202 @@ func RegisterTLSConfig(key string, config *tls.Config) error {
// DeregisterTLSConfig removes the tls.Config associated with key.
func DeregisterTLSConfig(key string) {
- if tlsConfigRegister != nil {
- delete(tlsConfigRegister, key)
+ delete(tlsConfigRegister, key)
+}
+
+// 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,
+ }
+
+ // TODO: use strings.IndexByte when we can depend on Go 1.2
+
+ // [user[:password]@][net[(addr)]]/dbname[?param1=value1&paramN=valueN]
+ // Find the last '/' (since the password or the net addr might contain a '/')
+ foundSlash := false
+ for i := len(dsn) - 1; i >= 0; i-- {
+ if dsn[i] == '/' {
+ foundSlash = true
+ var j, k int
+
+ // left part is empty if i <= 0
+ if i > 0 {
+ // [username[:password]@][protocol[(address)]]
+ // Find the last '@' in dsn[:i]
+ for j = i; j >= 0; j-- {
+ if dsn[j] == '@' {
+ // username[:password]
+ // Find the first ':' in dsn[:j]
+ for k = 0; k < j; k++ {
+ if dsn[k] == ':' {
+ cfg.passwd = dsn[k+1 : j]
+ break
+ }
+ }
+ cfg.user = dsn[:k]
+
+ break
+ }
+ }
+
+ // [protocol[(address)]]
+ // Find the first '(' in dsn[j+1:i]
+ for k = j + 1; k < i; k++ {
+ if dsn[k] == '(' {
+ // dsn[i-1] must be == ')' if an address is specified
+ if dsn[i-1] != ')' {
+ if strings.ContainsRune(dsn[k+1:i], ')') {
+ return nil, errInvalidDSNUnescaped
+ }
+ return nil, errInvalidDSNAddr
+ }
+ cfg.addr = dsn[k+1 : i-1]
+ break
+ }
+ }
+ cfg.net = dsn[j+1 : k]
+ }
+
+ // dbname[?param1=value1&...&paramN=valueN]
+ // Find the first '?' in dsn[i+1:]
+ for j = i + 1; j < len(dsn); j++ {
+ if dsn[j] == '?' {
+ if err = parseDSNParams(cfg, dsn[j+1:]); err != nil {
+ return
+ }
+ break
+ }
+ }
+ cfg.dbname = dsn[i+1 : j]
+
+ break
+ }
+ }
+
+ if !foundSlash && len(dsn) > 0 {
+ return nil, errInvalidDSNNoSlash
+ }
+
+ // 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
+}
+
+// parseDSNParams parses the DSN "query string"
+// Values must be url.QueryEscape'ed
+func parseDSNParams(cfg *config, params string) (err error) {
+ for _, v := range strings.Split(params, "&") {
+ param := strings.SplitN(v, "=", 2)
+ if len(param) != 2 {
+ continue
+ }
+
+ // cfg params
+ switch value := param[1]; param[0] {
+
+ // Disable INFILE whitelist / enable all files
+ case "allowAllFiles":
+ var isBool bool
+ cfg.allowAllFiles, isBool = readBool(value)
+ if !isBool {
+ return fmt.Errorf("Invalid Bool value: %s", value)
+ }
+
+ // Use old authentication mode (pre MySQL 4.1)
+ case "allowOldPasswords":
+ var isBool bool
+ cfg.allowOldPasswords, isBool = readBool(value)
+ if !isBool {
+ return fmt.Errorf("Invalid Bool value: %s", value)
+ }
+
+ // Switch "rowsAffected" mode
+ case "clientFoundRows":
+ var isBool bool
+ cfg.clientFoundRows, isBool = readBool(value)
+ if !isBool {
+ return fmt.Errorf("Invalid Bool value: %s", value)
+ }
+
+ // Collation
+ case "collation":
+ collation, ok := collations[value]
+ if !ok {
+ // Note possibility for false negatives:
+ // could be triggered although the collation is valid if the
+ // collations map does not contain entries the server supports.
+ err = errors.New("unknown collation")
+ return
+ }
+ cfg.collation = collation
+ break
+
+ // Time Location
+ case "loc":
+ if value, err = url.QueryUnescape(value); err != nil {
+ return
+ }
+ cfg.loc, err = time.LoadLocation(value)
+ if err != nil {
+ return
+ }
+
+ // Dial Timeout
+ case "timeout":
+ cfg.timeout, err = time.ParseDuration(value)
+ if err != nil {
+ return
+ }
+
+ // TLS-Encryption
+ case "tls":
+ boolValue, isBool := readBool(value)
+ if isBool {
+ if boolValue {
+ cfg.tls = &tls.Config{}
+ }
+ } else {
+ if strings.ToLower(value) == "skip-verify" {
+ cfg.tls = &tls.Config{InsecureSkipVerify: true}
+ } else if tlsConfig, ok := tlsConfigRegister[value]; ok {
+ cfg.tls = tlsConfig
+ } else {
+ return fmt.Errorf("Invalid value / unknown config name: %s", value)
+ }
+ }
+
+ default:
+ // lazy init
+ if cfg.params == nil {
+ cfg.params = make(map[string]string)
+ }
+
+ if cfg.params[param[0]], err = url.QueryUnescape(value); err != nil {
+ return
+ }
+ }
}
+
+ return
}
// Returns the bool value of the input.
@@ -252,15 +451,19 @@ func (nt NullTime) Value() (driver.Value, error) {
}
func parseDateTime(str string, loc *time.Location) (t time.Time, err error) {
- base := "0000-00-00 00:00:00.0000000"
switch len(str) {
- case 10, 19, 21, 22, 23, 24, 25, 26: // up to "YYYY-MM-DD HH:MM:SS.MMMMMM"
- if str == base[:len(str)] {
+ case 10: // YYYY-MM-DD
+ if str == "0000-00-00" {
return
}
- t, err = time.Parse(timeFormat[:len(str)], str)
+ t, err = time.Parse(timeFormat[:10], str)
+ case 19: // YYYY-MM-DD HH:MM:SS
+ if str == "0000-00-00 00:00:00" {
+ return
+ }
+ t, err = time.Parse(timeFormat, str)
default:
- err = fmt.Errorf("invalid time string: %s", str)
+ err = fmt.Errorf("Invalid Time-String: %s", str)
return
}
@@ -309,151 +512,87 @@ func parseBinaryDateTime(num uint64, data []byte, loc *time.Location) (driver.Va
loc,
), nil
}
- return nil, fmt.Errorf("invalid DATETIME packet length %d", num)
+ return nil, fmt.Errorf("Invalid DATETIME-packet length %d", num)
}
// zeroDateTime is used in formatBinaryDateTime to avoid an allocation
// if the DATE or DATETIME has the zero value.
// It must never be changed.
// The current behavior depends on database/sql copying the result.
-var zeroDateTime = []byte("0000-00-00 00:00:00.000000")
-
-const digits01 = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
-const digits10 = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
+var zeroDateTime = []byte("0000-00-00 00:00:00")
-func formatBinaryDateTime(src []byte, length uint8, justTime bool) (driver.Value, error) {
- // length expects the deterministic length of the zero value,
- // negative time and 100+ hours are automatically added if needed
+func formatBinaryDateTime(src []byte, withTime bool) (driver.Value, error) {
if len(src) == 0 {
- if justTime {
- return zeroDateTime[11 : 11+length], nil
+ if withTime {
+ return zeroDateTime, nil
}
- return zeroDateTime[:length], nil
- }
- var dst []byte // return value
- var pt, p1, p2, p3 byte // current digit pair
- var zOffs byte // offset of value in zeroDateTime
- if justTime {
- switch length {
- case
- 8, // time (can be up to 10 when negative and 100+ hours)
- 10, 11, 12, 13, 14, 15: // time with fractional seconds
- default:
- return nil, fmt.Errorf("illegal TIME length %d", length)
- }
- switch len(src) {
- case 8, 12:
- default:
- return nil, fmt.Errorf("invalid TIME packet length %d", len(src))
- }
- // +2 to enable negative time and 100+ hours
- dst = make([]byte, 0, length+2)
- if src[0] == 1 {
- dst = append(dst, '-')
- }
- if src[1] != 0 {
- hour := uint16(src[1])*24 + uint16(src[5])
- pt = byte(hour / 100)
- p1 = byte(hour - 100*uint16(pt))
- dst = append(dst, digits01[pt])
+ return zeroDateTime[:10], nil
+ }
+ var dst []byte
+ if withTime {
+ if len(src) == 11 {
+ dst = []byte("0000-00-00 00:00:00.000000")
} else {
- p1 = src[5]
+ dst = []byte("0000-00-00 00:00:00")
}
- zOffs = 11
- src = src[6:]
} else {
- switch length {
- case 10, 19, 21, 22, 23, 24, 25, 26:
- default:
- t := "DATE"
- if length > 10 {
- t += "TIME"
- }
- return nil, fmt.Errorf("illegal %s length %d", t, length)
- }
- switch len(src) {
- case 4, 7, 11:
- default:
- t := "DATE"
- if length > 10 {
- t += "TIME"
- }
- return nil, fmt.Errorf("illegal %s packet length %d", t, len(src))
- }
- dst = make([]byte, 0, length)
- // start with the date
+ dst = []byte("0000-00-00")
+ }
+ switch len(src) {
+ case 11:
+ microsecs := binary.LittleEndian.Uint32(src[7:11])
+ tmp32 := microsecs / 10
+ dst[25] += byte(microsecs - 10*tmp32)
+ tmp32, microsecs = tmp32/10, tmp32
+ dst[24] += byte(microsecs - 10*tmp32)
+ tmp32, microsecs = tmp32/10, tmp32
+ dst[23] += byte(microsecs - 10*tmp32)
+ tmp32, microsecs = tmp32/10, tmp32
+ dst[22] += byte(microsecs - 10*tmp32)
+ tmp32, microsecs = tmp32/10, tmp32
+ dst[21] += byte(microsecs - 10*tmp32)
+ dst[20] += byte(microsecs / 10)
+ fallthrough
+ case 7:
+ second := src[6]
+ tmp := second / 10
+ dst[18] += second - 10*tmp
+ dst[17] += tmp
+ minute := src[5]
+ tmp = minute / 10
+ dst[15] += minute - 10*tmp
+ dst[14] += tmp
+ hour := src[4]
+ tmp = hour / 10
+ dst[12] += hour - 10*tmp
+ dst[11] += tmp
+ fallthrough
+ case 4:
+ day := src[3]
+ tmp := day / 10
+ dst[9] += day - 10*tmp
+ dst[8] += tmp
+ month := src[2]
+ tmp = month / 10
+ dst[6] += month - 10*tmp
+ dst[5] += tmp
year := binary.LittleEndian.Uint16(src[:2])
- pt = byte(year / 100)
- p1 = byte(year - 100*uint16(pt))
- p2, p3 = src[2], src[3]
- dst = append(dst,
- digits10[pt], digits01[pt],
- digits10[p1], digits01[p1], '-',
- digits10[p2], digits01[p2], '-',
- digits10[p3], digits01[p3],
- )
- if length == 10 {
- return dst, nil
- }
- if len(src) == 4 {
- return append(dst, zeroDateTime[10:length]...), nil
- }
- dst = append(dst, ' ')
- p1 = src[4] // hour
- src = src[5:]
- }
- // p1 is 2-digit hour, src is after hour
- p2, p3 = src[0], src[1]
- dst = append(dst,
- digits10[p1], digits01[p1], ':',
- digits10[p2], digits01[p2], ':',
- digits10[p3], digits01[p3],
- )
- if length <= byte(len(dst)) {
+ tmp16 := year / 10
+ dst[3] += byte(year - 10*tmp16)
+ tmp16, year = tmp16/10, tmp16
+ dst[2] += byte(year - 10*tmp16)
+ tmp16, year = tmp16/10, tmp16
+ dst[1] += byte(year - 10*tmp16)
+ dst[0] += byte(tmp16)
return dst, nil
}
- src = src[2:]
- if len(src) == 0 {
- return append(dst, zeroDateTime[19:zOffs+length]...), nil
- }
- microsecs := binary.LittleEndian.Uint32(src[:4])
- p1 = byte(microsecs / 10000)
- microsecs -= 10000 * uint32(p1)
- p2 = byte(microsecs / 100)
- microsecs -= 100 * uint32(p2)
- p3 = byte(microsecs)
- switch decimals := zOffs + length - 20; decimals {
- default:
- return append(dst, '.',
- digits10[p1], digits01[p1],
- digits10[p2], digits01[p2],
- digits10[p3], digits01[p3],
- ), nil
- case 1:
- return append(dst, '.',
- digits10[p1],
- ), nil
- case 2:
- return append(dst, '.',
- digits10[p1], digits01[p1],
- ), nil
- case 3:
- return append(dst, '.',
- digits10[p1], digits01[p1],
- digits10[p2],
- ), nil
- case 4:
- return append(dst, '.',
- digits10[p1], digits01[p1],
- digits10[p2], digits01[p2],
- ), nil
- case 5:
- return append(dst, '.',
- digits10[p1], digits01[p1],
- digits10[p2], digits01[p2],
- digits10[p3],
- ), nil
+ var t string
+ if withTime {
+ t = "DATETIME"
+ } else {
+ t = "DATE"
}
+ return nil, fmt.Errorf("invalid %s-packet length %d", t, len(src))
}
/******************************************************************************
@@ -544,10 +683,6 @@ func skipLengthEncodedString(b []byte) (int, error) {
// returns the number read, whether the value is NULL and the number of bytes read
func readLengthEncodedInteger(b []byte) (uint64, bool, int) {
- // See issue #349
- if len(b) == 0 {
- return 0, true, 1
- }
switch b[0] {
// 251: NULL
@@ -589,152 +724,3 @@ func appendLengthEncodedInteger(b []byte, n uint64) []byte {
return append(b, 0xfe, byte(n), byte(n>>8), byte(n>>16), byte(n>>24),
byte(n>>32), byte(n>>40), byte(n>>48), byte(n>>56))
}
-
-// reserveBuffer checks cap(buf) and expand buffer to len(buf) + appendSize.
-// If cap(buf) is not enough, reallocate new buffer.
-func reserveBuffer(buf []byte, appendSize int) []byte {
- newSize := len(buf) + appendSize
- if cap(buf) < newSize {
- // Grow buffer exponentially
- newBuf := make([]byte, len(buf)*2+appendSize)
- copy(newBuf, buf)
- buf = newBuf
- }
- return buf[:newSize]
-}
-
-// escapeBytesBackslash escapes []byte with backslashes (\)
-// This escapes the contents of a string (provided as []byte) by adding backslashes before special
-// characters, and turning others into specific escape sequences, such as
-// turning newlines into \n and null bytes into \0.
-// https://github.com/mysql/mysql-server/blob/mysql-5.7.5/mysys/charset.c#L823-L932
-func escapeBytesBackslash(buf, v []byte) []byte {
- pos := len(buf)
- buf = reserveBuffer(buf, len(v)*2)
-
- for _, c := range v {
- switch c {
- case '\x00':
- buf[pos] = '\\'
- buf[pos+1] = '0'
- pos += 2
- case '\n':
- buf[pos] = '\\'
- buf[pos+1] = 'n'
- pos += 2
- case '\r':
- buf[pos] = '\\'
- buf[pos+1] = 'r'
- pos += 2
- case '\x1a':
- buf[pos] = '\\'
- buf[pos+1] = 'Z'
- pos += 2
- case '\'':
- buf[pos] = '\\'
- buf[pos+1] = '\''
- pos += 2
- case '"':
- buf[pos] = '\\'
- buf[pos+1] = '"'
- pos += 2
- case '\\':
- buf[pos] = '\\'
- buf[pos+1] = '\\'
- pos += 2
- default:
- buf[pos] = c
- pos++
- }
- }
-
- return buf[:pos]
-}
-
-// escapeStringBackslash is similar to escapeBytesBackslash but for string.
-func escapeStringBackslash(buf []byte, v string) []byte {
- pos := len(buf)
- buf = reserveBuffer(buf, len(v)*2)
-
- for i := 0; i < len(v); i++ {
- c := v[i]
- switch c {
- case '\x00':
- buf[pos] = '\\'
- buf[pos+1] = '0'
- pos += 2
- case '\n':
- buf[pos] = '\\'
- buf[pos+1] = 'n'
- pos += 2
- case '\r':
- buf[pos] = '\\'
- buf[pos+1] = 'r'
- pos += 2
- case '\x1a':
- buf[pos] = '\\'
- buf[pos+1] = 'Z'
- pos += 2
- case '\'':
- buf[pos] = '\\'
- buf[pos+1] = '\''
- pos += 2
- case '"':
- buf[pos] = '\\'
- buf[pos+1] = '"'
- pos += 2
- case '\\':
- buf[pos] = '\\'
- buf[pos+1] = '\\'
- pos += 2
- default:
- buf[pos] = c
- pos++
- }
- }
-
- return buf[:pos]
-}
-
-// escapeBytesQuotes escapes apostrophes in []byte by doubling them up.
-// This escapes the contents of a string by doubling up any apostrophes that
-// it contains. This is used when the NO_BACKSLASH_ESCAPES SQL_MODE is in
-// effect on the server.
-// https://github.com/mysql/mysql-server/blob/mysql-5.7.5/mysys/charset.c#L963-L1038
-func escapeBytesQuotes(buf, v []byte) []byte {
- pos := len(buf)
- buf = reserveBuffer(buf, len(v)*2)
-
- for _, c := range v {
- if c == '\'' {
- buf[pos] = '\''
- buf[pos+1] = '\''
- pos += 2
- } else {
- buf[pos] = c
- pos++
- }
- }
-
- return buf[:pos]
-}
-
-// escapeStringQuotes is similar to escapeBytesQuotes but for string.
-func escapeStringQuotes(buf []byte, v string) []byte {
- pos := len(buf)
- buf = reserveBuffer(buf, len(v)*2)
-
- for i := 0; i < len(v); i++ {
- c := v[i]
- if c == '\'' {
- buf[pos] = '\''
- buf[pos+1] = '\''
- pos += 2
- } else {
- buf[pos] = c
- pos++
- }
- }
-
- return buf[:pos]
-}