vitess.io/vitess@v0.16.2/go/vt/vtgate/semantics/analyzer.go (about)

     1  /*
     2  Copyright 2020 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 semantics
    18  
    19  import (
    20  	"vitess.io/vitess/go/mysql/collations"
    21  	"vitess.io/vitess/go/vt/vterrors"
    22  	"vitess.io/vitess/go/vt/vtgate/vindexes"
    23  
    24  	"vitess.io/vitess/go/vt/sqlparser"
    25  )
    26  
    27  // analyzer controls the flow of the analysis.
    28  // It starts the tree walking and controls which part of the analysis sees which parts of the tree
    29  type analyzer struct {
    30  	scoper   *scoper
    31  	tables   *tableCollector
    32  	binder   *binder
    33  	typer    *typer
    34  	rewriter *earlyRewriter
    35  
    36  	err          error
    37  	inProjection int
    38  
    39  	projErr      error
    40  	unshardedErr error
    41  	warning      string
    42  }
    43  
    44  // newAnalyzer create the semantic analyzer
    45  func newAnalyzer(dbName string, si SchemaInformation) *analyzer {
    46  	// TODO  dependencies between these components are a little tangled. We should try to clean up
    47  	s := newScoper()
    48  	a := &analyzer{
    49  		scoper: s,
    50  		tables: newTableCollector(s, si, dbName),
    51  		typer:  newTyper(),
    52  	}
    53  	s.org = a
    54  	a.tables.org = a
    55  
    56  	b := newBinder(s, a, a.tables, a.typer)
    57  	a.binder = b
    58  	a.rewriter = &earlyRewriter{
    59  		scoper:          s,
    60  		binder:          b,
    61  		expandedColumns: map[sqlparser.TableName][]*sqlparser.ColName{},
    62  	}
    63  	s.binder = b
    64  	return a
    65  }
    66  
    67  // Analyze analyzes the parsed query.
    68  func Analyze(statement sqlparser.Statement, currentDb string, si SchemaInformation) (*SemTable, error) {
    69  	analyzer := newAnalyzer(currentDb, newSchemaInfo(si))
    70  
    71  	// Analysis for initial scope
    72  	err := analyzer.analyze(statement)
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  
    77  	// Creation of the semantic table
    78  	semTable := analyzer.newSemTable(statement, si.ConnCollation())
    79  
    80  	return semTable, nil
    81  }
    82  
    83  func (a *analyzer) newSemTable(statement sqlparser.Statement, coll collations.ID) *SemTable {
    84  	var comments *sqlparser.ParsedComments
    85  	commentedStmt, isCommented := statement.(sqlparser.Commented)
    86  	if isCommented {
    87  		comments = commentedStmt.GetParsedComments()
    88  	}
    89  
    90  	return &SemTable{
    91  		Recursive:         a.binder.recursive,
    92  		Direct:            a.binder.direct,
    93  		ExprTypes:         a.typer.exprTypes,
    94  		Tables:            a.tables.Tables,
    95  		selectScope:       a.scoper.rScope,
    96  		NotSingleRouteErr: a.projErr,
    97  		NotUnshardedErr:   a.unshardedErr,
    98  		Warning:           a.warning,
    99  		Comments:          comments,
   100  		SubqueryMap:       a.binder.subqueryMap,
   101  		SubqueryRef:       a.binder.subqueryRef,
   102  		ColumnEqualities:  map[columnName][]sqlparser.Expr{},
   103  		Collation:         coll,
   104  		ExpandedColumns:   a.rewriter.expandedColumns,
   105  	}
   106  }
   107  
   108  func (a *analyzer) setError(err error) {
   109  	switch err := err.(type) {
   110  	case ProjError:
   111  		a.projErr = err.Inner
   112  	case ShardedError:
   113  		a.unshardedErr = err.Inner
   114  	default:
   115  		if a.inProjection > 0 && vterrors.ErrState(err) == vterrors.NonUniqError {
   116  			a.projErr = err
   117  		} else {
   118  			a.err = err
   119  		}
   120  	}
   121  }
   122  
   123  func (a *analyzer) analyzeDown(cursor *sqlparser.Cursor) bool {
   124  	// If we have an error we keep on going down the tree without checking for anything else
   125  	// this way we can abort when we come back up.
   126  	if !a.shouldContinue() {
   127  		return true
   128  	}
   129  
   130  	if err := a.scoper.down(cursor); err != nil {
   131  		a.setError(err)
   132  		return true
   133  	}
   134  	if err := a.checkForInvalidConstructs(cursor); err != nil {
   135  		a.setError(err)
   136  		return true
   137  	}
   138  	if err := a.rewriter.down(cursor); err != nil {
   139  		a.setError(err)
   140  		return true
   141  	}
   142  	// log any warn in rewriting.
   143  	a.warning = a.rewriter.warning
   144  
   145  	a.enterProjection(cursor)
   146  	// this is the visitor going down the tree. Returning false here would just not visit the children
   147  	// to the current node, but that is not what we want if we have encountered an error.
   148  	// In order to abort the whole visitation, we have to return true here and then return false in the `analyzeUp` method
   149  	return true
   150  }
   151  
   152  func (a *analyzer) analyzeUp(cursor *sqlparser.Cursor) bool {
   153  	if !a.shouldContinue() {
   154  		return false
   155  	}
   156  
   157  	if err := a.binder.up(cursor); err != nil {
   158  		a.setError(err)
   159  		return true
   160  	}
   161  
   162  	if err := a.scoper.up(cursor); err != nil {
   163  		a.setError(err)
   164  		return false
   165  	}
   166  	if err := a.tables.up(cursor); err != nil {
   167  		a.setError(err)
   168  		return false
   169  	}
   170  	if err := a.typer.up(cursor); err != nil {
   171  		a.setError(err)
   172  		return false
   173  	}
   174  
   175  	a.leaveProjection(cursor)
   176  	return a.shouldContinue()
   177  }
   178  
   179  func containsStar(s sqlparser.SelectExprs) bool {
   180  	for _, expr := range s {
   181  		_, isStar := expr.(*sqlparser.StarExpr)
   182  		if isStar {
   183  			return true
   184  		}
   185  	}
   186  	return false
   187  }
   188  
   189  func checkUnionColumns(union *sqlparser.Union) error {
   190  	firstProj := sqlparser.GetFirstSelect(union).SelectExprs
   191  	if containsStar(firstProj) {
   192  		// if we still have *, we can't figure out if the query is invalid or not
   193  		// we'll fail it at run time instead
   194  		return nil
   195  	}
   196  	count := len(firstProj)
   197  
   198  	secondProj := sqlparser.GetFirstSelect(union.Right).SelectExprs
   199  	if containsStar(secondProj) {
   200  		return nil
   201  	}
   202  
   203  	if len(secondProj) != count {
   204  		return NewError(UnionColumnsDoNotMatch)
   205  	}
   206  
   207  	return nil
   208  }
   209  
   210  /*
   211  errors that happen when we are evaluating SELECT expressions are saved until we know
   212  if we can merge everything into a single route or not
   213  */
   214  func (a *analyzer) enterProjection(cursor *sqlparser.Cursor) {
   215  	_, ok := cursor.Node().(sqlparser.SelectExprs)
   216  	if ok && isParentSelect(cursor) {
   217  		a.inProjection++
   218  	}
   219  }
   220  
   221  func (a *analyzer) leaveProjection(cursor *sqlparser.Cursor) {
   222  	_, ok := cursor.Node().(sqlparser.SelectExprs)
   223  	if ok && isParentSelect(cursor) {
   224  		a.inProjection--
   225  	}
   226  }
   227  
   228  func isParentSelect(cursor *sqlparser.Cursor) bool {
   229  	_, isSelect := cursor.Parent().(*sqlparser.Select)
   230  	return isSelect
   231  }
   232  
   233  func isParentSelectStatement(cursor *sqlparser.Cursor) bool {
   234  	_, isSelect := cursor.Parent().(sqlparser.SelectStatement)
   235  	return isSelect
   236  }
   237  
   238  type originable interface {
   239  	tableSetFor(t *sqlparser.AliasedTableExpr) TableSet
   240  	depsForExpr(expr sqlparser.Expr) (direct, recursive TableSet, typ *Type)
   241  }
   242  
   243  func (a *analyzer) depsForExpr(expr sqlparser.Expr) (direct, recursive TableSet, typ *Type) {
   244  	recursive = a.binder.recursive.dependencies(expr)
   245  	direct = a.binder.direct.dependencies(expr)
   246  	qt, isFound := a.typer.exprTypes[expr]
   247  	if !isFound {
   248  		return
   249  	}
   250  	typ = &qt
   251  	return
   252  }
   253  
   254  func (a *analyzer) analyze(statement sqlparser.Statement) error {
   255  	_ = sqlparser.Rewrite(statement, a.analyzeDown, a.analyzeUp)
   256  	return a.err
   257  }
   258  
   259  func (a *analyzer) checkForInvalidConstructs(cursor *sqlparser.Cursor) error {
   260  	switch node := cursor.Node().(type) {
   261  	case *sqlparser.Update:
   262  		if len(node.TableExprs) != 1 {
   263  			return ShardedError{Inner: NewError(UnsupportedMultiTablesInUpdate)}
   264  		}
   265  		alias, isAlias := node.TableExprs[0].(*sqlparser.AliasedTableExpr)
   266  		if !isAlias {
   267  			return ShardedError{Inner: NewError(UnsupportedMultiTablesInUpdate)}
   268  		}
   269  		_, isDerived := alias.Expr.(*sqlparser.DerivedTable)
   270  		if isDerived {
   271  			return NewError(TableNotUpdatable, alias.As.String())
   272  		}
   273  	case *sqlparser.Select:
   274  		parent := cursor.Parent()
   275  		if _, isUnion := parent.(*sqlparser.Union); isUnion && node.SQLCalcFoundRows {
   276  			return NewError(UnionWithSQLCalcFoundRows)
   277  		}
   278  		if _, isRoot := parent.(*sqlparser.RootNode); !isRoot && node.SQLCalcFoundRows {
   279  			return NewError(SQLCalcFoundRowsUsage)
   280  		}
   281  		errMsg := "INTO"
   282  		nextVal := false
   283  		if len(node.SelectExprs) == 1 {
   284  			if _, isNextVal := node.SelectExprs[0].(*sqlparser.Nextval); isNextVal {
   285  				nextVal = true
   286  				errMsg = "NEXT"
   287  			}
   288  		}
   289  		if !nextVal && node.Into == nil {
   290  			return nil
   291  		}
   292  		if a.scoper.currentScope().parent != nil {
   293  			return NewError(CantUseOptionHere, errMsg)
   294  		}
   295  	case *sqlparser.Nextval:
   296  		currScope := a.scoper.currentScope()
   297  		if currScope.parent != nil {
   298  			return NewError(CantUseOptionHere, "Incorrect usage/placement of 'INTO'")
   299  		}
   300  		if len(currScope.tables) != 1 {
   301  			return NewError(NextWithMultipleTables)
   302  		}
   303  		vindexTbl := currScope.tables[0].GetVindexTable()
   304  		if vindexTbl == nil {
   305  			return NewError(MissingInVSchema)
   306  		}
   307  		if vindexTbl.Type != vindexes.TypeSequence {
   308  			return NewError(NotSequenceTable)
   309  		}
   310  	case *sqlparser.JoinTableExpr:
   311  		if node.Join == sqlparser.NaturalJoinType || node.Join == sqlparser.NaturalRightJoinType || node.Join == sqlparser.NaturalLeftJoinType {
   312  			return NewError(UnsupportedNaturalJoin, node.Join.ToString())
   313  		}
   314  	case *sqlparser.LockingFunc:
   315  		return NewError(LockOnlyWithDual, node)
   316  	case *sqlparser.Union:
   317  		err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
   318  			switch node := node.(type) {
   319  			case *sqlparser.ColName:
   320  				if !node.Qualifier.IsEmpty() {
   321  					return false, NewError(QualifiedOrderInUnion, node.Qualifier.Name)
   322  				}
   323  			case *sqlparser.Subquery:
   324  				return false, nil
   325  			}
   326  			return true, nil
   327  		}, node.OrderBy)
   328  		if err != nil {
   329  			return err
   330  		}
   331  		err = checkUnionColumns(node)
   332  		if err != nil {
   333  			return err
   334  		}
   335  	case *sqlparser.JSONTableExpr:
   336  		return NewError(JSONTables)
   337  	}
   338  
   339  	return nil
   340  }
   341  
   342  func (a *analyzer) shouldContinue() bool {
   343  	return a.err == nil
   344  }
   345  
   346  func (a *analyzer) tableSetFor(t *sqlparser.AliasedTableExpr) TableSet {
   347  	return a.tables.tableSetFor(t)
   348  }
   349  
   350  // ProjError is used to mark an error as something that should only be returned
   351  // if the planner fails to merge everything down to a single route
   352  type ProjError struct {
   353  	Inner error
   354  }
   355  
   356  func (p ProjError) Error() string {
   357  	return p.Inner.Error()
   358  }
   359  
   360  // ShardedError is used to mark an error as something that should only be returned
   361  // if the query is not unsharded
   362  type ShardedError struct {
   363  	Inner error
   364  }
   365  
   366  func (p ShardedError) Error() string {
   367  	return p.Inner.Error()
   368  }