vitess.io/vitess@v0.16.2/go/vt/vtgate/engine/vindex_lookup.go (about) 1 /* 2 Copyright 2022 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package engine 18 19 import ( 20 "context" 21 22 vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" 23 24 "vitess.io/vitess/go/vt/key" 25 26 "vitess.io/vitess/go/sqltypes" 27 querypb "vitess.io/vitess/go/vt/proto/query" 28 vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" 29 "vitess.io/vitess/go/vt/vterrors" 30 "vitess.io/vitess/go/vt/vtgate/evalengine" 31 "vitess.io/vitess/go/vt/vtgate/vindexes" 32 ) 33 34 var _ Primitive = (*VindexLookup)(nil) 35 36 type VindexLookup struct { 37 Opcode Opcode 38 39 // The vindex to use to do the Map 40 Vindex vindexes.LookupPlanable 41 42 // Keyspace specifies the keyspace to send the query to. 43 Keyspace *vindexes.Keyspace 44 45 Arguments []string 46 47 // Values specifies the vindex values to use for routing. 48 Values []evalengine.Expr 49 50 // We fetch data in order to do the map from this primitive 51 Lookup Primitive 52 53 // This is the side that needs to be routed 54 SendTo *Route 55 } 56 57 // RouteType implements the Primitive interface 58 func (vr *VindexLookup) RouteType() string { 59 return "VindexLookup" 60 } 61 62 // GetKeyspaceName implements the Primitive interface 63 func (vr *VindexLookup) GetKeyspaceName() string { 64 return vr.SendTo.GetKeyspaceName() 65 } 66 67 // GetTableName implements the Primitive interface 68 func (vr *VindexLookup) GetTableName() string { 69 return vr.SendTo.GetTableName() 70 } 71 72 // GetFields implements the Primitive interface 73 func (vr *VindexLookup) GetFields(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { 74 return vr.SendTo.GetFields(ctx, vcursor, bindVars) 75 } 76 77 // NeedsTransaction implements the Primitive interface 78 func (vr *VindexLookup) NeedsTransaction() bool { 79 return vr.SendTo.NeedsTransaction() 80 } 81 82 // TryExecute implements the Primitive interface 83 func (vr *VindexLookup) TryExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) { 84 ids, err := vr.generateIds(vcursor, bindVars) 85 if err != nil { 86 return nil, err 87 } 88 results, err := vr.lookup(ctx, vcursor, ids) 89 if err != nil { 90 return nil, err 91 } 92 93 dest, err := vr.mapVindexToDestination(ids, results, bindVars) 94 if err != nil { 95 return nil, err 96 } 97 98 return vr.SendTo.executeAfterLookup(ctx, vcursor, bindVars, wantfields, ids, dest) 99 } 100 101 func (vr *VindexLookup) mapVindexToDestination(ids []sqltypes.Value, results []*sqltypes.Result, bindVars map[string]*querypb.BindVariable) ([]key.Destination, error) { 102 dest, err := vr.Vindex.MapResult(ids, results) 103 if err != nil { 104 return nil, err 105 } 106 107 if vr.Opcode == IN { 108 valsBV := &querypb.BindVariable{Type: querypb.Type_TUPLE} 109 valsBV.Values = make([]*querypb.Value, 0, len(ids)) 110 for _, value := range ids { 111 valsBV.Values = append(valsBV.Values, sqltypes.ValueToProto(value)) 112 } 113 bindVars[ListVarName] = valsBV 114 } 115 return dest, nil 116 } 117 118 // TryStreamExecute implements the Primitive interface 119 func (vr *VindexLookup) TryStreamExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error { 120 ids, err := vr.generateIds(vcursor, bindVars) 121 if err != nil { 122 return err 123 } 124 125 results, err := vr.lookup(ctx, vcursor, ids) 126 if err != nil { 127 return err 128 } 129 130 dest, err := vr.mapVindexToDestination(ids, results, bindVars) 131 if err != nil { 132 return err 133 } 134 135 return vr.SendTo.streamExecuteAfterLookup(ctx, vcursor, bindVars, wantfields, callback, ids, dest) 136 } 137 138 // Inputs implements the Primitive interface 139 func (vr *VindexLookup) Inputs() []Primitive { 140 if vr.Lookup != nil { 141 return []Primitive{vr.Lookup, vr.SendTo} 142 } 143 144 return []Primitive{vr.SendTo} 145 } 146 147 // description implements the Primitive interface 148 func (vr *VindexLookup) description() PrimitiveDescription { 149 other := map[string]any{} 150 if vr.Values != nil { 151 formattedValues := make([]string, 0, len(vr.Values)) 152 for _, value := range vr.Values { 153 formattedValues = append(formattedValues, evalengine.FormatExpr(value)) 154 } 155 other["Values"] = formattedValues 156 } 157 other["Vindex"] = vr.Vindex.String() 158 159 return PrimitiveDescription{ 160 OperatorType: "VindexLookup", 161 Variant: vr.Opcode.String(), 162 Keyspace: vr.Keyspace, 163 Other: other, 164 } 165 } 166 167 func (vr *VindexLookup) lookup(ctx context.Context, vcursor VCursor, ids []sqltypes.Value) ([]*sqltypes.Result, error) { 168 co := vr.Vindex.GetCommitOrder() 169 if co != vtgatepb.CommitOrder_NORMAL { 170 vcursor.Session().SetCommitOrder(co) 171 defer vcursor.Session().SetCommitOrder(vtgatepb.CommitOrder_NORMAL) 172 } 173 if ids[0].IsIntegral() || vr.Vindex.AllowBatch() { 174 return vr.executeBatch(ctx, vcursor, ids) 175 } 176 return vr.executeNonBatch(ctx, vcursor, ids) 177 } 178 179 func (vr *VindexLookup) executeNonBatch(ctx context.Context, vcursor VCursor, ids []sqltypes.Value) ([]*sqltypes.Result, error) { 180 results := make([]*sqltypes.Result, 0, len(ids)) 181 // for non integral and binary type, fallback to send query per id 182 for _, id := range ids { 183 vars, err := sqltypes.BuildBindVariable([]any{id}) 184 if err != nil { 185 return nil, err 186 } 187 bindVars := map[string]*querypb.BindVariable{ 188 vr.Arguments[0]: vars, 189 } 190 191 var result *sqltypes.Result 192 if vr.Vindex.AutoCommitEnabled() { 193 result, err = vcursor.ExecutePrimitiveStandalone(ctx, vr.Lookup, bindVars, false) 194 } else { 195 result, err = vcursor.ExecutePrimitive(ctx, vr.Lookup, bindVars, false) 196 } 197 if err != nil { 198 return nil, err 199 } 200 201 rows := make([][]sqltypes.Value, 0, len(result.Rows)) 202 for _, row := range result.Rows { 203 rows = append(rows, []sqltypes.Value{row[1]}) 204 } 205 results = append(results, &sqltypes.Result{ 206 Rows: rows, 207 }) 208 } 209 return results, nil 210 } 211 212 func (vr *VindexLookup) executeBatch(ctx context.Context, vcursor VCursor, ids []sqltypes.Value) ([]*sqltypes.Result, error) { 213 results := make([]*sqltypes.Result, 0, len(ids)) 214 // for integral types, batch query all ids and then map them back to the input order 215 vars, err := sqltypes.BuildBindVariable(ids) 216 if err != nil { 217 return nil, err 218 } 219 bindVars := map[string]*querypb.BindVariable{ 220 vr.Arguments[0]: vars, 221 } 222 223 var result *sqltypes.Result 224 if vr.Vindex.AutoCommitEnabled() { 225 result, err = vcursor.ExecutePrimitiveStandalone(ctx, vr.Lookup, bindVars, false) 226 } else { 227 result, err = vcursor.ExecutePrimitive(ctx, vr.Lookup, bindVars, false) 228 } 229 if err != nil { 230 return nil, err 231 } 232 233 if err != nil { 234 return nil, vterrors.Wrapf(err, "failed while running the lookup query") 235 } 236 resultMap := make(map[string][][]sqltypes.Value) 237 for _, row := range result.Rows { 238 resultMap[row[0].ToString()] = append(resultMap[row[0].ToString()], []sqltypes.Value{row[1]}) 239 } 240 241 for _, id := range ids { 242 results = append(results, &sqltypes.Result{ 243 Rows: resultMap[id.ToString()], 244 }) 245 } 246 return results, nil 247 } 248 249 func (vr *VindexLookup) generateIds(vcursor VCursor, bindVars map[string]*querypb.BindVariable) ([]sqltypes.Value, error) { 250 env := evalengine.EnvWithBindVars(bindVars, vcursor.ConnCollation()) 251 value, err := env.Evaluate(vr.Values[0]) 252 if err != nil { 253 return nil, err 254 } 255 switch vr.Opcode { 256 case Equal, EqualUnique: 257 return []sqltypes.Value{value.Value()}, nil 258 case IN: 259 return value.TupleValues(), nil 260 } 261 return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "opcode %s not supported for VindexLookup", vr.Opcode.String()) 262 }