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  }