vitess.io/vitess@v0.16.2/go/vt/vtgate/planbuilder/operators/route.go (about) 1 /* 2 Copyright 2021 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 operators 18 19 import ( 20 "vitess.io/vitess/go/mysql/collations" 21 "vitess.io/vitess/go/vt/key" 22 "vitess.io/vitess/go/vt/sqlparser" 23 "vitess.io/vitess/go/vt/vterrors" 24 "vitess.io/vitess/go/vt/vtgate/engine" 25 "vitess.io/vitess/go/vt/vtgate/evalengine" 26 "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" 27 "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" 28 29 "vitess.io/vitess/go/vt/vtgate/semantics" 30 "vitess.io/vitess/go/vt/vtgate/vindexes" 31 ) 32 33 type ( 34 Route struct { 35 Source ops.Operator 36 37 RouteOpCode engine.Opcode 38 Keyspace *vindexes.Keyspace 39 40 // here we store the possible vindexes we can use so that when we add predicates to the plan, 41 // we can quickly check if the new predicates enables any new vindex Options 42 VindexPreds []*VindexPlusPredicates 43 44 // the best option available is stored here 45 Selected *VindexOption 46 47 // The following two fields are used when routing information_schema queries 48 SysTableTableSchema []evalengine.Expr 49 SysTableTableName map[string]evalengine.Expr 50 51 // SeenPredicates contains all the predicates that have had a chance to influence routing. 52 // If we need to replan routing, we'll use this list 53 SeenPredicates []sqlparser.Expr 54 55 // TargetDestination specifies an explicit target destination tablet type 56 TargetDestination key.Destination 57 58 // Alternates contains alternate routes to equivalent sources in 59 // other keyspaces. 60 Alternates map[*vindexes.Keyspace]*Route 61 62 // Routes that have been merged into this one. 63 MergedWith []*Route 64 } 65 66 // VindexPlusPredicates is a struct used to store all the predicates that the vindex can be used to query 67 VindexPlusPredicates struct { 68 TableID semantics.TableSet 69 ColVindex *vindexes.ColumnVindex 70 71 // during planning, we store the alternatives found for this route in this slice 72 Options []*VindexOption 73 } 74 75 // VindexOption stores the information needed to know if we have all the information needed to use a vindex 76 VindexOption struct { 77 Ready bool 78 Values []evalengine.Expr 79 // columns that we have seen so far. Used only for multi-column vindexes so that we can track how many columns part of the vindex we have seen 80 ColsSeen map[string]any 81 ValueExprs []sqlparser.Expr 82 Predicates []sqlparser.Expr 83 OpCode engine.Opcode 84 FoundVindex vindexes.Vindex 85 Cost Cost 86 } 87 88 // Cost is used to make it easy to compare the Cost of two plans with each other 89 Cost struct { 90 VindexCost int 91 IsUnique bool 92 OpCode engine.Opcode 93 } 94 ) 95 96 var _ ops.PhysicalOperator = (*Route)(nil) 97 98 // IPhysical implements the PhysicalOperator interface 99 func (*Route) IPhysical() {} 100 101 // Cost implements the Operator interface 102 func (r *Route) Cost() int { 103 switch r.RouteOpCode { 104 case // these op codes will never be compared with each other - they are assigned by a rule and not a comparison 105 engine.DBA, 106 engine.Next, 107 engine.None, 108 engine.Reference, 109 engine.Unsharded: 110 return 0 111 // TODO revisit these costs when more of the gen4 planner is done 112 case engine.EqualUnique: 113 return 1 114 case engine.Equal: 115 return 5 116 case engine.IN: 117 return 10 118 case engine.MultiEqual: 119 return 10 120 case engine.Scatter: 121 return 20 122 } 123 return 1 124 } 125 126 // Clone implements the Operator interface 127 func (r *Route) Clone(inputs []ops.Operator) ops.Operator { 128 cloneRoute := *r 129 cloneRoute.Source = inputs[0] 130 cloneRoute.VindexPreds = make([]*VindexPlusPredicates, len(r.VindexPreds)) 131 for i, pred := range r.VindexPreds { 132 // we do this to create a copy of the struct 133 p := *pred 134 cloneRoute.VindexPreds[i] = &p 135 } 136 return &cloneRoute 137 } 138 139 // Inputs implements the Operator interface 140 func (r *Route) Inputs() []ops.Operator { 141 return []ops.Operator{r.Source} 142 } 143 144 func (r *Route) UpdateRoutingLogic(ctx *plancontext.PlanningContext, expr sqlparser.Expr) error { 145 r.SeenPredicates = append(r.SeenPredicates, expr) 146 return r.tryImprovingVindex(ctx, expr) 147 } 148 149 func (r *Route) tryImprovingVindex(ctx *plancontext.PlanningContext, expr sqlparser.Expr) error { 150 if r.canImprove() { 151 newVindexFound, err := r.searchForNewVindexes(ctx, expr) 152 if err != nil { 153 return err 154 } 155 156 // if we didn't open up any new vindex Options, no need to enter here 157 if newVindexFound { 158 r.PickBestAvailableVindex() 159 } 160 } 161 return nil 162 } 163 164 func (r *Route) searchForNewVindexes(ctx *plancontext.PlanningContext, predicate sqlparser.Expr) (bool, error) { 165 newVindexFound := false 166 switch node := predicate.(type) { 167 case *sqlparser.ExtractedSubquery: 168 originalCmp, ok := node.Original.(*sqlparser.ComparisonExpr) 169 if !ok { 170 break 171 } 172 173 // using the node.subquery which is the rewritten version of our subquery 174 cmp := &sqlparser.ComparisonExpr{ 175 Left: node.OtherSide, 176 Right: &sqlparser.Subquery{Select: node.Subquery.Select}, 177 Operator: originalCmp.Operator, 178 } 179 found, exitEarly, err := r.planComparison(ctx, cmp) 180 if err != nil || exitEarly { 181 return false, err 182 } 183 newVindexFound = newVindexFound || found 184 185 case *sqlparser.ComparisonExpr: 186 found, exitEarly, err := r.planComparison(ctx, node) 187 if err != nil || exitEarly { 188 return false, err 189 } 190 newVindexFound = newVindexFound || found 191 case *sqlparser.IsExpr: 192 found := r.planIsExpr(ctx, node) 193 newVindexFound = newVindexFound || found 194 } 195 return newVindexFound, nil 196 } 197 198 func (r *Route) planComparison(ctx *plancontext.PlanningContext, cmp *sqlparser.ComparisonExpr) (found bool, exitEarly bool, err error) { 199 if cmp.Operator != sqlparser.NullSafeEqualOp && (sqlparser.IsNull(cmp.Left) || sqlparser.IsNull(cmp.Right)) { 200 // we are looking at ANDed predicates in the WHERE clause. 201 // since we know that nothing returns true when compared to NULL, 202 // so we can safely bail out here 203 r.setSelectNoneOpcode() 204 return false, true, nil 205 } 206 207 switch cmp.Operator { 208 case sqlparser.EqualOp: 209 found := r.planEqualOp(ctx, cmp) 210 return found, false, nil 211 case sqlparser.InOp: 212 if r.isImpossibleIN(cmp) { 213 return false, true, nil 214 } 215 found := r.planInOp(ctx, cmp) 216 return found, false, nil 217 case sqlparser.NotInOp: 218 // NOT IN is always a scatter, except when we can be sure it would return nothing 219 if r.isImpossibleNotIN(cmp) { 220 return false, true, nil 221 } 222 case sqlparser.LikeOp: 223 found := r.planLikeOp(ctx, cmp) 224 return found, false, nil 225 226 } 227 return false, false, nil 228 } 229 230 func (r *Route) setSelectNoneOpcode() { 231 r.RouteOpCode = engine.None 232 // clear any chosen vindex as this query does not need to be sent down. 233 r.Selected = nil 234 } 235 236 func (r *Route) planEqualOp(ctx *plancontext.PlanningContext, node *sqlparser.ComparisonExpr) bool { 237 column, ok := node.Left.(*sqlparser.ColName) 238 other := node.Right 239 vdValue := other 240 if !ok { 241 column, ok = node.Right.(*sqlparser.ColName) 242 if !ok { 243 // either the LHS or RHS have to be a column to be useful for the vindex 244 return false 245 } 246 vdValue = node.Left 247 } 248 val := r.makeEvalEngineExpr(ctx, vdValue) 249 if val == nil { 250 return false 251 } 252 253 return r.haveMatchingVindex(ctx, node, vdValue, column, val, equalOrEqualUnique, justTheVindex) 254 } 255 256 // makePlanValue transforms the given sqlparser.Expr into a sqltypes.PlanValue. 257 // If the given sqlparser.Expr is an argument and can be found in the r.argToReplaceBySelect then the 258 // method will stops and return nil values. 259 // Otherwise, the method will try to apply makePlanValue for any equality the sqlparser.Expr n has. 260 // The first PlanValue that is successfully produced will be returned. 261 func (r *Route) makeEvalEngineExpr(ctx *plancontext.PlanningContext, n sqlparser.Expr) evalengine.Expr { 262 if ctx.IsSubQueryToReplace(n) { 263 return nil 264 } 265 266 for _, expr := range ctx.SemTable.GetExprAndEqualities(n) { 267 if subq, isSubq := expr.(*sqlparser.Subquery); isSubq { 268 extractedSubquery := ctx.SemTable.FindSubqueryReference(subq) 269 if extractedSubquery == nil { 270 continue 271 } 272 switch engine.PulloutOpcode(extractedSubquery.OpCode) { 273 case engine.PulloutIn, engine.PulloutNotIn: 274 expr = sqlparser.NewListArg(extractedSubquery.GetArgName()) 275 case engine.PulloutValue, engine.PulloutExists: 276 expr = sqlparser.NewArgument(extractedSubquery.GetArgName()) 277 } 278 } 279 pv, _ := evalengine.Translate(expr, ctx.SemTable) 280 if pv != nil { 281 return pv 282 } 283 } 284 285 return nil 286 } 287 288 func (r *Route) hasVindex(column *sqlparser.ColName) bool { 289 for _, v := range r.VindexPreds { 290 for _, col := range v.ColVindex.Columns { 291 if column.Name.Equal(col) { 292 return true 293 } 294 } 295 } 296 return false 297 } 298 299 func (r *Route) haveMatchingVindex( 300 ctx *plancontext.PlanningContext, 301 node sqlparser.Expr, 302 valueExpr sqlparser.Expr, 303 column *sqlparser.ColName, 304 value evalengine.Expr, 305 opcode func(*vindexes.ColumnVindex) engine.Opcode, 306 vfunc func(*vindexes.ColumnVindex) vindexes.Vindex, 307 ) bool { 308 newVindexFound := false 309 for _, v := range r.VindexPreds { 310 // check that the 311 if !ctx.SemTable.DirectDeps(column).IsSolvedBy(v.TableID) { 312 continue 313 } 314 switch v.ColVindex.Vindex.(type) { 315 case vindexes.SingleColumn: 316 col := v.ColVindex.Columns[0] 317 if column.Name.Equal(col) { 318 // single column vindex - just add the option 319 routeOpcode := opcode(v.ColVindex) 320 vindex := vfunc(v.ColVindex) 321 if vindex == nil || routeOpcode == engine.Scatter { 322 continue 323 } 324 v.Options = append(v.Options, &VindexOption{ 325 Values: []evalengine.Expr{value}, 326 ValueExprs: []sqlparser.Expr{valueExpr}, 327 Predicates: []sqlparser.Expr{node}, 328 OpCode: routeOpcode, 329 FoundVindex: vindex, 330 Cost: costFor(v.ColVindex, routeOpcode), 331 Ready: true, 332 }) 333 newVindexFound = true 334 } 335 case vindexes.MultiColumn: 336 colLoweredName := "" 337 indexOfCol := -1 338 for idx, col := range v.ColVindex.Columns { 339 if column.Name.Equal(col) { 340 colLoweredName = column.Name.Lowered() 341 indexOfCol = idx 342 break 343 } 344 } 345 if colLoweredName == "" { 346 break 347 } 348 349 var newOption []*VindexOption 350 for _, op := range v.Options { 351 if op.Ready { 352 continue 353 } 354 _, isPresent := op.ColsSeen[colLoweredName] 355 if isPresent { 356 continue 357 } 358 option := copyOption(op) 359 optionReady := option.updateWithNewColumn(colLoweredName, valueExpr, indexOfCol, value, node, v.ColVindex, opcode) 360 if optionReady { 361 newVindexFound = true 362 } 363 newOption = append(newOption, option) 364 } 365 v.Options = append(v.Options, newOption...) 366 367 // multi column vindex - just always add as new option 368 option := createOption(v.ColVindex, vfunc) 369 optionReady := option.updateWithNewColumn(colLoweredName, valueExpr, indexOfCol, value, node, v.ColVindex, opcode) 370 if optionReady { 371 newVindexFound = true 372 } 373 v.Options = append(v.Options, option) 374 } 375 } 376 return newVindexFound 377 } 378 379 func createOption( 380 colVindex *vindexes.ColumnVindex, 381 vfunc func(*vindexes.ColumnVindex) vindexes.Vindex, 382 ) *VindexOption { 383 values := make([]evalengine.Expr, len(colVindex.Columns)) 384 predicates := make([]sqlparser.Expr, len(colVindex.Columns)) 385 vindex := vfunc(colVindex) 386 387 return &VindexOption{ 388 Values: values, 389 Predicates: predicates, 390 ColsSeen: map[string]any{}, 391 FoundVindex: vindex, 392 } 393 } 394 395 func copyOption(orig *VindexOption) *VindexOption { 396 colsSeen := make(map[string]any, len(orig.ColsSeen)) 397 valueExprs := make([]sqlparser.Expr, len(orig.ValueExprs)) 398 values := make([]evalengine.Expr, len(orig.Values)) 399 predicates := make([]sqlparser.Expr, len(orig.Predicates)) 400 401 copy(values, orig.Values) 402 copy(valueExprs, orig.ValueExprs) 403 copy(predicates, orig.Predicates) 404 for k, v := range orig.ColsSeen { 405 colsSeen[k] = v 406 } 407 vo := &VindexOption{ 408 Values: values, 409 ColsSeen: colsSeen, 410 ValueExprs: valueExprs, 411 Predicates: predicates, 412 OpCode: orig.OpCode, 413 FoundVindex: orig.FoundVindex, 414 Cost: orig.Cost, 415 } 416 return vo 417 } 418 419 func (option *VindexOption) updateWithNewColumn( 420 colLoweredName string, 421 valueExpr sqlparser.Expr, 422 indexOfCol int, 423 value evalengine.Expr, 424 node sqlparser.Expr, 425 colVindex *vindexes.ColumnVindex, 426 opcode func(*vindexes.ColumnVindex) engine.Opcode, 427 ) bool { 428 option.ColsSeen[colLoweredName] = true 429 option.ValueExprs = append(option.ValueExprs, valueExpr) 430 option.Values[indexOfCol] = value 431 option.Predicates[indexOfCol] = node 432 option.Ready = len(option.ColsSeen) == len(colVindex.Columns) 433 routeOpcode := opcode(colVindex) 434 if option.OpCode < routeOpcode { 435 option.OpCode = routeOpcode 436 option.Cost = costFor(colVindex, routeOpcode) 437 } 438 return option.Ready 439 } 440 441 // PickBestAvailableVindex goes over the available vindexes for this route and picks the best one available. 442 func (r *Route) PickBestAvailableVindex() { 443 for _, v := range r.VindexPreds { 444 option := v.bestOption() 445 if option != nil && (r.Selected == nil || less(option.Cost, r.Selected.Cost)) { 446 r.Selected = option 447 r.RouteOpCode = option.OpCode 448 } 449 } 450 } 451 452 // canImprove returns true if additional predicates could help improving this plan 453 func (r *Route) canImprove() bool { 454 return r.RouteOpCode != engine.None 455 } 456 457 func (r *Route) IsSingleShard() bool { 458 switch r.RouteOpCode { 459 case engine.Unsharded, engine.DBA, engine.Next, engine.EqualUnique, engine.Reference: 460 return true 461 } 462 return false 463 } 464 465 func (r *Route) SelectedVindex() vindexes.Vindex { 466 if r.Selected == nil { 467 return nil 468 } 469 return r.Selected.FoundVindex 470 } 471 472 func (r *Route) VindexExpressions() []sqlparser.Expr { 473 if r.Selected == nil { 474 return nil 475 } 476 return r.Selected.ValueExprs 477 } 478 479 func (r *Route) isImpossibleIN(node *sqlparser.ComparisonExpr) bool { 480 switch nodeR := node.Right.(type) { 481 case sqlparser.ValTuple: 482 // WHERE col IN (null) 483 if len(nodeR) == 1 && sqlparser.IsNull(nodeR[0]) { 484 r.setSelectNoneOpcode() 485 return true 486 } 487 } 488 return false 489 } 490 491 func (r *Route) planInOp(ctx *plancontext.PlanningContext, cmp *sqlparser.ComparisonExpr) bool { 492 switch left := cmp.Left.(type) { 493 case *sqlparser.ColName: 494 vdValue := cmp.Right 495 496 valTuple, isTuple := vdValue.(sqlparser.ValTuple) 497 if isTuple && len(valTuple) == 1 { 498 return r.planEqualOp(ctx, &sqlparser.ComparisonExpr{Left: left, Right: valTuple[0], Operator: sqlparser.EqualOp}) 499 } 500 501 value := r.makeEvalEngineExpr(ctx, vdValue) 502 if value == nil { 503 return false 504 } 505 opcode := func(*vindexes.ColumnVindex) engine.Opcode { return engine.IN } 506 return r.haveMatchingVindex(ctx, cmp, vdValue, left, value, opcode, justTheVindex) 507 case sqlparser.ValTuple: 508 right, rightIsValTuple := cmp.Right.(sqlparser.ValTuple) 509 if !rightIsValTuple { 510 return false 511 } 512 return r.planCompositeInOpRecursive(ctx, cmp, left, right, nil) 513 } 514 515 return false 516 } 517 518 func (r *Route) isImpossibleNotIN(node *sqlparser.ComparisonExpr) bool { 519 switch node := node.Right.(type) { 520 case sqlparser.ValTuple: 521 for _, n := range node { 522 if sqlparser.IsNull(n) { 523 r.setSelectNoneOpcode() 524 return true 525 } 526 } 527 } 528 529 return false 530 } 531 532 func (r *Route) planLikeOp(ctx *plancontext.PlanningContext, node *sqlparser.ComparisonExpr) bool { 533 column, ok := node.Left.(*sqlparser.ColName) 534 if !ok { 535 return false 536 } 537 538 vdValue := node.Right 539 val := r.makeEvalEngineExpr(ctx, vdValue) 540 if val == nil { 541 return false 542 } 543 selectEqual := func(*vindexes.ColumnVindex) engine.Opcode { return engine.Equal } 544 vdx := func(vindex *vindexes.ColumnVindex) vindexes.Vindex { 545 if prefixable, ok := vindex.Vindex.(vindexes.Prefixable); ok { 546 return prefixable.PrefixVindex() 547 } 548 549 // if we can't use the vindex as a prefix-vindex, we can't use this vindex at all 550 return nil 551 } 552 return r.haveMatchingVindex(ctx, node, vdValue, column, val, selectEqual, vdx) 553 554 } 555 556 func (r *Route) planCompositeInOpRecursive( 557 ctx *plancontext.PlanningContext, 558 cmp *sqlparser.ComparisonExpr, 559 left, right sqlparser.ValTuple, 560 coordinates []int, 561 ) bool { 562 foundVindex := false 563 cindex := len(coordinates) 564 coordinates = append(coordinates, 0) 565 for i, expr := range left { 566 coordinates[cindex] = i 567 switch expr := expr.(type) { 568 case sqlparser.ValTuple: 569 ok := r.planCompositeInOpRecursive(ctx, cmp, expr, right, coordinates) 570 return ok || foundVindex 571 case *sqlparser.ColName: 572 // check if left col is a vindex 573 if !r.hasVindex(expr) { 574 continue 575 } 576 577 rightVals := make(sqlparser.ValTuple, len(right)) 578 for j, currRight := range right { 579 switch currRight := currRight.(type) { 580 case sqlparser.ValTuple: 581 val := tupleAccess(currRight, coordinates) 582 if val == nil { 583 return false 584 } 585 rightVals[j] = val 586 default: 587 return false 588 } 589 } 590 newPlanValues := r.makeEvalEngineExpr(ctx, rightVals) 591 if newPlanValues == nil { 592 return false 593 } 594 595 opcode := func(*vindexes.ColumnVindex) engine.Opcode { return engine.MultiEqual } 596 newVindex := r.haveMatchingVindex(ctx, cmp, rightVals, expr, newPlanValues, opcode, justTheVindex) 597 foundVindex = newVindex || foundVindex 598 } 599 } 600 return foundVindex 601 } 602 603 // Reset all vindex predicates on this route and re-build their options from 604 // the list of seen routing predicates. 605 func (r *Route) resetRoutingSelections(ctx *plancontext.PlanningContext) error { 606 switch r.RouteOpCode { 607 case engine.DBA, engine.Next, engine.Reference, engine.Unsharded: 608 // these we keep as is 609 default: 610 r.RouteOpCode = engine.Scatter 611 } 612 613 r.Selected = nil 614 for i, vp := range r.VindexPreds { 615 r.VindexPreds[i] = &VindexPlusPredicates{ColVindex: vp.ColVindex, TableID: vp.TableID} 616 } 617 618 for _, predicate := range r.SeenPredicates { 619 err := r.tryImprovingVindex(ctx, predicate) 620 if err != nil { 621 return err 622 } 623 } 624 return nil 625 } 626 627 func tupleAccess(expr sqlparser.Expr, coordinates []int) sqlparser.Expr { 628 tuple, _ := expr.(sqlparser.ValTuple) 629 for _, idx := range coordinates { 630 if idx >= len(tuple) { 631 return nil 632 } 633 expr = tuple[idx] 634 tuple, _ = expr.(sqlparser.ValTuple) 635 } 636 return expr 637 } 638 639 func equalOrEqualUnique(vindex *vindexes.ColumnVindex) engine.Opcode { 640 if vindex.IsPartialVindex() { 641 return engine.SubShard 642 } 643 if vindex.IsUnique() { 644 return engine.EqualUnique 645 } 646 647 return engine.Equal 648 } 649 650 func justTheVindex(vindex *vindexes.ColumnVindex) vindexes.Vindex { 651 return vindex.Vindex 652 } 653 654 // costFor returns a cost struct to make route choices easier to compare 655 func costFor(foundVindex *vindexes.ColumnVindex, opcode engine.Opcode) Cost { 656 switch opcode { 657 // For these opcodes, we should not have a vindex, so we just return the opcode as the cost 658 case engine.Unsharded, engine.Next, engine.DBA, engine.Reference, engine.None, engine.Scatter: 659 return Cost{ 660 OpCode: opcode, 661 } 662 } 663 664 return Cost{ 665 VindexCost: foundVindex.Cost(), 666 IsUnique: foundVindex.IsUnique(), 667 OpCode: opcode, 668 } 669 } 670 671 // less compares two costs and returns true if the first cost is cheaper than the second 672 func less(c1, c2 Cost) bool { 673 switch { 674 case c1.OpCode != c2.OpCode: 675 return c1.OpCode < c2.OpCode 676 case c1.IsUnique == c2.IsUnique: 677 return c1.VindexCost <= c2.VindexCost 678 default: 679 return c1.IsUnique 680 } 681 } 682 683 func (vpp *VindexPlusPredicates) bestOption() *VindexOption { 684 var best *VindexOption 685 var keepOptions []*VindexOption 686 for _, option := range vpp.Options { 687 if option.Ready { 688 if best == nil || less(option.Cost, best.Cost) { 689 best = option 690 } 691 } else { 692 keepOptions = append(keepOptions, option) 693 } 694 } 695 if best != nil { 696 keepOptions = append(keepOptions, best) 697 } 698 vpp.Options = keepOptions 699 return best 700 } 701 702 func (r *Route) planIsExpr(ctx *plancontext.PlanningContext, node *sqlparser.IsExpr) bool { 703 // we only handle IS NULL correct. IsExpr can contain other expressions as well 704 if node.Right != sqlparser.IsNullOp { 705 return false 706 } 707 column, ok := node.Left.(*sqlparser.ColName) 708 if !ok { 709 return false 710 } 711 vdValue := &sqlparser.NullVal{} 712 val := r.makeEvalEngineExpr(ctx, vdValue) 713 if val == nil { 714 return false 715 } 716 opcodeF := func(vindex *vindexes.ColumnVindex) engine.Opcode { 717 if _, ok := vindex.Vindex.(vindexes.Lookup); ok { 718 return engine.Scatter 719 } 720 return equalOrEqualUnique(vindex) 721 } 722 723 return r.haveMatchingVindex(ctx, node, vdValue, column, val, opcodeF, justTheVindex) 724 } 725 726 // createRoute returns either an information_schema route, or else consults the 727 // VSchema to find a suitable table, and then creates a route from that. 728 func createRoute( 729 ctx *plancontext.PlanningContext, 730 queryTable *QueryTable, 731 solves semantics.TableSet, 732 ) (ops.Operator, error) { 733 if queryTable.IsInfSchema { 734 return createInfSchemaRoute(ctx, queryTable) 735 } 736 return findVSchemaTableAndCreateRoute(ctx, queryTable, queryTable.Table, solves, true /*planAlternates*/) 737 } 738 739 // findVSchemaTableAndCreateRoute consults the VSchema to find a suitable 740 // table, and then creates a route from that. 741 func findVSchemaTableAndCreateRoute( 742 ctx *plancontext.PlanningContext, 743 queryTable *QueryTable, 744 tableName sqlparser.TableName, 745 solves semantics.TableSet, 746 planAlternates bool, 747 ) (*Route, error) { 748 vschemaTable, _, _, _, target, err := ctx.VSchema.FindTableOrVindex(tableName) 749 if target != nil { 750 return nil, vterrors.VT12001("SELECT with a target destination") 751 } 752 if err != nil { 753 return nil, err 754 } 755 756 return createRouteFromVSchemaTable( 757 ctx, 758 queryTable, 759 vschemaTable, 760 solves, 761 planAlternates, 762 ) 763 } 764 765 // createRouteFromTable creates a route from the given VSchema table. 766 func createRouteFromVSchemaTable( 767 ctx *plancontext.PlanningContext, 768 queryTable *QueryTable, 769 vschemaTable *vindexes.Table, 770 solves semantics.TableSet, 771 planAlternates bool, 772 ) (*Route, error) { 773 if vschemaTable.Name.String() != queryTable.Table.Name.String() { 774 // we are dealing with a routed table 775 queryTable = queryTable.Clone() 776 name := queryTable.Table.Name 777 queryTable.Table.Name = vschemaTable.Name 778 astTable, ok := queryTable.Alias.Expr.(sqlparser.TableName) 779 if !ok { 780 return nil, vterrors.VT13001("a derived table should never be a routed table") 781 } 782 realTableName := sqlparser.NewIdentifierCS(vschemaTable.Name.String()) 783 astTable.Name = realTableName 784 if queryTable.Alias.As.IsEmpty() { 785 // if the user hasn't specified an alias, we'll insert one here so the old table name still works 786 queryTable.Alias.As = sqlparser.NewIdentifierCS(name.String()) 787 } 788 } 789 plan := &Route{ 790 Source: &Table{ 791 QTable: queryTable, 792 VTable: vschemaTable, 793 }, 794 Keyspace: vschemaTable.Keyspace, 795 } 796 797 for _, columnVindex := range vschemaTable.ColumnVindexes { 798 plan.VindexPreds = append(plan.VindexPreds, &VindexPlusPredicates{ColVindex: columnVindex, TableID: solves}) 799 } 800 801 switch { 802 case vschemaTable.Type == vindexes.TypeSequence: 803 plan.RouteOpCode = engine.Next 804 case vschemaTable.Type == vindexes.TypeReference: 805 plan.RouteOpCode = engine.Reference 806 case !vschemaTable.Keyspace.Sharded: 807 plan.RouteOpCode = engine.Unsharded 808 case vschemaTable.Pinned != nil: 809 // Pinned tables have their keyspace ids already assigned. 810 // Use the Binary vindex, which is the identity function 811 // for keyspace id. 812 plan.RouteOpCode = engine.EqualUnique 813 vindex, _ := vindexes.NewBinary("binary", nil) 814 plan.Selected = &VindexOption{ 815 Ready: true, 816 Values: []evalengine.Expr{evalengine.NewLiteralString(vschemaTable.Pinned, collations.TypedCollation{})}, 817 ValueExprs: nil, 818 Predicates: nil, 819 OpCode: engine.EqualUnique, 820 FoundVindex: vindex, 821 Cost: Cost{ 822 OpCode: engine.EqualUnique, 823 }, 824 } 825 default: 826 plan.RouteOpCode = engine.Scatter 827 } 828 for _, predicate := range queryTable.Predicates { 829 err := plan.UpdateRoutingLogic(ctx, predicate) 830 if err != nil { 831 return nil, err 832 } 833 } 834 835 if plan.RouteOpCode == engine.Scatter && len(queryTable.Predicates) > 0 { 836 // If we have a scatter query, it's worth spending a little extra time seeing if we can't improve it 837 oldPredicates := queryTable.Predicates 838 queryTable.Predicates = nil 839 plan.SeenPredicates = nil 840 for _, pred := range oldPredicates { 841 rewritten := sqlparser.RewritePredicate(pred) 842 predicates := sqlparser.SplitAndExpression(nil, rewritten.(sqlparser.Expr)) 843 for _, predicate := range predicates { 844 queryTable.Predicates = append(queryTable.Predicates, predicate) 845 err := plan.UpdateRoutingLogic(ctx, predicate) 846 if err != nil { 847 return nil, err 848 } 849 } 850 } 851 852 if plan.RouteOpCode == engine.Scatter { 853 // if we _still_ haven't found a better route, we can run this additional rewrite on any ORs we have 854 for _, expr := range queryTable.Predicates { 855 or, ok := expr.(*sqlparser.OrExpr) 856 if !ok { 857 continue 858 } 859 for _, predicate := range sqlparser.ExtractINFromOR(or) { 860 err := plan.UpdateRoutingLogic(ctx, predicate) 861 if err != nil { 862 return nil, err 863 } 864 } 865 } 866 } 867 } 868 869 if planAlternates { 870 alternates, err := createAlternateRoutesFromVSchemaTable( 871 ctx, 872 queryTable, 873 vschemaTable, 874 solves, 875 ) 876 if err != nil { 877 return nil, err 878 } 879 plan.Alternates = alternates 880 } 881 882 return plan, nil 883 } 884 885 func createAlternateRoutesFromVSchemaTable( 886 ctx *plancontext.PlanningContext, 887 queryTable *QueryTable, 888 vschemaTable *vindexes.Table, 889 solves semantics.TableSet, 890 ) (map[*vindexes.Keyspace]*Route, error) { 891 routes := make(map[*vindexes.Keyspace]*Route) 892 893 switch vschemaTable.Type { 894 case "", vindexes.TypeReference: 895 for ksName, referenceTable := range vschemaTable.ReferencedBy { 896 route, err := findVSchemaTableAndCreateRoute( 897 ctx, 898 queryTable, 899 sqlparser.TableName{ 900 Name: referenceTable.Name, 901 Qualifier: sqlparser.NewIdentifierCS(ksName), 902 }, 903 solves, 904 false, /*planAlternates*/ 905 ) 906 if err != nil { 907 return nil, err 908 } 909 routes[route.Keyspace] = route 910 } 911 912 if vschemaTable.Source != nil { 913 route, err := findVSchemaTableAndCreateRoute( 914 ctx, 915 queryTable, 916 vschemaTable.Source.TableName, 917 solves, 918 false, /*planAlternates*/ 919 ) 920 if err != nil { 921 return nil, err 922 } 923 routes[route.Keyspace] = route 924 } 925 } 926 927 return routes, nil 928 } 929 930 func (r *Route) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) (ops.Operator, error) { 931 err := r.UpdateRoutingLogic(ctx, expr) 932 if err != nil { 933 return nil, err 934 } 935 newSrc, err := r.Source.AddPredicate(ctx, expr) 936 if err != nil { 937 return nil, err 938 } 939 r.Source = newSrc 940 return r, err 941 } 942 943 func (r *Route) AddColumn(ctx *plancontext.PlanningContext, e sqlparser.Expr) (int, error) { 944 return r.Source.AddColumn(ctx, e) 945 } 946 947 func (r *Route) AlternateInKeyspace(keyspace *vindexes.Keyspace) *Route { 948 if keyspace.Name == r.Keyspace.Name { 949 return nil 950 } 951 952 if route, ok := r.Alternates[keyspace]; ok { 953 return route 954 } 955 956 return nil 957 } 958 959 // TablesUsed returns tables used by MergedWith routes, which are not included 960 // in Inputs() and thus not a part of the operator tree 961 func (r *Route) TablesUsed() []string { 962 addString, collect := collectSortedUniqueStrings() 963 for _, mw := range r.MergedWith { 964 for _, u := range TablesUsed(mw) { 965 addString(u) 966 } 967 } 968 return collect() 969 }