vitess.io/vitess@v0.16.2/go/vt/vtgate/planbuilder/from.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  	"sort"
    22  	"strings"
    23  
    24  	"vitess.io/vitess/go/mysql/collations"
    25  	"vitess.io/vitess/go/vt/vtgate/evalengine"
    26  
    27  	querypb "vitess.io/vitess/go/vt/proto/query"
    28  	"vitess.io/vitess/go/vt/vterrors"
    29  
    30  	"vitess.io/vitess/go/vt/sqlparser"
    31  	"vitess.io/vitess/go/vt/vtgate/engine"
    32  	"vitess.io/vitess/go/vt/vtgate/vindexes"
    33  )
    34  
    35  // This file has functions to analyze the FROM clause.
    36  
    37  // processDMLTable analyzes the FROM clause for DMLs and returns a route.
    38  func (pb *primitiveBuilder) processDMLTable(tableExprs sqlparser.TableExprs, reservedVars *sqlparser.ReservedVars, where sqlparser.Expr) (*route, error) {
    39  	if err := pb.processTableExprs(tableExprs, reservedVars, where); err != nil {
    40  		return nil, err
    41  	}
    42  	rb, ok := pb.plan.(*route)
    43  	if !ok {
    44  		return nil, vterrors.VT12001("multi-shard or vindex write statement")
    45  	}
    46  	for _, sub := range rb.substitutions {
    47  		*sub.oldExpr = *sub.newExpr
    48  	}
    49  	return rb, nil
    50  }
    51  
    52  // processTableExprs analyzes the FROM clause. It produces a logicalPlan
    53  // with all the routes identified.
    54  func (pb *primitiveBuilder) processTableExprs(tableExprs sqlparser.TableExprs, reservedVars *sqlparser.ReservedVars, where sqlparser.Expr) error {
    55  	if len(tableExprs) == 1 {
    56  		return pb.processTableExpr(tableExprs[0], reservedVars, where)
    57  	}
    58  
    59  	if err := pb.processTableExpr(tableExprs[0], reservedVars, where); err != nil {
    60  		return err
    61  	}
    62  	rpb := newPrimitiveBuilder(pb.vschema, pb.jt)
    63  	if err := rpb.processTableExprs(tableExprs[1:], reservedVars, where); err != nil {
    64  		return err
    65  	}
    66  	return pb.join(rpb, nil, reservedVars, where)
    67  }
    68  
    69  // processTableExpr produces a logicalPlan subtree for the given TableExpr.
    70  func (pb *primitiveBuilder) processTableExpr(tableExpr sqlparser.TableExpr, reservedVars *sqlparser.ReservedVars, where sqlparser.Expr) error {
    71  	switch tableExpr := tableExpr.(type) {
    72  	case *sqlparser.AliasedTableExpr:
    73  		return pb.processAliasedTable(tableExpr, reservedVars)
    74  	case *sqlparser.ParenTableExpr:
    75  		err := pb.processTableExprs(tableExpr.Exprs, reservedVars, where)
    76  		// If it's a route, preserve the parenthesis so things
    77  		// don't associate differently when more things are pushed
    78  		// into it. FROM a, (b, c) should not become FROM a, b, c.
    79  		if rb, ok := pb.plan.(*route); ok {
    80  			sel, ok := rb.Select.(*sqlparser.Select)
    81  			if !ok {
    82  				return vterrors.VT13002(sqlparser.String(rb.Select))
    83  			}
    84  
    85  			sel.From = sqlparser.TableExprs{&sqlparser.ParenTableExpr{Exprs: sel.From}}
    86  		}
    87  		return err
    88  	case *sqlparser.JoinTableExpr:
    89  		return pb.processJoin(tableExpr, reservedVars, where)
    90  	case *sqlparser.JSONTableExpr:
    91  		return vterrors.VT12001("JSON_TABLE expressions")
    92  	}
    93  	return vterrors.VT13001(fmt.Sprintf("unexpected table expression type: %T", tableExpr))
    94  }
    95  
    96  // processAliasedTable produces a logicalPlan subtree for the given AliasedTableExpr.
    97  // If the expression is a subquery, then the primitive will create a table
    98  // for it in the symtab. If the subquery is a route, then we build a route
    99  // primitive with the subquery in the From clause, because a route is more
   100  // versatile than a subquery. If a subquery becomes a route, then any result
   101  // columns that represent underlying vindex columns are also exposed as
   102  // vindex columns.
   103  func (pb *primitiveBuilder) processAliasedTable(tableExpr *sqlparser.AliasedTableExpr, reservedVars *sqlparser.ReservedVars) error {
   104  	if tableExpr.Columns != nil {
   105  		return vterrors.VT12001("column aliases in derived table")
   106  	}
   107  	switch expr := tableExpr.Expr.(type) {
   108  	case sqlparser.TableName:
   109  		return pb.buildTablePrimitive(tableExpr, expr)
   110  	case *sqlparser.DerivedTable:
   111  		if expr.Lateral {
   112  			return vterrors.VT12001("lateral derived tables")
   113  		}
   114  		spb := newPrimitiveBuilder(pb.vschema, pb.jt)
   115  		switch stmt := expr.Select.(type) {
   116  		case *sqlparser.Select:
   117  			if err := spb.processSelect(stmt, reservedVars, nil, ""); err != nil {
   118  				return err
   119  			}
   120  		case *sqlparser.Union:
   121  			if err := spb.processUnion(stmt, reservedVars, nil); err != nil {
   122  				return err
   123  			}
   124  		default:
   125  			return vterrors.VT13001(fmt.Sprintf("unexpected SELECT type: %T", stmt))
   126  		}
   127  
   128  		subroute, ok := spb.plan.(*route)
   129  		if !ok {
   130  			var err error
   131  			pb.plan, pb.st, err = newSimpleProjection(tableExpr.As, spb.plan)
   132  			if err != nil {
   133  				return err
   134  			}
   135  			pb.plan.Reorder(0)
   136  			return nil
   137  		}
   138  
   139  		// Since a route is more versatile than a subquery, we
   140  		// build a route primitive that has the subquery in its
   141  		// FROM clause. This allows for other constructs to be
   142  		// later pushed into it.
   143  		rb, st := newRoute(&sqlparser.Select{From: []sqlparser.TableExpr{tableExpr}})
   144  		rb.substitutions = subroute.substitutions
   145  		rb.condition = subroute.condition
   146  		rb.eroute = subroute.eroute
   147  		subroute.Redirect = rb
   148  
   149  		// The subquery needs to be represented as a new logical table in the symtab.
   150  		// The new route will inherit the routeOptions of the underlying subquery.
   151  		// For this, we first build new vschema tables based on the columns returned
   152  		// by the subquery, and re-expose possible vindexes. When added to the symtab,
   153  		// a new set of column references will be generated against the new tables,
   154  		// and those vindex maps will be returned. They have to replace the old vindex
   155  		// maps of the inherited route options.
   156  		var tableNames []string
   157  		spbTables, err := spb.st.AllVschemaTableNames()
   158  		if err != nil {
   159  			return err
   160  		}
   161  		for _, table := range spbTables {
   162  			tableNames = append(tableNames, table.Name.String())
   163  		}
   164  		sort.Strings(tableNames)
   165  		vschemaTable := &vindexes.Table{
   166  			Keyspace: subroute.eroute.Keyspace,
   167  			Name:     sqlparser.NewIdentifierCS(strings.Join(tableNames, ", ")),
   168  		}
   169  		for _, rc := range subroute.ResultColumns() {
   170  			if rc.column.vindex == nil {
   171  				continue
   172  			}
   173  			// Check if a colvindex of the same name already exists.
   174  			// Dups are not allowed in subqueries in this situation.
   175  			for _, colVindex := range vschemaTable.ColumnVindexes {
   176  				if colVindex.Columns[0].Equal(rc.alias) {
   177  					return vterrors.VT12001(fmt.Sprintf("duplicate column aliases: %v", rc.alias))
   178  				}
   179  			}
   180  			vschemaTable.ColumnVindexes = append(vschemaTable.ColumnVindexes, &vindexes.ColumnVindex{
   181  				Columns: []sqlparser.IdentifierCI{rc.alias},
   182  				Vindex:  rc.column.vindex,
   183  			})
   184  		}
   185  		if err := st.AddVSchemaTable(sqlparser.TableName{Name: tableExpr.As}, vschemaTable, rb); err != nil {
   186  			return err
   187  		}
   188  
   189  		pb.plan, pb.st = rb, st
   190  		return nil
   191  	}
   192  	return vterrors.VT13001(fmt.Sprintf("unexpected table expression type: %T", tableExpr.Expr))
   193  }
   194  
   195  // buildTablePrimitive builds a primitive based on the table name.
   196  func (pb *primitiveBuilder) buildTablePrimitive(tableExpr *sqlparser.AliasedTableExpr, tableName sqlparser.TableName) error {
   197  	alias := tableName
   198  	if !tableExpr.As.IsEmpty() {
   199  		alias = sqlparser.TableName{Name: tableExpr.As}
   200  	}
   201  	sel := &sqlparser.Select{From: sqlparser.TableExprs([]sqlparser.TableExpr{tableExpr})}
   202  
   203  	if sqlparser.SystemSchema(tableName.Qualifier.String()) {
   204  		ks, err := pb.vschema.AnyKeyspace()
   205  		if err != nil {
   206  			return err
   207  		}
   208  		rb, st := newRoute(sel)
   209  		rb.eroute = engine.NewSimpleRoute(engine.DBA, ks)
   210  		rb.eroute.TableName = sqlparser.String(tableName)
   211  		pb.plan, pb.st = rb, st
   212  		// Add the table to symtab
   213  		return st.AddTable(&table{
   214  			alias:  alias,
   215  			origin: rb,
   216  		})
   217  	}
   218  
   219  	vschemaTable, vindex, _, destTableType, destTarget, err := pb.vschema.FindTableOrVindex(tableName)
   220  	if err != nil {
   221  		return err
   222  	}
   223  	if vindex != nil {
   224  		single, ok := vindex.(vindexes.SingleColumn)
   225  		if !ok {
   226  			return vterrors.VT12001("multi-column vindexes")
   227  		}
   228  		pb.plan, pb.st = newVindexFunc(alias, single)
   229  		return nil
   230  	}
   231  
   232  	sourceTable, err := pb.tryRedirectGen4InsertToSource(vschemaTable)
   233  	if err != nil {
   234  		return err
   235  	}
   236  	if sourceTable != nil {
   237  		vschemaTable = sourceTable
   238  	}
   239  
   240  	rb, st := newRoute(sel)
   241  	pb.plan, pb.st = rb, st
   242  	if err := st.AddVSchemaTable(alias, vschemaTable, rb); err != nil {
   243  		return err
   244  	}
   245  
   246  	sub := &tableSubstitution{
   247  		oldExpr: tableExpr,
   248  	}
   249  	if tableExpr.As.IsEmpty() {
   250  		if tableName.Name != vschemaTable.Name {
   251  			// Table name does not match. Change and alias it to old name.
   252  			sub.newExpr = &sqlparser.AliasedTableExpr{
   253  				Expr: sqlparser.TableName{Name: vschemaTable.Name},
   254  				As:   tableName.Name,
   255  			}
   256  		}
   257  	} else {
   258  		// Table is already aliased.
   259  		if tableName.Name != vschemaTable.Name {
   260  			// Table name does not match. Change it and reuse existing alias.
   261  			sub.newExpr = &sqlparser.AliasedTableExpr{
   262  				Expr: sqlparser.TableName{Name: vschemaTable.Name},
   263  				As:   tableExpr.As,
   264  			}
   265  		}
   266  	}
   267  	if sub != nil && sub.newExpr != nil {
   268  		rb.substitutions = []*tableSubstitution{sub}
   269  	}
   270  
   271  	var eroute *engine.Route
   272  	switch {
   273  	case vschemaTable.Type == vindexes.TypeSequence:
   274  		eroute = engine.NewSimpleRoute(engine.Next, vschemaTable.Keyspace)
   275  	case vschemaTable.Type == vindexes.TypeReference:
   276  		eroute = engine.NewSimpleRoute(engine.Reference, vschemaTable.Keyspace)
   277  	case !vschemaTable.Keyspace.Sharded:
   278  		eroute = engine.NewSimpleRoute(engine.Unsharded, vschemaTable.Keyspace)
   279  	case vschemaTable.Pinned == nil:
   280  		eroute = engine.NewSimpleRoute(engine.Scatter, vschemaTable.Keyspace)
   281  		eroute.TargetDestination = destTarget
   282  		eroute.TargetTabletType = destTableType
   283  	default:
   284  		// Pinned tables have their keyspace ids already assigned.
   285  		// Use the Binary vindex, which is the identity function
   286  		// for keyspace id.
   287  		eroute = engine.NewSimpleRoute(engine.EqualUnique, vschemaTable.Keyspace)
   288  		vindex, _ = vindexes.NewBinary("binary", nil)
   289  		eroute.Vindex = vindex
   290  		lit := evalengine.NewLiteralString(vschemaTable.Pinned, collations.TypedCollation{})
   291  		eroute.Values = []evalengine.Expr{lit}
   292  	}
   293  	eroute.TableName = sqlparser.String(vschemaTable.Name)
   294  	rb.eroute = eroute
   295  
   296  	return nil
   297  }
   298  
   299  // processJoin produces a logicalPlan subtree for the given Join.
   300  // If the left and right nodes can be part of the same route,
   301  // then it's a route. Otherwise, it's a join.
   302  func (pb *primitiveBuilder) processJoin(ajoin *sqlparser.JoinTableExpr, reservedVars *sqlparser.ReservedVars, where sqlparser.Expr) error {
   303  	switch ajoin.Join {
   304  	case sqlparser.NormalJoinType, sqlparser.StraightJoinType, sqlparser.LeftJoinType:
   305  	case sqlparser.RightJoinType:
   306  		convertToLeftJoin(ajoin)
   307  	default:
   308  		return vterrors.VT12001(ajoin.Join.ToString())
   309  	}
   310  	if err := pb.processTableExpr(ajoin.LeftExpr, reservedVars, where); err != nil {
   311  		return err
   312  	}
   313  	rpb := newPrimitiveBuilder(pb.vschema, pb.jt)
   314  	if err := rpb.processTableExpr(ajoin.RightExpr, reservedVars, where); err != nil {
   315  		return err
   316  	}
   317  	return pb.join(rpb, ajoin, reservedVars, where)
   318  }
   319  
   320  // If the primitiveBuilder context is a Gen4 planner, the statement is an
   321  // INSERT, and the vschema table is a reference with a valid source reference,
   322  // then redirect the INSERT back to the source.
   323  func (pb *primitiveBuilder) tryRedirectGen4InsertToSource(vschemaTable *vindexes.Table) (*vindexes.Table, error) {
   324  	if pb.stmt == nil {
   325  		return nil, nil
   326  	}
   327  	if _, ok := pb.stmt.(*sqlparser.Insert); !ok {
   328  		return nil, nil
   329  	}
   330  	if pb.vschema.Planner() == querypb.ExecuteOptions_V3 {
   331  		return nil, nil
   332  	}
   333  	if vschemaTable.Type != vindexes.TypeReference || vschemaTable.Source == nil {
   334  		return nil, nil
   335  	}
   336  	vschemaTable, _, _, _, _, err := pb.vschema.FindTableOrVindex(vschemaTable.Source.TableName)
   337  	return vschemaTable, err
   338  }
   339  
   340  // convertToLeftJoin converts a right join into a left join.
   341  func convertToLeftJoin(ajoin *sqlparser.JoinTableExpr) {
   342  	newRHS := ajoin.LeftExpr
   343  	// If the LHS is a join, we have to parenthesize it.
   344  	// Otherwise, it can be used as is.
   345  	if _, ok := newRHS.(*sqlparser.JoinTableExpr); ok {
   346  		newRHS = &sqlparser.ParenTableExpr{
   347  			Exprs: sqlparser.TableExprs{newRHS},
   348  		}
   349  	}
   350  	ajoin.LeftExpr, ajoin.RightExpr = ajoin.RightExpr, newRHS
   351  	ajoin.Join = sqlparser.LeftJoinType
   352  }
   353  
   354  func (pb *primitiveBuilder) join(rpb *primitiveBuilder, ajoin *sqlparser.JoinTableExpr, reservedVars *sqlparser.ReservedVars, where sqlparser.Expr) error {
   355  	// Merge the symbol tables. In the case of a left join, we have to
   356  	// ideally create new symbols that originate from the join primitive.
   357  	// However, this is not worth it for now, because the Push functions
   358  	// verify that only valid constructs are passed through in case of left join.
   359  	err := pb.st.Merge(rpb.st)
   360  	if err != nil {
   361  		return err
   362  	}
   363  
   364  	lRoute, leftIsRoute := pb.plan.(*route)
   365  	rRoute, rightIsRoute := rpb.plan.(*route)
   366  	if !leftIsRoute || !rightIsRoute {
   367  		return newJoin(pb, rpb, ajoin, reservedVars)
   368  	}
   369  
   370  	// Try merging the routes.
   371  	if !lRoute.JoinCanMerge(pb, rRoute, ajoin, where) {
   372  		return newJoin(pb, rpb, ajoin, reservedVars)
   373  	}
   374  
   375  	if lRoute.eroute.Opcode == engine.Reference {
   376  		// Swap the conditions & eroutes, and then merge.
   377  		lRoute.condition, rRoute.condition = rRoute.condition, lRoute.condition
   378  		lRoute.eroute, rRoute.eroute = rRoute.eroute, lRoute.eroute
   379  	}
   380  	lRoute.substitutions = append(lRoute.substitutions, rRoute.substitutions...)
   381  	rRoute.Redirect = lRoute
   382  
   383  	// Merge the AST.
   384  	sel, ok := lRoute.Select.(*sqlparser.Select)
   385  	if !ok {
   386  		return vterrors.VT13002(sqlparser.String(lRoute.Select))
   387  	}
   388  	if ajoin == nil {
   389  		rhsSel, ok := rRoute.Select.(*sqlparser.Select)
   390  		if !ok {
   391  			return vterrors.VT13002(sqlparser.String(rRoute.Select))
   392  		}
   393  		sel.From = append(sel.From, rhsSel.From...)
   394  	} else {
   395  		sel.From = sqlparser.TableExprs{ajoin}
   396  	}
   397  	// join table name
   398  	if lRoute.eroute.TableName != rRoute.eroute.TableName {
   399  		lRoute.eroute.TableName = strings.Join([]string{lRoute.eroute.TableName, rRoute.eroute.TableName}, ", ")
   400  	}
   401  
   402  	// join sysTableNames
   403  	for tableName, expr := range rRoute.eroute.SysTableTableName {
   404  		_, ok := lRoute.eroute.SysTableTableName[tableName]
   405  		if !ok {
   406  			lRoute.eroute.SysTableTableName[tableName] = expr
   407  		}
   408  	}
   409  
   410  	// Since the routes have merged, set st.singleRoute to point at
   411  	// the merged route.
   412  	pb.st.singleRoute = lRoute
   413  	if ajoin == nil {
   414  		return nil
   415  	}
   416  	pullouts, _, expr, err := pb.findOrigin(ajoin.Condition.On, reservedVars)
   417  	if err != nil {
   418  		return err
   419  	}
   420  	ajoin.Condition.On = expr
   421  	pb.addPullouts(pullouts)
   422  	for _, filter := range sqlparser.SplitAndExpression(nil, ajoin.Condition.On) {
   423  		lRoute.UpdatePlan(pb, filter)
   424  	}
   425  	return nil
   426  }