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:
@@ -325,6 +325,40 @@ public class Lexer {
|
||||
nextToken();
|
||||
}
|
||||
|
||||
public final void nextTokenEq() {
|
||||
if (ch == ' ') {
|
||||
scanChar();
|
||||
}
|
||||
|
||||
if (ch == '=') {
|
||||
scanChar();
|
||||
token = EQ;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ch == '.') {
|
||||
scanChar();
|
||||
token = DOT;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ch == 'a' || ch == 'A') {
|
||||
char ch_next = charAt(pos + 1);
|
||||
if (ch_next == 's' || ch_next == 'S') {
|
||||
char ch_next_2 = charAt(pos + 2);
|
||||
if (ch_next_2 == ' ') {
|
||||
pos += 2;
|
||||
ch = ' ';
|
||||
token = Token.AS;
|
||||
stringVal = "AS";
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nextToken();
|
||||
}
|
||||
|
||||
public final void nextTokenLParen() {
|
||||
if (ch == ' ') {
|
||||
scanChar();
|
||||
@@ -362,7 +396,30 @@ public class Lexer {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isFirstIdentifierChar(ch) && ch != 'N') {
|
||||
if (ch == 'n' || ch == 'N') {
|
||||
char c1 = 0, c2, c3, c4;
|
||||
if (pos + 4 < text.length()
|
||||
&& ((c1 = text.charAt(pos + 1)) == 'u' || c1 == 'U')
|
||||
&& ((c2 = text.charAt(pos + 2)) == 'l' || c2 == 'L')
|
||||
&& ((c3 = text.charAt(pos + 3)) == 'l' || c3 == 'L')
|
||||
&& (isWhitespace(c4 = text.charAt(pos + 4)) || c4 == ',' || c4 == ')')) {
|
||||
pos += 4;
|
||||
ch = c4;
|
||||
token = Token.NULL;
|
||||
stringVal = "NULL";
|
||||
return;
|
||||
}
|
||||
|
||||
if (c1 == '\'') {
|
||||
++pos;
|
||||
ch = '\'';
|
||||
scanString();
|
||||
token = Token.LITERAL_NCHARS;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (isFirstIdentifierChar(ch)) {
|
||||
scanIdentifier();
|
||||
return;
|
||||
}
|
||||
@@ -608,7 +665,7 @@ public class Lexer {
|
||||
case '`':
|
||||
throw new ParserException("TODO. " + info()); // TODO
|
||||
case '@':
|
||||
scanVariable();
|
||||
scanVariable_at();
|
||||
return;
|
||||
case '-':
|
||||
if (charAt(pos +1) == '-') {
|
||||
@@ -736,6 +793,9 @@ public class Lexer {
|
||||
if (ch == '=') {
|
||||
scanChar();
|
||||
token = Token.EQEQ;
|
||||
} else if (ch == '>') {
|
||||
scanChar();
|
||||
token = Token.EQGT;
|
||||
} else {
|
||||
token = Token.EQ;
|
||||
}
|
||||
@@ -781,6 +841,9 @@ public class Lexer {
|
||||
break;
|
||||
case '!':
|
||||
scanChar();
|
||||
while (isWhitespace(ch)) {
|
||||
scanChar();
|
||||
}
|
||||
if (ch == '=') {
|
||||
scanChar();
|
||||
token = Token.BANGEQ;
|
||||
@@ -1145,7 +1208,7 @@ public class Lexer {
|
||||
}
|
||||
|
||||
public void scanVariable() {
|
||||
if (ch != '@' && ch != ':' && ch != '#' && ch != '$') {
|
||||
if (ch != ':' && ch != '#' && ch != '$') {
|
||||
throw new ParserException("illegal variable. " + info());
|
||||
}
|
||||
|
||||
@@ -1154,17 +1217,7 @@ public class Lexer {
|
||||
char ch;
|
||||
|
||||
final char c1 = charAt(pos + 1);
|
||||
if (c1 == '@') {
|
||||
if (JdbcConstants.POSTGRESQL.equalsIgnoreCase(dbType)) {
|
||||
pos += 2;
|
||||
token = Token.MONKEYS_AT_AT;
|
||||
this.ch = charAt(++pos);
|
||||
return;
|
||||
}
|
||||
ch = charAt(++pos);
|
||||
|
||||
bufPos++;
|
||||
} else if (c1 == '>' && JdbcConstants.POSTGRESQL.equalsIgnoreCase(dbType)) {
|
||||
if (c1 == '>' && JdbcConstants.POSTGRESQL.equalsIgnoreCase(dbType)) {
|
||||
pos += 2;
|
||||
token = Token.MONKEYS_AT_GT;
|
||||
this.ch = charAt(++pos);
|
||||
@@ -1214,6 +1267,38 @@ public class Lexer {
|
||||
token = Token.VARIANT;
|
||||
}
|
||||
|
||||
protected void scanVariable_at() {
|
||||
if (ch != '@') {
|
||||
throw new ParserException("illegal variable. " + info());
|
||||
}
|
||||
|
||||
mark = pos;
|
||||
bufPos = 1;
|
||||
char ch;
|
||||
|
||||
final char c1 = charAt(pos + 1);
|
||||
if (c1 == '@') {
|
||||
++pos;
|
||||
bufPos++;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
ch = charAt(++pos);
|
||||
|
||||
if (!isIdentifierChar(ch)) {
|
||||
break;
|
||||
}
|
||||
|
||||
bufPos++;
|
||||
continue;
|
||||
}
|
||||
|
||||
this.ch = charAt(pos);
|
||||
|
||||
stringVal = addSymbol();
|
||||
token = Token.VARIANT;
|
||||
}
|
||||
|
||||
public void scanComment() {
|
||||
if (!allowComment) {
|
||||
throw new NotAllowCommentException();
|
||||
@@ -1641,12 +1726,12 @@ public class Lexer {
|
||||
}
|
||||
multmin = negative ? MULTMIN_RADIX_TEN : N_MULTMAX_RADIX_TEN;
|
||||
if (i < max) {
|
||||
digit = digits[charAt(i++)];
|
||||
digit = charAt(i++) - '0';
|
||||
result = -digit;
|
||||
}
|
||||
while (i < max) {
|
||||
// Accumulating negatively avoids surprises near MAX_VALUE
|
||||
digit = digits[charAt(i++)];
|
||||
digit = charAt(i++) - '0';
|
||||
if (result < multmin) {
|
||||
return new BigInteger(numberString());
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ import com.alibaba.druid.sql.ast.statement.SQLSelectItem;
|
||||
import com.alibaba.druid.sql.ast.statement.SQLSelectOrderByItem;
|
||||
import com.alibaba.druid.sql.ast.statement.SQLUnique;
|
||||
import com.alibaba.druid.sql.ast.statement.SQLUpdateSetItem;
|
||||
import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleArgumentExpr;
|
||||
import com.alibaba.druid.util.FnvHash;
|
||||
import com.alibaba.druid.util.JdbcConstants;
|
||||
|
||||
@@ -1462,12 +1463,38 @@ public class SQLExprParser extends SQLParser {
|
||||
accept(Token.RPAREN);
|
||||
item.setColumn(list);
|
||||
} else {
|
||||
item.setColumn(this.primary());
|
||||
String identName;
|
||||
long hash;
|
||||
|
||||
Token token = lexer.token();
|
||||
if (token == Token.IDENTIFIER) {
|
||||
identName = lexer.stringVal();
|
||||
hash = lexer.hash_lower();
|
||||
} else if (token == Token.LITERAL_CHARS) {
|
||||
identName = '\'' + lexer.stringVal() + '\'';
|
||||
hash = 0;
|
||||
} else {
|
||||
identName = lexer.stringVal();
|
||||
hash = 0;
|
||||
}
|
||||
lexer.nextTokenEq();
|
||||
SQLExpr expr = new SQLIdentifierExpr(identName, hash);
|
||||
while (lexer.token() == Token.DOT) {
|
||||
lexer.nextToken();
|
||||
String propertyName = lexer.stringVal();
|
||||
lexer.nextTokenEq();
|
||||
expr = new SQLPropertyExpr(expr, propertyName);
|
||||
}
|
||||
|
||||
item.setColumn(expr);
|
||||
}
|
||||
if (lexer.token == Token.COLONEQ) {
|
||||
lexer.nextToken();
|
||||
lexer.nextTokenValue();
|
||||
} else if (lexer.token == Token.EQ) {
|
||||
lexer.nextTokenValue();
|
||||
} else {
|
||||
accept(Token.EQ);
|
||||
throw new ParserException("syntax error, expect EQ, actual " + lexer.token + " "
|
||||
+ lexer.info());
|
||||
}
|
||||
|
||||
item.setValue(this.expr());
|
||||
@@ -1519,28 +1546,28 @@ public class SQLExprParser extends SQLParser {
|
||||
} catch (EOFParserException e) {
|
||||
throw new ParserException("EOF, " + expr + "=", e);
|
||||
}
|
||||
rightExp = equalityRest(rightExp);
|
||||
|
||||
if (lexer.token == Token.COLONEQ) {
|
||||
lexer.nextToken();
|
||||
SQLExpr colonExpr = expr();
|
||||
rightExp = new SQLBinaryOpExpr(rightExp, SQLBinaryOperator.Assignment, colonExpr, dbType);
|
||||
}
|
||||
|
||||
expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Equality, rightExp, dbType);
|
||||
} else if (token == Token.BANGEQ) {
|
||||
} else if (lexer.token() == Token.EQGT) {
|
||||
lexer.nextToken();
|
||||
rightExp = expr();
|
||||
String argumentName = ((SQLIdentifierExpr) expr).getName();
|
||||
return new OracleArgumentExpr(argumentName, rightExp);
|
||||
} else if (token == Token.BANGEQ || token == Token.CARETEQ) {
|
||||
lexer.nextToken();
|
||||
rightExp = bitOr();
|
||||
|
||||
rightExp = equalityRest(rightExp);
|
||||
|
||||
expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.NotEqual, rightExp, getDbType());
|
||||
} else if (token == Token.BANG) {
|
||||
lexer.nextToken();
|
||||
accept(Token.EQ);
|
||||
rightExp = bitOr();
|
||||
|
||||
rightExp = equalityRest(rightExp);
|
||||
|
||||
expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.NotEqual, rightExp, getDbType());
|
||||
expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.NotEqual, rightExp, dbType);
|
||||
} else if (token == Token.COLONEQ) {
|
||||
lexer.nextToken();
|
||||
rightExp = expr();
|
||||
expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Assignment, rightExp, getDbType());
|
||||
expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Assignment, rightExp, dbType);
|
||||
}
|
||||
|
||||
return expr;
|
||||
@@ -1562,9 +1589,19 @@ public class SQLExprParser extends SQLParser {
|
||||
return queryExpr;
|
||||
}
|
||||
|
||||
exprList(inListExpr.getTargetList(), inListExpr);
|
||||
List<SQLExpr> targetList = inListExpr.getTargetList();
|
||||
for (;;) {
|
||||
SQLExpr item = this.expr();
|
||||
item.setParent(inListExpr);
|
||||
targetList.add(item);
|
||||
if (lexer.token == Token.COMMA) {
|
||||
lexer.nextTokenValue();
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
accept(Token.RPAREN);
|
||||
expr = inListExpr;
|
||||
} else {
|
||||
SQLExpr itemExpr = primary();
|
||||
itemExpr.setParent(inListExpr);
|
||||
@@ -2632,6 +2669,13 @@ public class SQLExprParser extends SQLParser {
|
||||
String as = lexer.stringVal();
|
||||
|
||||
lexer.nextTokenComma();
|
||||
|
||||
if (lexer.token == Token.DOT) {
|
||||
lexer.nextToken();
|
||||
as += '.' + lexer.stringVal();
|
||||
lexer.nextToken();
|
||||
}
|
||||
|
||||
return new SQLSelectItem(expr, as, connectByRoot);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,8 +18,11 @@ package com.alibaba.druid.sql.parser;
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.druid.sql.ast.*;
|
||||
import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr;
|
||||
import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr;
|
||||
import com.alibaba.druid.sql.ast.statement.*;
|
||||
import com.alibaba.druid.sql.dialect.mysql.ast.expr.MySqlOrderingExpr;
|
||||
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;
|
||||
import com.alibaba.druid.util.FnvHash;
|
||||
import com.alibaba.druid.util.JdbcConstants;
|
||||
|
||||
@@ -317,7 +320,7 @@ public class SQLSelectParser extends SQLParser {
|
||||
}
|
||||
SQLExpr where = expr();
|
||||
|
||||
if (where != null && beforeComments != null) {
|
||||
if (beforeComments != null) {
|
||||
where.addBeforeComment(beforeComments);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,12 +19,10 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.druid.sql.ast.*;
|
||||
import com.alibaba.druid.sql.ast.expr.SQLCharExpr;
|
||||
import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
|
||||
import com.alibaba.druid.sql.ast.expr.SQLQueryExpr;
|
||||
import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr;
|
||||
import com.alibaba.druid.sql.ast.expr.*;
|
||||
import com.alibaba.druid.sql.ast.statement.*;
|
||||
import com.alibaba.druid.sql.ast.statement.SQLCreateTriggerStatement.TriggerType;
|
||||
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;
|
||||
import com.alibaba.druid.sql.dialect.oracle.parser.OracleExprParser;
|
||||
import com.alibaba.druid.util.FnvHash;
|
||||
import com.alibaba.druid.util.JdbcConstants;
|
||||
@@ -77,7 +75,7 @@ public class SQLStatementParser extends SQLParser {
|
||||
}
|
||||
|
||||
public void parseStatementList(List<SQLStatement> statementList) {
|
||||
parseStatementList(statementList, -1);
|
||||
parseStatementList(statementList, -1, null);
|
||||
}
|
||||
|
||||
public void parseStatementList(List<SQLStatement> statementList, int max) {
|
||||
@@ -2430,7 +2428,7 @@ public class SQLStatementParser extends SQLParser {
|
||||
*/
|
||||
public SQLStatement parseStatement( final boolean tryBest) {
|
||||
List<SQLStatement> list = new ArrayList<SQLStatement>();
|
||||
this.parseStatementList(list, 1);
|
||||
this.parseStatementList(list, 1, null);
|
||||
if (tryBest) {
|
||||
if (lexer.token != Token.EOF) {
|
||||
throw new ParserException("sql syntax error, no terminated. " + lexer.token);
|
||||
|
||||
@@ -317,7 +317,8 @@ public enum Token {
|
||||
COLON(":"),
|
||||
COLONCOLON("::"),
|
||||
COLONEQ(":="),
|
||||
EQEQ("=="),
|
||||
EQEQ("=="),
|
||||
EQGT("=>"),
|
||||
LTEQ("<="),
|
||||
LTEQGT("<=>"),
|
||||
LTGT("<>"),
|
||||
|
||||
Reference in New Issue
Block a user