vitess.io/vitess@v0.16.2/go/vt/vtgate/engine/send.go (about) 1 /* 2 Copyright 2020 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 "vitess.io/vitess/go/sqltypes" 23 "vitess.io/vitess/go/vt/key" 24 vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" 25 "vitess.io/vitess/go/vt/srvtopo" 26 "vitess.io/vitess/go/vt/vterrors" 27 "vitess.io/vitess/go/vt/vtgate/vindexes" 28 29 querypb "vitess.io/vitess/go/vt/proto/query" 30 ) 31 32 var _ Primitive = (*Send)(nil) 33 34 // Send is an operator to send query to the specific keyspace, tabletType and destination 35 type Send struct { 36 // Keyspace specifies the keyspace to send the query to. 37 Keyspace *vindexes.Keyspace 38 39 // TargetDestination specifies an explicit target destination to send the query to. 40 TargetDestination key.Destination 41 42 // Query specifies the query to be executed. 43 Query string 44 45 // IsDML specifies how to deal with autocommit behaviour 46 IsDML bool 47 48 // SingleShardOnly specifies that the query must be send to only single shard 49 SingleShardOnly bool 50 51 // ShardNameNeeded specified that the shard name is added to the bind variables 52 ShardNameNeeded bool 53 54 // MultishardAutocommit specifies that a multishard transaction query can autocommit 55 MultishardAutocommit bool 56 57 noInputs 58 } 59 60 // ShardName as key for setting shard name in bind variables map 61 const ShardName = "__vt_shard" 62 63 // NeedsTransaction implements the Primitive interface 64 func (s *Send) NeedsTransaction() bool { 65 return s.IsDML 66 } 67 68 // RouteType implements Primitive interface 69 func (s *Send) RouteType() string { 70 if s.IsDML { 71 return "SendDML" 72 } 73 74 return "Send" 75 } 76 77 // GetKeyspaceName implements Primitive interface 78 func (s *Send) GetKeyspaceName() string { 79 return s.Keyspace.Name 80 } 81 82 // GetTableName implements Primitive interface 83 func (s *Send) GetTableName() string { 84 return "" 85 } 86 87 // TryExecute implements Primitive interface 88 func (s *Send) TryExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) { 89 ctx, cancelFunc := addQueryTimeout(ctx, vcursor, 0) 90 defer cancelFunc() 91 rss, _, err := vcursor.ResolveDestinations(ctx, s.Keyspace.Name, nil, []key.Destination{s.TargetDestination}) 92 if err != nil { 93 return nil, err 94 } 95 96 if !s.Keyspace.Sharded && len(rss) != 1 { 97 return nil, vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "Keyspace does not have exactly one shard: %v", rss) 98 } 99 100 if s.SingleShardOnly && len(rss) != 1 { 101 return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "Unexpected error, DestinationKeyspaceID mapping to multiple shards: %s, got: %v", s.Query, s.TargetDestination) 102 } 103 104 queries := make([]*querypb.BoundQuery, len(rss)) 105 for i, rs := range rss { 106 bv := bindVars 107 if s.ShardNameNeeded { 108 bv = copyBindVars(bindVars) 109 bv[ShardName] = sqltypes.StringBindVariable(rs.Target.Shard) 110 } 111 queries[i] = &querypb.BoundQuery{ 112 Sql: s.Query, 113 BindVariables: bv, 114 } 115 } 116 117 rollbackOnError := s.IsDML // for non-dml queries, there's no need to do a rollback 118 result, errs := vcursor.ExecuteMultiShard(ctx, s, rss, queries, rollbackOnError, s.canAutoCommit(vcursor, rss)) 119 err = vterrors.Aggregate(errs) 120 if err != nil { 121 return nil, err 122 } 123 return result, nil 124 } 125 126 func (s *Send) canAutoCommit(vcursor VCursor, rss []*srvtopo.ResolvedShard) bool { 127 if s.IsDML { 128 return (len(rss) == 1 || s.MultishardAutocommit) && vcursor.AutocommitApproval() 129 } 130 return false 131 } 132 133 func copyBindVars(in map[string]*querypb.BindVariable) map[string]*querypb.BindVariable { 134 out := make(map[string]*querypb.BindVariable, len(in)+1) 135 for k, v := range in { 136 out[k] = v 137 } 138 return out 139 } 140 141 // TryStreamExecute implements Primitive interface 142 func (s *Send) TryStreamExecute(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error { 143 rss, _, err := vcursor.ResolveDestinations(ctx, s.Keyspace.Name, nil, []key.Destination{s.TargetDestination}) 144 if err != nil { 145 return err 146 } 147 148 if !s.Keyspace.Sharded && len(rss) != 1 { 149 return vterrors.Errorf(vtrpcpb.Code_FAILED_PRECONDITION, "Keyspace does not have exactly one shard: %v", rss) 150 } 151 152 if s.SingleShardOnly && len(rss) != 1 { 153 return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "Unexpected error, DestinationKeyspaceID mapping to multiple shards: %s, got: %v", s.Query, s.TargetDestination) 154 } 155 156 multiBindVars := make([]map[string]*querypb.BindVariable, len(rss)) 157 for i, rs := range rss { 158 bv := bindVars 159 if s.ShardNameNeeded { 160 bv = copyBindVars(bindVars) 161 bv[ShardName] = sqltypes.StringBindVariable(rs.Target.Shard) 162 } 163 multiBindVars[i] = bv 164 } 165 errors := vcursor.StreamExecuteMulti(ctx, s, s.Query, rss, multiBindVars, s.IsDML, s.canAutoCommit(vcursor, rss), callback) 166 return vterrors.Aggregate(errors) 167 } 168 169 // GetFields implements Primitive interface 170 func (s *Send) GetFields(ctx context.Context, vcursor VCursor, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) { 171 qr, err := vcursor.ExecutePrimitive(ctx, s, bindVars, false) 172 if err != nil { 173 return nil, err 174 } 175 qr.Rows = nil 176 return qr, nil 177 } 178 179 func (s *Send) description() PrimitiveDescription { 180 other := map[string]any{ 181 "Query": s.Query, 182 "Table": s.GetTableName(), 183 } 184 if s.IsDML { 185 other["IsDML"] = true 186 } 187 if s.SingleShardOnly { 188 other["SingleShardOnly"] = true 189 } 190 if s.ShardNameNeeded { 191 other["ShardNameNeeded"] = true 192 } 193 if s.MultishardAutocommit { 194 other["MultishardAutocommit"] = true 195 } 196 return PrimitiveDescription{ 197 OperatorType: "Send", 198 Keyspace: s.Keyspace, 199 TargetDestination: s.TargetDestination, 200 Other: other, 201 } 202 }