/*
* 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);
}
}