github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/xapi/tablecodec/tablecodec.go (about) 1 // Copyright 2016 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package tablecodec 15 16 import ( 17 "bytes" 18 "time" 19 20 "github.com/insionng/yougam/libraries/golang/protobuf/proto" 21 "github.com/insionng/yougam/libraries/juju/errors" 22 "github.com/insionng/yougam/libraries/pingcap/tidb/kv" 23 "github.com/insionng/yougam/libraries/pingcap/tidb/model" 24 "github.com/insionng/yougam/libraries/pingcap/tidb/mysql" 25 "github.com/insionng/yougam/libraries/pingcap/tidb/util/codec" 26 "github.com/insionng/yougam/libraries/pingcap/tidb/util/types" 27 "github.com/insionng/yougam/libraries/pingcap/tipb/go-tipb" 28 ) 29 30 var ( 31 tablePrefix = []byte{'t'} 32 recordPrefixSep = []byte("_r") 33 indexPrefixSep = []byte("_i") 34 ) 35 36 const ( 37 idLen = 8 38 prefixLen = 1 + idLen /*tableID*/ + 2 39 recordRowKeyLen = prefixLen + idLen /*handle*/ 40 ) 41 42 // EncodeRowKey encodes the table id and record handle into a kv.Key 43 func EncodeRowKey(tableID int64, encodedHandle []byte) kv.Key { 44 buf := make([]byte, 0, recordRowKeyLen) 45 buf = appendTableRecordPrefix(buf, tableID) 46 buf = append(buf, encodedHandle...) 47 return buf 48 } 49 50 // EncodeColumnKey encodes the table id, row handle and columnID into a kv.Key 51 func EncodeColumnKey(tableID int64, handle int64, columnID int64) kv.Key { 52 buf := make([]byte, 0, recordRowKeyLen+idLen) 53 buf = appendTableRecordPrefix(buf, tableID) 54 buf = codec.EncodeInt(buf, handle) 55 buf = codec.EncodeInt(buf, columnID) 56 return buf 57 } 58 59 // DecodeRowKey decodes the key and gets the handle. 60 func DecodeRowKey(key kv.Key) (handle int64, err error) { 61 k := key 62 if !key.HasPrefix(tablePrefix) { 63 return 0, errors.Errorf("invalid record key - %q", k) 64 } 65 66 key = key[len(tablePrefix):] 67 // Table ID is not needed. 68 key, _, err = codec.DecodeInt(key) 69 if err != nil { 70 return 0, errors.Trace(err) 71 } 72 73 if !key.HasPrefix(recordPrefixSep) { 74 return 0, errors.Errorf("invalid record key - %q", k) 75 } 76 77 key = key[len(recordPrefixSep):] 78 79 key, handle, err = codec.DecodeInt(key) 80 if err != nil { 81 return 0, errors.Trace(err) 82 } 83 return 84 } 85 86 // DecodeValues decodes a byte slice into datums with column types. 87 func DecodeValues(data []byte, fts []*types.FieldType, inIndex bool) ([]types.Datum, error) { 88 if data == nil { 89 return nil, nil 90 } 91 values, err := codec.Decode(data) 92 if err != nil { 93 return nil, errors.Trace(err) 94 } 95 if len(values) > len(fts) { 96 return nil, errors.Errorf("invalid column count %d is less than value count %d", len(fts), len(values)) 97 } 98 if inIndex { 99 // We don't need to unflatten index columns for now. 100 return values, nil 101 } 102 103 for i := range values { 104 values[i], err = unflatten(values[i], fts[i]) 105 if err != nil { 106 return nil, errors.Trace(err) 107 } 108 } 109 return values, nil 110 } 111 112 // unflatten converts a raw datum to a column datum. 113 func unflatten(datum types.Datum, ft *types.FieldType) (types.Datum, error) { 114 if datum.Kind() == types.KindNull { 115 return datum, nil 116 } 117 switch ft.Tp { 118 case mysql.TypeFloat: 119 datum.SetFloat32(float32(datum.GetFloat64())) 120 return datum, nil 121 case mysql.TypeTiny, mysql.TypeShort, mysql.TypeYear, mysql.TypeInt24, 122 mysql.TypeLong, mysql.TypeLonglong, mysql.TypeDouble, mysql.TypeTinyBlob, 123 mysql.TypeMediumBlob, mysql.TypeBlob, mysql.TypeLongBlob, mysql.TypeVarchar, 124 mysql.TypeString: 125 return datum, nil 126 case mysql.TypeDate, mysql.TypeDatetime, mysql.TypeTimestamp: 127 var t mysql.Time 128 t.Type = ft.Tp 129 t.Fsp = ft.Decimal 130 err := t.Unmarshal(datum.GetBytes()) 131 if err != nil { 132 return datum, errors.Trace(err) 133 } 134 datum.SetMysqlTime(t) 135 return datum, nil 136 case mysql.TypeDuration: 137 dur := mysql.Duration{Duration: time.Duration(datum.GetInt64())} 138 datum.SetValue(dur) 139 return datum, nil 140 case mysql.TypeNewDecimal: 141 dec, err := mysql.ParseDecimal(datum.GetString()) 142 if err != nil { 143 return datum, errors.Trace(err) 144 } 145 datum.SetValue(dec) 146 return datum, nil 147 case mysql.TypeEnum: 148 enum, err := mysql.ParseEnumValue(ft.Elems, datum.GetUint64()) 149 if err != nil { 150 return datum, errors.Trace(err) 151 } 152 datum.SetValue(enum) 153 return datum, nil 154 case mysql.TypeSet: 155 set, err := mysql.ParseSetValue(ft.Elems, datum.GetUint64()) 156 if err != nil { 157 return datum, errors.Trace(err) 158 } 159 datum.SetValue(set) 160 return datum, nil 161 case mysql.TypeBit: 162 bit := mysql.Bit{Value: datum.GetUint64(), Width: ft.Flen} 163 datum.SetValue(bit) 164 return datum, nil 165 } 166 return datum, nil 167 } 168 169 // EncodeIndexSeekKey encodes an index value to kv.Key. 170 func EncodeIndexSeekKey(tableID int64, idxID int64, encodedValue []byte) kv.Key { 171 key := make([]byte, 0, prefixLen+len(encodedValue)) 172 key = appendTableIndexPrefix(key, tableID) 173 key = codec.EncodeInt(key, idxID) 174 key = append(key, encodedValue...) 175 return key 176 } 177 178 // DecodeIndexKey decodes datums from an index key. 179 func DecodeIndexKey(key kv.Key) ([]types.Datum, error) { 180 b := key[prefixLen+idLen:] 181 return codec.Decode(b) 182 } 183 184 // Record prefix is "t[tableID]_r". 185 func appendTableRecordPrefix(buf []byte, tableID int64) []byte { 186 buf = append(buf, tablePrefix...) 187 buf = codec.EncodeInt(buf, tableID) 188 buf = append(buf, recordPrefixSep...) 189 return buf 190 } 191 192 // Index prefix is "t[tableID]_i". 193 func appendTableIndexPrefix(buf []byte, tableID int64) []byte { 194 buf = append(buf, tablePrefix...) 195 buf = codec.EncodeInt(buf, tableID) 196 buf = append(buf, indexPrefixSep...) 197 return buf 198 } 199 200 func columnToProto(c *model.ColumnInfo) *tipb.ColumnInfo { 201 pc := &tipb.ColumnInfo{ 202 ColumnId: proto.Int64(c.ID), 203 Collation: proto.Int32(collationToProto(c.FieldType.Collate)), 204 ColumnLen: proto.Int32(int32(c.FieldType.Flen)), 205 Decimal: proto.Int32(int32(c.FieldType.Decimal)), 206 Flag: proto.Int32(int32(c.Flag)), 207 Elems: c.Elems, 208 } 209 t := int32(c.FieldType.Tp) 210 pc.Tp = &t 211 return pc 212 } 213 214 func collationToProto(c string) int32 { 215 v, ok := mysql.CollationNames[c] 216 if ok { 217 return int32(v) 218 } 219 return int32(mysql.DefaultCollationID) 220 } 221 222 // ColumnsToProto converts a slice of model.ColumnInfo to a slice of tipb.ColumnInfo. 223 func ColumnsToProto(columns []*model.ColumnInfo, pkIsHandle bool) []*tipb.ColumnInfo { 224 cols := make([]*tipb.ColumnInfo, 0, len(columns)) 225 for _, c := range columns { 226 col := columnToProto(c) 227 if pkIsHandle && mysql.HasPriKeyFlag(c.Flag) { 228 col.PkHandle = proto.Bool(true) 229 } else { 230 col.PkHandle = proto.Bool(false) 231 } 232 cols = append(cols, col) 233 } 234 return cols 235 } 236 237 // ProtoColumnsToFieldTypes converts tipb column info slice to FieldTyps slice. 238 func ProtoColumnsToFieldTypes(pColumns []*tipb.ColumnInfo) []*types.FieldType { 239 fields := make([]*types.FieldType, len(pColumns)) 240 for i, v := range pColumns { 241 field := new(types.FieldType) 242 field.Tp = byte(v.GetTp()) 243 field.Collate = mysql.Collations[byte(v.GetCollation())] 244 field.Decimal = int(v.GetDecimal()) 245 field.Flen = int(v.GetColumnLen()) 246 field.Flag = uint(v.GetFlag()) 247 field.Elems = v.GetElems() 248 fields[i] = field 249 } 250 return fields 251 } 252 253 // IndexToProto converts a model.IndexInfo to a tipb.IndexInfo. 254 func IndexToProto(t *model.TableInfo, idx *model.IndexInfo) *tipb.IndexInfo { 255 pi := &tipb.IndexInfo{ 256 TableId: proto.Int64(t.ID), 257 IndexId: proto.Int64(idx.ID), 258 Unique: proto.Bool(idx.Unique), 259 } 260 cols := make([]*tipb.ColumnInfo, 0, len(idx.Columns)) 261 for _, c := range idx.Columns { 262 cols = append(cols, columnToProto(t.Columns[c.Offset])) 263 } 264 pi.Columns = cols 265 return pi 266 } 267 268 // EncodeTableRanges encodes table ranges into kv.KeyRanges. 269 func EncodeTableRanges(tid int64, rans []*tipb.KeyRange) []kv.KeyRange { 270 keyRanges := make([]kv.KeyRange, 0, len(rans)) 271 for _, r := range rans { 272 start := EncodeRowKey(tid, r.Low) 273 end := EncodeRowKey(tid, r.High) 274 nr := kv.KeyRange{ 275 StartKey: start, 276 EndKey: end, 277 } 278 keyRanges = append(keyRanges, nr) 279 } 280 return keyRanges 281 } 282 283 // EncodeIndexRanges encodes index ranges into kv.KeyRanges. 284 func EncodeIndexRanges(tid, idxID int64, rans []*tipb.KeyRange) []kv.KeyRange { 285 keyRanges := make([]kv.KeyRange, 0, len(rans)) 286 for _, r := range rans { 287 // Convert range to kv.KeyRange 288 start := EncodeIndexSeekKey(tid, idxID, r.Low) 289 end := EncodeIndexSeekKey(tid, idxID, r.High) 290 nr := kv.KeyRange{ 291 StartKey: start, 292 EndKey: end, 293 } 294 keyRanges = append(keyRanges, nr) 295 } 296 return keyRanges 297 } 298 299 // TruncateToRowKeyLen truncates the key to row key length if the key is longer than row key. 300 func TruncateToRowKeyLen(key kv.Key) kv.Key { 301 if len(key) > recordRowKeyLen { 302 return key[:recordRowKeyLen] 303 } 304 return key 305 } 306 307 type keyRangeSorter struct { 308 ranges []kv.KeyRange 309 } 310 311 func (r *keyRangeSorter) Len() int { 312 return len(r.ranges) 313 } 314 315 func (r *keyRangeSorter) Less(i, j int) bool { 316 a := r.ranges[i] 317 b := r.ranges[j] 318 cmp := bytes.Compare(a.StartKey, b.StartKey) 319 return cmp < 0 320 } 321 322 func (r *keyRangeSorter) Swap(i, j int) { 323 r.ranges[i], r.ranges[j] = r.ranges[j], r.ranges[i] 324 }