vitess.io/vitess@v0.16.2/go/vt/vtgate/planbuilder/route.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  	"strconv"
    22  
    23  	"vitess.io/vitess/go/mysql/collations"
    24  	"vitess.io/vitess/go/vt/vterrors"
    25  	"vitess.io/vitess/go/vt/vtgate/evalengine"
    26  	"vitess.io/vitess/go/vt/vtgate/semantics"
    27  
    28  	"vitess.io/vitess/go/vt/sqlparser"
    29  	"vitess.io/vitess/go/vt/vtgate/engine"
    30  	"vitess.io/vitess/go/vt/vtgate/vindexes"
    31  )
    32  
    33  var _ logicalPlan = (*route)(nil)
    34  
    35  // route is used to build a Route primitive.
    36  // It's used to build one of the Select routes like
    37  // SelectScatter, etc. Portions of the original Select AST
    38  // are moved into this node, which will be used to build
    39  // the final SQL for this route.
    40  type route struct {
    41  	v3Plan
    42  	order int
    43  
    44  	// Redirect may point to another route if this route
    45  	// was merged with it. The Resolve function chases
    46  	// this pointer till the last un-redirected route.
    47  	Redirect *route
    48  
    49  	// Select is the AST for the query fragment that will be
    50  	// executed by this route.
    51  	Select sqlparser.SelectStatement
    52  
    53  	// resultColumns represent the columns returned by this route.
    54  	resultColumns []*resultColumn
    55  
    56  	// weight_string keeps track of the weight_string expressions
    57  	// that were added additionally for each column. These expressions
    58  	// are added to be used for collation of text columns.
    59  	weightStrings map[*resultColumn]int
    60  
    61  	// substitutions contain the list of table expressions that
    62  	// have to be substituted in the route's query.
    63  	substitutions []*tableSubstitution
    64  
    65  	// condition stores the AST condition that will be used
    66  	// to resolve the ERoute Values field.
    67  	condition sqlparser.Expr
    68  
    69  	// eroute is the primitive being built.
    70  	eroute *engine.Route
    71  }
    72  
    73  type tableSubstitution struct {
    74  	newExpr, oldExpr *sqlparser.AliasedTableExpr
    75  }
    76  
    77  func newRoute(stmt sqlparser.SelectStatement) (*route, *symtab) {
    78  	rb := &route{
    79  		Select:        stmt,
    80  		order:         1,
    81  		weightStrings: make(map[*resultColumn]int),
    82  	}
    83  	return rb, newSymtabWithRoute(rb)
    84  }
    85  
    86  // Resolve resolves redirects, and returns the last
    87  // un-redirected route.
    88  func (rb *route) Resolve() *route {
    89  	for rb.Redirect != nil {
    90  		rb = rb.Redirect
    91  	}
    92  	return rb
    93  }
    94  
    95  // Order implements the logicalPlan interface
    96  func (rb *route) Order() int {
    97  	return rb.order
    98  }
    99  
   100  // Reorder implements the logicalPlan interface
   101  func (rb *route) Reorder(order int) {
   102  	rb.order = order + 1
   103  }
   104  
   105  // Primitive implements the logicalPlan interface
   106  func (rb *route) Primitive() engine.Primitive {
   107  	return rb.eroute
   108  }
   109  
   110  // ResultColumns implements the logicalPlan interface
   111  func (rb *route) ResultColumns() []*resultColumn {
   112  	return rb.resultColumns
   113  }
   114  
   115  // PushAnonymous pushes an anonymous expression like '*' or NEXT VALUES
   116  // into the select expression list of the route. This function is
   117  // similar to PushSelect.
   118  func (rb *route) PushAnonymous(expr sqlparser.SelectExpr) *resultColumn {
   119  	// TODO: we should not assume that the query is a SELECT
   120  	sel := rb.Select.(*sqlparser.Select)
   121  	sel.SelectExprs = append(sel.SelectExprs, expr)
   122  
   123  	// We just create a place-holder resultColumn. It won't
   124  	// match anything.
   125  	rc := &resultColumn{column: &column{origin: rb}}
   126  	rb.resultColumns = append(rb.resultColumns, rc)
   127  
   128  	return rc
   129  }
   130  
   131  // SetLimit adds a LIMIT clause to the route.
   132  func (rb *route) SetLimit(limit *sqlparser.Limit) {
   133  	rb.Select.SetLimit(limit)
   134  }
   135  
   136  // Wireup implements the logicalPlan interface
   137  func (rb *route) Wireup(plan logicalPlan, jt *jointab) error {
   138  	// Precaution: update ERoute.Values only if it's not set already.
   139  	if rb.eroute.Values == nil {
   140  		// Resolve values stored in the logical plan.
   141  		switch vals := rb.condition.(type) {
   142  		case *sqlparser.ComparisonExpr:
   143  			pv, err := rb.procureValues(plan, jt, vals.Right)
   144  			if err != nil {
   145  				return err
   146  			}
   147  			rb.eroute.Values = []evalengine.Expr{pv}
   148  			vals.Right = sqlparser.ListArg(engine.ListVarName)
   149  		case nil:
   150  			// no-op.
   151  		default:
   152  			pv, err := rb.procureValues(plan, jt, vals)
   153  			if err != nil {
   154  				return err
   155  			}
   156  			rb.eroute.Values = []evalengine.Expr{pv}
   157  		}
   158  	}
   159  
   160  	// Fix up the AST.
   161  	_ = sqlparser.Walk(func(node sqlparser.SQLNode) (bool, error) {
   162  		switch node := node.(type) {
   163  		case *sqlparser.Select:
   164  			if len(node.SelectExprs) == 0 {
   165  				node.SelectExprs = []sqlparser.SelectExpr{
   166  					&sqlparser.AliasedExpr{
   167  						Expr: sqlparser.NewIntLiteral("1"),
   168  					},
   169  				}
   170  			}
   171  		case *sqlparser.ComparisonExpr:
   172  			if node.Operator == sqlparser.EqualOp {
   173  				if rb.exprIsValue(node.Left) && !rb.exprIsValue(node.Right) {
   174  					node.Left, node.Right = node.Right, node.Left
   175  				}
   176  			}
   177  		}
   178  		return true, nil
   179  	}, rb.Select)
   180  
   181  	// Substitute table names
   182  	for _, sub := range rb.substitutions {
   183  		*sub.oldExpr = *sub.newExpr
   184  	}
   185  
   186  	// Generate query while simultaneously resolving values.
   187  	varFormatter := func(buf *sqlparser.TrackedBuffer, node sqlparser.SQLNode) {
   188  		switch node := node.(type) {
   189  		case *sqlparser.ColName:
   190  			if !rb.isLocal(node) {
   191  				joinVar := jt.Procure(plan, node, rb.Order())
   192  				buf.WriteArg(":", joinVar)
   193  				return
   194  			}
   195  		case sqlparser.TableName:
   196  			if !sqlparser.SystemSchema(node.Qualifier.String()) {
   197  				node.Name.Format(buf)
   198  				return
   199  			}
   200  			node.Format(buf)
   201  			return
   202  		}
   203  		node.Format(buf)
   204  	}
   205  	buf := sqlparser.NewTrackedBuffer(varFormatter)
   206  	varFormatter(buf, rb.Select)
   207  	rb.eroute.Query = buf.ParsedQuery().Query
   208  	rb.eroute.FieldQuery = rb.generateFieldQuery(rb.Select, jt)
   209  	return nil
   210  }
   211  
   212  // prepareTheAST does minor fixups of the SELECT struct before producing the query string
   213  func (rb *route) prepareTheAST() {
   214  	_ = sqlparser.Walk(func(node sqlparser.SQLNode) (bool, error) {
   215  		switch node := node.(type) {
   216  		case *sqlparser.Select:
   217  			if len(node.SelectExprs) == 0 {
   218  				node.SelectExprs = []sqlparser.SelectExpr{
   219  					&sqlparser.AliasedExpr{
   220  						Expr: sqlparser.NewIntLiteral("1"),
   221  					},
   222  				}
   223  			}
   224  		case *sqlparser.ComparisonExpr:
   225  			// 42 = colName -> colName = 42
   226  			b := node.Operator == sqlparser.EqualOp
   227  			value := sqlparser.IsValue(node.Left)
   228  			name := sqlparser.IsColName(node.Right)
   229  			if b &&
   230  				value &&
   231  				name {
   232  				node.Left, node.Right = node.Right, node.Left
   233  			}
   234  		}
   235  		return true, nil
   236  	}, rb.Select)
   237  }
   238  
   239  // procureValues procures and converts the input into
   240  // the expected types for rb.Values.
   241  func (rb *route) procureValues(plan logicalPlan, jt *jointab, val sqlparser.Expr) (evalengine.Expr, error) {
   242  	switch typedVal := val.(type) {
   243  	case sqlparser.ValTuple:
   244  		exprs := make([]evalengine.Expr, 0, len(typedVal))
   245  		for _, item := range typedVal {
   246  			v, err := rb.procureValues(plan, jt, item)
   247  			if err != nil {
   248  				return nil, err
   249  			}
   250  			exprs = append(exprs, v)
   251  		}
   252  		return evalengine.NewTupleExpr(exprs...), nil
   253  	case *sqlparser.ColName:
   254  		joinVar := jt.Procure(plan, typedVal, rb.Order())
   255  		return evalengine.NewBindVar(joinVar, collations.TypedCollation{}), nil
   256  	default:
   257  		return evalengine.Translate(typedVal, semantics.EmptySemTable())
   258  	}
   259  }
   260  
   261  func (rb *route) isLocal(col *sqlparser.ColName) bool {
   262  	return col.Metadata.(*column).Origin() == rb
   263  }
   264  
   265  // generateFieldQuery generates a query with an impossible where.
   266  // This will be used on the RHS node to fetch field info if the LHS
   267  // returns no result.
   268  func (rb *route) generateFieldQuery(sel sqlparser.SelectStatement, jt *jointab) string {
   269  	formatter := func(buf *sqlparser.TrackedBuffer, node sqlparser.SQLNode) {
   270  		switch node := node.(type) {
   271  		case *sqlparser.ColName:
   272  			if !rb.isLocal(node) {
   273  				_, joinVar := jt.Lookup(node)
   274  				buf.WriteArg(":", joinVar)
   275  				return
   276  			}
   277  		case sqlparser.TableName:
   278  			if !sqlparser.SystemSchema(node.Qualifier.String()) {
   279  				node.Name.Format(buf)
   280  				return
   281  			}
   282  			node.Format(buf)
   283  			return
   284  		}
   285  		sqlparser.FormatImpossibleQuery(buf, node)
   286  	}
   287  
   288  	buffer := sqlparser.NewTrackedBuffer(formatter)
   289  	node := buffer.WriteNode(sel)
   290  	query := node.ParsedQuery()
   291  	return query.Query
   292  }
   293  
   294  // SupplyVar implements the logicalPlan interface
   295  func (rb *route) SupplyVar(from, to int, col *sqlparser.ColName, varname string) {
   296  	// route is an atomic primitive. So, SupplyVar cannot be
   297  	// called on it.
   298  	panic("BUG: route is an atomic node.")
   299  }
   300  
   301  // SupplyCol implements the logicalPlan interface
   302  func (rb *route) SupplyCol(col *sqlparser.ColName) (rc *resultColumn, colNumber int) {
   303  	c := col.Metadata.(*column)
   304  	for i, rc := range rb.resultColumns {
   305  		if rc.column == c {
   306  			return rc, i
   307  		}
   308  	}
   309  
   310  	// A new result has to be returned.
   311  	rc = &resultColumn{column: c}
   312  	rb.resultColumns = append(rb.resultColumns, rc)
   313  	// TODO: we should not assume that the query is a SELECT query
   314  	sel := rb.Select.(*sqlparser.Select)
   315  	sel.SelectExprs = append(sel.SelectExprs, &sqlparser.AliasedExpr{Expr: col})
   316  	return rc, len(rb.resultColumns) - 1
   317  }
   318  
   319  // SupplyWeightString implements the logicalPlan interface
   320  func (rb *route) SupplyWeightString(colNumber int, alsoAddToGroupBy bool) (weightcolNumber int, err error) {
   321  	rc := rb.resultColumns[colNumber]
   322  	s, ok := rb.Select.(*sqlparser.Select)
   323  	if !ok {
   324  		return 0, vterrors.VT13001("unexpected AST struct for query")
   325  	}
   326  
   327  	aliasExpr, ok := s.SelectExprs[colNumber].(*sqlparser.AliasedExpr)
   328  	if !ok {
   329  		return 0, vterrors.VT13001(fmt.Sprintf("unexpected AST struct for query %T", s.SelectExprs[colNumber]))
   330  	}
   331  	weightStringExpr := &sqlparser.FuncExpr{
   332  		Name: sqlparser.NewIdentifierCI("weight_string"),
   333  		Exprs: []sqlparser.SelectExpr{
   334  			&sqlparser.AliasedExpr{
   335  				Expr: aliasExpr.Expr,
   336  			},
   337  		},
   338  	}
   339  	expr := &sqlparser.AliasedExpr{
   340  		Expr: weightStringExpr,
   341  	}
   342  	if alsoAddToGroupBy {
   343  		sel, isSelect := rb.Select.(*sqlparser.Select)
   344  		if !isSelect {
   345  			return 0, vterrors.VT13001(fmt.Sprintf("cannot add weight string in %T", rb.Select))
   346  		}
   347  		sel.AddGroupBy(weightStringExpr)
   348  	}
   349  
   350  	if weightcolNumber, ok := rb.weightStrings[rc]; ok {
   351  		return weightcolNumber, nil
   352  	}
   353  	// It's ok to pass nil for pb and logicalPlan because PushSelect doesn't use them.
   354  	// TODO: we are ignoring a potential error here. need to clean this up
   355  	_, _, weightcolNumber, err = planProjection(nil, rb, expr, nil)
   356  	if err != nil {
   357  		return 0, err
   358  	}
   359  	rb.weightStrings[rc] = weightcolNumber
   360  	return weightcolNumber, nil
   361  }
   362  
   363  // Rewrite implements the logicalPlan interface
   364  func (rb *route) Rewrite(inputs ...logicalPlan) error {
   365  	if len(inputs) != 0 {
   366  		return vterrors.VT13001("route: wrong number of inputs")
   367  	}
   368  	return nil
   369  }
   370  
   371  // Inputs implements the logicalPlan interface
   372  func (rb *route) Inputs() []logicalPlan {
   373  	return []logicalPlan{}
   374  }
   375  
   376  // MergeSubquery returns true if the subquery route could successfully be merged
   377  // with the outer route.
   378  func (rb *route) MergeSubquery(pb *primitiveBuilder, inner *route) bool {
   379  	if rb.SubqueryCanMerge(pb, inner) {
   380  		if inner.eroute.Opcode == engine.DBA && (len(inner.eroute.SysTableTableName) > 0 || len(inner.eroute.SysTableTableSchema) > 0) {
   381  			switch rb.eroute.Opcode {
   382  			case engine.DBA, engine.Reference:
   383  				rb.eroute.SysTableTableSchema = append(rb.eroute.SysTableTableSchema, inner.eroute.SysTableTableSchema...)
   384  				for k, v := range inner.eroute.SysTableTableName {
   385  					if rb.eroute.SysTableTableName == nil {
   386  						rb.eroute.SysTableTableName = map[string]evalengine.Expr{}
   387  					}
   388  					rb.eroute.SysTableTableName[k] = v
   389  				}
   390  				rb.eroute.Opcode = engine.DBA
   391  			default:
   392  				return false
   393  			}
   394  		} else {
   395  			if rb.eroute.Opcode == engine.Reference {
   396  				rb.eroute.RoutingParameters = inner.eroute.RoutingParameters
   397  				rb.condition = inner.condition
   398  			}
   399  		}
   400  
   401  		rb.substitutions = append(rb.substitutions, inner.substitutions...)
   402  		inner.Redirect = rb
   403  		return true
   404  	}
   405  	return false
   406  }
   407  
   408  // MergeUnion returns true if the rhs route could successfully be merged
   409  // with the rb route.
   410  func (rb *route) MergeUnion(right *route, isDistinct bool) bool {
   411  	if rb.unionCanMerge(right, isDistinct) {
   412  		rb.substitutions = append(rb.substitutions, right.substitutions...)
   413  		right.Redirect = rb
   414  		return true
   415  	}
   416  	return false
   417  }
   418  
   419  func (rb *route) isSingleShard() bool {
   420  	return rb.eroute.Opcode.IsSingleShard()
   421  }
   422  
   423  // JoinCanMerge, SubqueryCanMerge and unionCanMerge have subtly different behaviors.
   424  // The difference in behavior is around SelectReference.
   425  // It's not worth trying to reuse the code between them.
   426  func (rb *route) JoinCanMerge(pb *primitiveBuilder, rrb *route, ajoin *sqlparser.JoinTableExpr, where sqlparser.Expr) bool {
   427  	if rb.eroute.Keyspace.Name != rrb.eroute.Keyspace.Name {
   428  		return false
   429  	}
   430  	if rrb.eroute.Opcode == engine.Reference {
   431  		// Any opcode can join with a reference table.
   432  		return true
   433  	}
   434  	switch rb.eroute.Opcode {
   435  	case engine.Unsharded:
   436  		return rb.eroute.Opcode == rrb.eroute.Opcode
   437  	case engine.EqualUnique:
   438  		// Check if they target the same shard.
   439  		if rrb.eroute.Opcode == engine.EqualUnique && rb.eroute.Vindex == rrb.eroute.Vindex && valEqual(rb.condition, rrb.condition) {
   440  			return true
   441  		}
   442  	case engine.Reference:
   443  		return true
   444  	case engine.Next:
   445  		return false
   446  	case engine.DBA:
   447  		if rrb.eroute.Opcode != engine.DBA {
   448  			return false
   449  		}
   450  		if where == nil {
   451  			return true
   452  		}
   453  		return ajoin != nil
   454  	}
   455  	if ajoin == nil {
   456  		return false
   457  	}
   458  	for _, filter := range sqlparser.SplitAndExpression(nil, ajoin.Condition.On) {
   459  		if rb.canMergeOnFilter(pb, rrb, filter) {
   460  			return true
   461  		}
   462  	}
   463  	return false
   464  }
   465  
   466  func (rb *route) SubqueryCanMerge(pb *primitiveBuilder, inner *route) bool {
   467  	if rb.eroute.Keyspace.Name != inner.eroute.Keyspace.Name {
   468  		return false
   469  	}
   470  
   471  	// if either side is a reference table, and we know the other side will only run once,
   472  	// we can just merge them and use the opcode of the other side
   473  	if rb.eroute.Opcode == engine.Reference || inner.eroute.Opcode == engine.Reference {
   474  		return rb.isSingleShard() && inner.isSingleShard()
   475  	}
   476  
   477  	switch rb.eroute.Opcode {
   478  	case engine.Unsharded, engine.DBA:
   479  		return rb.eroute.Opcode == inner.eroute.Opcode
   480  	case engine.EqualUnique:
   481  		// Check if they target the same shard.
   482  		if inner.eroute.Opcode == engine.EqualUnique && rb.eroute.Vindex == inner.eroute.Vindex && valEqual(rb.condition, inner.condition) {
   483  			return true
   484  		}
   485  	case engine.Next:
   486  		return false
   487  	}
   488  
   489  	switch vals := inner.condition.(type) {
   490  	case *sqlparser.ColName:
   491  		if pb.st.Vindex(vals, rb) == inner.eroute.Vindex {
   492  			return true
   493  		}
   494  	}
   495  	return false
   496  }
   497  
   498  func (rb *route) unionCanMerge(other *route, distinct bool) bool {
   499  	if rb.eroute.Keyspace.Name != other.eroute.Keyspace.Name {
   500  		return false
   501  	}
   502  	switch rb.eroute.Opcode {
   503  	case engine.Unsharded, engine.Reference:
   504  		return rb.eroute.Opcode == other.eroute.Opcode
   505  	case engine.DBA:
   506  		return other.eroute.Opcode == engine.DBA &&
   507  			len(rb.eroute.SysTableTableSchema) == 0 &&
   508  			len(rb.eroute.SysTableTableName) == 0 &&
   509  			len(other.eroute.SysTableTableSchema) == 0 &&
   510  			len(other.eroute.SysTableTableName) == 0
   511  	case engine.EqualUnique:
   512  		// Check if they target the same shard.
   513  		if other.eroute.Opcode == engine.EqualUnique && rb.eroute.Vindex == other.eroute.Vindex && valEqual(rb.condition, other.condition) {
   514  			return true
   515  		}
   516  	case engine.Scatter:
   517  		return other.eroute.Opcode == engine.Scatter && !distinct
   518  	case engine.Next:
   519  		return false
   520  	}
   521  	return false
   522  }
   523  
   524  // canMergeOnFilter returns true if the join constraint makes the routes
   525  // mergeable by unique vindex. The constraint has to be an equality
   526  // like a.id = b.id where both columns have the same unique vindex.
   527  func (rb *route) canMergeOnFilter(pb *primitiveBuilder, rrb *route, filter sqlparser.Expr) bool {
   528  	comparison, ok := filter.(*sqlparser.ComparisonExpr)
   529  	if !ok {
   530  		return false
   531  	}
   532  	if comparison.Operator != sqlparser.EqualOp {
   533  		return false
   534  	}
   535  	left := comparison.Left
   536  	right := comparison.Right
   537  	lVindex := pb.st.Vindex(left, rb)
   538  	if lVindex == nil {
   539  		left, right = right, left
   540  		lVindex = pb.st.Vindex(left, rb)
   541  	}
   542  	if lVindex == nil || !lVindex.IsUnique() {
   543  		return false
   544  	}
   545  	rVindex := pb.st.Vindex(right, rrb)
   546  	if rVindex == nil {
   547  		return false
   548  	}
   549  	return rVindex == lVindex
   550  }
   551  
   552  // UpdatePlan evaluates the primitive against the specified
   553  // filter. If it's an improvement, the primitive is updated.
   554  // We assume that the filter has already been pushed into
   555  // the route.
   556  func (rb *route) UpdatePlan(pb *primitiveBuilder, filter sqlparser.Expr) {
   557  	switch rb.eroute.Opcode {
   558  	// For these opcodes, a new filter will not make any difference, so we can just exit early
   559  	case engine.Unsharded, engine.Next, engine.DBA, engine.Reference, engine.None:
   560  		return
   561  	}
   562  	opcode, vindex, values := rb.computePlan(pb, filter)
   563  	if opcode == engine.Scatter {
   564  		return
   565  	}
   566  	// If we get SelectNone in next filters, override the previous route plan.
   567  	if opcode == engine.None {
   568  		rb.updateRoute(opcode, vindex, values)
   569  		return
   570  	}
   571  	switch rb.eroute.Opcode {
   572  	case engine.EqualUnique:
   573  		if opcode == engine.EqualUnique && vindex.Cost() < rb.eroute.Vindex.Cost() {
   574  			rb.updateRoute(opcode, vindex, values)
   575  		}
   576  	case engine.Equal:
   577  		switch opcode {
   578  		case engine.EqualUnique:
   579  			rb.updateRoute(opcode, vindex, values)
   580  		case engine.Equal:
   581  			if vindex.Cost() < rb.eroute.Vindex.Cost() {
   582  				rb.updateRoute(opcode, vindex, values)
   583  			}
   584  		}
   585  	case engine.IN:
   586  		switch opcode {
   587  		case engine.EqualUnique, engine.Equal:
   588  			rb.updateRoute(opcode, vindex, values)
   589  		case engine.IN:
   590  			if vindex.Cost() < rb.eroute.Vindex.Cost() {
   591  				rb.updateRoute(opcode, vindex, values)
   592  			}
   593  		}
   594  	case engine.MultiEqual:
   595  		switch opcode {
   596  		case engine.EqualUnique, engine.Equal, engine.IN:
   597  			rb.updateRoute(opcode, vindex, values)
   598  		case engine.MultiEqual:
   599  			if vindex.Cost() < rb.eroute.Vindex.Cost() {
   600  				rb.updateRoute(opcode, vindex, values)
   601  			}
   602  		}
   603  	case engine.Scatter:
   604  		switch opcode {
   605  		case engine.EqualUnique, engine.Equal, engine.IN, engine.MultiEqual, engine.None:
   606  			rb.updateRoute(opcode, vindex, values)
   607  		}
   608  	}
   609  }
   610  
   611  func (rb *route) updateRoute(opcode engine.Opcode, vindex vindexes.SingleColumn, condition sqlparser.Expr) {
   612  	rb.eroute.Opcode = opcode
   613  	rb.eroute.Vindex = vindex
   614  	rb.condition = condition
   615  }
   616  
   617  // computePlan computes the plan for the specified filter.
   618  func (rb *route) computePlan(pb *primitiveBuilder, filter sqlparser.Expr) (opcode engine.Opcode, vindex vindexes.SingleColumn, condition sqlparser.Expr) {
   619  	switch node := filter.(type) {
   620  	case *sqlparser.ComparisonExpr:
   621  		switch node.Operator {
   622  		case sqlparser.EqualOp:
   623  			return rb.computeEqualPlan(pb, node)
   624  		case sqlparser.InOp:
   625  			return rb.computeINPlan(pb, node)
   626  		case sqlparser.NotInOp:
   627  			return rb.computeNotInPlan(node.Right), nil, nil
   628  		case sqlparser.LikeOp:
   629  			return rb.computeLikePlan(pb, node)
   630  		}
   631  	case *sqlparser.IsExpr:
   632  		return rb.computeISPlan(pb, node)
   633  	}
   634  	return engine.Scatter, nil, nil
   635  }
   636  
   637  // computeLikePlan computes the plan for 'LIKE' constraint
   638  func (rb *route) computeLikePlan(pb *primitiveBuilder, comparison *sqlparser.ComparisonExpr) (opcode engine.Opcode, vindex vindexes.SingleColumn, condition sqlparser.Expr) {
   639  
   640  	left := comparison.Left
   641  	right := comparison.Right
   642  
   643  	if sqlparser.IsNull(right) {
   644  		return engine.None, nil, nil
   645  	}
   646  	if !rb.exprIsValue(right) {
   647  		return engine.Scatter, nil, nil
   648  	}
   649  	vindex = pb.st.Vindex(left, rb)
   650  	if vindex == nil {
   651  		// if there is no vindex defined, scatter
   652  		return engine.Scatter, nil, nil
   653  	}
   654  	if subsharding, ok := vindex.(vindexes.Prefixable); ok {
   655  		return engine.Equal, subsharding.PrefixVindex(), right
   656  	}
   657  
   658  	return engine.Scatter, nil, nil
   659  }
   660  
   661  // computeEqualPlan computes the plan for an equality constraint.
   662  func (rb *route) computeEqualPlan(pb *primitiveBuilder, comparison *sqlparser.ComparisonExpr) (opcode engine.Opcode, vindex vindexes.SingleColumn, condition sqlparser.Expr) {
   663  	left := comparison.Left
   664  	right := comparison.Right
   665  
   666  	if sqlparser.IsNull(right) {
   667  		return engine.None, nil, nil
   668  	}
   669  
   670  	vindex = pb.st.Vindex(left, rb)
   671  	if vindex == nil {
   672  		left, right = right, left
   673  		vindex = pb.st.Vindex(left, rb)
   674  		if vindex == nil {
   675  			return engine.Scatter, nil, nil
   676  		}
   677  	}
   678  	if !rb.exprIsValue(right) {
   679  		return engine.Scatter, nil, nil
   680  	}
   681  	if vindex.IsUnique() {
   682  		return engine.EqualUnique, vindex, right
   683  	}
   684  	return engine.Equal, vindex, right
   685  }
   686  
   687  // computeIS computes the plan for an equality constraint.
   688  func (rb *route) computeISPlan(pb *primitiveBuilder, comparison *sqlparser.IsExpr) (opcode engine.Opcode, vindex vindexes.SingleColumn, expr sqlparser.Expr) {
   689  	// we only handle IS NULL correct. IsExpr can contain other expressions as well
   690  	if comparison.Right != sqlparser.IsNullOp {
   691  		return engine.Scatter, nil, nil
   692  	}
   693  
   694  	vindex = pb.st.Vindex(comparison.Left, rb)
   695  	// fallback to scatter gather if there is no vindex
   696  	if vindex == nil {
   697  		return engine.Scatter, nil, nil
   698  	}
   699  	if _, isLookup := vindex.(vindexes.Lookup); isLookup {
   700  		// the lookup table is keyed by the lookup value, so it does not support nulls
   701  		return engine.Scatter, nil, nil
   702  	}
   703  	if vindex.IsUnique() {
   704  		return engine.EqualUnique, vindex, &sqlparser.NullVal{}
   705  	}
   706  	return engine.Equal, vindex, &sqlparser.NullVal{}
   707  }
   708  
   709  // computeINPlan computes the plan for an IN constraint.
   710  func (rb *route) computeINPlan(pb *primitiveBuilder, comparison *sqlparser.ComparisonExpr) (opcode engine.Opcode, vindex vindexes.SingleColumn, expr sqlparser.Expr) {
   711  	switch comparison.Left.(type) {
   712  	case *sqlparser.ColName:
   713  		return rb.computeSimpleINPlan(pb, comparison)
   714  	case sqlparser.ValTuple:
   715  		return rb.computeCompositeINPlan(pb, comparison)
   716  	}
   717  	return engine.Scatter, nil, nil
   718  }
   719  
   720  // computeSimpleINPlan computes the plan for a simple IN constraint.
   721  func (rb *route) computeSimpleINPlan(pb *primitiveBuilder, comparison *sqlparser.ComparisonExpr) (opcode engine.Opcode, vindex vindexes.SingleColumn, expr sqlparser.Expr) {
   722  	vindex = pb.st.Vindex(comparison.Left, rb)
   723  	if vindex == nil {
   724  		return engine.Scatter, nil, nil
   725  	}
   726  	switch node := comparison.Right.(type) {
   727  	case sqlparser.ValTuple:
   728  		if len(node) == 1 && sqlparser.IsNull(node[0]) {
   729  			return engine.None, nil, nil
   730  		}
   731  
   732  		for _, n := range node {
   733  			if !rb.exprIsValue(n) {
   734  				return engine.Scatter, nil, nil
   735  			}
   736  		}
   737  		return engine.IN, vindex, comparison
   738  	case sqlparser.ListArg:
   739  		return engine.IN, vindex, comparison
   740  	}
   741  	return engine.Scatter, nil, nil
   742  }
   743  
   744  // computeCompositeINPlan computes the plan for a composite IN constraint.
   745  func (rb *route) computeCompositeINPlan(pb *primitiveBuilder, comparison *sqlparser.ComparisonExpr) (opcode engine.Opcode, vindex vindexes.SingleColumn, values sqlparser.Expr) {
   746  	leftTuple := comparison.Left.(sqlparser.ValTuple)
   747  	return rb.iterateCompositeIN(pb, comparison, nil, leftTuple)
   748  }
   749  
   750  // iterateCompositeIN recursively walks the LHS tuple of the IN clause looking
   751  // for column names. For those that match a vindex, it builds a multi-value plan
   752  // using the corresponding values in the RHS. It returns the best of the plans built.
   753  func (rb *route) iterateCompositeIN(
   754  	pb *primitiveBuilder,
   755  	comparison *sqlparser.ComparisonExpr,
   756  	coordinates []int,
   757  	tuple sqlparser.ValTuple,
   758  ) (opcode engine.Opcode, vindex vindexes.SingleColumn, values sqlparser.Expr) {
   759  	opcode = engine.Scatter
   760  
   761  	cindex := len(coordinates)
   762  	coordinates = append(coordinates, 0)
   763  	for idx, expr := range tuple {
   764  		coordinates[cindex] = idx
   765  		switch expr := expr.(type) {
   766  		case sqlparser.ValTuple:
   767  			newOpcode, newVindex, newValues := rb.iterateCompositeIN(pb, comparison, coordinates, expr)
   768  			opcode, vindex, values = bestOfComposite(opcode, newOpcode, vindex, newVindex, values, newValues)
   769  		case *sqlparser.ColName:
   770  			newVindex := pb.st.Vindex(expr, rb)
   771  			if newVindex != nil {
   772  				newOpcode, newValues := rb.compositePlanForCol(pb, comparison, coordinates)
   773  				opcode, vindex, values = bestOfComposite(opcode, newOpcode, vindex, newVindex, values, newValues)
   774  			}
   775  		}
   776  	}
   777  	return opcode, vindex, values
   778  }
   779  
   780  // compositePlanForCol builds a plan for a matched column in the LHS
   781  // of a composite IN clause.
   782  func (rb *route) compositePlanForCol(pb *primitiveBuilder, comparison *sqlparser.ComparisonExpr, coordinates []int) (opcode engine.Opcode, values sqlparser.Expr) {
   783  	rightTuple, ok := comparison.Right.(sqlparser.ValTuple)
   784  	if !ok {
   785  		return engine.Scatter, nil
   786  	}
   787  	retVal := make(sqlparser.ValTuple, len(rightTuple))
   788  	for i, rval := range rightTuple {
   789  		val := tupleAccess(rval, coordinates)
   790  		if val == nil {
   791  			return engine.Scatter, nil
   792  		}
   793  		if !rb.exprIsValue(val) {
   794  			return engine.Scatter, nil
   795  		}
   796  		retVal[i] = val
   797  	}
   798  	return engine.MultiEqual, retVal
   799  }
   800  
   801  // tupleAccess returns the value of the expression that corresponds
   802  // to the specified coordinates.
   803  func tupleAccess(expr sqlparser.Expr, coordinates []int) sqlparser.Expr {
   804  	tuple, _ := expr.(sqlparser.ValTuple)
   805  	for _, idx := range coordinates {
   806  		if idx >= len(tuple) {
   807  			return nil
   808  		}
   809  		expr = tuple[idx]
   810  		tuple, _ = expr.(sqlparser.ValTuple)
   811  	}
   812  	return expr
   813  }
   814  
   815  // bestOfComposite returns the best of two composite IN clause plans.
   816  func bestOfComposite(opcode1, opcode2 engine.Opcode, vindex1, vindex2 vindexes.SingleColumn, values1, values2 sqlparser.Expr) (opcode engine.Opcode, vindex vindexes.SingleColumn, values sqlparser.Expr) {
   817  	if opcode1 == engine.Scatter {
   818  		return opcode2, vindex2, values2
   819  	}
   820  	if opcode2 == engine.Scatter {
   821  		return opcode1, vindex1, values1
   822  	}
   823  	if vindex1.Cost() < vindex2.Cost() {
   824  		return opcode1, vindex1, values1
   825  	}
   826  	return opcode2, vindex2, values2
   827  }
   828  
   829  // computeNotInPlan looks for null values to produce a SelectNone if found
   830  func (rb *route) computeNotInPlan(right sqlparser.Expr) engine.Opcode {
   831  	switch node := right.(type) {
   832  	case sqlparser.ValTuple:
   833  		for _, n := range node {
   834  			if sqlparser.IsNull(n) {
   835  				return engine.None
   836  			}
   837  		}
   838  	}
   839  
   840  	return engine.Scatter
   841  }
   842  
   843  // exprIsValue returns true if the expression can be treated as a value
   844  // for the routeOption. External references are treated as value.
   845  func (rb *route) exprIsValue(expr sqlparser.Expr) bool {
   846  	if node, ok := expr.(*sqlparser.ColName); ok {
   847  		return node.Metadata.(*column).Origin() != rb
   848  	}
   849  	return sqlparser.IsValue(expr)
   850  }
   851  
   852  // queryTimeout returns DirectiveQueryTimeout value if set, otherwise returns 0.
   853  func queryTimeout(d *sqlparser.CommentDirectives) int {
   854  	val, _ := d.GetString(sqlparser.DirectiveQueryTimeout, "0")
   855  	if intVal, err := strconv.Atoi(val); err == nil {
   856  		return intVal
   857  	}
   858  	return 0
   859  }