github.com/team-ide/go-dialect@v1.9.20/vitess/sqlparser/analyzer.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 sqlparser
    18  
    19  // analyzer.go contains utility analysis functions.
    20  
    21  import (
    22  	"fmt"
    23  	"strings"
    24  	"unicode"
    25  )
    26  
    27  // StatementType encodes the type of a SQL statement
    28  type StatementType int
    29  
    30  // These constants are used to identify the SQL statement type.
    31  // Changing this list will require reviewing all calls to Preview.
    32  const (
    33  	StmtSelect StatementType = iota
    34  	StmtStream
    35  	StmtInsert
    36  	StmtReplace
    37  	StmtUpdate
    38  	StmtDelete
    39  	StmtDDL
    40  	StmtBegin
    41  	StmtCommit
    42  	StmtRollback
    43  	StmtSet
    44  	StmtShow
    45  	StmtUse
    46  	StmtOther
    47  	StmtUnknown
    48  	StmtComment
    49  	StmtPriv
    50  	StmtExplain
    51  	StmtSavepoint
    52  	StmtSRollback
    53  	StmtRelease
    54  	StmtVStream
    55  	StmtLockTables
    56  	StmtUnlockTables
    57  	StmtFlush
    58  	StmtCallProc
    59  	StmtRevert
    60  	StmtShowMigrationLogs
    61  )
    62  
    63  //ASTToStatementType returns a StatementType from an AST stmt
    64  func ASTToStatementType(stmt Statement) StatementType {
    65  	switch stmt.(type) {
    66  	case *Select, *Union:
    67  		return StmtSelect
    68  	case *Insert:
    69  		return StmtInsert
    70  	case *Update:
    71  		return StmtUpdate
    72  	case *Delete:
    73  		return StmtDelete
    74  	case *Set:
    75  		return StmtSet
    76  	case *Show:
    77  		return StmtShow
    78  	case DDLStatement, DBDDLStatement, *AlterVschema:
    79  		return StmtDDL
    80  	case *RevertMigration:
    81  		return StmtRevert
    82  	case *ShowMigrationLogs:
    83  		return StmtShowMigrationLogs
    84  	case *Use:
    85  		return StmtUse
    86  	case *OtherRead, *OtherAdmin, *Load:
    87  		return StmtOther
    88  	case Explain:
    89  		return StmtExplain
    90  	case *Begin:
    91  		return StmtBegin
    92  	case *Commit:
    93  		return StmtCommit
    94  	case *Rollback:
    95  		return StmtRollback
    96  	case *Savepoint:
    97  		return StmtSavepoint
    98  	case *SRollback:
    99  		return StmtSRollback
   100  	case *Release:
   101  		return StmtRelease
   102  	case *LockTables:
   103  		return StmtLockTables
   104  	case *UnlockTables:
   105  		return StmtUnlockTables
   106  	case *Flush:
   107  		return StmtFlush
   108  	case *CallProc:
   109  		return StmtCallProc
   110  	case *Stream:
   111  		return StmtStream
   112  	case *VStream:
   113  		return StmtVStream
   114  	default:
   115  		return StmtUnknown
   116  	}
   117  }
   118  
   119  //CanNormalize takes Statement and returns if the statement can be normalized.
   120  func CanNormalize(stmt Statement) bool {
   121  	switch stmt.(type) {
   122  	case *Select, *Union, *Insert, *Update, *Delete, *Set, *CallProc, *Stream: // TODO: we could merge this logic into ASTrewriter
   123  		return true
   124  	}
   125  	return false
   126  }
   127  
   128  // CachePlan takes Statement and returns true if the query plan should be cached
   129  func CachePlan(stmt Statement) bool {
   130  	var directives CommentDirectives
   131  	switch stmt := stmt.(type) {
   132  	case *Select:
   133  		directives = ExtractCommentDirectives(stmt.Comments)
   134  	case *Insert:
   135  		directives = ExtractCommentDirectives(stmt.Comments)
   136  	case *Update:
   137  		directives = ExtractCommentDirectives(stmt.Comments)
   138  	case *Delete:
   139  		directives = ExtractCommentDirectives(stmt.Comments)
   140  	case *Union, *Stream:
   141  		return true
   142  	default:
   143  		return false
   144  	}
   145  	return !directives.IsSet(DirectiveSkipQueryPlanCache)
   146  }
   147  
   148  //MustRewriteAST takes Statement and returns true if RewriteAST must run on it for correct execution irrespective of user flags.
   149  func MustRewriteAST(stmt Statement, hasSelectLimit bool) bool {
   150  	switch node := stmt.(type) {
   151  	case *Set:
   152  		return true
   153  	case *Show:
   154  		switch node.Internal.(type) {
   155  		case *ShowBasic:
   156  			return true
   157  		}
   158  		return false
   159  	case SelectStatement:
   160  		return hasSelectLimit
   161  	}
   162  	return false
   163  }
   164  
   165  // Preview analyzes the beginning of the query using a simpler and faster
   166  // textual comparison to identify the statement type.
   167  func Preview(sql string) StatementType {
   168  	trimmed := StripLeadingComments(sql)
   169  
   170  	if strings.Index(trimmed, "/*!") == 0 {
   171  		return StmtComment
   172  	}
   173  
   174  	isNotLetter := func(r rune) bool { return !unicode.IsLetter(r) }
   175  	firstWord := strings.TrimLeftFunc(trimmed, isNotLetter)
   176  
   177  	if end := strings.IndexFunc(firstWord, unicode.IsSpace); end != -1 {
   178  		firstWord = firstWord[:end]
   179  	}
   180  	// Comparison is done in order of priority.
   181  	loweredFirstWord := strings.ToLower(firstWord)
   182  	switch loweredFirstWord {
   183  	case "select":
   184  		return StmtSelect
   185  	case "stream":
   186  		return StmtStream
   187  	case "vstream":
   188  		return StmtVStream
   189  	case "revert":
   190  		return StmtRevert
   191  	case "insert":
   192  		return StmtInsert
   193  	case "replace":
   194  		return StmtReplace
   195  	case "update":
   196  		return StmtUpdate
   197  	case "delete":
   198  		return StmtDelete
   199  	case "savepoint":
   200  		return StmtSavepoint
   201  	case "lock":
   202  		return StmtLockTables
   203  	case "unlock":
   204  		return StmtUnlockTables
   205  	}
   206  	// For the following statements it is not sufficient to rely
   207  	// on loweredFirstWord. This is because they are not statements
   208  	// in the grammar and we are relying on Preview to parse them.
   209  	// For instance, we don't want: "BEGIN JUNK" to be parsed
   210  	// as StmtBegin.
   211  	trimmedNoComments, _ := SplitMarginComments(trimmed)
   212  	switch strings.ToLower(trimmedNoComments) {
   213  	case "begin", "start transaction":
   214  		return StmtBegin
   215  	case "commit":
   216  		return StmtCommit
   217  	case "rollback":
   218  		return StmtRollback
   219  	}
   220  	switch loweredFirstWord {
   221  	case "create", "alter", "rename", "drop", "truncate":
   222  		return StmtDDL
   223  	case "flush":
   224  		return StmtFlush
   225  	case "set":
   226  		return StmtSet
   227  	case "show":
   228  		return StmtShow
   229  	case "use":
   230  		return StmtUse
   231  	case "describe", "desc", "explain":
   232  		return StmtExplain
   233  	case "analyze", "repair", "optimize":
   234  		return StmtOther
   235  	case "grant", "revoke":
   236  		return StmtPriv
   237  	case "release":
   238  		return StmtRelease
   239  	case "rollback":
   240  		return StmtSRollback
   241  	}
   242  	return StmtUnknown
   243  }
   244  
   245  func (s StatementType) String() string {
   246  	switch s {
   247  	case StmtSelect:
   248  		return "SELECT"
   249  	case StmtStream:
   250  		return "STREAM"
   251  	case StmtVStream:
   252  		return "VSTREAM"
   253  	case StmtRevert:
   254  		return "REVERT"
   255  	case StmtInsert:
   256  		return "INSERT"
   257  	case StmtReplace:
   258  		return "REPLACE"
   259  	case StmtUpdate:
   260  		return "UPDATE"
   261  	case StmtDelete:
   262  		return "DELETE"
   263  	case StmtDDL:
   264  		return "DDL"
   265  	case StmtBegin:
   266  		return "BEGIN"
   267  	case StmtCommit:
   268  		return "COMMIT"
   269  	case StmtRollback:
   270  		return "ROLLBACK"
   271  	case StmtSet:
   272  		return "SET"
   273  	case StmtShow:
   274  		return "SHOW"
   275  	case StmtUse:
   276  		return "USE"
   277  	case StmtOther:
   278  		return "OTHER"
   279  	case StmtPriv:
   280  		return "PRIV"
   281  	case StmtExplain:
   282  		return "EXPLAIN"
   283  	case StmtSavepoint:
   284  		return "SAVEPOINT"
   285  	case StmtSRollback:
   286  		return "SAVEPOINT_ROLLBACK"
   287  	case StmtRelease:
   288  		return "RELEASE"
   289  	case StmtLockTables:
   290  		return "LOCK_TABLES"
   291  	case StmtUnlockTables:
   292  		return "UNLOCK_TABLES"
   293  	case StmtFlush:
   294  		return "FLUSH"
   295  	case StmtCallProc:
   296  		return "CALL_PROC"
   297  	default:
   298  		return "UNKNOWN"
   299  	}
   300  }
   301  
   302  // IsDML returns true if the query is an INSERT, UPDATE or DELETE statement.
   303  func IsDML(sql string) bool {
   304  	switch Preview(sql) {
   305  	case StmtInsert, StmtReplace, StmtUpdate, StmtDelete:
   306  		return true
   307  	}
   308  	return false
   309  }
   310  
   311  //IsDMLStatement returns true if the query is an INSERT, UPDATE or DELETE statement.
   312  func IsDMLStatement(stmt Statement) bool {
   313  	switch stmt.(type) {
   314  	case *Insert, *Update, *Delete:
   315  		return true
   316  	}
   317  
   318  	return false
   319  }
   320  
   321  // SplitAndExpression breaks up the Expr into AND-separated conditions
   322  // and appends them to filters. Outer parenthesis are removed. Precedence
   323  // should be taken into account if expressions are recombined.
   324  func SplitAndExpression(filters []Expr, node Expr) []Expr {
   325  	if node == nil {
   326  		return filters
   327  	}
   328  	switch node := node.(type) {
   329  	case *AndExpr:
   330  		filters = SplitAndExpression(filters, node.Left)
   331  		return SplitAndExpression(filters, node.Right)
   332  	}
   333  	return append(filters, node)
   334  }
   335  
   336  // AndExpressions ands together two or more expressions, minimising the expr when possible
   337  func AndExpressions(exprs ...Expr) Expr {
   338  	switch len(exprs) {
   339  	case 0:
   340  		return nil
   341  	case 1:
   342  		return exprs[0]
   343  	default:
   344  		result := (Expr)(nil)
   345  	outer:
   346  		// we'll loop and remove any duplicates
   347  		for i, expr := range exprs {
   348  			if expr == nil {
   349  				continue
   350  			}
   351  			if result == nil {
   352  				result = expr
   353  				continue outer
   354  			}
   355  
   356  			for j := 0; j < i; j++ {
   357  				if EqualsExpr(expr, exprs[j]) {
   358  					continue outer
   359  				}
   360  			}
   361  			result = &AndExpr{Left: result, Right: expr}
   362  		}
   363  		return result
   364  	}
   365  }
   366  
   367  // TableFromStatement returns the qualified table name for the query.
   368  // This works only for select statements.
   369  func TableFromStatement(sql string) (TableName, error) {
   370  	stmt, err := Parse(sql)
   371  	if err != nil {
   372  		return TableName{}, err
   373  	}
   374  	sel, ok := stmt.(*Select)
   375  	if !ok {
   376  		return TableName{}, fmt.Errorf("unrecognized statement: %s", sql)
   377  	}
   378  	if len(sel.From) != 1 {
   379  		return TableName{}, fmt.Errorf("table expression is complex")
   380  	}
   381  	aliased, ok := sel.From[0].(*AliasedTableExpr)
   382  	if !ok {
   383  		return TableName{}, fmt.Errorf("table expression is complex")
   384  	}
   385  	tableName, ok := aliased.Expr.(TableName)
   386  	if !ok {
   387  		return TableName{}, fmt.Errorf("table expression is complex")
   388  	}
   389  	return tableName, nil
   390  }
   391  
   392  // GetTableName returns the table name from the SimpleTableExpr
   393  // only if it's a simple expression. Otherwise, it returns "".
   394  func GetTableName(node SimpleTableExpr) TableIdent {
   395  	if n, ok := node.(TableName); ok && n.Qualifier.IsEmpty() {
   396  		return n.Name
   397  	}
   398  	// sub-select or '.' expression
   399  	return NewTableIdent("")
   400  }
   401  
   402  // IsColName returns true if the Expr is a *ColName.
   403  func IsColName(node Expr) bool {
   404  	_, ok := node.(*ColName)
   405  	return ok
   406  }
   407  
   408  // IsValue returns true if the Expr is a string, integral or value arg.
   409  // NULL is not considered to be a value.
   410  func IsValue(node Expr) bool {
   411  	switch v := node.(type) {
   412  	case Argument:
   413  		return true
   414  	case *Literal:
   415  		switch v.Type {
   416  		case StrVal, HexVal, IntVal:
   417  			return true
   418  		}
   419  	}
   420  	return false
   421  }
   422  
   423  // IsNull returns true if the Expr is SQL NULL
   424  func IsNull(node Expr) bool {
   425  	switch node.(type) {
   426  	case *NullVal:
   427  		return true
   428  	}
   429  	return false
   430  }
   431  
   432  // IsSimpleTuple returns true if the Expr is a ValTuple that
   433  // contains simple values or if it's a list arg.
   434  func IsSimpleTuple(node Expr) bool {
   435  	switch vals := node.(type) {
   436  	case ValTuple:
   437  		for _, n := range vals {
   438  			if !IsValue(n) {
   439  				return false
   440  			}
   441  		}
   442  		return true
   443  	case ListArg:
   444  		return true
   445  	}
   446  	// It's a subquery
   447  	return false
   448  }
   449  
   450  //IsLockingFunc returns true for all functions that are used to work with mysql advisory locks
   451  func IsLockingFunc(node Expr) bool {
   452  	switch p := node.(type) {
   453  	case *FuncExpr:
   454  		_, found := lockingFunctions[p.Name.Lowered()]
   455  		return found
   456  	}
   457  	return false
   458  }
   459  
   460  var lockingFunctions = map[string]interface{}{
   461  	"get_lock":          nil,
   462  	"is_free_lock":      nil,
   463  	"is_used_lock":      nil,
   464  	"release_all_locks": nil,
   465  	"release_lock":      nil,
   466  }