提交 62b941ed authored 作者: Owner's avatar Owner

Issue#576 Add support for insert,update,merge,delete and create table statemets in WITH statement

上级 02e67c53
......@@ -195,28 +195,30 @@ SHOW TABLES
"Commands (DML)","WITH","
WITH [ RECURSIVE ] { name [( columnName [,...] )]
AS ( select ) [,...] }
select
{ select | insert | update | merge | delete | createTable }
","
Can be used to create a recursive or non-recursive query (common table expression).
For recursive queries the first select has to be a UNION.
One or more common table entries can be referred to by name.
Column name declarations are now optional - the column names will be inferred from the named select queries.
The final action in a WITH statement can be a select , insert , update , merge , delete or create table.
","
WITH RECURSIVE t(n) AS (
WITH RECURSIVE cte(n) AS (
SELECT 1
UNION ALL
SELECT n + 1
FROM t
FROM cte
WHERE n < 100
)
SELECT sum(n) FROM t;
","
WITH t1 AS (
SELECT sum(n) FROM cte;
Example 2:
WITH cte1 AS (
SELECT 1 AS FIRST_COLUMN
), t2 AS (
SELECT FIRST_COLUMN+1 AS FIRST_COLUMN FROM t1
), cte2 AS (
SELECT FIRST_COLUMN+1 AS FIRST_COLUMN FROM cte1
)
SELECT sum(FIRST_COLUMN) FROM t2;
SELECT sum(FIRST_COLUMN) FROM cte2;
"
"Commands (DDL)","ALTER INDEX RENAME","
......
......@@ -386,7 +386,7 @@ public abstract class Command implements CommandInterface {
param.setValue(null, true);
}
}
public void setCleanupCallbacks(List<Runnable> cleanupCallbacks) {
this.cleanupCallbacks = cleanupCallbacks;
}
......
......@@ -192,6 +192,8 @@ public class Parser {
return o1 == o2 ? 0 : compareTableFilters(o1, o2);
}
};
public static final String WITH_STATEMENT_SUPPORTS_LIMITED_STATEMENTS =
"WITH statement supports only SELECT, CREATE TABLE, INSERT, UPDATE, MERGE or DELETE statements";
private final Database database;
private final Session session;
......@@ -482,8 +484,8 @@ public class Parser {
break;
case 'w':
case 'W':
if (isToken("WITH")) {
c = parseSelect();
if (readIf("WITH")) {
c = parseWithStatementOrQuery();
}
break;
case ';':
......@@ -1758,6 +1760,21 @@ public class Parser {
return command;
}
private Prepared parseWithStatementOrQuery() {
int paramIndex = parameters.size();
Prepared command = parseWith();
ArrayList<Parameter> params = New.arrayList();
for (int i = paramIndex, size = parameters.size(); i < size; i++) {
params.add(parameters.get(i));
}
command.setParameterList(params);
if(command instanceof Query){
Query query = (Query) command;
query.init();
}
return command;
}
private Query parseSelectUnion() {
int start = lastParseIndex;
Query command = parseSelectSub();
......@@ -1940,7 +1957,14 @@ public class Parser {
return command;
}
if (readIf("WITH")) {
Query query = parseWith();
Query query = null;
try {
query = (Query) parseWith();
}
catch(ClassCastException e){
throw DbException.get(ErrorCode.SYNTAX_ERROR_1,
"WITH statement supports only SELECT (query) in this context");
}
// recursive can not be lazy
query.setNeverLazy(true);
return query;
......@@ -4890,16 +4914,53 @@ public class Parser {
return command;
}
private Query parseWith() {
private Prepared parseWith() {
List<TableView> viewsCreated = new ArrayList<TableView>();
readIf("RECURSIVE");
do {
viewsCreated.add(parseSingleCommonTableExpression());
} while (readIf(","));
Query q = parseSelectUnion();
q.setPrepareAlways(true);
List<Runnable> cleanupCallbacks = new ArrayList<Runnable>();
Prepared p = null;
if(isToken("SELECT")) {
Query query = parseSelectUnion();
query.setPrepareAlways(true);
query.setNeverLazy(true);
p = query;
}
else if(readIf("INSERT")) {
p = parseInsert();
p.setPrepareAlways(true);
}
else if(readIf("UPDATE")) {
p = parseUpdate();
p.setPrepareAlways(true);
}
else if(readIf("MERGE")) {
p = parseMerge();
p.setPrepareAlways(true);
}
else if(readIf("DELETE")) {
p = parseDelete();
p.setPrepareAlways(true);
}
else if(readIf("CREATE")) {
if (!isToken("TABLE")){
throw DbException.get(ErrorCode.SYNTAX_ERROR_1,
WITH_STATEMENT_SUPPORTS_LIMITED_STATEMENTS);
}
p = parseCreate();
p.setPrepareAlways(true);
}
else {
throw DbException.get(ErrorCode.SYNTAX_ERROR_1,
WITH_STATEMENT_SUPPORTS_LIMITED_STATEMENTS);
}
List<Runnable> cleanupCallbacks = new ArrayList<Runnable>();
// clean up temp views starting with last to first (in case of dependencies)
Collections.reverse(viewsCreated);
......@@ -4918,8 +4979,8 @@ public class Parser {
}
});
}
q.setCleanupCallbacks(cleanupCallbacks);
return q;
p.setCleanupCallbacks(cleanupCallbacks);
return p;
}
private TableView parseSingleCommonTableExpression() {
......
......@@ -29,14 +29,20 @@ public class TestGeneralCommonTableQueries extends TestBase {
@Override
public void test() throws Exception {
testSimple();
testSimpleSelect();
testImpliedColumnNames();
testChainedQuery();
testParameterizedQuery();
testNumberedParameterizedQuery();
testInsert();
testUpdate();
testDelete();
testMerge();
testCreateTable();
}
private void testSimple() throws Exception {
private void testSimpleSelect() throws Exception {
deleteDb("commonTableExpressionQueries");
Connection conn = getConnection("commonTableExpressionQueries");
Statement stat;
......@@ -215,16 +221,171 @@ public class TestGeneralCommonTableQueries extends TestBase {
assertFalse(rs.next());
try{
prep = conn.prepareStatement("SELECT * FROM t1 UNION ALL SELECT * FROM t2 UNION ALL SELECT X, 'Q' FROM SYSTEM_RANGE(5,6)");
prep = conn.prepareStatement("SELECT * FROM t1 UNION ALL SELECT * FROM t2 "+
"UNION ALL SELECT X, 'Q' FROM SYSTEM_RANGE(5,6)");
rs = prep.executeQuery();
fail("Temp view T1 was accessible after previous WITH statement finished - but should not have been.");
fail("Temp view T1 was accessible after previous WITH statement finished "+
"- but should not have been.");
}
catch(JdbcSQLException e){
// ensure the T1 table has been removed even without auto commit
assertContains(e.getMessage(),"Table \"T1\" not found;");
// ensure the T1 table has been removed even without auto commit
assertContains(e.getMessage(),"Table \"T1\" not found;");
}
conn.close();
deleteDb("commonTableExpressionQueries");
}
}
private void testInsert() throws Exception {
deleteDb("commonTableExpressionQueries");
Connection conn = getConnection("commonTableExpressionQueries");
Statement stat;
PreparedStatement prep;
ResultSet rs;
int rowCount;
stat = conn.createStatement();
stat.execute("CREATE TABLE T1 ( ID INT IDENTITY, X INT NULL, Y VARCHAR(100) NULL )");
prep = conn.prepareStatement("WITH v1 AS ("
+ " SELECT R.X, 'X1' AS Y FROM SYSTEM_RANGE(?1,?2) R"
+ ")"
+ "INSERT INTO T1 (X,Y) SELECT v1.X, v1.Y FROM v1");
prep.setInt(1, 1);
prep.setInt(2, 2);
rowCount = prep.executeUpdate();
assertEquals(2, rowCount);
rs = stat.executeQuery("SELECT ID, X,Y FROM T1");
for (int n : new int[]{1, 2}) {
assertTrue(rs.next());
assertTrue(rs.getInt(1) != 0);
assertEquals(n, rs.getInt(2));
assertEquals("X1", rs.getString(3));
}
conn.close();
deleteDb("commonTableExpressionQueries");
}
private void testUpdate() throws Exception {
deleteDb("commonTableExpressionQueries");
Connection conn = getConnection("commonTableExpressionQueries");
Statement stat;
PreparedStatement prep;
ResultSet rs;
int rowCount;
stat = conn.createStatement();
stat.execute("CREATE TABLE IF NOT EXISTS T1 AS SELECT R.X AS ID, R.X, 'X1' AS Y FROM SYSTEM_RANGE(1,2) R");
prep = conn.prepareStatement("WITH v1 AS ("
+" SELECT R.X, 'X1' AS Y FROM SYSTEM_RANGE(?1,?2) R"
+")"
+"UPDATE T1 SET Y = 'Y1' WHERE X IN ( SELECT v1.X FROM v1 )");
prep.setInt(1, 1);
prep.setInt(2, 2);
rowCount = prep.executeUpdate();
assertEquals(2,rowCount);
rs = stat.executeQuery("SELECT ID, X,Y FROM T1");
for (int n : new int[] { 1, 2 }) {
assertTrue(rs.next());
assertTrue(rs.getInt(1)!=0);
assertEquals(n, rs.getInt(2));
assertEquals("Y1", rs.getString(3));
}
conn.close();
deleteDb("commonTableExpressionQueries");
}
private void testDelete() throws Exception {
deleteDb("commonTableExpressionQueries");
Connection conn = getConnection("commonTableExpressionQueries");
Statement stat;
PreparedStatement prep;
ResultSet rs;
int rowCount;
stat = conn.createStatement();
stat.execute("CREATE TABLE IF NOT EXISTS T1 AS SELECT R.X AS ID, R.X, 'X1' AS Y FROM SYSTEM_RANGE(1,2) R");
prep = conn.prepareStatement("WITH v1 AS ("
+" SELECT R.X, 'X1' AS Y FROM SYSTEM_RANGE(1,2) R"
+")"
+"DELETE FROM T1 WHERE X IN ( SELECT v1.X FROM v1 )");
rowCount = prep.executeUpdate();
assertEquals(2,rowCount);
rs = stat.executeQuery("SELECT ID, X,Y FROM T1");
assertFalse(rs.next());
conn.close();
deleteDb("commonTableExpressionQueries");
}
private void testMerge() throws Exception {
deleteDb("commonTableExpressionQueries");
Connection conn = getConnection("commonTableExpressionQueries");
Statement stat;
PreparedStatement prep;
ResultSet rs;
int rowCount;
stat = conn.createStatement();
stat.execute("CREATE TABLE IF NOT EXISTS T1 AS SELECT R.X AS ID, R.X, 'X1' AS Y FROM SYSTEM_RANGE(1,2) R");
prep = conn.prepareStatement("WITH v1 AS ("
+" SELECT R.X, 'X1' AS Y FROM SYSTEM_RANGE(1,3) R"
+")"
+"MERGE INTO T1 KEY(ID) SELECT v1.X AS ID, v1.X, v1.Y FROM v1");
rowCount = prep.executeUpdate();
assertEquals(3,rowCount);
rs = stat.executeQuery("SELECT ID, X,Y FROM T1");
for (int n : new int[] { 1, 2, 3 }) {
assertTrue(rs.next());
assertTrue(rs.getInt(1)!=0);
assertEquals(n, rs.getInt(2));
assertEquals("X1", rs.getString(3));
}
conn.close();
deleteDb("commonTableExpressionQueries");
}
private void testCreateTable() throws Exception {
deleteDb("commonTableExpressionQueries");
Connection conn = getConnection("commonTableExpressionQueries");
Statement stat;
PreparedStatement prep;
ResultSet rs;
boolean success;
stat = conn.createStatement();
prep = conn.prepareStatement("WITH v1 AS ("
+" SELECT R.X, 'X1' AS Y FROM SYSTEM_RANGE(1,3) R"
+")"
+"CREATE TABLE IF NOT EXISTS T1 AS SELECT v1.X AS ID, v1.X, v1.Y FROM v1");
success = prep.execute();
assertEquals(false,success);
rs = stat.executeQuery("SELECT ID, X,Y FROM T1");
for (int n : new int[] { 1, 2, 3 }) {
assertTrue(rs.next());
assertTrue(rs.getInt(1)!=0);
assertEquals(n, rs.getInt(2));
assertEquals("X1", rs.getString(3));
}
conn.close();
deleteDb("commonTableExpressionQueries");
}
}
\ No newline at end of file
......@@ -69,7 +69,7 @@ breadth break breaking breaks bridge bring brings brittain broke broken broker
brought brown browse browser browsers brute brvbar bsdiff bson bsr btc btree
btrfs bucher bucket buckets buddha buf buff buffer buffered buffering buffers bug
bugfix bugfixes buggy bugs build builder building builds built bukkit bulk bull
bundle bundled bundles bungisoft burden business busy but button bxor bye
builtin bundle bundled bundles bungisoft burden business busy but button bxor bye
bypassing byte bytea bytecode bytes bzip cabinet cacao cachable cache cacheable
cached caches caching cafe cajun cal calculate calculated calculates calculating
calculation calculations calendar calendars call callable callback callbacks
......@@ -449,7 +449,7 @@ originate originates originating originator orion orld orm orphan orphaned
orphans osde osgi oslash osmond other others otherwise otilde otimes otterstrom
ought ouml our out outback outdated outer outfile outline outln outperforms
output outset outside outstanding over overall overcareful overflow overflows
overhead overlap overlapping overlaps overload overloaded overloading overridden
overhead overlap overlapping overlaps overload overloaded overloading overridden overriding
override overrides overtakes overtaking overview overwrite overwrites overwriting
overwritten overwrote owl own ownable owned owner owners ownership owns oymaurice
pacific pack package packages packaging packets pad padded padding page paged
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论