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  }