github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/parser/yy_parser.go (about) 1 // Copyright 2015 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package parser 15 16 import ( 17 "regexp" 18 "strings" 19 20 "github.com/insionng/yougam/libraries/juju/errors" 21 "github.com/insionng/yougam/libraries/pingcap/tidb/ast" 22 "github.com/insionng/yougam/libraries/pingcap/tidb/mysql" 23 "github.com/insionng/yougam/libraries/pingcap/tidb/terror" 24 ) 25 26 // Error instances. 27 var ( 28 ErrSyntax = terror.ClassParser.New(CodeSyntaxErr, "syntax error") 29 ) 30 31 // Error codes. 32 const ( 33 CodeSyntaxErr terror.ErrCode = 1 34 ) 35 36 var ( 37 specCodePattern = regexp.MustCompile(`\/\*!(M?[0-9]{5,6})?([^*]|\*+[^*/])*\*+\/`) 38 specCodeStart = regexp.MustCompile(`^\/\*!(M?[0-9]{5,6} )?[ \t]*`) 39 specCodeEnd = regexp.MustCompile(`[ \t]*\*\/$`) 40 ) 41 42 func trimComment(txt string) string { 43 txt = specCodeStart.ReplaceAllString(txt, "") 44 return specCodeEnd.ReplaceAllString(txt, "") 45 } 46 47 // See: http://dev.mysql.com/doc/refman/5.7/en/comments.html 48 // Convert "/*!VersionNumber MySQL-specific-code */" to "MySQL-specific-code". 49 // TODO: Find a better way: 50 // 1. RegExpr is slow. 51 // 2. Handle nested comment. 52 func handleMySQLSpecificCode(sql string) string { 53 if strings.Index(sql, "/*!") == -1 { 54 // Fast way to check if text contains MySQL-specific code. 55 return sql 56 } 57 // SQL text contains MySQL-specific code. We should convert it to normal SQL text. 58 return specCodePattern.ReplaceAllStringFunc(sql, trimComment) 59 } 60 61 // Parse parses a query string to raw ast.StmtNode. 62 // If charset or collation is "", default charset and collation will be used. 63 func Parse(sql, charset, collation string) ([]ast.StmtNode, error) { 64 if charset == "" { 65 charset = mysql.DefaultCharset 66 } 67 if collation == "" { 68 collation = mysql.DefaultCollationName 69 } 70 sql = handleMySQLSpecificCode(sql) 71 l := NewLexer(sql) 72 l.SetCharsetInfo(charset, collation) 73 yyParse(l) 74 if len(l.Errors()) != 0 { 75 return nil, errors.Trace(l.Errors()[0]) 76 } 77 return l.Stmts(), nil 78 } 79 80 // ParseOneStmt parses a query and returns an ast.StmtNode. 81 // The query must have one statement, otherwise ErrSyntax is returned. 82 func ParseOneStmt(sql, charset, collation string) (ast.StmtNode, error) { 83 stmts, err := Parse(sql, charset, collation) 84 if err != nil { 85 return nil, errors.Trace(err) 86 } 87 if len(stmts) != 1 { 88 return nil, ErrSyntax 89 } 90 return stmts[0], nil 91 }