vitess.io/vitess@v0.16.2/go/vt/vtgate/planbuilder/horizon_planning.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 22 "vitess.io/vitess/go/sqltypes" 23 "vitess.io/vitess/go/vt/vtgate/planbuilder/operators" 24 "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" 25 26 "vitess.io/vitess/go/vt/vtgate/semantics" 27 28 "vitess.io/vitess/go/vt/vterrors" 29 30 "vitess.io/vitess/go/vt/sqlparser" 31 "vitess.io/vitess/go/vt/vtgate/engine" 32 ) 33 34 type horizonPlanning struct { 35 sel *sqlparser.Select 36 qp *operators.QueryProjection 37 } 38 39 func (hp *horizonPlanning) planHorizon(ctx *plancontext.PlanningContext, plan logicalPlan, truncateColumns bool) (logicalPlan, error) { 40 rb, isRoute := plan.(*routeGen4) 41 if !isRoute && ctx.SemTable.NotSingleRouteErr != nil { 42 // If we got here, we don't have a single shard plan 43 return nil, ctx.SemTable.NotSingleRouteErr 44 } 45 46 if isRoute && rb.isSingleShard() { 47 err := planSingleShardRoutePlan(hp.sel, rb) 48 if err != nil { 49 return nil, err 50 } 51 return plan, nil 52 } 53 54 // If the current plan is a simpleProjection, we want to rewrite derived expression. 55 // In transformDerivedPlan (operator_transformers.go), derived tables that are not 56 // a simple route are put behind a simpleProjection. In this simple projection, 57 // every Route will represent the original derived table. Thus, pushing new expressions 58 // to those Routes require us to rewrite them. 59 // On the other hand, when a derived table is a simple Route, we do not put it under 60 // a simpleProjection. We create a new Route that contains the derived table in the 61 // FROM clause. Meaning that, when we push expressions to the select list of this 62 // new Route, we do not want them to rewrite them. 63 sp, derivedTable := plan.(*simpleProjection) 64 if derivedTable { 65 oldRewriteDerivedExpr := ctx.RewriteDerivedExpr 66 defer func() { 67 ctx.RewriteDerivedExpr = oldRewriteDerivedExpr 68 }() 69 ctx.RewriteDerivedExpr = true 70 } 71 72 var err error 73 hp.qp, err = operators.CreateQPFromSelect(ctx, hp.sel) 74 if err != nil { 75 return nil, err 76 } 77 78 needsOrdering := len(hp.qp.OrderExprs) > 0 79 80 // If we still have a HAVING clause, it's because it could not be pushed to the WHERE, 81 // so it probably has aggregations 82 canShortcut := isRoute && hp.sel.Having == nil && !needsOrdering 83 84 switch { 85 case hp.qp.NeedsAggregation() || hp.sel.Having != nil: 86 plan, err = hp.planAggregations(ctx, plan) 87 if err != nil { 88 return nil, err 89 } 90 // if we already did sorting, we don't need to do it again 91 needsOrdering = needsOrdering && !hp.qp.CanPushDownSorting 92 case canShortcut: 93 err = planSingleShardRoutePlan(hp.sel, rb) 94 if err != nil { 95 return nil, err 96 } 97 case derivedTable: 98 pusher := func(ae *sqlparser.AliasedExpr) (int, error) { 99 offset, _, err := pushProjection(ctx, ae, sp.input, true, true, false) 100 return offset, err 101 } 102 needsVtGate, projections, colNames, err := hp.qp.NeedsProjecting(ctx, pusher) 103 if err != nil { 104 return nil, err 105 } 106 if !needsVtGate { 107 break 108 } 109 110 // there were some expressions we could not push down entirely, 111 // so replace the simpleProjection with a real projection 112 plan = &projection{ 113 source: sp.input, 114 columns: projections, 115 columnNames: colNames, 116 } 117 default: 118 err = pushProjections(ctx, plan, hp.qp.SelectExprs) 119 if err != nil { 120 return nil, err 121 } 122 } 123 124 // If we didn't already take care of ORDER BY during aggregation planning, we need to handle it now 125 if needsOrdering { 126 plan, err = hp.planOrderBy(ctx, hp.qp.OrderExprs, plan) 127 if err != nil { 128 return nil, err 129 } 130 } 131 132 plan, err = hp.planDistinct(ctx, plan) 133 if err != nil { 134 return nil, err 135 } 136 137 if !truncateColumns { 138 return plan, nil 139 } 140 141 plan, err = hp.truncateColumnsIfNeeded(ctx, plan) 142 if err != nil { 143 return nil, err 144 } 145 146 return plan, nil 147 } 148 149 func pushProjections(ctx *plancontext.PlanningContext, plan logicalPlan, selectExprs []operators.SelectExpr) error { 150 for _, e := range selectExprs { 151 aliasExpr, err := e.GetAliasedExpr() 152 if err != nil { 153 return err 154 } 155 if _, _, err := pushProjection(ctx, aliasExpr, plan, true, false, false); err != nil { 156 return err 157 } 158 } 159 return nil 160 } 161 162 func (hp *horizonPlanning) truncateColumnsIfNeeded(ctx *plancontext.PlanningContext, plan logicalPlan) (logicalPlan, error) { 163 if len(plan.OutputColumns()) == hp.qp.GetColumnCount() { 164 return plan, nil 165 } 166 switch p := plan.(type) { 167 case *routeGen4: 168 p.eroute.SetTruncateColumnCount(hp.qp.GetColumnCount()) 169 case *joinGen4, *semiJoin, *hashJoin: 170 // since this is a join, we can safely add extra columns and not need to truncate them 171 case *orderedAggregate: 172 p.truncateColumnCount = hp.qp.GetColumnCount() 173 case *memorySort: 174 p.truncater.SetTruncateColumnCount(hp.qp.GetColumnCount()) 175 case *pulloutSubquery: 176 newUnderlyingPlan, err := hp.truncateColumnsIfNeeded(ctx, p.underlying) 177 if err != nil { 178 return nil, err 179 } 180 p.underlying = newUnderlyingPlan 181 default: 182 plan = &simpleProjection{ 183 logicalPlanCommon: newBuilderCommon(plan), 184 eSimpleProj: &engine.SimpleProjection{}, 185 } 186 187 exprs := hp.qp.SelectExprs[0:hp.qp.GetColumnCount()] 188 err := pushProjections(ctx, plan, exprs) 189 if err != nil { 190 return nil, err 191 } 192 } 193 return plan, nil 194 } 195 196 func checkIfAlreadyExists(expr *sqlparser.AliasedExpr, node sqlparser.SelectStatement, semTable *semantics.SemTable) int { 197 // Here to find if the expr already exists in the SelectStatement, we have 3 cases 198 // input is a Select -> In this case we want to search in the select 199 // input is a Union -> In this case we want to search in the First Select of the Union 200 // input is a Parenthesised Select -> In this case we want to search in the select 201 // all these three cases are handled by the call to GetFirstSelect. 202 sel := sqlparser.GetFirstSelect(node) 203 204 exprCol, isExprCol := expr.Expr.(*sqlparser.ColName) 205 206 // first pass - search for aliased expressions 207 for i, selectExpr := range sel.SelectExprs { 208 if !isExprCol { 209 break 210 } 211 212 selectExpr, ok := selectExpr.(*sqlparser.AliasedExpr) 213 if ok && selectExpr.As.Equal(exprCol.Name) { 214 return i 215 } 216 } 217 218 // next pass - we are searching the actual expressions and not the aliases 219 for i, selectExpr := range sel.SelectExprs { 220 selectExpr, ok := selectExpr.(*sqlparser.AliasedExpr) 221 if !ok { 222 continue 223 } 224 225 if semTable.EqualsExpr(expr.Expr, selectExpr.Expr) { 226 return i 227 } 228 } 229 return -1 230 } 231 232 func (hp *horizonPlanning) planAggregations(ctx *plancontext.PlanningContext, plan logicalPlan) (logicalPlan, error) { 233 isPushable := !isJoin(plan) 234 grouping := hp.qp.GetGrouping() 235 vindexOverlapWithGrouping := hasUniqueVindex(ctx.SemTable, grouping) 236 if isPushable && vindexOverlapWithGrouping { 237 // If we have a plan that we can push the group by and aggregation through, we don't need to do aggregation 238 // at the vtgate level at all 239 err := hp.planAggregationWithoutOA(ctx, plan) 240 if err != nil { 241 return nil, err 242 } 243 resultPlan, err := hp.planOrderBy(ctx, hp.qp.OrderExprs, plan) 244 if err != nil { 245 return nil, err 246 } 247 248 newPlan, err := hp.planHaving(ctx, resultPlan) 249 if err != nil { 250 return nil, err 251 } 252 253 return newPlan, nil 254 } 255 256 return hp.planAggrUsingOA(ctx, plan, grouping) 257 } 258 259 func (hp *horizonPlanning) planAggrUsingOA( 260 ctx *plancontext.PlanningContext, 261 plan logicalPlan, 262 grouping []operators.GroupBy, 263 ) (logicalPlan, error) { 264 oa := &orderedAggregate{ 265 groupByKeys: make([]*engine.GroupByParams, 0, len(grouping)), 266 } 267 268 var order []operators.OrderBy 269 if hp.qp.CanPushDownSorting { 270 hp.qp.AlignGroupByAndOrderBy(ctx) 271 // the grouping order might have changed, so we reload the grouping expressions 272 grouping = hp.qp.GetGrouping() 273 order = hp.qp.OrderExprs 274 } else { 275 for _, expr := range grouping { 276 order = append(order, expr.AsOrderBy()) 277 } 278 } 279 280 // here we are building up the grouping keys for the OA, 281 // but they are lacking the input offsets because we have yet to push the columns down 282 for _, expr := range grouping { 283 oa.groupByKeys = append(oa.groupByKeys, &engine.GroupByParams{ 284 Expr: expr.Inner, 285 FromGroupBy: true, 286 CollationID: ctx.SemTable.CollationForExpr(expr.Inner), 287 }) 288 } 289 290 if hp.sel.Having != nil { 291 rewriter := hp.qp.AggrRewriter(ctx) 292 sqlparser.SafeRewrite(hp.sel.Having.Expr, rewriter.RewriteDown(), rewriter.RewriteUp()) 293 if rewriter.Err != nil { 294 return nil, rewriter.Err 295 } 296 } 297 298 aggregationExprs, err := hp.qp.AggregationExpressions(ctx) 299 if err != nil { 300 return nil, err 301 } 302 303 // If we have a distinct aggregating expression, 304 // we handle it by pushing it down to the underlying input as a grouping column 305 distinctGroupBy, distinctOffsets, aggrs, err := hp.handleDistinctAggr(ctx, aggregationExprs) 306 if err != nil { 307 return nil, err 308 } 309 310 if len(distinctGroupBy) > 0 { 311 grouping = append(grouping, distinctGroupBy...) 312 // all the distinct grouping aggregates use the same expression, so it should be OK to just add it once 313 order = append(order, distinctGroupBy[0].AsOrderBy()) 314 oa.preProcess = true 315 } 316 317 newPlan, groupingOffsets, aggrParamOffsets, pushed, err := hp.pushAggregation(ctx, plan, grouping, aggrs, false) 318 if err != nil { 319 return nil, err 320 } 321 if !pushed { 322 oa.preProcess = true 323 oa.aggrOnEngine = true 324 } 325 326 plan = newPlan 327 328 _, isRoute := plan.(*routeGen4) 329 needsProj := !isRoute 330 var aggPlan = plan 331 var proj *projection 332 if needsProj { 333 length := getLengthOfProjection(groupingOffsets, aggrs) 334 proj = &projection{ 335 source: plan, 336 columns: make([]sqlparser.Expr, length), 337 columnNames: make([]string, length), 338 } 339 aggPlan = proj 340 } 341 342 aggrParams, err := generateAggregateParams(aggrs, aggrParamOffsets, proj, pushed) 343 if err != nil { 344 return nil, err 345 } 346 347 if proj != nil { 348 groupingOffsets, err = passGroupingColumns(proj, groupingOffsets, grouping) 349 if err != nil { 350 return nil, err 351 } 352 } 353 354 // Next we add the aggregation expressions and grouping offsets to the OA 355 addColumnsToOA(ctx, oa, distinctGroupBy, aggrParams, distinctOffsets, groupingOffsets, aggregationExprs) 356 357 aggPlan, err = hp.planOrderBy(ctx, order, aggPlan) 358 if err != nil { 359 return nil, err 360 } 361 362 oa.resultsBuilder = resultsBuilder{ 363 logicalPlanCommon: newBuilderCommon(aggPlan), 364 weightStrings: make(map[*resultColumn]int), 365 } 366 367 return hp.planHaving(ctx, oa) 368 } 369 370 func passGroupingColumns(proj *projection, groupings []offsets, grouping []operators.GroupBy) (projGrpOffsets []offsets, err error) { 371 for idx, grp := range groupings { 372 origGrp := grouping[idx] 373 var offs offsets 374 expr := origGrp.AsAliasedExpr() 375 offs.col, err = proj.addColumn(origGrp.InnerIndex, sqlparser.NewOffset(grp.col, expr.Expr), expr.ColumnName()) 376 if err != nil { 377 return nil, err 378 } 379 if grp.wsCol != -1 { 380 offs.wsCol, err = proj.addColumn(nil, sqlparser.NewOffset(grp.wsCol, weightStringFor(expr.Expr)), "") 381 if err != nil { 382 return nil, err 383 } 384 } 385 projGrpOffsets = append(projGrpOffsets, offs) 386 } 387 return projGrpOffsets, nil 388 } 389 390 func generateAggregateParams(aggrs []operators.Aggr, aggrParamOffsets [][]offsets, proj *projection, pushed bool) ([]*engine.AggregateParams, error) { 391 aggrParams := make([]*engine.AggregateParams, len(aggrs)) 392 for idx, paramOffset := range aggrParamOffsets { 393 aggr := aggrs[idx] 394 incomingOffset := paramOffset[0].col 395 var offset int 396 if proj != nil { 397 var aggrExpr sqlparser.Expr 398 for _, ofs := range paramOffset { 399 curr := &sqlparser.Offset{V: ofs.col} 400 if aggrExpr == nil { 401 aggrExpr = curr 402 } else { 403 aggrExpr = &sqlparser.BinaryExpr{ 404 Operator: sqlparser.MultOp, 405 Left: aggrExpr, 406 Right: &sqlparser.FuncExpr{ 407 Name: sqlparser.NewIdentifierCI("coalesce"), 408 Exprs: sqlparser.SelectExprs{ 409 &sqlparser.AliasedExpr{Expr: curr}, 410 &sqlparser.AliasedExpr{Expr: sqlparser.NewIntLiteral("1")}, 411 }, 412 }, 413 } 414 } 415 } 416 417 pos, err := proj.addColumn(aggr.Index, aggrExpr, aggr.Alias) 418 if err != nil { 419 return nil, err 420 } 421 offset = pos 422 } else { 423 offset = incomingOffset 424 } 425 426 opcode := engine.AggregateSum 427 switch aggr.OpCode { 428 case engine.AggregateMin, engine.AggregateMax, engine.AggregateRandom: 429 opcode = aggr.OpCode 430 case engine.AggregateCount, engine.AggregateCountStar, engine.AggregateCountDistinct, engine.AggregateSumDistinct: 431 if !pushed { 432 opcode = aggr.OpCode 433 } 434 } 435 436 aggrParams[idx] = &engine.AggregateParams{ 437 Opcode: opcode, 438 Col: offset, 439 Alias: aggr.Alias, 440 Expr: aggr.Original.Expr, 441 Original: aggr.Original, 442 OrigOpcode: aggr.OpCode, 443 } 444 } 445 return aggrParams, nil 446 } 447 448 func addColumnsToOA( 449 ctx *plancontext.PlanningContext, 450 oa *orderedAggregate, 451 // these are the group by expressions that where added because we have unique aggregations 452 distinctGroupBy []operators.GroupBy, 453 // these are the aggregate params we already have for non-distinct aggregations 454 aggrParams []*engine.AggregateParams, 455 // distinctOffsets mark out where we need to use the distinctGroupBy offsets 456 // to create *engine.AggregateParams for the distinct aggregations 457 distinctOffsets []int, 458 // these are the offsets for the group by params 459 groupings []offsets, 460 // aggregationExprs are all the original aggregation expressions the query requested 461 aggregationExprs []operators.Aggr, 462 ) { 463 if len(distinctGroupBy) == 0 { 464 // no distinct aggregations 465 oa.aggregates = aggrParams 466 } else { 467 count := len(groupings) - len(distinctOffsets) 468 addDistinctAggr := func(offset int) { 469 // the last grouping we pushed is the one we added for the distinct aggregation 470 o := groupings[count] 471 count++ 472 a := aggregationExprs[offset] 473 collID := ctx.SemTable.CollationForExpr(a.Func.GetArg()) 474 oa.aggregates = append(oa.aggregates, &engine.AggregateParams{ 475 Opcode: a.OpCode, 476 Col: o.col, 477 KeyCol: o.col, 478 WAssigned: o.wsCol >= 0, 479 WCol: o.wsCol, 480 Alias: a.Alias, 481 Original: a.Original, 482 CollationID: collID, 483 }) 484 } 485 lastOffset := distinctOffsets[len(distinctOffsets)-1] 486 distinctIdx := 0 487 for i := 0; i <= lastOffset || i <= len(aggrParams); i++ { 488 for distinctIdx < len(distinctOffsets) && i == distinctOffsets[distinctIdx] { 489 // we loop here since we could be dealing with multiple distinct aggregations after each other 490 addDistinctAggr(i) 491 distinctIdx++ 492 } 493 if i < len(aggrParams) { 494 oa.aggregates = append(oa.aggregates, aggrParams[i]) 495 } 496 } 497 498 // we have to remove the tail of the grouping offsets, so we only have the offsets for the GROUP BY in the query 499 groupings = groupings[:len(groupings)-len(distinctOffsets)] 500 } 501 502 for i, grouping := range groupings { 503 oa.groupByKeys[i].KeyCol = grouping.col 504 oa.groupByKeys[i].WeightStringCol = grouping.wsCol 505 } 506 } 507 508 // handleDistinctAggr takes in a slice of aggregations and returns GroupBy elements that replace 509 // the distinct aggregations in the input, along with a slice of offsets and the non-distinct aggregations left, 510 // so we can later reify the original aggregations 511 func (hp *horizonPlanning) handleDistinctAggr(ctx *plancontext.PlanningContext, exprs []operators.Aggr) ( 512 distincts []operators.GroupBy, offsets []int, aggrs []operators.Aggr, err error) { 513 var distinctExpr sqlparser.Expr 514 for i, expr := range exprs { 515 if !expr.Distinct { 516 aggrs = append(aggrs, expr) 517 continue 518 } 519 520 inner, innerWS, err := hp.qp.GetSimplifiedExpr(expr.Func.GetArg()) 521 if err != nil { 522 return nil, nil, nil, err 523 } 524 if exprHasVindex(ctx.SemTable, innerWS, false) { 525 aggrs = append(aggrs, expr) 526 continue 527 } 528 if distinctExpr == nil { 529 distinctExpr = innerWS 530 } else { 531 if !ctx.SemTable.EqualsExpr(distinctExpr, innerWS) { 532 err = vterrors.VT12001(fmt.Sprintf("only one DISTINCT aggregation is allowed in a SELECT: %s", sqlparser.String(expr.Original))) 533 return nil, nil, nil, err 534 } 535 } 536 distincts = append(distincts, operators.GroupBy{ 537 Inner: inner, 538 WeightStrExpr: innerWS, 539 InnerIndex: expr.Index, 540 }) 541 offsets = append(offsets, i) 542 } 543 return 544 } 545 546 func (hp *horizonPlanning) planAggregationWithoutOA(ctx *plancontext.PlanningContext, plan logicalPlan) error { 547 for _, expr := range hp.qp.SelectExprs { 548 aliasedExpr, err := expr.GetAliasedExpr() 549 if err != nil { 550 return err 551 } 552 _, _, err = pushProjection(ctx, aliasedExpr, plan, true, false, false) 553 if err != nil { 554 return err 555 } 556 } 557 for _, expr := range hp.qp.GetGrouping() { 558 // since all the grouping will be done at the mysql level, 559 // we know that we won't need any weight_string() calls 560 err := planGroupByGen4(ctx, expr, plan /*weighString*/, false) 561 if err != nil { 562 return err 563 } 564 } 565 return nil 566 } 567 568 type offsets struct { 569 col, wsCol int 570 } 571 572 func newOffset(col int) offsets { 573 return offsets{col: col, wsCol: -1} 574 } 575 576 func (hp *horizonPlanning) createGroupingsForColumns(columns []*sqlparser.ColName) ([]operators.GroupBy, error) { 577 var lhsGrouping []operators.GroupBy 578 for _, lhsColumn := range columns { 579 expr, wsExpr, err := hp.qp.GetSimplifiedExpr(lhsColumn) 580 if err != nil { 581 return nil, err 582 } 583 584 lhsGrouping = append(lhsGrouping, operators.GroupBy{ 585 Inner: expr, 586 WeightStrExpr: wsExpr, 587 }) 588 } 589 return lhsGrouping, nil 590 } 591 592 func hasUniqueVindex(semTable *semantics.SemTable, groupByExprs []operators.GroupBy) bool { 593 for _, groupByExpr := range groupByExprs { 594 if exprHasUniqueVindex(semTable, groupByExpr.WeightStrExpr) { 595 return true 596 } 597 } 598 return false 599 } 600 601 func (hp *horizonPlanning) planOrderBy(ctx *plancontext.PlanningContext, orderExprs []operators.OrderBy, plan logicalPlan) (logicalPlan, error) { 602 switch plan := plan.(type) { 603 case *routeGen4: 604 newPlan, err := planOrderByForRoute(ctx, orderExprs, plan, hp.qp.HasStar) 605 if err != nil { 606 return nil, err 607 } 608 return newPlan, nil 609 case *joinGen4: 610 newPlan, err := hp.planOrderByForJoin(ctx, orderExprs, plan) 611 if err != nil { 612 return nil, err 613 } 614 615 return newPlan, nil 616 case *hashJoin: 617 newPlan, err := hp.planOrderByForHashJoin(ctx, orderExprs, plan) 618 if err != nil { 619 return nil, err 620 } 621 622 return newPlan, nil 623 case *orderedAggregate: 624 // remove ORDER BY NULL from the list of order by expressions since we will be doing the ordering on vtgate level so NULL is not useful 625 var orderExprsWithoutNils []operators.OrderBy 626 for _, expr := range orderExprs { 627 if sqlparser.IsNull(expr.Inner.Expr) { 628 continue 629 } 630 orderExprsWithoutNils = append(orderExprsWithoutNils, expr) 631 } 632 orderExprs = orderExprsWithoutNils 633 634 for _, order := range orderExprs { 635 if sqlparser.ContainsAggregation(order.WeightStrExpr) { 636 ms, err := createMemorySortPlanOnAggregation(ctx, plan, orderExprs) 637 if err != nil { 638 return nil, err 639 } 640 return ms, nil 641 } 642 } 643 newInput, err := hp.planOrderBy(ctx, orderExprs, plan.input) 644 if err != nil { 645 return nil, err 646 } 647 plan.input = newInput 648 return plan, nil 649 case *memorySort: 650 return plan, nil 651 case *simpleProjection: 652 return hp.createMemorySortPlan(ctx, plan, orderExprs, true) 653 case *vindexFunc: 654 // This is evaluated at VTGate only, so weight_string function cannot be used. 655 return hp.createMemorySortPlan(ctx, plan, orderExprs /* useWeightStr */, false) 656 case *limit, *semiJoin, *filter, *pulloutSubquery, *projection: 657 inputs := plan.Inputs() 658 if len(inputs) == 0 { 659 break 660 } 661 newFirstInput, err := hp.planOrderBy(ctx, orderExprs, inputs[0]) 662 if err != nil { 663 return nil, err 664 } 665 inputs[0] = newFirstInput 666 err = plan.Rewrite(inputs...) 667 if err != nil { 668 return nil, err 669 } 670 return plan, nil 671 } 672 return nil, vterrors.VT13001(fmt.Sprintf("ORDER BY in complex query %T", plan)) 673 } 674 675 func isSpecialOrderBy(o operators.OrderBy) bool { 676 if sqlparser.IsNull(o.Inner.Expr) { 677 return true 678 } 679 f, isFunction := o.Inner.Expr.(*sqlparser.FuncExpr) 680 return isFunction && f.Name.Lowered() == "rand" 681 } 682 683 func planOrderByForRoute(ctx *plancontext.PlanningContext, orderExprs []operators.OrderBy, plan *routeGen4, hasStar bool) (logicalPlan, error) { 684 for _, order := range orderExprs { 685 err := checkOrderExprCanBePlannedInScatter(ctx, plan, order, hasStar) 686 if err != nil { 687 return nil, err 688 } 689 plan.Select.AddOrder(order.Inner) 690 if isSpecialOrderBy(order) { 691 continue 692 } 693 var wsExpr sqlparser.Expr 694 if ctx.SemTable.NeedsWeightString(order.Inner.Expr) { 695 wsExpr = order.WeightStrExpr 696 } 697 698 offset, weightStringOffset, err := wrapAndPushExpr(ctx, order.Inner.Expr, wsExpr, plan) 699 if err != nil { 700 return nil, err 701 } 702 plan.eroute.OrderBy = append(plan.eroute.OrderBy, engine.OrderByParams{ 703 Col: offset, 704 WeightStringCol: weightStringOffset, 705 Desc: order.Inner.Direction == sqlparser.DescOrder, 706 CollationID: ctx.SemTable.CollationForExpr(order.Inner.Expr), 707 }) 708 } 709 return plan, nil 710 } 711 712 // checkOrderExprCanBePlannedInScatter verifies that the given order by expression can be planned. 713 // It checks if the expression exists in the plan's select list when the query is a scatter. 714 func checkOrderExprCanBePlannedInScatter(ctx *plancontext.PlanningContext, plan *routeGen4, order operators.OrderBy, hasStar bool) error { 715 if !hasStar { 716 return nil 717 } 718 sel := sqlparser.GetFirstSelect(plan.Select) 719 found := false 720 for _, expr := range sel.SelectExprs { 721 aliasedExpr, isAliasedExpr := expr.(*sqlparser.AliasedExpr) 722 if isAliasedExpr && ctx.SemTable.EqualsExpr(aliasedExpr.Expr, order.Inner.Expr) { 723 found = true 724 break 725 } 726 } 727 if !found { 728 return vterrors.VT12001(fmt.Sprintf("in scatter query: ORDER BY must reference a column in the SELECT list: %s", sqlparser.String(order.Inner))) 729 } 730 return nil 731 } 732 733 // wrapAndPushExpr pushes the expression and weighted_string function to the plan using semantics.SemTable 734 // It returns (expr offset, weight_string offset, error) 735 func wrapAndPushExpr(ctx *plancontext.PlanningContext, expr sqlparser.Expr, weightStrExpr sqlparser.Expr, plan logicalPlan) (int, int, error) { 736 offset, _, err := pushProjection(ctx, &sqlparser.AliasedExpr{Expr: expr}, plan, true, true, false) 737 if err != nil { 738 return 0, 0, err 739 } 740 if weightStrExpr == nil { 741 return offset, -1, nil 742 } 743 if !sqlparser.IsColName(expr) { 744 switch unary := expr.(type) { 745 case *sqlparser.CastExpr: 746 expr = unary.Expr 747 case *sqlparser.ConvertExpr: 748 expr = unary.Expr 749 } 750 if !sqlparser.IsColName(expr) { 751 return 0, 0, vterrors.VT13001(fmt.Sprintf("in scatter query: complex ORDER BY expression: %s", sqlparser.String(expr))) 752 } 753 } 754 qt := ctx.SemTable.TypeFor(expr) 755 wsNeeded := true 756 if qt != nil && sqltypes.IsNumber(*qt) { 757 wsNeeded = false 758 } 759 760 weightStringOffset := -1 761 if wsNeeded { 762 aliasedExpr := &sqlparser.AliasedExpr{Expr: weightStringFor(weightStrExpr)} 763 weightStringOffset, _, err = pushProjection(ctx, aliasedExpr, plan, true, true, false) 764 if err != nil { 765 return 0, 0, err 766 } 767 } 768 return offset, weightStringOffset, nil 769 } 770 771 func weightStringFor(expr sqlparser.Expr) sqlparser.Expr { 772 return &sqlparser.WeightStringFuncExpr{Expr: expr} 773 } 774 775 func (hp *horizonPlanning) planOrderByForHashJoin(ctx *plancontext.PlanningContext, orderExprs []operators.OrderBy, plan *hashJoin) (logicalPlan, error) { 776 if len(orderExprs) == 1 && isSpecialOrderBy(orderExprs[0]) { 777 rhs, err := hp.planOrderBy(ctx, orderExprs, plan.Right) 778 if err != nil { 779 return nil, err 780 } 781 plan.Right = rhs 782 return plan, nil 783 } 784 if orderExprsDependsOnTableSet(orderExprs, ctx.SemTable, plan.Right.ContainsTables()) { 785 newRight, err := hp.planOrderBy(ctx, orderExprs, plan.Right) 786 if err != nil { 787 return nil, err 788 } 789 plan.Right = newRight 790 return plan, nil 791 } 792 sortPlan, err := hp.createMemorySortPlan(ctx, plan, orderExprs, true) 793 if err != nil { 794 return nil, err 795 } 796 return sortPlan, nil 797 } 798 799 func (hp *horizonPlanning) planOrderByForJoin(ctx *plancontext.PlanningContext, orderExprs []operators.OrderBy, plan *joinGen4) (logicalPlan, error) { 800 if len(orderExprs) == 1 && isSpecialOrderBy(orderExprs[0]) { 801 lhs, err := hp.planOrderBy(ctx, orderExprs, plan.Left) 802 if err != nil { 803 return nil, err 804 } 805 rhs, err := hp.planOrderBy(ctx, orderExprs, plan.Right) 806 if err != nil { 807 return nil, err 808 } 809 plan.Left = lhs 810 plan.Right = rhs 811 return plan, nil 812 } 813 // We can only push down sorting on the LHS of the join. 814 // If the order is on the RHS, we need to do the sorting on the vtgate 815 if orderExprsDependsOnTableSet(orderExprs, ctx.SemTable, plan.Left.ContainsTables()) { 816 newLeft, err := hp.planOrderBy(ctx, orderExprs, plan.Left) 817 if err != nil { 818 return nil, err 819 } 820 plan.Left = newLeft 821 return plan, nil 822 } 823 sortPlan, err := hp.createMemorySortPlan(ctx, plan, orderExprs, true) 824 if err != nil { 825 return nil, err 826 } 827 return sortPlan, nil 828 } 829 830 func createMemorySortPlanOnAggregation(ctx *plancontext.PlanningContext, plan *orderedAggregate, orderExprs []operators.OrderBy) (logicalPlan, error) { 831 primitive := &engine.MemorySort{} 832 ms := &memorySort{ 833 resultsBuilder: resultsBuilder{ 834 logicalPlanCommon: newBuilderCommon(plan), 835 weightStrings: make(map[*resultColumn]int), 836 truncater: primitive, 837 }, 838 eMemorySort: primitive, 839 } 840 841 for _, order := range orderExprs { 842 offset, woffset, found := findExprInOrderedAggr(ctx, plan, order) 843 if !found { 844 return nil, vterrors.VT13001(fmt.Sprintf("expected to find ORDER BY expression (%s) in orderedAggregate", sqlparser.String(order.Inner))) 845 } 846 847 collationID := ctx.SemTable.CollationForExpr(order.WeightStrExpr) 848 ms.eMemorySort.OrderBy = append(ms.eMemorySort.OrderBy, engine.OrderByParams{ 849 Col: offset, 850 WeightStringCol: woffset, 851 Desc: order.Inner.Direction == sqlparser.DescOrder, 852 StarColFixedIndex: offset, 853 CollationID: collationID, 854 }) 855 } 856 return ms, nil 857 } 858 859 func findExprInOrderedAggr(ctx *plancontext.PlanningContext, plan *orderedAggregate, order operators.OrderBy) (keyCol int, weightStringCol int, found bool) { 860 for _, key := range plan.groupByKeys { 861 if ctx.SemTable.EqualsExpr(order.WeightStrExpr, key.Expr) || 862 ctx.SemTable.EqualsExpr(order.Inner.Expr, key.Expr) { 863 return key.KeyCol, key.WeightStringCol, true 864 } 865 } 866 for _, aggregate := range plan.aggregates { 867 if ctx.SemTable.EqualsExpr(order.WeightStrExpr, aggregate.Original.Expr) || 868 ctx.SemTable.EqualsExpr(order.Inner.Expr, aggregate.Original.Expr) { 869 return aggregate.Col, -1, true 870 } 871 } 872 return 0, 0, false 873 } 874 875 func (hp *horizonPlanning) createMemorySortPlan(ctx *plancontext.PlanningContext, plan logicalPlan, orderExprs []operators.OrderBy, useWeightStr bool) (logicalPlan, error) { 876 primitive := &engine.MemorySort{} 877 ms := &memorySort{ 878 resultsBuilder: resultsBuilder{ 879 logicalPlanCommon: newBuilderCommon(plan), 880 weightStrings: make(map[*resultColumn]int), 881 truncater: primitive, 882 }, 883 eMemorySort: primitive, 884 } 885 886 for _, order := range orderExprs { 887 wsExpr := order.WeightStrExpr 888 if !useWeightStr { 889 wsExpr = nil 890 } 891 offset, weightStringOffset, err := wrapAndPushExpr(ctx, order.Inner.Expr, wsExpr, plan) 892 if err != nil { 893 return nil, err 894 } 895 ms.eMemorySort.OrderBy = append(ms.eMemorySort.OrderBy, engine.OrderByParams{ 896 Col: offset, 897 WeightStringCol: weightStringOffset, 898 Desc: order.Inner.Direction == sqlparser.DescOrder, 899 StarColFixedIndex: offset, 900 CollationID: ctx.SemTable.CollationForExpr(order.Inner.Expr), 901 }) 902 } 903 return ms, nil 904 } 905 906 func orderExprsDependsOnTableSet(orderExprs []operators.OrderBy, semTable *semantics.SemTable, ts semantics.TableSet) bool { 907 for _, expr := range orderExprs { 908 exprDependencies := semTable.RecursiveDeps(expr.Inner.Expr) 909 if !exprDependencies.IsSolvedBy(ts) { 910 return false 911 } 912 } 913 return true 914 } 915 916 func (hp *horizonPlanning) planDistinct(ctx *plancontext.PlanningContext, plan logicalPlan) (logicalPlan, error) { 917 if !hp.qp.NeedsDistinct() { 918 return plan, nil 919 } 920 switch p := plan.(type) { 921 case *routeGen4: 922 // we always make the underlying query distinct, 923 // and then we might also add a distinct operator on top if it is needed 924 p.Select.MakeDistinct() 925 if p.isSingleShard() || selectHasUniqueVindex(ctx.SemTable, hp.qp.SelectExprs) { 926 return plan, nil 927 } 928 929 return hp.addDistinct(ctx, plan) 930 case *joinGen4, *pulloutSubquery: 931 return hp.addDistinct(ctx, plan) 932 case *orderedAggregate: 933 return hp.planDistinctOA(ctx.SemTable, p) 934 default: 935 return nil, vterrors.VT13001(fmt.Sprintf("unknown plan type for DISTINCT %T", plan)) 936 } 937 } 938 939 func (hp *horizonPlanning) planDistinctOA(semTable *semantics.SemTable, currPlan *orderedAggregate) (logicalPlan, error) { 940 oa := &orderedAggregate{ 941 resultsBuilder: resultsBuilder{ 942 logicalPlanCommon: newBuilderCommon(currPlan), 943 weightStrings: make(map[*resultColumn]int), 944 }, 945 } 946 for _, sExpr := range hp.qp.SelectExprs { 947 expr, err := sExpr.GetExpr() 948 if err != nil { 949 return nil, err 950 } 951 found := false 952 for _, grpParam := range currPlan.groupByKeys { 953 if semTable.EqualsExpr(expr, grpParam.Expr) { 954 found = true 955 oa.groupByKeys = append(oa.groupByKeys, grpParam) 956 break 957 } 958 } 959 if found { 960 continue 961 } 962 for _, aggrParam := range currPlan.aggregates { 963 if semTable.EqualsExpr(expr, aggrParam.Expr) { 964 found = true 965 oa.groupByKeys = append(oa.groupByKeys, &engine.GroupByParams{KeyCol: aggrParam.Col, WeightStringCol: -1, CollationID: semTable.CollationForExpr(expr)}) 966 break 967 } 968 } 969 if !found { 970 return nil, vterrors.VT13001(fmt.Sprintf("unable to plan DISTINCT query as the column is not projected: %s", sqlparser.String(sExpr.Col))) 971 } 972 } 973 return oa, nil 974 } 975 976 func (hp *horizonPlanning) addDistinct(ctx *plancontext.PlanningContext, plan logicalPlan) (logicalPlan, error) { 977 var orderExprs []operators.OrderBy 978 var groupByKeys []*engine.GroupByParams 979 for index, sExpr := range hp.qp.SelectExprs { 980 aliasExpr, err := sExpr.GetAliasedExpr() 981 if err != nil { 982 return nil, err 983 } 984 if isAmbiguousOrderBy(index, aliasExpr.As, hp.qp.SelectExprs) { 985 return nil, vterrors.VT13001(fmt.Sprintf("generating ORDER BY clause: ambiguous symbol reference: %s", sqlparser.String(aliasExpr.As))) 986 } 987 var inner sqlparser.Expr 988 if aliasExpr.As.IsEmpty() { 989 inner = aliasExpr.Expr 990 } else { 991 // If we have an alias, we need to use the alias and not the original expression 992 // to make sure dependencies work correctly, 993 // we simply copy the dependencies of the original expression here 994 inner = sqlparser.NewColName(aliasExpr.As.String()) 995 ctx.SemTable.CopyDependencies(aliasExpr.Expr, inner) 996 } 997 grpParam := &engine.GroupByParams{KeyCol: index, WeightStringCol: -1, CollationID: ctx.SemTable.CollationForExpr(inner), Expr: inner} 998 _, wOffset, err := wrapAndPushExpr(ctx, aliasExpr.Expr, aliasExpr.Expr, plan) 999 if err != nil { 1000 return nil, err 1001 } 1002 grpParam.WeightStringCol = wOffset 1003 groupByKeys = append(groupByKeys, grpParam) 1004 1005 orderExprs = append(orderExprs, operators.OrderBy{ 1006 Inner: &sqlparser.Order{Expr: inner}, 1007 WeightStrExpr: aliasExpr.Expr}, 1008 ) 1009 } 1010 innerPlan, err := hp.planOrderBy(ctx, orderExprs, plan) 1011 if err != nil { 1012 return nil, err 1013 } 1014 oa := &orderedAggregate{ 1015 resultsBuilder: resultsBuilder{ 1016 logicalPlanCommon: newBuilderCommon(innerPlan), 1017 weightStrings: make(map[*resultColumn]int), 1018 }, 1019 groupByKeys: groupByKeys, 1020 } 1021 return oa, nil 1022 } 1023 1024 func isAmbiguousOrderBy(index int, col sqlparser.IdentifierCI, exprs []operators.SelectExpr) bool { 1025 if col.String() == "" { 1026 return false 1027 } 1028 for i, expr := range exprs { 1029 if i == index { 1030 continue 1031 } 1032 aliasExpr, isAlias := expr.Col.(*sqlparser.AliasedExpr) 1033 if !isAlias { 1034 // TODO: handle star expression error 1035 return true 1036 } 1037 alias := aliasExpr.As 1038 if alias.IsEmpty() { 1039 if col, ok := aliasExpr.Expr.(*sqlparser.ColName); ok { 1040 alias = col.Name 1041 } 1042 } 1043 if col.Equal(alias) { 1044 return true 1045 } 1046 } 1047 return false 1048 } 1049 1050 func selectHasUniqueVindex(semTable *semantics.SemTable, sel []operators.SelectExpr) bool { 1051 for _, expr := range sel { 1052 exp, err := expr.GetExpr() 1053 if err != nil { 1054 // TODO: handle star expression error 1055 return false 1056 } 1057 if exprHasUniqueVindex(semTable, exp) { 1058 return true 1059 } 1060 } 1061 return false 1062 } 1063 1064 func (hp *horizonPlanning) planHaving(ctx *plancontext.PlanningContext, plan logicalPlan) (logicalPlan, error) { 1065 if hp.sel.Having == nil { 1066 return plan, nil 1067 } 1068 return pushHaving(ctx, hp.sel.Having.Expr, plan) 1069 } 1070 1071 func pushHaving(ctx *plancontext.PlanningContext, expr sqlparser.Expr, plan logicalPlan) (logicalPlan, error) { 1072 switch node := plan.(type) { 1073 case *routeGen4: 1074 sel := sqlparser.GetFirstSelect(node.Select) 1075 sel.AddHaving(expr) 1076 return plan, nil 1077 case *pulloutSubquery: 1078 return pushHaving(ctx, expr, node.underlying) 1079 case *simpleProjection: 1080 return nil, vterrors.VT13001("filtering on results of cross-shard derived table") 1081 case *orderedAggregate: 1082 return newFilter(ctx, plan, expr) 1083 } 1084 return nil, vterrors.VT13001(fmt.Sprintf("unreachable %T.filtering", plan)) 1085 } 1086 1087 func isJoin(plan logicalPlan) bool { 1088 switch plan.(type) { 1089 case *joinGen4, *hashJoin: 1090 return true 1091 default: 1092 return false 1093 } 1094 } 1095 1096 func exprHasUniqueVindex(semTable *semantics.SemTable, expr sqlparser.Expr) bool { 1097 return exprHasVindex(semTable, expr, true) 1098 } 1099 1100 func exprHasVindex(semTable *semantics.SemTable, expr sqlparser.Expr, hasToBeUnique bool) bool { 1101 col, isCol := expr.(*sqlparser.ColName) 1102 if !isCol { 1103 return false 1104 } 1105 ts := semTable.RecursiveDeps(expr) 1106 tableInfo, err := semTable.TableInfoFor(ts) 1107 if err != nil { 1108 return false 1109 } 1110 vschemaTable := tableInfo.GetVindexTable() 1111 for _, vindex := range vschemaTable.ColumnVindexes { 1112 if len(vindex.Columns) > 1 || hasToBeUnique && !vindex.IsUnique() { 1113 return false 1114 } 1115 if col.Name.Equal(vindex.Columns[0]) { 1116 return true 1117 } 1118 } 1119 return false 1120 } 1121 1122 func planSingleShardRoutePlan(sel sqlparser.SelectStatement, rb *routeGen4) error { 1123 err := stripDownQuery(sel, rb.Select) 1124 if err != nil { 1125 return err 1126 } 1127 return sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { 1128 if aliasedExpr, ok := node.(sqlparser.SelectExpr); ok { 1129 removeKeyspaceFromSelectExpr(aliasedExpr) 1130 } 1131 return true, nil 1132 }, rb.Select) 1133 1134 } 1135 1136 func removeKeyspaceFromSelectExpr(expr sqlparser.SelectExpr) { 1137 switch expr := expr.(type) { 1138 case *sqlparser.AliasedExpr: 1139 sqlparser.RemoveKeyspaceFromColName(expr.Expr) 1140 case *sqlparser.StarExpr: 1141 expr.TableName.Qualifier = sqlparser.NewIdentifierCS("") 1142 } 1143 } 1144 1145 func stripDownQuery(from, to sqlparser.SelectStatement) error { 1146 var err error 1147 1148 switch node := from.(type) { 1149 case *sqlparser.Select: 1150 toNode, ok := to.(*sqlparser.Select) 1151 if !ok { 1152 return vterrors.VT13001("AST did not match") 1153 } 1154 toNode.Distinct = node.Distinct 1155 toNode.GroupBy = node.GroupBy 1156 toNode.Having = node.Having 1157 toNode.OrderBy = node.OrderBy 1158 toNode.Comments = node.Comments 1159 toNode.SelectExprs = node.SelectExprs 1160 for _, expr := range toNode.SelectExprs { 1161 removeKeyspaceFromSelectExpr(expr) 1162 } 1163 case *sqlparser.Union: 1164 toNode, ok := to.(*sqlparser.Union) 1165 if !ok { 1166 return vterrors.VT13001("AST did not match") 1167 } 1168 err = stripDownQuery(node.Left, toNode.Left) 1169 if err != nil { 1170 return err 1171 } 1172 err = stripDownQuery(node.Right, toNode.Right) 1173 if err != nil { 1174 return err 1175 } 1176 toNode.OrderBy = node.OrderBy 1177 default: 1178 return vterrors.VT13001(fmt.Sprintf("this should not happen - we have covered all implementations of SelectStatement %T", from)) 1179 } 1180 return nil 1181 } 1182 1183 func planGroupByGen4(ctx *plancontext.PlanningContext, groupExpr operators.GroupBy, plan logicalPlan, wsAdded bool) error { 1184 switch node := plan.(type) { 1185 case *routeGen4: 1186 sel := node.Select.(*sqlparser.Select) 1187 sel.AddGroupBy(groupExpr.Inner) 1188 // If a weight_string function is added to the select list, 1189 // then we need to add that to the group by clause otherwise the query will fail on mysql with full_group_by error 1190 // as the weight_string function might not be functionally dependent on the group by. 1191 if wsAdded { 1192 sel.AddGroupBy(weightStringFor(groupExpr.WeightStrExpr)) 1193 } 1194 return nil 1195 case *pulloutSubquery: 1196 return planGroupByGen4(ctx, groupExpr, node.underlying, wsAdded) 1197 case *semiJoin: 1198 return vterrors.VT13001("GROUP BY in a query having a correlated subquery") 1199 default: 1200 return vterrors.VT13001(fmt.Sprintf("GROUP BY on: %T", plan)) 1201 } 1202 } 1203 1204 func getLengthOfProjection(groupingOffsets []offsets, aggregations []operators.Aggr) int { 1205 length := 0 1206 for _, groupBy := range groupingOffsets { 1207 if groupBy.wsCol != -1 { 1208 length++ 1209 } 1210 length++ 1211 } 1212 length += len(aggregations) 1213 return length 1214 }