github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/xapi/xapi.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 xapi
    15  
    16  import (
    17  	"io"
    18  	"io/ioutil"
    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/util/codec"
    24  	"github.com/insionng/yougam/libraries/pingcap/tidb/util/types"
    25  	"github.com/insionng/yougam/libraries/pingcap/tidb/xapi/tablecodec"
    26  	"github.com/insionng/yougam/libraries/pingcap/tipb/go-tipb"
    27  )
    28  
    29  // SelectResult is used to get response rows from SelectRequest.
    30  type SelectResult struct {
    31  	index  bool
    32  	fields []*types.FieldType
    33  	resp   kv.Response
    34  }
    35  
    36  // Next returns the next row.
    37  func (r *SelectResult) Next() (subResult *SubResult, err error) {
    38  	var reader io.ReadCloser
    39  	reader, err = r.resp.Next()
    40  	if err != nil {
    41  		return nil, errors.Trace(err)
    42  	}
    43  	if reader == nil {
    44  		return nil, nil
    45  	}
    46  	subResult = &SubResult{
    47  		index:  r.index,
    48  		fields: r.fields,
    49  		reader: reader,
    50  	}
    51  	return
    52  }
    53  
    54  // Close closes SelectResult.
    55  func (r *SelectResult) Close() error {
    56  	return r.resp.Close()
    57  }
    58  
    59  // SubResult represents a subset of select result.
    60  type SubResult struct {
    61  	index  bool
    62  	fields []*types.FieldType
    63  	reader io.ReadCloser
    64  	resp   *tipb.SelectResponse
    65  	cursor int
    66  }
    67  
    68  // Next returns the next row of the sub result.
    69  // If no more row to return, data would be nil.
    70  func (r *SubResult) Next() (handle int64, data []types.Datum, err error) {
    71  	if r.resp == nil {
    72  		r.resp = new(tipb.SelectResponse)
    73  		var b []byte
    74  		b, err = ioutil.ReadAll(r.reader)
    75  		r.reader.Close()
    76  		if err != nil {
    77  			return 0, nil, errors.Trace(err)
    78  		}
    79  		err = proto.Unmarshal(b, r.resp)
    80  		if err != nil {
    81  			return 0, nil, errors.Trace(err)
    82  		}
    83  		if r.resp.Error != nil {
    84  			return 0, nil, errors.Errorf("[%d %s]", r.resp.Error.GetCode(), r.resp.Error.GetMsg())
    85  		}
    86  	}
    87  	if r.cursor >= len(r.resp.Rows) {
    88  		return 0, nil, nil
    89  	}
    90  	row := r.resp.Rows[r.cursor]
    91  	data, err = tablecodec.DecodeValues(row.Data, r.fields, r.index)
    92  	if err != nil {
    93  		return 0, nil, errors.Trace(err)
    94  	}
    95  	if data == nil {
    96  		// When no column is referenced, the data may be nil, like 'select count(*) from t'.
    97  		// In this case, we need to create a zero length datum slice,
    98  		// as caller will check if data is nil to finish iteration.
    99  		data = make([]types.Datum, 0)
   100  	}
   101  	handleBytes := row.GetHandle()
   102  	datums, err := codec.Decode(handleBytes)
   103  	if err != nil {
   104  		return 0, nil, errors.Trace(err)
   105  	}
   106  	handle = datums[0].GetInt64()
   107  	r.cursor++
   108  	return
   109  }
   110  
   111  // Close closes the sub result.
   112  func (r *SubResult) Close() error {
   113  	return nil
   114  }
   115  
   116  // Select do a select request, returns SelectResult.
   117  func Select(client kv.Client, req *tipb.SelectRequest, concurrency int) (*SelectResult, error) {
   118  	// Convert tipb.*Request to kv.Request
   119  	kvReq, err := composeRequest(req, concurrency)
   120  	if err != nil {
   121  		return nil, errors.Trace(err)
   122  	}
   123  	resp := client.Send(kvReq)
   124  	if resp == nil {
   125  		return nil, errors.New("client returns nil response")
   126  	}
   127  	result := &SelectResult{resp: resp}
   128  	if req.TableInfo != nil {
   129  		result.fields = tablecodec.ProtoColumnsToFieldTypes(req.TableInfo.Columns)
   130  	} else {
   131  		result.fields = tablecodec.ProtoColumnsToFieldTypes(req.IndexInfo.Columns)
   132  		result.index = true
   133  	}
   134  	return result, nil
   135  }
   136  
   137  // Convert tipb.Request to kv.Request.
   138  func composeRequest(req *tipb.SelectRequest, concurrency int) (*kv.Request, error) {
   139  	kvReq := &kv.Request{
   140  		Concurrency: concurrency,
   141  		KeepOrder:   true,
   142  	}
   143  	if req.IndexInfo != nil {
   144  		kvReq.Tp = kv.ReqTypeIndex
   145  		tid := req.IndexInfo.GetTableId()
   146  		idxID := req.IndexInfo.GetIndexId()
   147  		kvReq.KeyRanges = tablecodec.EncodeIndexRanges(tid, idxID, req.Ranges)
   148  	} else {
   149  		kvReq.Tp = kv.ReqTypeSelect
   150  		tid := req.GetTableInfo().GetTableId()
   151  		kvReq.KeyRanges = tablecodec.EncodeTableRanges(tid, req.Ranges)
   152  	}
   153  	if req.OrderBy != nil {
   154  		kvReq.Desc = *req.OrderBy[0].Desc
   155  	}
   156  	var err error
   157  	kvReq.Data, err = proto.Marshal(req)
   158  	if err != nil {
   159  		return nil, errors.Trace(err)
   160  	}
   161  	return kvReq, nil
   162  }
   163  
   164  // SupportExpression checks if the expression is supported by the client.
   165  func SupportExpression(client kv.Client, expr *tipb.Expr) bool {
   166  	return false
   167  }