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

Merge pull request #1096 from katzyn/parser

Parse and format TOP, INTERSECTS, and DUAL properly
...@@ -503,9 +503,9 @@ unless they are quoted (surrounded with double quotes). The list is currently: ...@@ -503,9 +503,9 @@ unless they are quoted (surrounded with double quotes). The list is currently:
</p><p> </p><p>
<code> <code>
ALL, CHECK, CONSTRAINT, CROSS, CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP, DISTINCT, EXCEPT, ALL, CHECK, CONSTRAINT, CROSS, CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP, DISTINCT, EXCEPT,
EXISTS, FALSE, FETCH, FOR, FOREIGN, FROM, FULL, GROUP, HAVING, INNER, INTERSECT, IS, JOIN, EXISTS, FALSE, FETCH, FOR, FOREIGN, FROM, FULL, GROUP, HAVING, INNER, INTERSECT, INTERSECTS,
LIKE, LIMIT, MINUS, NATURAL, NOT, NULL, OFFSET, ON, ORDER, PRIMARY, ROWNUM, SELECT, SYSDATE, IS, JOIN, LIKE, LIMIT, MINUS, NATURAL, NOT, NULL, OFFSET, ON, ORDER, PRIMARY, ROWNUM, SELECT,
SYSTIME, SYSTIMESTAMP, TODAY, TRUE, UNION, UNIQUE, WHERE, WITH SYSDATE, SYSTIME, SYSTIMESTAMP, TODAY, TOP, TRUE, UNION, UNIQUE, WHERE, WITH
</code> </code>
</p><p> </p><p>
Certain words of this list are keywords because they are functions that can be used without '()' for compatibility, Certain words of this list are keywords because they are functions that can be used without '()' for compatibility,
......
...@@ -21,8 +21,12 @@ Change Log ...@@ -21,8 +21,12 @@ Change Log
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul> <ul>
<li>Issue #1089: Parser does not quote words INTERSECTS, DUAL, TOP
</li>
<li>Issue #230: Renaming a column does not update foreign key constraint <li>Issue #230: Renaming a column does not update foreign key constraint
</li> </li>
<li>PR #1087: improve performance of planning large queries
</li>
<li>Issue #394: Recover tool places COLLATION and BINARY_COLLATION after temporary tables <li>Issue #394: Recover tool places COLLATION and BINARY_COLLATION after temporary tables
</li> </li>
<li>Improve the script-based unit testing to check the error code of the exception thrown. <li>Improve the script-based unit testing to check the error code of the exception thrown.
......
...@@ -684,10 +684,6 @@ public class Parser { ...@@ -684,10 +684,6 @@ public class Parser {
if (equalsToken("SESSION", schemaName)) { if (equalsToken("SESSION", schemaName)) {
// for local temporary tables // for local temporary tables
schema = database.getSchema(session.getCurrentSchemaName()); schema = database.getSchema(session.getCurrentSchemaName());
} else if (database.getMode().sysDummy1 &&
"SYSIBM".equals(schemaName)) {
// IBM DB2 and Apache Derby compatibility: SYSIBM.SYSDUMMY1
schema = database.getSchema(session.getCurrentSchemaName());
} }
} }
return schema; return schema;
...@@ -1372,7 +1368,7 @@ public class Parser { ...@@ -1372,7 +1368,7 @@ public class Parser {
private TableFilter readTableFilter() { private TableFilter readTableFilter() {
Table table; Table table;
String alias = null; String alias = null;
if (readIf("(")) { label: if (readIf("(")) {
if (isSelect()) { if (isSelect()) {
Query query = parseSelectUnion(); Query query = parseSelectUnion();
read(")"); read(")");
...@@ -1406,7 +1402,19 @@ public class Parser { ...@@ -1406,7 +1402,19 @@ public class Parser {
table = parseValuesTable(0).getTable(); table = parseValuesTable(0).getTable();
} else { } else {
String tableName = readIdentifierWithSchema(null); String tableName = readIdentifierWithSchema(null);
Schema schema = getSchema(); Schema schema;
if (schemaName == null) {
schema = null;
} else {
schema = findSchema(schemaName);
if (schema == null) {
if (isDualTable(tableName)) {
table = getDualTable(false);
break label;
}
throw DbException.get(ErrorCode.SCHEMA_NOT_FOUND_1, schemaName);
}
}
boolean foundLeftBracket = readIf("("); boolean foundLeftBracket = readIf("(");
if (foundLeftBracket && readIf("INDEX")) { if (foundLeftBracket && readIf("INDEX")) {
// Sybase compatibility with // Sybase compatibility with
...@@ -1442,13 +1450,8 @@ public class Parser { ...@@ -1442,13 +1450,8 @@ public class Parser {
} }
table = new FunctionTable(mainSchema, session, expr, call); table = new FunctionTable(mainSchema, session, expr, call);
} }
} else if (equalsToken("DUAL", tableName)) {
table = getDualTable(false);
} else if (database.getMode().sysDummy1 &&
equalsToken("SYSDUMMY1", tableName)) {
table = getDualTable(false);
} else { } else {
table = readTableOrView(tableName); table = readTableOrView(tableName, true);
} }
} }
ArrayList<String> derivedColumnNames = null; ArrayList<String> derivedColumnNames = null;
...@@ -4206,7 +4209,7 @@ public class Parser { ...@@ -4206,7 +4209,7 @@ public class Parser {
// if not yet converted to uppercase, do it now // if not yet converted to uppercase, do it now
s = StringUtils.toUpperEnglish(s); s = StringUtils.toUpperEnglish(s);
} }
return getSaveTokenType(s, false); return ParserUtil.getSaveTokenType(s, false);
} }
private boolean isKeyword(String s) { private boolean isKeyword(String s) {
...@@ -4217,10 +4220,6 @@ public class Parser { ...@@ -4217,10 +4220,6 @@ public class Parser {
return ParserUtil.isKeyword(s); return ParserUtil.isKeyword(s);
} }
private static int getSaveTokenType(String s, boolean functionsAsKeywords) {
return ParserUtil.getSaveTokenType(s, functionsAsKeywords);
}
private Column parseColumnForTable(String columnName, private Column parseColumnForTable(String columnName,
boolean defaultNullable) { boolean defaultNullable) {
Column column; Column column;
...@@ -5937,15 +5936,23 @@ public class Parser { ...@@ -5937,15 +5936,23 @@ public class Parser {
return command; return command;
} }
boolean isDualTable(String tableName) {
return ((schemaName == null || equalsToken(schemaName, "SYS")) && equalsToken("DUAL", tableName))
|| (database.getMode().sysDummy1 && (schemaName == null || equalsToken(schemaName, "SYSIBM")))
&& equalsToken("SYSDUMMY1", tableName);
}
private Table readTableOrView() { private Table readTableOrView() {
return readTableOrView(readIdentifierWithSchema(null)); return readTableOrView(readIdentifierWithSchema(null), false);
} }
private Table readTableOrView(String tableName) { private Table readTableOrView(String tableName, boolean allowDual) {
// same algorithm than readSequence
if (schemaName != null) { if (schemaName != null) {
return getSchema().getTableOrView(session, tableName); Table table = getSchema().resolveTableOrView(session, tableName);
if (table != null) {
return table;
} }
} else {
Table table = database.getSchema(session.getCurrentSchemaName()) Table table = database.getSchema(session.getCurrentSchemaName())
.resolveTableOrView(session, tableName); .resolveTableOrView(session, tableName);
if (table != null) { if (table != null) {
...@@ -5961,6 +5968,10 @@ public class Parser { ...@@ -5961,6 +5968,10 @@ public class Parser {
} }
} }
} }
}
if (allowDual && isDualTable(tableName)) {
return getDualTable(false);
}
throw DbException.get(ErrorCode.TABLE_OR_VIEW_NOT_FOUND_1, tableName); throw DbException.get(ErrorCode.TABLE_OR_VIEW_NOT_FOUND_1, tableName);
} }
...@@ -6878,7 +6889,7 @@ public class Parser { ...@@ -6878,7 +6889,7 @@ public class Parser {
if (s == null) { if (s == null) {
return "\"\""; return "\"\"";
} }
if (ParserUtil.isSimpleIdentifier(s, false)) { if (ParserUtil.isSimpleIdentifier(s)) {
return s; return s;
} }
return StringUtils.quoteIdentifier(s); return StringUtils.quoteIdentifier(s);
......
...@@ -1405,7 +1405,7 @@ public class JdbcStatement extends TraceObject implements Statement, JdbcStateme ...@@ -1405,7 +1405,7 @@ public class JdbcStatement extends TraceObject implements Statement, JdbcStateme
*/ */
@Override @Override
public boolean isSimpleIdentifier(String identifier) throws SQLException { public boolean isSimpleIdentifier(String identifier) throws SQLException {
return ParserUtil.isSimpleIdentifier(identifier, true); return ParserUtil.isSimpleIdentifier(identifier);
} }
/** /**
......
...@@ -58,11 +58,10 @@ public class ParserUtil { ...@@ -58,11 +58,10 @@ public class ParserUtil {
* Is this a simple identifier (in the JDBC specification sense). * Is this a simple identifier (in the JDBC specification sense).
* *
* @param s identifier to check * @param s identifier to check
* @param functionsAsKeywords treat system functions as keywords
* @return is specified identifier may be used without quotes * @return is specified identifier may be used without quotes
* @throws NullPointerException if s is {@code null} * @throws NullPointerException if s is {@code null}
*/ */
public static boolean isSimpleIdentifier(String s, boolean functionsAsKeywords) { public static boolean isSimpleIdentifier(String s) {
if (s.length() == 0) { if (s.length() == 0) {
return false; return false;
} }
...@@ -78,17 +77,18 @@ public class ParserUtil { ...@@ -78,17 +77,18 @@ public class ParserUtil {
return false; return false;
} }
} }
return getSaveTokenType(s, functionsAsKeywords) == IDENTIFIER; return getSaveTokenType(s, true) == IDENTIFIER;
} }
/** /**
* Get the token type. * Get the token type.
* *
* @param s the token * @param s the token
* @param functionsAsKeywords whether "current data / time" functions are keywords * @param additionalKeywords whether TOP, INTERSECTS, and "current data /
* time" functions are keywords
* @return the token type * @return the token type
*/ */
public static int getSaveTokenType(String s, boolean functionsAsKeywords) { public static int getSaveTokenType(String s, boolean additionalKeywords) {
switch (s.charAt(0)) { switch (s.charAt(0)) {
case 'A': case 'A':
return getKeywordOrIdentifier(s, "ALL", KEYWORD); return getKeywordOrIdentifier(s, "ALL", KEYWORD);
...@@ -100,7 +100,7 @@ public class ParserUtil { ...@@ -100,7 +100,7 @@ public class ParserUtil {
} else if ("CROSS".equals(s)) { } else if ("CROSS".equals(s)) {
return KEYWORD; return KEYWORD;
} }
if (functionsAsKeywords) { if (additionalKeywords) {
if ("CURRENT_DATE".equals(s) || "CURRENT_TIME".equals(s) || "CURRENT_TIMESTAMP".equals(s)) { if ("CURRENT_DATE".equals(s) || "CURRENT_TIME".equals(s) || "CURRENT_TIMESTAMP".equals(s)) {
return KEYWORD; return KEYWORD;
} }
...@@ -131,12 +131,15 @@ public class ParserUtil { ...@@ -131,12 +131,15 @@ public class ParserUtil {
case 'H': case 'H':
return getKeywordOrIdentifier(s, "HAVING", KEYWORD); return getKeywordOrIdentifier(s, "HAVING", KEYWORD);
case 'I': case 'I':
if ("INNER".equals(s)) { if ("INNER".equals(s) || "INTERSECT".equals(s) || "IS".equals(s)) {
return KEYWORD; return KEYWORD;
} else if ("INTERSECT".equals(s)) { }
if (additionalKeywords) {
if ("INTERSECTS".equals(s)) {
return KEYWORD; return KEYWORD;
} }
return getKeywordOrIdentifier(s, "IS", KEYWORD); }
return IDENTIFIER;
case 'J': case 'J':
return getKeywordOrIdentifier(s, "JOIN", KEYWORD); return getKeywordOrIdentifier(s, "JOIN", KEYWORD);
case 'L': case 'L':
...@@ -168,7 +171,7 @@ public class ParserUtil { ...@@ -168,7 +171,7 @@ public class ParserUtil {
if ("SELECT".equals(s)) { if ("SELECT".equals(s)) {
return KEYWORD; return KEYWORD;
} }
if (functionsAsKeywords) { if (additionalKeywords) {
if ("SYSDATE".equals(s) || "SYSTIME".equals(s) || "SYSTIMESTAMP".equals(s)) { if ("SYSDATE".equals(s) || "SYSTIME".equals(s) || "SYSTIMESTAMP".equals(s)) {
return KEYWORD; return KEYWORD;
} }
...@@ -178,8 +181,8 @@ public class ParserUtil { ...@@ -178,8 +181,8 @@ public class ParserUtil {
if ("TRUE".equals(s)) { if ("TRUE".equals(s)) {
return TRUE; return TRUE;
} }
if (functionsAsKeywords) { if (additionalKeywords) {
if ("TODAY".equals(s)) { if ("TODAY".equals(s) || "TOP".equals(s)) {
return KEYWORD; return KEYWORD;
} }
} }
......
...@@ -87,6 +87,7 @@ public class TestScript extends TestBase { ...@@ -87,6 +87,7 @@ public class TestScript extends TestBase {
testScript("testScript.sql"); testScript("testScript.sql");
testScript("derived-column-names.sql"); testScript("derived-column-names.sql");
testScript("dual.sql");
testScript("indexes.sql"); testScript("indexes.sql");
testScript("information_schema.sql"); testScript("information_schema.sql");
testScript("joins.sql"); testScript("joins.sql");
......
-- Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0,
-- and the EPL 1.0 (http://h2database.com/html/license.html).
-- Initial Developer: H2 Group
--
SELECT * FROM DUAL;
>> 1
CREATE TABLE DUAL(A INT);
> ok
INSERT INTO DUAL VALUES (2);
> update count: 1
SELECT A FROM DUAL;
>> 2
SELECT * FROM SYS.DUAL;
>> 1
DROP TABLE DUAL;
> ok
SET MODE DB2;
> ok
SELECT * FROM SYSDUMMY1;
>> 1
CREATE TABLE SYSDUMMY1(A INT);
> ok
INSERT INTO SYSDUMMY1 VALUES (2);
> update count: 1
SELECT A FROM SYSDUMMY1;
>> 2
SELECT * FROM SYSIBM.SYSDUMMY1;
>> 1
DROP TABLE SYSDUMMY1;
> ok
SET MODE Regular;
> ok
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论