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

Huge headway in testing

上级 bb5c0974
...@@ -21,7 +21,7 @@ OFFSET specified how many rows to skip. ...@@ -21,7 +21,7 @@ OFFSET specified how many rows to skip.
Please note using high offset values should be avoided because it can cause performance problems. Please note using high offset values should be avoided because it can cause performance problems.
SAMPLE_SIZE limits the number of rows read for aggregate queries. SAMPLE_SIZE limits the number of rows read for aggregate queries.
Multiple set operators (UNION, INTERSECT, MINUS, EXPECT) are evaluated Multiple set operators (UNION, INTERSECT, MINUS, EXCEPT) are evaluated
from left to right. For compatibility with other databases and future versions from left to right. For compatibility with other databases and future versions
of H2 please use parentheses. of H2 please use parentheses.
......
...@@ -1138,10 +1138,9 @@ public class Parser { ...@@ -1138,10 +1138,9 @@ public class Parser {
String[] querySQLOutput = new String[]{null}; String[] querySQLOutput = new String[]{null};
List<Column> columnTemplateList = createQueryColumnTemplateList(null, command.getQuery(), querySQLOutput); List<Column> columnTemplateList = createQueryColumnTemplateList(null, command.getQuery(), querySQLOutput);
System.out.println("pre:alias="+command.getQueryAlias()+",sql="+querySQLOutput[0]+",ctlist="+columnTemplateList+",schema="+getSchema()+",schemaName="+schemaName); System.out.println("pre:alias="+command.getQueryAlias()+",sql="+querySQLOutput[0]+",ctlist="+columnTemplateList+",schema="+getSchema()+",schemaName="+schemaName);
TableView temporarySourceTableView = createTemporarySessionView(command.getQueryAlias(), querySQLOutput[0], columnTemplateList, false); TableView temporarySourceTableView = createTemporarySessionView(command.getQueryAlias(), querySQLOutput[0], columnTemplateList, false, false);
command.setTemporaryTableView(temporarySourceTableView); command.setTemporaryTableView(temporarySourceTableView);
//Table tableOrView = command.getQuery().getTables().toArray(new Table[]{null})[0];
System.out.println("sourceTableFilter with tableOrView="+temporarySourceTableView); System.out.println("sourceTableFilter with tableOrView="+temporarySourceTableView);
System.out.println("sourceTableFilter rightsChecked="+rightsChecked); System.out.println("sourceTableFilter rightsChecked="+rightsChecked);
TableFilter sourceTableFilter = new TableFilter(session, temporarySourceTableView, command.getQueryAlias(), rightsChecked, TableFilter sourceTableFilter = new TableFilter(session, temporarySourceTableView, command.getQueryAlias(), rightsChecked,
...@@ -5190,7 +5189,7 @@ public class Parser { ...@@ -5190,7 +5189,7 @@ public class Parser {
} finally { } finally {
session.removeLocalTempTable(recursiveTable); session.removeLocalTempTable(recursiveTable);
} }
TableView view = createTemporarySessionView(tempViewName, querySQLOutput[0], columnTemplateList,true/*allowRecursiveQueryDetection*/); TableView view = createTemporarySessionView(tempViewName, querySQLOutput[0], columnTemplateList,true/*allowRecursiveQueryDetection*/, true);
return view; return view;
} }
...@@ -5201,18 +5200,26 @@ public class Parser { ...@@ -5201,18 +5200,26 @@ public class Parser {
ArrayList<Expression> withExpressions = withQuery.getExpressions(); ArrayList<Expression> withExpressions = withQuery.getExpressions();
System.out.println("withExpressions="+withExpressions); System.out.println("withExpressions="+withExpressions);
for (int i = 0; i < withExpressions.size(); ++i) { for (int i = 0; i < withExpressions.size(); ++i) {
Expression withExp = withExpressions.get(i); Expression columnExp = withExpressions.get(i);
System.out.println("withExp.alias="+withExp.getAlias()+",name="+withExp.getColumnName()); // use the passed in column name if supplied, otherwise use alias (if used) otherwise use column name
String columnName = cols != null ? cols[i] // derived from column expression
: (withExp.getAlias()!=null ? withExp.getAlias() : withExp.getColumnName()); String columnName;
if (cols != null){
columnName = cols[i];
} else if (columnExp.getAlias()!=null){
columnName = columnExp.getAlias();
}
else{
columnName = columnExp.getColumnName();
}
columnTemplateList.add(new Column(columnName, columnTemplateList.add(new Column(columnName,
withExpressions.get(i).getType())); columnExp.getType()));
} }
return columnTemplateList; return columnTemplateList;
} }
private TableView createTemporarySessionView(String tempViewName, String querySQL, private TableView createTemporarySessionView(String tempViewName, String querySQL,
List<Column> columnTemplateList, boolean allowRecursiveQueryDetection) { List<Column> columnTemplateList, boolean allowRecursiveQueryDetection, boolean addViewToSession) {
Schema schema = getSchema(); Schema schema = getSchema();
int id = database.allocateObjectId(); int id = database.allocateObjectId();
// No easy way to determine if this is a recursive query up front, so we just compile // No easy way to determine if this is a recursive query up front, so we just compile
...@@ -5229,7 +5236,9 @@ public class Parser { ...@@ -5229,7 +5236,9 @@ public class Parser {
} }
view.setTableExpression(true); view.setTableExpression(true);
view.setTemporary(true); view.setTemporary(true);
if(addViewToSession){
session.addLocalTempTable(view); session.addLocalTempTable(view);
}
view.setOnCommitDrop(true); view.setOnCommitDrop(true);
return view; return view;
} }
......
...@@ -361,7 +361,7 @@ public class Session extends SessionWithState { ...@@ -361,7 +361,7 @@ public class Session extends SessionWithState {
} }
if (localTempTables.get(table.getName()) != null) { if (localTempTables.get(table.getName()) != null) {
throw DbException.get(ErrorCode.TABLE_OR_VIEW_ALREADY_EXISTS_1, throw DbException.get(ErrorCode.TABLE_OR_VIEW_ALREADY_EXISTS_1,
table.getSQL()); table.getSQL()+" AS "+table.getName());
} }
modificationId++; modificationId++;
localTempTables.put(table.getName(), table); localTempTables.put(table.getName(), table);
......
...@@ -9,7 +9,6 @@ import java.sql.Connection; ...@@ -9,7 +9,6 @@ import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.Statement; import java.sql.Statement;
import org.h2.test.TestBase; import org.h2.test.TestBase;
/** /**
* Test merge using syntax. * Test merge using syntax.
...@@ -17,6 +16,8 @@ import org.h2.test.TestBase; ...@@ -17,6 +16,8 @@ import org.h2.test.TestBase;
public class TestMergeUsing extends TestBase { public class TestMergeUsing extends TestBase {
private static final String GATHER_ORDERED_RESULTS_SQL = "SELECT ID, NAME FROM PARENT ORDER BY ID ASC";
/** /**
* Run just this test. * Run just this test.
* *
...@@ -28,35 +29,86 @@ public class TestMergeUsing extends TestBase { ...@@ -28,35 +29,86 @@ public class TestMergeUsing extends TestBase {
@Override @Override
public void test() throws Exception { public void test() throws Exception {
testMergeUsing();
// Simple ID,NAME inserts, target table with PK initially empty
testMergeUsing(
"CREATE TABLE PARENT(ID INT, NAME VARCHAR, PRIMARY KEY(ID) );",
"MERGE INTO PARENT AS P USING (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) ) AS S ON (P.ID = S.ID AND 1=1 AND S.ID = P.ID) WHEN MATCHED THEN UPDATE SET P.NAME = S.NAME WHERE 2 = 2 WHEN NOT MATCHED THEN INSERT (ID, NAME) VALUES (S.ID, S.NAME)",
GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2)",
2
);
// Simple NAME updates, target table missing PK
testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );",
"MERGE INTO PARENT AS P USING (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) ) AS S ON (P.ID = S.ID AND 1=1 AND S.ID = P.ID) WHEN MATCHED THEN UPDATE SET P.NAME = S.NAME||S.ID WHERE 1 = 1 WHEN NOT MATCHED THEN INSERT (ID, NAME) VALUES (S.ID, S.NAME)",
GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X||X AS NAME FROM SYSTEM_RANGE(1,2)",
2
);
// No NAME updates, WHERE clause is always false, insert clause missing
testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );",
"MERGE INTO PARENT AS P USING (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) ) AS S ON (P.ID = S.ID) WHEN MATCHED THEN UPDATE SET P.NAME = S.NAME||S.ID WHERE 1 = 2",
GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2)",
0
);
// No NAME updates, no WHERE clause, insert clause missing
testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );",
"MERGE INTO PARENT AS P USING (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) ) AS S ON (P.ID = S.ID) WHEN MATCHED THEN UPDATE SET P.NAME = S.NAME||S.ID",
GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X||X AS NAME FROM SYSTEM_RANGE(1,2)",
2
);
// Two delete updates done, no WHERE clause, insert clause missing
testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );",
"MERGE INTO PARENT AS P USING (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) ) AS S ON (P.ID = S.ID) WHEN MATCHED THEN DELETE",
GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) WHERE 1=0",
2
);
// One insert, one update one delete happens, target table missing PK
testMergeUsing(
"CREATE TABLE PARENT AS (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) );",
"MERGE INTO PARENT AS P USING (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,3) ) AS S ON (P.ID = S.ID) WHEN MATCHED THEN UPDATE SET P.NAME = S.NAME||S.ID WHERE P.ID = 2 DELETE WHERE P.ID = 1 WHEN NOT MATCHED THEN INSERT (ID, NAME) VALUES (S.ID, S.NAME)",
GATHER_ORDERED_RESULTS_SQL,
"SELECT X AS ID, 'Marcy'||X||X AS NAME FROM SYSTEM_RANGE(2,2) UNION ALL SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(3,3)",
3
);
} }
private void testMergeUsing() throws Exception { private void testMergeUsing(String setupSQL, String statementUnderTest, String gatherResultsSQL,
String expectedResultsSQL, int expectedRowUpdateCount) throws Exception {
deleteDb("mergeUsingQueries"); deleteDb("mergeUsingQueries");
Connection conn = getConnection("mergeUsingQueries"); Connection conn = getConnection("mergeUsingQueries");
Statement stat; Statement stat;
PreparedStatement prep; PreparedStatement prep;
ResultSet rs; ResultSet rs;
int rowCount; int rowCountUpdate;
stat = conn.createStatement(); stat = conn.createStatement();
stat.execute("CREATE TABLE PARENT(ID INT, NAME VARCHAR, PRIMARY KEY(ID) );"); stat.execute(setupSQL);
prep = conn.prepareStatement("MERGE INTO PARENT AS P USING (SELECT X AS ID, 'Marcy'||X AS NAME FROM SYSTEM_RANGE(1,2) ) AS S ON (P.ID = S.ID AND 1=1 AND S.ID = P.ID) WHEN MATCHED THEN UPDATE SET P.NAME = S.NAME WHERE 2 = 2 WHEN NOT MATCHED THEN INSERT (ID, NAME) VALUES (S.ID, S.NAME)"); prep = conn.prepareStatement(statementUnderTest);
rowCount = prep.executeUpdate(); rowCountUpdate = prep.executeUpdate();
int[] rowArray = new int[] { 1,2 }; rs = stat.executeQuery("( "+gatherResultsSQL+" ) MINUS ( "+expectedResultsSQL+" )");
assertEquals(rowArray.length,rowCount);
rs = stat.executeQuery("SELECT ID, NAME FROM PARENT ORDER BY ID ASC"); int rowCount = 0;
StringBuffer diffBuffer = new StringBuffer("");
for (int n : rowArray) { while (rs.next()) {
assertTrue(rs.next()); rowCount++;
assertEquals(n,rs.getInt(1)); diffBuffer.append("|");
assertEquals("Marcy"+n, rs.getString(2)); for(int ndx = 0; ndx < rs.getMetaData().getColumnCount(); ndx++){
System.out.println("id="+rs.getInt(1)+",name="+rs.getString(2)); diffBuffer.append(rs.getObject(ndx));
diffBuffer.append("|\n");
}
} }
assertFalse(rs.next()); assertEquals("Differences is expected output found:"+diffBuffer,0,rowCount);
assertEquals("Expected update counts differ",expectedRowUpdateCount,rowCountUpdate);
conn.close(); conn.close();
deleteDb("mergeUsingQueries"); deleteDb("mergeUsingQueries");
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论