improved sql parser.

This commit is contained in:
wenshao
2017-09-17 17:11:26 +08:00
parent b54a987cc8
commit 3c98a1eee5
23 changed files with 955 additions and 31 deletions
@@ -446,7 +446,7 @@ public class SQLTransformUtils {
int precision, scale = 0;
if (arg0 instanceof SQLAllColumnExpr) {
precision = 19;
scale = 0;
scale = -1;
} else {
precision = ((SQLIntegerExpr) arg0).getNumber().intValue();
}
@@ -476,6 +476,8 @@ public class SQLTransformUtils {
} else {
dataType = new SQLDataTypeImpl(SQLDataType.Constants.DECIMAL, precision);
}
} else if (scale == -1) {
dataType = new SQLDataTypeImpl(SQLDataType.Constants.DOUBLE_PRECISION);
} else {
dataType = new SQLDataTypeImpl(SQLDataType.Constants.DECIMAL, precision, scale);
}
@@ -622,4 +624,13 @@ public class SQLTransformUtils {
return dataType;
}
public static SQLExpr transformOracleToPostgresql(SQLMethodInvokeExpr x) {
long nameHashCode64 = x.methodNameHashCode64();
if (nameHashCode64 == FnvHash.Constants.SYS_GUID) {
SQLMethodInvokeExpr uuid_generate_v4 = new SQLMethodInvokeExpr("uuid_generate_v4");
return uuid_generate_v4;
}
return x;
}
}
@@ -41,6 +41,7 @@ public interface SQLDataType extends SQLObject {
String CHAR = "CHAR";
String NCHAR = "NCHAR";
String VARCHAR = "VARCHAR";
String DATE = "DATE";
String TIMESTAMP = "TIMESTAMP";
String XML = "XML";
@@ -26,11 +26,12 @@ import com.alibaba.druid.sql.ast.SQLExprImpl;
import com.alibaba.druid.sql.ast.SQLReplaceable;
import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor;
import com.alibaba.druid.sql.visitor.SQLASTVisitor;
import com.alibaba.druid.util.FnvHash;
public class SQLMethodInvokeExpr extends SQLExprImpl implements SQLReplaceable, Serializable {
private static final long serialVersionUID = 1L;
private String methodName;
private String name;
private SQLExpr owner;
private final List<SQLExpr> parameters = new ArrayList<SQLExpr>();
@@ -40,34 +41,50 @@ public class SQLMethodInvokeExpr extends SQLExprImpl implements SQLReplaceable,
private String trimOption;
private long nameHashCode64;
public SQLMethodInvokeExpr(){
}
public SQLMethodInvokeExpr(String methodName){
this.methodName = methodName;
this.name = methodName;
}
public SQLMethodInvokeExpr(String methodName, long nameHashCode64){
this.name = methodName;
this.nameHashCode64 = nameHashCode64;
}
public SQLMethodInvokeExpr(String methodName, SQLExpr owner){
this.methodName = methodName;
this.name = methodName;
setOwner(owner);
}
public SQLMethodInvokeExpr(String methodName, SQLExpr owner, SQLExpr... params){
this.methodName = methodName;
this.name = methodName;
setOwner(owner);
for (SQLExpr param : params) {
this.addParameter(param);
}
}
public long methodNameHashCode64() {
if (nameHashCode64 == 0
&& name != null) {
nameHashCode64 = FnvHash.hashCode64(name);
}
return nameHashCode64;
}
public String getMethodName() {
return this.methodName;
return this.name;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
this.name = methodName;
this.nameHashCode64 = 0L;
}
public SQLExpr getOwner() {
@@ -106,7 +123,7 @@ public class SQLMethodInvokeExpr extends SQLExprImpl implements SQLReplaceable,
buf.append(".");
}
buf.append(this.methodName);
buf.append(this.name);
buf.append("(");
for (int i = 0, size = this.parameters.size(); i < size; ++i) {
if (i != 0) {
@@ -144,7 +161,7 @@ public class SQLMethodInvokeExpr extends SQLExprImpl implements SQLReplaceable,
SQLMethodInvokeExpr that = (SQLMethodInvokeExpr) o;
if (methodName != null ? !methodName.equals(that.methodName) : that.methodName != null) return false;
if (name != null ? !name.equals(that.name) : that.name != null) return false;
if (owner != null ? !owner.equals(that.owner) : that.owner != null) return false;
if (parameters != null ? !parameters.equals(that.parameters) : that.parameters != null) return false;
return from != null ? from.equals(that.from) : that.from == null;
@@ -153,7 +170,7 @@ public class SQLMethodInvokeExpr extends SQLExprImpl implements SQLReplaceable,
@Override
public int hashCode() {
int result = methodName != null ? methodName.hashCode() : 0;
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (owner != null ? owner.hashCode() : 0);
result = 31 * result + (parameters != null ? parameters.hashCode() : 0);
result = 31 * result + (from != null ? from.hashCode() : 0);
@@ -163,7 +180,7 @@ public class SQLMethodInvokeExpr extends SQLExprImpl implements SQLReplaceable,
public SQLMethodInvokeExpr clone() {
SQLMethodInvokeExpr x = new SQLMethodInvokeExpr();
x.methodName = methodName;
x.name = name;
if (owner != null) {
x.setOwner(owner.clone());
@@ -204,7 +221,7 @@ public class SQLMethodInvokeExpr extends SQLExprImpl implements SQLReplaceable,
return false;
}
if (!SQLUtils.nameEquals(function, methodName)) {
if (!SQLUtils.nameEquals(function, name)) {
return false;
}
@@ -224,19 +241,19 @@ public class SQLMethodInvokeExpr extends SQLExprImpl implements SQLReplaceable,
}
public SQLDataType computeDataType() {
if (SQLUtils.nameEquals("to_date", methodName)
|| SQLUtils.nameEquals("add_months", methodName)) {
if (SQLUtils.nameEquals("to_date", name)
|| SQLUtils.nameEquals("add_months", name)) {
return SQLDateExpr.DEFAULT_DATA_TYPE;
}
if (parameters.size() == 1) {
if (SQLUtils.nameEquals("trunc", methodName)) {
if (SQLUtils.nameEquals("trunc", name)) {
return parameters.get(0).computeDataType();
}
} else if (parameters.size() == 2) {
SQLExpr param0 = parameters.get(0);
SQLExpr param1 = parameters.get(1);
if (SQLUtils.nameEquals("nvl", methodName) || SQLUtils.nameEquals("ifnull", methodName)) {
if (SQLUtils.nameEquals("nvl", name) || SQLUtils.nameEquals("ifnull", name)) {
SQLDataType dataType = param0.computeDataType();
if (dataType != null) {
return dataType;
@@ -24,7 +24,7 @@ import java.io.IOException;
import java.math.BigDecimal;
public class SQLNumberExpr extends SQLNumericLiteralExpr implements SQLValuableExpr {
private static SQLDataType defaultDataType = new SQLDataTypeImpl("number");
public final static SQLDataType defaultDataType = new SQLDataTypeImpl("number");
private Number number;
@@ -26,7 +26,7 @@ import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr;
import com.alibaba.druid.sql.visitor.SQLASTVisitor;
import com.alibaba.druid.util.JdbcConstants;
public class SQLColumnDefinition extends SQLObjectImpl implements SQLTableElement, SQLObjectWithDataType {
public class SQLColumnDefinition extends SQLObjectImpl implements SQLTableElement, SQLObjectWithDataType, SQLReplaceable {
protected String dbType;
protected SQLName name;
@@ -250,6 +250,21 @@ public class SQLColumnDefinition extends SQLObjectImpl implements SQLTableElemen
this.storage = storage;
}
@Override
public boolean replace(SQLExpr expr, SQLExpr target) {
if (defaultExpr == expr) {
setDefaultExpr(target);
return true;
}
if (name == expr) {
setName((SQLName) target);
return true;
}
return false;
}
public static class Identity extends SQLObjectImpl {
private Integer seed;
@@ -124,6 +124,12 @@ public class MySqlExprParser extends SQLExprParser {
lexer.reset(6, ' ', Token.UPDATE);
return;
}
if (c0 == '/' && c1 == '*' && isEnabled(SQLParserFeature.OptimizedForParameterized)) {
MySqlLexer mySqlLexer = (MySqlLexer) lexer;
mySqlLexer.skipFirstHintsOrMultiCommentAndNextToken();
return;
}
}
this.lexer.nextToken();
@@ -401,6 +401,89 @@ public class MySqlLexer extends Lexer {
scanString2();
}
public void skipFirstHintsOrMultiCommentAndNextToken() {
int starIndex = pos + 2;
for (;;) {
starIndex = text.indexOf('*', starIndex);
if (starIndex == -1 || starIndex == text.length() - 1) {
this.token = Token.ERROR;
return;
}
int slashIndex = starIndex + 1;
if (charAt(slashIndex) == '/') {
pos = slashIndex + 1;
ch = text.charAt(pos);
if (pos < text.length() - 6) {
int pos_6 = pos + 6;
char c0 = ch;
char c1 = text.charAt(pos + 1);
char c2 = text.charAt(pos + 2);
char c3 = text.charAt(pos + 3);
char c4 = text.charAt(pos + 4);
char c5 = text.charAt(pos + 5);
char c6 = text.charAt(pos_6);
if (c0 == 's' && c1 == 'e' && c2 == 'l' && c3 == 'e' && c4 == 'c' && c5 == 't' && c6 == ' ') {
this.comments = null;
reset(pos_6, ' ', Token.SELECT);
return;
}
if (c0 == 'i' && c1 == 'n' && c2 == 's' && c3 == 'e' && c4 == 'r' && c5 == 't' && c6 == ' ') {
this.comments = null;
reset(pos_6, ' ', Token.INSERT);
return;
}
if (c0 == 'u' && c1 == 'p' && c2 == 'd' && c3 == 'a' && c4 == 't' && c5 == 'e' && c6 == ' ') {
this.comments = null;
reset(pos_6, ' ', Token.UPDATE);
return;
}
if (c0 == 'd' && c1 == 'e' && c2 == 'l' && c3 == 'e' && c4 == 't' && c5 == 'e' && c6 == ' ') {
this.comments = null;
reset(pos_6, ' ', Token.DELETE);
return;
}
if (c0 == 'S' && c1 == 'E' && c2 == 'L' && c3 == 'E' && c4 == 'C' && c5 == 'T' && c6 == ' ') {
this.comments = null;
reset(pos_6, ' ', Token.SELECT);
return;
}
if (c0 == 'I' && c1 == 'N' && c2 == 'S' && c3 == 'E' && c4 == 'R' && c5 == 'T' && c6 == ' ') {
this.comments = null;
reset(pos_6, ' ', Token.INSERT);
return;
}
if (c0 == 'U' && c1 == 'P' && c2 == 'D' && c3 == 'A' && c4 == 'T' && c5 == 'E' && c6 == ' ') {
this.comments = null;
reset(pos_6, ' ', Token.UPDATE);
return;
}
if (c0 == 'D' && c1 == 'E' && c2 == 'L' && c3 == 'E' && c4 == 'T' && c5 == 'E' && c6 == ' ') {
this.comments = null;
reset(pos_6, ' ', Token.DELETE);
return;
}
nextToken();
return;
} else {
nextToken();
return;
}
}
starIndex++;
}
}
public void scanComment() {
Token lastToken = this.token;
@@ -60,7 +60,7 @@ public class Lexer {
protected long hash;
protected int commentCount = 0;
protected List<String> comments = new ArrayList<String>(2);
protected List<String> comments = null;
protected boolean skipComment = true;
private SavePoint savePoint = null;
@@ -913,7 +913,7 @@ public class SQLExprParser extends SQLParser {
SQLExpr owner = null;
String trimOption = null;
long hash_lower;
long hash_lower = 0L;
if (expr instanceof SQLIdentifierExpr) {
SQLIdentifierExpr identifierExpr = (SQLIdentifierExpr) expr;
methodName = identifierExpr.getName();
@@ -964,7 +964,7 @@ public class SQLExprParser extends SQLParser {
return aggregateExpr;
}
methodInvokeExpr = new SQLMethodInvokeExpr(methodName);
methodInvokeExpr = new SQLMethodInvokeExpr(methodName, hash_lower);
if (owner != null) {
methodInvokeExpr.setOwner(owner);
}
@@ -565,10 +565,16 @@ public class SQLSelectParser extends SQLParser {
protected SQLTableSource parseTableSourceRest(SQLTableSource tableSource) {
if ((tableSource.getAlias() == null) || (tableSource.getAlias().length() == 0)) {
if (lexer.token != Token.LEFT && lexer.token != Token.RIGHT && lexer.token != Token.FULL
&& (!lexer.identifierEquals(FnvHash.Constants.STRAIGHT_JOIN))
&& (!lexer.identifierEquals(FnvHash.Constants.CROSS))
&& lexer.token != Token.OUTER) {
Token token = lexer.token;
long hash;
if (token != Token.LEFT
&& token != Token.RIGHT
&& token != Token.FULL
&& token != Token.OUTER
&& !(token == Token.IDENTIFIER
&& ((hash = lexer.hash_lower()) == FnvHash.Constants.STRAIGHT_JOIN
|| hash == FnvHash.Constants.CROSS)))
{
String alias = tableAlias();
if (alias != null) {
tableSource.setAlias(alias);
@@ -15,10 +15,14 @@
*/
package com.alibaba.druid.sql.parser;
import java.nio.charset.Charset;
/**
* @author wenshao[szujobs@hotmail.com]
*/
public class SymbolTable {
private static final Charset UTF8 = Charset.forName("UTF-8");
public static SymbolTable global = new SymbolTable(8192);
private final Entry[] entries;
@@ -29,15 +33,12 @@ public class SymbolTable {
this.entries = new Entry[tableSize];
}
public String addSymbol(String buffer, int offset, int len, long hash) {
final int bucket = ((int) hash) & indexMask;
Entry entry = entries[bucket];
if (entry != null) {
if (hash == entry.hash //
&& len == entry.len) {
if (hash == entry.hash) {
return entry.value;
}
@@ -52,12 +53,64 @@ public class SymbolTable {
return str;
}
public String addSymbol(byte[] buffer, int offset, int len, long hash) {
final int bucket = ((int) hash) & indexMask;
Entry entry = entries[bucket];
if (entry != null) {
if (hash == entry.hash) {
return entry.value;
}
String str = subString(buffer, offset, len);
return str;
}
String str = subString(buffer, offset, len);
entry = new Entry(hash, len, str);
entries[bucket] = entry;
return str;
}
public String addSymbol(String symbol, long hash) {
final int bucket = ((int) hash) & indexMask;
Entry entry = entries[bucket];
if (entry != null) {
if (hash == entry.hash) {
return entry.value;
}
return symbol;
}
entry = new Entry(hash, symbol.length(), symbol);
entries[bucket] = entry;
return symbol;
}
public String findSymbol(long hash) {
final int bucket = ((int) hash) & indexMask;
Entry entry = entries[bucket];
if (entry != null && entry.hash == hash) {
return entry.value;
}
return null;
}
private static String subString(String src, int offset, int len) {
char[] chars = new char[len];
src.getChars(offset, offset + len, chars, 0);
return new String(chars);
}
private static String subString(byte[] bytes, int from, int len) {
byte[] strBytes = new byte[len];
System.arraycopy(bytes, from, strBytes, 0, len);
return new String(strBytes, UTF8);
}
private static class Entry {
public final long hash;
public final int len;
@@ -65,7 +65,7 @@ public class ParameterizedOutputVisitorUtils {
}
if (visitor.getReplaceCount() == 0
&& parser.getLexer().getCommentCount() == 0) {
&& parser.getLexer().getCommentCount() == 0 && sql.charAt(0) != '/') {
return sql;
}
@@ -55,6 +55,25 @@ public final class FnvHash {
return hash;
}
public static long fnv1a_64(byte[] input, int offset, int end) {
if (input == null) {
return 0;
}
if (input.length < end) {
end = input.length;
}
long hash = BASIC;
for (int i = offset; i < end; ++i) {
byte c = input[i];
hash ^= c;
hash *= PRIME;
}
return hash;
}
public static long fnv1a_64(char[] chars) {
if (chars == null) {
return 0;
@@ -468,5 +487,13 @@ public final class FnvHash {
long SOURCE = fnv1a_64_lower("SOURCE");
long STAR = fnv1a_64_lower("*");
long TO_CHAR = fnv1a_64_lower("TO_CHAR");
long SYS_GUID = fnv1a_64_lower("SYS_GUID");
long STATISTICS = fnv1a_64_lower("STATISTICS");
long TRANSACTION = fnv1a_64_lower("TRANSACTION");
long OFF = fnv1a_64_lower("OFF");
long IDENTITY_INSERT = fnv1a_64_lower("IDENTITY_INSERT");
}
}