vitess.io/vitess@v0.16.2/go/vt/vtgate/planbuilder/operators/route_planning.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  	"bytes"
    21  	"fmt"
    22  	"io"
    23  
    24  	"vitess.io/vitess/go/vt/vtgate/planbuilder/operators/rewrite"
    25  
    26  	"vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops"
    27  
    28  	"vitess.io/vitess/go/vt/key"
    29  	"vitess.io/vitess/go/vt/sqlparser"
    30  	"vitess.io/vitess/go/vt/vterrors"
    31  	"vitess.io/vitess/go/vt/vtgate/engine"
    32  	"vitess.io/vitess/go/vt/vtgate/evalengine"
    33  	"vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext"
    34  	"vitess.io/vitess/go/vt/vtgate/semantics"
    35  	"vitess.io/vitess/go/vt/vtgate/vindexes"
    36  
    37  	querypb "vitess.io/vitess/go/vt/proto/query"
    38  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    39  )
    40  
    41  type (
    42  	tableSetPair struct {
    43  		left, right semantics.TableSet
    44  	}
    45  
    46  	opCacheMap map[tableSetPair]ops.Operator
    47  )
    48  
    49  // TransformToPhysical takes an operator tree and rewrites any parts that have not yet been planned as physical operators.
    50  // This is where a lot of the optimisations of the query plans are done.
    51  // Here we try to merge query parts into the same route primitives. At the end of this process,
    52  // all the operators in the tree are guaranteed to be PhysicalOperators
    53  func transformToPhysical(ctx *plancontext.PlanningContext, in ops.Operator) (ops.Operator, error) {
    54  	op, err := rewrite.BottomUp(in, func(operator ops.Operator) (ops.Operator, rewrite.TreeIdentity, error) {
    55  		switch op := operator.(type) {
    56  		case *QueryGraph:
    57  			return optimizeQueryGraph(ctx, op)
    58  		case *Join:
    59  			return optimizeJoin(ctx, op)
    60  		case *Derived:
    61  			return optimizeDerived(ctx, op)
    62  		case *SubQuery:
    63  			return optimizeSubQuery(ctx, op)
    64  		case *Filter:
    65  			return optimizeFilter(op)
    66  		default:
    67  			return operator, rewrite.SameTree, nil
    68  		}
    69  	})
    70  
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	err = rewrite.Visit(op, func(op ops.Operator) error {
    76  		if _, isPhys := op.(ops.PhysicalOperator); !isPhys {
    77  			return vterrors.VT13001(fmt.Sprintf("failed to transform %T to a physical operator", op))
    78  		}
    79  		return nil
    80  	})
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  
    85  	return Compact(ctx, op)
    86  }
    87  
    88  func optimizeFilter(op *Filter) (ops.Operator, rewrite.TreeIdentity, error) {
    89  	if route, ok := op.Source.(*Route); ok {
    90  		// let's push the filter into the route
    91  		op.Source = route.Source
    92  		route.Source = op
    93  		return route, rewrite.NewTree, nil
    94  	}
    95  
    96  	return op, rewrite.SameTree, nil
    97  }
    98  
    99  func optimizeDerived(ctx *plancontext.PlanningContext, op *Derived) (ops.Operator, rewrite.TreeIdentity, error) {
   100  	innerRoute, ok := op.Source.(*Route)
   101  	if !ok {
   102  		return op, rewrite.SameTree, nil
   103  	}
   104  
   105  	if !(innerRoute.RouteOpCode == engine.EqualUnique) && !op.IsMergeable(ctx) {
   106  		// no need to check anything if we are sure that we will only hit a single shard
   107  		return op, rewrite.SameTree, nil
   108  	}
   109  
   110  	op.Source = innerRoute.Source
   111  	innerRoute.Source = op
   112  
   113  	return innerRoute, rewrite.NewTree, nil
   114  }
   115  
   116  func optimizeJoin(ctx *plancontext.PlanningContext, op *Join) (ops.Operator, rewrite.TreeIdentity, error) {
   117  	join, err := mergeOrJoin(ctx, op.LHS, op.RHS, sqlparser.SplitAndExpression(nil, op.Predicate), !op.LeftJoin)
   118  	if err != nil {
   119  		return nil, rewrite.SameTree, err
   120  	}
   121  	return join, rewrite.NewTree, nil
   122  }
   123  
   124  func optimizeQueryGraph(ctx *plancontext.PlanningContext, op *QueryGraph) (result ops.Operator, changed rewrite.TreeIdentity, err error) {
   125  	changed = rewrite.NewTree
   126  	switch {
   127  	case ctx.PlannerVersion == querypb.ExecuteOptions_Gen4Left2Right:
   128  		result, err = leftToRightSolve(ctx, op)
   129  	default:
   130  		result, err = greedySolve(ctx, op)
   131  	}
   132  
   133  	unresolved := op.UnsolvedPredicates(ctx.SemTable)
   134  	if len(unresolved) > 0 {
   135  		// if we have any predicates that none of the joins or tables took care of,
   136  		// we add a single filter on top, so we don't lose it. This is used for sub-query planning
   137  		result = newFilter(result, ctx.SemTable.AndExpressions(unresolved...))
   138  	}
   139  
   140  	return
   141  }
   142  
   143  func buildVindexTableForDML(ctx *plancontext.PlanningContext, tableInfo semantics.TableInfo, table *QueryTable, dmlType string) (*vindexes.Table, engine.Opcode, key.Destination, error) {
   144  	vindexTable := tableInfo.GetVindexTable()
   145  	opCode := engine.Unsharded
   146  	if vindexTable.Keyspace.Sharded {
   147  		opCode = engine.Scatter
   148  	}
   149  
   150  	if vindexTable.Source != nil {
   151  		sourceTable, _, _, _, _, err := ctx.VSchema.FindTableOrVindex(vindexTable.Source.TableName)
   152  		if err != nil {
   153  			return nil, 0, nil, err
   154  		}
   155  		vindexTable = sourceTable
   156  	}
   157  
   158  	var dest key.Destination
   159  	var typ topodatapb.TabletType
   160  	var err error
   161  	tblName, ok := table.Alias.Expr.(sqlparser.TableName)
   162  	if ok {
   163  		_, _, _, typ, dest, err = ctx.VSchema.FindTableOrVindex(tblName)
   164  		if err != nil {
   165  			return nil, 0, nil, err
   166  		}
   167  		if dest != nil {
   168  			if typ != topodatapb.TabletType_PRIMARY {
   169  				return nil, 0, nil, vterrors.VT09002(dmlType)
   170  			}
   171  			// we are dealing with an explicitly targeted UPDATE
   172  			opCode = engine.ByDestination
   173  		}
   174  	}
   175  	return vindexTable, opCode, dest, nil
   176  }
   177  
   178  func generateOwnedVindexQuery(tblExpr sqlparser.TableExpr, del *sqlparser.Delete, table *vindexes.Table, ksidCols []sqlparser.IdentifierCI) string {
   179  	buf := sqlparser.NewTrackedBuffer(nil)
   180  	for idx, col := range ksidCols {
   181  		if idx == 0 {
   182  			buf.Myprintf("select %v", col)
   183  		} else {
   184  			buf.Myprintf(", %v", col)
   185  		}
   186  	}
   187  	for _, cv := range table.Owned {
   188  		for _, column := range cv.Columns {
   189  			buf.Myprintf(", %v", column)
   190  		}
   191  	}
   192  	buf.Myprintf(" from %v%v%v%v for update", tblExpr, del.Where, del.OrderBy, del.Limit)
   193  	return buf.String()
   194  }
   195  
   196  func getUpdateVindexInformation(
   197  	updStmt *sqlparser.Update,
   198  	vindexTable *vindexes.Table,
   199  	tableID semantics.TableSet,
   200  	predicates []sqlparser.Expr,
   201  ) ([]*VindexPlusPredicates, map[string]*engine.VindexValues, string, error) {
   202  	if !vindexTable.Keyspace.Sharded {
   203  		return nil, nil, "", nil
   204  	}
   205  
   206  	primaryVindex, vindexAndPredicates, err := getVindexInformation(tableID, predicates, vindexTable)
   207  	if err != nil {
   208  		return nil, nil, "", err
   209  	}
   210  
   211  	changedVindexValues, ownedVindexQuery, err := buildChangedVindexesValues(updStmt, vindexTable, primaryVindex.Columns)
   212  	if err != nil {
   213  		return nil, nil, "", err
   214  	}
   215  	return vindexAndPredicates, changedVindexValues, ownedVindexQuery, nil
   216  }
   217  
   218  /*
   219  		The greedy planner will plan a query by finding first finding the best route plan for every table.
   220  	    Then, iteratively, it finds the cheapest join that can be produced between the remaining plans,
   221  		and removes the two inputs to this cheapest plan and instead adds the join.
   222  		As an optimization, it first only considers joining tables that have predicates defined between them
   223  */
   224  func greedySolve(ctx *plancontext.PlanningContext, qg *QueryGraph) (ops.Operator, error) {
   225  	routeOps, err := seedOperatorList(ctx, qg)
   226  	planCache := opCacheMap{}
   227  	if err != nil {
   228  		return nil, err
   229  	}
   230  
   231  	op, err := mergeRoutes(ctx, qg, routeOps, planCache, false)
   232  	if err != nil {
   233  		return nil, err
   234  	}
   235  	return op, nil
   236  }
   237  
   238  func leftToRightSolve(ctx *plancontext.PlanningContext, qg *QueryGraph) (ops.Operator, error) {
   239  	plans, err := seedOperatorList(ctx, qg)
   240  	if err != nil {
   241  		return nil, err
   242  	}
   243  
   244  	var acc ops.Operator
   245  	for _, plan := range plans {
   246  		if acc == nil {
   247  			acc = plan
   248  			continue
   249  		}
   250  		joinPredicates := qg.GetPredicates(TableID(acc), TableID(plan))
   251  		acc, err = mergeOrJoin(ctx, acc, plan, joinPredicates, true)
   252  		if err != nil {
   253  			return nil, err
   254  		}
   255  	}
   256  
   257  	return acc, nil
   258  }
   259  
   260  // seedOperatorList returns a route for each table in the qg
   261  func seedOperatorList(ctx *plancontext.PlanningContext, qg *QueryGraph) ([]ops.Operator, error) {
   262  	plans := make([]ops.Operator, len(qg.Tables))
   263  
   264  	// we start by seeding the table with the single routes
   265  	for i, table := range qg.Tables {
   266  		solves := ctx.SemTable.TableSetFor(table.Alias)
   267  		plan, err := createRoute(ctx, table, solves)
   268  		if err != nil {
   269  			return nil, err
   270  		}
   271  		if qg.NoDeps != nil {
   272  			plan, err = plan.AddPredicate(ctx, qg.NoDeps)
   273  			if err != nil {
   274  				return nil, err
   275  			}
   276  		}
   277  		plans[i] = plan
   278  	}
   279  	return plans, nil
   280  }
   281  
   282  func createInfSchemaRoute(ctx *plancontext.PlanningContext, table *QueryTable) (ops.Operator, error) {
   283  	ks, err := ctx.VSchema.AnyKeyspace()
   284  	if err != nil {
   285  		return nil, err
   286  	}
   287  	r := &Route{
   288  		RouteOpCode: engine.DBA,
   289  		Source: &Table{
   290  			QTable: table,
   291  			VTable: &vindexes.Table{
   292  				Name:     table.Table.Name,
   293  				Keyspace: ks,
   294  			},
   295  		},
   296  		Keyspace: ks,
   297  	}
   298  	for _, pred := range table.Predicates {
   299  		isTableSchema, bvName, out, err := extractInfoSchemaRoutingPredicate(pred, ctx.ReservedVars)
   300  		if err != nil {
   301  			return nil, err
   302  		}
   303  		if out == nil {
   304  			// we didn't find a predicate to use for routing, continue to look for next predicate
   305  			continue
   306  		}
   307  
   308  		if isTableSchema {
   309  			r.SysTableTableSchema = append(r.SysTableTableSchema, out)
   310  		} else {
   311  			if r.SysTableTableName == nil {
   312  				r.SysTableTableName = map[string]evalengine.Expr{}
   313  			}
   314  			r.SysTableTableName[bvName] = out
   315  		}
   316  	}
   317  	return r, nil
   318  }
   319  
   320  func mergeRoutes(ctx *plancontext.PlanningContext, qg *QueryGraph, physicalOps []ops.Operator, planCache opCacheMap, crossJoinsOK bool) (ops.Operator, error) {
   321  	if len(physicalOps) == 0 {
   322  		return nil, nil
   323  	}
   324  	for len(physicalOps) > 1 {
   325  		bestTree, lIdx, rIdx, err := findBestJoin(ctx, qg, physicalOps, planCache, crossJoinsOK)
   326  		if err != nil {
   327  			return nil, err
   328  		}
   329  		// if we found a plan, we'll replace the two plans that were joined with the join plan created
   330  		if bestTree != nil {
   331  			// we remove one plan, and replace the other
   332  			if rIdx > lIdx {
   333  				physicalOps = removeAt(physicalOps, rIdx)
   334  				physicalOps = removeAt(physicalOps, lIdx)
   335  			} else {
   336  				physicalOps = removeAt(physicalOps, lIdx)
   337  				physicalOps = removeAt(physicalOps, rIdx)
   338  			}
   339  			physicalOps = append(physicalOps, bestTree)
   340  		} else {
   341  			if crossJoinsOK {
   342  				return nil, vterrors.VT13001("should not happen: we should be able to merge cross joins")
   343  			}
   344  			// we will only fail to find a join plan when there are only cross joins left
   345  			// when that happens, we switch over to allow cross joins as well.
   346  			// this way we prioritize joining physicalOps with predicates first
   347  			crossJoinsOK = true
   348  		}
   349  	}
   350  	return physicalOps[0], nil
   351  }
   352  
   353  func removeAt(plans []ops.Operator, idx int) []ops.Operator {
   354  	return append(plans[:idx], plans[idx+1:]...)
   355  }
   356  
   357  func findBestJoin(
   358  	ctx *plancontext.PlanningContext,
   359  	qg *QueryGraph,
   360  	plans []ops.Operator,
   361  	planCache opCacheMap,
   362  	crossJoinsOK bool,
   363  ) (bestPlan ops.Operator, lIdx int, rIdx int, err error) {
   364  	for i, lhs := range plans {
   365  		for j, rhs := range plans {
   366  			if i == j {
   367  				continue
   368  			}
   369  			joinPredicates := qg.GetPredicates(TableID(lhs), TableID(rhs))
   370  			if len(joinPredicates) == 0 && !crossJoinsOK {
   371  				// if there are no predicates joining the two tables,
   372  				// creating a join between them would produce a
   373  				// cartesian product, which is almost always a bad idea
   374  				continue
   375  			}
   376  			plan, err := getJoinFor(ctx, planCache, lhs, rhs, joinPredicates)
   377  			if err != nil {
   378  				return nil, 0, 0, err
   379  			}
   380  			if bestPlan == nil || CostOf(plan) < CostOf(bestPlan) {
   381  				bestPlan = plan
   382  				// remember which plans we based on, so we can remove them later
   383  				lIdx = i
   384  				rIdx = j
   385  			}
   386  		}
   387  	}
   388  	return bestPlan, lIdx, rIdx, nil
   389  }
   390  
   391  func getJoinFor(ctx *plancontext.PlanningContext, cm opCacheMap, lhs, rhs ops.Operator, joinPredicates []sqlparser.Expr) (ops.Operator, error) {
   392  	solves := tableSetPair{left: TableID(lhs), right: TableID(rhs)}
   393  	cachedPlan := cm[solves]
   394  	if cachedPlan != nil {
   395  		return cachedPlan, nil
   396  	}
   397  
   398  	join, err := mergeOrJoin(ctx, lhs, rhs, joinPredicates, true)
   399  	if err != nil {
   400  		return nil, err
   401  	}
   402  	cm[solves] = join
   403  	return join, nil
   404  }
   405  
   406  // requiresSwitchingSides will return true if any of the operators with the root from the given operator tree
   407  // is of the type that should not be on the RHS of a join
   408  func requiresSwitchingSides(ctx *plancontext.PlanningContext, op ops.Operator) bool {
   409  	required := false
   410  
   411  	_ = rewrite.Visit(op, func(current ops.Operator) error {
   412  		derived, isDerived := current.(*Derived)
   413  
   414  		if isDerived && !derived.IsMergeable(ctx) {
   415  			required = true
   416  			return io.EOF
   417  		}
   418  
   419  		return nil
   420  	})
   421  
   422  	return required
   423  }
   424  
   425  func mergeOrJoin(ctx *plancontext.PlanningContext, lhs, rhs ops.Operator, joinPredicates []sqlparser.Expr, inner bool) (ops.Operator, error) {
   426  	merger := func(a, b *Route) (*Route, error) {
   427  		return createRouteOperatorForJoin(ctx, a, b, joinPredicates, inner)
   428  	}
   429  
   430  	newPlan, _ := tryMerge(ctx, lhs, rhs, joinPredicates, merger)
   431  	if newPlan != nil {
   432  		return newPlan, nil
   433  	}
   434  
   435  	if len(joinPredicates) > 0 && requiresSwitchingSides(ctx, rhs) {
   436  		if !inner {
   437  			return nil, vterrors.VT12001("LEFT JOIN with derived tables")
   438  		}
   439  
   440  		if requiresSwitchingSides(ctx, lhs) {
   441  			return nil, vterrors.VT12001("JOIN between derived tables")
   442  		}
   443  
   444  		join := NewApplyJoin(Clone(rhs), Clone(lhs), nil, !inner)
   445  		return pushJoinPredicates(ctx, joinPredicates, join)
   446  	}
   447  
   448  	join := NewApplyJoin(Clone(lhs), Clone(rhs), nil, !inner)
   449  	return pushJoinPredicates(ctx, joinPredicates, join)
   450  }
   451  
   452  func createRouteOperatorForJoin(ctx *plancontext.PlanningContext, aRoute, bRoute *Route, joinPredicates []sqlparser.Expr, inner bool) (*Route, error) {
   453  	// append system table names from both the routes.
   454  	sysTableName := aRoute.SysTableTableName
   455  	if sysTableName == nil {
   456  		sysTableName = bRoute.SysTableTableName
   457  	} else {
   458  		for k, v := range bRoute.SysTableTableName {
   459  			sysTableName[k] = v
   460  		}
   461  	}
   462  
   463  	join := NewApplyJoin(aRoute.Source, bRoute.Source, ctx.SemTable.AndExpressions(joinPredicates...), !inner)
   464  	r := &Route{
   465  		RouteOpCode:         aRoute.RouteOpCode,
   466  		Keyspace:            aRoute.Keyspace,
   467  		VindexPreds:         append(aRoute.VindexPreds, bRoute.VindexPreds...),
   468  		SysTableTableSchema: append(aRoute.SysTableTableSchema, bRoute.SysTableTableSchema...),
   469  		SeenPredicates:      append(aRoute.SeenPredicates, bRoute.SeenPredicates...),
   470  		SysTableTableName:   sysTableName,
   471  		Source:              join,
   472  		MergedWith:          []*Route{bRoute},
   473  	}
   474  
   475  	if aRoute.SelectedVindex() == bRoute.SelectedVindex() {
   476  		r.Selected = aRoute.Selected
   477  	}
   478  
   479  	return r, nil
   480  }
   481  
   482  type mergeFunc func(a, b *Route) (*Route, error)
   483  
   484  func operatorsToRoutes(a, b ops.Operator) (*Route, *Route) {
   485  	aRoute, ok := a.(*Route)
   486  	if !ok {
   487  		return nil, nil
   488  	}
   489  	bRoute, ok := b.(*Route)
   490  	if !ok {
   491  		return nil, nil
   492  	}
   493  	return aRoute, bRoute
   494  }
   495  
   496  func tryMerge(
   497  	ctx *plancontext.PlanningContext,
   498  	a, b ops.Operator,
   499  	joinPredicates []sqlparser.Expr,
   500  	merger mergeFunc,
   501  ) (ops.Operator, error) {
   502  	aRoute, bRoute := operatorsToRoutes(Clone(a), Clone(b))
   503  	if aRoute == nil || bRoute == nil {
   504  		return nil, nil
   505  	}
   506  
   507  	sameKeyspace := aRoute.Keyspace == bRoute.Keyspace
   508  
   509  	if !sameKeyspace {
   510  		if altARoute := aRoute.AlternateInKeyspace(bRoute.Keyspace); altARoute != nil {
   511  			aRoute = altARoute
   512  			sameKeyspace = true
   513  		} else if altBRoute := bRoute.AlternateInKeyspace(aRoute.Keyspace); altBRoute != nil {
   514  			bRoute = altBRoute
   515  			sameKeyspace = true
   516  		}
   517  	}
   518  
   519  	if sameKeyspace || (isDualTable(aRoute) || isDualTable(bRoute)) {
   520  		tree, err := tryMergeReferenceTable(aRoute, bRoute, merger)
   521  		if tree != nil || err != nil {
   522  			return tree, err
   523  		}
   524  	}
   525  
   526  	switch aRoute.RouteOpCode {
   527  	case engine.Unsharded, engine.DBA:
   528  		if aRoute.RouteOpCode == bRoute.RouteOpCode && sameKeyspace {
   529  			return merger(aRoute, bRoute)
   530  		}
   531  	case engine.EqualUnique:
   532  		// If the two routes fully match, they can be merged together.
   533  		if bRoute.RouteOpCode == engine.EqualUnique {
   534  			aVdx := aRoute.SelectedVindex()
   535  			bVdx := bRoute.SelectedVindex()
   536  			aExpr := aRoute.VindexExpressions()
   537  			bExpr := bRoute.VindexExpressions()
   538  			if aVdx == bVdx && gen4ValuesEqual(ctx, aExpr, bExpr) {
   539  				return merger(aRoute, bRoute)
   540  			}
   541  		}
   542  
   543  		// If the two routes don't match, fall through to the next case and see if we
   544  		// can merge via join predicates instead.
   545  		fallthrough
   546  
   547  	case engine.Scatter, engine.IN, engine.None:
   548  		if len(joinPredicates) == 0 {
   549  			// If we are doing two Scatters, we have to make sure that the
   550  			// joins are on the correct vindex to allow them to be merged
   551  			// no join predicates - no vindex
   552  			return nil, nil
   553  		}
   554  
   555  		if !sameKeyspace {
   556  			return nil, vterrors.VT12001("cross-shard correlated subquery")
   557  		}
   558  
   559  		canMerge := canMergeOnFilters(ctx, aRoute, bRoute, joinPredicates)
   560  		if !canMerge {
   561  			return nil, nil
   562  		}
   563  		r, err := merger(aRoute, bRoute)
   564  		if err != nil {
   565  			return nil, err
   566  		}
   567  
   568  		// If we have a `None` route opcode, we want to keep it -
   569  		// we only try to find a better Vindex for other route opcodes
   570  		if aRoute.RouteOpCode != engine.None {
   571  			r.PickBestAvailableVindex()
   572  		}
   573  
   574  		return r, nil
   575  	}
   576  	return nil, nil
   577  }
   578  
   579  func isDualTable(route *Route) bool {
   580  	sources := leaves(route)
   581  	if len(sources) > 1 {
   582  		return false
   583  	}
   584  	src, ok := sources[0].(*Table)
   585  	if !ok {
   586  		return false
   587  	}
   588  	return src.VTable.Name.String() == "dual" && src.QTable.Table.Qualifier.IsEmpty()
   589  }
   590  
   591  func leaves(op ops.Operator) (sources []ops.Operator) {
   592  	switch op := op.(type) {
   593  	// these are the leaves
   594  	case *Table:
   595  		return []ops.Operator{op}
   596  		// physical
   597  	case *ApplyJoin:
   598  		return []ops.Operator{op.LHS, op.RHS}
   599  	case *Filter:
   600  		return []ops.Operator{op.Source}
   601  	case *Route:
   602  		return []ops.Operator{op.Source}
   603  	}
   604  
   605  	panic(fmt.Sprintf("leaves unknown type: %T", op))
   606  }
   607  
   608  func tryMergeReferenceTable(aRoute, bRoute *Route, merger mergeFunc) (*Route, error) {
   609  	var (
   610  		// if either side is a reference table, we can just merge it and use the opcode of the other side
   611  		opCode engine.Opcode
   612  		vindex *VindexOption
   613  		ks     *vindexes.Keyspace
   614  	)
   615  
   616  	switch {
   617  	case aRoute.RouteOpCode == engine.Reference:
   618  		vindex = bRoute.Selected
   619  		opCode = bRoute.RouteOpCode
   620  		ks = bRoute.Keyspace
   621  	case bRoute.RouteOpCode == engine.Reference:
   622  		vindex = aRoute.Selected
   623  		opCode = aRoute.RouteOpCode
   624  		ks = aRoute.Keyspace
   625  	default:
   626  		return nil, nil
   627  	}
   628  
   629  	r, err := merger(aRoute, bRoute)
   630  	if err != nil {
   631  		return nil, err
   632  	}
   633  	r.RouteOpCode = opCode
   634  	r.Selected = vindex
   635  	r.Keyspace = ks
   636  	return r, nil
   637  }
   638  
   639  func canMergeOnFilter(ctx *plancontext.PlanningContext, a, b *Route, predicate sqlparser.Expr) bool {
   640  	comparison, ok := predicate.(*sqlparser.ComparisonExpr)
   641  	if !ok {
   642  		return false
   643  	}
   644  	if comparison.Operator != sqlparser.EqualOp {
   645  		return false
   646  	}
   647  	left := comparison.Left
   648  	right := comparison.Right
   649  
   650  	lVindex := findColumnVindex(ctx, a, left)
   651  	if lVindex == nil {
   652  		left, right = right, left
   653  		lVindex = findColumnVindex(ctx, a, left)
   654  	}
   655  	if lVindex == nil || !lVindex.IsUnique() {
   656  		return false
   657  	}
   658  	rVindex := findColumnVindex(ctx, b, right)
   659  	if rVindex == nil {
   660  		return false
   661  	}
   662  	return rVindex == lVindex
   663  }
   664  
   665  func findColumnVindex(ctx *plancontext.PlanningContext, a ops.Operator, exp sqlparser.Expr) vindexes.SingleColumn {
   666  	_, isCol := exp.(*sqlparser.ColName)
   667  	if !isCol {
   668  		return nil
   669  	}
   670  
   671  	exp = unwrapDerivedTables(ctx, exp)
   672  	if exp == nil {
   673  		return nil
   674  	}
   675  
   676  	var singCol vindexes.SingleColumn
   677  
   678  	// for each equality expression that exp has with other column name, we check if it
   679  	// can be solved by any table in our routeTree. If an equality expression can be solved,
   680  	// we check if the equality expression and our table share the same vindex, if they do:
   681  	// the method will return the associated vindexes.SingleColumn.
   682  	for _, expr := range ctx.SemTable.GetExprAndEqualities(exp) {
   683  		col, isCol := expr.(*sqlparser.ColName)
   684  		if !isCol {
   685  			continue
   686  		}
   687  
   688  		deps := ctx.SemTable.RecursiveDeps(expr)
   689  
   690  		_ = rewrite.Visit(a, func(rel ops.Operator) error {
   691  			to, isTableOp := rel.(TableIDIntroducer)
   692  			if !isTableOp {
   693  				return nil
   694  			}
   695  			id := to.Introduces()
   696  			if deps.IsSolvedBy(id) {
   697  				tableInfo, err := ctx.SemTable.TableInfoFor(id)
   698  				if err != nil {
   699  					// an error here is OK, we just can't ask this operator about its column vindexes
   700  					return nil
   701  				}
   702  				vtable := tableInfo.GetVindexTable()
   703  				if vtable != nil {
   704  					for _, vindex := range vtable.ColumnVindexes {
   705  						sC, isSingle := vindex.Vindex.(vindexes.SingleColumn)
   706  						if isSingle && vindex.Columns[0].Equal(col.Name) {
   707  							singCol = sC
   708  							return io.EOF
   709  						}
   710  					}
   711  				}
   712  			}
   713  			return nil
   714  		})
   715  		if singCol != nil {
   716  			return singCol
   717  		}
   718  	}
   719  
   720  	return singCol
   721  }
   722  
   723  // unwrapDerivedTables we want to find the bottom layer of derived tables
   724  // nolint
   725  func unwrapDerivedTables(ctx *plancontext.PlanningContext, exp sqlparser.Expr) sqlparser.Expr {
   726  	for {
   727  		// if we are dealing with derived tables in derived tables
   728  		tbl, err := ctx.SemTable.TableInfoForExpr(exp)
   729  		if err != nil {
   730  			return nil
   731  		}
   732  		_, ok := tbl.(*semantics.DerivedTable)
   733  		if !ok {
   734  			break
   735  		}
   736  
   737  		exp = semantics.RewriteDerivedTableExpression(exp, tbl)
   738  		exp = getColName(exp)
   739  		if exp == nil {
   740  			return nil
   741  		}
   742  	}
   743  	return exp
   744  }
   745  
   746  func getColName(exp sqlparser.Expr) *sqlparser.ColName {
   747  	switch exp := exp.(type) {
   748  	case *sqlparser.ColName:
   749  		return exp
   750  	case *sqlparser.Max, *sqlparser.Min:
   751  		aggr := exp.(sqlparser.AggrFunc).GetArg()
   752  		colName, ok := aggr.(*sqlparser.ColName)
   753  		if ok {
   754  			return colName
   755  		}
   756  	}
   757  	// for any other expression than a column, or the extremum of a column, we return nil
   758  	return nil
   759  }
   760  
   761  func canMergeOnFilters(ctx *plancontext.PlanningContext, a, b *Route, joinPredicates []sqlparser.Expr) bool {
   762  	for _, predicate := range joinPredicates {
   763  		for _, expr := range sqlparser.SplitAndExpression(nil, predicate) {
   764  			if canMergeOnFilter(ctx, a, b, expr) {
   765  				return true
   766  			}
   767  		}
   768  	}
   769  	return false
   770  }
   771  
   772  func gen4ValuesEqual(ctx *plancontext.PlanningContext, a, b []sqlparser.Expr) bool {
   773  	if len(a) != len(b) {
   774  		return false
   775  	}
   776  
   777  	// TODO: check SemTable's columnEqualities for better plan
   778  
   779  	for i, aExpr := range a {
   780  		bExpr := b[i]
   781  		if !gen4ValEqual(ctx, aExpr, bExpr) {
   782  			return false
   783  		}
   784  	}
   785  	return true
   786  }
   787  
   788  func gen4ValEqual(ctx *plancontext.PlanningContext, a, b sqlparser.Expr) bool {
   789  	switch a := a.(type) {
   790  	case *sqlparser.ColName:
   791  		if b, ok := b.(*sqlparser.ColName); ok {
   792  			if !a.Name.Equal(b.Name) {
   793  				return false
   794  			}
   795  
   796  			return ctx.SemTable.DirectDeps(a) == ctx.SemTable.DirectDeps(b)
   797  		}
   798  	case sqlparser.Argument:
   799  		b, ok := b.(sqlparser.Argument)
   800  		if !ok {
   801  			return false
   802  		}
   803  		return a == b
   804  	case *sqlparser.Literal:
   805  		b, ok := b.(*sqlparser.Literal)
   806  		if !ok {
   807  			return false
   808  		}
   809  		switch a.Type {
   810  		case sqlparser.StrVal:
   811  			switch b.Type {
   812  			case sqlparser.StrVal:
   813  				return a.Val == b.Val
   814  			case sqlparser.HexVal:
   815  				return hexEqual(b, a)
   816  			}
   817  		case sqlparser.HexVal:
   818  			return hexEqual(a, b)
   819  		case sqlparser.IntVal:
   820  			if b.Type == (sqlparser.IntVal) {
   821  				return a.Val == b.Val
   822  			}
   823  		}
   824  	}
   825  	return false
   826  }
   827  
   828  func hexEqual(a, b *sqlparser.Literal) bool {
   829  	v, err := a.HexDecode()
   830  	if err != nil {
   831  		return false
   832  	}
   833  	switch b.Type {
   834  	case sqlparser.StrVal:
   835  		return bytes.Equal(v, b.Bytes())
   836  	case sqlparser.HexVal:
   837  		v2, err := b.HexDecode()
   838  		if err != nil {
   839  			return false
   840  		}
   841  		return bytes.Equal(v, v2)
   842  	}
   843  	return false
   844  }
   845  
   846  func pushJoinPredicates(ctx *plancontext.PlanningContext, exprs []sqlparser.Expr, op *ApplyJoin) (ops.Operator, error) {
   847  	if len(exprs) == 0 {
   848  		return op, nil
   849  	}
   850  
   851  	for _, expr := range exprs {
   852  		_, err := AddPredicate(op, ctx, expr, true, newFilter)
   853  		if err != nil {
   854  			return nil, err
   855  		}
   856  	}
   857  
   858  	return op, nil
   859  }