vitess.io/vitess@v0.16.2/go/vt/vtgate/planbuilder/gen4_planner.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 planbuilder 18 19 import ( 20 "fmt" 21 22 querypb "vitess.io/vitess/go/vt/proto/query" 23 "vitess.io/vitess/go/vt/sqlparser" 24 "vitess.io/vitess/go/vt/vterrors" 25 "vitess.io/vitess/go/vt/vtgate/engine" 26 "vitess.io/vitess/go/vt/vtgate/planbuilder/operators" 27 "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" 28 "vitess.io/vitess/go/vt/vtgate/semantics" 29 "vitess.io/vitess/go/vt/vtgate/vindexes" 30 ) 31 32 func gen4Planner(query string, plannerVersion querypb.ExecuteOptions_PlannerVersion) stmtPlanner { 33 return func(stmt sqlparser.Statement, reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema) (*planResult, error) { 34 switch stmt := stmt.(type) { 35 case sqlparser.SelectStatement: 36 return gen4SelectStmtPlanner(query, plannerVersion, stmt, reservedVars, vschema) 37 case *sqlparser.Update: 38 return gen4UpdateStmtPlanner(plannerVersion, stmt, reservedVars, vschema) 39 case *sqlparser.Delete: 40 return gen4DeleteStmtPlanner(plannerVersion, stmt, reservedVars, vschema) 41 default: 42 return nil, vterrors.VT12001(fmt.Sprintf("%T", stmt)) 43 } 44 } 45 } 46 47 func gen4SelectStmtPlanner( 48 query string, 49 plannerVersion querypb.ExecuteOptions_PlannerVersion, 50 stmt sqlparser.SelectStatement, 51 reservedVars *sqlparser.ReservedVars, 52 vschema plancontext.VSchema, 53 ) (*planResult, error) { 54 switch node := stmt.(type) { 55 case *sqlparser.Select: 56 if node.With != nil { 57 return nil, vterrors.VT12001("WITH expression in SELECT statement") 58 } 59 case *sqlparser.Union: 60 if node.With != nil { 61 return nil, vterrors.VT12001("WITH expression in UNION statement") 62 } 63 } 64 65 sel, isSel := stmt.(*sqlparser.Select) 66 if isSel { 67 // handle dual table for processing at vtgate. 68 p, err := handleDualSelects(sel, vschema) 69 if err != nil { 70 return nil, err 71 } 72 if p != nil { 73 used := "dual" 74 keyspace, ksErr := vschema.DefaultKeyspace() 75 if ksErr == nil { 76 // we are just getting the ks to log the correct table use. 77 // no need to fail this if we can't find the default keyspace 78 used = keyspace.Name + ".dual" 79 } 80 return newPlanResult(p, used), nil 81 } 82 83 if sel.SQLCalcFoundRows && sel.Limit != nil { 84 return gen4planSQLCalcFoundRows(vschema, sel, query, reservedVars) 85 } 86 // if there was no limit, we can safely ignore the SQLCalcFoundRows directive 87 sel.SQLCalcFoundRows = false 88 } 89 90 getPlan := func(selStatement sqlparser.SelectStatement) (logicalPlan, *semantics.SemTable, []string, error) { 91 return newBuildSelectPlan(selStatement, reservedVars, vschema, plannerVersion) 92 } 93 94 plan, _, tablesUsed, err := getPlan(stmt) 95 if err != nil { 96 return nil, err 97 } 98 99 if shouldRetryAfterPredicateRewriting(plan) { 100 // by transforming the predicates to CNF, the planner will sometimes find better plans 101 plan2, _, tablesUsed := gen4PredicateRewrite(stmt, getPlan) 102 if plan2 != nil { 103 return newPlanResult(plan2.Primitive(), tablesUsed...), nil 104 } 105 } 106 107 primitive := plan.Primitive() 108 if !isSel { 109 return newPlanResult(primitive, tablesUsed...), nil 110 } 111 112 // this is done because engine.Route doesn't handle the empty result well 113 // if it doesn't find a shard to send the query to. 114 // All other engine primitives can handle this, so we only need it when 115 // Route is the last (and only) instruction before the user sees a result 116 if isOnlyDual(sel) || (len(sel.GroupBy) == 0 && sel.SelectExprs.AllAggregation()) { 117 switch prim := primitive.(type) { 118 case *engine.Route: 119 prim.NoRoutesSpecialHandling = true 120 case *engine.VindexLookup: 121 prim.SendTo.NoRoutesSpecialHandling = true 122 } 123 } 124 return newPlanResult(primitive, tablesUsed...), nil 125 } 126 127 func gen4planSQLCalcFoundRows(vschema plancontext.VSchema, sel *sqlparser.Select, query string, reservedVars *sqlparser.ReservedVars) (*planResult, error) { 128 ksName := "" 129 if ks, _ := vschema.DefaultKeyspace(); ks != nil { 130 ksName = ks.Name 131 } 132 semTable, err := semantics.Analyze(sel, ksName, vschema) 133 if err != nil { 134 return nil, err 135 } 136 // record any warning as planner warning. 137 vschema.PlannerWarning(semTable.Warning) 138 139 plan, tablesUsed, err := buildSQLCalcFoundRowsPlan(query, sel, reservedVars, vschema, planSelectGen4) 140 if err != nil { 141 return nil, err 142 } 143 return newPlanResult(plan.Primitive(), tablesUsed...), nil 144 } 145 146 func planSelectGen4(reservedVars *sqlparser.ReservedVars, vschema plancontext.VSchema, sel *sqlparser.Select) (*jointab, logicalPlan, []string, error) { 147 plan, _, tablesUsed, err := newBuildSelectPlan(sel, reservedVars, vschema, 0) 148 if err != nil { 149 return nil, nil, nil, err 150 } 151 return nil, plan, tablesUsed, nil 152 } 153 154 func gen4PredicateRewrite(stmt sqlparser.Statement, getPlan func(selStatement sqlparser.SelectStatement) (logicalPlan, *semantics.SemTable, []string, error)) (logicalPlan, *semantics.SemTable, []string) { 155 rewritten, isSel := sqlparser.RewritePredicate(stmt).(sqlparser.SelectStatement) 156 if !isSel { 157 // Fail-safe code, should never happen 158 return nil, nil, nil 159 } 160 plan2, st, op, err := getPlan(rewritten) 161 if err == nil && !shouldRetryAfterPredicateRewriting(plan2) { 162 // we only use this new plan if it's better than the old one we got 163 return plan2, st, op 164 } 165 return nil, nil, nil 166 } 167 168 func newBuildSelectPlan( 169 selStmt sqlparser.SelectStatement, 170 reservedVars *sqlparser.ReservedVars, 171 vschema plancontext.VSchema, 172 version querypb.ExecuteOptions_PlannerVersion, 173 ) (plan logicalPlan, semTable *semantics.SemTable, tablesUsed []string, err error) { 174 ksName := "" 175 if ks, _ := vschema.DefaultKeyspace(); ks != nil { 176 ksName = ks.Name 177 } 178 semTable, err = semantics.Analyze(selStmt, ksName, vschema) 179 if err != nil { 180 return nil, nil, nil, err 181 } 182 // record any warning as planner warning. 183 vschema.PlannerWarning(semTable.Warning) 184 185 ctx := plancontext.NewPlanningContext(reservedVars, semTable, vschema, version) 186 187 if ks, _ := semTable.SingleUnshardedKeyspace(); ks != nil { 188 plan, tablesUsed, err = unshardedShortcut(ctx, selStmt, ks) 189 if err != nil { 190 return nil, nil, nil, err 191 } 192 plan, err = pushCommentDirectivesOnPlan(plan, selStmt) 193 if err != nil { 194 return nil, nil, nil, err 195 } 196 return plan, semTable, tablesUsed, err 197 } 198 199 // From this point on, we know it is not an unsharded query and return the NotUnshardedErr if there is any 200 if semTable.NotUnshardedErr != nil { 201 return nil, nil, nil, semTable.NotUnshardedErr 202 } 203 204 err = queryRewrite(semTable, reservedVars, selStmt) 205 if err != nil { 206 return nil, nil, nil, err 207 } 208 209 op, err := operators.PlanQuery(ctx, selStmt) 210 if err != nil { 211 return nil, nil, nil, err 212 } 213 214 plan, err = transformToLogicalPlan(ctx, op, true) 215 if err != nil { 216 return nil, nil, nil, err 217 } 218 219 optimizePlan(plan) 220 221 sel, isSel := selStmt.(*sqlparser.Select) 222 if isSel { 223 if err = setMiscFunc(plan, sel); err != nil { 224 return nil, nil, nil, err 225 } 226 } 227 228 if err = plan.WireupGen4(ctx); err != nil { 229 return nil, nil, nil, err 230 } 231 232 plan, err = pushCommentDirectivesOnPlan(plan, selStmt) 233 if err != nil { 234 return nil, nil, nil, err 235 } 236 237 return plan, semTable, operators.TablesUsed(op), nil 238 } 239 240 // optimizePlan removes unnecessary simpleProjections that have been created while planning 241 func optimizePlan(plan logicalPlan) { 242 for _, lp := range plan.Inputs() { 243 optimizePlan(lp) 244 } 245 246 this, ok := plan.(*simpleProjection) 247 if !ok { 248 return 249 } 250 251 input, ok := this.input.(*simpleProjection) 252 if !ok { 253 return 254 } 255 256 for i, col := range this.eSimpleProj.Cols { 257 this.eSimpleProj.Cols[i] = input.eSimpleProj.Cols[col] 258 } 259 this.input = input.input 260 } 261 262 func gen4UpdateStmtPlanner( 263 version querypb.ExecuteOptions_PlannerVersion, 264 updStmt *sqlparser.Update, 265 reservedVars *sqlparser.ReservedVars, 266 vschema plancontext.VSchema, 267 ) (*planResult, error) { 268 if updStmt.With != nil { 269 return nil, vterrors.VT12001("WITH expression in UPDATE statement") 270 } 271 272 ksName := "" 273 if ks, _ := vschema.DefaultKeyspace(); ks != nil { 274 ksName = ks.Name 275 } 276 semTable, err := semantics.Analyze(updStmt, ksName, vschema) 277 if err != nil { 278 return nil, err 279 } 280 // record any warning as planner warning. 281 vschema.PlannerWarning(semTable.Warning) 282 283 err = rewriteRoutedTables(updStmt, vschema) 284 if err != nil { 285 return nil, err 286 } 287 288 if ks, tables := semTable.SingleUnshardedKeyspace(); ks != nil { 289 edml := engine.NewDML() 290 edml.Keyspace = ks 291 edml.Table = tables 292 edml.Opcode = engine.Unsharded 293 edml.Query = generateQuery(updStmt) 294 upd := &engine.Update{DML: edml} 295 return newPlanResult(upd, operators.QualifiedTables(ks, tables)...), nil 296 } 297 298 if semTable.NotUnshardedErr != nil { 299 return nil, semTable.NotUnshardedErr 300 } 301 302 err = queryRewrite(semTable, reservedVars, updStmt) 303 if err != nil { 304 return nil, err 305 } 306 307 ctx := plancontext.NewPlanningContext(reservedVars, semTable, vschema, version) 308 309 op, err := operators.PlanQuery(ctx, updStmt) 310 if err != nil { 311 return nil, err 312 } 313 314 plan, err := transformToLogicalPlan(ctx, op, true) 315 if err != nil { 316 return nil, err 317 } 318 319 plan, err = pushCommentDirectivesOnPlan(plan, updStmt) 320 if err != nil { 321 return nil, err 322 } 323 324 setLockOnAllSelect(plan) 325 326 if err := plan.WireupGen4(ctx); err != nil { 327 return nil, err 328 } 329 330 return newPlanResult(plan.Primitive(), operators.TablesUsed(op)...), nil 331 } 332 333 func gen4DeleteStmtPlanner( 334 version querypb.ExecuteOptions_PlannerVersion, 335 deleteStmt *sqlparser.Delete, 336 reservedVars *sqlparser.ReservedVars, 337 vschema plancontext.VSchema, 338 ) (*planResult, error) { 339 if deleteStmt.With != nil { 340 return nil, vterrors.VT12001("WITH expression in DELETE statement") 341 } 342 343 var err error 344 if len(deleteStmt.TableExprs) == 1 && len(deleteStmt.Targets) == 1 { 345 deleteStmt, err = rewriteSingleTbl(deleteStmt) 346 if err != nil { 347 return nil, err 348 } 349 } 350 351 ksName := "" 352 if ks, _ := vschema.DefaultKeyspace(); ks != nil { 353 ksName = ks.Name 354 } 355 semTable, err := semantics.Analyze(deleteStmt, ksName, vschema) 356 if err != nil { 357 return nil, err 358 } 359 360 // record any warning as planner warning. 361 vschema.PlannerWarning(semTable.Warning) 362 err = rewriteRoutedTables(deleteStmt, vschema) 363 if err != nil { 364 return nil, err 365 } 366 367 if ks, tables := semTable.SingleUnshardedKeyspace(); ks != nil { 368 edml := engine.NewDML() 369 edml.Keyspace = ks 370 edml.Table = tables 371 edml.Opcode = engine.Unsharded 372 edml.Query = generateQuery(deleteStmt) 373 del := &engine.Delete{DML: edml} 374 return newPlanResult(del, operators.QualifiedTables(ks, tables)...), nil 375 } 376 377 if err := checkIfDeleteSupported(deleteStmt, semTable); err != nil { 378 return nil, err 379 } 380 381 err = queryRewrite(semTable, reservedVars, deleteStmt) 382 if err != nil { 383 return nil, err 384 } 385 386 ctx := plancontext.NewPlanningContext(reservedVars, semTable, vschema, version) 387 op, err := operators.PlanQuery(ctx, deleteStmt) 388 if err != nil { 389 return nil, err 390 } 391 392 plan, err := transformToLogicalPlan(ctx, op, true) 393 if err != nil { 394 return nil, err 395 } 396 397 plan, err = pushCommentDirectivesOnPlan(plan, deleteStmt) 398 if err != nil { 399 return nil, err 400 } 401 402 setLockOnAllSelect(plan) 403 404 if err := plan.WireupGen4(ctx); err != nil { 405 return nil, err 406 } 407 408 return newPlanResult(plan.Primitive(), operators.TablesUsed(op)...), nil 409 } 410 411 func rewriteRoutedTables(stmt sqlparser.Statement, vschema plancontext.VSchema) error { 412 // Rewrite routed tables 413 return sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { 414 aliasTbl, isAlias := node.(*sqlparser.AliasedTableExpr) 415 if !isAlias { 416 return true, nil 417 } 418 tableName, ok := aliasTbl.Expr.(sqlparser.TableName) 419 if !ok { 420 return true, nil 421 } 422 var vschemaTable *vindexes.Table 423 vschemaTable, _, _, _, _, err = vschema.FindTableOrVindex(tableName) 424 if err != nil { 425 return false, err 426 } 427 428 if vschemaTable.Name.String() != tableName.Name.String() { 429 name := tableName.Name 430 if aliasTbl.As.IsEmpty() { 431 // if the user hasn't specified an alias, we'll insert one here so the old table name still works 432 aliasTbl.As = sqlparser.NewIdentifierCS(name.String()) 433 } 434 tableName.Name = sqlparser.NewIdentifierCS(vschemaTable.Name.String()) 435 aliasTbl.Expr = tableName 436 } 437 438 return true, nil 439 }, stmt) 440 } 441 442 func setLockOnAllSelect(plan logicalPlan) { 443 _, _ = visit(plan, func(plan logicalPlan) (bool, logicalPlan, error) { 444 switch node := plan.(type) { 445 case *routeGen4: 446 node.Select.SetLock(sqlparser.ShareModeLock) 447 return true, node, nil 448 } 449 return true, plan, nil 450 }) 451 } 452 453 func planLimit(limit *sqlparser.Limit, plan logicalPlan) (logicalPlan, error) { 454 if limit == nil { 455 return plan, nil 456 } 457 rb, ok := plan.(*routeGen4) 458 if ok && rb.isSingleShard() { 459 rb.SetLimit(limit) 460 return plan, nil 461 } 462 463 lPlan, err := createLimit(plan, limit) 464 if err != nil { 465 return nil, err 466 } 467 468 // visit does not modify the plan. 469 _, err = visit(lPlan, setUpperLimit) 470 if err != nil { 471 return nil, err 472 } 473 return lPlan, nil 474 } 475 476 func planHorizon(ctx *plancontext.PlanningContext, plan logicalPlan, in sqlparser.SelectStatement, truncateColumns bool) (logicalPlan, error) { 477 switch node := in.(type) { 478 case *sqlparser.Select: 479 hp := horizonPlanning{ 480 sel: node, 481 } 482 483 replaceSubQuery(ctx, node) 484 var err error 485 plan, err = hp.planHorizon(ctx, plan, truncateColumns) 486 if err != nil { 487 return nil, err 488 } 489 plan, err = planLimit(node.Limit, plan) 490 if err != nil { 491 return nil, err 492 } 493 case *sqlparser.Union: 494 var err error 495 rb, isRoute := plan.(*routeGen4) 496 if !isRoute && ctx.SemTable.NotSingleRouteErr != nil { 497 return nil, ctx.SemTable.NotSingleRouteErr 498 } 499 if isRoute && rb.isSingleShard() { 500 err = planSingleShardRoutePlan(node, rb) 501 } else { 502 plan, err = planOrderByOnUnion(ctx, plan, node) 503 } 504 if err != nil { 505 return nil, err 506 } 507 508 plan, err = planLimit(node.Limit, plan) 509 if err != nil { 510 return nil, err 511 } 512 } 513 return plan, nil 514 515 } 516 517 func planOrderByOnUnion(ctx *plancontext.PlanningContext, plan logicalPlan, union *sqlparser.Union) (logicalPlan, error) { 518 qp, err := operators.CreateQPFromUnion(union) 519 if err != nil { 520 return nil, err 521 } 522 hp := horizonPlanning{ 523 qp: qp, 524 } 525 if len(qp.OrderExprs) > 0 { 526 plan, err = hp.planOrderBy(ctx, qp.OrderExprs, plan) 527 if err != nil { 528 return nil, err 529 } 530 } 531 return plan, nil 532 } 533 534 func pushCommentDirectivesOnPlan(plan logicalPlan, stmt sqlparser.Statement) (logicalPlan, error) { 535 var directives *sqlparser.CommentDirectives 536 cmt, ok := stmt.(sqlparser.Commented) 537 if ok { 538 directives = cmt.GetParsedComments().Directives() 539 scatterAsWarns := directives.IsSet(sqlparser.DirectiveScatterErrorsAsWarnings) 540 timeout := queryTimeout(directives) 541 542 if scatterAsWarns || timeout > 0 { 543 _, _ = visit(plan, func(logicalPlan logicalPlan) (bool, logicalPlan, error) { 544 switch plan := logicalPlan.(type) { 545 case *routeGen4: 546 plan.eroute.ScatterErrorsAsWarnings = scatterAsWarns 547 plan.eroute.QueryTimeout = timeout 548 } 549 return true, logicalPlan, nil 550 }) 551 } 552 } 553 554 return plan, nil 555 } 556 557 // checkIfDeleteSupported checks if the delete query is supported or we must return an error. 558 func checkIfDeleteSupported(del *sqlparser.Delete, semTable *semantics.SemTable) error { 559 if semTable.NotUnshardedErr != nil { 560 return semTable.NotUnshardedErr 561 } 562 563 // Delete is only supported for a single TableExpr which is supposed to be an aliased expression 564 multiShardErr := vterrors.VT12001("multi-shard or vindex write statement") 565 if len(del.TableExprs) != 1 { 566 return multiShardErr 567 } 568 _, isAliasedExpr := del.TableExprs[0].(*sqlparser.AliasedTableExpr) 569 if !isAliasedExpr { 570 return multiShardErr 571 } 572 573 if len(del.Targets) > 1 { 574 return vterrors.VT12001("multi-table DELETE statement in a sharded keyspace") 575 } 576 577 err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) { 578 switch node.(type) { 579 case *sqlparser.Subquery, *sqlparser.DerivedTable: 580 // We have a subquery, so we must fail the planning. 581 // If this subquery and the table expression were all belonging to the same unsharded keyspace, 582 // we would have already created a plan for them before doing these checks. 583 return false, vterrors.VT12001("subqueries in DML") 584 } 585 return true, nil 586 }, del) 587 if err != nil { 588 return err 589 } 590 591 return nil 592 }