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 }