vitess.io/vitess@v0.16.2/go/vt/vtgate/planbuilder/select.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/vt/log" 23 24 "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" 25 26 "vitess.io/vitess/go/vt/key" 27 28 "vitess.io/vitess/go/vt/vterrors" 29 30 "vitess.io/vitess/go/vt/vtgate/evalengine" 31 32 "vitess.io/vitess/go/vt/sqlparser" 33 "vitess.io/vitess/go/vt/vtgate/engine" 34 ) 35 36 func buildSelectPlan(query string) stmtPlanner { 37 return func(stmt sqlparser.Statement, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema) (*planResult, error) { 38 sel := stmt.(*sqlparser.Select) 39 if sel.With != nil { 40 return nil, vterrors.VT12001("WITH expression in SELECT statement") 41 } 42 43 p, err := handleDualSelects(sel, vschema) 44 if err != nil { 45 return nil, err 46 } 47 if p != nil { 48 return newPlanResult(p), nil 49 } 50 51 getPlan := func(sel *sqlparser.Select) (logicalPlan, error) { 52 pb := newPrimitiveBuilder(vschema, newJointab(reservedVars)) 53 if err := pb.processSelect(sel, reservedVars, nil, query); err != nil { 54 return nil, err 55 } 56 if err := pb.plan.Wireup(pb.plan, pb.jt); err != nil { 57 return nil, err 58 } 59 return pb.plan, nil 60 } 61 62 plan, err := getPlan(sel) 63 if err != nil { 64 return nil, err 65 } 66 67 if shouldRetryAfterPredicateRewriting(plan) { 68 // by transforming the predicates to CNF, the planner will sometimes find better plans 69 primitive := rewriteToCNFAndReplan(stmt, getPlan) 70 if primitive != nil { 71 return newPlanResult(primitive), nil 72 } 73 } 74 primitive := plan.Primitive() 75 if rb, ok := primitive.(*engine.Route); ok { 76 // this is done because engine.Route doesn't handle the empty result well 77 // if it doesn't find a shard to send the query to. 78 // All other engine primitives can handle this, so we only need it when 79 // Route is the last (and only) instruction before the user sees a result 80 if isOnlyDual(sel) || (len(sel.GroupBy) == 0 && sel.SelectExprs.AllAggregation()) { 81 rb.NoRoutesSpecialHandling = true 82 } 83 } 84 85 return newPlanResult(primitive), nil 86 } 87 } 88 89 func rewriteToCNFAndReplan(stmt sqlparser.Statement, getPlan func(sel *sqlparser.Select) (logicalPlan, error)) engine.Primitive { 90 rewritten := sqlparser.RewritePredicate(stmt) 91 sel2, isSelect := rewritten.(*sqlparser.Select) 92 if isSelect { 93 log.Infof("retrying plan after cnf: %s", sqlparser.String(sel2)) 94 plan2, err := getPlan(sel2) 95 if err == nil && !shouldRetryAfterPredicateRewriting(plan2) { 96 // we only use this new plan if it's better than the old one we got 97 return plan2.Primitive() 98 } 99 } 100 return nil 101 } 102 103 func shouldRetryAfterPredicateRewriting(plan logicalPlan) bool { 104 // if we have a I_S query, but have not found table_schema or table_name, let's try CNF 105 var opcode engine.Opcode 106 var sysTableTableName map[string]evalengine.Expr 107 var sysTableTableSchema []evalengine.Expr 108 109 switch routePlan := plan.(type) { 110 case *routeGen4: 111 opcode = routePlan.eroute.Opcode 112 sysTableTableName = routePlan.eroute.SysTableTableName 113 sysTableTableSchema = routePlan.eroute.SysTableTableSchema 114 case *route: 115 opcode = routePlan.eroute.Opcode 116 sysTableTableName = routePlan.eroute.SysTableTableName 117 sysTableTableSchema = routePlan.eroute.SysTableTableSchema 118 default: 119 return false 120 } 121 122 return opcode == engine.DBA && 123 len(sysTableTableName) == 0 && 124 len(sysTableTableSchema) == 0 125 } 126 127 // processSelect builds a primitive tree for the given query or subquery. 128 // The tree built by this function has the following general structure: 129 // 130 // The leaf nodes can be a route, vindexFunc or subquery. In the symtab, 131 // the tables map has columns that point to these leaf nodes. A subquery 132 // itself contains a logicalPlan tree, but it's opaque and is made to look 133 // like a table for the analysis of the current tree. 134 // 135 // The leaf nodes are usually tied together by join nodes. While the join 136 // nodes are built, they have ON clauses. Those are analyzed and pushed 137 // down into the leaf nodes as the tree is formed. Join nodes are formed 138 // during analysis of the FROM clause. 139 // 140 // During the WHERE clause analysis, the target leaf node is identified 141 // for each part, and the PushFilter function is used to push the condition 142 // down. The same strategy is used for the other clauses. 143 // 144 // So, a typical plan would either be a simple leaf node, or may consist 145 // of leaf nodes tied together by join nodes. 146 // 147 // If a query has aggregates that cannot be pushed down, an aggregator 148 // primitive is built. The current orderedAggregate primitive can only 149 // be built on top of a route. The orderedAggregate expects the rows 150 // to be ordered as they are returned. This work is performed by the 151 // underlying route. This means that a compatible ORDER BY clause 152 // can also be handled by this combination of primitives. In this case, 153 // the tree would consist of an orderedAggregate whose input is a route. 154 // 155 // If a query has an ORDER BY, but the route is a scatter, then the 156 // ordering is pushed down into the route itself. This results in a simple 157 // route primitive. 158 // 159 // The LIMIT clause is the last construct of a query. If it cannot be 160 // pushed into a route, then a primitive is created on top of any 161 // of the above trees to make it discard unwanted rows. 162 func (pb *primitiveBuilder) processSelect(sel *sqlparser.Select, reservedVars *sqlparser.ReservedVars, outer *symtab, query string) error { 163 // Check and error if there is any locking function present in select expression. 164 for _, expr := range sel.SelectExprs { 165 if aExpr, ok := expr.(*sqlparser.AliasedExpr); ok && sqlparser.IsLockingFunc(aExpr.Expr) { 166 return vterrors.VT12001(fmt.Sprintf("%v is allowed only with dual", sqlparser.String(aExpr))) 167 } 168 } 169 if sel.SQLCalcFoundRows { 170 if outer != nil || query == "" { 171 return vterrors.VT03008("SQL_CALC_FOUND_ROWS") 172 } 173 sel.SQLCalcFoundRows = false 174 if sel.Limit != nil { 175 plan, _, err := buildSQLCalcFoundRowsPlan(query, sel, reservedVars, pb.vschema, planSelectV3) 176 if err != nil { 177 return err 178 } 179 pb.plan = plan 180 return nil 181 } 182 } 183 184 // Into is not supported in subquery. 185 if sel.Into != nil && (outer != nil || query == "") { 186 return vterrors.VT03008("INTO") 187 } 188 189 var where sqlparser.Expr 190 if sel.Where != nil { 191 where = sel.Where.Expr 192 } 193 if err := pb.processTableExprs(sel.From, reservedVars, where); err != nil { 194 return err 195 } 196 197 if rb, ok := pb.plan.(*route); ok { 198 // TODO(sougou): this can probably be improved. 199 directives := sel.Comments.Directives() 200 rb.eroute.QueryTimeout = queryTimeout(directives) 201 if rb.eroute.TargetDestination != nil { 202 return vterrors.VT12001("SELECT with a target destination") 203 } 204 if directives.IsSet(sqlparser.DirectiveScatterErrorsAsWarnings) { 205 rb.eroute.ScatterErrorsAsWarnings = true 206 } 207 } 208 209 // Set the outer symtab after processing of FROM clause. 210 // This is because correlation is not allowed there. 211 pb.st.Outer = outer 212 if sel.Where != nil { 213 if err := pb.pushFilter(sel.Where.Expr, sqlparser.WhereStr, reservedVars); err != nil { 214 return err 215 } 216 } 217 if err := pb.checkAggregates(sel); err != nil { 218 return err 219 } 220 if err := pb.pushSelectExprs(sel, reservedVars); err != nil { 221 return err 222 } 223 if sel.Having != nil { 224 if err := pb.pushFilter(sel.Having.Expr, sqlparser.HavingStr, reservedVars); err != nil { 225 return err 226 } 227 } 228 if err := pb.pushOrderBy(sel.OrderBy); err != nil { 229 return err 230 } 231 if err := pb.pushLimit(sel.Limit); err != nil { 232 return err 233 } 234 235 return setMiscFunc(pb.plan, sel) 236 } 237 238 func setMiscFunc(in logicalPlan, sel *sqlparser.Select) error { 239 _, err := visit(in, func(plan logicalPlan) (bool, logicalPlan, error) { 240 switch node := plan.(type) { 241 case *route: 242 err := copyCommentsAndLocks(node.Select, sel, node.eroute.Opcode) 243 if err != nil { 244 return false, nil, err 245 } 246 return true, node, nil 247 case *routeGen4: 248 err := copyCommentsAndLocks(node.Select, sel, node.eroute.Opcode) 249 if err != nil { 250 return false, nil, err 251 } 252 return true, node, nil 253 } 254 return true, plan, nil 255 }) 256 257 if err != nil { 258 return err 259 } 260 return nil 261 } 262 263 func copyCommentsAndLocks(statement sqlparser.SelectStatement, sel *sqlparser.Select, opcode engine.Opcode) error { 264 query := sqlparser.GetFirstSelect(statement) 265 query.Comments = sel.Comments 266 query.Lock = sel.Lock 267 if sel.Into != nil { 268 if opcode != engine.Unsharded { 269 return vterrors.VT12001("INTO on sharded keyspace") 270 } 271 query.Into = sel.Into 272 } 273 return nil 274 } 275 276 func buildSQLCalcFoundRowsPlan( 277 originalQuery string, 278 sel *sqlparser.Select, 279 reservedVars *sqlparser.ReservedVars, 280 vschema plancontext.VSchema, 281 planSelect func(reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, sel *sqlparser.Select) (*jointab, logicalPlan, []string, error), 282 ) (logicalPlan, []string, error) { 283 ljt, limitPlan, _, err := planSelect(reservedVars, vschema, sel) 284 if err != nil { 285 return nil, nil, err 286 } 287 288 statement2, reserved2, err := sqlparser.Parse2(originalQuery) 289 if err != nil { 290 return nil, nil, err 291 } 292 sel2 := statement2.(*sqlparser.Select) 293 294 sel2.SQLCalcFoundRows = false 295 sel2.OrderBy = nil 296 sel2.Limit = nil 297 298 countStartExpr := []sqlparser.SelectExpr{&sqlparser.AliasedExpr{ 299 Expr: &sqlparser.CountStar{}, 300 }} 301 if sel2.GroupBy == nil && sel2.Having == nil { 302 // if there is no grouping, we can use the same query and 303 // just replace the SELECT sub-clause to have a single count(*) 304 sel2.SelectExprs = countStartExpr 305 } else { 306 // when there is grouping, we have to move the original query into a derived table. 307 // select id, sum(12) from user group by id => 308 // select count(*) from (select id, sum(12) from user group by id) t 309 sel3 := &sqlparser.Select{ 310 SelectExprs: countStartExpr, 311 From: []sqlparser.TableExpr{ 312 &sqlparser.AliasedTableExpr{ 313 Expr: &sqlparser.DerivedTable{Select: sel2}, 314 As: sqlparser.NewIdentifierCS("t"), 315 }, 316 }, 317 } 318 sel2 = sel3 319 } 320 321 reservedVars2 := sqlparser.NewReservedVars("vtg", reserved2) 322 323 cjt, countPlan, tablesUsed, err := planSelect(reservedVars2, vschema, sel2) 324 if err != nil { 325 return nil, nil, err 326 } 327 return &sqlCalcFoundRows{LimitQuery: limitPlan, CountQuery: countPlan, ljt: ljt, cjt: cjt}, tablesUsed, nil 328 } 329 330 func planSelectV3(reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, sel *sqlparser.Select) (*jointab, logicalPlan, []string, error) { 331 ljt := newJointab(reservedVars) 332 frpb := newPrimitiveBuilder(vschema, ljt) 333 err := frpb.processSelect(sel, reservedVars, nil, "") 334 return ljt, frpb.plan, nil, err 335 } 336 337 func handleDualSelects(sel *sqlparser.Select, vschema plancontext.VSchema) (engine.Primitive, error) { 338 if !isOnlyDual(sel) { 339 return nil, nil 340 } 341 342 exprs := make([]evalengine.Expr, len(sel.SelectExprs)) 343 cols := make([]string, len(sel.SelectExprs)) 344 var lockFunctions []*engine.LockFunc 345 for i, e := range sel.SelectExprs { 346 expr, ok := e.(*sqlparser.AliasedExpr) 347 if !ok { 348 return nil, nil 349 } 350 var err error 351 lFunc, isLFunc := expr.Expr.(*sqlparser.LockingFunc) 352 if isLFunc { 353 elem := &engine.LockFunc{Typ: expr.Expr.(*sqlparser.LockingFunc)} 354 if lFunc.Name != nil { 355 n, err := evalengine.Translate(lFunc.Name, nil) 356 if err != nil { 357 return nil, err 358 } 359 elem.Name = n 360 } 361 lockFunctions = append(lockFunctions, elem) 362 continue 363 } 364 if len(lockFunctions) > 0 { 365 return nil, vterrors.VT12001(fmt.Sprintf("LOCK function and other expression: [%s] in same select query", sqlparser.String(expr))) 366 } 367 exprs[i], err = evalengine.Translate(expr.Expr, evalengine.LookupDefaultCollation(vschema.ConnCollation())) 368 if err != nil { 369 return nil, nil 370 } 371 cols[i] = expr.As.String() 372 if cols[i] == "" { 373 cols[i] = sqlparser.String(expr.Expr) 374 } 375 } 376 if len(lockFunctions) > 0 { 377 return buildLockingPrimitive(sel, vschema, lockFunctions) 378 } 379 return &engine.Projection{ 380 Exprs: exprs, 381 Cols: cols, 382 Input: &engine.SingleRow{}, 383 }, nil 384 } 385 386 func buildLockingPrimitive(sel *sqlparser.Select, vschema plancontext.VSchema, lockFunctions []*engine.LockFunc) (engine.Primitive, error) { 387 ks, err := vschema.FirstSortedKeyspace() 388 if err != nil { 389 return nil, err 390 } 391 buf := sqlparser.NewTrackedBuffer(sqlparser.FormatImpossibleQuery).WriteNode(sel) 392 return &engine.Lock{ 393 Keyspace: ks, 394 TargetDestination: key.DestinationKeyspaceID{0}, 395 FieldQuery: buf.String(), 396 LockFunctions: lockFunctions, 397 }, nil 398 } 399 400 func isOnlyDual(sel *sqlparser.Select) bool { 401 if sel.Where != nil || sel.GroupBy != nil || sel.Having != nil || sel.Limit != nil || sel.OrderBy != nil { 402 // we can only deal with queries without any other subclauses - just SELECT and FROM, nothing else is allowed 403 return false 404 } 405 406 if len(sel.From) > 1 { 407 return false 408 } 409 table, ok := sel.From[0].(*sqlparser.AliasedTableExpr) 410 if !ok { 411 return false 412 } 413 tableName, ok := table.Expr.(sqlparser.TableName) 414 415 return ok && tableName.Name.String() == "dual" && tableName.Qualifier.IsEmpty() 416 } 417 418 // pushFilter identifies the target route for the specified bool expr, 419 // pushes it down, and updates the route info if the new constraint improves 420 // the primitive. This function can push to a WHERE or HAVING clause. 421 func (pb *primitiveBuilder) pushFilter(in sqlparser.Expr, whereType string, reservedVars *sqlparser.ReservedVars) error { 422 filters := sqlparser.SplitAndExpression(nil, in) 423 reorderBySubquery(filters) 424 for _, filter := range filters { 425 pullouts, origin, expr, err := pb.findOrigin(filter, reservedVars) 426 if err != nil { 427 return err 428 } 429 rut, isRoute := origin.(*route) 430 if isRoute && rut.eroute.Opcode == engine.DBA { 431 err := pb.findSysInfoRoutingPredicates(expr, rut, reservedVars) 432 if err != nil { 433 return err 434 } 435 } 436 // The returned expression may be complex. Resplit before pushing. 437 for _, subexpr := range sqlparser.SplitAndExpression(nil, expr) { 438 pb.plan, err = planFilter(pb, pb.plan, subexpr, whereType, origin) 439 if err != nil { 440 return err 441 } 442 } 443 pb.addPullouts(pullouts) 444 } 445 return nil 446 } 447 448 // reorderBySubquery reorders the filters by pushing subqueries 449 // to the end. This allows the non-subquery filters to be 450 // pushed first because they can potentially improve the routing 451 // plan, which can later allow a filter containing a subquery 452 // to successfully merge with the corresponding route. 453 func reorderBySubquery(filters []sqlparser.Expr) { 454 max := len(filters) 455 for i := 0; i < max; i++ { 456 if !hasSubquery(filters[i]) { 457 continue 458 } 459 saved := filters[i] 460 for j := i; j < len(filters)-1; j++ { 461 filters[j] = filters[j+1] 462 } 463 filters[len(filters)-1] = saved 464 max-- 465 } 466 } 467 468 // addPullouts adds the pullout subqueries to the primitiveBuilder. 469 func (pb *primitiveBuilder) addPullouts(pullouts []*pulloutSubquery) { 470 for _, pullout := range pullouts { 471 pullout.setUnderlying(pb.plan) 472 pb.plan = pullout 473 pb.plan.Reorder(0) 474 } 475 } 476 477 // pushSelectExprs identifies the target route for the 478 // select expressions and pushes them down. 479 func (pb *primitiveBuilder) pushSelectExprs(sel *sqlparser.Select, reservedVars *sqlparser.ReservedVars) error { 480 resultColumns, err := pb.pushSelectRoutes(sel.SelectExprs, reservedVars) 481 if err != nil { 482 return err 483 } 484 pb.st.SetResultColumns(resultColumns) 485 return pb.pushGroupBy(sel) 486 } 487 488 // pushSelectRoutes is a convenience function that pushes all the select 489 // expressions and returns the list of resultColumns generated for it. 490 func (pb *primitiveBuilder) pushSelectRoutes(selectExprs sqlparser.SelectExprs, reservedVars *sqlparser.ReservedVars) ([]*resultColumn, error) { 491 resultColumns := make([]*resultColumn, 0, len(selectExprs)) 492 for _, node := range selectExprs { 493 switch node := node.(type) { 494 case *sqlparser.AliasedExpr: 495 pullouts, origin, expr, err := pb.findOrigin(node.Expr, reservedVars) 496 if err != nil { 497 return nil, err 498 } 499 node.Expr = expr 500 newBuilder, rc, _, err := planProjection(pb, pb.plan, node, origin) 501 if err != nil { 502 return nil, err 503 } 504 pb.plan = newBuilder 505 resultColumns = append(resultColumns, rc) 506 pb.addPullouts(pullouts) 507 case *sqlparser.StarExpr: 508 var expanded bool 509 var err error 510 resultColumns, expanded, err = pb.expandStar(resultColumns, node) 511 if err != nil { 512 return nil, err 513 } 514 if expanded { 515 continue 516 } 517 // We'll allow select * for simple routes. 518 rb, ok := pb.plan.(*route) 519 if !ok { 520 return nil, vterrors.VT12001("'*' expression in cross-shard query") 521 } 522 // Validate keyspace reference if any. 523 if !node.TableName.IsEmpty() { 524 if _, err := pb.st.FindTable(node.TableName); err != nil { 525 return nil, err 526 } 527 } 528 resultColumns = append(resultColumns, rb.PushAnonymous(node)) 529 case *sqlparser.Nextval: 530 rb, ok := pb.plan.(*route) 531 if !ok { 532 // This code is unreachable because the parser doesn't allow joins for next val statements. 533 return nil, vterrors.VT12001("SELECT NEXT query in cross-shard query") 534 } 535 if rb.eroute.Opcode != engine.Next { 536 return nil, vterrors.VT03018() 537 } 538 rb.eroute.Opcode = engine.Next 539 resultColumns = append(resultColumns, rb.PushAnonymous(node)) 540 default: 541 return nil, vterrors.VT13001(fmt.Sprintf("unexpected SELECT expression type: %T", node)) 542 } 543 } 544 return resultColumns, nil 545 } 546 547 // expandStar expands a StarExpr and pushes the expanded 548 // expressions down if the tables have authoritative column lists. 549 // If not, it returns false. 550 // This function breaks the abstraction a bit: it directly sets the 551 // the Metadata for newly created expressions. In all other cases, 552 // the Metadata is set through a symtab Find. 553 func (pb *primitiveBuilder) expandStar(inrcs []*resultColumn, expr *sqlparser.StarExpr) (outrcs []*resultColumn, expanded bool, err error) { 554 tables := pb.st.AllTables() 555 if tables == nil { 556 // no table metadata available. 557 return inrcs, false, nil 558 } 559 if expr.TableName.IsEmpty() { 560 for _, t := range tables { 561 // All tables must have authoritative column lists. 562 if !t.isAuthoritative { 563 return inrcs, false, nil 564 } 565 } 566 singleTable := false 567 if len(tables) == 1 { 568 singleTable = true 569 } 570 for _, t := range tables { 571 for _, col := range t.columnNames { 572 var expr *sqlparser.AliasedExpr 573 if singleTable { 574 // If there's only one table, we use unqualified column names. 575 expr = &sqlparser.AliasedExpr{ 576 Expr: &sqlparser.ColName{ 577 Metadata: t.columns[col.Lowered()], 578 Name: col, 579 }, 580 } 581 } else { 582 // If a and b have id as their column, then 583 // select * from a join b should result in 584 // select a.id as id, b.id as id from a join b. 585 expr = &sqlparser.AliasedExpr{ 586 Expr: &sqlparser.ColName{ 587 Metadata: t.columns[col.Lowered()], 588 Name: col, 589 Qualifier: t.alias, 590 }, 591 As: col, 592 } 593 } 594 newBuilder, rc, _, err := planProjection(pb, pb.plan, expr, t.Origin()) 595 if err != nil { 596 // Unreachable because PushSelect won't fail on ColName. 597 return inrcs, false, err 598 } 599 pb.plan = newBuilder 600 inrcs = append(inrcs, rc) 601 } 602 } 603 return inrcs, true, nil 604 } 605 606 // Expression qualified with table name. 607 t, err := pb.st.FindTable(expr.TableName) 608 if err != nil { 609 return inrcs, false, err 610 } 611 if !t.isAuthoritative { 612 return inrcs, false, nil 613 } 614 for _, col := range t.columnNames { 615 expr := &sqlparser.AliasedExpr{ 616 Expr: &sqlparser.ColName{ 617 Metadata: t.columns[col.Lowered()], 618 Name: col, 619 Qualifier: expr.TableName, 620 }, 621 } 622 newBuilder, rc, _, err := planProjection(pb, pb.plan, expr, t.Origin()) 623 if err != nil { 624 // Unreachable because PushSelect won't fail on ColName. 625 return inrcs, false, err 626 } 627 pb.plan = newBuilder 628 inrcs = append(inrcs, rc) 629 } 630 return inrcs, true, nil 631 }