vitess.io/vitess@v0.16.2/go/vt/vtgate/engine/update_test.go (about) 1 /* 2 Copyright 2019 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/mysql/collations" 25 "vitess.io/vitess/go/vt/vtgate/evalengine" 26 27 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 28 29 "github.com/stretchr/testify/require" 30 31 "vitess.io/vitess/go/sqltypes" 32 "vitess.io/vitess/go/vt/vtgate/vindexes" 33 34 querypb "vitess.io/vitess/go/vt/proto/query" 35 vschemapb "vitess.io/vitess/go/vt/proto/vschema" 36 ) 37 38 func TestUpdateUnsharded(t *testing.T) { 39 upd := &Update{ 40 DML: &DML{ 41 RoutingParameters: &RoutingParameters{ 42 Opcode: Unsharded, 43 Keyspace: &vindexes.Keyspace{ 44 Name: "ks", 45 Sharded: false, 46 }, 47 }, 48 49 Query: "dummy_update", 50 }, 51 } 52 53 vc := newDMLTestVCursor("0") 54 _, err := upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 55 require.NoError(t, err) 56 vc.ExpectLog(t, []string{ 57 `ResolveDestinations ks [] Destinations:DestinationAllShards()`, 58 `ExecuteMultiShard ks.0: dummy_update {} true true`, 59 }) 60 61 // Failure cases 62 vc = &loggingVCursor{shardErr: errors.New("shard_error")} 63 _, err = upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 64 require.EqualError(t, err, `shard_error`) 65 66 vc = &loggingVCursor{} 67 _, err = upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 68 require.EqualError(t, err, `Keyspace 'ks' does not have exactly one shard: []`) 69 } 70 71 func TestUpdateEqual(t *testing.T) { 72 vindex, _ := vindexes.NewHash("", nil) 73 upd := &Update{ 74 DML: &DML{ 75 RoutingParameters: &RoutingParameters{ 76 Opcode: Equal, 77 Keyspace: &vindexes.Keyspace{ 78 Name: "ks", 79 Sharded: true, 80 }, 81 Vindex: vindex, 82 Values: []evalengine.Expr{evalengine.NewLiteralInt(1)}, 83 }, 84 Query: "dummy_update", 85 }, 86 } 87 88 vc := newDMLTestVCursor("-20", "20-") 89 _, err := upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 90 require.NoError(t, err) 91 vc.ExpectLog(t, []string{ 92 `ResolveDestinations ks [type:INT64 value:"1"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6)`, 93 `ExecuteMultiShard ks.-20: dummy_update {} true true`, 94 }) 95 96 // Failure case 97 upd.Values = []evalengine.Expr{evalengine.NewBindVar("aa", collations.TypedCollation{})} 98 _, err = upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 99 require.EqualError(t, err, `query arguments missing for aa`) 100 } 101 102 func TestUpdateEqualMultiCol(t *testing.T) { 103 vindex, _ := vindexes.NewRegionExperimental("", map[string]string{"region_bytes": "1"}) 104 upd := &Update{ 105 DML: &DML{ 106 RoutingParameters: &RoutingParameters{ 107 Opcode: Equal, 108 Keyspace: &vindexes.Keyspace{ 109 Name: "ks", 110 Sharded: true, 111 }, 112 Vindex: vindex, 113 Values: []evalengine.Expr{evalengine.NewLiteralInt(1), evalengine.NewLiteralInt(2)}, 114 }, 115 Query: "dummy_update", 116 }, 117 } 118 119 vc := newDMLTestVCursor("-20", "20-") 120 _, err := upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 121 require.NoError(t, err) 122 vc.ExpectLog(t, []string{ 123 `ResolveDestinationsMultiCol ks [[INT64(1) INT64(2)]] Destinations:DestinationKeyspaceID(0106e7ea22ce92708f)`, 124 `ExecuteMultiShard ks.-20: dummy_update {} true true`, 125 }) 126 } 127 128 func TestUpdateScatter(t *testing.T) { 129 vindex, _ := vindexes.NewHash("", nil) 130 upd := &Update{ 131 DML: &DML{ 132 RoutingParameters: &RoutingParameters{ 133 Opcode: Scatter, 134 Keyspace: &vindexes.Keyspace{ 135 Name: "ks", 136 Sharded: true, 137 }, 138 Vindex: vindex, 139 Values: []evalengine.Expr{evalengine.NewLiteralInt(1)}, 140 }, 141 Query: "dummy_update", 142 }, 143 } 144 145 vc := newDMLTestVCursor("-20", "20-") 146 _, err := upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 147 require.NoError(t, err) 148 149 vc.ExpectLog(t, []string{ 150 `ResolveDestinations ks [] Destinations:DestinationAllShards()`, 151 `ExecuteMultiShard ks.-20: dummy_update {} ks.20-: dummy_update {} true false`, 152 }) 153 154 // works with multishard autocommit 155 upd = &Update{ 156 DML: &DML{ 157 RoutingParameters: &RoutingParameters{ 158 Opcode: Scatter, 159 Keyspace: &vindexes.Keyspace{ 160 Name: "ks", 161 Sharded: true, 162 }, 163 Vindex: vindex, 164 Values: []evalengine.Expr{evalengine.NewLiteralInt(1)}, 165 }, 166 Query: "dummy_update", 167 MultiShardAutocommit: true, 168 }, 169 } 170 171 vc = newDMLTestVCursor("-20", "20-") 172 _, err = upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 173 require.NoError(t, err) 174 175 vc.ExpectLog(t, []string{ 176 `ResolveDestinations ks [] Destinations:DestinationAllShards()`, 177 `ExecuteMultiShard ks.-20: dummy_update {} ks.20-: dummy_update {} true true`, 178 }) 179 } 180 181 func TestUpdateEqualNoRoute(t *testing.T) { 182 vindex, _ := vindexes.NewLookupUnique("", map[string]string{ 183 "table": "lkp", 184 "from": "from", 185 "to": "toc", 186 }) 187 upd := &Update{ 188 DML: &DML{ 189 RoutingParameters: &RoutingParameters{ 190 Opcode: Equal, 191 Keyspace: &vindexes.Keyspace{ 192 Name: "ks", 193 Sharded: true, 194 }, 195 Vindex: vindex, 196 Values: []evalengine.Expr{evalengine.NewLiteralInt(1)}, 197 }, 198 Query: "dummy_update", 199 }, 200 } 201 202 vc := newDMLTestVCursor("0") 203 _, err := upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 204 require.NoError(t, err) 205 vc.ExpectLog(t, []string{ 206 // This lookup query will return no rows. So, the DML will not be sent anywhere. 207 `Execute select from, toc from lkp where from in ::from from: type:TUPLE values:{type:INT64 value:"1"} false`, 208 `ResolveDestinations ks [type:INT64 value:"1"] Destinations:DestinationNone()`, 209 }) 210 } 211 212 func TestUpdateEqualNoScatter(t *testing.T) { 213 t.Skip("planner does not produces this plan anymore") 214 vindex, _ := vindexes.NewLookupUnique("", map[string]string{ 215 "table": "lkp", 216 "from": "from", 217 "to": "toc", 218 "write_only": "true", 219 }) 220 upd := &Update{ 221 DML: &DML{ 222 RoutingParameters: &RoutingParameters{ 223 Opcode: Equal, 224 Keyspace: &vindexes.Keyspace{ 225 Name: "ks", 226 Sharded: true, 227 }, 228 Vindex: vindex, 229 Values: []evalengine.Expr{evalengine.NewLiteralInt(1)}, 230 }, 231 Query: "dummy_update", 232 }, 233 } 234 235 vc := newDMLTestVCursor("0") 236 _, err := upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 237 require.EqualError(t, err, `cannot map vindex to unique keyspace id: DestinationKeyRange(-)`) 238 } 239 240 func TestUpdateEqualChangedVindex(t *testing.T) { 241 ks := buildTestVSchema().Keyspaces["sharded"] 242 upd := &Update{ 243 DML: &DML{ 244 RoutingParameters: &RoutingParameters{ 245 Opcode: Equal, 246 Keyspace: ks.Keyspace, 247 Vindex: ks.Vindexes["hash"], 248 Values: []evalengine.Expr{evalengine.NewLiteralInt(1)}, 249 }, 250 Query: "dummy_update", 251 Table: []*vindexes.Table{ 252 ks.Tables["t1"], 253 }, 254 OwnedVindexQuery: "dummy_subquery", 255 KsidVindex: ks.Vindexes["hash"], 256 KsidLength: 1, 257 }, 258 ChangedVindexValues: map[string]*VindexValues{ 259 "twocol": { 260 PvMap: map[string]evalengine.Expr{ 261 "c1": evalengine.NewLiteralInt(1), 262 "c2": evalengine.NewLiteralInt(2), 263 }, 264 Offset: 4, 265 }, 266 "onecol": { 267 PvMap: map[string]evalengine.Expr{ 268 "c3": evalengine.NewLiteralInt(3), 269 }, 270 Offset: 5, 271 }, 272 }, 273 } 274 275 results := []*sqltypes.Result{sqltypes.MakeTestResult( 276 sqltypes.MakeTestFields( 277 "id|c1|c2|c3|twocol|onecol", 278 "int64|int64|int64|int64|int64|int64", 279 ), 280 "1|4|5|6|0|0", 281 )} 282 vc := newDMLTestVCursor("-20", "20-") 283 vc.results = results 284 285 _, err := upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 286 require.NoError(t, err) 287 vc.ExpectLog(t, []string{ 288 `ResolveDestinations sharded [type:INT64 value:"1"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6)`, 289 // ResolveDestinations is hard-coded to return -20. 290 // It gets used to perform the subquery to fetch the changing column values. 291 `ExecuteMultiShard sharded.-20: dummy_subquery {} false false`, 292 // Those values are returned as 4,5 for twocol and 6 for onecol. 293 // 4,5 have to be replaced by 1,2 (the new values). 294 `Execute delete from lkp2 where from1 = :from1 and from2 = :from2 and toc = :toc from1: type:INT64 value:"4" from2: type:INT64 value:"5" toc: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 295 `Execute insert into lkp2(from1, from2, toc) values(:from1_0, :from2_0, :toc_0) from1_0: type:INT64 value:"1" from2_0: type:INT64 value:"2" toc_0: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 296 // 6 has to be replaced by 3. 297 `Execute delete from lkp1 where from = :from and toc = :toc from: type:INT64 value:"6" toc: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 298 `Execute insert into lkp1(from, toc) values(:from_0, :toc_0) from_0: type:INT64 value:"3" toc_0: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 299 // Finally, the actual update, which is also sent to -20, same route as the subquery. 300 `ExecuteMultiShard sharded.-20: dummy_update {} true true`, 301 }) 302 303 // No rows changing 304 vc = newDMLTestVCursor("-20", "20-") 305 306 _, err = upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 307 require.NoError(t, err) 308 vc.ExpectLog(t, []string{ 309 `ResolveDestinations sharded [type:INT64 value:"1"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6)`, 310 // ResolveDestinations is hard-coded to return -20. 311 // It gets used to perform the subquery to fetch the changing column values. 312 `ExecuteMultiShard sharded.-20: dummy_subquery {} false false`, 313 // Subquery returns no rows. So, no vindexes are updated. We still pass-through the original update. 314 `ExecuteMultiShard sharded.-20: dummy_update {} true true`, 315 }) 316 317 // multiple rows changing. 318 results = []*sqltypes.Result{sqltypes.MakeTestResult( 319 sqltypes.MakeTestFields( 320 "id|c1|c2|c3|twocol|onecol", 321 "int64|int64|int64|int64|int64|int64", 322 ), 323 "1|4|5|6|0|0", 324 "1|7|8|9|0|0", 325 )} 326 vc = newDMLTestVCursor("-20", "20-") 327 vc.results = results 328 329 _, err = upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 330 require.NoError(t, err) 331 vc.ExpectLog(t, []string{ 332 `ResolveDestinations sharded [type:INT64 value:"1"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6)`, 333 // ResolveDestinations is hard-coded to return -20. 334 // It gets used to perform the subquery to fetch the changing column values. 335 `ExecuteMultiShard sharded.-20: dummy_subquery {} false false`, 336 // Those values are returned as 4,5 for twocol and 6 for onecol. 337 // 4,5 have to be replaced by 1,2 (the new values). 338 `Execute delete from lkp2 where from1 = :from1 and from2 = :from2 and toc = :toc from1: type:INT64 value:"4" from2: type:INT64 value:"5" toc: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 339 `Execute insert into lkp2(from1, from2, toc) values(:from1_0, :from2_0, :toc_0) from1_0: type:INT64 value:"1" from2_0: type:INT64 value:"2" toc_0: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 340 // 6 has to be replaced by 3. 341 `Execute delete from lkp1 where from = :from and toc = :toc from: type:INT64 value:"6" toc: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 342 `Execute insert into lkp1(from, toc) values(:from_0, :toc_0) from_0: type:INT64 value:"3" toc_0: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 343 // 7,8 have to be replaced by 1,2 (the new values). 344 `Execute delete from lkp2 where from1 = :from1 and from2 = :from2 and toc = :toc from1: type:INT64 value:"7" from2: type:INT64 value:"8" toc: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 345 `Execute insert into lkp2(from1, from2, toc) values(:from1_0, :from2_0, :toc_0) from1_0: type:INT64 value:"1" from2_0: type:INT64 value:"2" toc_0: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 346 // 9 has to be replaced by 3. 347 `Execute delete from lkp1 where from = :from and toc = :toc from: type:INT64 value:"9" toc: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 348 `Execute insert into lkp1(from, toc) values(:from_0, :toc_0) from_0: type:INT64 value:"3" toc_0: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 349 // Finally, the actual update, which is also sent to -20, same route as the subquery. 350 `ExecuteMultiShard sharded.-20: dummy_update {} true true`, 351 }) 352 353 // multiple rows changing, but only some vindex actually changes 354 results = []*sqltypes.Result{sqltypes.MakeTestResult( 355 sqltypes.MakeTestFields( 356 "id|c1|c2|c3|twocol|onecol", 357 "int64|int64|int64|int64|int64|int64", 358 ), 359 "1|4|5|6|0|1", // twocol changes 360 "1|7|8|9|1|0", // onecol changes 361 )} 362 vc = newDMLTestVCursor("-20", "20-") 363 vc.results = results 364 365 _, err = upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 366 require.NoError(t, err) 367 vc.ExpectLog(t, []string{ 368 `ResolveDestinations sharded [type:INT64 value:"1"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6)`, 369 // ResolveDestinations is hard-coded to return -20. 370 // It gets used to perform the subquery to fetch the changing column values. 371 `ExecuteMultiShard sharded.-20: dummy_subquery {} false false`, 372 // Those values are returned as 4,5 for twocol and 6 for onecol. 373 // 4,5 have to be replaced by 1,2 (the new values). 374 `Execute delete from lkp2 where from1 = :from1 and from2 = :from2 and toc = :toc from1: type:INT64 value:"4" from2: type:INT64 value:"5" toc: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 375 `Execute insert into lkp2(from1, from2, toc) values(:from1_0, :from2_0, :toc_0) from1_0: type:INT64 value:"1" from2_0: type:INT64 value:"2" toc_0: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 376 // 9 has to be replaced by 3. 377 `Execute delete from lkp1 where from = :from and toc = :toc from: type:INT64 value:"9" toc: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 378 `Execute insert into lkp1(from, toc) values(:from_0, :toc_0) from_0: type:INT64 value:"3" toc_0: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 379 // Finally, the actual update, which is also sent to -20, same route as the subquery. 380 `ExecuteMultiShard sharded.-20: dummy_update {} true true`, 381 }) 382 383 } 384 385 func TestUpdateEqualMultiColChangedVindex(t *testing.T) { 386 ks := buildTestVSchema().Keyspaces["sharded"] 387 upd := &Update{ 388 DML: &DML{ 389 RoutingParameters: &RoutingParameters{ 390 Opcode: Equal, 391 Keyspace: ks.Keyspace, 392 Vindex: ks.Vindexes["rg_vdx"], 393 Values: []evalengine.Expr{evalengine.NewLiteralInt(1), evalengine.NewLiteralInt(2)}, 394 }, 395 Query: "dummy_update", 396 Table: []*vindexes.Table{ 397 ks.Tables["rg_tbl"], 398 }, 399 OwnedVindexQuery: "dummy_subquery", 400 KsidVindex: ks.Vindexes["rg_vdx"], 401 KsidLength: 2, 402 }, 403 ChangedVindexValues: map[string]*VindexValues{ 404 "lkp_rg": { 405 PvMap: map[string]evalengine.Expr{ 406 "colc": evalengine.NewLiteralInt(5), 407 }, 408 Offset: 3, 409 }, 410 }, 411 } 412 413 results := []*sqltypes.Result{sqltypes.MakeTestResult( 414 sqltypes.MakeTestFields( 415 "cola|colb|colc|colc=5", 416 "int64|int64|int64|int64", 417 ), 418 "1|2|4|0", 419 )} 420 vc := newDMLTestVCursor("-20", "20-") 421 vc.results = results 422 423 _, err := upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 424 require.NoError(t, err) 425 vc.ExpectLog(t, []string{ 426 `ResolveDestinationsMultiCol sharded [[INT64(1) INT64(2)]] Destinations:DestinationKeyspaceID(0106e7ea22ce92708f)`, 427 // ResolveDestinations is hard-coded to return -20. 428 // It gets used to perform the subquery to fetch the changing column values. 429 `ExecuteMultiShard sharded.-20: dummy_subquery {} false false`, 430 // 4 has to be replaced by 5. 431 `Execute delete from lkp_rg_tbl where from = :from and toc = :toc from: type:INT64 value:"4" toc: type:VARBINARY value:"\x01\x06\xe7\xea\"Βp\x8f" true`, 432 `Execute insert into lkp_rg_tbl(from, toc) values(:from_0, :toc_0) from_0: type:INT64 value:"5" toc_0: type:VARBINARY value:"\x01\x06\xe7\xea\"Βp\x8f" true`, 433 // Finally, the actual update, which is also sent to -20, same route as the subquery. 434 `ExecuteMultiShard sharded.-20: dummy_update {} true true`, 435 }) 436 437 // No rows changing 438 vc.Rewind() 439 vc.results = nil 440 _, err = upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 441 require.NoError(t, err) 442 vc.ExpectLog(t, []string{ 443 `ResolveDestinationsMultiCol sharded [[INT64(1) INT64(2)]] Destinations:DestinationKeyspaceID(0106e7ea22ce92708f)`, 444 // ResolveDestinations is hard-coded to return -20. 445 // It gets used to perform the subquery to fetch the changing column values. 446 `ExecuteMultiShard sharded.-20: dummy_subquery {} false false`, 447 // Subquery returns no rows. So, no vindexes are updated. We still pass-through the original update. 448 `ExecuteMultiShard sharded.-20: dummy_update {} true true`, 449 }) 450 451 // multiple rows changing. 452 results = []*sqltypes.Result{sqltypes.MakeTestResult( 453 sqltypes.MakeTestFields( 454 "cola|colb|colc|colc=5", 455 "int64|int64|int64|int64", 456 ), 457 "1|2|4|0", 458 "1|2|6|0", 459 )} 460 vc.Rewind() 461 vc.results = results 462 463 _, err = upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 464 require.NoError(t, err) 465 vc.ExpectLog(t, []string{ 466 `ResolveDestinationsMultiCol sharded [[INT64(1) INT64(2)]] Destinations:DestinationKeyspaceID(0106e7ea22ce92708f)`, 467 // ResolveDestinations is hard-coded to return -20. 468 // It gets used to perform the subquery to fetch the changing column values. 469 `ExecuteMultiShard sharded.-20: dummy_subquery {} false false`, 470 // 4 has to be replaced by 5. 471 `Execute delete from lkp_rg_tbl where from = :from and toc = :toc from: type:INT64 value:"4" toc: type:VARBINARY value:"\x01\x06\xe7\xea\"Βp\x8f" true`, 472 `Execute insert into lkp_rg_tbl(from, toc) values(:from_0, :toc_0) from_0: type:INT64 value:"5" toc_0: type:VARBINARY value:"\x01\x06\xe7\xea\"Βp\x8f" true`, 473 // 6 has to be replaced by 5. 474 `Execute delete from lkp_rg_tbl where from = :from and toc = :toc from: type:INT64 value:"6" toc: type:VARBINARY value:"\x01\x06\xe7\xea\"Βp\x8f" true`, 475 `Execute insert into lkp_rg_tbl(from, toc) values(:from_0, :toc_0) from_0: type:INT64 value:"5" toc_0: type:VARBINARY value:"\x01\x06\xe7\xea\"Βp\x8f" true`, 476 // Finally, the actual update, which is also sent to -20, same route as the subquery. 477 `ExecuteMultiShard sharded.-20: dummy_update {} true true`, 478 }) 479 480 // multiple rows changing, but only some rows actually changes 481 results = []*sqltypes.Result{sqltypes.MakeTestResult( 482 sqltypes.MakeTestFields( 483 "cola|colb|colc|colc=5", 484 "int64|int64|int64|int64", 485 ), 486 "1|2|5|1", 487 "1|2|7|0", 488 )} 489 vc.Rewind() 490 vc.results = results 491 492 _, err = upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 493 require.NoError(t, err) 494 vc.ExpectLog(t, []string{ 495 `ResolveDestinationsMultiCol sharded [[INT64(1) INT64(2)]] Destinations:DestinationKeyspaceID(0106e7ea22ce92708f)`, 496 // ResolveDestinations is hard-coded to return -20. 497 // It gets used to perform the subquery to fetch the changing column values. 498 `ExecuteMultiShard sharded.-20: dummy_subquery {} false false`, 499 // 7 has to be replaced by 5. 500 `Execute delete from lkp_rg_tbl where from = :from and toc = :toc from: type:INT64 value:"7" toc: type:VARBINARY value:"\x01\x06\xe7\xea\"Βp\x8f" true`, 501 `Execute insert into lkp_rg_tbl(from, toc) values(:from_0, :toc_0) from_0: type:INT64 value:"5" toc_0: type:VARBINARY value:"\x01\x06\xe7\xea\"Βp\x8f" true`, 502 // Finally, the actual update, which is also sent to -20, same route as the subquery. 503 `ExecuteMultiShard sharded.-20: dummy_update {} true true`, 504 }) 505 } 506 507 func TestUpdateScatterChangedVindex(t *testing.T) { 508 // update t1 set c1 = 1, c2 = 2, c3 = 3 509 ks := buildTestVSchema().Keyspaces["sharded"] 510 upd := &Update{ 511 DML: &DML{ 512 RoutingParameters: &RoutingParameters{ 513 Opcode: Scatter, 514 Keyspace: ks.Keyspace, 515 }, 516 Query: "dummy_update", 517 Table: []*vindexes.Table{ 518 ks.Tables["t1"], 519 }, 520 OwnedVindexQuery: "dummy_subquery", 521 KsidVindex: ks.Vindexes["hash"], 522 KsidLength: 1, 523 }, 524 ChangedVindexValues: map[string]*VindexValues{ 525 "twocol": { 526 PvMap: map[string]evalengine.Expr{ 527 "c1": evalengine.NewLiteralInt(1), 528 "c2": evalengine.NewLiteralInt(2), 529 }, 530 Offset: 4, 531 }, 532 "onecol": { 533 PvMap: map[string]evalengine.Expr{ 534 "c3": evalengine.NewLiteralInt(3), 535 }, 536 Offset: 5, 537 }, 538 }, 539 } 540 541 results := []*sqltypes.Result{sqltypes.MakeTestResult( 542 sqltypes.MakeTestFields( 543 "id|c1|c2|c3|twocol|onecol", 544 "int64|int64|int64|int64|int64|int64", 545 ), 546 "1|4|5|6|0|0", 547 )} 548 vc := newDMLTestVCursor("-20", "20-") 549 vc.results = results 550 551 _, err := upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 552 require.NoError(t, err) 553 vc.ExpectLog(t, []string{ 554 `ResolveDestinations sharded [] Destinations:DestinationAllShards()`, 555 `ExecuteMultiShard sharded.-20: dummy_subquery {} sharded.20-: dummy_subquery {} false false`, 556 // Those values are returned as 4,5 for twocol and 6 for onecol. 557 // 4,5 have to be replaced by 1,2 (the new values). 558 `Execute delete from lkp2 where from1 = :from1 and from2 = :from2 and toc = :toc from1: type:INT64 value:"4" from2: type:INT64 value:"5" toc: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 559 `Execute insert into lkp2(from1, from2, toc) values(:from1_0, :from2_0, :toc_0) from1_0: type:INT64 value:"1" from2_0: type:INT64 value:"2" toc_0: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 560 // 6 has to be replaced by 3. 561 `Execute delete from lkp1 where from = :from and toc = :toc from: type:INT64 value:"6" toc: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 562 `Execute insert into lkp1(from, toc) values(:from_0, :toc_0) from_0: type:INT64 value:"3" toc_0: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 563 // Finally, the actual update, which is also sent to -20, same route as the subquery. 564 `ExecuteMultiShard sharded.-20: dummy_update {} sharded.20-: dummy_update {} true false`, 565 }) 566 567 // No rows changing 568 vc = newDMLTestVCursor("-20", "20-") 569 570 _, err = upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 571 if err != nil { 572 t.Fatal(err) 573 } 574 vc.ExpectLog(t, []string{ 575 `ResolveDestinations sharded [] Destinations:DestinationAllShards()`, 576 // ResolveDestinations is hard-coded to return -20. 577 // It gets used to perform the subquery to fetch the changing column values. 578 `ExecuteMultiShard sharded.-20: dummy_subquery {} sharded.20-: dummy_subquery {} false false`, 579 // Subquery returns no rows. So, no vindexes are deleted. We still pass-through the original delete. 580 `ExecuteMultiShard sharded.-20: dummy_update {} sharded.20-: dummy_update {} true false`, 581 }) 582 583 // Update can affect multiple rows 584 results = []*sqltypes.Result{sqltypes.MakeTestResult( 585 sqltypes.MakeTestFields( 586 "id|c1|c2|c3|twocol|onecol", 587 "int64|int64|int64|int64|int64|int64", 588 ), 589 "1|4|5|6|0|0", 590 "1|7|8|9|0|0", 591 )} 592 vc = newDMLTestVCursor("-20", "20-") 593 vc.results = results 594 595 _, err = upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 596 require.NoError(t, err) 597 vc.ExpectLog(t, []string{ 598 `ResolveDestinations sharded [] Destinations:DestinationAllShards()`, 599 // ResolveDestinations is hard-coded to return -20. 600 // It gets used to perform the subquery to fetch the changing column values. 601 `ExecuteMultiShard sharded.-20: dummy_subquery {} sharded.20-: dummy_subquery {} false false`, 602 // Those values are returned as 4,5 for twocol and 6 for onecol. 603 // 4,5 have to be replaced by 1,2 (the new values). 604 `Execute delete from lkp2 where from1 = :from1 and from2 = :from2 and toc = :toc from1: type:INT64 value:"4" from2: type:INT64 value:"5" toc: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 605 `Execute insert into lkp2(from1, from2, toc) values(:from1_0, :from2_0, :toc_0) from1_0: type:INT64 value:"1" from2_0: type:INT64 value:"2" toc_0: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 606 // 6 has to be replaced by 3. 607 `Execute delete from lkp1 where from = :from and toc = :toc from: type:INT64 value:"6" toc: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 608 `Execute insert into lkp1(from, toc) values(:from_0, :toc_0) from_0: type:INT64 value:"3" toc_0: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 609 // Those values are returned as 7,8 for twocol and 9 for onecol. 610 // 7,8 have to be replaced by 1,2 (the new values). 611 `Execute delete from lkp2 where from1 = :from1 and from2 = :from2 and toc = :toc from1: type:INT64 value:"7" from2: type:INT64 value:"8" toc: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 612 `Execute insert into lkp2(from1, from2, toc) values(:from1_0, :from2_0, :toc_0) from1_0: type:INT64 value:"1" from2_0: type:INT64 value:"2" toc_0: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 613 // 9 has to be replaced by 3. 614 `Execute delete from lkp1 where from = :from and toc = :toc from: type:INT64 value:"9" toc: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 615 `Execute insert into lkp1(from, toc) values(:from_0, :toc_0) from_0: type:INT64 value:"3" toc_0: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 616 // Finally, the actual update, which is also sent to -20, same route as the subquery. 617 `ExecuteMultiShard sharded.-20: dummy_update {} sharded.20-: dummy_update {} true false`, 618 }) 619 620 } 621 622 func TestUpdateIn(t *testing.T) { 623 ks := buildTestVSchema().Keyspaces["sharded"] 624 upd := &Update{ 625 DML: &DML{ 626 RoutingParameters: &RoutingParameters{ 627 Opcode: IN, 628 Keyspace: ks.Keyspace, 629 Vindex: ks.Vindexes["hash"], 630 Values: []evalengine.Expr{evalengine.TupleExpr{ 631 evalengine.NewLiteralInt(1), 632 evalengine.NewLiteralInt(2), 633 }}}, 634 Query: "dummy_update", 635 }, 636 } 637 638 vc := newDMLTestVCursor("-20", "20-") 639 _, err := upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 640 require.NoError(t, err) 641 vc.ExpectLog(t, []string{ 642 `ResolveDestinations sharded [type:INT64 value:"1" type:INT64 value:"2"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6),DestinationKeyspaceID(06e7ea22ce92708f)`, 643 // ResolveDestinations is hard-coded to return -20. 644 `ExecuteMultiShard sharded.-20: dummy_update {} true true`, 645 }) 646 } 647 648 func TestUpdateInStreamExecute(t *testing.T) { 649 ks := buildTestVSchema().Keyspaces["sharded"] 650 upd := &Update{DML: &DML{ 651 RoutingParameters: &RoutingParameters{ 652 Opcode: IN, 653 Keyspace: ks.Keyspace, 654 Vindex: ks.Vindexes["hash"], 655 Values: []evalengine.Expr{evalengine.TupleExpr{ 656 evalengine.NewLiteralInt(1), 657 evalengine.NewLiteralInt(2), 658 }}}, 659 Query: "dummy_update", 660 }} 661 662 vc := newDMLTestVCursor("-20", "20-") 663 err := upd.TryStreamExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false, func(result *sqltypes.Result) error { 664 return nil 665 }) 666 require.NoError(t, err) 667 vc.ExpectLog(t, []string{ 668 `ResolveDestinations sharded [type:INT64 value:"1" type:INT64 value:"2"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6),DestinationKeyspaceID(06e7ea22ce92708f)`, 669 // ResolveDestinations is hard-coded to return -20. 670 `ExecuteMultiShard sharded.-20: dummy_update {} true true`, 671 }) 672 } 673 674 func TestUpdateInMultiCol(t *testing.T) { 675 ks := buildTestVSchema().Keyspaces["sharded"] 676 upd := &Update{DML: &DML{ 677 RoutingParameters: &RoutingParameters{ 678 Opcode: IN, 679 Keyspace: ks.Keyspace, 680 Vindex: ks.Vindexes["rg_vdx"], 681 Values: []evalengine.Expr{ 682 evalengine.TupleExpr{evalengine.NewLiteralInt(1), evalengine.NewLiteralInt(2)}, 683 evalengine.TupleExpr{evalengine.NewLiteralInt(3), evalengine.NewLiteralInt(4)}, 684 }, 685 }, 686 Query: "dummy_update", 687 }} 688 689 vc := newDMLTestVCursor("-20", "20-") 690 _, err := upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 691 require.NoError(t, err) 692 vc.ExpectLog(t, []string{ 693 `ResolveDestinationsMultiCol sharded [[INT64(1) INT64(3)] [INT64(1) INT64(4)] [INT64(2) INT64(3)] [INT64(2) INT64(4)]] Destinations:DestinationKeyspaceID(014eb190c9a2fa169c),DestinationKeyspaceID(01d2fd8867d50d2dfe),DestinationKeyspaceID(024eb190c9a2fa169c),DestinationKeyspaceID(02d2fd8867d50d2dfe)`, 694 // ResolveDestinations is hard-coded to return -20. 695 `ExecuteMultiShard sharded.-20: dummy_update {} true true`, 696 }) 697 } 698 699 func TestUpdateInChangedVindex(t *testing.T) { 700 ks := buildTestVSchema().Keyspaces["sharded"] 701 upd := &Update{ 702 DML: &DML{ 703 RoutingParameters: &RoutingParameters{ 704 Opcode: IN, 705 Keyspace: ks.Keyspace, 706 Vindex: ks.Vindexes["hash"], 707 Values: []evalengine.Expr{evalengine.TupleExpr{ 708 evalengine.NewLiteralInt(1), 709 evalengine.NewLiteralInt(2), 710 }}, 711 }, 712 Query: "dummy_update", 713 Table: []*vindexes.Table{ 714 ks.Tables["t1"], 715 }, 716 OwnedVindexQuery: "dummy_subquery", 717 KsidVindex: ks.Vindexes["hash"], 718 KsidLength: 1, 719 }, 720 ChangedVindexValues: map[string]*VindexValues{ 721 "twocol": { 722 PvMap: map[string]evalengine.Expr{ 723 "c1": evalengine.NewLiteralInt(1), 724 "c2": evalengine.NewLiteralInt(2), 725 }, 726 Offset: 4, 727 }, 728 "onecol": { 729 PvMap: map[string]evalengine.Expr{ 730 "c3": evalengine.NewLiteralInt(3), 731 }, 732 Offset: 5, 733 }, 734 }, 735 } 736 737 results := []*sqltypes.Result{sqltypes.MakeTestResult( 738 sqltypes.MakeTestFields( 739 "id|c1|c2|c3|twocol|onecol", 740 "int64|int64|int64|int64|int64|int64", 741 ), 742 "1|4|5|6|0|0", 743 "2|21|22|23|0|0", 744 )} 745 vc := newDMLTestVCursor("-20", "20-") 746 vc.results = results 747 748 _, err := upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 749 require.NoError(t, err) 750 vc.ExpectLog(t, []string{ 751 `ResolveDestinations sharded [type:INT64 value:"1" type:INT64 value:"2"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6),DestinationKeyspaceID(06e7ea22ce92708f)`, 752 // ResolveDestinations is hard-coded to return -20. 753 // It gets used to perform the subquery to fetch the changing column values. 754 `ExecuteMultiShard sharded.-20: dummy_subquery {} false false`, 755 // Those values are returned as 4,5 for twocol and 6 for onecol. 756 // 4,5 have to be replaced by 1,2 (the new values). 757 `Execute delete from lkp2 where from1 = :from1 and from2 = :from2 and toc = :toc from1: type:INT64 value:"4" from2: type:INT64 value:"5" toc: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 758 `Execute insert into lkp2(from1, from2, toc) values(:from1_0, :from2_0, :toc_0) from1_0: type:INT64 value:"1" from2_0: type:INT64 value:"2" toc_0: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 759 // 6 has to be replaced by 3. 760 `Execute delete from lkp1 where from = :from and toc = :toc from: type:INT64 value:"6" toc: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 761 `Execute insert into lkp1(from, toc) values(:from_0, :toc_0) from_0: type:INT64 value:"3" toc_0: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 762 // 21,22 have to be replaced by 1,2 (the new values). 763 `Execute delete from lkp2 where from1 = :from1 and from2 = :from2 and toc = :toc from1: type:INT64 value:"21" from2: type:INT64 value:"22" toc: type:VARBINARY value:"\x06\xe7\xea\"Βp\x8f" true`, 764 `Execute insert into lkp2(from1, from2, toc) values(:from1_0, :from2_0, :toc_0) from1_0: type:INT64 value:"1" from2_0: type:INT64 value:"2" toc_0: type:VARBINARY value:"\x06\xe7\xea\"Βp\x8f" true`, 765 // 23 has to be replaced by 3. 766 `Execute delete from lkp1 where from = :from and toc = :toc from: type:INT64 value:"23" toc: type:VARBINARY value:"\x06\xe7\xea\"Βp\x8f" true`, 767 `Execute insert into lkp1(from, toc) values(:from_0, :toc_0) from_0: type:INT64 value:"3" toc_0: type:VARBINARY value:"\x06\xe7\xea\"Βp\x8f" true`, 768 // Finally, the actual update, which is also sent to -20, same route as the subquery. 769 `ExecuteMultiShard sharded.-20: dummy_update {} true true`, 770 }) 771 772 // No rows changing 773 vc = newDMLTestVCursor("-20", "20-") 774 775 _, err = upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 776 require.NoError(t, err) 777 vc.ExpectLog(t, []string{ 778 `ResolveDestinations sharded [type:INT64 value:"1" type:INT64 value:"2"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6),DestinationKeyspaceID(06e7ea22ce92708f)`, 779 // ResolveDestinations is hard-coded to return -20. 780 // It gets used to perform the subquery to fetch the changing column values. 781 `ExecuteMultiShard sharded.-20: dummy_subquery {} false false`, 782 // Subquery returns no rows. So, no vindexes are updated. We still pass-through the original update. 783 `ExecuteMultiShard sharded.-20: dummy_update {} true true`, 784 }) 785 786 // multiple rows changing. 787 results = []*sqltypes.Result{sqltypes.MakeTestResult( 788 sqltypes.MakeTestFields( 789 "id|c1|c2|c3|twocol|onecol", 790 "int64|int64|int64|int64|int64|int64", 791 ), 792 "1|4|5|6|0|0", 793 "1|7|8|9|0|0", 794 "2|21|22|23|0|0", 795 )} 796 vc = newDMLTestVCursor("-20", "20-") 797 vc.results = results 798 799 _, err = upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 800 require.NoError(t, err) 801 vc.ExpectLog(t, []string{ 802 `ResolveDestinations sharded [type:INT64 value:"1" type:INT64 value:"2"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6),DestinationKeyspaceID(06e7ea22ce92708f)`, 803 // ResolveDestinations is hard-coded to return -20. 804 // It gets used to perform the subquery to fetch the changing column values. 805 `ExecuteMultiShard sharded.-20: dummy_subquery {} false false`, 806 // Those values are returned as 4,5 for twocol and 6 for onecol. 807 // 4,5 have to be replaced by 1,2 (the new values). 808 `Execute delete from lkp2 where from1 = :from1 and from2 = :from2 and toc = :toc from1: type:INT64 value:"4" from2: type:INT64 value:"5" toc: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 809 `Execute insert into lkp2(from1, from2, toc) values(:from1_0, :from2_0, :toc_0) from1_0: type:INT64 value:"1" from2_0: type:INT64 value:"2" toc_0: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 810 // 6 has to be replaced by 3. 811 `Execute delete from lkp1 where from = :from and toc = :toc from: type:INT64 value:"6" toc: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 812 `Execute insert into lkp1(from, toc) values(:from_0, :toc_0) from_0: type:INT64 value:"3" toc_0: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 813 // 7,8 have to be replaced by 1,2 (the new values). 814 `Execute delete from lkp2 where from1 = :from1 and from2 = :from2 and toc = :toc from1: type:INT64 value:"7" from2: type:INT64 value:"8" toc: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 815 `Execute insert into lkp2(from1, from2, toc) values(:from1_0, :from2_0, :toc_0) from1_0: type:INT64 value:"1" from2_0: type:INT64 value:"2" toc_0: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 816 // 9 has to be replaced by 3. 817 `Execute delete from lkp1 where from = :from and toc = :toc from: type:INT64 value:"9" toc: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 818 `Execute insert into lkp1(from, toc) values(:from_0, :toc_0) from_0: type:INT64 value:"3" toc_0: type:VARBINARY value:"\x16k@\xb4J\xbaK\xd6" true`, 819 // 21,22 have to be replaced by 1,2 (the new values). 820 `Execute delete from lkp2 where from1 = :from1 and from2 = :from2 and toc = :toc from1: type:INT64 value:"21" from2: type:INT64 value:"22" toc: type:VARBINARY value:"\x06\xe7\xea\"Βp\x8f" true`, 821 `Execute insert into lkp2(from1, from2, toc) values(:from1_0, :from2_0, :toc_0) from1_0: type:INT64 value:"1" from2_0: type:INT64 value:"2" toc_0: type:VARBINARY value:"\x06\xe7\xea\"Βp\x8f" true`, 822 // 23 has to be replaced by 3. 823 `Execute delete from lkp1 where from = :from and toc = :toc from: type:INT64 value:"23" toc: type:VARBINARY value:"\x06\xe7\xea\"Βp\x8f" true`, 824 `Execute insert into lkp1(from, toc) values(:from_0, :toc_0) from_0: type:INT64 value:"3" toc_0: type:VARBINARY value:"\x06\xe7\xea\"Βp\x8f" true`, 825 // Finally, the actual update, which is also sent to -20, same route as the subquery. 826 `ExecuteMultiShard sharded.-20: dummy_update {} true true`, 827 }) 828 } 829 830 func TestUpdateInChangedVindexMultiCol(t *testing.T) { 831 ks := buildTestVSchema().Keyspaces["sharded"] 832 upd := &Update{ 833 DML: &DML{ 834 RoutingParameters: &RoutingParameters{ 835 Opcode: IN, 836 Keyspace: ks.Keyspace, 837 Vindex: ks.Vindexes["rg_vdx"], 838 Values: []evalengine.Expr{ 839 evalengine.TupleExpr{evalengine.NewLiteralInt(1), evalengine.NewLiteralInt(2)}, 840 evalengine.NewLiteralInt(3), 841 }, 842 }, 843 Query: "dummy_update", 844 Table: []*vindexes.Table{ 845 ks.Tables["rg_tbl"], 846 }, 847 OwnedVindexQuery: "dummy_subquery", 848 KsidVindex: ks.Vindexes["rg_vdx"], 849 KsidLength: 2, 850 }, 851 ChangedVindexValues: map[string]*VindexValues{ 852 "lkp_rg": { 853 PvMap: map[string]evalengine.Expr{ 854 "colc": evalengine.NewLiteralInt(5), 855 }, 856 Offset: 3, 857 }, 858 }, 859 } 860 861 results := []*sqltypes.Result{sqltypes.MakeTestResult( 862 sqltypes.MakeTestFields( 863 "cola|colb|colc|colc=5", 864 "int64|int64|int64|int64", 865 ), 866 "1|3|4|0", 867 "2|3|5|1", 868 "1|3|6|0", 869 "2|3|7|0", 870 )} 871 vc := newDMLTestVCursor("-20", "20-") 872 vc.results = results 873 874 _, err := upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 875 require.NoError(t, err) 876 vc.ExpectLog(t, []string{ 877 `ResolveDestinationsMultiCol sharded [[INT64(1) INT64(3)] [INT64(2) INT64(3)]] Destinations:DestinationKeyspaceID(014eb190c9a2fa169c),DestinationKeyspaceID(024eb190c9a2fa169c)`, 878 // ResolveDestinations is hard-coded to return -20. 879 // It gets used to perform the subquery to fetch the changing column values. 880 `ExecuteMultiShard sharded.-20: dummy_subquery {} false false`, 881 // Those values are returned as 4,5,6 and 7 for colc, but 5 is unchanged so only 3 rows will be updated. 882 `Execute delete from lkp_rg_tbl where from = :from and toc = :toc from: type:INT64 value:"4" toc: type:VARBINARY value:"\x01N\xb1\x90ɢ\xfa\x16\x9c" true`, 883 `Execute insert into lkp_rg_tbl(from, toc) values(:from_0, :toc_0) from_0: type:INT64 value:"5" toc_0: type:VARBINARY value:"\x01N\xb1\x90ɢ\xfa\x16\x9c" true`, 884 `Execute delete from lkp_rg_tbl where from = :from and toc = :toc from: type:INT64 value:"6" toc: type:VARBINARY value:"\x01N\xb1\x90ɢ\xfa\x16\x9c" true`, 885 `Execute insert into lkp_rg_tbl(from, toc) values(:from_0, :toc_0) from_0: type:INT64 value:"5" toc_0: type:VARBINARY value:"\x01N\xb1\x90ɢ\xfa\x16\x9c" true`, 886 `Execute delete from lkp_rg_tbl where from = :from and toc = :toc from: type:INT64 value:"7" toc: type:VARBINARY value:"\x02N\xb1\x90ɢ\xfa\x16\x9c" true`, 887 `Execute insert into lkp_rg_tbl(from, toc) values(:from_0, :toc_0) from_0: type:INT64 value:"5" toc_0: type:VARBINARY value:"\x02N\xb1\x90ɢ\xfa\x16\x9c" true`, 888 // Finally, the actual update, which is also sent to -20, same route as the subquery. 889 `ExecuteMultiShard sharded.-20: dummy_update {} true true`, 890 }) 891 } 892 893 func TestUpdateEqualSubshard(t *testing.T) { 894 vindex, _ := vindexes.NewRegionExperimental("", map[string]string{"region_bytes": "1"}) 895 upd := &Update{ 896 DML: &DML{ 897 RoutingParameters: &RoutingParameters{ 898 Opcode: SubShard, 899 Keyspace: &vindexes.Keyspace{ 900 Name: "ks", 901 Sharded: true, 902 }, 903 Vindex: vindex, 904 Values: []evalengine.Expr{evalengine.NewLiteralInt(1)}, 905 }, 906 Query: "dummy_update", 907 }, 908 } 909 910 vc := newDMLTestVCursor("-20", "20-") 911 vc.shardForKsid = []string{"-20", "20-"} 912 _, err := upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 913 require.NoError(t, err) 914 vc.ExpectLog(t, []string{ 915 `ResolveDestinationsMultiCol ks [[INT64(1)]] Destinations:DestinationKeyRange(01-02)`, 916 `ExecuteMultiShard ks.-20: dummy_update {} ks.20-: dummy_update {} true false`, 917 }) 918 919 vc.Rewind() 920 // as it is single shard so autocommit should be allowed. 921 vc.shardForKsid = []string{"-20"} 922 _, err = upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 923 require.NoError(t, err) 924 vc.ExpectLog(t, []string{ 925 `ResolveDestinationsMultiCol ks [[INT64(1)]] Destinations:DestinationKeyRange(01-02)`, 926 `ExecuteMultiShard ks.-20: dummy_update {} true true`, 927 }) 928 } 929 930 func TestUpdateMultiEqual(t *testing.T) { 931 ks := buildTestVSchema().Keyspaces["sharded"] 932 upd := &Update{ 933 DML: &DML{ 934 RoutingParameters: &RoutingParameters{ 935 Opcode: MultiEqual, 936 Keyspace: ks.Keyspace, 937 Vindex: ks.Vindexes["hash"], 938 Values: []evalengine.Expr{evalengine.NewTupleExpr( 939 evalengine.NewLiteralInt(1), 940 evalengine.NewLiteralInt(5), 941 )}, 942 }, 943 Query: "dummy_update", 944 }, 945 } 946 947 vc := newDMLTestVCursor("-20", "20-") 948 vc.shardForKsid = []string{"-20", "20-"} 949 _, err := upd.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 950 require.NoError(t, err) 951 vc.ExpectLog(t, []string{ 952 `ResolveDestinations sharded [type:INT64 value:"1" type:INT64 value:"5"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6),DestinationKeyspaceID(70bb023c810ca87a)`, 953 `ExecuteMultiShard sharded.-20: dummy_update {} sharded.20-: dummy_update {} true false`, 954 }) 955 } 956 957 func buildTestVSchema() *vindexes.VSchema { 958 invschema := &vschemapb.SrvVSchema{ 959 Keyspaces: map[string]*vschemapb.Keyspace{ 960 "sharded": { 961 Sharded: true, 962 Vindexes: map[string]*vschemapb.Vindex{ 963 "hash": { 964 Type: "hash", 965 }, 966 "rg_vdx": { 967 Type: "region_experimental", 968 Params: map[string]string{ 969 "region_bytes": "1", 970 }, 971 }, 972 "twocol": { 973 Type: "lookup", 974 Params: map[string]string{ 975 "table": "lkp2", 976 "from": "from1,from2", 977 "to": "toc", 978 }, 979 Owner: "t1", 980 }, 981 "onecol": { 982 Type: "lookup", 983 Params: map[string]string{ 984 "table": "lkp1", 985 "from": "from", 986 "to": "toc", 987 }, 988 Owner: "t1", 989 }, 990 "lkp_rg": { 991 Type: "lookup", 992 Params: map[string]string{ 993 "table": "lkp_rg_tbl", 994 "from": "from", 995 "to": "toc", 996 }, 997 Owner: "rg_tbl", 998 }, 999 }, 1000 Tables: map[string]*vschemapb.Table{ 1001 "t1": { 1002 ColumnVindexes: []*vschemapb.ColumnVindex{{ 1003 Name: "hash", 1004 Columns: []string{"id"}, 1005 }, { 1006 Name: "twocol", 1007 Columns: []string{"c1", "c2"}, 1008 }, { 1009 Name: "onecol", 1010 Columns: []string{"c3"}, 1011 }}, 1012 }, 1013 "t2": { 1014 ColumnVindexes: []*vschemapb.ColumnVindex{{ 1015 Name: "hash", 1016 Columns: []string{"id"}, 1017 }}, 1018 }, 1019 "rg_tbl": { 1020 ColumnVindexes: []*vschemapb.ColumnVindex{{ 1021 Name: "rg_vdx", 1022 Columns: []string{"cola", "colb"}, 1023 }, { 1024 Name: "lkp_rg", 1025 Columns: []string{"colc"}, 1026 }}, 1027 }, 1028 }, 1029 }, 1030 }, 1031 } 1032 vs := vindexes.BuildVSchema(invschema) 1033 return vs 1034 } 1035 1036 func newDMLTestVCursor(shards ...string) *loggingVCursor { 1037 return &loggingVCursor{shards: shards, resolvedTargetTabletType: topodatapb.TabletType_PRIMARY} 1038 }