vitess.io/vitess@v0.16.2/go/vt/vtgate/planbuilder/operators/route.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 operators
    18  
    19  import (
    20  	"vitess.io/vitess/go/mysql/collations"
    21  	"vitess.io/vitess/go/vt/key"
    22  	"vitess.io/vitess/go/vt/sqlparser"
    23  	"vitess.io/vitess/go/vt/vterrors"
    24  	"vitess.io/vitess/go/vt/vtgate/engine"
    25  	"vitess.io/vitess/go/vt/vtgate/evalengine"
    26  	"vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops"
    27  	"vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext"
    28  
    29  	"vitess.io/vitess/go/vt/vtgate/semantics"
    30  	"vitess.io/vitess/go/vt/vtgate/vindexes"
    31  )
    32  
    33  type (
    34  	Route struct {
    35  		Source ops.Operator
    36  
    37  		RouteOpCode engine.Opcode
    38  		Keyspace    *vindexes.Keyspace
    39  
    40  		// here we store the possible vindexes we can use so that when we add predicates to the plan,
    41  		// we can quickly check if the new predicates enables any new vindex Options
    42  		VindexPreds []*VindexPlusPredicates
    43  
    44  		// the best option available is stored here
    45  		Selected *VindexOption
    46  
    47  		// The following two fields are used when routing information_schema queries
    48  		SysTableTableSchema []evalengine.Expr
    49  		SysTableTableName   map[string]evalengine.Expr
    50  
    51  		// SeenPredicates contains all the predicates that have had a chance to influence routing.
    52  		// If we need to replan routing, we'll use this list
    53  		SeenPredicates []sqlparser.Expr
    54  
    55  		// TargetDestination specifies an explicit target destination tablet type
    56  		TargetDestination key.Destination
    57  
    58  		// Alternates contains alternate routes to equivalent sources in
    59  		// other keyspaces.
    60  		Alternates map[*vindexes.Keyspace]*Route
    61  
    62  		// Routes that have been merged into this one.
    63  		MergedWith []*Route
    64  	}
    65  
    66  	// VindexPlusPredicates is a struct used to store all the predicates that the vindex can be used to query
    67  	VindexPlusPredicates struct {
    68  		TableID   semantics.TableSet
    69  		ColVindex *vindexes.ColumnVindex
    70  
    71  		// during planning, we store the alternatives found for this route in this slice
    72  		Options []*VindexOption
    73  	}
    74  
    75  	// VindexOption stores the information needed to know if we have all the information needed to use a vindex
    76  	VindexOption struct {
    77  		Ready  bool
    78  		Values []evalengine.Expr
    79  		// columns that we have seen so far. Used only for multi-column vindexes so that we can track how many columns part of the vindex we have seen
    80  		ColsSeen    map[string]any
    81  		ValueExprs  []sqlparser.Expr
    82  		Predicates  []sqlparser.Expr
    83  		OpCode      engine.Opcode
    84  		FoundVindex vindexes.Vindex
    85  		Cost        Cost
    86  	}
    87  
    88  	// Cost is used to make it easy to compare the Cost of two plans with each other
    89  	Cost struct {
    90  		VindexCost int
    91  		IsUnique   bool
    92  		OpCode     engine.Opcode
    93  	}
    94  )
    95  
    96  var _ ops.PhysicalOperator = (*Route)(nil)
    97  
    98  // IPhysical implements the PhysicalOperator interface
    99  func (*Route) IPhysical() {}
   100  
   101  // Cost implements the Operator interface
   102  func (r *Route) Cost() int {
   103  	switch r.RouteOpCode {
   104  	case // these op codes will never be compared with each other - they are assigned by a rule and not a comparison
   105  		engine.DBA,
   106  		engine.Next,
   107  		engine.None,
   108  		engine.Reference,
   109  		engine.Unsharded:
   110  		return 0
   111  	// TODO revisit these costs when more of the gen4 planner is done
   112  	case engine.EqualUnique:
   113  		return 1
   114  	case engine.Equal:
   115  		return 5
   116  	case engine.IN:
   117  		return 10
   118  	case engine.MultiEqual:
   119  		return 10
   120  	case engine.Scatter:
   121  		return 20
   122  	}
   123  	return 1
   124  }
   125  
   126  // Clone implements the Operator interface
   127  func (r *Route) Clone(inputs []ops.Operator) ops.Operator {
   128  	cloneRoute := *r
   129  	cloneRoute.Source = inputs[0]
   130  	cloneRoute.VindexPreds = make([]*VindexPlusPredicates, len(r.VindexPreds))
   131  	for i, pred := range r.VindexPreds {
   132  		// we do this to create a copy of the struct
   133  		p := *pred
   134  		cloneRoute.VindexPreds[i] = &p
   135  	}
   136  	return &cloneRoute
   137  }
   138  
   139  // Inputs implements the Operator interface
   140  func (r *Route) Inputs() []ops.Operator {
   141  	return []ops.Operator{r.Source}
   142  }
   143  
   144  func (r *Route) UpdateRoutingLogic(ctx *plancontext.PlanningContext, expr sqlparser.Expr) error {
   145  	r.SeenPredicates = append(r.SeenPredicates, expr)
   146  	return r.tryImprovingVindex(ctx, expr)
   147  }
   148  
   149  func (r *Route) tryImprovingVindex(ctx *plancontext.PlanningContext, expr sqlparser.Expr) error {
   150  	if r.canImprove() {
   151  		newVindexFound, err := r.searchForNewVindexes(ctx, expr)
   152  		if err != nil {
   153  			return err
   154  		}
   155  
   156  		// if we didn't open up any new vindex Options, no need to enter here
   157  		if newVindexFound {
   158  			r.PickBestAvailableVindex()
   159  		}
   160  	}
   161  	return nil
   162  }
   163  
   164  func (r *Route) searchForNewVindexes(ctx *plancontext.PlanningContext, predicate sqlparser.Expr) (bool, error) {
   165  	newVindexFound := false
   166  	switch node := predicate.(type) {
   167  	case *sqlparser.ExtractedSubquery:
   168  		originalCmp, ok := node.Original.(*sqlparser.ComparisonExpr)
   169  		if !ok {
   170  			break
   171  		}
   172  
   173  		// using the node.subquery which is the rewritten version of our subquery
   174  		cmp := &sqlparser.ComparisonExpr{
   175  			Left:     node.OtherSide,
   176  			Right:    &sqlparser.Subquery{Select: node.Subquery.Select},
   177  			Operator: originalCmp.Operator,
   178  		}
   179  		found, exitEarly, err := r.planComparison(ctx, cmp)
   180  		if err != nil || exitEarly {
   181  			return false, err
   182  		}
   183  		newVindexFound = newVindexFound || found
   184  
   185  	case *sqlparser.ComparisonExpr:
   186  		found, exitEarly, err := r.planComparison(ctx, node)
   187  		if err != nil || exitEarly {
   188  			return false, err
   189  		}
   190  		newVindexFound = newVindexFound || found
   191  	case *sqlparser.IsExpr:
   192  		found := r.planIsExpr(ctx, node)
   193  		newVindexFound = newVindexFound || found
   194  	}
   195  	return newVindexFound, nil
   196  }
   197  
   198  func (r *Route) planComparison(ctx *plancontext.PlanningContext, cmp *sqlparser.ComparisonExpr) (found bool, exitEarly bool, err error) {
   199  	if cmp.Operator != sqlparser.NullSafeEqualOp && (sqlparser.IsNull(cmp.Left) || sqlparser.IsNull(cmp.Right)) {
   200  		// we are looking at ANDed predicates in the WHERE clause.
   201  		// since we know that nothing returns true when compared to NULL,
   202  		// so we can safely bail out here
   203  		r.setSelectNoneOpcode()
   204  		return false, true, nil
   205  	}
   206  
   207  	switch cmp.Operator {
   208  	case sqlparser.EqualOp:
   209  		found := r.planEqualOp(ctx, cmp)
   210  		return found, false, nil
   211  	case sqlparser.InOp:
   212  		if r.isImpossibleIN(cmp) {
   213  			return false, true, nil
   214  		}
   215  		found := r.planInOp(ctx, cmp)
   216  		return found, false, nil
   217  	case sqlparser.NotInOp:
   218  		// NOT IN is always a scatter, except when we can be sure it would return nothing
   219  		if r.isImpossibleNotIN(cmp) {
   220  			return false, true, nil
   221  		}
   222  	case sqlparser.LikeOp:
   223  		found := r.planLikeOp(ctx, cmp)
   224  		return found, false, nil
   225  
   226  	}
   227  	return false, false, nil
   228  }
   229  
   230  func (r *Route) setSelectNoneOpcode() {
   231  	r.RouteOpCode = engine.None
   232  	// clear any chosen vindex as this query does not need to be sent down.
   233  	r.Selected = nil
   234  }
   235  
   236  func (r *Route) planEqualOp(ctx *plancontext.PlanningContext, node *sqlparser.ComparisonExpr) bool {
   237  	column, ok := node.Left.(*sqlparser.ColName)
   238  	other := node.Right
   239  	vdValue := other
   240  	if !ok {
   241  		column, ok = node.Right.(*sqlparser.ColName)
   242  		if !ok {
   243  			// either the LHS or RHS have to be a column to be useful for the vindex
   244  			return false
   245  		}
   246  		vdValue = node.Left
   247  	}
   248  	val := r.makeEvalEngineExpr(ctx, vdValue)
   249  	if val == nil {
   250  		return false
   251  	}
   252  
   253  	return r.haveMatchingVindex(ctx, node, vdValue, column, val, equalOrEqualUnique, justTheVindex)
   254  }
   255  
   256  // makePlanValue transforms the given sqlparser.Expr into a sqltypes.PlanValue.
   257  // If the given sqlparser.Expr is an argument and can be found in the r.argToReplaceBySelect then the
   258  // method will stops and return nil values.
   259  // Otherwise, the method will try to apply makePlanValue for any equality the sqlparser.Expr n has.
   260  // The first PlanValue that is successfully produced will be returned.
   261  func (r *Route) makeEvalEngineExpr(ctx *plancontext.PlanningContext, n sqlparser.Expr) evalengine.Expr {
   262  	if ctx.IsSubQueryToReplace(n) {
   263  		return nil
   264  	}
   265  
   266  	for _, expr := range ctx.SemTable.GetExprAndEqualities(n) {
   267  		if subq, isSubq := expr.(*sqlparser.Subquery); isSubq {
   268  			extractedSubquery := ctx.SemTable.FindSubqueryReference(subq)
   269  			if extractedSubquery == nil {
   270  				continue
   271  			}
   272  			switch engine.PulloutOpcode(extractedSubquery.OpCode) {
   273  			case engine.PulloutIn, engine.PulloutNotIn:
   274  				expr = sqlparser.NewListArg(extractedSubquery.GetArgName())
   275  			case engine.PulloutValue, engine.PulloutExists:
   276  				expr = sqlparser.NewArgument(extractedSubquery.GetArgName())
   277  			}
   278  		}
   279  		pv, _ := evalengine.Translate(expr, ctx.SemTable)
   280  		if pv != nil {
   281  			return pv
   282  		}
   283  	}
   284  
   285  	return nil
   286  }
   287  
   288  func (r *Route) hasVindex(column *sqlparser.ColName) bool {
   289  	for _, v := range r.VindexPreds {
   290  		for _, col := range v.ColVindex.Columns {
   291  			if column.Name.Equal(col) {
   292  				return true
   293  			}
   294  		}
   295  	}
   296  	return false
   297  }
   298  
   299  func (r *Route) haveMatchingVindex(
   300  	ctx *plancontext.PlanningContext,
   301  	node sqlparser.Expr,
   302  	valueExpr sqlparser.Expr,
   303  	column *sqlparser.ColName,
   304  	value evalengine.Expr,
   305  	opcode func(*vindexes.ColumnVindex) engine.Opcode,
   306  	vfunc func(*vindexes.ColumnVindex) vindexes.Vindex,
   307  ) bool {
   308  	newVindexFound := false
   309  	for _, v := range r.VindexPreds {
   310  		// check that the
   311  		if !ctx.SemTable.DirectDeps(column).IsSolvedBy(v.TableID) {
   312  			continue
   313  		}
   314  		switch v.ColVindex.Vindex.(type) {
   315  		case vindexes.SingleColumn:
   316  			col := v.ColVindex.Columns[0]
   317  			if column.Name.Equal(col) {
   318  				// single column vindex - just add the option
   319  				routeOpcode := opcode(v.ColVindex)
   320  				vindex := vfunc(v.ColVindex)
   321  				if vindex == nil || routeOpcode == engine.Scatter {
   322  					continue
   323  				}
   324  				v.Options = append(v.Options, &VindexOption{
   325  					Values:      []evalengine.Expr{value},
   326  					ValueExprs:  []sqlparser.Expr{valueExpr},
   327  					Predicates:  []sqlparser.Expr{node},
   328  					OpCode:      routeOpcode,
   329  					FoundVindex: vindex,
   330  					Cost:        costFor(v.ColVindex, routeOpcode),
   331  					Ready:       true,
   332  				})
   333  				newVindexFound = true
   334  			}
   335  		case vindexes.MultiColumn:
   336  			colLoweredName := ""
   337  			indexOfCol := -1
   338  			for idx, col := range v.ColVindex.Columns {
   339  				if column.Name.Equal(col) {
   340  					colLoweredName = column.Name.Lowered()
   341  					indexOfCol = idx
   342  					break
   343  				}
   344  			}
   345  			if colLoweredName == "" {
   346  				break
   347  			}
   348  
   349  			var newOption []*VindexOption
   350  			for _, op := range v.Options {
   351  				if op.Ready {
   352  					continue
   353  				}
   354  				_, isPresent := op.ColsSeen[colLoweredName]
   355  				if isPresent {
   356  					continue
   357  				}
   358  				option := copyOption(op)
   359  				optionReady := option.updateWithNewColumn(colLoweredName, valueExpr, indexOfCol, value, node, v.ColVindex, opcode)
   360  				if optionReady {
   361  					newVindexFound = true
   362  				}
   363  				newOption = append(newOption, option)
   364  			}
   365  			v.Options = append(v.Options, newOption...)
   366  
   367  			// multi column vindex - just always add as new option
   368  			option := createOption(v.ColVindex, vfunc)
   369  			optionReady := option.updateWithNewColumn(colLoweredName, valueExpr, indexOfCol, value, node, v.ColVindex, opcode)
   370  			if optionReady {
   371  				newVindexFound = true
   372  			}
   373  			v.Options = append(v.Options, option)
   374  		}
   375  	}
   376  	return newVindexFound
   377  }
   378  
   379  func createOption(
   380  	colVindex *vindexes.ColumnVindex,
   381  	vfunc func(*vindexes.ColumnVindex) vindexes.Vindex,
   382  ) *VindexOption {
   383  	values := make([]evalengine.Expr, len(colVindex.Columns))
   384  	predicates := make([]sqlparser.Expr, len(colVindex.Columns))
   385  	vindex := vfunc(colVindex)
   386  
   387  	return &VindexOption{
   388  		Values:      values,
   389  		Predicates:  predicates,
   390  		ColsSeen:    map[string]any{},
   391  		FoundVindex: vindex,
   392  	}
   393  }
   394  
   395  func copyOption(orig *VindexOption) *VindexOption {
   396  	colsSeen := make(map[string]any, len(orig.ColsSeen))
   397  	valueExprs := make([]sqlparser.Expr, len(orig.ValueExprs))
   398  	values := make([]evalengine.Expr, len(orig.Values))
   399  	predicates := make([]sqlparser.Expr, len(orig.Predicates))
   400  
   401  	copy(values, orig.Values)
   402  	copy(valueExprs, orig.ValueExprs)
   403  	copy(predicates, orig.Predicates)
   404  	for k, v := range orig.ColsSeen {
   405  		colsSeen[k] = v
   406  	}
   407  	vo := &VindexOption{
   408  		Values:      values,
   409  		ColsSeen:    colsSeen,
   410  		ValueExprs:  valueExprs,
   411  		Predicates:  predicates,
   412  		OpCode:      orig.OpCode,
   413  		FoundVindex: orig.FoundVindex,
   414  		Cost:        orig.Cost,
   415  	}
   416  	return vo
   417  }
   418  
   419  func (option *VindexOption) updateWithNewColumn(
   420  	colLoweredName string,
   421  	valueExpr sqlparser.Expr,
   422  	indexOfCol int,
   423  	value evalengine.Expr,
   424  	node sqlparser.Expr,
   425  	colVindex *vindexes.ColumnVindex,
   426  	opcode func(*vindexes.ColumnVindex) engine.Opcode,
   427  ) bool {
   428  	option.ColsSeen[colLoweredName] = true
   429  	option.ValueExprs = append(option.ValueExprs, valueExpr)
   430  	option.Values[indexOfCol] = value
   431  	option.Predicates[indexOfCol] = node
   432  	option.Ready = len(option.ColsSeen) == len(colVindex.Columns)
   433  	routeOpcode := opcode(colVindex)
   434  	if option.OpCode < routeOpcode {
   435  		option.OpCode = routeOpcode
   436  		option.Cost = costFor(colVindex, routeOpcode)
   437  	}
   438  	return option.Ready
   439  }
   440  
   441  // PickBestAvailableVindex goes over the available vindexes for this route and picks the best one available.
   442  func (r *Route) PickBestAvailableVindex() {
   443  	for _, v := range r.VindexPreds {
   444  		option := v.bestOption()
   445  		if option != nil && (r.Selected == nil || less(option.Cost, r.Selected.Cost)) {
   446  			r.Selected = option
   447  			r.RouteOpCode = option.OpCode
   448  		}
   449  	}
   450  }
   451  
   452  // canImprove returns true if additional predicates could help improving this plan
   453  func (r *Route) canImprove() bool {
   454  	return r.RouteOpCode != engine.None
   455  }
   456  
   457  func (r *Route) IsSingleShard() bool {
   458  	switch r.RouteOpCode {
   459  	case engine.Unsharded, engine.DBA, engine.Next, engine.EqualUnique, engine.Reference:
   460  		return true
   461  	}
   462  	return false
   463  }
   464  
   465  func (r *Route) SelectedVindex() vindexes.Vindex {
   466  	if r.Selected == nil {
   467  		return nil
   468  	}
   469  	return r.Selected.FoundVindex
   470  }
   471  
   472  func (r *Route) VindexExpressions() []sqlparser.Expr {
   473  	if r.Selected == nil {
   474  		return nil
   475  	}
   476  	return r.Selected.ValueExprs
   477  }
   478  
   479  func (r *Route) isImpossibleIN(node *sqlparser.ComparisonExpr) bool {
   480  	switch nodeR := node.Right.(type) {
   481  	case sqlparser.ValTuple:
   482  		// WHERE col IN (null)
   483  		if len(nodeR) == 1 && sqlparser.IsNull(nodeR[0]) {
   484  			r.setSelectNoneOpcode()
   485  			return true
   486  		}
   487  	}
   488  	return false
   489  }
   490  
   491  func (r *Route) planInOp(ctx *plancontext.PlanningContext, cmp *sqlparser.ComparisonExpr) bool {
   492  	switch left := cmp.Left.(type) {
   493  	case *sqlparser.ColName:
   494  		vdValue := cmp.Right
   495  
   496  		valTuple, isTuple := vdValue.(sqlparser.ValTuple)
   497  		if isTuple && len(valTuple) == 1 {
   498  			return r.planEqualOp(ctx, &sqlparser.ComparisonExpr{Left: left, Right: valTuple[0], Operator: sqlparser.EqualOp})
   499  		}
   500  
   501  		value := r.makeEvalEngineExpr(ctx, vdValue)
   502  		if value == nil {
   503  			return false
   504  		}
   505  		opcode := func(*vindexes.ColumnVindex) engine.Opcode { return engine.IN }
   506  		return r.haveMatchingVindex(ctx, cmp, vdValue, left, value, opcode, justTheVindex)
   507  	case sqlparser.ValTuple:
   508  		right, rightIsValTuple := cmp.Right.(sqlparser.ValTuple)
   509  		if !rightIsValTuple {
   510  			return false
   511  		}
   512  		return r.planCompositeInOpRecursive(ctx, cmp, left, right, nil)
   513  	}
   514  
   515  	return false
   516  }
   517  
   518  func (r *Route) isImpossibleNotIN(node *sqlparser.ComparisonExpr) bool {
   519  	switch node := node.Right.(type) {
   520  	case sqlparser.ValTuple:
   521  		for _, n := range node {
   522  			if sqlparser.IsNull(n) {
   523  				r.setSelectNoneOpcode()
   524  				return true
   525  			}
   526  		}
   527  	}
   528  
   529  	return false
   530  }
   531  
   532  func (r *Route) planLikeOp(ctx *plancontext.PlanningContext, node *sqlparser.ComparisonExpr) bool {
   533  	column, ok := node.Left.(*sqlparser.ColName)
   534  	if !ok {
   535  		return false
   536  	}
   537  
   538  	vdValue := node.Right
   539  	val := r.makeEvalEngineExpr(ctx, vdValue)
   540  	if val == nil {
   541  		return false
   542  	}
   543  	selectEqual := func(*vindexes.ColumnVindex) engine.Opcode { return engine.Equal }
   544  	vdx := func(vindex *vindexes.ColumnVindex) vindexes.Vindex {
   545  		if prefixable, ok := vindex.Vindex.(vindexes.Prefixable); ok {
   546  			return prefixable.PrefixVindex()
   547  		}
   548  
   549  		// if we can't use the vindex as a prefix-vindex, we can't use this vindex at all
   550  		return nil
   551  	}
   552  	return r.haveMatchingVindex(ctx, node, vdValue, column, val, selectEqual, vdx)
   553  
   554  }
   555  
   556  func (r *Route) planCompositeInOpRecursive(
   557  	ctx *plancontext.PlanningContext,
   558  	cmp *sqlparser.ComparisonExpr,
   559  	left, right sqlparser.ValTuple,
   560  	coordinates []int,
   561  ) bool {
   562  	foundVindex := false
   563  	cindex := len(coordinates)
   564  	coordinates = append(coordinates, 0)
   565  	for i, expr := range left {
   566  		coordinates[cindex] = i
   567  		switch expr := expr.(type) {
   568  		case sqlparser.ValTuple:
   569  			ok := r.planCompositeInOpRecursive(ctx, cmp, expr, right, coordinates)
   570  			return ok || foundVindex
   571  		case *sqlparser.ColName:
   572  			// check if left col is a vindex
   573  			if !r.hasVindex(expr) {
   574  				continue
   575  			}
   576  
   577  			rightVals := make(sqlparser.ValTuple, len(right))
   578  			for j, currRight := range right {
   579  				switch currRight := currRight.(type) {
   580  				case sqlparser.ValTuple:
   581  					val := tupleAccess(currRight, coordinates)
   582  					if val == nil {
   583  						return false
   584  					}
   585  					rightVals[j] = val
   586  				default:
   587  					return false
   588  				}
   589  			}
   590  			newPlanValues := r.makeEvalEngineExpr(ctx, rightVals)
   591  			if newPlanValues == nil {
   592  				return false
   593  			}
   594  
   595  			opcode := func(*vindexes.ColumnVindex) engine.Opcode { return engine.MultiEqual }
   596  			newVindex := r.haveMatchingVindex(ctx, cmp, rightVals, expr, newPlanValues, opcode, justTheVindex)
   597  			foundVindex = newVindex || foundVindex
   598  		}
   599  	}
   600  	return foundVindex
   601  }
   602  
   603  // Reset all vindex predicates on this route and re-build their options from
   604  // the list of seen routing predicates.
   605  func (r *Route) resetRoutingSelections(ctx *plancontext.PlanningContext) error {
   606  	switch r.RouteOpCode {
   607  	case engine.DBA, engine.Next, engine.Reference, engine.Unsharded:
   608  		// these we keep as is
   609  	default:
   610  		r.RouteOpCode = engine.Scatter
   611  	}
   612  
   613  	r.Selected = nil
   614  	for i, vp := range r.VindexPreds {
   615  		r.VindexPreds[i] = &VindexPlusPredicates{ColVindex: vp.ColVindex, TableID: vp.TableID}
   616  	}
   617  
   618  	for _, predicate := range r.SeenPredicates {
   619  		err := r.tryImprovingVindex(ctx, predicate)
   620  		if err != nil {
   621  			return err
   622  		}
   623  	}
   624  	return nil
   625  }
   626  
   627  func tupleAccess(expr sqlparser.Expr, coordinates []int) sqlparser.Expr {
   628  	tuple, _ := expr.(sqlparser.ValTuple)
   629  	for _, idx := range coordinates {
   630  		if idx >= len(tuple) {
   631  			return nil
   632  		}
   633  		expr = tuple[idx]
   634  		tuple, _ = expr.(sqlparser.ValTuple)
   635  	}
   636  	return expr
   637  }
   638  
   639  func equalOrEqualUnique(vindex *vindexes.ColumnVindex) engine.Opcode {
   640  	if vindex.IsPartialVindex() {
   641  		return engine.SubShard
   642  	}
   643  	if vindex.IsUnique() {
   644  		return engine.EqualUnique
   645  	}
   646  
   647  	return engine.Equal
   648  }
   649  
   650  func justTheVindex(vindex *vindexes.ColumnVindex) vindexes.Vindex {
   651  	return vindex.Vindex
   652  }
   653  
   654  // costFor returns a cost struct to make route choices easier to compare
   655  func costFor(foundVindex *vindexes.ColumnVindex, opcode engine.Opcode) Cost {
   656  	switch opcode {
   657  	// For these opcodes, we should not have a vindex, so we just return the opcode as the cost
   658  	case engine.Unsharded, engine.Next, engine.DBA, engine.Reference, engine.None, engine.Scatter:
   659  		return Cost{
   660  			OpCode: opcode,
   661  		}
   662  	}
   663  
   664  	return Cost{
   665  		VindexCost: foundVindex.Cost(),
   666  		IsUnique:   foundVindex.IsUnique(),
   667  		OpCode:     opcode,
   668  	}
   669  }
   670  
   671  // less compares two costs and returns true if the first cost is cheaper than the second
   672  func less(c1, c2 Cost) bool {
   673  	switch {
   674  	case c1.OpCode != c2.OpCode:
   675  		return c1.OpCode < c2.OpCode
   676  	case c1.IsUnique == c2.IsUnique:
   677  		return c1.VindexCost <= c2.VindexCost
   678  	default:
   679  		return c1.IsUnique
   680  	}
   681  }
   682  
   683  func (vpp *VindexPlusPredicates) bestOption() *VindexOption {
   684  	var best *VindexOption
   685  	var keepOptions []*VindexOption
   686  	for _, option := range vpp.Options {
   687  		if option.Ready {
   688  			if best == nil || less(option.Cost, best.Cost) {
   689  				best = option
   690  			}
   691  		} else {
   692  			keepOptions = append(keepOptions, option)
   693  		}
   694  	}
   695  	if best != nil {
   696  		keepOptions = append(keepOptions, best)
   697  	}
   698  	vpp.Options = keepOptions
   699  	return best
   700  }
   701  
   702  func (r *Route) planIsExpr(ctx *plancontext.PlanningContext, node *sqlparser.IsExpr) bool {
   703  	// we only handle IS NULL correct. IsExpr can contain other expressions as well
   704  	if node.Right != sqlparser.IsNullOp {
   705  		return false
   706  	}
   707  	column, ok := node.Left.(*sqlparser.ColName)
   708  	if !ok {
   709  		return false
   710  	}
   711  	vdValue := &sqlparser.NullVal{}
   712  	val := r.makeEvalEngineExpr(ctx, vdValue)
   713  	if val == nil {
   714  		return false
   715  	}
   716  	opcodeF := func(vindex *vindexes.ColumnVindex) engine.Opcode {
   717  		if _, ok := vindex.Vindex.(vindexes.Lookup); ok {
   718  			return engine.Scatter
   719  		}
   720  		return equalOrEqualUnique(vindex)
   721  	}
   722  
   723  	return r.haveMatchingVindex(ctx, node, vdValue, column, val, opcodeF, justTheVindex)
   724  }
   725  
   726  // createRoute returns either an information_schema route, or else consults the
   727  // VSchema to find a suitable table, and then creates a route from that.
   728  func createRoute(
   729  	ctx *plancontext.PlanningContext,
   730  	queryTable *QueryTable,
   731  	solves semantics.TableSet,
   732  ) (ops.Operator, error) {
   733  	if queryTable.IsInfSchema {
   734  		return createInfSchemaRoute(ctx, queryTable)
   735  	}
   736  	return findVSchemaTableAndCreateRoute(ctx, queryTable, queryTable.Table, solves, true /*planAlternates*/)
   737  }
   738  
   739  // findVSchemaTableAndCreateRoute consults the VSchema to find a suitable
   740  // table, and then creates a route from that.
   741  func findVSchemaTableAndCreateRoute(
   742  	ctx *plancontext.PlanningContext,
   743  	queryTable *QueryTable,
   744  	tableName sqlparser.TableName,
   745  	solves semantics.TableSet,
   746  	planAlternates bool,
   747  ) (*Route, error) {
   748  	vschemaTable, _, _, _, target, err := ctx.VSchema.FindTableOrVindex(tableName)
   749  	if target != nil {
   750  		return nil, vterrors.VT12001("SELECT with a target destination")
   751  	}
   752  	if err != nil {
   753  		return nil, err
   754  	}
   755  
   756  	return createRouteFromVSchemaTable(
   757  		ctx,
   758  		queryTable,
   759  		vschemaTable,
   760  		solves,
   761  		planAlternates,
   762  	)
   763  }
   764  
   765  // createRouteFromTable creates a route from the given VSchema table.
   766  func createRouteFromVSchemaTable(
   767  	ctx *plancontext.PlanningContext,
   768  	queryTable *QueryTable,
   769  	vschemaTable *vindexes.Table,
   770  	solves semantics.TableSet,
   771  	planAlternates bool,
   772  ) (*Route, error) {
   773  	if vschemaTable.Name.String() != queryTable.Table.Name.String() {
   774  		// we are dealing with a routed table
   775  		queryTable = queryTable.Clone()
   776  		name := queryTable.Table.Name
   777  		queryTable.Table.Name = vschemaTable.Name
   778  		astTable, ok := queryTable.Alias.Expr.(sqlparser.TableName)
   779  		if !ok {
   780  			return nil, vterrors.VT13001("a derived table should never be a routed table")
   781  		}
   782  		realTableName := sqlparser.NewIdentifierCS(vschemaTable.Name.String())
   783  		astTable.Name = realTableName
   784  		if queryTable.Alias.As.IsEmpty() {
   785  			// if the user hasn't specified an alias, we'll insert one here so the old table name still works
   786  			queryTable.Alias.As = sqlparser.NewIdentifierCS(name.String())
   787  		}
   788  	}
   789  	plan := &Route{
   790  		Source: &Table{
   791  			QTable: queryTable,
   792  			VTable: vschemaTable,
   793  		},
   794  		Keyspace: vschemaTable.Keyspace,
   795  	}
   796  
   797  	for _, columnVindex := range vschemaTable.ColumnVindexes {
   798  		plan.VindexPreds = append(plan.VindexPreds, &VindexPlusPredicates{ColVindex: columnVindex, TableID: solves})
   799  	}
   800  
   801  	switch {
   802  	case vschemaTable.Type == vindexes.TypeSequence:
   803  		plan.RouteOpCode = engine.Next
   804  	case vschemaTable.Type == vindexes.TypeReference:
   805  		plan.RouteOpCode = engine.Reference
   806  	case !vschemaTable.Keyspace.Sharded:
   807  		plan.RouteOpCode = engine.Unsharded
   808  	case vschemaTable.Pinned != nil:
   809  		// Pinned tables have their keyspace ids already assigned.
   810  		// Use the Binary vindex, which is the identity function
   811  		// for keyspace id.
   812  		plan.RouteOpCode = engine.EqualUnique
   813  		vindex, _ := vindexes.NewBinary("binary", nil)
   814  		plan.Selected = &VindexOption{
   815  			Ready:       true,
   816  			Values:      []evalengine.Expr{evalengine.NewLiteralString(vschemaTable.Pinned, collations.TypedCollation{})},
   817  			ValueExprs:  nil,
   818  			Predicates:  nil,
   819  			OpCode:      engine.EqualUnique,
   820  			FoundVindex: vindex,
   821  			Cost: Cost{
   822  				OpCode: engine.EqualUnique,
   823  			},
   824  		}
   825  	default:
   826  		plan.RouteOpCode = engine.Scatter
   827  	}
   828  	for _, predicate := range queryTable.Predicates {
   829  		err := plan.UpdateRoutingLogic(ctx, predicate)
   830  		if err != nil {
   831  			return nil, err
   832  		}
   833  	}
   834  
   835  	if plan.RouteOpCode == engine.Scatter && len(queryTable.Predicates) > 0 {
   836  		// If we have a scatter query, it's worth spending a little extra time seeing if we can't improve it
   837  		oldPredicates := queryTable.Predicates
   838  		queryTable.Predicates = nil
   839  		plan.SeenPredicates = nil
   840  		for _, pred := range oldPredicates {
   841  			rewritten := sqlparser.RewritePredicate(pred)
   842  			predicates := sqlparser.SplitAndExpression(nil, rewritten.(sqlparser.Expr))
   843  			for _, predicate := range predicates {
   844  				queryTable.Predicates = append(queryTable.Predicates, predicate)
   845  				err := plan.UpdateRoutingLogic(ctx, predicate)
   846  				if err != nil {
   847  					return nil, err
   848  				}
   849  			}
   850  		}
   851  
   852  		if plan.RouteOpCode == engine.Scatter {
   853  			// if we _still_ haven't found a better route, we can run this additional rewrite on any ORs we have
   854  			for _, expr := range queryTable.Predicates {
   855  				or, ok := expr.(*sqlparser.OrExpr)
   856  				if !ok {
   857  					continue
   858  				}
   859  				for _, predicate := range sqlparser.ExtractINFromOR(or) {
   860  					err := plan.UpdateRoutingLogic(ctx, predicate)
   861  					if err != nil {
   862  						return nil, err
   863  					}
   864  				}
   865  			}
   866  		}
   867  	}
   868  
   869  	if planAlternates {
   870  		alternates, err := createAlternateRoutesFromVSchemaTable(
   871  			ctx,
   872  			queryTable,
   873  			vschemaTable,
   874  			solves,
   875  		)
   876  		if err != nil {
   877  			return nil, err
   878  		}
   879  		plan.Alternates = alternates
   880  	}
   881  
   882  	return plan, nil
   883  }
   884  
   885  func createAlternateRoutesFromVSchemaTable(
   886  	ctx *plancontext.PlanningContext,
   887  	queryTable *QueryTable,
   888  	vschemaTable *vindexes.Table,
   889  	solves semantics.TableSet,
   890  ) (map[*vindexes.Keyspace]*Route, error) {
   891  	routes := make(map[*vindexes.Keyspace]*Route)
   892  
   893  	switch vschemaTable.Type {
   894  	case "", vindexes.TypeReference:
   895  		for ksName, referenceTable := range vschemaTable.ReferencedBy {
   896  			route, err := findVSchemaTableAndCreateRoute(
   897  				ctx,
   898  				queryTable,
   899  				sqlparser.TableName{
   900  					Name:      referenceTable.Name,
   901  					Qualifier: sqlparser.NewIdentifierCS(ksName),
   902  				},
   903  				solves,
   904  				false, /*planAlternates*/
   905  			)
   906  			if err != nil {
   907  				return nil, err
   908  			}
   909  			routes[route.Keyspace] = route
   910  		}
   911  
   912  		if vschemaTable.Source != nil {
   913  			route, err := findVSchemaTableAndCreateRoute(
   914  				ctx,
   915  				queryTable,
   916  				vschemaTable.Source.TableName,
   917  				solves,
   918  				false, /*planAlternates*/
   919  			)
   920  			if err != nil {
   921  				return nil, err
   922  			}
   923  			routes[route.Keyspace] = route
   924  		}
   925  	}
   926  
   927  	return routes, nil
   928  }
   929  
   930  func (r *Route) AddPredicate(ctx *plancontext.PlanningContext, expr sqlparser.Expr) (ops.Operator, error) {
   931  	err := r.UpdateRoutingLogic(ctx, expr)
   932  	if err != nil {
   933  		return nil, err
   934  	}
   935  	newSrc, err := r.Source.AddPredicate(ctx, expr)
   936  	if err != nil {
   937  		return nil, err
   938  	}
   939  	r.Source = newSrc
   940  	return r, err
   941  }
   942  
   943  func (r *Route) AddColumn(ctx *plancontext.PlanningContext, e sqlparser.Expr) (int, error) {
   944  	return r.Source.AddColumn(ctx, e)
   945  }
   946  
   947  func (r *Route) AlternateInKeyspace(keyspace *vindexes.Keyspace) *Route {
   948  	if keyspace.Name == r.Keyspace.Name {
   949  		return nil
   950  	}
   951  
   952  	if route, ok := r.Alternates[keyspace]; ok {
   953  		return route
   954  	}
   955  
   956  	return nil
   957  }
   958  
   959  // TablesUsed returns tables used by MergedWith routes, which are not included
   960  // in Inputs() and thus not a part of the operator tree
   961  func (r *Route) TablesUsed() []string {
   962  	addString, collect := collectSortedUniqueStrings()
   963  	for _, mw := range r.MergedWith {
   964  		for _, u := range TablesUsed(mw) {
   965  			addString(u)
   966  		}
   967  	}
   968  	return collect()
   969  }