vitess.io/vitess@v0.16.2/go/vt/vtgate/planbuilder/routeGen4.go (about)

     1  /*
     2  Copyright 2021 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package planbuilder
    18  
    19  import (
    20  	querypb "vitess.io/vitess/go/vt/proto/query"
    21  	"vitess.io/vitess/go/vt/vterrors"
    22  	"vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext"
    23  	"vitess.io/vitess/go/vt/vtgate/semantics"
    24  
    25  	"vitess.io/vitess/go/vt/sqlparser"
    26  	"vitess.io/vitess/go/vt/vtgate/engine"
    27  	"vitess.io/vitess/go/vt/vtgate/vindexes"
    28  )
    29  
    30  var _ logicalPlan = (*routeGen4)(nil)
    31  
    32  // routeGen4 is used to build a Route primitive.
    33  // It's used to build one of the Select routes like
    34  // SelectScatter, etc. Portions of the original Select AST
    35  // are moved into this node, which will be used to build
    36  // the final SQL for this route.
    37  type routeGen4 struct {
    38  	gen4Plan
    39  
    40  	// Select is the AST for the query fragment that will be
    41  	// executed by this route.
    42  	Select sqlparser.SelectStatement
    43  
    44  	// condition stores the AST condition that will be used
    45  	// to resolve the ERoute Values field.
    46  	condition sqlparser.Expr
    47  
    48  	// eroute is the primitive being built.
    49  	eroute *engine.Route
    50  
    51  	// is the engine primitive we will return from the Primitive() method. Note that it could be different than eroute
    52  	enginePrimitive engine.Primitive
    53  
    54  	// tables keeps track of which tables this route is covering
    55  	tables semantics.TableSet
    56  }
    57  
    58  // Primitive implements the logicalPlan interface
    59  func (rb *routeGen4) Primitive() engine.Primitive {
    60  	return rb.enginePrimitive
    61  }
    62  
    63  // SetLimit adds a LIMIT clause to the route.
    64  func (rb *routeGen4) SetLimit(limit *sqlparser.Limit) {
    65  	rb.Select.SetLimit(limit)
    66  }
    67  
    68  // WireupGen4 implements the logicalPlan interface
    69  func (rb *routeGen4) WireupGen4(ctx *plancontext.PlanningContext) error {
    70  	rb.prepareTheAST()
    71  
    72  	// prepare the queries we will pass down
    73  	rb.eroute.Query = sqlparser.String(rb.Select)
    74  	buffer := sqlparser.NewTrackedBuffer(sqlparser.FormatImpossibleQuery)
    75  	node := buffer.WriteNode(rb.Select)
    76  	parsedQuery := node.ParsedQuery()
    77  	rb.eroute.FieldQuery = parsedQuery.Query
    78  
    79  	// if we have a planable vindex lookup, let's extract it into its own primitive
    80  	planableVindex, ok := rb.eroute.RoutingParameters.Vindex.(vindexes.LookupPlanable)
    81  	if !ok {
    82  		rb.enginePrimitive = rb.eroute
    83  		return nil
    84  	}
    85  
    86  	query, args := planableVindex.Query()
    87  	stmt, reserved, err := sqlparser.Parse2(query)
    88  	if err != nil {
    89  		return err
    90  	}
    91  	reservedVars := sqlparser.NewReservedVars("vtg", reserved)
    92  
    93  	lookupPrimitive, err := gen4SelectStmtPlanner(query, querypb.ExecuteOptions_Gen4, stmt.(sqlparser.SelectStatement), reservedVars, ctx.VSchema)
    94  	if err != nil {
    95  		return vterrors.Wrapf(err, "failed to plan the lookup query: [%s]", query)
    96  	}
    97  
    98  	rb.enginePrimitive = &engine.VindexLookup{
    99  		Opcode:    rb.eroute.Opcode,
   100  		Vindex:    planableVindex,
   101  		Keyspace:  rb.eroute.Keyspace,
   102  		Values:    rb.eroute.Values,
   103  		SendTo:    rb.eroute,
   104  		Arguments: args,
   105  		Lookup:    lookupPrimitive.primitive,
   106  	}
   107  
   108  	rb.eroute.RoutingParameters.Opcode = engine.ByDestination
   109  	rb.eroute.RoutingParameters.Values = nil
   110  	rb.eroute.RoutingParameters.Vindex = nil
   111  
   112  	return nil
   113  }
   114  
   115  // ContainsTables implements the logicalPlan interface
   116  func (rb *routeGen4) ContainsTables() semantics.TableSet {
   117  	return rb.tables
   118  }
   119  
   120  // OutputColumns implements the logicalPlan interface
   121  func (rb *routeGen4) OutputColumns() []sqlparser.SelectExpr {
   122  	return sqlparser.GetFirstSelect(rb.Select).SelectExprs
   123  }
   124  
   125  // prepareTheAST does minor fixups of the SELECT struct before producing the query string
   126  func (rb *routeGen4) prepareTheAST() {
   127  	_ = sqlparser.Walk(func(node sqlparser.SQLNode) (bool, error) {
   128  		switch node := node.(type) {
   129  		case *sqlparser.Select:
   130  			if len(node.SelectExprs) == 0 {
   131  				node.SelectExprs = []sqlparser.SelectExpr{
   132  					&sqlparser.AliasedExpr{
   133  						Expr: sqlparser.NewIntLiteral("1"),
   134  					},
   135  				}
   136  			}
   137  		case *sqlparser.ComparisonExpr:
   138  			// 42 = colName -> colName = 42
   139  			b := node.Operator == sqlparser.EqualOp
   140  			value := sqlparser.IsValue(node.Left)
   141  			name := sqlparser.IsColName(node.Right)
   142  			if b &&
   143  				value &&
   144  				name {
   145  				node.Left, node.Right = node.Right, node.Left
   146  			}
   147  		}
   148  		return true, nil
   149  	}, rb.Select)
   150  }
   151  
   152  func (rb *routeGen4) isLocal(col *sqlparser.ColName) bool {
   153  	return col.Metadata.(*column).Origin() == rb
   154  }
   155  
   156  // generateFieldQuery generates a query with an impossible where.
   157  // This will be used on the RHS node to fetch field info if the LHS
   158  // returns no result.
   159  func (rb *routeGen4) generateFieldQuery(sel sqlparser.SelectStatement, jt *jointab) string {
   160  	formatter := func(buf *sqlparser.TrackedBuffer, node sqlparser.SQLNode) {
   161  		switch node := node.(type) {
   162  		case *sqlparser.ColName:
   163  			if !rb.isLocal(node) {
   164  				_, joinVar := jt.Lookup(node)
   165  				buf.WriteArg(":", joinVar)
   166  				return
   167  			}
   168  		case sqlparser.TableName:
   169  			if !sqlparser.SystemSchema(node.Qualifier.String()) {
   170  				node.Name.Format(buf)
   171  				return
   172  			}
   173  			node.Format(buf)
   174  			return
   175  		}
   176  		sqlparser.FormatImpossibleQuery(buf, node)
   177  	}
   178  
   179  	buffer := sqlparser.NewTrackedBuffer(formatter)
   180  	node := buffer.WriteNode(sel)
   181  	query := node.ParsedQuery()
   182  	return query.Query
   183  }
   184  
   185  // Rewrite implements the logicalPlan interface
   186  func (rb *routeGen4) Rewrite(inputs ...logicalPlan) error {
   187  	if len(inputs) != 0 {
   188  		return vterrors.VT13001("route: wrong number of inputs")
   189  	}
   190  	return nil
   191  }
   192  
   193  // Inputs implements the logicalPlan interface
   194  func (rb *routeGen4) Inputs() []logicalPlan {
   195  	return []logicalPlan{}
   196  }
   197  
   198  func (rb *routeGen4) isSingleShard() bool {
   199  	return rb.eroute.Opcode.IsSingleShard()
   200  }
   201  
   202  func (rb *routeGen4) unionCanMerge(other *routeGen4, distinct bool) bool {
   203  	if rb.eroute.Keyspace.Name != other.eroute.Keyspace.Name {
   204  		return false
   205  	}
   206  	switch rb.eroute.Opcode {
   207  	case engine.Unsharded, engine.Reference:
   208  		return rb.eroute.Opcode == other.eroute.Opcode
   209  	case engine.DBA:
   210  		return other.eroute.Opcode == engine.DBA &&
   211  			len(rb.eroute.SysTableTableSchema) == 0 &&
   212  			len(rb.eroute.SysTableTableName) == 0 &&
   213  			len(other.eroute.SysTableTableSchema) == 0 &&
   214  			len(other.eroute.SysTableTableName) == 0
   215  	case engine.EqualUnique:
   216  		// Check if they target the same shard.
   217  		if other.eroute.Opcode == engine.EqualUnique && rb.eroute.Vindex == other.eroute.Vindex && valEqual(rb.condition, other.condition) {
   218  			return true
   219  		}
   220  	case engine.Scatter:
   221  		return other.eroute.Opcode == engine.Scatter && !distinct
   222  	case engine.Next:
   223  		return false
   224  	}
   225  	return false
   226  }
   227  
   228  func (rb *routeGen4) updateRoute(opcode engine.Opcode, vindex vindexes.SingleColumn, condition sqlparser.Expr) {
   229  	rb.eroute.Opcode = opcode
   230  	rb.eroute.Vindex = vindex
   231  	rb.condition = condition
   232  }
   233  
   234  // computeNotInPlan looks for null values to produce a SelectNone if found
   235  func (rb *routeGen4) computeNotInPlan(right sqlparser.Expr) engine.Opcode {
   236  	switch node := right.(type) {
   237  	case sqlparser.ValTuple:
   238  		for _, n := range node {
   239  			if sqlparser.IsNull(n) {
   240  				return engine.None
   241  			}
   242  		}
   243  	}
   244  
   245  	return engine.Scatter
   246  }
   247  
   248  // exprIsValue returns true if the expression can be treated as a value
   249  // for the routeOption. External references are treated as value.
   250  func (rb *routeGen4) exprIsValue(expr sqlparser.Expr) bool {
   251  	if node, ok := expr.(*sqlparser.ColName); ok {
   252  		return node.Metadata.(*column).Origin() != rb
   253  	}
   254  	return sqlparser.IsValue(expr)
   255  }