提交 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 ...@@ -195,28 +195,30 @@ SHOW TABLES
"Commands (DML)","WITH"," "Commands (DML)","WITH","
WITH [ RECURSIVE ] { name [( columnName [,...] )] WITH [ RECURSIVE ] { name [( columnName [,...] )]
AS ( select ) [,...] } AS ( select ) [,...] }
select { select | insert | update | merge | delete | createTable }
"," ","
Can be used to create a recursive or non-recursive query (common table expression). 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. For recursive queries the first select has to be a UNION.
One or more common table entries can be referred to by name. 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. 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 SELECT 1
UNION ALL UNION ALL
SELECT n + 1 SELECT n + 1
FROM t FROM cte
WHERE n < 100 WHERE n < 100
) )
SELECT sum(n) FROM t; SELECT sum(n) FROM cte;
","
WITH t1 AS ( Example 2:
WITH cte1 AS (
SELECT 1 AS FIRST_COLUMN SELECT 1 AS FIRST_COLUMN
), t2 AS ( ), cte2 AS (
SELECT FIRST_COLUMN+1 AS FIRST_COLUMN FROM t1 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"," "Commands (DDL)","ALTER INDEX RENAME","
......
...@@ -386,7 +386,7 @@ public abstract class Command implements CommandInterface { ...@@ -386,7 +386,7 @@ public abstract class Command implements CommandInterface {
param.setValue(null, true); param.setValue(null, true);
} }
} }
public void setCleanupCallbacks(List<Runnable> cleanupCallbacks) { public void setCleanupCallbacks(List<Runnable> cleanupCallbacks) {
this.cleanupCallbacks = cleanupCallbacks; this.cleanupCallbacks = cleanupCallbacks;
} }
......
...@@ -192,6 +192,8 @@ public class Parser { ...@@ -192,6 +192,8 @@ public class Parser {
return o1 == o2 ? 0 : compareTableFilters(o1, o2); 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 Database database;
private final Session session; private final Session session;
...@@ -482,8 +484,8 @@ public class Parser { ...@@ -482,8 +484,8 @@ public class Parser {
break; break;
case 'w': case 'w':
case 'W': case 'W':
if (isToken("WITH")) { if (readIf("WITH")) {
c = parseSelect(); c = parseWithStatementOrQuery();
} }
break; break;
case ';': case ';':
...@@ -1758,6 +1760,21 @@ public class Parser { ...@@ -1758,6 +1760,21 @@ public class Parser {
return command; 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() { private Query parseSelectUnion() {
int start = lastParseIndex; int start = lastParseIndex;
Query command = parseSelectSub(); Query command = parseSelectSub();
...@@ -1940,7 +1957,14 @@ public class Parser { ...@@ -1940,7 +1957,14 @@ public class Parser {
return command; return command;
} }
if (readIf("WITH")) { 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 // recursive can not be lazy
query.setNeverLazy(true); query.setNeverLazy(true);
return query; return query;
...@@ -4890,16 +4914,53 @@ public class Parser { ...@@ -4890,16 +4914,53 @@ public class Parser {
return command; return command;
} }
private Query parseWith() { private Prepared parseWith() {
List<TableView> viewsCreated = new ArrayList<TableView>(); List<TableView> viewsCreated = new ArrayList<TableView>();
readIf("RECURSIVE"); readIf("RECURSIVE");
do { do {
viewsCreated.add(parseSingleCommonTableExpression()); viewsCreated.add(parseSingleCommonTableExpression());
} while (readIf(",")); } while (readIf(","));
Query q = parseSelectUnion(); Prepared p = null;
q.setPrepareAlways(true);
List<Runnable> cleanupCallbacks = new ArrayList<Runnable>(); 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) // clean up temp views starting with last to first (in case of dependencies)
Collections.reverse(viewsCreated); Collections.reverse(viewsCreated);
...@@ -4918,8 +4979,8 @@ public class Parser { ...@@ -4918,8 +4979,8 @@ public class Parser {
} }
}); });
} }
q.setCleanupCallbacks(cleanupCallbacks); p.setCleanupCallbacks(cleanupCallbacks);
return q; return p;
} }
private TableView parseSingleCommonTableExpression() { private TableView parseSingleCommonTableExpression() {
......
...@@ -29,14 +29,20 @@ public class TestGeneralCommonTableQueries extends TestBase { ...@@ -29,14 +29,20 @@ public class TestGeneralCommonTableQueries extends TestBase {
@Override @Override
public void test() throws Exception { public void test() throws Exception {
testSimple(); testSimpleSelect();
testImpliedColumnNames(); testImpliedColumnNames();
testChainedQuery(); testChainedQuery();
testParameterizedQuery(); testParameterizedQuery();
testNumberedParameterizedQuery(); testNumberedParameterizedQuery();
testInsert();
testUpdate();
testDelete();
testMerge();
testCreateTable();
} }
private void testSimple() throws Exception { private void testSimpleSelect() throws Exception {
deleteDb("commonTableExpressionQueries"); deleteDb("commonTableExpressionQueries");
Connection conn = getConnection("commonTableExpressionQueries"); Connection conn = getConnection("commonTableExpressionQueries");
Statement stat; Statement stat;
...@@ -215,16 +221,171 @@ public class TestGeneralCommonTableQueries extends TestBase { ...@@ -215,16 +221,171 @@ public class TestGeneralCommonTableQueries extends TestBase {
assertFalse(rs.next()); assertFalse(rs.next());
try{ 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(); 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){ catch(JdbcSQLException e){
// ensure the T1 table has been removed even without auto commit // ensure the T1 table has been removed even without auto commit
assertContains(e.getMessage(),"Table \"T1\" not found;"); assertContains(e.getMessage(),"Table \"T1\" not found;");
} }
conn.close(); conn.close();
deleteDb("commonTableExpressionQueries"); 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 ...@@ -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 brought brown browse browser browsers brute brvbar bsdiff bson bsr btc btree
btrfs bucher bucket buckets buddha buf buff buffer buffered buffering buffers bug btrfs bucher bucket buckets buddha buf buff buffer buffered buffering buffers bug
bugfix bugfixes buggy bugs build builder building builds built bukkit bulk bull 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 bypassing byte bytea bytecode bytes bzip cabinet cacao cachable cache cacheable
cached caches caching cafe cajun cal calculate calculated calculates calculating cached caches caching cafe cajun cal calculate calculated calculates calculating
calculation calculations calendar calendars call callable callback callbacks calculation calculations calendar calendars call callable callback callbacks
...@@ -449,7 +449,7 @@ originate originates originating originator orion orld orm orphan orphaned ...@@ -449,7 +449,7 @@ originate originates originating originator orion orld orm orphan orphaned
orphans osde osgi oslash osmond other others otherwise otilde otimes otterstrom orphans osde osgi oslash osmond other others otherwise otilde otimes otterstrom
ought ouml our out outback outdated outer outfile outline outln outperforms ought ouml our out outback outdated outer outfile outline outln outperforms
output outset outside outstanding over overall overcareful overflow overflows 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 override overrides overtakes overtaking overview overwrite overwrites overwriting
overwritten overwrote owl own ownable owned owner owners ownership owns oymaurice overwritten overwrote owl own ownable owned owner owners ownership owns oymaurice
pacific pack package packages packaging packets pad padded padding page paged pacific pack package packages packaging packets pad padded padding page paged
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论