/* * Copyright 1999-2012 Alibaba Group. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * (created at 2011-6-17) */ package com.alibaba.cobar.parser.recognizer; import java.sql.SQLSyntaxErrorException; import java.util.HashMap; import java.util.Map; import com.alibaba.cobar.parser.ast.stmt.SQLStatement; import com.alibaba.cobar.parser.ast.stmt.ddl.DDLCreateIndexStatement; import com.alibaba.cobar.parser.ast.stmt.ddl.DDLStatement; import com.alibaba.cobar.parser.recognizer.mysql.MySQLToken; import com.alibaba.cobar.parser.recognizer.mysql.lexer.MySQLLexer; import com.alibaba.cobar.parser.recognizer.mysql.syntax.MySQLDALParser; import com.alibaba.cobar.parser.recognizer.mysql.syntax.MySQLDDLParser; import com.alibaba.cobar.parser.recognizer.mysql.syntax.MySQLDMLCallParser; import com.alibaba.cobar.parser.recognizer.mysql.syntax.MySQLDMLDeleteParser; import com.alibaba.cobar.parser.recognizer.mysql.syntax.MySQLDMLInsertParser; import com.alibaba.cobar.parser.recognizer.mysql.syntax.MySQLDMLReplaceParser; import com.alibaba.cobar.parser.recognizer.mysql.syntax.MySQLDMLSelectParser; import com.alibaba.cobar.parser.recognizer.mysql.syntax.MySQLDMLUpdateParser; import com.alibaba.cobar.parser.recognizer.mysql.syntax.MySQLExprParser; import com.alibaba.cobar.parser.recognizer.mysql.syntax.MySQLMTSParser; import com.alibaba.cobar.parser.recognizer.mysql.syntax.MySQLParser; /** * @author <a href="mailto:shuo.qius@alibaba-inc.com">QIU Shuo</a> */ public final class SQLParserDelegate { private static enum SpecialIdentifier { ROLLBACK, SAVEPOINT, TRUNCATE } private static final Map<String, SpecialIdentifier> specialIdentifiers = new HashMap<String, SpecialIdentifier>(); static { specialIdentifiers.put("TRUNCATE", SpecialIdentifier.TRUNCATE); specialIdentifiers.put("SAVEPOINT", SpecialIdentifier.SAVEPOINT); specialIdentifiers.put("ROLLBACK", SpecialIdentifier.ROLLBACK); } private static boolean isEOFedDDL(SQLStatement stmt) { if (stmt instanceof DDLStatement) { if (stmt instanceof DDLCreateIndexStatement) return false; } return true; } private static String buildErrorMsg(Exception e, MySQLLexer lexer, String sql) { StringBuilder sb = new StringBuilder( "You have an error in your SQL syntax; Error occurs around this fragment: "); final int ch = lexer.getCurrentIndex(); int from = ch - 16; if (from < 0) from = 0; int to = ch + 9; if (to >= sql.length()) to = sql.length() - 1; String fragment = sql.substring(from, to + 1); sb.append('{').append(fragment).append('}').append(". Error cause: " + e.getMessage()); return sb.toString(); } public static SQLStatement parse(String sql, MySQLLexer lexer, String charset) throws SQLSyntaxErrorException { try { SQLStatement stmt = null; boolean isEOF = true; MySQLExprParser exprParser = new MySQLExprParser(lexer, charset); stmtSwitch: switch (lexer.token()) { case KW_DESC: case KW_DESCRIBE: stmt = new MySQLDALParser(lexer, exprParser).desc(); break stmtSwitch; case KW_SELECT: case PUNC_LEFT_PAREN: stmt = new MySQLDMLSelectParser(lexer, exprParser).selectUnion(); break stmtSwitch; case KW_DELETE: stmt = new MySQLDMLDeleteParser(lexer, exprParser).delete(); break stmtSwitch; case KW_INSERT: stmt = new MySQLDMLInsertParser(lexer, exprParser).insert(); break stmtSwitch; case KW_REPLACE: stmt = new MySQLDMLReplaceParser(lexer, exprParser).replace(); break stmtSwitch; case KW_UPDATE: stmt = new MySQLDMLUpdateParser(lexer, exprParser).update(); break stmtSwitch; case KW_CALL: stmt = new MySQLDMLCallParser(lexer, exprParser).call(); break stmtSwitch; case KW_SET: stmt = new MySQLDALParser(lexer, exprParser).set(); break stmtSwitch; case KW_SHOW: stmt = new MySQLDALParser(lexer, exprParser).show(); break stmtSwitch; case KW_ALTER: case KW_CREATE: case KW_DROP: case KW_RENAME: stmt = new MySQLDDLParser(lexer, exprParser).ddlStmt(); isEOF = isEOFedDDL(stmt); break stmtSwitch; case KW_RELEASE: stmt = new MySQLMTSParser(lexer).release(); break stmtSwitch; case IDENTIFIER: SpecialIdentifier si = null; if ((si = specialIdentifiers.get(lexer.stringValueUppercase())) != null) { switch (si) { case TRUNCATE: stmt = new MySQLDDLParser(lexer, exprParser).truncate(); break stmtSwitch; case SAVEPOINT: stmt = new MySQLMTSParser(lexer).savepoint(); break stmtSwitch; case ROLLBACK: stmt = new MySQLMTSParser(lexer).rollback(); break stmtSwitch; } } default: throw new SQLSyntaxErrorException("sql is not a supported statement"); } if (isEOF) { while (lexer.token() == MySQLToken.PUNC_SEMICOLON) { lexer.nextToken(); } if (lexer.token() != MySQLToken.EOF) { throw new SQLSyntaxErrorException("SQL syntax error!"); } } return stmt; } catch (Exception e) { throw new SQLSyntaxErrorException(buildErrorMsg(e, lexer, sql), e); } } public static SQLStatement parse(String sql, String charset) throws SQLSyntaxErrorException { return parse(sql, new MySQLLexer(sql), charset); } public static SQLStatement parse(String sql) throws SQLSyntaxErrorException { return parse(sql, MySQLParser.DEFAULT_CHARSET); } }