提交 bf03bc6d authored 作者: Owner's avatar Owner

Found & fixed bug in column types from persisted store.

上级 b2e546f3
...@@ -1051,7 +1051,7 @@ public class Parser { ...@@ -1051,7 +1051,7 @@ public class Parser {
} }
} }
private static Prepared prepare(Session s, String sql, public static Prepared prepare(Session s, String sql,
ArrayList<Value> paramValues) { ArrayList<Value> paramValues) {
Prepared prep = s.prepare(sql); Prepared prep = s.prepare(sql);
ArrayList<Parameter> params = prep.getParameters(); ArrayList<Parameter> params = prep.getParameters();
...@@ -5169,6 +5169,7 @@ public class Parser { ...@@ -5169,6 +5169,7 @@ public class Parser {
// as in CREATE VIEW abc AS WITH my_cte - this auto detects that condition // as in CREATE VIEW abc AS WITH my_cte - this auto detects that condition
if(session.isParsingView()){ if(session.isParsingView()){
isPersistent = true; isPersistent = true;
System.out.println("getParsingViewName="+session.getParsingViewName());
} }
do { do {
...@@ -5360,7 +5361,7 @@ public class Parser { ...@@ -5360,7 +5361,7 @@ public class Parser {
* Query object * Query object
* @return a list of column object returned by withQuery * @return a list of column object returned by withQuery
*/ */
private static List<Column> createQueryColumnTemplateList(String[] cols, public static List<Column> createQueryColumnTemplateList(String[] cols,
Query theQuery, String[] querySQLOutput) { Query theQuery, String[] querySQLOutput) {
List<Column> columnTemplateList = new ArrayList<>(); List<Column> columnTemplateList = new ArrayList<>();
theQuery.prepare(); theQuery.prepare();
......
...@@ -79,7 +79,8 @@ import org.h2.value.Value; ...@@ -79,7 +79,8 @@ import org.h2.value.Value;
* 4) Previously if neither UPDATE or DELETE clause is supplied, but INSERT is supplied - the INSERT * 4) Previously if neither UPDATE or DELETE clause is supplied, but INSERT is supplied - the INSERT
* action is always triggered. This is because the embedded UPDATE and DELETE statement's * action is always triggered. This is because the embedded UPDATE and DELETE statement's
* returned update row count is used to detect a matching join. * returned update row count is used to detect a matching join.
* If neither of the two the statements are provided, no matching join is EVER detected. * If neither of the two the statements are provided, no matching join is NEVER detected.
*
* A fix for this is now implemented as described below: * A fix for this is now implemented as described below:
* We now generate a "matchSelect" query and use that to always detect * We now generate a "matchSelect" query and use that to always detect
* a match join - rather than relying on UPDATE or DELETE statements. * a match join - rather than relying on UPDATE or DELETE statements.
......
...@@ -11,6 +11,7 @@ import java.util.HashSet; ...@@ -11,6 +11,7 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.command.Parser;
import org.h2.command.Prepared; import org.h2.command.Prepared;
import org.h2.command.dml.Query; import org.h2.command.dml.Query;
import org.h2.engine.Constants; import org.h2.engine.Constants;
...@@ -713,9 +714,32 @@ public class TableView extends Table { ...@@ -713,9 +714,32 @@ public class TableView extends Table {
ArrayList<Parameter> parameters, Column[] columnTemplates, Session session, ArrayList<Parameter> parameters, Column[] columnTemplates, Session session,
boolean literalsChecked, boolean isTableExpression, boolean isPersistent, Database db){ boolean literalsChecked, boolean isTableExpression, boolean isPersistent, Database db){
Table recursiveTable = Parser.createShadowTableForRecursiveTableExpression(isPersistent, session, name,
schema, Arrays.asList(columnTemplates), db);
List<Column> columnTemplateList;
String[] querySQLOutput = new String[]{null};
ArrayList<String> columnNames = new ArrayList<String>();
for(Column columnTemplate: columnTemplates){
columnNames.add(columnTemplate.getName());
}
try {
Prepared withQuery = session.prepare(querySQL, false, false);
if(isPersistent){
withQuery.setSession(session);
}
columnTemplateList = Parser.createQueryColumnTemplateList(columnNames.toArray(new String[1]),
(Query) withQuery, querySQLOutput);
} finally {
Parser.destroyShadowTableForRecursiveExpression(isPersistent, session, recursiveTable);
}
// build with recursion turned on // build with recursion turned on
TableView view = new TableView(schema, id, name, querySQL, TableView view = new TableView(schema, id, name, querySQL,
parameters, columnTemplates, session, parameters, columnTemplateList.toArray(columnTemplates), session,
true/* try recursive */, literalsChecked, isTableExpression, isPersistent ); true/* try recursive */, literalsChecked, isTableExpression, isPersistent );
// is recursion really detected ? if not - recreate it without recursion flag and no recursive index // is recursion really detected ? if not - recreate it without recursion flag and no recursive index
......
...@@ -10,7 +10,7 @@ import org.h2.test.TestBase; ...@@ -10,7 +10,7 @@ import org.h2.test.TestBase;
public abstract class AbstractBaseForCommonTableExpressions extends TestBase { public abstract class AbstractBaseForCommonTableExpressions extends TestBase {
protected void testRepeatedQueryWithSetup(int maxRetries, String[] expectedRowData, String[] expectedColumnNames, int expectedNumbeOfRows, String SETUP_SQL, protected void testRepeatedQueryWithSetup(int maxRetries, String[] expectedRowData, String[] expectedColumnNames, int expectedNumbeOfRows, String SETUP_SQL,
String WITH_QUERY, int closeAndReopenDatabaseConnectionOnIteration) throws SQLException { String WITH_QUERY, int closeAndReopenDatabaseConnectionOnIteration, String[] expectedColumnTypes) throws SQLException {
deleteDb("commonTableExpressionQueries"); deleteDb("commonTableExpressionQueries");
Connection conn = getConnection("commonTableExpressionQueries"); Connection conn = getConnection("commonTableExpressionQueries");
...@@ -37,6 +37,8 @@ public abstract class AbstractBaseForCommonTableExpressions extends TestBase { ...@@ -37,6 +37,8 @@ public abstract class AbstractBaseForCommonTableExpressions extends TestBase {
assertTrue(rs.getMetaData().getColumnLabel(columnIndex)!=null); assertTrue(rs.getMetaData().getColumnLabel(columnIndex)!=null);
assertEquals(expectedColumnNames[columnIndex-1],rs.getMetaData().getColumnLabel(columnIndex)); assertEquals(expectedColumnNames[columnIndex-1],rs.getMetaData().getColumnLabel(columnIndex));
assertEquals("wrongly type column "+rs.getMetaData().getColumnLabel(columnIndex)+" on iteration#"+queryRunTries,
expectedColumnTypes[columnIndex-1],rs.getMetaData().getColumnTypeName(columnIndex));
} }
int rowNdx=0; int rowNdx=0;
......
...@@ -41,7 +41,7 @@ public class TestGeneralCommonTableQueries extends AbstractBaseForCommonTableExp ...@@ -41,7 +41,7 @@ public class TestGeneralCommonTableQueries extends AbstractBaseForCommonTableExp
testMerge(); testMerge();
testCreateTable(); testCreateTable();
testNestedSQL(); testNestedSQL();
testRecursiveTable(); testLazyQueryExecutionAndRecursiveTable();
} }
private void testSimpleSelect() throws Exception { private void testSimpleSelect() throws Exception {
...@@ -469,66 +469,27 @@ public class TestGeneralCommonTableQueries extends AbstractBaseForCommonTableExp ...@@ -469,66 +469,27 @@ public class TestGeneralCommonTableQueries extends AbstractBaseForCommonTableExp
conn.close(); conn.close();
deleteDb("commonTableExpressionQueries"); deleteDb("commonTableExpressionQueries");
} }
private void testRecursiveTable() throws Exception {
String[] expectedRowData =new String[]{"|meat|null","|fruit|3","|veg|2"}; private void testLazyQueryExecutionAndRecursiveTable() throws Exception {
String[] expectedColumnNames =new String[]{"VAL",
"SUM(SELECT\n" + String[] expectedRowData =new String[]{"|1","|2","|3"};
" X\n" + String[] expectedColumnTypes =new String[]{"INTEGER"};
"FROM PUBLIC.\"\" BB\n" + String[] expectedColumnNames =new String[]{"N"};
" /* SELECT\n" + //Test lazy mvStore memory mvcc multiThreaded
" SUM(1) AS X,\n" + String SETUP_SQL = "SET LAZY_QUERY_EXECUTION 1;\n"
" A\n" + //+ "SET MEMORY 1;SET MV_STORE true; SET MVCC TRUE;"
" FROM PUBLIC.B\n" + //+ "SET MULTI_THREADED TRUE;"
" /++ PUBLIC.B.tableScan ++/\n" +
" /++ WHERE A IS ?1\n" +
" ++/\n" +
" /++ scanCount: 4 ++/\n" +
" INNER JOIN PUBLIC.C\n" +
" /++ PUBLIC.C.tableScan ++/\n" +
" ON 1=1\n" +
" WHERE (A IS ?1)\n" +
" AND (B.VAL = C.B)\n" +
" GROUP BY A: A IS A.VAL\n" +
" */\n" +
" /* scanCount: 1 */\n" +
"WHERE BB.A IS A.VAL)"};
String SETUP_SQL =
"DROP TABLE IF EXISTS A; "
+"DROP TABLE IF EXISTS B; "
+"DROP TABLE IF EXISTS C; "
+"CREATE TABLE A(VAL VARCHAR(255)); "
+"CREATE TABLE B(A VARCHAR(255), VAL VARCHAR(255)); "
+"CREATE TABLE C(B VARCHAR(255), VAL VARCHAR(255)); "
+" "
+"INSERT INTO A VALUES('fruit'); "
+"INSERT INTO B VALUES('fruit','apple'); "
+"INSERT INTO B VALUES('fruit','banana'); "
+"INSERT INTO C VALUES('apple', 'golden delicious');"
+"INSERT INTO C VALUES('apple', 'granny smith'); "
+"INSERT INTO C VALUES('apple', 'pippin'); "
+"INSERT INTO A VALUES('veg'); "
+"INSERT INTO B VALUES('veg', 'carrot'); "
+"INSERT INTO C VALUES('carrot', 'nantes'); "
+"INSERT INTO C VALUES('carrot', 'imperator'); "
+"INSERT INTO C VALUES(null, 'banapple'); "
+"INSERT INTO A VALUES('meat'); "
; ;
String WITH_QUERY = "WITH BB as (SELECT \n" + String WITH_QUERY = "with recursive r(n) as (\n"+
"sum(1) as X, \n" + "(select 1) union all (select n+1 from r where n < 3)\n"+
"a \n" + ")\n"+
"FROM B \n" + "select n from r";
"JOIN C ON B.val=C.b \n" +
"GROUP BY a) \n" +
"SELECT \n" +
"A.val, \n" +
"sum(SELECT X FROM BB WHERE BB.a IS A.val)\n" +
"FROM A \n" + "GROUP BY A.val";
int maxRetries = 3; int maxRetries = 3;
int expectedNumberOfRows = expectedRowData.length; int expectedNumberOfRows = expectedRowData.length;
testRepeatedQueryWithSetup(maxRetries, expectedRowData, expectedColumnNames, expectedNumberOfRows, SETUP_SQL, testRepeatedQueryWithSetup(maxRetries, expectedRowData, expectedColumnNames, expectedNumberOfRows, SETUP_SQL,
WITH_QUERY, maxRetries-1); WITH_QUERY, maxRetries-1, expectedColumnTypes);
} }
} }
...@@ -30,12 +30,29 @@ public class TestPersistentCommonTableExpressions extends AbstractBaseForCommonT ...@@ -30,12 +30,29 @@ public class TestPersistentCommonTableExpressions extends AbstractBaseForCommonT
} }
private void testRecursiveTable() throws Exception { private void testRecursiveTable() throws Exception {
int maxRetries = 4;
String[] expectedRowData =new String[]{"|meat|null","|fruit|3","|veg|2"}; String[] expectedRowData =new String[]{"|meat|null","|fruit|3","|veg|2"};
String[] expectedColumnTypes =new String[]{"VARCHAR","DECIMAL"};
String[] expectedColumnNames =new String[]{"VAL", String[] expectedColumnNames =new String[]{"VAL",
"SUM(SELECT\n X\nFROM PUBLIC.\"\" BB\n /* SELECT\n SUM(1) AS X,\n A\n FROM PUBLIC.B\n /++ PUBLIC.B.tableScan ++/\n /++ WHERE A IS ?1\n ++/\n /++ scanCount: 4 ++/\n INNER JOIN PUBLIC.C\n /++ PUBLIC.C.tableScan ++/\n ON 1=1\n WHERE (A IS ?1)\n AND (B.VAL = C.B)\n GROUP BY A: A IS A.VAL\n */\n /* scanCount: 1 */\nWHERE BB.A IS A.VAL)"}; "SUM(SELECT\n" +
int expectedNumberOfRows = 3; " X\n" +
"FROM PUBLIC.\"\" BB\n" +
" /* SELECT\n" +
" SUM(1) AS X,\n" +
" A\n" +
" FROM PUBLIC.B\n" +
" /++ PUBLIC.B.tableScan ++/\n" +
" /++ WHERE A IS ?1\n" +
" ++/\n" +
" /++ scanCount: 4 ++/\n" +
" INNER JOIN PUBLIC.C\n" +
" /++ PUBLIC.C.tableScan ++/\n" +
" ON 1=1\n" +
" WHERE (A IS ?1)\n" +
" AND (B.VAL = C.B)\n" +
" GROUP BY A: A IS A.VAL\n" +
" */\n" +
" /* scanCount: 1 */\n" +
"WHERE BB.A IS A.VAL)"};
String SETUP_SQL = String SETUP_SQL =
"DROP TABLE IF EXISTS A; " "DROP TABLE IF EXISTS A; "
...@@ -58,24 +75,23 @@ public class TestPersistentCommonTableExpressions extends AbstractBaseForCommonT ...@@ -58,24 +75,23 @@ public class TestPersistentCommonTableExpressions extends AbstractBaseForCommonT
+"INSERT INTO C VALUES(null, 'banapple'); " +"INSERT INTO C VALUES(null, 'banapple'); "
+"INSERT INTO A VALUES('meat'); " +"INSERT INTO A VALUES('meat'); "
; ;
String WITH_QUERY = String WITH_QUERY = "WITH BB as (SELECT \n" +
"WITH BB as (SELECT \n" "sum(1) as X, \n" +
+"sum(1) as X, \n" "a \n" +
+"a \n" "FROM B \n" +
+"FROM B \n" "JOIN C ON B.val=C.b \n" +
+"JOIN C ON B.val=C.b \n" "GROUP BY a) \n" +
+"GROUP BY a) \n" "SELECT \n" +
+"SELECT \n" "A.val, \n" +
+"A.val, \n" "sum(SELECT X FROM BB WHERE BB.a IS A.val)\n" +
+"sum(SELECT X FROM BB WHERE BB.a IS A.val)\n" "FROM A \n" + "GROUP BY A.val";
+"FROM A \n" int maxRetries = 3;
+"GROUP BY A.val"; int expectedNumberOfRows = expectedRowData.length;
testRepeatedQueryWithSetup(maxRetries, expectedRowData, expectedColumnNames, expectedNumberOfRows, SETUP_SQL, testRepeatedQueryWithSetup(maxRetries, expectedRowData, expectedColumnNames, expectedNumberOfRows, SETUP_SQL,
WITH_QUERY, maxRetries-1); WITH_QUERY, maxRetries-1, expectedColumnTypes);
}
}
private void testPersistentRecursiveTableInCreateView() throws Exception { private void testPersistentRecursiveTableInCreateView() throws Exception {
String SETUP_SQL = "--SET TRACE_LEVEL_SYSTEM_OUT 3;\n" String SETUP_SQL = "--SET TRACE_LEVEL_SYSTEM_OUT 3;\n"
...@@ -120,10 +136,12 @@ public class TestPersistentCommonTableExpressions extends AbstractBaseForCommonT ...@@ -120,10 +136,12 @@ public class TestPersistentCommonTableExpressions extends AbstractBaseForCommonT
"|1|2|null|121" "|1|2|null|121"
}; };
String[] expectedColumnNames =new String[]{"SUB_TREE_ROOT_ID","TREE_LEVEL","PARENT_FK","CHILD_FK"}; String[] expectedColumnNames =new String[]{"SUB_TREE_ROOT_ID","TREE_LEVEL","PARENT_FK","CHILD_FK"};
String[] expectedColumnTypes =new String[]{"INTEGER","INTEGER","INTEGER","INTEGER"};
int expectedNumberOfRows = 11; int expectedNumberOfRows = 11;
testRepeatedQueryWithSetup(maxRetries, expectedRowData, expectedColumnNames, expectedNumberOfRows, SETUP_SQL, testRepeatedQueryWithSetup(maxRetries, expectedRowData, expectedColumnNames, expectedNumberOfRows, SETUP_SQL,
WITH_QUERY, maxRetries-1); WITH_QUERY, maxRetries-1,expectedColumnTypes);
} }
private void testPersistentNonRecursiveTableInCreateView() throws Exception { private void testPersistentNonRecursiveTableInCreateView() throws Exception {
String SETUP_SQL = "" String SETUP_SQL = ""
+"DROP VIEW IF EXISTS v_my_nr_tree; \n" +"DROP VIEW IF EXISTS v_my_nr_tree; \n"
...@@ -158,8 +176,9 @@ public class TestPersistentCommonTableExpressions extends AbstractBaseForCommonT ...@@ -158,8 +176,9 @@ public class TestPersistentCommonTableExpressions extends AbstractBaseForCommonT
"|121|0|12|121", "|121|0|12|121",
}; };
String[] expectedColumnNames =new String[]{"SUB_TREE_ROOT_ID","TREE_LEVEL","PARENT_FK","CHILD_FK"}; String[] expectedColumnNames =new String[]{"SUB_TREE_ROOT_ID","TREE_LEVEL","PARENT_FK","CHILD_FK"};
String[] expectedColumnTypes =new String[]{"INTEGER","INTEGER","INTEGER","INTEGER"};
int expectedNumberOfRows = 5; int expectedNumberOfRows = 5;
testRepeatedQueryWithSetup(maxRetries, expectedRowData, expectedColumnNames, expectedNumberOfRows, SETUP_SQL, testRepeatedQueryWithSetup(maxRetries, expectedRowData, expectedColumnNames, expectedNumberOfRows, SETUP_SQL,
WITH_QUERY, maxRetries-1); WITH_QUERY, maxRetries-1, expectedColumnTypes);
} }
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论