mirror of
https://github.com/alibaba/druid.git
synced 2023-03-04 19:17:16 +00:00
improved sql parser.
This commit is contained in:
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user