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  }