Unverified 提交 3f586cbb authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov 提交者: GitHub

Merge pull request #1277 from katzyn/misc

Assorted changes in Parser
...@@ -28,7 +28,6 @@ import static org.h2.util.ParserUtil.INNER; ...@@ -28,7 +28,6 @@ import static org.h2.util.ParserUtil.INNER;
import static org.h2.util.ParserUtil.INTERSECT; import static org.h2.util.ParserUtil.INTERSECT;
import static org.h2.util.ParserUtil.IS; import static org.h2.util.ParserUtil.IS;
import static org.h2.util.ParserUtil.JOIN; import static org.h2.util.ParserUtil.JOIN;
import static org.h2.util.ParserUtil.KEYWORD;
import static org.h2.util.ParserUtil.LIKE; import static org.h2.util.ParserUtil.LIKE;
import static org.h2.util.ParserUtil.LIMIT; import static org.h2.util.ParserUtil.LIMIT;
import static org.h2.util.ParserUtil.MINUS; import static org.h2.util.ParserUtil.MINUS;
...@@ -325,6 +324,66 @@ public class Parser { ...@@ -325,6 +324,66 @@ public class Parser {
*/ */
private static final int DOT = COMMA + 1; private static final int DOT = COMMA + 1;
/**
* The token "{".
*/
private static final int OPEN_BRACE = DOT + 1;
/**
* The token "}".
*/
private static final int CLOSE_BRACE = OPEN_BRACE + 1;
/**
* The token "/".
*/
private static final int SLASH = CLOSE_BRACE + 1;
/**
* The token "%".
*/
private static final int PERCENT = SLASH + 1;
/**
* The token ";".
*/
private static final int SEMICOLON = PERCENT + 1;
/**
* The token ":".
*/
private static final int COLON = SEMICOLON + 1;
/**
* The token "[".
*/
private static final int OPEN_BRACKET = COLON + 1;
/**
* The token "]".
*/
private static final int CLOSE_BRACKET = OPEN_BRACKET + 1;
/**
* The token "~".
*/
private static final int TILDE = CLOSE_BRACKET + 1;
/**
* The token "::".
*/
private static final int COLON_COLON = TILDE + 1;
/**
* The token ":=".
*/
private static final int COLON_EQ = COLON_COLON + 1;
/**
* The token "!~".
*/
private static final int NOT_TILDE = COLON_EQ + 1;
private static final String[] TOKENS = { private static final String[] TOKENS = {
// Unused // Unused
null, null,
...@@ -448,6 +507,30 @@ public class Parser { ...@@ -448,6 +507,30 @@ public class Parser {
",", ",",
// DOT // DOT
".", ".",
// OPEN_BRACE
"{",
// CLOSE_BRACE
"}",
// SLASH
"/",
// PERCENT
"%",
// SEMICOLON
";",
// COLON
":",
// OPEN_BRACKET
"[",
// CLOSE_BRACKET
"]",
// TILDE
"~",
// COLON_COLON
"::",
// COLON_EQ
":=",
// NOT_TILDE
"!~",
// End // End
}; };
...@@ -527,7 +610,7 @@ public class Parser { ...@@ -527,7 +610,7 @@ public class Parser {
public Command prepareCommand(String sql) { public Command prepareCommand(String sql) {
try { try {
Prepared p = parse(sql); Prepared p = parse(sql);
boolean hasMore = isToken(";"); boolean hasMore = isToken(SEMICOLON);
if (!hasMore && currentTokenType != END) { if (!hasMore && currentTokenType != END) {
throw getSyntaxError(); throw getSyntaxError();
} }
...@@ -589,24 +672,35 @@ public class Parser { ...@@ -589,24 +672,35 @@ public class Parser {
private Prepared parsePrepared() { private Prepared parsePrepared() {
int start = lastParseIndex; int start = lastParseIndex;
Prepared c = null; Prepared c = null;
String token = currentToken; switch (currentTokenType) {
if (token.length() == 0) { case END:
case SEMICOLON:
c = new NoOperation(session); c = new NoOperation(session);
} else { setSQL(c, null, start);
char first = token.charAt(0); return c;
switch (first) { case PARAMETER:
case '?': // read the ? as a parameter
// read the ? as a parameter readTerm();
readTerm(); // this is an 'out' parameter - set a dummy value
// this is an 'out' parameter - set a dummy value parameters.get(0).setValue(ValueNull.INSTANCE);
parameters.get(0).setValue(ValueNull.INSTANCE); read(EQUAL);
read(EQUAL); read("CALL");
read("CALL"); c = parseCall();
c = parseCall(); break;
break; case OPEN_PAREN:
case '(': case FROM:
c = parseSelect(); case SELECT:
c = parseSelect();
break;
case WITH:
read();
c = parseWithStatementOrQuery();
break;
case IDENTIFIER:
if (currentTokenQuoted) {
break; break;
}
switch (currentToken.charAt(0)) {
case 'a': case 'a':
case 'A': case 'A':
if (readIf("ALTER")) { if (readIf("ALTER")) {
...@@ -658,12 +752,6 @@ public class Parser { ...@@ -658,12 +752,6 @@ public class Parser {
c = parseExecute(); c = parseExecute();
} }
break; break;
case 'f':
case 'F':
if (isToken(FROM)) {
c = parseSelect();
}
break;
case 'g': case 'g':
case 'G': case 'G':
if (readIf("GRANT")) { if (readIf("GRANT")) {
...@@ -710,9 +798,7 @@ public class Parser { ...@@ -710,9 +798,7 @@ public class Parser {
break; break;
case 's': case 's':
case 'S': case 'S':
if (isToken(SELECT)) { if (readIf("SET")) {
c = parseSelect();
} else if (readIf("SET")) {
c = parseSet(); c = parseSet();
} else if (readIf("SAVEPOINT")) { } else if (readIf("SAVEPOINT")) {
c = parseSavepoint(); c = parseSavepoint();
...@@ -743,52 +829,40 @@ public class Parser { ...@@ -743,52 +829,40 @@ public class Parser {
if (readIf("VALUES")) { if (readIf("VALUES")) {
c = parseValues(); c = parseValues();
} }
break;
case 'w':
case 'W':
if (readIf(WITH)) {
c = parseWithStatementOrQuery();
}
break;
case ';':
c = new NoOperation(session);
break;
default:
throw getSyntaxError();
} }
if (indexedParameterList != null) { }
for (int i = 0, size = indexedParameterList.size(); if (c == null) {
i < size; i++) { throw getSyntaxError();
if (indexedParameterList.get(i) == null) { }
indexedParameterList.set(i, new Parameter(i)); if (indexedParameterList != null) {
} for (int i = 0, size = indexedParameterList.size();
i < size; i++) {
if (indexedParameterList.get(i) == null) {
indexedParameterList.set(i, new Parameter(i));
} }
parameters = indexedParameterList;
} }
if (readIf("{")) { parameters = indexedParameterList;
do { }
int index = (int) readLong() - 1; if (readIf(OPEN_BRACE)) {
if (index < 0 || index >= parameters.size()) { do {
throw getSyntaxError(); int index = (int) readLong() - 1;
} if (index < 0 || index >= parameters.size()) {
Parameter p = parameters.get(index); throw getSyntaxError();
if (p == null) {
throw getSyntaxError();
}
read(":");
Expression expr = readExpression();
expr = expr.optimize(session);
p.setValue(expr.getValue(session));
} while (readIf(COMMA));
read("}");
for (Parameter p : parameters) {
p.checkSet();
} }
parameters.clear(); Parameter p = parameters.get(index);
if (p == null) {
throw getSyntaxError();
}
read(COLON);
Expression expr = readExpression();
expr = expr.optimize(session);
p.setValue(expr.getValue(session));
} while (readIf(COMMA));
read(CLOSE_BRACE);
for (Parameter p : parameters) {
p.checkSet();
} }
} parameters.clear();
if (c == null) {
throw getSyntaxError();
} }
setSQL(c, null, start); setSQL(c, null, start);
return c; return c;
...@@ -2797,7 +2871,7 @@ public class Parser { ...@@ -2797,7 +2871,7 @@ public class Parser {
while (true) { while (true) {
if (readIf(STRING_CONCAT)) { if (readIf(STRING_CONCAT)) {
r = new Operation(OpType.CONCAT, r, readSum()); r = new Operation(OpType.CONCAT, r, readSum());
} else if (readIf("~")) { } else if (readIf(TILDE)) {
if (readIf(ASTERISK)) { if (readIf(ASTERISK)) {
Function function = Function.getFunction(database, "CAST"); Function function = Function.getFunction(database, "CAST");
function.setDataType(new Column("X", function.setDataType(new Column("X",
...@@ -2806,7 +2880,7 @@ public class Parser { ...@@ -2806,7 +2880,7 @@ public class Parser {
r = function; r = function;
} }
r = new CompareLike(database, r, readSum(), null, true); r = new CompareLike(database, r, readSum(), null, true);
} else if (readIf("!~")) { } else if (readIf(NOT_TILDE)) {
if (readIf(ASTERISK)) { if (readIf(ASTERISK)) {
Function function = Function.getFunction(database, "CAST"); Function function = Function.getFunction(database, "CAST");
function.setDataType(new Column("X", function.setDataType(new Column("X",
...@@ -2840,9 +2914,9 @@ public class Parser { ...@@ -2840,9 +2914,9 @@ public class Parser {
while (true) { while (true) {
if (readIf(ASTERISK)) { if (readIf(ASTERISK)) {
r = new Operation(OpType.MULTIPLY, r, readTerm()); r = new Operation(OpType.MULTIPLY, r, readTerm());
} else if (readIf("/")) { } else if (readIf(SLASH)) {
r = new Operation(OpType.DIVIDE, r, readTerm()); r = new Operation(OpType.DIVIDE, r, readTerm());
} else if (readIf("%")) { } else if (readIf(PERCENT)) {
r = new Operation(OpType.MODULUS, r, readTerm()); r = new Operation(OpType.MODULUS, r, readTerm());
} else { } else {
return r; return r;
...@@ -3174,9 +3248,6 @@ public class Parser { ...@@ -3174,9 +3248,6 @@ public class Parser {
} }
private Expression readFunctionWithoutParameters(String name) { private Expression readFunctionWithoutParameters(String name) {
if (readIf(OPEN_PAREN)) {
read(CLOSE_PAREN);
}
if (database.isAllowBuiltinAliasOverride()) { if (database.isAllowBuiltinAliasOverride()) {
FunctionAlias functionAlias = database.getSchema(session.getCurrentSchemaName()).findFunction(name); FunctionAlias functionAlias = database.getSchema(session.getCurrentSchemaName()).findFunction(name);
if (functionAlias != null) { if (functionAlias != null) {
...@@ -3319,7 +3390,7 @@ public class Parser { ...@@ -3319,7 +3390,7 @@ public class Parser {
case AT: case AT:
read(); read();
r = new Variable(session, readAliasIdentifier()); r = new Variable(session, readAliasIdentifier());
if (readIf(":=")) { if (readIf(COLON_EQ)) {
Expression value = readExpression(); Expression value = readExpression();
Function function = Function.getFunction(database, "SET"); Function function = Function.getFunction(database, "SET");
function.setParameter(0, r); function.setParameter(0, r);
...@@ -3377,16 +3448,8 @@ public class Parser { ...@@ -3377,16 +3448,8 @@ public class Parser {
r = readFunctionWithoutParameters("LOCALTIME"); r = readFunctionWithoutParameters("LOCALTIME");
} else if (equalsToken("SYSTIME", name)) { } else if (equalsToken("SYSTIME", name)) {
r = readFunctionWithoutParameters("CURRENT_TIME"); r = readFunctionWithoutParameters("CURRENT_TIME");
} else if (equalsToken("CURRENT", name)) { } else if (database.getMode().getEnum() == ModeEnum.DB2 && equalsToken("CURRENT", name)) {
if (readIf("TIMESTAMP")) { r = parseDB2SpecialRegisters(name);
r = readFunctionWithoutParameters("CURRENT_TIMESTAMP");
} else if (readIf("TIME")) {
r = readFunctionWithoutParameters("CURRENT_TIME");
} else if (readIf("DATE")) {
r = readFunctionWithoutParameters("CURRENT_DATE");
} else {
r = new ExpressionColumn(database, null, null, name);
}
} else if (equalsToken("NEXT", name) && readIf("VALUE")) { } else if (equalsToken("NEXT", name) && readIf("VALUE")) {
read(FOR); read(FOR);
Sequence sequence = readSequence(); Sequence sequence = readSequence();
...@@ -3553,7 +3616,7 @@ public class Parser { ...@@ -3553,7 +3616,7 @@ public class Parser {
default: default:
throw getSyntaxError(); throw getSyntaxError();
} }
if (readIf("[")) { if (readIf(OPEN_BRACKET)) {
Function function = Function.getFunction(database, "ARRAY_GET"); Function function = Function.getFunction(database, "ARRAY_GET");
function.setParameter(0, r); function.setParameter(0, r);
r = readExpression(); r = readExpression();
...@@ -3561,9 +3624,9 @@ public class Parser { ...@@ -3561,9 +3624,9 @@ public class Parser {
.get(1))); .get(1)));
function.setParameter(1, r); function.setParameter(1, r);
r = function; r = function;
read("]"); read(CLOSE_BRACKET);
} }
if (readIf("::")) { if (readIf(COLON_COLON)) {
// PostgreSQL compatibility // PostgreSQL compatibility
if (isToken("PG_CATALOG")) { if (isToken("PG_CATALOG")) {
read("PG_CATALOG"); read("PG_CATALOG");
...@@ -3588,6 +3651,24 @@ public class Parser { ...@@ -3588,6 +3651,24 @@ public class Parser {
return r; return r;
} }
private Expression parseDB2SpecialRegisters(String name) {
// Only "CURRENT" name is supported
if (readIf("TIMESTAMP")) {
if (readIf(WITH)) {
read("TIME");
read("ZONE");
return readFunctionWithoutParameters("CURRENT_TIMESTAMP");
}
return readFunctionWithoutParameters("LOCALTIMESTAMP");
} else if (readIf("TIME")) {
return readFunctionWithoutParameters("CURRENT_TIME");
} else if (readIf("DATE")) {
return readFunctionWithoutParameters("CURRENT_DATE");
}
// No match, parse CURRENT as a column
return new ExpressionColumn(database, null, null, name);
}
private Expression readCase() { private Expression readCase() {
if (readIf("END")) { if (readIf("END")) {
readIf("CASE"); readIf("CASE");
...@@ -3912,16 +3993,13 @@ public class Parser { ...@@ -3912,16 +3993,13 @@ public class Parser {
case CHAR_SPECIAL_2: case CHAR_SPECIAL_2:
if (types[i] == CHAR_SPECIAL_2) { if (types[i] == CHAR_SPECIAL_2) {
char c1 = chars[i++]; char c1 = chars[i++];
currentToken = sqlCommand.substring(start, i);
currentTokenType = getSpecialType2(c, c1); currentTokenType = getSpecialType2(c, c1);
} else { } else {
currentToken = sqlCommand.substring(start, i);
currentTokenType = getSpecialType1(c); currentTokenType = getSpecialType1(c);
} }
parseIndex = i; parseIndex = i;
return; return;
case CHAR_SPECIAL_1: case CHAR_SPECIAL_1:
currentToken = sqlCommand.substring(start, i);
currentTokenType = getSpecialType1(c); currentTokenType = getSpecialType1(c);
parseIndex = i; parseIndex = i;
return; return;
...@@ -4023,7 +4101,6 @@ public class Parser { ...@@ -4023,7 +4101,6 @@ public class Parser {
return; return;
} }
case CHAR_END: case CHAR_END:
currentToken = "";
currentTokenType = END; currentTokenType = END;
parseIndex = i; parseIndex = i;
return; return;
...@@ -4368,15 +4445,23 @@ public class Parser { ...@@ -4368,15 +4445,23 @@ public class Parser {
case ',': case ',':
return COMMA; return COMMA;
case '{': case '{':
return OPEN_BRACE;
case '}': case '}':
return CLOSE_BRACE;
case '/': case '/':
return SLASH;
case '%': case '%':
return PERCENT;
case ';': case ';':
return SEMICOLON;
case ':': case ':':
return COLON;
case '[': case '[':
return OPEN_BRACKET;
case ']': case ']':
return CLOSE_BRACKET;
case '~': case '~':
return KEYWORD; return TILDE;
case '(': case '(':
return OPEN_PAREN; return OPEN_PAREN;
case ')': case ')':
...@@ -4395,8 +4480,10 @@ public class Parser { ...@@ -4395,8 +4480,10 @@ public class Parser {
private int getSpecialType2(char c0, char c1) { private int getSpecialType2(char c0, char c1) {
switch (c0) { switch (c0) {
case ':': case ':':
if (c1 == ':' || c1 == '=') { if (c1 == ':') {
return KEYWORD; return COLON_COLON;
} else if (c1 == '=') {
return COLON_EQ;
} }
break; break;
case '>': case '>':
...@@ -4415,7 +4502,7 @@ public class Parser { ...@@ -4415,7 +4502,7 @@ public class Parser {
if (c1 == '=') { if (c1 == '=') {
return NOT_EQUAL; return NOT_EQUAL;
} else if (c1 == '~') { } else if (c1 == '~') {
return KEYWORD; return NOT_TILDE;
} }
break; break;
case '|': case '|':
......
...@@ -561,6 +561,19 @@ public class TestCompatibility extends TestDb { ...@@ -561,6 +561,19 @@ public class TestCompatibility extends TestDb {
"select date from test where date = '2014-04-05-09.48.28.020005'"); "select date from test where date = '2014-04-05-09.48.28.020005'");
assertResult("2014-04-05 09:48:28.020005", stat, assertResult("2014-04-05 09:48:28.020005", stat,
"select date from test where date = '2014-04-05 09:48:28.020005'"); "select date from test where date = '2014-04-05 09:48:28.020005'");
// Test limited support for DB2's special registers
// Standard SQL functions like LOCALTIMESTAMP, CURRENT_TIMESTAMP and
// others are used to compare values, their implementation in H2 is
// compatible with standard, but may be not really compatible with DB2.
assertResult("TRUE", stat, "SELECT LOCALTIMESTAMP = CURRENT TIMESTAMP");
assertResult("TRUE", stat, "SELECT CAST(LOCALTIMESTAMP AS VARCHAR) = CAST(CURRENT TIMESTAMP AS VARCHAR)");
assertResult("TRUE", stat, "SELECT CURRENT_TIMESTAMP = CURRENT TIMESTAMP WITH TIME ZONE");
assertResult("TRUE", stat,
"SELECT CAST(CURRENT_TIMESTAMP AS VARCHAR) = CAST(CURRENT TIMESTAMP WITH TIME ZONE AS VARCHAR)");
assertResult("TRUE", stat, "SELECT CURRENT_TIME = CURRENT TIME");
assertResult("TRUE", stat, "SELECT CURRENT_DATE = CURRENT DATE");
} }
private void testDerby() throws SQLException { private void testDerby() throws SQLException {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论