vitess.io/vitess@v0.16.2/go/vt/vtgate/engine/send_test.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 "errors" 22 "testing" 23 24 "vitess.io/vitess/go/sqltypes" 25 26 "github.com/stretchr/testify/require" 27 28 "vitess.io/vitess/go/vt/key" 29 querypb "vitess.io/vitess/go/vt/proto/query" 30 "vitess.io/vitess/go/vt/vtgate/vindexes" 31 ) 32 33 func TestSendTable(t *testing.T) { 34 type testCase struct { 35 testName string 36 sharded bool 37 shards []string 38 destination key.Destination 39 expectedQueryLog []string 40 expectedError string 41 isDML bool 42 singleShardOnly bool 43 multiShardAutocommit bool 44 } 45 46 singleShard := []string{"0"} 47 twoShards := []string{"-20", "20-"} 48 tests := []testCase{ 49 { 50 testName: "unsharded with no autocommit", 51 sharded: false, 52 shards: singleShard, 53 destination: key.DestinationAllShards{}, 54 expectedQueryLog: []string{ 55 `ResolveDestinations ks [] Destinations:DestinationAllShards()`, 56 `ExecuteMultiShard ks.0: dummy_query {} false false`, 57 }, 58 isDML: false, 59 multiShardAutocommit: false, 60 }, 61 { 62 testName: "sharded with no autocommit", 63 sharded: true, 64 shards: twoShards, 65 destination: key.DestinationShard("20-"), 66 expectedQueryLog: []string{ 67 `ResolveDestinations ks [] Destinations:DestinationShard(20-)`, 68 `ExecuteMultiShard ks.DestinationShard(20-): dummy_query {} false false`, 69 }, 70 isDML: false, 71 multiShardAutocommit: false, 72 }, 73 { 74 testName: "unsharded", 75 sharded: false, 76 shards: singleShard, 77 destination: key.DestinationAllShards{}, 78 expectedQueryLog: []string{ 79 `ResolveDestinations ks [] Destinations:DestinationAllShards()`, 80 `ExecuteMultiShard ks.0: dummy_query {} true true`, 81 }, 82 isDML: true, 83 multiShardAutocommit: false, 84 }, 85 { 86 testName: "sharded with single shard destination", 87 sharded: true, 88 shards: twoShards, 89 destination: key.DestinationShard("20-"), 90 expectedQueryLog: []string{ 91 `ResolveDestinations ks [] Destinations:DestinationShard(20-)`, 92 `ExecuteMultiShard ks.DestinationShard(20-): dummy_query {} true true`, 93 }, 94 isDML: true, 95 multiShardAutocommit: false, 96 }, 97 { 98 testName: "sharded with multi shard destination", 99 sharded: true, 100 shards: twoShards, 101 destination: key.DestinationAllShards{}, 102 expectedQueryLog: []string{ 103 `ResolveDestinations ks [] Destinations:DestinationAllShards()`, 104 `ExecuteMultiShard ks.-20: dummy_query {} ks.20-: dummy_query {} true false`, 105 }, 106 isDML: true, 107 multiShardAutocommit: false, 108 }, 109 { 110 testName: "sharded with multi shard destination and autocommit", 111 sharded: true, 112 shards: twoShards, 113 destination: key.DestinationAllShards{}, 114 expectedQueryLog: []string{ 115 `ResolveDestinations ks [] Destinations:DestinationAllShards()`, 116 `ExecuteMultiShard ks.-20: dummy_query {} ks.20-: dummy_query {} true true`, 117 }, 118 isDML: true, 119 multiShardAutocommit: true, 120 }, 121 { 122 testName: "sharded with multi shard destination", 123 sharded: true, 124 shards: twoShards, 125 destination: key.DestinationAllShards{}, 126 expectedQueryLog: []string{ 127 `ResolveDestinations ks [] Destinations:DestinationAllShards()`, 128 }, 129 expectedError: "Unexpected error, DestinationKeyspaceID mapping to multiple shards: dummy_query, got: DestinationAllShards()", 130 isDML: true, 131 singleShardOnly: true, 132 multiShardAutocommit: false, 133 }, 134 } 135 136 for _, tc := range tests { 137 t.Run(tc.testName, func(t *testing.T) { 138 send := &Send{ 139 Keyspace: &vindexes.Keyspace{ 140 Name: "ks", 141 Sharded: tc.sharded, 142 }, 143 Query: "dummy_query", 144 TargetDestination: tc.destination, 145 IsDML: tc.isDML, 146 SingleShardOnly: tc.singleShardOnly, 147 MultishardAutocommit: tc.multiShardAutocommit, 148 } 149 vc := &loggingVCursor{shards: tc.shards} 150 _, err := send.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 151 if tc.expectedError != "" { 152 require.EqualError(t, err, tc.expectedError) 153 } else { 154 require.NoError(t, err) 155 } 156 vc.ExpectLog(t, tc.expectedQueryLog) 157 158 // Failure cases 159 vc = &loggingVCursor{shardErr: errors.New("shard_error")} 160 _, err = send.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 161 require.EqualError(t, err, "shard_error") 162 163 if !tc.sharded { 164 vc = &loggingVCursor{} 165 _, err = send.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 166 require.EqualError(t, err, "Keyspace does not have exactly one shard: []") 167 } 168 }) 169 } 170 } 171 172 func TestSendTable_StreamExecute(t *testing.T) { 173 type testCase struct { 174 testName string 175 sharded bool 176 shards []string 177 destination key.Destination 178 expectedQueryLog []string 179 expectedError string 180 isDML bool 181 singleShardOnly bool 182 } 183 184 singleShard := []string{"0"} 185 twoShards := []string{"-20", "20-"} 186 tests := []testCase{ 187 { 188 testName: "unsharded with no autocommit", 189 sharded: false, 190 shards: singleShard, 191 destination: key.DestinationAllShards{}, 192 expectedQueryLog: []string{ 193 `ResolveDestinations ks [] Destinations:DestinationAllShards()`, 194 `StreamExecuteMulti dummy_query ks.0: {} `, 195 }, 196 isDML: false, 197 }, 198 { 199 testName: "sharded with no autocommit", 200 sharded: true, 201 shards: twoShards, 202 destination: key.DestinationShard("20-"), 203 expectedQueryLog: []string{ 204 `ResolveDestinations ks [] Destinations:DestinationShard(20-)`, 205 `StreamExecuteMulti dummy_query ks.DestinationShard(20-): {} `, 206 }, 207 isDML: false, 208 }, 209 { 210 testName: "unsharded", 211 sharded: false, 212 shards: singleShard, 213 destination: key.DestinationAllShards{}, 214 expectedQueryLog: []string{ 215 `ResolveDestinations ks [] Destinations:DestinationAllShards()`, 216 `StreamExecuteMulti dummy_query ks.0: {} `, 217 }, 218 isDML: true, 219 }, 220 { 221 testName: "sharded with single shard destination", 222 sharded: true, 223 shards: twoShards, 224 destination: key.DestinationShard("20-"), 225 expectedQueryLog: []string{ 226 `ResolveDestinations ks [] Destinations:DestinationShard(20-)`, 227 `StreamExecuteMulti dummy_query ks.DestinationShard(20-): {} `, 228 }, 229 isDML: true, 230 }, 231 { 232 testName: "sharded with multi shard destination", 233 sharded: true, 234 shards: twoShards, 235 destination: key.DestinationAllShards{}, 236 expectedQueryLog: []string{ 237 `ResolveDestinations ks [] Destinations:DestinationAllShards()`, 238 `StreamExecuteMulti dummy_query ks.-20: {} ks.20-: {} `, 239 }, 240 isDML: true, 241 }, 242 { 243 testName: "sharded with multi shard destination single shard setting", 244 sharded: true, 245 shards: twoShards, 246 destination: key.DestinationAllShards{}, 247 expectedQueryLog: []string{ 248 `ResolveDestinations ks [] Destinations:DestinationAllShards()`, 249 }, 250 expectedError: "Unexpected error, DestinationKeyspaceID mapping to multiple shards: dummy_query, got: DestinationAllShards()", 251 isDML: true, 252 singleShardOnly: true, 253 }, 254 } 255 256 for _, tc := range tests { 257 t.Run(tc.testName, func(t *testing.T) { 258 send := &Send{ 259 Keyspace: &vindexes.Keyspace{ 260 Name: "ks", 261 Sharded: tc.sharded, 262 }, 263 Query: "dummy_query", 264 TargetDestination: tc.destination, 265 IsDML: tc.isDML, 266 SingleShardOnly: tc.singleShardOnly, 267 } 268 vc := &loggingVCursor{shards: tc.shards} 269 _, err := wrapStreamExecute(send, vc, map[string]*querypb.BindVariable{}, false) 270 if tc.expectedError != "" { 271 require.EqualError(t, err, tc.expectedError) 272 } else { 273 require.NoError(t, err) 274 } 275 vc.ExpectLog(t, tc.expectedQueryLog) 276 277 // Failure cases 278 vc = &loggingVCursor{shardErr: errors.New("shard_error")} 279 _, err = wrapStreamExecute(send, vc, map[string]*querypb.BindVariable{}, false) 280 require.EqualError(t, err, "shard_error") 281 282 if !tc.sharded { 283 vc = &loggingVCursor{} 284 _, err = wrapStreamExecute(send, vc, map[string]*querypb.BindVariable{}, false) 285 require.EqualError(t, err, "Keyspace does not have exactly one shard: []") 286 } 287 }) 288 } 289 } 290 291 func TestSendGetFields(t *testing.T) { 292 results := []*sqltypes.Result{sqltypes.MakeTestResult( 293 sqltypes.MakeTestFields( 294 "id|c1|c2|c3", 295 "int64|int64|int64|int64", 296 ), 297 "1|4|5|6", 298 "2|7|8|9", 299 )} 300 301 send := &Send{ 302 Keyspace: &vindexes.Keyspace{ 303 Name: "ks", 304 Sharded: true, 305 }, 306 Query: "dummy_query", 307 TargetDestination: key.DestinationAllShards{}, 308 IsDML: true, 309 SingleShardOnly: false, 310 } 311 vc := &loggingVCursor{shards: []string{"-20", "20-"}, results: results} 312 qr, err := send.GetFields(context.Background(), vc, map[string]*querypb.BindVariable{}) 313 require.NoError(t, err) 314 vc.ExpectLog(t, []string{ 315 `ResolveDestinations ks [] Destinations:DestinationAllShards()`, 316 `ExecuteMultiShard ks.-20: dummy_query {} ks.20-: dummy_query {} true false`, 317 }) 318 require.Nil(t, qr.Rows) 319 require.Equal(t, 4, len(qr.Fields)) 320 }