github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/server/util.go (about) 1 // Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. 2 // 3 // This Source Code Form is subject to the terms of the Mozilla Public 4 // License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 // You can obtain one at http://mozilla.org/MPL/2.0/. 6 7 // The MIT License (MIT) 8 // 9 // Copyright (c) 2014 wandoulabs 10 // Copyright (c) 2014 siddontang 11 // 12 // Permission is hereby granted, free of charge, to any person obtaining a copy of 13 // this software and associated documentation files (the "Software"), to deal in 14 // the Software without restriction, including without limitation the rights to 15 // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 16 // the Software, and to permit persons to whom the Software is furnished to do so, 17 // subject to the following conditions: 18 // 19 // The above copyright notice and this permission notice shall be included in all 20 // copies or substantial portions of the Software. 21 22 // Copyright 2015 PingCAP, Inc. 23 // 24 // Licensed under the Apache License, Version 2.0 (the "License"); 25 // you may not use this file except in compliance with the License. 26 // You may obtain a copy of the License at 27 // 28 // http://www.apache.org/licenses/LICENSE-2.0 29 // 30 // Unless required by applicable law or agreed to in writing, software 31 // distributed under the License is distributed on an "AS IS" BASIS, 32 // See the License for the specific language governing permissions and 33 // limitations under the License. 34 35 package server 36 37 import ( 38 "encoding/binary" 39 "io" 40 "math" 41 "strconv" 42 "time" 43 44 "github.com/insionng/yougam/libraries/juju/errors" 45 "github.com/insionng/yougam/libraries/pingcap/tidb/mysql" 46 "github.com/insionng/yougam/libraries/pingcap/tidb/util/arena" 47 "github.com/insionng/yougam/libraries/pingcap/tidb/util/hack" 48 "github.com/insionng/yougam/libraries/pingcap/tidb/util/types" 49 ) 50 51 func parseLengthEncodedInt(b []byte) (num uint64, isNull bool, n int) { 52 switch b[0] { 53 // 251: NULL 54 case 0xfb: 55 n = 1 56 isNull = true 57 return 58 59 // 252: value of following 2 60 case 0xfc: 61 num = uint64(b[1]) | uint64(b[2])<<8 62 n = 3 63 return 64 65 // 253: value of following 3 66 case 0xfd: 67 num = uint64(b[1]) | uint64(b[2])<<8 | uint64(b[3])<<16 68 n = 4 69 return 70 71 // 254: value of following 8 72 case 0xfe: 73 num = uint64(b[1]) | uint64(b[2])<<8 | uint64(b[3])<<16 | 74 uint64(b[4])<<24 | uint64(b[5])<<32 | uint64(b[6])<<40 | 75 uint64(b[7])<<48 | uint64(b[8])<<56 76 n = 9 77 return 78 } 79 80 // 0-250: value of first byte 81 num = uint64(b[0]) 82 n = 1 83 return 84 } 85 86 func dumpLengthEncodedInt(n uint64) []byte { 87 switch { 88 case n <= 250: 89 return tinyIntCache[n] 90 91 case n <= 0xffff: 92 return []byte{0xfc, byte(n), byte(n >> 8)} 93 94 case n <= 0xffffff: 95 return []byte{0xfd, byte(n), byte(n >> 8), byte(n >> 16)} 96 97 case n <= 0xffffffffffffffff: 98 return []byte{0xfe, byte(n), byte(n >> 8), byte(n >> 16), byte(n >> 24), 99 byte(n >> 32), byte(n >> 40), byte(n >> 48), byte(n >> 56)} 100 } 101 102 return nil 103 } 104 105 func parseLengthEncodedBytes(b []byte) ([]byte, bool, int, error) { 106 // Get length 107 num, isNull, n := parseLengthEncodedInt(b) 108 if num < 1 { 109 return nil, isNull, n, nil 110 } 111 112 n += int(num) 113 114 // Check data length 115 if len(b) >= n { 116 return b[n-int(num) : n], false, n, nil 117 } 118 119 return nil, false, n, io.EOF 120 } 121 122 func dumpLengthEncodedString(b []byte, alloc arena.Allocator) []byte { 123 data := alloc.Alloc(len(b) + 9) 124 data = append(data, dumpLengthEncodedInt(uint64(len(b)))...) 125 data = append(data, b...) 126 return data 127 } 128 129 func dumpUint16(n uint16) []byte { 130 return []byte{ 131 byte(n), 132 byte(n >> 8), 133 } 134 } 135 136 func dumpUint32(n uint32) []byte { 137 return []byte{ 138 byte(n), 139 byte(n >> 8), 140 byte(n >> 16), 141 byte(n >> 24), 142 } 143 } 144 145 func dumpUint64(n uint64) []byte { 146 return []byte{ 147 byte(n), 148 byte(n >> 8), 149 byte(n >> 16), 150 byte(n >> 24), 151 byte(n >> 32), 152 byte(n >> 40), 153 byte(n >> 48), 154 byte(n >> 56), 155 } 156 } 157 158 var tinyIntCache [251][]byte 159 160 func init() { 161 for i := 0; i < len(tinyIntCache); i++ { 162 tinyIntCache[i] = []byte{byte(i)} 163 } 164 } 165 166 func dumpBinaryTime(dur time.Duration) (data []byte) { 167 if dur == 0 { 168 data = tinyIntCache[0] 169 return 170 } 171 data = make([]byte, 13) 172 data[0] = 12 173 if dur < 0 { 174 data[1] = 1 175 dur = -dur 176 } 177 days := dur / (24 * time.Hour) 178 dur -= days * 24 * time.Hour 179 data[2] = byte(days) 180 hours := dur / time.Hour 181 dur -= hours * time.Hour 182 data[6] = byte(hours) 183 minutes := dur / time.Minute 184 dur -= minutes * time.Minute 185 data[7] = byte(minutes) 186 seconds := dur / time.Second 187 dur -= seconds * time.Second 188 data[8] = byte(seconds) 189 if dur == 0 { 190 data[0] = 8 191 return data[:9] 192 } 193 binary.LittleEndian.PutUint32(data[9:13], uint32(dur/time.Microsecond)) 194 return 195 } 196 197 func dumpBinaryDateTime(t mysql.Time, loc *time.Location) (data []byte) { 198 if t.Type == mysql.TypeTimestamp && loc != nil { 199 t.Time = t.In(loc) 200 } 201 202 year, mon, day := t.Year(), t.Month(), t.Day() 203 if t.IsZero() { 204 year, mon, day = 1, time.January, 1 205 } 206 switch t.Type { 207 case mysql.TypeTimestamp, mysql.TypeDatetime: 208 data = append(data, 11) 209 data = append(data, dumpUint16(uint16(year))...) 210 data = append(data, byte(mon), byte(day), byte(t.Hour()), byte(t.Minute()), byte(t.Second())) 211 data = append(data, dumpUint32(uint32((t.Nanosecond() / 1000)))...) 212 case mysql.TypeDate, mysql.TypeNewDate: 213 data = append(data, 4) 214 data = append(data, dumpUint16(uint16(year))...) //year 215 data = append(data, byte(mon), byte(day)) 216 } 217 return 218 } 219 220 func uniformValue(value interface{}) interface{} { 221 switch v := value.(type) { 222 case int8: 223 return int64(v) 224 case int16: 225 return int64(v) 226 case int32: 227 return int64(v) 228 case int64: 229 return int64(v) 230 case uint8: 231 return uint64(v) 232 case uint16: 233 return uint64(v) 234 case uint32: 235 return uint64(v) 236 case uint64: 237 return uint64(v) 238 default: 239 return value 240 } 241 } 242 243 func dumpRowValuesBinary(alloc arena.Allocator, columns []*ColumnInfo, row []types.Datum) (data []byte, err error) { 244 if len(columns) != len(row) { 245 err = mysql.ErrMalformPacket 246 return 247 } 248 data = append(data, mysql.OKHeader) 249 nullsLen := ((len(columns) + 7 + 2) / 8) 250 nulls := make([]byte, nullsLen) 251 for i, val := range row { 252 if val.Kind() == types.KindNull { 253 bytePos := (i + 2) / 8 254 bitPos := byte((i + 2) % 8) 255 nulls[bytePos] |= 1 << bitPos 256 } 257 } 258 data = append(data, nulls...) 259 for i, val := range row { 260 switch val.Kind() { 261 case types.KindInt64: 262 v := val.GetInt64() 263 switch columns[i].Type { 264 case mysql.TypeTiny: 265 data = append(data, byte(v)) 266 case mysql.TypeShort, mysql.TypeYear: 267 data = append(data, dumpUint16(uint16(v))...) 268 case mysql.TypeInt24, mysql.TypeLong: 269 data = append(data, dumpUint32(uint32(v))...) 270 case mysql.TypeLonglong: 271 data = append(data, dumpUint64(uint64(v))...) 272 } 273 case types.KindUint64: 274 v := val.GetUint64() 275 switch columns[i].Type { 276 case mysql.TypeTiny: 277 data = append(data, byte(v)) 278 case mysql.TypeShort, mysql.TypeYear: 279 data = append(data, dumpUint16(uint16(v))...) 280 case mysql.TypeInt24, mysql.TypeLong: 281 data = append(data, dumpUint32(uint32(v))...) 282 case mysql.TypeLonglong: 283 data = append(data, dumpUint64(uint64(v))...) 284 } 285 case types.KindFloat32: 286 floatBits := math.Float32bits(val.GetFloat32()) 287 data = append(data, dumpUint32(floatBits)...) 288 case types.KindFloat64: 289 floatBits := math.Float64bits(val.GetFloat64()) 290 data = append(data, dumpUint64(floatBits)...) 291 case types.KindString, types.KindBytes: 292 data = append(data, dumpLengthEncodedString(val.GetBytes(), alloc)...) 293 case types.KindMysqlDecimal: 294 data = append(data, dumpLengthEncodedString(hack.Slice(val.GetMysqlDecimal().String()), alloc)...) 295 case types.KindMysqlTime: 296 data = append(data, dumpBinaryDateTime(val.GetMysqlTime(), nil)...) 297 case types.KindMysqlDuration: 298 data = append(data, dumpBinaryTime(val.GetMysqlDuration().Duration)...) 299 case types.KindMysqlSet: 300 data = append(data, dumpLengthEncodedString(hack.Slice(val.GetMysqlSet().String()), alloc)...) 301 case types.KindMysqlHex: 302 data = append(data, dumpLengthEncodedString(hack.Slice(val.GetMysqlHex().ToString()), alloc)...) 303 case types.KindMysqlEnum: 304 data = append(data, dumpLengthEncodedString(hack.Slice(val.GetMysqlEnum().String()), alloc)...) 305 case types.KindMysqlBit: 306 data = append(data, dumpLengthEncodedString(hack.Slice(val.GetMysqlBit().ToString()), alloc)...) 307 } 308 } 309 return 310 } 311 312 func dumpTextValue(mysqlType uint8, value types.Datum) ([]byte, error) { 313 switch value.Kind() { 314 case types.KindInt64: 315 return strconv.AppendInt(nil, value.GetInt64(), 10), nil 316 case types.KindUint64: 317 return strconv.AppendUint(nil, value.GetUint64(), 10), nil 318 case types.KindFloat32: 319 return strconv.AppendFloat(nil, value.GetFloat64(), 'f', -1, 32), nil 320 case types.KindFloat64: 321 return strconv.AppendFloat(nil, value.GetFloat64(), 'f', -1, 64), nil 322 case types.KindString, types.KindBytes: 323 return value.GetBytes(), nil 324 case types.KindMysqlTime: 325 return hack.Slice(value.GetMysqlTime().String()), nil 326 case types.KindMysqlDuration: 327 return hack.Slice(value.GetMysqlDuration().String()), nil 328 case types.KindMysqlDecimal: 329 return hack.Slice(value.GetMysqlDecimal().String()), nil 330 case types.KindMysqlEnum: 331 return hack.Slice(value.GetMysqlEnum().String()), nil 332 case types.KindMysqlSet: 333 return hack.Slice(value.GetMysqlSet().String()), nil 334 case types.KindMysqlBit: 335 return hack.Slice(value.GetMysqlBit().ToString()), nil 336 case types.KindMysqlHex: 337 return hack.Slice(value.GetMysqlHex().ToString()), nil 338 default: 339 return nil, errors.Errorf("invalid type %T", value) 340 } 341 }