vitess.io/vitess@v0.16.2/go/vt/vtgate/engine/route_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 "fmt" 23 "strconv" 24 "testing" 25 26 "github.com/stretchr/testify/assert" 27 28 "vitess.io/vitess/go/mysql/collations" 29 vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" 30 "vitess.io/vitess/go/vt/servenv" 31 "vitess.io/vitess/go/vt/vterrors" 32 33 "vitess.io/vitess/go/vt/sqlparser" 34 35 "vitess.io/vitess/go/vt/vtgate/evalengine" 36 37 "github.com/stretchr/testify/require" 38 39 "vitess.io/vitess/go/mysql" 40 "vitess.io/vitess/go/sqltypes" 41 querypb "vitess.io/vitess/go/vt/proto/query" 42 "vitess.io/vitess/go/vt/vtgate/vindexes" 43 ) 44 45 var defaultSelectResult = sqltypes.MakeTestResult( 46 sqltypes.MakeTestFields( 47 "id", 48 "int64", 49 ), 50 "1", 51 ) 52 53 func init() { 54 // We require MySQL 8.0 collations for the comparisons in the tests 55 mySQLVersion := "8.0.0" 56 servenv.SetMySQLServerVersionForTest(mySQLVersion) 57 collationEnv = collations.NewEnvironment(mySQLVersion) 58 } 59 60 func TestSelectUnsharded(t *testing.T) { 61 sel := NewRoute( 62 Unsharded, 63 &vindexes.Keyspace{ 64 Name: "ks", 65 Sharded: false, 66 }, 67 "dummy_select", 68 "dummy_select_field", 69 ) 70 71 vc := &loggingVCursor{ 72 shards: []string{"0"}, 73 results: []*sqltypes.Result{defaultSelectResult}, 74 } 75 result, err := sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 76 require.NoError(t, err) 77 vc.ExpectLog(t, []string{ 78 `ResolveDestinations ks [] Destinations:DestinationAllShards()`, 79 `ExecuteMultiShard ks.0: dummy_select {} false false`, 80 }) 81 expectResult(t, "sel.Execute", result, defaultSelectResult) 82 83 vc.Rewind() 84 result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) 85 require.NoError(t, err) 86 vc.ExpectLog(t, []string{ 87 `ResolveDestinations ks [] Destinations:DestinationAllShards()`, 88 `StreamExecuteMulti dummy_select ks.0: {} `, 89 }) 90 expectResult(t, "sel.StreamExecute", result, defaultSelectResult) 91 } 92 93 func TestInformationSchemaWithTableAndSchemaWithRoutedTables(t *testing.T) { 94 stringListToExprList := func(in []string) []evalengine.Expr { 95 var schema []evalengine.Expr 96 for _, s := range in { 97 schema = append(schema, evalengine.NewLiteralString([]byte(s), collations.TypedCollation{})) 98 } 99 return schema 100 } 101 102 type testCase struct { 103 tableSchema []string 104 tableName map[string]evalengine.Expr 105 testName string 106 expectedLog []string 107 routed bool 108 } 109 tests := []testCase{{ 110 testName: "both schema and table predicates - routed table", 111 tableSchema: []string{"schema"}, 112 tableName: map[string]evalengine.Expr{"table_name": evalengine.NewLiteralString([]byte("table"), collations.TypedCollation{})}, 113 routed: true, 114 expectedLog: []string{ 115 "FindTable(`schema`.`table`)", 116 "ResolveDestinations routedKeyspace [] Destinations:DestinationAnyShard()", 117 "ExecuteMultiShard routedKeyspace.1: dummy_select {__replacevtschemaname: type:INT64 value:\"1\" table_name: type:VARCHAR value:\"routedTable\"} false false"}, 118 }, { 119 testName: "both schema and table predicates - not routed", 120 tableSchema: []string{"schema"}, 121 tableName: map[string]evalengine.Expr{"table_name": evalengine.NewLiteralString([]byte("table"), collations.TypedCollation{})}, 122 routed: false, 123 expectedLog: []string{ 124 "FindTable(`schema`.`table`)", 125 "ResolveDestinations schema [] Destinations:DestinationAnyShard()", 126 "ExecuteMultiShard schema.1: dummy_select {__replacevtschemaname: type:INT64 value:\"1\" table_name: type:VARCHAR value:\"table\"} false false"}, 127 }, { 128 testName: "multiple schema and table predicates", 129 tableSchema: []string{"schema", "schema", "schema"}, 130 tableName: map[string]evalengine.Expr{"t1": evalengine.NewLiteralString([]byte("table"), collations.TypedCollation{}), "t2": evalengine.NewLiteralString([]byte("table"), collations.TypedCollation{}), "t3": evalengine.NewLiteralString([]byte("table"), collations.TypedCollation{})}, 131 routed: false, 132 expectedLog: []string{ 133 "FindTable(`schema`.`table`)", 134 "FindTable(`schema`.`table`)", 135 "FindTable(`schema`.`table`)", 136 "ResolveDestinations schema [] Destinations:DestinationAnyShard()", 137 "ExecuteMultiShard schema.1: dummy_select {__replacevtschemaname: type:INT64 value:\"1\" t1: type:VARCHAR value:\"table\" t2: type:VARCHAR value:\"table\" t3: type:VARCHAR value:\"table\"} false false"}, 138 }, { 139 testName: "table name predicate - routed table", 140 tableName: map[string]evalengine.Expr{"table_name": evalengine.NewLiteralString([]byte("tableName"), collations.TypedCollation{})}, 141 routed: true, 142 expectedLog: []string{ 143 "FindTable(tableName)", 144 "ResolveDestinations routedKeyspace [] Destinations:DestinationAnyShard()", 145 "ExecuteMultiShard routedKeyspace.1: dummy_select {table_name: type:VARCHAR value:\"routedTable\"} false false"}, 146 }, { 147 testName: "table name predicate - not routed", 148 tableName: map[string]evalengine.Expr{"table_name": evalengine.NewLiteralString([]byte("tableName"), collations.TypedCollation{})}, 149 routed: false, 150 expectedLog: []string{ 151 "FindTable(tableName)", 152 "ResolveDestinations ks [] Destinations:DestinationAnyShard()", 153 "ExecuteMultiShard ks.1: dummy_select {table_name: type:VARCHAR value:\"tableName\"} false false"}, 154 }, { 155 testName: "schema predicate", 156 tableSchema: []string{"myKeyspace"}, 157 expectedLog: []string{ 158 "ResolveDestinations myKeyspace [] Destinations:DestinationAnyShard()", 159 "ExecuteMultiShard myKeyspace.1: dummy_select {__replacevtschemaname: type:INT64 value:\"1\"} false false"}, 160 }, { 161 testName: "multiple schema predicates", 162 tableSchema: []string{"myKeyspace", "myKeyspace", "myKeyspace", "myKeyspace"}, 163 expectedLog: []string{ 164 "ResolveDestinations myKeyspace [] Destinations:DestinationAnyShard()", 165 "ExecuteMultiShard myKeyspace.1: dummy_select {__replacevtschemaname: type:INT64 value:\"1\"} false false"}, 166 }, { 167 testName: "no predicates", 168 expectedLog: []string{ 169 "ResolveDestinations ks [] Destinations:DestinationAnyShard()", 170 "ExecuteMultiShard ks.1: dummy_select {} false false"}, 171 }} 172 for _, tc := range tests { 173 t.Run(tc.testName, func(t *testing.T) { 174 sel := &Route{ 175 RoutingParameters: &RoutingParameters{ 176 Opcode: DBA, 177 Keyspace: &vindexes.Keyspace{ 178 Name: "ks", 179 Sharded: false, 180 }, 181 SysTableTableSchema: stringListToExprList(tc.tableSchema), 182 SysTableTableName: tc.tableName, 183 }, 184 Query: "dummy_select", 185 FieldQuery: "dummy_select_field", 186 } 187 vc := &loggingVCursor{ 188 shards: []string{"1"}, 189 results: []*sqltypes.Result{defaultSelectResult}, 190 } 191 if tc.routed { 192 vc.tableRoutes = tableRoutes{ 193 tbl: &vindexes.Table{ 194 Name: sqlparser.NewIdentifierCS("routedTable"), 195 Keyspace: &vindexes.Keyspace{Name: "routedKeyspace"}, 196 }} 197 } 198 _, err := sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 199 require.NoError(t, err) 200 vc.ExpectLog(t, tc.expectedLog) 201 }) 202 } 203 } 204 205 func TestSelectScatter(t *testing.T) { 206 sel := NewRoute( 207 Scatter, 208 &vindexes.Keyspace{ 209 Name: "ks", 210 Sharded: true, 211 }, 212 "dummy_select", 213 "dummy_select_field", 214 ) 215 216 vc := &loggingVCursor{ 217 shards: []string{"-20", "20-"}, 218 results: []*sqltypes.Result{defaultSelectResult}, 219 } 220 result, err := sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 221 require.NoError(t, err) 222 vc.ExpectLog(t, []string{ 223 `ResolveDestinations ks [] Destinations:DestinationAllShards()`, 224 `ExecuteMultiShard ks.-20: dummy_select {} ks.20-: dummy_select {} false false`, 225 }) 226 expectResult(t, "sel.Execute", result, defaultSelectResult) 227 228 vc.Rewind() 229 result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) 230 require.NoError(t, err) 231 vc.ExpectLog(t, []string{ 232 `ResolveDestinations ks [] Destinations:DestinationAllShards()`, 233 `StreamExecuteMulti dummy_select ks.-20: {} ks.20-: {} `, 234 }) 235 expectResult(t, "sel.StreamExecute", result, defaultSelectResult) 236 } 237 238 func TestSelectEqualUnique(t *testing.T) { 239 vindex, _ := vindexes.NewHash("", nil) 240 sel := NewRoute( 241 EqualUnique, 242 &vindexes.Keyspace{ 243 Name: "ks", 244 Sharded: true, 245 }, 246 "dummy_select", 247 "dummy_select_field", 248 ) 249 sel.Vindex = vindex.(vindexes.SingleColumn) 250 251 sel.Values = []evalengine.Expr{ 252 evalengine.NewLiteralInt(1), 253 } 254 vc := &loggingVCursor{ 255 shards: []string{"-20", "20-"}, 256 results: []*sqltypes.Result{defaultSelectResult}, 257 } 258 result, err := sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 259 require.NoError(t, err) 260 vc.ExpectLog(t, []string{ 261 `ResolveDestinations ks [type:INT64 value:"1"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6)`, 262 `ExecuteMultiShard ks.-20: dummy_select {} false false`, 263 }) 264 expectResult(t, "sel.Execute", result, defaultSelectResult) 265 266 vc.Rewind() 267 result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) 268 require.NoError(t, err) 269 vc.ExpectLog(t, []string{ 270 `ResolveDestinations ks [type:INT64 value:"1"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6)`, 271 `StreamExecuteMulti dummy_select ks.-20: {} `, 272 }) 273 expectResult(t, "sel.StreamExecute", result, defaultSelectResult) 274 } 275 276 func TestSelectNone(t *testing.T) { 277 vindex, _ := vindexes.NewHash("", nil) 278 sel := NewRoute( 279 None, 280 &vindexes.Keyspace{ 281 Name: "ks", 282 Sharded: true, 283 }, 284 "dummy_select", 285 "dummy_select_field", 286 ) 287 sel.Vindex = vindex.(vindexes.SingleColumn) 288 sel.Values = nil 289 290 vc := &loggingVCursor{ 291 shards: []string{"-20", "20-"}, 292 results: []*sqltypes.Result{}, 293 } 294 result, err := sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 295 require.NoError(t, err) 296 require.Empty(t, vc.log) 297 expectResult(t, "sel.Execute", result, &sqltypes.Result{}) 298 299 vc.Rewind() 300 result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) 301 require.NoError(t, err) 302 require.Empty(t, vc.log) 303 expectResult(t, "sel.StreamExecute", result, nil) 304 305 vc.Rewind() 306 307 // test with special no-routes handling 308 sel.NoRoutesSpecialHandling = true 309 result, err = sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 310 require.NoError(t, err) 311 vc.ExpectLog(t, []string{ 312 `ResolveDestinations ks [] Destinations:DestinationAnyShard()`, 313 `ExecuteMultiShard ks.-20: dummy_select {} false false`, 314 }) 315 expectResult(t, "sel.Execute", result, &sqltypes.Result{}) 316 317 vc.Rewind() 318 result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) 319 require.NoError(t, err) 320 vc.ExpectLog(t, []string{ 321 `ResolveDestinations ks [] Destinations:DestinationAnyShard()`, 322 `StreamExecuteMulti dummy_select ks.-20: {} `, 323 }) 324 expectResult(t, "sel.StreamExecute", result, &sqltypes.Result{}) 325 } 326 327 func TestSelectEqualUniqueScatter(t *testing.T) { 328 vindex, _ := vindexes.NewLookupUnique("", map[string]string{ 329 "table": "lkp", 330 "from": "from", 331 "to": "toc", 332 "write_only": "true", 333 }) 334 sel := NewRoute( 335 EqualUnique, 336 &vindexes.Keyspace{ 337 Name: "ks", 338 Sharded: true, 339 }, 340 "dummy_select", 341 "dummy_select_field", 342 ) 343 sel.Vindex = vindex.(vindexes.SingleColumn) 344 sel.Values = []evalengine.Expr{ 345 evalengine.NewLiteralInt(1), 346 } 347 vc := &loggingVCursor{ 348 shards: []string{"-20", "20-"}, 349 shardForKsid: []string{"-20", "20-"}, 350 results: []*sqltypes.Result{defaultSelectResult}, 351 } 352 result, err := sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 353 require.NoError(t, err) 354 vc.ExpectLog(t, []string{ 355 `ResolveDestinations ks [type:INT64 value:"1"] Destinations:DestinationKeyRange(-)`, 356 `ExecuteMultiShard ks.-20: dummy_select {} ks.20-: dummy_select {} false false`, 357 }) 358 expectResult(t, "sel.Execute", result, defaultSelectResult) 359 360 vc.Rewind() 361 result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) 362 require.NoError(t, err) 363 vc.ExpectLog(t, []string{ 364 `ResolveDestinations ks [type:INT64 value:"1"] Destinations:DestinationKeyRange(-)`, 365 `StreamExecuteMulti dummy_select ks.-20: {} ks.20-: {} `, 366 }) 367 expectResult(t, "sel.StreamExecute", result, defaultSelectResult) 368 } 369 370 func TestSelectEqual(t *testing.T) { 371 vindex, _ := vindexes.NewLookup("", map[string]string{ 372 "table": "lkp", 373 "from": "from", 374 "to": "toc", 375 }) 376 sel := NewRoute( 377 Equal, 378 &vindexes.Keyspace{ 379 Name: "ks", 380 Sharded: true, 381 }, 382 "dummy_select", 383 "dummy_select_field", 384 ) 385 sel.Vindex = vindex.(vindexes.SingleColumn) 386 sel.Values = []evalengine.Expr{ 387 evalengine.NewLiteralInt(1), 388 } 389 vc := &loggingVCursor{ 390 shards: []string{"-20", "20-"}, 391 results: []*sqltypes.Result{ 392 sqltypes.MakeTestResult( 393 sqltypes.MakeTestFields( 394 "fromc|toc", 395 "int64|varbinary", 396 ), 397 "1|\x00", 398 "1|\x80", 399 ), 400 defaultSelectResult, 401 }, 402 } 403 result, err := sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 404 require.NoError(t, err) 405 vc.ExpectLog(t, []string{ 406 `Execute select from, toc from lkp where from in ::from from: type:TUPLE values:{type:INT64 value:"1"} false`, 407 `ResolveDestinations ks [type:INT64 value:"1"] Destinations:DestinationKeyspaceIDs(00,80)`, 408 `ExecuteMultiShard ks.-20: dummy_select {} ks.20-: dummy_select {} false false`, 409 }) 410 expectResult(t, "sel.Execute", result, defaultSelectResult) 411 412 vc.Rewind() 413 result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) 414 require.NoError(t, err) 415 vc.ExpectLog(t, []string{ 416 `Execute select from, toc from lkp where from in ::from from: type:TUPLE values:{type:INT64 value:"1"} false`, 417 `ResolveDestinations ks [type:INT64 value:"1"] Destinations:DestinationKeyspaceIDs(00,80)`, 418 `StreamExecuteMulti dummy_select ks.-20: {} ks.20-: {} `, 419 }) 420 expectResult(t, "sel.StreamExecute", result, defaultSelectResult) 421 } 422 423 func TestSelectEqualNoRoute(t *testing.T) { 424 vindex, _ := vindexes.NewLookupUnique("", map[string]string{ 425 "table": "lkp", 426 "from": "from", 427 "to": "toc", 428 }) 429 sel := NewRoute( 430 Equal, 431 &vindexes.Keyspace{ 432 Name: "ks", 433 Sharded: true, 434 }, 435 "dummy_select", 436 "dummy_select_field", 437 ) 438 sel.Vindex = vindex.(vindexes.SingleColumn) 439 sel.Values = []evalengine.Expr{ 440 evalengine.NewLiteralInt(1), 441 } 442 443 vc := &loggingVCursor{shards: []string{"-20", "20-"}} 444 result, err := sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 445 require.NoError(t, err) 446 vc.ExpectLog(t, []string{ 447 `Execute select from, toc from lkp where from in ::from from: type:TUPLE values:{type:INT64 value:"1"} false`, 448 `ResolveDestinations ks [type:INT64 value:"1"] Destinations:DestinationNone()`, 449 }) 450 expectResult(t, "sel.Execute", result, &sqltypes.Result{}) 451 452 vc.Rewind() 453 result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) 454 require.NoError(t, err) 455 vc.ExpectLog(t, []string{ 456 `Execute select from, toc from lkp where from in ::from from: type:TUPLE values:{type:INT64 value:"1"} false`, 457 `ResolveDestinations ks [type:INT64 value:"1"] Destinations:DestinationNone()`, 458 }) 459 expectResult(t, "sel.StreamExecute", result, nil) 460 461 // test with special no-routes handling 462 sel.NoRoutesSpecialHandling = true 463 vc.Rewind() 464 465 result, err = sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 466 require.NoError(t, err) 467 vc.ExpectLog(t, []string{ 468 `Execute select from, toc from lkp where from in ::from from: type:TUPLE values:{type:INT64 value:"1"} false`, 469 `ResolveDestinations ks [type:INT64 value:"1"] Destinations:DestinationNone()`, 470 `ResolveDestinations ks [] Destinations:DestinationAnyShard()`, 471 `ExecuteMultiShard ks.-20: dummy_select {} false false`, 472 }) 473 expectResult(t, "sel.Execute", result, &sqltypes.Result{}) 474 475 vc.Rewind() 476 result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) 477 require.NoError(t, err) 478 vc.ExpectLog(t, []string{ 479 `Execute select from, toc from lkp where from in ::from from: type:TUPLE values:{type:INT64 value:"1"} false`, 480 `ResolveDestinations ks [type:INT64 value:"1"] Destinations:DestinationNone()`, 481 `ResolveDestinations ks [] Destinations:DestinationAnyShard()`, 482 `StreamExecuteMulti dummy_select ks.-20: {} `, 483 }) 484 expectResult(t, "sel.StreamExecute", result, &sqltypes.Result{}) 485 } 486 487 func TestINUnique(t *testing.T) { 488 vindex, _ := vindexes.NewHash("", nil) 489 sel := NewRoute( 490 IN, 491 &vindexes.Keyspace{ 492 Name: "ks", 493 Sharded: true, 494 }, 495 "dummy_select", 496 "dummy_select_field", 497 ) 498 sel.Vindex = vindex.(vindexes.SingleColumn) 499 sel.Values = []evalengine.Expr{ 500 evalengine.TupleExpr{ 501 evalengine.NewLiteralInt(1), 502 evalengine.NewLiteralInt(2), 503 evalengine.NewLiteralInt(4), 504 }, 505 } 506 vc := &loggingVCursor{ 507 shards: []string{"-20", "20-"}, 508 shardForKsid: []string{"-20", "-20", "20-"}, 509 results: []*sqltypes.Result{defaultSelectResult}, 510 } 511 result, err := sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 512 require.NoError(t, err) 513 vc.ExpectLog(t, []string{ 514 `ResolveDestinations ks [type:INT64 value:"1" type:INT64 value:"2" type:INT64 value:"4"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6),DestinationKeyspaceID(06e7ea22ce92708f),DestinationKeyspaceID(d2fd8867d50d2dfe)`, 515 `ExecuteMultiShard ` + 516 `ks.-20: dummy_select {__vals: type:TUPLE values:{type:INT64 value:"1"} values:{type:INT64 value:"2"}} ` + 517 `ks.20-: dummy_select {__vals: type:TUPLE values:{type:INT64 value:"4"}} ` + 518 `false false`, 519 }) 520 expectResult(t, "sel.Execute", result, defaultSelectResult) 521 522 vc.Rewind() 523 result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) 524 require.NoError(t, err) 525 vc.ExpectLog(t, []string{ 526 `ResolveDestinations ks [type:INT64 value:"1" type:INT64 value:"2" type:INT64 value:"4"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6),DestinationKeyspaceID(06e7ea22ce92708f),DestinationKeyspaceID(d2fd8867d50d2dfe)`, 527 `StreamExecuteMulti dummy_select ks.-20: {__vals: type:TUPLE values:{type:INT64 value:"1"} values:{type:INT64 value:"2"}} ks.20-: {__vals: type:TUPLE values:{type:INT64 value:"4"}} `, 528 }) 529 expectResult(t, "sel.StreamExecute", result, defaultSelectResult) 530 } 531 532 func TestINNonUnique(t *testing.T) { 533 vindex, _ := vindexes.NewLookup("", map[string]string{ 534 "table": "lkp", 535 "from": "from", 536 "to": "toc", 537 }) 538 sel := NewRoute( 539 IN, 540 &vindexes.Keyspace{ 541 Name: "ks", 542 Sharded: true, 543 }, 544 "dummy_select", 545 "dummy_select_field", 546 ) 547 sel.Vindex = vindex.(vindexes.SingleColumn) 548 sel.Values = []evalengine.Expr{ 549 evalengine.TupleExpr{ 550 evalengine.NewLiteralInt(1), 551 evalengine.NewLiteralInt(2), 552 evalengine.NewLiteralInt(4), 553 }, 554 } 555 556 fields := sqltypes.MakeTestFields( 557 "fromc|toc", 558 "int64|varbinary", 559 ) 560 vc := &loggingVCursor{ 561 shards: []string{"-20", "20-"}, 562 results: []*sqltypes.Result{ 563 // 1 will be sent to both shards. 564 // 2 will go to -20. 565 // 4 will go to 20-. 566 sqltypes.MakeTestResult( 567 fields, 568 "1|\x00", 569 "1|\x80", 570 "2|\x00", 571 "4|\x80", 572 ), 573 defaultSelectResult, 574 }, 575 } 576 result, err := sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 577 require.NoError(t, err) 578 vc.ExpectLog(t, []string{ 579 `Execute select from, toc from lkp where from in ::from from: type:TUPLE values:{type:INT64 value:"1"} values:{type:INT64 value:"2"} values:{type:INT64 value:"4"} false`, 580 `ResolveDestinations ks [type:INT64 value:"1" type:INT64 value:"2" type:INT64 value:"4"] Destinations:DestinationKeyspaceIDs(00,80),DestinationKeyspaceIDs(00),DestinationKeyspaceIDs(80)`, 581 `ExecuteMultiShard ` + 582 `ks.-20: dummy_select {__vals: type:TUPLE values:{type:INT64 value:"1"} values:{type:INT64 value:"2"}} ` + 583 `ks.20-: dummy_select {__vals: type:TUPLE values:{type:INT64 value:"1"} values:{type:INT64 value:"4"}} ` + 584 `false false`, 585 }) 586 expectResult(t, "sel.Execute", result, defaultSelectResult) 587 588 vc.Rewind() 589 result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) 590 require.NoError(t, err) 591 vc.ExpectLog(t, []string{ 592 `Execute select from, toc from lkp where from in ::from from: type:TUPLE values:{type:INT64 value:"1"} values:{type:INT64 value:"2"} values:{type:INT64 value:"4"} false`, 593 `ResolveDestinations ks [type:INT64 value:"1" type:INT64 value:"2" type:INT64 value:"4"] Destinations:DestinationKeyspaceIDs(00,80),DestinationKeyspaceIDs(00),DestinationKeyspaceIDs(80)`, 594 `StreamExecuteMulti dummy_select ks.-20: {__vals: type:TUPLE values:{type:INT64 value:"1"} values:{type:INT64 value:"2"}} ks.20-: {__vals: type:TUPLE values:{type:INT64 value:"1"} values:{type:INT64 value:"4"}} `, 595 }) 596 expectResult(t, "sel.StreamExecute", result, defaultSelectResult) 597 } 598 599 func TestMultiEqual(t *testing.T) { 600 vindex, _ := vindexes.NewHash("", nil) 601 sel := NewRoute( 602 MultiEqual, 603 &vindexes.Keyspace{ 604 Name: "ks", 605 Sharded: true, 606 }, 607 "dummy_select", 608 "dummy_select_field", 609 ) 610 sel.Vindex = vindex.(vindexes.SingleColumn) 611 sel.Values = []evalengine.Expr{ 612 evalengine.TupleExpr{ 613 evalengine.NewLiteralInt(1), 614 evalengine.NewLiteralInt(2), 615 evalengine.NewLiteralInt(4), 616 }, 617 } 618 619 vc := &loggingVCursor{ 620 shards: []string{"-20", "20-"}, 621 shardForKsid: []string{"-20", "-20", "20-"}, 622 results: []*sqltypes.Result{defaultSelectResult}, 623 } 624 result, err := sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 625 require.NoError(t, err) 626 vc.ExpectLog(t, []string{ 627 `ResolveDestinations ks [type:INT64 value:"1" type:INT64 value:"2" type:INT64 value:"4"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6),DestinationKeyspaceID(06e7ea22ce92708f),DestinationKeyspaceID(d2fd8867d50d2dfe)`, 628 `ExecuteMultiShard ks.-20: dummy_select {} ks.20-: dummy_select {} false false`, 629 }) 630 expectResult(t, "sel.Execute", result, defaultSelectResult) 631 632 vc.Rewind() 633 result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) 634 require.NoError(t, err) 635 vc.ExpectLog(t, []string{ 636 `ResolveDestinations ks [type:INT64 value:"1" type:INT64 value:"2" type:INT64 value:"4"] Destinations:DestinationKeyspaceID(166b40b44aba4bd6),DestinationKeyspaceID(06e7ea22ce92708f),DestinationKeyspaceID(d2fd8867d50d2dfe)`, 637 `StreamExecuteMulti dummy_select ks.-20: {} ks.20-: {} `, 638 }) 639 expectResult(t, "sel.StreamExecute", result, defaultSelectResult) 640 } 641 642 func TestSelectLike(t *testing.T) { 643 subshard, _ := vindexes.NewCFC("cfc", map[string]string{"hash": "md5", "offsets": "[1,2]"}) 644 vindex := subshard.(*vindexes.CFC).PrefixVindex() 645 vc := &loggingVCursor{ 646 // we have shards '-0c80', '0c80-0d', '0d-40', '40-80', '80-' 647 shards: []string{"\x0c\x80", "\x0d", "\x40", "\x80"}, 648 results: []*sqltypes.Result{defaultSelectResult}, 649 } 650 651 sel := NewRoute( 652 Equal, 653 &vindexes.Keyspace{ 654 Name: "ks", 655 Sharded: true, 656 }, 657 "dummy_select", 658 "dummy_select_field", 659 ) 660 661 sel.Vindex = vindex 662 sel.Values = []evalengine.Expr{ 663 evalengine.NewLiteralString([]byte("a%"), collations.TypedCollation{}), 664 } 665 // md5("a") = 0cc175b9c0f1b6a831c399e269772661 666 // keyspace id prefix for "a" is 0x0c 667 vc.shardForKsid = []string{"-0c80", "0c80-0d"} 668 result, err := sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 669 require.NoError(t, err) 670 671 // range 0c-0d hits 2 shards ks.-0c80 ks.0c80-0d; 672 // note that 0c-0d should not hit ks.0d-40 673 vc.ExpectLog(t, []string{ 674 `ResolveDestinations ks [type:VARCHAR value:"a%"] Destinations:DestinationKeyRange(0c-0d)`, 675 `ExecuteMultiShard ks.-0c80: dummy_select {} ks.0c80-0d: dummy_select {} false false`, 676 }) 677 expectResult(t, "sel.Execute", result, defaultSelectResult) 678 679 vc.Rewind() 680 681 result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) 682 require.NoError(t, err) 683 684 vc.ExpectLog(t, []string{ 685 `ResolveDestinations ks [type:VARCHAR value:"a%"] Destinations:DestinationKeyRange(0c-0d)`, 686 `StreamExecuteMulti dummy_select ks.-0c80: {} ks.0c80-0d: {} `, 687 }) 688 expectResult(t, "sel.StreamExecute", result, defaultSelectResult) 689 690 vc.Rewind() 691 692 sel.Values = []evalengine.Expr{ 693 evalengine.NewLiteralString([]byte("ab%"), collations.TypedCollation{}), 694 } 695 // md5("b") = 92eb5ffee6ae2fec3ad71c777531578f 696 // keyspace id prefix for "ab" is 0x0c92 697 // adding one byte to the prefix just hit one shard 698 vc.shardForKsid = []string{"0c80-0d"} 699 result, err = sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 700 if err != nil { 701 t.Fatal(err) 702 } 703 vc.ExpectLog(t, []string{ 704 `ResolveDestinations ks [type:VARCHAR value:"ab%"] Destinations:DestinationKeyRange(0c92-0c93)`, 705 `ExecuteMultiShard ks.0c80-0d: dummy_select {} false false`, 706 }) 707 expectResult(t, "sel.Execute", result, defaultSelectResult) 708 709 vc.Rewind() 710 711 result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) 712 require.NoError(t, err) 713 714 vc.ExpectLog(t, []string{ 715 `ResolveDestinations ks [type:VARCHAR value:"ab%"] Destinations:DestinationKeyRange(0c92-0c93)`, 716 `StreamExecuteMulti dummy_select ks.0c80-0d: {} `, 717 }) 718 expectResult(t, "sel.StreamExecute", result, defaultSelectResult) 719 720 } 721 722 func TestSelectNext(t *testing.T) { 723 sel := NewRoute( 724 Next, 725 &vindexes.Keyspace{ 726 Name: "ks", 727 Sharded: false, 728 }, 729 "dummy_select", 730 "dummy_select_field", 731 ) 732 733 vc := &loggingVCursor{ 734 shards: []string{"-"}, 735 results: []*sqltypes.Result{defaultSelectResult}, 736 } 737 result, err := sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 738 require.NoError(t, err) 739 vc.ExpectLog(t, []string{ 740 `ResolveDestinations ks [] Destinations:DestinationAllShards()`, 741 `ExecuteMultiShard ks.-: dummy_select {} false false`, 742 }) 743 expectResult(t, "sel.Execute", result, defaultSelectResult) 744 745 vc.Rewind() 746 result, _ = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) 747 vc.ExpectLog(t, []string{ 748 `ResolveDestinations ks [] Destinations:DestinationAllShards()`, 749 `StreamExecuteMulti dummy_select ks.-: {} `, 750 }) 751 expectResult(t, "sel.StreamExecute", result, defaultSelectResult) 752 } 753 754 func TestSelectDBA(t *testing.T) { 755 sel := NewRoute( 756 DBA, 757 &vindexes.Keyspace{ 758 Name: "ks", 759 Sharded: true, 760 }, 761 "dummy_select", 762 "dummy_select_field", 763 ) 764 765 vc := &loggingVCursor{ 766 shards: []string{"-20", "20-"}, 767 results: []*sqltypes.Result{defaultSelectResult}, 768 } 769 result, err := sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 770 require.NoError(t, err) 771 vc.ExpectLog(t, []string{ 772 `ResolveDestinations ks [] Destinations:DestinationAnyShard()`, 773 `ExecuteMultiShard ks.-20: dummy_select {} false false`, 774 }) 775 expectResult(t, "sel.Execute", result, defaultSelectResult) 776 777 vc.Rewind() 778 result, _ = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) 779 vc.ExpectLog(t, []string{ 780 `ResolveDestinations ks [] Destinations:DestinationAnyShard()`, 781 `StreamExecuteMulti dummy_select ks.-20: {} `, 782 }) 783 expectResult(t, "sel.StreamExecute", result, defaultSelectResult) 784 } 785 786 func TestSelectReference(t *testing.T) { 787 sel := NewRoute( 788 Reference, 789 &vindexes.Keyspace{ 790 Name: "ks", 791 Sharded: true, 792 }, 793 "dummy_select", 794 "dummy_select_field", 795 ) 796 797 vc := &loggingVCursor{ 798 shards: []string{"-20", "20-"}, 799 results: []*sqltypes.Result{defaultSelectResult}, 800 } 801 result, err := sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 802 require.NoError(t, err) 803 vc.ExpectLog(t, []string{ 804 `ResolveDestinations ks [] Destinations:DestinationAnyShard()`, 805 `ExecuteMultiShard ks.-20: dummy_select {} false false`, 806 }) 807 expectResult(t, "sel.Execute", result, defaultSelectResult) 808 809 vc.Rewind() 810 result, _ = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) 811 vc.ExpectLog(t, []string{ 812 `ResolveDestinations ks [] Destinations:DestinationAnyShard()`, 813 `StreamExecuteMulti dummy_select ks.-20: {} `, 814 }) 815 expectResult(t, "sel.StreamExecute", result, defaultSelectResult) 816 } 817 818 func TestRouteGetFields(t *testing.T) { 819 vindex, _ := vindexes.NewLookupUnique("", map[string]string{ 820 "table": "lkp", 821 "from": "from", 822 "to": "toc", 823 }) 824 sel := NewRoute( 825 Equal, 826 &vindexes.Keyspace{ 827 Name: "ks", 828 Sharded: true, 829 }, 830 "dummy_select", 831 "dummy_select_field", 832 ) 833 sel.Vindex = vindex.(vindexes.SingleColumn) 834 sel.Values = []evalengine.Expr{ 835 evalengine.NewLiteralInt(1), 836 } 837 838 vc := &loggingVCursor{shards: []string{"-20", "20-"}} 839 result, err := sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, true) 840 require.NoError(t, err) 841 vc.ExpectLog(t, []string{ 842 `Execute select from, toc from lkp where from in ::from from: type:TUPLE values:{type:INT64 value:"1"} false`, 843 `ResolveDestinations ks [type:INT64 value:"1"] Destinations:DestinationNone()`, 844 `ResolveDestinations ks [] Destinations:DestinationAnyShard()`, 845 `ExecuteMultiShard ks.-20: dummy_select_field {} false false`, 846 }) 847 expectResult(t, "sel.Execute", result, &sqltypes.Result{}) 848 849 vc.Rewind() 850 result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, true) 851 require.NoError(t, err) 852 vc.ExpectLog(t, []string{ 853 `Execute select from, toc from lkp where from in ::from from: type:TUPLE values:{type:INT64 value:"1"} false`, 854 `ResolveDestinations ks [type:INT64 value:"1"] Destinations:DestinationNone()`, 855 `ResolveDestinations ks [] Destinations:DestinationAnyShard()`, 856 `ExecuteMultiShard ks.-20: dummy_select_field {} false false`, 857 }) 858 expectResult(t, "sel.StreamExecute", result, &sqltypes.Result{}) 859 vc.Rewind() 860 861 // test with special no-routes handling 862 sel.NoRoutesSpecialHandling = true 863 result, err = sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, true) 864 require.NoError(t, err) 865 vc.ExpectLog(t, []string{ 866 `Execute select from, toc from lkp where from in ::from from: type:TUPLE values:{type:INT64 value:"1"} false`, 867 `ResolveDestinations ks [type:INT64 value:"1"] Destinations:DestinationNone()`, 868 `ResolveDestinations ks [] Destinations:DestinationAnyShard()`, 869 `ExecuteMultiShard ks.-20: dummy_select {} false false`, 870 }) 871 expectResult(t, "sel.Execute", result, &sqltypes.Result{}) 872 873 vc.Rewind() 874 result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, true) 875 require.NoError(t, err) 876 vc.ExpectLog(t, []string{ 877 `Execute select from, toc from lkp where from in ::from from: type:TUPLE values:{type:INT64 value:"1"} false`, 878 `ResolveDestinations ks [type:INT64 value:"1"] Destinations:DestinationNone()`, 879 `ResolveDestinations ks [] Destinations:DestinationAnyShard()`, 880 `StreamExecuteMulti dummy_select ks.-20: {} `, 881 }) 882 expectResult(t, "sel.StreamExecute", result, &sqltypes.Result{}) 883 } 884 885 func TestRouteSort(t *testing.T) { 886 sel := NewRoute( 887 Unsharded, 888 &vindexes.Keyspace{ 889 Name: "ks", 890 Sharded: false, 891 }, 892 "dummy_select", 893 "dummy_select_field", 894 ) 895 sel.OrderBy = []OrderByParams{{ 896 Col: 0, 897 WeightStringCol: -1, 898 }} 899 900 vc := &loggingVCursor{ 901 shards: []string{"0"}, 902 results: []*sqltypes.Result{ 903 sqltypes.MakeTestResult( 904 sqltypes.MakeTestFields( 905 "id", 906 "int64", 907 ), 908 "1", 909 "1", 910 "3", 911 "2", 912 ), 913 }, 914 } 915 result, err := sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 916 require.NoError(t, err) 917 vc.ExpectLog(t, []string{ 918 `ResolveDestinations ks [] Destinations:DestinationAllShards()`, 919 `ExecuteMultiShard ks.0: dummy_select {} false false`, 920 }) 921 wantResult := sqltypes.MakeTestResult( 922 sqltypes.MakeTestFields( 923 "id", 924 "int64", 925 ), 926 "1", 927 "1", 928 "2", 929 "3", 930 ) 931 expectResult(t, "sel.Execute", result, wantResult) 932 933 sel.OrderBy[0].Desc = true 934 vc.Rewind() 935 result, err = sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 936 require.NoError(t, err) 937 wantResult = sqltypes.MakeTestResult( 938 sqltypes.MakeTestFields( 939 "id", 940 "int64", 941 ), 942 "3", 943 "2", 944 "1", 945 "1", 946 ) 947 expectResult(t, "sel.Execute", result, wantResult) 948 949 vc = &loggingVCursor{ 950 shards: []string{"0"}, 951 results: []*sqltypes.Result{ 952 sqltypes.MakeTestResult( 953 sqltypes.MakeTestFields( 954 "id", 955 "varchar", 956 ), 957 "1", 958 "2", 959 "3", 960 ), 961 }, 962 } 963 _, err = sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 964 require.EqualError(t, err, `cannot compare strings, collation is unknown or unsupported (collation ID: 0)`) 965 } 966 967 func TestRouteSortWeightStrings(t *testing.T) { 968 sel := NewRoute( 969 Unsharded, 970 &vindexes.Keyspace{ 971 Name: "ks", 972 Sharded: false, 973 }, 974 "dummy_select", 975 "dummy_select_field", 976 ) 977 sel.OrderBy = []OrderByParams{{ 978 Col: 1, 979 WeightStringCol: 0, 980 }} 981 982 vc := &loggingVCursor{ 983 shards: []string{"0"}, 984 results: []*sqltypes.Result{ 985 sqltypes.MakeTestResult( 986 sqltypes.MakeTestFields( 987 "weightString|normal", 988 "varbinary|varchar", 989 ), 990 "v|x", 991 "g|d", 992 "a|a", 993 "c|t", 994 "f|p", 995 ), 996 }, 997 } 998 999 var result *sqltypes.Result 1000 var wantResult *sqltypes.Result 1001 var err error 1002 t.Run("Sort using Weight Strings", func(t *testing.T) { 1003 result, err = sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 1004 require.NoError(t, err) 1005 vc.ExpectLog(t, []string{ 1006 `ResolveDestinations ks [] Destinations:DestinationAllShards()`, 1007 `ExecuteMultiShard ks.0: dummy_select {} false false`, 1008 }) 1009 wantResult = sqltypes.MakeTestResult( 1010 sqltypes.MakeTestFields( 1011 "weightString|normal", 1012 "varbinary|varchar", 1013 ), 1014 "a|a", 1015 "c|t", 1016 "f|p", 1017 "g|d", 1018 "v|x", 1019 ) 1020 expectResult(t, "sel.Execute", result, wantResult) 1021 }) 1022 1023 t.Run("Descending ordering using weighted strings", func(t *testing.T) { 1024 sel.OrderBy[0].Desc = true 1025 vc.Rewind() 1026 result, err = sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 1027 require.NoError(t, err) 1028 wantResult = sqltypes.MakeTestResult( 1029 sqltypes.MakeTestFields( 1030 "weightString|normal", 1031 "varbinary|varchar", 1032 ), 1033 "v|x", 1034 "g|d", 1035 "f|p", 1036 "c|t", 1037 "a|a", 1038 ) 1039 expectResult(t, "sel.Execute", result, wantResult) 1040 }) 1041 1042 t.Run("Error when no weight string set", func(t *testing.T) { 1043 sel.OrderBy = []OrderByParams{{ 1044 Col: 1, 1045 WeightStringCol: -1, 1046 }} 1047 1048 vc = &loggingVCursor{ 1049 shards: []string{"0"}, 1050 results: []*sqltypes.Result{ 1051 sqltypes.MakeTestResult( 1052 sqltypes.MakeTestFields( 1053 "weightString|normal", 1054 "varbinary|varchar", 1055 ), 1056 "v|x", 1057 "g|d", 1058 "a|a", 1059 "c|t", 1060 "f|p", 1061 ), 1062 }, 1063 } 1064 _, err = sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 1065 require.EqualError(t, err, `cannot compare strings, collation is unknown or unsupported (collation ID: 0)`) 1066 }) 1067 } 1068 1069 func TestRouteSortCollation(t *testing.T) { 1070 sel := NewRoute( 1071 Unsharded, 1072 &vindexes.Keyspace{ 1073 Name: "ks", 1074 Sharded: false, 1075 }, 1076 "dummy_select", 1077 "dummy_select_field", 1078 ) 1079 1080 collationID, _ := collations.Local().LookupID("utf8mb4_hu_0900_ai_ci") 1081 1082 sel.OrderBy = []OrderByParams{{ 1083 Col: 0, 1084 CollationID: collationID, 1085 }} 1086 1087 vc := &loggingVCursor{ 1088 shards: []string{"0"}, 1089 results: []*sqltypes.Result{ 1090 sqltypes.MakeTestResult( 1091 sqltypes.MakeTestFields( 1092 "normal", 1093 "varchar", 1094 ), 1095 "c", 1096 "d", 1097 "cs", 1098 "cs", 1099 "c", 1100 ), 1101 }, 1102 } 1103 1104 var result *sqltypes.Result 1105 var wantResult *sqltypes.Result 1106 var err error 1107 t.Run("Sort using Collation", func(t *testing.T) { 1108 result, err = sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 1109 require.NoError(t, err) 1110 vc.ExpectLog(t, []string{ 1111 `ResolveDestinations ks [] Destinations:DestinationAllShards()`, 1112 `ExecuteMultiShard ks.0: dummy_select {} false false`, 1113 }) 1114 wantResult = sqltypes.MakeTestResult( 1115 sqltypes.MakeTestFields( 1116 "normal", 1117 "varchar", 1118 ), 1119 "c", 1120 "c", 1121 "cs", 1122 "cs", 1123 "d", 1124 ) 1125 expectResult(t, "sel.Execute", result, wantResult) 1126 }) 1127 1128 t.Run("Descending ordering using Collation", func(t *testing.T) { 1129 sel.OrderBy[0].Desc = true 1130 vc.Rewind() 1131 result, err = sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 1132 require.NoError(t, err) 1133 wantResult = sqltypes.MakeTestResult( 1134 sqltypes.MakeTestFields( 1135 "normal", 1136 "varchar", 1137 ), 1138 "d", 1139 "cs", 1140 "cs", 1141 "c", 1142 "c", 1143 ) 1144 expectResult(t, "sel.Execute", result, wantResult) 1145 }) 1146 1147 t.Run("Error when Unknown Collation", func(t *testing.T) { 1148 sel.OrderBy = []OrderByParams{{ 1149 Col: 0, 1150 CollationID: collations.Unknown, 1151 }} 1152 1153 vc := &loggingVCursor{ 1154 shards: []string{"0"}, 1155 results: []*sqltypes.Result{ 1156 sqltypes.MakeTestResult( 1157 sqltypes.MakeTestFields( 1158 "normal", 1159 "varchar", 1160 ), 1161 "c", 1162 "d", 1163 "cs", 1164 "cs", 1165 "c", 1166 ), 1167 }, 1168 } 1169 _, err = sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 1170 require.EqualError(t, err, "cannot compare strings, collation is unknown or unsupported (collation ID: 0)") 1171 }) 1172 1173 t.Run("Error when Unsupported Collation", func(t *testing.T) { 1174 sel.OrderBy = []OrderByParams{{ 1175 Col: 0, 1176 CollationID: 1111, 1177 }} 1178 1179 vc := &loggingVCursor{ 1180 shards: []string{"0"}, 1181 results: []*sqltypes.Result{ 1182 sqltypes.MakeTestResult( 1183 sqltypes.MakeTestFields( 1184 "normal", 1185 "varchar", 1186 ), 1187 "c", 1188 "d", 1189 "cs", 1190 "cs", 1191 "c", 1192 ), 1193 }, 1194 } 1195 _, err = sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 1196 require.EqualError(t, err, "cannot compare strings, collation is unknown or unsupported (collation ID: 1111)") 1197 }) 1198 } 1199 1200 func TestRouteSortTruncate(t *testing.T) { 1201 sel := NewRoute( 1202 Unsharded, 1203 &vindexes.Keyspace{ 1204 Name: "ks", 1205 Sharded: false, 1206 }, 1207 "dummy_select", 1208 "dummy_select_field", 1209 ) 1210 sel.OrderBy = []OrderByParams{{ 1211 Col: 0, 1212 }} 1213 sel.TruncateColumnCount = 1 1214 1215 vc := &loggingVCursor{ 1216 shards: []string{"0"}, 1217 results: []*sqltypes.Result{ 1218 sqltypes.MakeTestResult( 1219 sqltypes.MakeTestFields( 1220 "id|col", 1221 "int64|int64", 1222 ), 1223 "1|1", 1224 "1|1", 1225 "3|1", 1226 "2|1", 1227 ), 1228 }, 1229 } 1230 result, err := sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 1231 require.NoError(t, err) 1232 vc.ExpectLog(t, []string{ 1233 `ResolveDestinations ks [] Destinations:DestinationAllShards()`, 1234 `ExecuteMultiShard ks.0: dummy_select {} false false`, 1235 }) 1236 wantResult := sqltypes.MakeTestResult( 1237 sqltypes.MakeTestFields( 1238 "id", 1239 "int64", 1240 ), 1241 "1", 1242 "1", 1243 "2", 1244 "3", 1245 ) 1246 expectResult(t, "sel.Execute", result, wantResult) 1247 } 1248 1249 func TestRouteStreamTruncate(t *testing.T) { 1250 sel := NewRoute( 1251 Unsharded, 1252 &vindexes.Keyspace{ 1253 Name: "ks", 1254 Sharded: false, 1255 }, 1256 "dummy_select", 1257 "dummy_select_field", 1258 ) 1259 sel.TruncateColumnCount = 1 1260 1261 vc := &loggingVCursor{ 1262 shards: []string{"0"}, 1263 results: []*sqltypes.Result{ 1264 sqltypes.MakeTestResult( 1265 sqltypes.MakeTestFields( 1266 "id|col", 1267 "int64|int64", 1268 ), 1269 "1|1", 1270 "2|1", 1271 ), 1272 }, 1273 } 1274 result, err := sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 1275 require.NoError(t, err) 1276 vc.ExpectLog(t, []string{ 1277 `ResolveDestinations ks [] Destinations:DestinationAllShards()`, 1278 `ExecuteMultiShard ks.0: dummy_select {} false false`, 1279 }) 1280 wantResult := sqltypes.MakeTestResult( 1281 sqltypes.MakeTestFields( 1282 "id", 1283 "int64", 1284 ), 1285 "1", 1286 "2", 1287 ) 1288 expectResult(t, "sel.Execute", result, wantResult) 1289 } 1290 1291 func TestRouteStreamSortTruncate(t *testing.T) { 1292 sel := NewRoute( 1293 Unsharded, 1294 &vindexes.Keyspace{ 1295 Name: "ks", 1296 Sharded: false, 1297 }, 1298 "dummy_select", 1299 "dummy_select_field", 1300 ) 1301 sel.OrderBy = []OrderByParams{{ 1302 Col: 0, 1303 }} 1304 sel.TruncateColumnCount = 1 1305 1306 vc := &loggingVCursor{ 1307 shards: []string{"0"}, 1308 results: []*sqltypes.Result{ 1309 sqltypes.MakeTestResult( 1310 sqltypes.MakeTestFields( 1311 "id|col", 1312 "int64|int64", 1313 ), 1314 "1|1", 1315 "2|1", 1316 ), 1317 }, 1318 } 1319 result, err := wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, true) 1320 require.NoError(t, err) 1321 vc.ExpectLog(t, []string{ 1322 `ResolveDestinations ks [] Destinations:DestinationAllShards()`, 1323 `StreamExecuteMulti dummy_select ks.0: {} `, 1324 }) 1325 1326 // We're not really testing sort functionality here because that part is tested 1327 // in merge_sort_test. We're only testing that truncation happens even if sort 1328 // is enabled, which is a different code path from unsorted truncate. 1329 wantResult := sqltypes.MakeTestResult( 1330 sqltypes.MakeTestFields( 1331 "id", 1332 "int64", 1333 ), 1334 "1", 1335 "2", 1336 ) 1337 expectResult(t, "sel.Execute", result, wantResult) 1338 } 1339 1340 func TestParamsFail(t *testing.T) { 1341 sel := NewRoute( 1342 Unsharded, 1343 &vindexes.Keyspace{ 1344 Name: "ks", 1345 Sharded: false, 1346 }, 1347 "dummy_select", 1348 "dummy_select_field", 1349 ) 1350 1351 vc := &loggingVCursor{shardErr: errors.New("shard error")} 1352 _, err := sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 1353 require.EqualError(t, err, `shard error`) 1354 1355 vc.Rewind() 1356 _, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) 1357 require.EqualError(t, err, `shard error`) 1358 } 1359 1360 func TestExecFail(t *testing.T) { 1361 1362 t.Run("unsharded", func(t *testing.T) { 1363 // Unsharded error 1364 sel := NewRoute( 1365 Unsharded, 1366 &vindexes.Keyspace{ 1367 Name: "ks", 1368 Sharded: false, 1369 }, 1370 "dummy_select", 1371 "dummy_select_field", 1372 ) 1373 1374 vc := &loggingVCursor{shards: []string{"0"}, resultErr: vterrors.NewErrorf(vtrpcpb.Code_CANCELED, vterrors.QueryInterrupted, "query timeout")} 1375 _, err := sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 1376 require.EqualError(t, err, `query timeout`) 1377 assert.Empty(t, vc.warnings) 1378 1379 vc.Rewind() 1380 _, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) 1381 require.EqualError(t, err, `query timeout`) 1382 }) 1383 1384 t.Run("normal route with no scatter errors as warnings", func(t *testing.T) { 1385 // Scatter fails if one of N fails without ScatterErrorsAsWarnings 1386 sel := NewRoute( 1387 Scatter, 1388 &vindexes.Keyspace{ 1389 Name: "ks", 1390 Sharded: true, 1391 }, 1392 "dummy_select", 1393 "dummy_select_field", 1394 ) 1395 1396 vc := &loggingVCursor{ 1397 shards: []string{"-20", "20-"}, 1398 results: []*sqltypes.Result{defaultSelectResult}, 1399 multiShardErrs: []error{ 1400 errors.New("result error -20"), 1401 }, 1402 } 1403 _, err := sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 1404 require.EqualError(t, err, `result error -20`) 1405 vc.ExpectWarnings(t, nil) 1406 vc.ExpectLog(t, []string{ 1407 `ResolveDestinations ks [] Destinations:DestinationAllShards()`, 1408 `ExecuteMultiShard ks.-20: dummy_select {} ks.20-: dummy_select {} false false`, 1409 }) 1410 }) 1411 1412 t.Run("ScatterErrorsAsWarnings", func(t *testing.T) { 1413 // Scatter succeeds if one of N fails with ScatterErrorsAsWarnings 1414 sel := NewRoute( 1415 Scatter, 1416 &vindexes.Keyspace{ 1417 Name: "ks", 1418 Sharded: true, 1419 }, 1420 "dummy_select", 1421 "dummy_select_field", 1422 ) 1423 sel.ScatterErrorsAsWarnings = true 1424 1425 vc := &loggingVCursor{ 1426 shards: []string{"-20", "20-"}, 1427 results: []*sqltypes.Result{defaultSelectResult}, 1428 multiShardErrs: []error{ 1429 errors.New("result error -20"), 1430 nil, 1431 }, 1432 } 1433 result, err := sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 1434 require.NoError(t, err, "unexpected ScatterErrorsAsWarnings error %v", err) 1435 vc.ExpectLog(t, []string{ 1436 `ResolveDestinations ks [] Destinations:DestinationAllShards()`, 1437 `ExecuteMultiShard ks.-20: dummy_select {} ks.20-: dummy_select {} false false`, 1438 }) 1439 expectResult(t, "sel.Execute", result, defaultSelectResult) 1440 1441 vc.Rewind() 1442 vc.resultErr = mysql.NewSQLError(mysql.ERQueryInterrupted, "", "query timeout -20") 1443 // test when there is order by column 1444 sel.OrderBy = []OrderByParams{{ 1445 WeightStringCol: -1, 1446 Col: 0, 1447 }} 1448 _, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) 1449 require.NoError(t, err, "unexpected ScatterErrorsAsWarnings error %v", err) 1450 vc.ExpectWarnings(t, []*querypb.QueryWarning{{Code: mysql.ERQueryInterrupted, Message: "query timeout -20 (errno 1317) (sqlstate HY000)"}}) 1451 }) 1452 } 1453 1454 func TestSelectEqualUniqueMultiColumnVindex(t *testing.T) { 1455 vindex, _ := vindexes.NewRegionExperimental("", map[string]string{"region_bytes": "1"}) 1456 sel := NewRoute( 1457 EqualUnique, 1458 &vindexes.Keyspace{ 1459 Name: "ks", 1460 Sharded: true, 1461 }, 1462 "dummy_select", 1463 "dummy_select_field", 1464 ) 1465 sel.Vindex = vindex 1466 sel.Values = []evalengine.Expr{ 1467 evalengine.NewLiteralInt(1), 1468 evalengine.NewLiteralInt(2), 1469 } 1470 1471 vc := &loggingVCursor{ 1472 shards: []string{"-20", "20-"}, 1473 results: []*sqltypes.Result{defaultSelectResult}, 1474 } 1475 result, err := sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 1476 require.NoError(t, err) 1477 vc.ExpectLog(t, []string{ 1478 `ResolveDestinationsMultiCol ks [[INT64(1) INT64(2)]] Destinations:DestinationKeyspaceID(0106e7ea22ce92708f)`, 1479 `ExecuteMultiShard ks.-20: dummy_select {} false false`, 1480 }) 1481 expectResult(t, "sel.Execute", result, defaultSelectResult) 1482 1483 vc.Rewind() 1484 result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) 1485 require.NoError(t, err) 1486 vc.ExpectLog(t, []string{ 1487 `ResolveDestinationsMultiCol ks [[INT64(1) INT64(2)]] Destinations:DestinationKeyspaceID(0106e7ea22ce92708f)`, 1488 `StreamExecuteMulti dummy_select ks.-20: {} `, 1489 }) 1490 expectResult(t, "sel.StreamExecute", result, defaultSelectResult) 1491 } 1492 1493 func TestSelectEqualMultiColumnVindex(t *testing.T) { 1494 vindex, _ := vindexes.NewRegionExperimental("", map[string]string{"region_bytes": "1"}) 1495 vc := &loggingVCursor{ 1496 shards: []string{"-20", "20-"}, 1497 shardForKsid: []string{"-20", "20-"}, 1498 results: []*sqltypes.Result{defaultSelectResult}, 1499 } 1500 sel := NewRoute( 1501 Equal, 1502 &vindexes.Keyspace{ 1503 Name: "ks", 1504 Sharded: true, 1505 }, 1506 "dummy_select", 1507 "dummy_select_field", 1508 ) 1509 sel.Vindex = vindex 1510 sel.Values = []evalengine.Expr{evalengine.NewLiteralInt(32)} 1511 1512 result, err := sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 1513 require.NoError(t, err) 1514 vc.ExpectLog(t, []string{ 1515 `ResolveDestinationsMultiCol ks [[INT64(32)]] Destinations:DestinationKeyRange(20-21)`, 1516 `ExecuteMultiShard ks.-20: dummy_select {} ks.20-: dummy_select {} false false`, 1517 }) 1518 expectResult(t, "sel.StreamExecute", result, defaultSelectResult) 1519 1520 vc.Rewind() 1521 result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) 1522 require.NoError(t, err) 1523 vc.ExpectLog(t, []string{ 1524 `ResolveDestinationsMultiCol ks [[INT64(32)]] Destinations:DestinationKeyRange(20-21)`, 1525 `StreamExecuteMulti dummy_select ks.-20: {} ks.20-: {} `, 1526 }) 1527 expectResult(t, "sel.StreamExecute", result, defaultSelectResult) 1528 } 1529 1530 func TestINMultiColumnVindex(t *testing.T) { 1531 vindex, _ := vindexes.NewRegionExperimental("", map[string]string{"region_bytes": "1"}) 1532 sel := NewRoute( 1533 IN, 1534 &vindexes.Keyspace{ 1535 Name: "ks", 1536 Sharded: true, 1537 }, 1538 "dummy_select", 1539 "dummy_select_field", 1540 ) 1541 sel.Vindex = vindex 1542 sel.Values = []evalengine.Expr{ 1543 evalengine.NewTupleExpr( 1544 evalengine.NewLiteralInt(1), 1545 evalengine.NewLiteralInt(2), 1546 ), 1547 evalengine.NewTupleExpr( 1548 evalengine.NewLiteralInt(3), 1549 evalengine.NewLiteralInt(4), 1550 ), 1551 } 1552 1553 vc := &loggingVCursor{ 1554 shards: []string{"-20", "20-"}, 1555 shardForKsid: []string{"-20", "20-", "20-", "20-"}, 1556 results: []*sqltypes.Result{defaultSelectResult}, 1557 } 1558 result, err := sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 1559 require.NoError(t, err) 1560 vc.ExpectLog(t, []string{ 1561 `ResolveDestinationsMultiCol ks [[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)`, 1562 `ExecuteMultiShard ks.-20: dummy_select {__vals0: type:TUPLE values:{type:INT64 value:"1"} __vals1: type:TUPLE values:{type:INT64 value:"3"}} ks.20-: dummy_select {__vals0: type:TUPLE values:{type:INT64 value:"1"} values:{type:INT64 value:"2"} __vals1: type:TUPLE values:{type:INT64 value:"4"} values:{type:INT64 value:"3"}} false false`, 1563 }) 1564 expectResult(t, "sel.Execute", result, defaultSelectResult) 1565 1566 vc.Rewind() 1567 result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) 1568 require.NoError(t, err) 1569 vc.ExpectLog(t, []string{ 1570 `ResolveDestinationsMultiCol ks [[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)`, 1571 `StreamExecuteMulti dummy_select ks.-20: {__vals0: type:TUPLE values:{type:INT64 value:"1"} __vals1: type:TUPLE values:{type:INT64 value:"3"}} ks.20-: {__vals0: type:TUPLE values:{type:INT64 value:"1"} values:{type:INT64 value:"2"} __vals1: type:TUPLE values:{type:INT64 value:"4"} values:{type:INT64 value:"3"}} `, 1572 }) 1573 expectResult(t, "sel.StreamExecute", result, defaultSelectResult) 1574 } 1575 1576 func TestINMixedMultiColumnComparision(t *testing.T) { 1577 vindex, _ := vindexes.NewRegionExperimental("", map[string]string{"region_bytes": "1"}) 1578 sel := NewRoute( 1579 IN, 1580 &vindexes.Keyspace{ 1581 Name: "ks", 1582 Sharded: true, 1583 }, 1584 "dummy_select", 1585 "dummy_select_field", 1586 ) 1587 sel.Vindex = vindex 1588 sel.Values = []evalengine.Expr{ 1589 evalengine.NewLiteralInt(1), 1590 evalengine.NewTupleExpr( 1591 evalengine.NewLiteralInt(3), 1592 evalengine.NewLiteralInt(4), 1593 ), 1594 } 1595 1596 vc := &loggingVCursor{ 1597 shards: []string{"-20", "20-"}, 1598 shardForKsid: []string{"-20", "20-"}, 1599 results: []*sqltypes.Result{defaultSelectResult}, 1600 } 1601 result, err := sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 1602 require.NoError(t, err) 1603 vc.ExpectLog(t, []string{ 1604 `ResolveDestinationsMultiCol ks [[INT64(1) INT64(3)] [INT64(1) INT64(4)]] Destinations:DestinationKeyspaceID(014eb190c9a2fa169c),DestinationKeyspaceID(01d2fd8867d50d2dfe)`, 1605 `ExecuteMultiShard ks.-20: dummy_select {__vals1: type:TUPLE values:{type:INT64 value:"3"}} ks.20-: dummy_select {__vals1: type:TUPLE values:{type:INT64 value:"4"}} false false`, 1606 }) 1607 expectResult(t, "sel.Execute", result, defaultSelectResult) 1608 1609 vc.Rewind() 1610 result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) 1611 require.NoError(t, err) 1612 vc.ExpectLog(t, []string{ 1613 `ResolveDestinationsMultiCol ks [[INT64(1) INT64(3)] [INT64(1) INT64(4)]] Destinations:DestinationKeyspaceID(014eb190c9a2fa169c),DestinationKeyspaceID(01d2fd8867d50d2dfe)`, 1614 `StreamExecuteMulti dummy_select ks.-20: {__vals1: type:TUPLE values:{type:INT64 value:"3"}} ks.20-: {__vals1: type:TUPLE values:{type:INT64 value:"4"}} `, 1615 }) 1616 expectResult(t, "sel.StreamExecute", result, defaultSelectResult) 1617 } 1618 1619 func TestMultiEqualMultiCol(t *testing.T) { 1620 vindex, _ := vindexes.NewRegionExperimental("", map[string]string{"region_bytes": "1"}) 1621 sel := NewRoute( 1622 MultiEqual, 1623 &vindexes.Keyspace{Name: "ks", Sharded: true}, 1624 "dummy_select", 1625 "dummy_select_field", 1626 ) 1627 sel.Vindex = vindex 1628 sel.Values = []evalengine.Expr{ 1629 evalengine.NewTupleExpr( 1630 evalengine.NewLiteralInt(1), 1631 evalengine.NewLiteralInt(3), 1632 ), 1633 evalengine.NewTupleExpr( 1634 evalengine.NewLiteralInt(2), 1635 evalengine.NewLiteralInt(4), 1636 ), 1637 } 1638 1639 vc := &loggingVCursor{ 1640 shards: []string{"-20", "20-40", "40-"}, 1641 shardForKsid: []string{"-20", "40-"}, 1642 results: []*sqltypes.Result{defaultSelectResult}, 1643 } 1644 result, err := sel.TryExecute(context.Background(), vc, map[string]*querypb.BindVariable{}, false) 1645 require.NoError(t, err) 1646 vc.ExpectLog(t, []string{ 1647 `ResolveDestinationsMultiCol ks [[INT64(1) INT64(2)] [INT64(3) INT64(4)]] Destinations:DestinationKeyspaceID(0106e7ea22ce92708f),DestinationKeyspaceID(03d2fd8867d50d2dfe)`, 1648 `ExecuteMultiShard ks.-20: dummy_select {} ks.40-: dummy_select {} false false`, 1649 }) 1650 expectResult(t, "sel.Execute", result, defaultSelectResult) 1651 1652 vc.Rewind() 1653 result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false) 1654 require.NoError(t, err) 1655 vc.ExpectLog(t, []string{ 1656 `ResolveDestinationsMultiCol ks [[INT64(1) INT64(2)] [INT64(3) INT64(4)]] Destinations:DestinationKeyspaceID(0106e7ea22ce92708f),DestinationKeyspaceID(03d2fd8867d50d2dfe)`, 1657 `StreamExecuteMulti dummy_select ks.-20: {} ks.40-: {} `, 1658 }) 1659 expectResult(t, "sel.StreamExecute", result, defaultSelectResult) 1660 } 1661 1662 func TestBuildRowColValues(t *testing.T) { 1663 out := buildRowColValues([][]sqltypes.Value{ 1664 {sqltypes.NewInt64(1), sqltypes.NewInt64(10)}, 1665 {sqltypes.NewInt64(2), sqltypes.NewInt64(20)}, 1666 }, []sqltypes.Value{ 1667 sqltypes.NewInt64(3), 1668 sqltypes.NewInt64(4), 1669 }) 1670 1671 require.Len(t, out, 4) 1672 require.EqualValues(t, "[INT64(1) INT64(10) INT64(3)]", fmt.Sprintf("%s", out[0])) 1673 require.EqualValues(t, "[INT64(1) INT64(10) INT64(4)]", fmt.Sprintf("%s", out[1])) 1674 require.EqualValues(t, "[INT64(2) INT64(20) INT64(3)]", fmt.Sprintf("%s", out[2])) 1675 require.EqualValues(t, "[INT64(2) INT64(20) INT64(4)]", fmt.Sprintf("%s", out[3])) 1676 } 1677 1678 func TestBuildMultiColumnVindexValues(t *testing.T) { 1679 testcases := []struct { 1680 input [][][]sqltypes.Value 1681 output [][][]*querypb.Value 1682 }{ 1683 { 1684 input: [][][]sqltypes.Value{ 1685 { 1686 {sqltypes.NewInt64(1), sqltypes.NewInt64(10)}, 1687 {sqltypes.NewInt64(2), sqltypes.NewInt64(20)}, 1688 }, { 1689 {sqltypes.NewInt64(10), sqltypes.NewInt64(10)}, 1690 {sqltypes.NewInt64(20), sqltypes.NewInt64(20)}, 1691 }, 1692 }, 1693 output: [][][]*querypb.Value{ 1694 { 1695 {sqltypes.ValueToProto(sqltypes.NewInt64(1)), sqltypes.ValueToProto(sqltypes.NewInt64(2))}, 1696 {sqltypes.ValueToProto(sqltypes.NewInt64(10)), sqltypes.ValueToProto(sqltypes.NewInt64(20))}, 1697 }, { 1698 {sqltypes.ValueToProto(sqltypes.NewInt64(10)), sqltypes.ValueToProto(sqltypes.NewInt64(20))}, 1699 {sqltypes.ValueToProto(sqltypes.NewInt64(10)), sqltypes.ValueToProto(sqltypes.NewInt64(20))}, 1700 }, 1701 }, 1702 }, { 1703 input: [][][]sqltypes.Value{{ 1704 {sqltypes.NewInt64(10), sqltypes.NewInt64(10), sqltypes.NewInt64(1)}, 1705 {sqltypes.NewInt64(20), sqltypes.NewInt64(20), sqltypes.NewInt64(1)}, 1706 }, 1707 }, 1708 output: [][][]*querypb.Value{{ 1709 {sqltypes.ValueToProto(sqltypes.NewInt64(10)), sqltypes.ValueToProto(sqltypes.NewInt64(20))}, 1710 {sqltypes.ValueToProto(sqltypes.NewInt64(10)), sqltypes.ValueToProto(sqltypes.NewInt64(20))}, 1711 {sqltypes.ValueToProto(sqltypes.NewInt64(1))}, 1712 }, 1713 }, 1714 }, 1715 } 1716 1717 for idx, testcase := range testcases { 1718 t.Run(strconv.Itoa(idx), func(t *testing.T) { 1719 out := buildMultiColumnVindexValues(testcase.input) 1720 require.EqualValues(t, testcase.output, out) 1721 }) 1722 } 1723 }