提交 a5472db7 authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Add explicit table query

上级 c6933120
......@@ -238,6 +238,19 @@ Lists the schemas, tables, or the columns of a table.
SHOW TABLES
"
"Commands (DML)","Explicit TABLE","
TABLE [schemaName.]tableName
","
Selects data from a table.
This command is an equivalent to SELECT * FROM TABLE.
FROM, WHERE, GROUP BY, HAVING, and WINDOW clauses from the SELECT command are not allowed.
","
TABLE TEST;
TABLE TEST ORDER BY ID FETCH FIRST ROW ONLY;
"
"Commands (DML)","WITH","
WITH [ RECURSIVE ] { name [( columnName [,...] )] AS ( select ) [,...] }
{ select | insert | update | merge | delete | createTable }
......
......@@ -481,8 +481,8 @@ EXISTS, FALSE, FETCH, FOR, FOREIGN, FROM, FULL, GROUP, HAVING,
IF, INNER, INTERSECT, INTERSECTS, INTERVAL, IS, JOIN, LIKE,
LIMIT, LOCALTIME, LOCALTIMESTAMP, MINUS, NATURAL, NOT, NULL,
OFFSET, ON, ORDER, PRIMARY, ROW, ROWNUM, SELECT, SYSDATE,
SYSTIME, SYSTIMESTAMP, TODAY, TOP, TRUE, UNION, UNIQUE, VALUES,
WHERE, WINDOW, WITH
SYSTIME, SYSTIMESTAMP, TABLE, TODAY, TOP, TRUE, UNION, UNIQUE,
VALUES, WHERE, WINDOW, WITH
</code>
</p><p>
Certain words of this list are keywords because they are functions that can be used without '()',
......
......@@ -52,6 +52,7 @@ import static org.h2.util.ParserUtil.PRIMARY;
import static org.h2.util.ParserUtil.ROW;
import static org.h2.util.ParserUtil.ROWNUM;
import static org.h2.util.ParserUtil.SELECT;
import static org.h2.util.ParserUtil.TABLE;
import static org.h2.util.ParserUtil.TRUE;
import static org.h2.util.ParserUtil.UNION;
import static org.h2.util.ParserUtil.UNIQUE;
......@@ -251,7 +252,7 @@ import org.h2.value.ValueTimestampTimeZone;
public class Parser {
private static final String WITH_STATEMENT_SUPPORTS_LIMITED_SUB_STATEMENTS =
"WITH statement supports only SELECT, CREATE TABLE, INSERT, UPDATE, MERGE or DELETE statements";
"WITH statement supports only SELECT, TABLE, CREATE TABLE, INSERT, UPDATE, MERGE or DELETE statements";
// used during the tokenizer phase
private static final int CHAR_END = 1, CHAR_VALUE = 2, CHAR_QUOTED = 3;
......@@ -510,6 +511,8 @@ public class Parser {
"ROWNUM",
// SELECT
"SELECT",
// TABLE
"TABLE",
// TRUE
"TRUE",
// UNION
......@@ -745,6 +748,7 @@ public class Parser {
case OPEN_PAREN:
case FROM:
case SELECT:
case TABLE:
c = parseSelect();
break;
case VALUES:
......@@ -944,7 +948,7 @@ public class Parser {
private Prepared parseAnalyze() {
Analyze command = new Analyze(session);
if (readIf("TABLE")) {
if (readIf(TABLE)) {
Table table = readTableOrView();
command.setTable(table);
}
......@@ -1436,7 +1440,20 @@ public class Parser {
// need to read ahead, it could be a nested union:
// ((select 1) union (select 1))
}
boolean select = isToken(SELECT) || isToken(FROM) || isToken(WITH);
boolean select;
switch (currentTokenType) {
case FROM:
case SELECT:
case WITH:
select = true;
break;
case TABLE:
read();
select = !readIf(OPEN_PAREN);
break;
default:
select = false;
}
parseIndex = start;
read();
return select;
......@@ -1761,6 +1778,9 @@ public class Parser {
}
} else if (readIf(VALUES)) {
table = parseValuesTable(0).getTable();
} else if (readIf(TABLE)) {
read(OPEN_PAREN);
table = readTableFunction("TABLE", null, database.getSchema(Constants.SCHEMA_MAIN));
} else {
String tableName = readIdentifierWithSchema(null);
Schema schema;
......@@ -1801,15 +1821,7 @@ public class Parser {
table = new RangeTable(mainSchema, min, max, false);
}
} else {
Expression expr = readFunction(schema, tableName);
if (!(expr instanceof FunctionCall)) {
throw getSyntaxError();
}
FunctionCall call = (FunctionCall) expr;
if (!call.isDeterministic()) {
recompileAlways = true;
}
table = new FunctionTable(mainSchema, session, expr, call);
table = readTableFunction(tableName, schema, mainSchema);
}
} else {
table = readTableOrView(tableName);
......@@ -1853,6 +1865,18 @@ public class Parser {
return filter;
}
private Table readTableFunction(String tableName, Schema schema, Schema mainSchema) {
Expression expr = readFunction(schema, tableName);
if (!(expr instanceof FunctionCall)) {
throw getSyntaxError();
}
FunctionCall call = (FunctionCall) expr;
if (!call.isDeterministic()) {
recompileAlways = true;
}
return new FunctionTable(mainSchema, session, expr, call);
}
private IndexHints parseIndexHints(Table table) {
read(OPEN_PAREN);
LinkedHashSet<String> indexNames = new LinkedHashSet<>();
......@@ -1919,7 +1943,7 @@ public class Parser {
}
private Prepared parseTruncate() {
read("TABLE");
read(TABLE);
Table table = readTableOrView();
boolean restart;
if (readIf("CONTINUE")) {
......@@ -1949,7 +1973,7 @@ public class Parser {
int type = 0;
read(ON);
boolean column = false;
if (readIf("TABLE") || readIf("VIEW")) {
if (readIf(TABLE) || readIf("VIEW")) {
type = DbObject.TABLE_OR_VIEW;
} else if (readIf("COLUMN")) {
column = true;
......@@ -2016,7 +2040,7 @@ public class Parser {
}
private Prepared parseDrop() {
if (readIf("TABLE")) {
if (readIf(TABLE)) {
boolean ifExists = readIfExists(false);
String tableName = readIdentifierWithSchema();
DropTable command = new DropTable(session, getSchema());
......@@ -2316,11 +2340,18 @@ public class Parser {
readIf(FOR);
}
}
if (isToken(SELECT) || isToken(FROM) || isToken(OPEN_PAREN) || isToken(WITH)) {
switch (currentTokenType) {
case FROM:
case SELECT:
case TABLE:
case WITH:
case OPEN_PAREN:
Query query = parseSelect();
query.setNeverLazy(true);
command.setCommand(query);
} else if (readIf("DELETE")) {
break;
default:
if (readIf("DELETE")) {
command.setCommand(parseDelete());
} else if (readIf("UPDATE")) {
command.setCommand(parseUpdate());
......@@ -2331,6 +2362,7 @@ public class Parser {
} else {
throw getSyntaxError();
}
}
return command;
}
......@@ -2647,6 +2679,18 @@ public class Parser {
fromFirst = false;
} else if (readIf(FROM)) {
fromFirst = true;
} else if (readIf(TABLE)) {
int start = lastParseIndex;
Table table = readTableOrView();
Select command = new Select(session);
TableFilter filter = new TableFilter(session, table, null, rightsChecked,
command, orderInFrom++, null);
command.addTableFilter(filter, true);
ArrayList<Expression> expressions = new ArrayList<>();
expressions.add(new Wildcard(null, null));
command.setExpressions(expressions);
setSQL(command, "SELECT", start);
return command;
} else {
throw getSyntaxError();
}
......@@ -3760,6 +3804,17 @@ public class Parser {
case WITH:
r = new Subquery(parseSelect());
break;
case TABLE:
int index = lastParseIndex;
read();
if (readIf(OPEN_PAREN)) {
r = readFunction(null, "TABLE");
} else {
parseIndex = index;
read();
r = new Subquery(parseSelect());
}
break;
case IDENTIFIER:
String name = currentToken;
if (currentTokenQuoted) {
......@@ -5592,22 +5647,22 @@ public class Parser {
if (readIf("LINKED")) {
return parseCreateLinkedTable(true, false, force);
}
read("TABLE");
read(TABLE);
return parseCreateTable(true, false, cached);
} else if (readIf("GLOBAL")) {
read("TEMPORARY");
if (readIf("LINKED")) {
return parseCreateLinkedTable(true, true, force);
}
read("TABLE");
read(TABLE);
return parseCreateTable(true, true, cached);
} else if (readIf("TEMP") || readIf("TEMPORARY")) {
if (readIf("LINKED")) {
return parseCreateLinkedTable(true, true, force);
}
read("TABLE");
read(TABLE);
return parseCreateTable(true, true, cached);
} else if (readIf("TABLE")) {
} else if (readIf(TABLE)) {
if (!cached && !memory) {
cached = database.getDefaultTableType() == Table.TYPE_CACHED;
}
......@@ -6110,10 +6165,17 @@ public class Parser {
parentheses++;
}
if (isToken(SELECT)) {
Query query = parseSelectUnion();
query.setPrepareAlways(true);
query.setNeverLazy(true);
p = query;
p = parseWithQuery();
} else if (isToken(TABLE)) {
int index = lastParseIndex;
read();
if (!isToken(OPEN_PAREN)) {
parseIndex = index;
read();
p = parseWithQuery();
} else {
throw DbException.get(ErrorCode.SYNTAX_ERROR_1, WITH_STATEMENT_SUPPORTS_LIMITED_SUB_STATEMENTS);
}
} else if (readIf("INSERT")) {
p = parseInsert();
p.setPrepareAlways(true);
......@@ -6127,7 +6189,7 @@ public class Parser {
p = parseDelete();
p.setPrepareAlways(true);
} else if (readIf("CREATE")) {
if (!isToken("TABLE")) {
if (!isToken(TABLE)) {
throw DbException.get(ErrorCode.SYNTAX_ERROR_1,
WITH_STATEMENT_SUPPORTS_LIMITED_SUB_STATEMENTS);
......@@ -6150,6 +6212,13 @@ public class Parser {
return p;
}
private Prepared parseWithQuery() {
Query query = parseSelectUnion();
query.setPrepareAlways(true);
query.setNeverLazy(true);
return query;
}
private TableView parseSingleCommonTableExpression(boolean isTemporary) {
String cteViewName = readIdentifierWithSchema();
Schema schema = getSchema();
......@@ -6328,7 +6397,7 @@ public class Parser {
}
private Prepared parseAlter() {
if (readIf("TABLE")) {
if (readIf(TABLE)) {
return parseAlterTable();
} else if (readIf("USER")) {
return parseAlterUser();
......@@ -6864,7 +6933,7 @@ public class Parser {
schemaNames.add(readUniqueIdentifier());
} while (readIf(COMMA));
command.setSchemaNames(schemaNames);
} else if (readIf("TABLE")) {
} else if (readIf(TABLE)) {
ArrayList<Table> tables = Utils.newSmallArrayList();
do {
tables.add(readTableOrView());
......@@ -7506,7 +7575,7 @@ public class Parser {
private CreateLinkedTable parseCreateLinkedTable(boolean temp,
boolean globalTemp, boolean force) {
read("TABLE");
read(TABLE);
boolean ifNotExists = readIfNotExists();
String tableName = readIdentifierWithSchema();
CreateLinkedTable command = new CreateLinkedTable(session, getSchema());
......
......@@ -110,8 +110,8 @@ public class FullText {
stat.execute("CREATE SCHEMA IF NOT EXISTS " + SCHEMA);
stat.execute("CREATE TABLE IF NOT EXISTS " + SCHEMA +
".INDEXES(ID INT AUTO_INCREMENT PRIMARY KEY, " +
"SCHEMA VARCHAR, TABLE VARCHAR, COLUMNS VARCHAR, " +
"UNIQUE(SCHEMA, TABLE))");
"SCHEMA VARCHAR, \"TABLE\" VARCHAR, COLUMNS VARCHAR, " +
"UNIQUE(SCHEMA, \"TABLE\"))");
stat.execute("CREATE TABLE IF NOT EXISTS " + SCHEMA +
".WORDS(ID INT AUTO_INCREMENT PRIMARY KEY, " +
"NAME VARCHAR, UNIQUE(NAME))");
......@@ -176,7 +176,7 @@ public class FullText {
String table, String columnList) throws SQLException {
init(conn);
PreparedStatement prep = conn.prepareStatement("INSERT INTO " + SCHEMA
+ ".INDEXES(SCHEMA, TABLE, COLUMNS) VALUES(?, ?, ?)");
+ ".INDEXES(SCHEMA, \"TABLE\", COLUMNS) VALUES(?, ?, ?)");
prep.setString(1, schema);
prep.setString(2, table);
prep.setString(3, columnList);
......@@ -221,7 +221,7 @@ public class FullText {
throws SQLException {
init(conn);
PreparedStatement prep = conn.prepareStatement("SELECT ID FROM " + SCHEMA
+ ".INDEXES WHERE SCHEMA=? AND TABLE=?");
+ ".INDEXES WHERE SCHEMA=? AND \"TABLE\"=?");
prep.setString(1, schema);
prep.setString(2, table);
ResultSet rs = prep.executeQuery();
......@@ -929,7 +929,7 @@ public class FullText {
ArrayList<String> indexList = Utils.newSmallArrayList();
PreparedStatement prep = conn.prepareStatement(
"SELECT ID, COLUMNS FROM " + SCHEMA + ".INDEXES" +
" WHERE SCHEMA=? AND TABLE=?");
" WHERE SCHEMA=? AND \"TABLE\"=?");
prep.setString(1, schemaName);
prep.setString(2, tableName);
rs = prep.executeQuery();
......
......@@ -114,8 +114,8 @@ public class FullTextLucene extends FullText {
try (Statement stat = conn.createStatement()) {
stat.execute("CREATE SCHEMA IF NOT EXISTS " + SCHEMA);
stat.execute("CREATE TABLE IF NOT EXISTS " + SCHEMA +
".INDEXES(SCHEMA VARCHAR, TABLE VARCHAR, " +
"COLUMNS VARCHAR, PRIMARY KEY(SCHEMA, TABLE))");
".INDEXES(SCHEMA VARCHAR, \"TABLE\" VARCHAR, " +
"COLUMNS VARCHAR, PRIMARY KEY(SCHEMA, \"TABLE\"))");
stat.execute("CREATE ALIAS IF NOT EXISTS FTL_CREATE_INDEX FOR \"" +
FullTextLucene.class.getName() + ".createIndex\"");
stat.execute("CREATE ALIAS IF NOT EXISTS FTL_DROP_INDEX FOR \"" +
......@@ -144,7 +144,7 @@ public class FullTextLucene extends FullText {
String table, String columnList) throws SQLException {
init(conn);
PreparedStatement prep = conn.prepareStatement("INSERT INTO " + SCHEMA
+ ".INDEXES(SCHEMA, TABLE, COLUMNS) VALUES(?, ?, ?)");
+ ".INDEXES(SCHEMA, \"TABLE\", COLUMNS) VALUES(?, ?, ?)");
prep.setString(1, schema);
prep.setString(2, table);
prep.setString(3, columnList);
......@@ -166,7 +166,7 @@ public class FullTextLucene extends FullText {
init(conn);
PreparedStatement prep = conn.prepareStatement("DELETE FROM " + SCHEMA
+ ".INDEXES WHERE SCHEMA=? AND TABLE=?");
+ ".INDEXES WHERE SCHEMA=? AND \"TABLE\"=?");
prep.setString(1, schema);
prep.setString(2, table);
int rowCount = prep.executeUpdate();
......@@ -548,7 +548,7 @@ public class FullTextLucene extends FullText {
ArrayList<String> indexList = Utils.newSmallArrayList();
PreparedStatement prep = conn.prepareStatement(
"SELECT COLUMNS FROM " + SCHEMA
+ ".INDEXES WHERE SCHEMA=? AND TABLE=?");
+ ".INDEXES WHERE SCHEMA=? AND \"TABLE\"=?");
prep.setString(1, schemaName);
prep.setString(2, tableName);
rs = prep.executeQuery();
......
......@@ -1553,8 +1553,8 @@ public class JdbcDatabaseMetaData extends TraceObject implements
* IF, INNER, INTERSECT, INTERSECTS, INTERVAL, IS, JOIN, LIKE,
* LIMIT, LOCALTIME, LOCALTIMESTAMP, MINUS, NATURAL, NOT, NULL,
* OFFSET, ON, ORDER, PRIMARY, ROW, ROWNUM, SELECT, SYSDATE,
* SYSTIME, SYSTIMESTAMP, TODAY, TOP, TRUE, UNION, UNIQUE, VALUES,
* WHERE, WINDOW, WITH
* SYSTIME, SYSTIMESTAMP, TABLE, TODAY, TOP, TRUE, UNION, UNIQUE,
* VALUES, WHERE, WINDOW, WITH
* </pre>
*
* @return a list of additional the keywords
......
......@@ -139,10 +139,10 @@ public class LobStorageBackend implements LobStorageInterface {
}
if (create) {
stat.execute("CREATE CACHED TABLE IF NOT EXISTS " + LOBS +
"(ID BIGINT PRIMARY KEY, BYTE_COUNT BIGINT, TABLE INT) HIDDEN");
"(ID BIGINT PRIMARY KEY, BYTE_COUNT BIGINT, \"TABLE\" INT) HIDDEN");
stat.execute("CREATE INDEX IF NOT EXISTS " +
"INFORMATION_SCHEMA.INDEX_LOB_TABLE ON " +
LOBS + "(TABLE)");
LOBS + "(\"TABLE\")");
stat.execute("CREATE CACHED TABLE IF NOT EXISTS " + LOB_MAP +
"(LOB BIGINT, SEQ INT, POS BIGINT, HASH INT, " +
"BLOCK BIGINT, PRIMARY KEY(LOB, SEQ)) HIDDEN");
......@@ -191,7 +191,7 @@ public class LobStorageBackend implements LobStorageInterface {
public void removeAllForTable(int tableId) {
init();
try {
String sql = "SELECT ID FROM " + LOBS + " WHERE TABLE = ?";
String sql = "SELECT ID FROM " + LOBS + " WHERE \"TABLE\" = ?";
PreparedStatement prep = prepare(sql);
prep.setInt(1, tableId);
ResultSet rs = prep.executeQuery();
......@@ -414,7 +414,7 @@ public class LobStorageBackend implements LobStorageInterface {
synchronized (database) {
synchronized (conn.getSession()) {
String sql = "INSERT INTO " + LOBS +
"(ID, BYTE_COUNT, TABLE) VALUES(?, ?, ?)";
"(ID, BYTE_COUNT, \"TABLE\") VALUES(?, ?, ?)";
PreparedStatement prep = prepare(sql);
prep.setLong(1, lobId);
prep.setLong(2, byteCount);
......@@ -456,7 +456,7 @@ public class LobStorageBackend implements LobStorageInterface {
reuse(sql, prep);
sql = "INSERT INTO " + LOBS +
"(ID, BYTE_COUNT, TABLE) " +
"(ID, BYTE_COUNT, \"TABLE\") " +
"SELECT ?, BYTE_COUNT, ? FROM " + LOBS +
" WHERE ID = ?";
prep = prepare(sql);
......
......@@ -232,10 +232,15 @@ public class ParserUtil {
*/
public static final int SELECT = ROWNUM + 1;
/**
* The token "TABLE".
*/
public static final int TABLE = SELECT + 1;
/**
* The token "TRUE".
*/
public static final int TRUE = SELECT + 1;
public static final int TRUE = TABLE + 1;
/**
* The token "UNION".
......@@ -490,7 +495,9 @@ public class ParserUtil {
}
return IDENTIFIER;
case 'T':
if (eq("TRUE", s, ignoreCase, start, end)) {
if (eq("TABLE", s, ignoreCase, start, end)) {
return TABLE;
} else if (eq("TRUE", s, ignoreCase, start, end)) {
return TRUE;
}
if (additionalKeywords) {
......
......@@ -155,7 +155,7 @@ public class TestScript extends TestDb {
testScript("ddl/" + s + ".sql");
}
for (String s : new String[] { "delete", "error_reporting", "insertIgnore", "merge", "mergeUsing", "replace",
"script", "select", "show", "with" }) {
"script", "select", "show", "table", "with" }) {
testScript("dml/" + s + ".sql");
}
for (String s : new String[] { "help" }) {
......
-- 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
--
CREATE TABLE TEST(A INT, B INT, C INT);
> ok
INSERT INTO TEST VALUES (1, 1, 1), (1, 1, 2), (1, 1, 3), (1, 2, 1), (1, 2, 2), (1, 2, 3),
(2, 1, 1), (2, 1, 2), (2, 1, 3), (2, 2, 1), (2, 2, 2), (2, 2, 3);
> update count: 12
TABLE TEST ORDER BY A, B;
> A B C
> - - -
> 1 1 1
> 1 1 2
> 1 1 3
> 1 2 1
> 1 2 2
> 1 2 3
> 2 1 1
> 2 1 2
> 2 1 3
> 2 2 1
> 2 2 2
> 2 2 3
> rows (partially ordered): 12
TABLE TEST ORDER BY A, B, C FETCH FIRST 4 ROWS ONLY;
> A B C
> - - -
> 1 1 1
> 1 1 2
> 1 1 3
> 1 2 1
> rows (ordered): 4
SELECT * FROM (TABLE TEST) ORDER BY A, B, C FETCH FIRST ROW ONLY;
> A B C
> - - -
> 1 1 1
> rows (ordered): 1
SELECT (1, 2, 3) IN (TABLE TEST);
>> TRUE
SELECT (TABLE TEST FETCH FIRST ROW ONLY) "ROW";
> ROW
> -------------
> ROW (1, 1, 1)
> rows: 1
DROP TABLE TEST;
> ok
......@@ -130,3 +130,21 @@ WITH CTE_TEST AS (SELECT 1, 2) ((SELECT * FROM CTE_TEST));
> - -
> 1 2
> rows: 1
CREATE TABLE TEST(A INT, B INT) AS SELECT 1, 2;
> ok
WITH CTE_TEST AS (TABLE TEST) ((SELECT * FROM CTE_TEST));
> A B
> - -
> 1 2
> rows: 1
WITH CTE_TEST AS (TABLE TEST) ((TABLE CTE_TEST));
> A B
> - -
> 1 2
> rows: 1
DROP TABLE TEST;
> ok
......@@ -31,11 +31,17 @@ SELECT * FROM (SELECT * FROM TEST) x ORDER BY id;
drop table test;
> ok
call table(id int = (1));
> ID
> --
> 1
> rows: 1
explain select * from table(id int = (1, 2), name varchar=('Hello', 'World'));
>> SELECT TABLE.ID, TABLE.NAME FROM TABLE(ID INT=ROW (1, 2), NAME VARCHAR=ROW ('Hello', 'World')) /* function */
>> SELECT "TABLE".ID, "TABLE".NAME FROM TABLE(ID INT=ROW (1, 2), NAME VARCHAR=ROW ('Hello', 'World')) /* function */
explain select * from table(id int = ARRAY[1, 2], name varchar=ARRAY['Hello', 'World']);
>> SELECT TABLE.ID, TABLE.NAME FROM TABLE(ID INT=ARRAY [1, 2], NAME VARCHAR=ARRAY ['Hello', 'World']) /* function */
>> SELECT "TABLE".ID, "TABLE".NAME FROM TABLE(ID INT=ARRAY [1, 2], NAME VARCHAR=ARRAY ['Hello', 'World']) /* function */
select * from table(id int=(1, 2), name varchar=('Hello', 'World')) x order by id;
> ID NAME
......@@ -43,3 +49,10 @@ select * from table(id int=(1, 2), name varchar=('Hello', 'World')) x order by i
> 1 Hello
> 2 World
> rows (ordered): 2
SELECT * FROM (TABLE(ID INT = (1, 2)));
> ID
> --
> 1
> 2
> rows: 2
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论