github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/store/localstore/xapi_test.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 localstore 15 16 import ( 17 "fmt" 18 "io/ioutil" 19 "math" 20 "sort" 21 "testing" 22 "time" 23 24 "github.com/insionng/yougam/libraries/golang/protobuf/proto" 25 "github.com/insionng/yougam/libraries/juju/errors" 26 . "github.com/insionng/yougam/libraries/pingcap/check" 27 "github.com/insionng/yougam/libraries/pingcap/tidb/kv" 28 "github.com/insionng/yougam/libraries/pingcap/tidb/mysql" 29 "github.com/insionng/yougam/libraries/pingcap/tidb/util/codec" 30 "github.com/insionng/yougam/libraries/pingcap/tidb/util/testleak" 31 "github.com/insionng/yougam/libraries/pingcap/tidb/util/types" 32 "github.com/insionng/yougam/libraries/pingcap/tidb/xapi/tablecodec" 33 "github.com/insionng/yougam/libraries/pingcap/tipb/go-tipb" 34 ) 35 36 func TestT(t *testing.T) { 37 TestingT(t) 38 } 39 40 var _ = Suite(&testXAPISuite{}) 41 42 type testXAPISuite struct { 43 } 44 45 var tbInfo = &simpleTableInfo{ 46 tID: 1, 47 cTypes: []byte{mysql.TypeVarchar, mysql.TypeDouble}, 48 cIDs: []int64{3, 4}, 49 indices: []int{0}, // column 3 of varchar type. 50 iIDs: []int64{5}, 51 } 52 53 func (s *testXAPISuite) TestSelect(c *C) { 54 defer testleak.AfterTest(c)() 55 store := createMemStore(time.Now().Nanosecond()) 56 count := int64(10) 57 err := prepareTableData(store, tbInfo, count, genValues) 58 c.Check(err, IsNil) 59 60 // Select Table request. 61 txn, err := store.Begin() 62 c.Check(err, IsNil) 63 client := txn.GetClient() 64 req, err := prepareSelectRequest(tbInfo, txn.StartTS()) 65 c.Check(err, IsNil) 66 resp := client.Send(req) 67 subResp, err := resp.Next() 68 c.Check(err, IsNil) 69 data, err := ioutil.ReadAll(subResp) 70 c.Check(err, IsNil) 71 selResp := new(tipb.SelectResponse) 72 proto.Unmarshal(data, selResp) 73 c.Check(selResp.Rows, HasLen, int(count)) 74 for i, row := range selResp.Rows { 75 handle := int64(i + 1) 76 expectedDatums := []types.Datum{types.NewDatum(handle)} 77 expectedDatums = append(expectedDatums, genValues(handle, tbInfo)...) 78 var expectedEncoded []byte 79 expectedEncoded, err = codec.EncodeValue(nil, expectedDatums...) 80 c.Assert(err, IsNil) 81 c.Assert(row.Data, BytesEquals, expectedEncoded) 82 } 83 txn.Commit() 84 85 // Select Index request. 86 txn, err = store.Begin() 87 c.Check(err, IsNil) 88 client = txn.GetClient() 89 req, err = prepareIndexRequest(tbInfo, txn.StartTS()) 90 c.Check(err, IsNil) 91 resp = client.Send(req) 92 subResp, err = resp.Next() 93 c.Check(err, IsNil) 94 data, err = ioutil.ReadAll(subResp) 95 c.Check(err, IsNil) 96 idxResp := new(tipb.SelectResponse) 97 proto.Unmarshal(data, idxResp) 98 c.Check(idxResp.Rows, HasLen, int(count)) 99 handles := make([]int, 0, 10) 100 for _, row := range idxResp.Rows { 101 var err error 102 datums, err := codec.Decode(row.Handle) 103 c.Check(err, IsNil) 104 c.Check(datums, HasLen, 1) 105 handles = append(handles, int(datums[0].GetInt64())) 106 } 107 sort.Ints(handles) 108 for i, h := range handles { 109 c.Assert(h, Equals, i+1) 110 } 111 txn.Commit() 112 113 store.Close() 114 } 115 116 // simpleTableInfo just have the minimum information enough to describe the table. 117 // The first column is pk handle column. 118 type simpleTableInfo struct { 119 tID int64 // table ID. 120 cTypes []byte // columns not including pk handle column. 121 cIDs []int64 122 indices []int // indexed column offsets. only single column index for now. 123 iIDs []int64 124 } 125 126 func (s *simpleTableInfo) toPBTableInfo() *tipb.TableInfo { 127 tbInfo := new(tipb.TableInfo) 128 tbInfo.TableId = proto.Int64(s.tID) 129 pkColumn := new(tipb.ColumnInfo) 130 pkColumn.Tp = proto.Int32(int32(mysql.TypeLonglong)) 131 // It's ok to just use table ID for pk column ID, as it doesn't have a column kv. 132 pkColumn.ColumnId = tbInfo.TableId 133 pkColumn.PkHandle = proto.Bool(true) 134 pkColumn.Flag = proto.Int32(0) 135 tbInfo.Columns = append(tbInfo.Columns, pkColumn) 136 for i, colTp := range s.cTypes { 137 coInfo := &tipb.ColumnInfo{ 138 ColumnId: proto.Int64(s.cIDs[i]), 139 Tp: proto.Int32(int32(colTp)), 140 PkHandle: proto.Bool(false), 141 } 142 tbInfo.Columns = append(tbInfo.Columns, coInfo) 143 } 144 return tbInfo 145 } 146 147 func (s *simpleTableInfo) toPBIndexInfo(idxOff int) *tipb.IndexInfo { 148 idxInfo := new(tipb.IndexInfo) 149 idxInfo.TableId = proto.Int64(s.tID) 150 idxInfo.IndexId = proto.Int64(s.iIDs[idxOff]) 151 colOff := s.indices[idxOff] 152 idxInfo.Columns = []*tipb.ColumnInfo{ 153 { 154 ColumnId: proto.Int64(s.cIDs[colOff]), 155 Tp: proto.Int32((int32(s.cTypes[colOff]))), 156 PkHandle: proto.Bool(false), 157 }, 158 } 159 return idxInfo 160 } 161 162 func genValues(handle int64, tbl *simpleTableInfo) []types.Datum { 163 values := make([]types.Datum, 0, len(tbl.cTypes)) 164 for _, tp := range tbl.cTypes { 165 switch tp { 166 case mysql.TypeLong: 167 values = append(values, types.NewDatum(handle)) 168 case mysql.TypeVarchar: 169 values = append(values, types.NewDatum(fmt.Sprintf("varchar:%d", handle))) 170 case mysql.TypeDouble: 171 values = append(values, types.NewDatum(float64(handle)/10)) 172 default: 173 values = append(values, types.Datum{}) 174 } 175 } 176 return values 177 } 178 179 type genValueFunc func(handle int64, tbl *simpleTableInfo) []types.Datum 180 181 func prepareTableData(store kv.Storage, tbl *simpleTableInfo, count int64, gen genValueFunc) error { 182 txn, err := store.Begin() 183 if err != nil { 184 return errors.Trace(err) 185 } 186 for i := int64(1); i <= count; i++ { 187 setRow(txn, i, tbl, gen) 188 } 189 return txn.Commit() 190 } 191 192 func setRow(txn kv.Transaction, handle int64, tbl *simpleTableInfo, gen genValueFunc) error { 193 rowKey := tablecodec.EncodeRowKey(tbl.tID, codec.EncodeInt(nil, handle)) 194 txn.Set(rowKey, []byte(txn.String())) 195 columnValues := gen(handle, tbl) 196 for i, v := range columnValues { 197 cKey, cVal, err := encodeColumnKV(tbl.tID, handle, tbl.cIDs[i], v) 198 if err != nil { 199 return errors.Trace(err) 200 } 201 err = txn.Set(cKey, cVal) 202 if err != nil { 203 return errors.Trace(err) 204 } 205 } 206 for i, idxCol := range tbl.indices { 207 idxVal := columnValues[idxCol] 208 encoded, err := codec.EncodeKey(nil, idxVal, types.NewDatum(handle)) 209 if err != nil { 210 return errors.Trace(err) 211 } 212 idxKey := tablecodec.EncodeIndexSeekKey(tbl.tID, tbl.iIDs[i], encoded) 213 err = txn.Set(idxKey, []byte{0}) 214 if err != nil { 215 return errors.Trace(err) 216 } 217 } 218 return nil 219 } 220 221 func encodeColumnKV(tid, handle, cid int64, value types.Datum) (kv.Key, []byte, error) { 222 key := tablecodec.EncodeColumnKey(tid, handle, cid) 223 val, err := codec.EncodeValue(nil, value) 224 if err != nil { 225 return nil, nil, errors.Trace(err) 226 } 227 return key, val, nil 228 } 229 230 func prepareSelectRequest(simpleInfo *simpleTableInfo, startTs uint64) (*kv.Request, error) { 231 selReq := new(tipb.SelectRequest) 232 selReq.TableInfo = simpleInfo.toPBTableInfo() 233 selReq.StartTs = proto.Uint64(startTs) 234 selReq.Ranges = []*tipb.KeyRange{fullPBTableRange} 235 data, err := proto.Marshal(selReq) 236 if err != nil { 237 return nil, errors.Trace(err) 238 } 239 req := new(kv.Request) 240 req.Tp = kv.ReqTypeSelect 241 req.Concurrency = 1 242 req.KeyRanges = []kv.KeyRange{fullTableRange(simpleInfo.tID)} 243 req.Data = data 244 return req, nil 245 } 246 247 func fullTableRange(tid int64) kv.KeyRange { 248 return kv.KeyRange{ 249 StartKey: tablecodec.EncodeRowKey(tid, codec.EncodeInt(nil, math.MinInt64)), 250 EndKey: tablecodec.EncodeRowKey(tid, codec.EncodeInt(nil, math.MaxInt64)), 251 } 252 } 253 254 var fullPBTableRange = &tipb.KeyRange{ 255 Low: codec.EncodeInt(nil, math.MinInt64), 256 High: codec.EncodeInt(nil, math.MaxInt64), 257 } 258 var fullPBIndexRange = &tipb.KeyRange{ 259 Low: []byte{0}, 260 High: []byte{255}, 261 } 262 263 func fullIndexRange(tid int64, idxID int64) kv.KeyRange { 264 return kv.KeyRange{ 265 StartKey: tablecodec.EncodeIndexSeekKey(tid, idxID, []byte{0}), 266 EndKey: tablecodec.EncodeIndexSeekKey(tid, idxID, []byte{255}), 267 } 268 } 269 270 func prepareIndexRequest(simpleInfo *simpleTableInfo, startTs uint64) (*kv.Request, error) { 271 selReq := new(tipb.SelectRequest) 272 selReq.IndexInfo = simpleInfo.toPBIndexInfo(0) 273 selReq.StartTs = proto.Uint64(startTs) 274 selReq.Ranges = []*tipb.KeyRange{fullPBIndexRange} 275 data, err := proto.Marshal(selReq) 276 if err != nil { 277 return nil, errors.Trace(err) 278 } 279 req := new(kv.Request) 280 req.Tp = kv.ReqTypeIndex 281 req.Concurrency = 1 282 req.KeyRanges = []kv.KeyRange{fullIndexRange(simpleInfo.tID, simpleInfo.iIDs[0])} 283 req.Data = data 284 return req, nil 285 }