vitess.io/vitess@v0.16.2/go/vt/vtgate/planbuilder/route.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 planbuilder 18 19 import ( 20 "fmt" 21 "strconv" 22 23 "vitess.io/vitess/go/mysql/collations" 24 "vitess.io/vitess/go/vt/vterrors" 25 "vitess.io/vitess/go/vt/vtgate/evalengine" 26 "vitess.io/vitess/go/vt/vtgate/semantics" 27 28 "vitess.io/vitess/go/vt/sqlparser" 29 "vitess.io/vitess/go/vt/vtgate/engine" 30 "vitess.io/vitess/go/vt/vtgate/vindexes" 31 ) 32 33 var _ logicalPlan = (*route)(nil) 34 35 // route is used to build a Route primitive. 36 // It's used to build one of the Select routes like 37 // SelectScatter, etc. Portions of the original Select AST 38 // are moved into this node, which will be used to build 39 // the final SQL for this route. 40 type route struct { 41 v3Plan 42 order int 43 44 // Redirect may point to another route if this route 45 // was merged with it. The Resolve function chases 46 // this pointer till the last un-redirected route. 47 Redirect *route 48 49 // Select is the AST for the query fragment that will be 50 // executed by this route. 51 Select sqlparser.SelectStatement 52 53 // resultColumns represent the columns returned by this route. 54 resultColumns []*resultColumn 55 56 // weight_string keeps track of the weight_string expressions 57 // that were added additionally for each column. These expressions 58 // are added to be used for collation of text columns. 59 weightStrings map[*resultColumn]int 60 61 // substitutions contain the list of table expressions that 62 // have to be substituted in the route's query. 63 substitutions []*tableSubstitution 64 65 // condition stores the AST condition that will be used 66 // to resolve the ERoute Values field. 67 condition sqlparser.Expr 68 69 // eroute is the primitive being built. 70 eroute *engine.Route 71 } 72 73 type tableSubstitution struct { 74 newExpr, oldExpr *sqlparser.AliasedTableExpr 75 } 76 77 func newRoute(stmt sqlparser.SelectStatement) (*route, *symtab) { 78 rb := &route{ 79 Select: stmt, 80 order: 1, 81 weightStrings: make(map[*resultColumn]int), 82 } 83 return rb, newSymtabWithRoute(rb) 84 } 85 86 // Resolve resolves redirects, and returns the last 87 // un-redirected route. 88 func (rb *route) Resolve() *route { 89 for rb.Redirect != nil { 90 rb = rb.Redirect 91 } 92 return rb 93 } 94 95 // Order implements the logicalPlan interface 96 func (rb *route) Order() int { 97 return rb.order 98 } 99 100 // Reorder implements the logicalPlan interface 101 func (rb *route) Reorder(order int) { 102 rb.order = order + 1 103 } 104 105 // Primitive implements the logicalPlan interface 106 func (rb *route) Primitive() engine.Primitive { 107 return rb.eroute 108 } 109 110 // ResultColumns implements the logicalPlan interface 111 func (rb *route) ResultColumns() []*resultColumn { 112 return rb.resultColumns 113 } 114 115 // PushAnonymous pushes an anonymous expression like '*' or NEXT VALUES 116 // into the select expression list of the route. This function is 117 // similar to PushSelect. 118 func (rb *route) PushAnonymous(expr sqlparser.SelectExpr) *resultColumn { 119 // TODO: we should not assume that the query is a SELECT 120 sel := rb.Select.(*sqlparser.Select) 121 sel.SelectExprs = append(sel.SelectExprs, expr) 122 123 // We just create a place-holder resultColumn. It won't 124 // match anything. 125 rc := &resultColumn{column: &column{origin: rb}} 126 rb.resultColumns = append(rb.resultColumns, rc) 127 128 return rc 129 } 130 131 // SetLimit adds a LIMIT clause to the route. 132 func (rb *route) SetLimit(limit *sqlparser.Limit) { 133 rb.Select.SetLimit(limit) 134 } 135 136 // Wireup implements the logicalPlan interface 137 func (rb *route) Wireup(plan logicalPlan, jt *jointab) error { 138 // Precaution: update ERoute.Values only if it's not set already. 139 if rb.eroute.Values == nil { 140 // Resolve values stored in the logical plan. 141 switch vals := rb.condition.(type) { 142 case *sqlparser.ComparisonExpr: 143 pv, err := rb.procureValues(plan, jt, vals.Right) 144 if err != nil { 145 return err 146 } 147 rb.eroute.Values = []evalengine.Expr{pv} 148 vals.Right = sqlparser.ListArg(engine.ListVarName) 149 case nil: 150 // no-op. 151 default: 152 pv, err := rb.procureValues(plan, jt, vals) 153 if err != nil { 154 return err 155 } 156 rb.eroute.Values = []evalengine.Expr{pv} 157 } 158 } 159 160 // Fix up the AST. 161 _ = sqlparser.Walk(func(node sqlparser.SQLNode) (bool, error) { 162 switch node := node.(type) { 163 case *sqlparser.Select: 164 if len(node.SelectExprs) == 0 { 165 node.SelectExprs = []sqlparser.SelectExpr{ 166 &sqlparser.AliasedExpr{ 167 Expr: sqlparser.NewIntLiteral("1"), 168 }, 169 } 170 } 171 case *sqlparser.ComparisonExpr: 172 if node.Operator == sqlparser.EqualOp { 173 if rb.exprIsValue(node.Left) && !rb.exprIsValue(node.Right) { 174 node.Left, node.Right = node.Right, node.Left 175 } 176 } 177 } 178 return true, nil 179 }, rb.Select) 180 181 // Substitute table names 182 for _, sub := range rb.substitutions { 183 *sub.oldExpr = *sub.newExpr 184 } 185 186 // Generate query while simultaneously resolving values. 187 varFormatter := func(buf *sqlparser.TrackedBuffer, node sqlparser.SQLNode) { 188 switch node := node.(type) { 189 case *sqlparser.ColName: 190 if !rb.isLocal(node) { 191 joinVar := jt.Procure(plan, node, rb.Order()) 192 buf.WriteArg(":", joinVar) 193 return 194 } 195 case sqlparser.TableName: 196 if !sqlparser.SystemSchema(node.Qualifier.String()) { 197 node.Name.Format(buf) 198 return 199 } 200 node.Format(buf) 201 return 202 } 203 node.Format(buf) 204 } 205 buf := sqlparser.NewTrackedBuffer(varFormatter) 206 varFormatter(buf, rb.Select) 207 rb.eroute.Query = buf.ParsedQuery().Query 208 rb.eroute.FieldQuery = rb.generateFieldQuery(rb.Select, jt) 209 return nil 210 } 211 212 // prepareTheAST does minor fixups of the SELECT struct before producing the query string 213 func (rb *route) prepareTheAST() { 214 _ = sqlparser.Walk(func(node sqlparser.SQLNode) (bool, error) { 215 switch node := node.(type) { 216 case *sqlparser.Select: 217 if len(node.SelectExprs) == 0 { 218 node.SelectExprs = []sqlparser.SelectExpr{ 219 &sqlparser.AliasedExpr{ 220 Expr: sqlparser.NewIntLiteral("1"), 221 }, 222 } 223 } 224 case *sqlparser.ComparisonExpr: 225 // 42 = colName -> colName = 42 226 b := node.Operator == sqlparser.EqualOp 227 value := sqlparser.IsValue(node.Left) 228 name := sqlparser.IsColName(node.Right) 229 if b && 230 value && 231 name { 232 node.Left, node.Right = node.Right, node.Left 233 } 234 } 235 return true, nil 236 }, rb.Select) 237 } 238 239 // procureValues procures and converts the input into 240 // the expected types for rb.Values. 241 func (rb *route) procureValues(plan logicalPlan, jt *jointab, val sqlparser.Expr) (evalengine.Expr, error) { 242 switch typedVal := val.(type) { 243 case sqlparser.ValTuple: 244 exprs := make([]evalengine.Expr, 0, len(typedVal)) 245 for _, item := range typedVal { 246 v, err := rb.procureValues(plan, jt, item) 247 if err != nil { 248 return nil, err 249 } 250 exprs = append(exprs, v) 251 } 252 return evalengine.NewTupleExpr(exprs...), nil 253 case *sqlparser.ColName: 254 joinVar := jt.Procure(plan, typedVal, rb.Order()) 255 return evalengine.NewBindVar(joinVar, collations.TypedCollation{}), nil 256 default: 257 return evalengine.Translate(typedVal, semantics.EmptySemTable()) 258 } 259 } 260 261 func (rb *route) isLocal(col *sqlparser.ColName) bool { 262 return col.Metadata.(*column).Origin() == rb 263 } 264 265 // generateFieldQuery generates a query with an impossible where. 266 // This will be used on the RHS node to fetch field info if the LHS 267 // returns no result. 268 func (rb *route) generateFieldQuery(sel sqlparser.SelectStatement, jt *jointab) string { 269 formatter := func(buf *sqlparser.TrackedBuffer, node sqlparser.SQLNode) { 270 switch node := node.(type) { 271 case *sqlparser.ColName: 272 if !rb.isLocal(node) { 273 _, joinVar := jt.Lookup(node) 274 buf.WriteArg(":", joinVar) 275 return 276 } 277 case sqlparser.TableName: 278 if !sqlparser.SystemSchema(node.Qualifier.String()) { 279 node.Name.Format(buf) 280 return 281 } 282 node.Format(buf) 283 return 284 } 285 sqlparser.FormatImpossibleQuery(buf, node) 286 } 287 288 buffer := sqlparser.NewTrackedBuffer(formatter) 289 node := buffer.WriteNode(sel) 290 query := node.ParsedQuery() 291 return query.Query 292 } 293 294 // SupplyVar implements the logicalPlan interface 295 func (rb *route) SupplyVar(from, to int, col *sqlparser.ColName, varname string) { 296 // route is an atomic primitive. So, SupplyVar cannot be 297 // called on it. 298 panic("BUG: route is an atomic node.") 299 } 300 301 // SupplyCol implements the logicalPlan interface 302 func (rb *route) SupplyCol(col *sqlparser.ColName) (rc *resultColumn, colNumber int) { 303 c := col.Metadata.(*column) 304 for i, rc := range rb.resultColumns { 305 if rc.column == c { 306 return rc, i 307 } 308 } 309 310 // A new result has to be returned. 311 rc = &resultColumn{column: c} 312 rb.resultColumns = append(rb.resultColumns, rc) 313 // TODO: we should not assume that the query is a SELECT query 314 sel := rb.Select.(*sqlparser.Select) 315 sel.SelectExprs = append(sel.SelectExprs, &sqlparser.AliasedExpr{Expr: col}) 316 return rc, len(rb.resultColumns) - 1 317 } 318 319 // SupplyWeightString implements the logicalPlan interface 320 func (rb *route) SupplyWeightString(colNumber int, alsoAddToGroupBy bool) (weightcolNumber int, err error) { 321 rc := rb.resultColumns[colNumber] 322 s, ok := rb.Select.(*sqlparser.Select) 323 if !ok { 324 return 0, vterrors.VT13001("unexpected AST struct for query") 325 } 326 327 aliasExpr, ok := s.SelectExprs[colNumber].(*sqlparser.AliasedExpr) 328 if !ok { 329 return 0, vterrors.VT13001(fmt.Sprintf("unexpected AST struct for query %T", s.SelectExprs[colNumber])) 330 } 331 weightStringExpr := &sqlparser.FuncExpr{ 332 Name: sqlparser.NewIdentifierCI("weight_string"), 333 Exprs: []sqlparser.SelectExpr{ 334 &sqlparser.AliasedExpr{ 335 Expr: aliasExpr.Expr, 336 }, 337 }, 338 } 339 expr := &sqlparser.AliasedExpr{ 340 Expr: weightStringExpr, 341 } 342 if alsoAddToGroupBy { 343 sel, isSelect := rb.Select.(*sqlparser.Select) 344 if !isSelect { 345 return 0, vterrors.VT13001(fmt.Sprintf("cannot add weight string in %T", rb.Select)) 346 } 347 sel.AddGroupBy(weightStringExpr) 348 } 349 350 if weightcolNumber, ok := rb.weightStrings[rc]; ok { 351 return weightcolNumber, nil 352 } 353 // It's ok to pass nil for pb and logicalPlan because PushSelect doesn't use them. 354 // TODO: we are ignoring a potential error here. need to clean this up 355 _, _, weightcolNumber, err = planProjection(nil, rb, expr, nil) 356 if err != nil { 357 return 0, err 358 } 359 rb.weightStrings[rc] = weightcolNumber 360 return weightcolNumber, nil 361 } 362 363 // Rewrite implements the logicalPlan interface 364 func (rb *route) Rewrite(inputs ...logicalPlan) error { 365 if len(inputs) != 0 { 366 return vterrors.VT13001("route: wrong number of inputs") 367 } 368 return nil 369 } 370 371 // Inputs implements the logicalPlan interface 372 func (rb *route) Inputs() []logicalPlan { 373 return []logicalPlan{} 374 } 375 376 // MergeSubquery returns true if the subquery route could successfully be merged 377 // with the outer route. 378 func (rb *route) MergeSubquery(pb *primitiveBuilder, inner *route) bool { 379 if rb.SubqueryCanMerge(pb, inner) { 380 if inner.eroute.Opcode == engine.DBA && (len(inner.eroute.SysTableTableName) > 0 || len(inner.eroute.SysTableTableSchema) > 0) { 381 switch rb.eroute.Opcode { 382 case engine.DBA, engine.Reference: 383 rb.eroute.SysTableTableSchema = append(rb.eroute.SysTableTableSchema, inner.eroute.SysTableTableSchema...) 384 for k, v := range inner.eroute.SysTableTableName { 385 if rb.eroute.SysTableTableName == nil { 386 rb.eroute.SysTableTableName = map[string]evalengine.Expr{} 387 } 388 rb.eroute.SysTableTableName[k] = v 389 } 390 rb.eroute.Opcode = engine.DBA 391 default: 392 return false 393 } 394 } else { 395 if rb.eroute.Opcode == engine.Reference { 396 rb.eroute.RoutingParameters = inner.eroute.RoutingParameters 397 rb.condition = inner.condition 398 } 399 } 400 401 rb.substitutions = append(rb.substitutions, inner.substitutions...) 402 inner.Redirect = rb 403 return true 404 } 405 return false 406 } 407 408 // MergeUnion returns true if the rhs route could successfully be merged 409 // with the rb route. 410 func (rb *route) MergeUnion(right *route, isDistinct bool) bool { 411 if rb.unionCanMerge(right, isDistinct) { 412 rb.substitutions = append(rb.substitutions, right.substitutions...) 413 right.Redirect = rb 414 return true 415 } 416 return false 417 } 418 419 func (rb *route) isSingleShard() bool { 420 return rb.eroute.Opcode.IsSingleShard() 421 } 422 423 // JoinCanMerge, SubqueryCanMerge and unionCanMerge have subtly different behaviors. 424 // The difference in behavior is around SelectReference. 425 // It's not worth trying to reuse the code between them. 426 func (rb *route) JoinCanMerge(pb *primitiveBuilder, rrb *route, ajoin *sqlparser.JoinTableExpr, where sqlparser.Expr) bool { 427 if rb.eroute.Keyspace.Name != rrb.eroute.Keyspace.Name { 428 return false 429 } 430 if rrb.eroute.Opcode == engine.Reference { 431 // Any opcode can join with a reference table. 432 return true 433 } 434 switch rb.eroute.Opcode { 435 case engine.Unsharded: 436 return rb.eroute.Opcode == rrb.eroute.Opcode 437 case engine.EqualUnique: 438 // Check if they target the same shard. 439 if rrb.eroute.Opcode == engine.EqualUnique && rb.eroute.Vindex == rrb.eroute.Vindex && valEqual(rb.condition, rrb.condition) { 440 return true 441 } 442 case engine.Reference: 443 return true 444 case engine.Next: 445 return false 446 case engine.DBA: 447 if rrb.eroute.Opcode != engine.DBA { 448 return false 449 } 450 if where == nil { 451 return true 452 } 453 return ajoin != nil 454 } 455 if ajoin == nil { 456 return false 457 } 458 for _, filter := range sqlparser.SplitAndExpression(nil, ajoin.Condition.On) { 459 if rb.canMergeOnFilter(pb, rrb, filter) { 460 return true 461 } 462 } 463 return false 464 } 465 466 func (rb *route) SubqueryCanMerge(pb *primitiveBuilder, inner *route) bool { 467 if rb.eroute.Keyspace.Name != inner.eroute.Keyspace.Name { 468 return false 469 } 470 471 // if either side is a reference table, and we know the other side will only run once, 472 // we can just merge them and use the opcode of the other side 473 if rb.eroute.Opcode == engine.Reference || inner.eroute.Opcode == engine.Reference { 474 return rb.isSingleShard() && inner.isSingleShard() 475 } 476 477 switch rb.eroute.Opcode { 478 case engine.Unsharded, engine.DBA: 479 return rb.eroute.Opcode == inner.eroute.Opcode 480 case engine.EqualUnique: 481 // Check if they target the same shard. 482 if inner.eroute.Opcode == engine.EqualUnique && rb.eroute.Vindex == inner.eroute.Vindex && valEqual(rb.condition, inner.condition) { 483 return true 484 } 485 case engine.Next: 486 return false 487 } 488 489 switch vals := inner.condition.(type) { 490 case *sqlparser.ColName: 491 if pb.st.Vindex(vals, rb) == inner.eroute.Vindex { 492 return true 493 } 494 } 495 return false 496 } 497 498 func (rb *route) unionCanMerge(other *route, distinct bool) bool { 499 if rb.eroute.Keyspace.Name != other.eroute.Keyspace.Name { 500 return false 501 } 502 switch rb.eroute.Opcode { 503 case engine.Unsharded, engine.Reference: 504 return rb.eroute.Opcode == other.eroute.Opcode 505 case engine.DBA: 506 return other.eroute.Opcode == engine.DBA && 507 len(rb.eroute.SysTableTableSchema) == 0 && 508 len(rb.eroute.SysTableTableName) == 0 && 509 len(other.eroute.SysTableTableSchema) == 0 && 510 len(other.eroute.SysTableTableName) == 0 511 case engine.EqualUnique: 512 // Check if they target the same shard. 513 if other.eroute.Opcode == engine.EqualUnique && rb.eroute.Vindex == other.eroute.Vindex && valEqual(rb.condition, other.condition) { 514 return true 515 } 516 case engine.Scatter: 517 return other.eroute.Opcode == engine.Scatter && !distinct 518 case engine.Next: 519 return false 520 } 521 return false 522 } 523 524 // canMergeOnFilter returns true if the join constraint makes the routes 525 // mergeable by unique vindex. The constraint has to be an equality 526 // like a.id = b.id where both columns have the same unique vindex. 527 func (rb *route) canMergeOnFilter(pb *primitiveBuilder, rrb *route, filter sqlparser.Expr) bool { 528 comparison, ok := filter.(*sqlparser.ComparisonExpr) 529 if !ok { 530 return false 531 } 532 if comparison.Operator != sqlparser.EqualOp { 533 return false 534 } 535 left := comparison.Left 536 right := comparison.Right 537 lVindex := pb.st.Vindex(left, rb) 538 if lVindex == nil { 539 left, right = right, left 540 lVindex = pb.st.Vindex(left, rb) 541 } 542 if lVindex == nil || !lVindex.IsUnique() { 543 return false 544 } 545 rVindex := pb.st.Vindex(right, rrb) 546 if rVindex == nil { 547 return false 548 } 549 return rVindex == lVindex 550 } 551 552 // UpdatePlan evaluates the primitive against the specified 553 // filter. If it's an improvement, the primitive is updated. 554 // We assume that the filter has already been pushed into 555 // the route. 556 func (rb *route) UpdatePlan(pb *primitiveBuilder, filter sqlparser.Expr) { 557 switch rb.eroute.Opcode { 558 // For these opcodes, a new filter will not make any difference, so we can just exit early 559 case engine.Unsharded, engine.Next, engine.DBA, engine.Reference, engine.None: 560 return 561 } 562 opcode, vindex, values := rb.computePlan(pb, filter) 563 if opcode == engine.Scatter { 564 return 565 } 566 // If we get SelectNone in next filters, override the previous route plan. 567 if opcode == engine.None { 568 rb.updateRoute(opcode, vindex, values) 569 return 570 } 571 switch rb.eroute.Opcode { 572 case engine.EqualUnique: 573 if opcode == engine.EqualUnique && vindex.Cost() < rb.eroute.Vindex.Cost() { 574 rb.updateRoute(opcode, vindex, values) 575 } 576 case engine.Equal: 577 switch opcode { 578 case engine.EqualUnique: 579 rb.updateRoute(opcode, vindex, values) 580 case engine.Equal: 581 if vindex.Cost() < rb.eroute.Vindex.Cost() { 582 rb.updateRoute(opcode, vindex, values) 583 } 584 } 585 case engine.IN: 586 switch opcode { 587 case engine.EqualUnique, engine.Equal: 588 rb.updateRoute(opcode, vindex, values) 589 case engine.IN: 590 if vindex.Cost() < rb.eroute.Vindex.Cost() { 591 rb.updateRoute(opcode, vindex, values) 592 } 593 } 594 case engine.MultiEqual: 595 switch opcode { 596 case engine.EqualUnique, engine.Equal, engine.IN: 597 rb.updateRoute(opcode, vindex, values) 598 case engine.MultiEqual: 599 if vindex.Cost() < rb.eroute.Vindex.Cost() { 600 rb.updateRoute(opcode, vindex, values) 601 } 602 } 603 case engine.Scatter: 604 switch opcode { 605 case engine.EqualUnique, engine.Equal, engine.IN, engine.MultiEqual, engine.None: 606 rb.updateRoute(opcode, vindex, values) 607 } 608 } 609 } 610 611 func (rb *route) updateRoute(opcode engine.Opcode, vindex vindexes.SingleColumn, condition sqlparser.Expr) { 612 rb.eroute.Opcode = opcode 613 rb.eroute.Vindex = vindex 614 rb.condition = condition 615 } 616 617 // computePlan computes the plan for the specified filter. 618 func (rb *route) computePlan(pb *primitiveBuilder, filter sqlparser.Expr) (opcode engine.Opcode, vindex vindexes.SingleColumn, condition sqlparser.Expr) { 619 switch node := filter.(type) { 620 case *sqlparser.ComparisonExpr: 621 switch node.Operator { 622 case sqlparser.EqualOp: 623 return rb.computeEqualPlan(pb, node) 624 case sqlparser.InOp: 625 return rb.computeINPlan(pb, node) 626 case sqlparser.NotInOp: 627 return rb.computeNotInPlan(node.Right), nil, nil 628 case sqlparser.LikeOp: 629 return rb.computeLikePlan(pb, node) 630 } 631 case *sqlparser.IsExpr: 632 return rb.computeISPlan(pb, node) 633 } 634 return engine.Scatter, nil, nil 635 } 636 637 // computeLikePlan computes the plan for 'LIKE' constraint 638 func (rb *route) computeLikePlan(pb *primitiveBuilder, comparison *sqlparser.ComparisonExpr) (opcode engine.Opcode, vindex vindexes.SingleColumn, condition sqlparser.Expr) { 639 640 left := comparison.Left 641 right := comparison.Right 642 643 if sqlparser.IsNull(right) { 644 return engine.None, nil, nil 645 } 646 if !rb.exprIsValue(right) { 647 return engine.Scatter, nil, nil 648 } 649 vindex = pb.st.Vindex(left, rb) 650 if vindex == nil { 651 // if there is no vindex defined, scatter 652 return engine.Scatter, nil, nil 653 } 654 if subsharding, ok := vindex.(vindexes.Prefixable); ok { 655 return engine.Equal, subsharding.PrefixVindex(), right 656 } 657 658 return engine.Scatter, nil, nil 659 } 660 661 // computeEqualPlan computes the plan for an equality constraint. 662 func (rb *route) computeEqualPlan(pb *primitiveBuilder, comparison *sqlparser.ComparisonExpr) (opcode engine.Opcode, vindex vindexes.SingleColumn, condition sqlparser.Expr) { 663 left := comparison.Left 664 right := comparison.Right 665 666 if sqlparser.IsNull(right) { 667 return engine.None, nil, nil 668 } 669 670 vindex = pb.st.Vindex(left, rb) 671 if vindex == nil { 672 left, right = right, left 673 vindex = pb.st.Vindex(left, rb) 674 if vindex == nil { 675 return engine.Scatter, nil, nil 676 } 677 } 678 if !rb.exprIsValue(right) { 679 return engine.Scatter, nil, nil 680 } 681 if vindex.IsUnique() { 682 return engine.EqualUnique, vindex, right 683 } 684 return engine.Equal, vindex, right 685 } 686 687 // computeIS computes the plan for an equality constraint. 688 func (rb *route) computeISPlan(pb *primitiveBuilder, comparison *sqlparser.IsExpr) (opcode engine.Opcode, vindex vindexes.SingleColumn, expr sqlparser.Expr) { 689 // we only handle IS NULL correct. IsExpr can contain other expressions as well 690 if comparison.Right != sqlparser.IsNullOp { 691 return engine.Scatter, nil, nil 692 } 693 694 vindex = pb.st.Vindex(comparison.Left, rb) 695 // fallback to scatter gather if there is no vindex 696 if vindex == nil { 697 return engine.Scatter, nil, nil 698 } 699 if _, isLookup := vindex.(vindexes.Lookup); isLookup { 700 // the lookup table is keyed by the lookup value, so it does not support nulls 701 return engine.Scatter, nil, nil 702 } 703 if vindex.IsUnique() { 704 return engine.EqualUnique, vindex, &sqlparser.NullVal{} 705 } 706 return engine.Equal, vindex, &sqlparser.NullVal{} 707 } 708 709 // computeINPlan computes the plan for an IN constraint. 710 func (rb *route) computeINPlan(pb *primitiveBuilder, comparison *sqlparser.ComparisonExpr) (opcode engine.Opcode, vindex vindexes.SingleColumn, expr sqlparser.Expr) { 711 switch comparison.Left.(type) { 712 case *sqlparser.ColName: 713 return rb.computeSimpleINPlan(pb, comparison) 714 case sqlparser.ValTuple: 715 return rb.computeCompositeINPlan(pb, comparison) 716 } 717 return engine.Scatter, nil, nil 718 } 719 720 // computeSimpleINPlan computes the plan for a simple IN constraint. 721 func (rb *route) computeSimpleINPlan(pb *primitiveBuilder, comparison *sqlparser.ComparisonExpr) (opcode engine.Opcode, vindex vindexes.SingleColumn, expr sqlparser.Expr) { 722 vindex = pb.st.Vindex(comparison.Left, rb) 723 if vindex == nil { 724 return engine.Scatter, nil, nil 725 } 726 switch node := comparison.Right.(type) { 727 case sqlparser.ValTuple: 728 if len(node) == 1 && sqlparser.IsNull(node[0]) { 729 return engine.None, nil, nil 730 } 731 732 for _, n := range node { 733 if !rb.exprIsValue(n) { 734 return engine.Scatter, nil, nil 735 } 736 } 737 return engine.IN, vindex, comparison 738 case sqlparser.ListArg: 739 return engine.IN, vindex, comparison 740 } 741 return engine.Scatter, nil, nil 742 } 743 744 // computeCompositeINPlan computes the plan for a composite IN constraint. 745 func (rb *route) computeCompositeINPlan(pb *primitiveBuilder, comparison *sqlparser.ComparisonExpr) (opcode engine.Opcode, vindex vindexes.SingleColumn, values sqlparser.Expr) { 746 leftTuple := comparison.Left.(sqlparser.ValTuple) 747 return rb.iterateCompositeIN(pb, comparison, nil, leftTuple) 748 } 749 750 // iterateCompositeIN recursively walks the LHS tuple of the IN clause looking 751 // for column names. For those that match a vindex, it builds a multi-value plan 752 // using the corresponding values in the RHS. It returns the best of the plans built. 753 func (rb *route) iterateCompositeIN( 754 pb *primitiveBuilder, 755 comparison *sqlparser.ComparisonExpr, 756 coordinates []int, 757 tuple sqlparser.ValTuple, 758 ) (opcode engine.Opcode, vindex vindexes.SingleColumn, values sqlparser.Expr) { 759 opcode = engine.Scatter 760 761 cindex := len(coordinates) 762 coordinates = append(coordinates, 0) 763 for idx, expr := range tuple { 764 coordinates[cindex] = idx 765 switch expr := expr.(type) { 766 case sqlparser.ValTuple: 767 newOpcode, newVindex, newValues := rb.iterateCompositeIN(pb, comparison, coordinates, expr) 768 opcode, vindex, values = bestOfComposite(opcode, newOpcode, vindex, newVindex, values, newValues) 769 case *sqlparser.ColName: 770 newVindex := pb.st.Vindex(expr, rb) 771 if newVindex != nil { 772 newOpcode, newValues := rb.compositePlanForCol(pb, comparison, coordinates) 773 opcode, vindex, values = bestOfComposite(opcode, newOpcode, vindex, newVindex, values, newValues) 774 } 775 } 776 } 777 return opcode, vindex, values 778 } 779 780 // compositePlanForCol builds a plan for a matched column in the LHS 781 // of a composite IN clause. 782 func (rb *route) compositePlanForCol(pb *primitiveBuilder, comparison *sqlparser.ComparisonExpr, coordinates []int) (opcode engine.Opcode, values sqlparser.Expr) { 783 rightTuple, ok := comparison.Right.(sqlparser.ValTuple) 784 if !ok { 785 return engine.Scatter, nil 786 } 787 retVal := make(sqlparser.ValTuple, len(rightTuple)) 788 for i, rval := range rightTuple { 789 val := tupleAccess(rval, coordinates) 790 if val == nil { 791 return engine.Scatter, nil 792 } 793 if !rb.exprIsValue(val) { 794 return engine.Scatter, nil 795 } 796 retVal[i] = val 797 } 798 return engine.MultiEqual, retVal 799 } 800 801 // tupleAccess returns the value of the expression that corresponds 802 // to the specified coordinates. 803 func tupleAccess(expr sqlparser.Expr, coordinates []int) sqlparser.Expr { 804 tuple, _ := expr.(sqlparser.ValTuple) 805 for _, idx := range coordinates { 806 if idx >= len(tuple) { 807 return nil 808 } 809 expr = tuple[idx] 810 tuple, _ = expr.(sqlparser.ValTuple) 811 } 812 return expr 813 } 814 815 // bestOfComposite returns the best of two composite IN clause plans. 816 func bestOfComposite(opcode1, opcode2 engine.Opcode, vindex1, vindex2 vindexes.SingleColumn, values1, values2 sqlparser.Expr) (opcode engine.Opcode, vindex vindexes.SingleColumn, values sqlparser.Expr) { 817 if opcode1 == engine.Scatter { 818 return opcode2, vindex2, values2 819 } 820 if opcode2 == engine.Scatter { 821 return opcode1, vindex1, values1 822 } 823 if vindex1.Cost() < vindex2.Cost() { 824 return opcode1, vindex1, values1 825 } 826 return opcode2, vindex2, values2 827 } 828 829 // computeNotInPlan looks for null values to produce a SelectNone if found 830 func (rb *route) computeNotInPlan(right sqlparser.Expr) engine.Opcode { 831 switch node := right.(type) { 832 case sqlparser.ValTuple: 833 for _, n := range node { 834 if sqlparser.IsNull(n) { 835 return engine.None 836 } 837 } 838 } 839 840 return engine.Scatter 841 } 842 843 // exprIsValue returns true if the expression can be treated as a value 844 // for the routeOption. External references are treated as value. 845 func (rb *route) exprIsValue(expr sqlparser.Expr) bool { 846 if node, ok := expr.(*sqlparser.ColName); ok { 847 return node.Metadata.(*column).Origin() != rb 848 } 849 return sqlparser.IsValue(expr) 850 } 851 852 // queryTimeout returns DirectiveQueryTimeout value if set, otherwise returns 0. 853 func queryTimeout(d *sqlparser.CommentDirectives) int { 854 val, _ := d.GetString(sqlparser.DirectiveQueryTimeout, "0") 855 if intVal, err := strconv.Atoi(val); err == nil { 856 return intVal 857 } 858 return 0 859 }