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 }