diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/CostBasedDecisionIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/CostBasedDecisionIT.java index d7ae0e40b7c..c313393a91f 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/CostBasedDecisionIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/CostBasedDecisionIT.java @@ -18,6 +18,7 @@ package org.apache.phoenix.end2end; import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.sql.Connection; @@ -27,6 +28,9 @@ import java.util.Map; import java.util.Properties; +import org.apache.phoenix.compile.ExplainPlan; +import org.apache.phoenix.compile.ExplainPlanAttributes; +import org.apache.phoenix.jdbc.PhoenixPreparedStatement; import org.apache.phoenix.query.BaseTest; import org.apache.phoenix.query.QueryServices; import org.apache.phoenix.util.PropertiesUtil; @@ -110,10 +114,21 @@ public void testCostOverridesStaticPlanOrdering2() throws Exception { String query = "SELECT c1, max(rowkey), max(c2) FROM " + tableName + " where rowkey <= 'z' GROUP BY c1"; // Use the index table plan that opts out order-by when stats are not available. - verifyQueryPlan(query, - "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + tableName + " [*] - ['z']\n" + - " SERVER AGGREGATE INTO DISTINCT ROWS BY [C1]\n" + - "CLIENT MERGE SORT"); + ExplainPlan plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(tableName, explainPlanAttributes.getTableName()); + assertEquals(" [*] - ['z']", explainPlanAttributes.getKeyRanges()); + assertEquals("SERVER AGGREGATE INTO DISTINCT ROWS BY [C1]", + explainPlanAttributes.getServerAggregate()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); PreparedStatement stmt = conn.prepareStatement("UPSERT INTO " + tableName + " (rowkey, c1, c2) VALUES (?, ?, ?)"); for (int i = 0; i < 10000; i++) { @@ -129,11 +144,22 @@ public void testCostOverridesStaticPlanOrdering2() throws Exception { // Given that the range on C1 is meaningless and group-by becomes // order-preserving if using the data table, the data table plan should // come out as the best plan based on the costs. - verifyQueryPlan(query, - "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + tableName + " [1]\n" + - " SERVER FILTER BY FIRST KEY ONLY AND \"ROWKEY\" <= 'z'\n" + - " SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [\"C1\"]\n" + - "CLIENT MERGE SORT"); + plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + explainPlanAttributes = plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(tableName, explainPlanAttributes.getTableName()); + assertEquals(" [1]", explainPlanAttributes.getKeyRanges()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY AND \"ROWKEY\" <= 'z'", + explainPlanAttributes.getServerWhereFilter()); + assertEquals("SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [\"C1\"]", + explainPlanAttributes.getServerAggregate()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); } finally { conn.close(); } @@ -156,10 +182,22 @@ public void testCostOverridesStaticPlanOrdering3() throws Exception { String query = "SELECT * FROM " + tableName + " where c1 BETWEEN 10 AND 20 AND c2 < 9000 AND C3 < 5000"; // Use the idx2 plan with a wider PK slot span when stats are not available. - verifyQueryPlan(query, - "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + tableName + " [2,*] - [2,9,000]\n" + - " SERVER FILTER BY ((\"C1\" >= 10 AND \"C1\" <= 20) AND TO_INTEGER(\"C3\") < 5000)\n" + - "CLIENT MERGE SORT"); + ExplainPlan plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(tableName, explainPlanAttributes.getTableName()); + assertEquals(" [2,*] - [2,9,000]", + explainPlanAttributes.getKeyRanges()); + assertEquals("SERVER FILTER BY ((\"C1\" >= 10 AND \"C1\" <= 20) AND TO_INTEGER(\"C3\") < 5000)", + explainPlanAttributes.getServerWhereFilter()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); PreparedStatement stmt = conn.prepareStatement("UPSERT INTO " + tableName + " (rowkey, c1, c2, c3) VALUES (?, ?, ?, ?)"); for (int i = 0; i < 10000; i++) { @@ -173,10 +211,21 @@ public void testCostOverridesStaticPlanOrdering3() throws Exception { conn.createStatement().execute("UPDATE STATISTICS " + tableName); // Use the idx2 plan that scans less data when stats become available. - verifyQueryPlan(query, - "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + tableName + " [1,10] - [1,20]\n" + - " SERVER FILTER BY (\"C2\" < 9000 AND \"C3\" < 5000)\n" + - "CLIENT MERGE SORT"); + plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + explainPlanAttributes = plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(tableName, explainPlanAttributes.getTableName()); + assertEquals(" [1,10] - [1,20]", + explainPlanAttributes.getKeyRanges()); + assertEquals("SERVER FILTER BY (\"C2\" < 9000 AND \"C3\" < 5000)", + explainPlanAttributes.getServerWhereFilter()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); } finally { conn.close(); } @@ -394,11 +443,17 @@ public void testHintOverridesCost() throws Exception { String query = "SELECT rowkey, c1, c2 FROM " + tableName + " where rowkey between 1 and 10 ORDER BY c1"; String hintedQuery = query.replaceFirst("SELECT", "SELECT /*+ INDEX(" + tableName + " " + tableName + "_idx) */"); - String dataPlan = "SERVER SORTED BY [C1]"; + String dataPlan = "[C1]"; String indexPlan = "SERVER FILTER BY FIRST KEY ONLY AND (\"ROWKEY\" >= 1 AND \"ROWKEY\" <= 10)"; // Use the index table plan that opts out order-by when stats are not available. - verifyQueryPlan(query, indexPlan); + ExplainPlan plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals(indexPlan, + explainPlanAttributes.getServerWhereFilter()); PreparedStatement stmt = conn.prepareStatement("UPSERT INTO " + tableName + " (rowkey, c1, c2) VALUES (?, ?, ?)"); for (int i = 0; i < 10000; i++) { @@ -412,10 +467,22 @@ public void testHintOverridesCost() throws Exception { conn.createStatement().execute("UPDATE STATISTICS " + tableName); // Use the data table plan that has a lower cost when stats are available. - verifyQueryPlan(query, dataPlan); + plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals(dataPlan, + explainPlanAttributes.getServerSortedBy()); // Use the index table plan as has been hinted. - verifyQueryPlan(hintedQuery, indexPlan); + plan = conn.prepareStatement(hintedQuery) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals(indexPlan, + explainPlanAttributes.getServerWhereFilter()); } finally { conn.close(); } @@ -427,12 +494,27 @@ public void testJoinStrategy() throws Exception { String q = "SELECT *\n" + "FROM " + testTable500 + " t1 JOIN " + testTable1000 + " t2\n" + "ON t1.ID = t2.ID"; - String expected = - "SORT-MERGE-JOIN (INNER) TABLES\n" + - " CLIENT PARALLEL 1-WAY FULL SCAN OVER " + testTable500 + "\n" + - "AND\n" + - " CLIENT PARALLEL 1-WAY FULL SCAN OVER " + testTable1000; - verifyQueryPlan(q, expected); + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + try (Connection conn = DriverManager.getConnection(getUrl(), props)) { + ExplainPlan plan = conn.prepareStatement(q) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("SORT-MERGE-JOIN (INNER)", + explainPlanAttributes.getAbstractExplainPlan()); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(testTable500, explainPlanAttributes.getTableName()); + ExplainPlanAttributes rhsTable = + explainPlanAttributes.getRhsJoinQueryExplainPlan(); + assertEquals("PARALLEL 1-WAY", + rhsTable.getIteratorTypeAndScanSize()); + assertEquals("FULL SCAN ", rhsTable.getExplainScanType()); + assertEquals(testTable1000, rhsTable.getTableName()); + } } /** Sort-merge-join w/ both children ordered wins over hash-join in an un-grouped aggregate query. */ @@ -442,15 +524,33 @@ public void testJoinStrategy2() throws Exception { "FROM " + testTable500 + " t1 JOIN " + testTable1000 + " t2\n" + "ON t1.ID = t2.ID\n" + "WHERE t1.COL1 < 200"; - String expected = - "SORT-MERGE-JOIN (INNER) TABLES\n" + - " CLIENT PARALLEL 1-WAY FULL SCAN OVER " + testTable500 + "\n" + - " SERVER FILTER BY COL1 < 200\n" + - "AND (SKIP MERGE)\n" + - " CLIENT PARALLEL 1-WAY FULL SCAN OVER " + testTable1000 + "\n" + - " SERVER FILTER BY FIRST KEY ONLY\n" + - "CLIENT AGGREGATE INTO SINGLE ROW"; - verifyQueryPlan(q, expected); + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + try (Connection conn = DriverManager.getConnection(getUrl(), props)) { + ExplainPlan plan = conn.prepareStatement(q) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("SORT-MERGE-JOIN (INNER)", + explainPlanAttributes.getAbstractExplainPlan()); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals("SERVER FILTER BY COL1 < 200", + explainPlanAttributes.getServerWhereFilter()); + assertEquals(testTable500, explainPlanAttributes.getTableName()); + assertEquals("CLIENT AGGREGATE INTO SINGLE ROW", + explainPlanAttributes.getClientAggregate()); + ExplainPlanAttributes rhsTable = + explainPlanAttributes.getRhsJoinQueryExplainPlan(); + assertEquals("PARALLEL 1-WAY", + rhsTable.getIteratorTypeAndScanSize()); + assertEquals("FULL SCAN ", rhsTable.getExplainScanType()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + rhsTable.getServerWhereFilter()); + assertEquals(testTable1000, rhsTable.getTableName()); + } } /** Hash-join w/ PK/FK optimization wins over sort-merge-join w/ larger side ordered. */ @@ -542,13 +642,31 @@ public void testJoinStrategy8() throws Exception { "FROM " + testTable500 + " t1 JOIN " + testTable1000 + " t2\n" + "ON t1.ID = t2.ID\n" + "ORDER BY t1.COL1 LIMIT 5"; - String expected = - "SORT-MERGE-JOIN (INNER) TABLES\n" + - " CLIENT PARALLEL 1-WAY FULL SCAN OVER " + testTable500 + "\n" + - "AND\n" + - " CLIENT PARALLEL 1-WAY FULL SCAN OVER " + testTable1000 + "\n" + - "CLIENT TOP 5 ROWS SORTED BY [T1.COL1]"; - verifyQueryPlan(q, expected); + Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); + try (Connection conn = DriverManager.getConnection(getUrl(), props)) { + ExplainPlan plan = conn.prepareStatement(q) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("SORT-MERGE-JOIN (INNER)", + explainPlanAttributes.getAbstractExplainPlan()); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(testTable500, explainPlanAttributes.getTableName()); + assertEquals("[T1.COL1]", + explainPlanAttributes.getClientSortedBy()); + assertEquals(new Integer(5), + explainPlanAttributes.getClientRowLimit()); + ExplainPlanAttributes rhsTable = + explainPlanAttributes.getRhsJoinQueryExplainPlan(); + assertEquals("PARALLEL 1-WAY", + rhsTable.getIteratorTypeAndScanSize()); + assertEquals("FULL SCAN ", rhsTable.getExplainScanType()); + assertEquals(testTable1000, rhsTable.getTableName()); + } } /** diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/FlappingLocalIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/FlappingLocalIndexIT.java index 6ebf3441818..00326070c50 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/FlappingLocalIndexIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/FlappingLocalIndexIT.java @@ -19,6 +19,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import java.io.IOException; @@ -40,11 +41,13 @@ import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment; import org.apache.hadoop.hbase.coprocessor.RegionObserver; import org.apache.hadoop.hbase.util.Pair; +import org.apache.phoenix.compile.ExplainPlan; +import org.apache.phoenix.compile.ExplainPlanAttributes; import org.apache.phoenix.end2end.index.BaseLocalIndexIT; +import org.apache.phoenix.jdbc.PhoenixPreparedStatement; import org.apache.phoenix.query.QueryConstants; import org.apache.phoenix.query.QueryServices; import org.apache.phoenix.query.QueryServicesOptions; -import org.apache.phoenix.util.QueryUtil; import org.apache.phoenix.util.SchemaUtil; import org.apache.phoenix.util.TestUtil; import org.junit.Test; @@ -153,15 +156,25 @@ public void testLocalIndexScan() throws Exception { int numRegions = admin.getTableRegions(physicalTableName).size(); String query = "SELECT * FROM " + tableName +" where v1 like 'a%'"; - rs = conn1.createStatement().executeQuery("EXPLAIN "+ query); - - assertEquals( - "CLIENT PARALLEL " + numRegions + "-WAY RANGE SCAN OVER " - + indexPhysicalTableName + " [1,'a'] - [1,'b']\n" - + " SERVER FILTER BY FIRST KEY ONLY\n" - + "CLIENT MERGE SORT", - QueryUtil.getExplainPlan(rs)); - + + ExplainPlan plan = conn1.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL " + numRegions + "-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(indexPhysicalTableName, + explainPlanAttributes.getTableName()); + assertEquals(" [1,'a'] - [1,'b']", + explainPlanAttributes.getKeyRanges()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); + rs = conn1.createStatement().executeQuery(query); assertTrue(rs.next()); assertEquals("f", rs.getString("t_id")); @@ -177,15 +190,24 @@ public void testLocalIndexScan() throws Exception { assertEquals(2, rs.getInt("k3")); assertFalse(rs.next()); query = "SELECT t_id, k1, k2,V1 FROM " + tableName +" where v1='a'"; - rs = conn1.createStatement().executeQuery("EXPLAIN "+ query); - - assertEquals( - "CLIENT PARALLEL " + numRegions + "-WAY RANGE SCAN OVER " - + indexPhysicalTableName + " [1,'a']\n" - + " SERVER FILTER BY FIRST KEY ONLY\n" - + "CLIENT MERGE SORT", - QueryUtil.getExplainPlan(rs)); - + + plan = conn1.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + explainPlanAttributes = plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL " + numRegions + "-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(indexPhysicalTableName, + explainPlanAttributes.getTableName()); + assertEquals(" [1,'a']", + explainPlanAttributes.getKeyRanges()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); + rs = conn1.createStatement().executeQuery(query); assertTrue(rs.next()); assertEquals("f", rs.getString("t_id")); @@ -197,12 +219,25 @@ public void testLocalIndexScan() throws Exception { assertEquals(4, rs.getInt("k2")); assertFalse(rs.next()); query = "SELECT t_id, k1, k2,V1, k3 FROM " + tableName +" where v1<='z' order by k3"; - rs = conn1.createStatement().executeQuery("EXPLAIN "+ query); - - assertEquals("CLIENT PARALLEL " + numRegions + "-WAY RANGE SCAN OVER " + indexPhysicalTableName - + " [1,*] - [1,'z']\n" + " SERVER FILTER BY FIRST KEY ONLY\n" - + " SERVER SORTED BY [\"K3\"]\n" + "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs)); - + + plan = conn1.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + explainPlanAttributes = plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL " + numRegions + "-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(indexPhysicalTableName, + explainPlanAttributes.getTableName()); + assertEquals(" [1,*] - [1,'z']", + explainPlanAttributes.getKeyRanges()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); + assertEquals("[\"K3\"]", explainPlanAttributes.getServerSortedBy()); + rs = conn1.createStatement().executeQuery(query); assertTrue(rs.next()); assertEquals(1, rs.getInt("k3")); @@ -219,15 +254,24 @@ public void testLocalIndexScan() throws Exception { assertFalse(rs.next()); query = "SELECT t_id, k1, k2,v1 from " + tableName + " order by V1,t_id"; - rs = conn1.createStatement().executeQuery("EXPLAIN " + query); - - assertEquals( - "CLIENT PARALLEL " + numRegions + "-WAY RANGE SCAN OVER " - + indexPhysicalTableName +" [1]\n" - + " SERVER FILTER BY FIRST KEY ONLY\n" - + "CLIENT MERGE SORT", - QueryUtil.getExplainPlan(rs)); - + + plan = conn1.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + explainPlanAttributes = plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL " + numRegions + "-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(indexPhysicalTableName, + explainPlanAttributes.getTableName()); + assertEquals(" [1]", explainPlanAttributes.getKeyRanges()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); + assertNull(explainPlanAttributes.getServerSortedBy()); + rs = conn1.createStatement().executeQuery(query); assertTrue(rs.next()); assertEquals("f", rs.getString("t_id")); diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/InListIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/InListIT.java index 055494c34e7..aa9944eb8e1 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/InListIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/InListIT.java @@ -35,6 +35,8 @@ import java.util.List; import java.util.Properties; +import org.apache.phoenix.compile.ExplainPlan; +import org.apache.phoenix.compile.ExplainPlanAttributes; import org.apache.phoenix.compile.QueryPlan; import org.apache.phoenix.iterate.ExplainTable; import org.apache.phoenix.schema.SortOrder; @@ -734,14 +736,22 @@ public void testPkDescOrderedTenantViewOnGlobalViewWithRightQueryPlan() throws E "SELECT * FROM " + tenantView + " WHERE (ID1, ID2) " + "IN (('005xx000001Sv6o', '000000000000500'))")) { QueryPlan queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt); - assertTrue(queryPlan.getExplainPlan().toString().contains(ExplainTable.POINT_LOOKUP_ON_STRING)); + ExplainPlan plan = queryPlan.getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertTrue(explainPlanAttributes.getExplainScanType() + .startsWith(ExplainTable.POINT_LOOKUP_ON_STRING)); } try (PreparedStatement preparedStmt = viewConn.prepareStatement( "SELECT * FROM " + tenantView + " WHERE (ID2, ID1) " + "IN (('000000000000500', '005xx000001Sv6o'))")) { QueryPlan queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt); - assertTrue(queryPlan.getExplainPlan().toString().contains(ExplainTable.POINT_LOOKUP_ON_STRING)); + ExplainPlan plan = queryPlan.getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertTrue(explainPlanAttributes.getExplainScanType() + .startsWith(ExplainTable.POINT_LOOKUP_ON_STRING)); } stmt.execute("DELETE FROM " + tenantView + " WHERE (ID2, ID1) IN " + @@ -777,7 +787,11 @@ public void testColumnDescOrderedTenantViewOnGlobalViewWithStringValue() throws "('bar', '000000000000400')," + "('foo', '000000000000300'))")) { QueryPlan queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt); - assertTrue(queryPlan.getExplainPlan().toString().contains(ExplainTable.POINT_LOOKUP_ON_STRING)); + ExplainPlan plan = queryPlan.getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertTrue(explainPlanAttributes.getExplainScanType() + .startsWith(ExplainTable.POINT_LOOKUP_ON_STRING)); } try (PreparedStatement preparedStmt = viewConn.prepareStatement( @@ -785,7 +799,11 @@ public void testColumnDescOrderedTenantViewOnGlobalViewWithStringValue() throws "(('bar', '005xx000001Sv6o')," + "('foo', '005xx000001Sv6o'))")) { QueryPlan queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt); - assertTrue(queryPlan.getExplainPlan().toString().contains(ExplainTable.POINT_LOOKUP_ON_STRING)); + ExplainPlan plan = queryPlan.getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertTrue(explainPlanAttributes.getExplainScanType() + .startsWith(ExplainTable.POINT_LOOKUP_ON_STRING)); } stmt.execute("DELETE FROM " + tenantView + " WHERE (ID2, ID1) IN " + @@ -825,7 +843,11 @@ public void testInListExpressionWithRightQueryPlanForTenantViewOnGlobalView() th "('005xx000001Sv6o', '000000000000400')," + "('005xx000001Sv6o', '000000000000300'))")) { QueryPlan queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt); - assertTrue(queryPlan.getExplainPlan().toString().contains(ExplainTable.POINT_LOOKUP_ON_STRING)); + ExplainPlan plan = queryPlan.getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertTrue(explainPlanAttributes.getExplainScanType() + .startsWith(ExplainTable.POINT_LOOKUP_ON_STRING)); } try (PreparedStatement preparedStmt = viewConn.prepareStatement( @@ -833,7 +855,11 @@ public void testInListExpressionWithRightQueryPlanForTenantViewOnGlobalView() th "(('000000000000400', '005xx000001Sv6o')," + "('000000000000300', '005xx000001Sv6o'))")) { QueryPlan queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt); - assertTrue(queryPlan.getExplainPlan().toString().contains(ExplainTable.POINT_LOOKUP_ON_STRING)); + ExplainPlan plan = queryPlan.getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertTrue(explainPlanAttributes.getExplainScanType() + .startsWith(ExplainTable.POINT_LOOKUP_ON_STRING)); } ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM " + tenantView + " WHERE (ID2, ID1) IN " + "(('000000000000400', '005xx000001Sv6o')," + @@ -874,14 +900,21 @@ private void testFullPkListPlan(String tenantView) throws Exception { "('005xx000001Sv6o', '000000000000400'))"); QueryPlan queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt); assertEquals(numberOfRowsToScan, queryPlan.getEstimatedRowsToScan()); - assertTrue(queryPlan.getExplainPlan().toString().contains(ExplainTable.POINT_LOOKUP_ON_STRING)); + ExplainPlan plan = queryPlan.getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertTrue(explainPlanAttributes.getExplainScanType() + .startsWith(ExplainTable.POINT_LOOKUP_ON_STRING)); viewConn.prepareStatement("DELETE FROM " + tenantView + " WHERE (ID1, ID2) IN " + "(('005xx000001Sv6o', '000000000000500')," + "('005xx000001Sv6o', '000000000000400'))"); queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt); assertEquals(numberOfRowsToScan, queryPlan.getEstimatedRowsToScan()); - assertTrue(queryPlan.getExplainPlan().toString().contains(ExplainTable.POINT_LOOKUP_ON_STRING)); + plan = queryPlan.getExplainPlan(); + explainPlanAttributes = plan.getPlanStepsAsAttributes(); + assertTrue(explainPlanAttributes.getExplainScanType() + .startsWith(ExplainTable.POINT_LOOKUP_ON_STRING)); } } @@ -891,7 +924,13 @@ private void testPartialPkListPlan(String tenantView) throws Exception { "(('005xx000001Sv6o')," + "('005xx000001Sv6o'))"); QueryPlan queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt); - assertTrue(queryPlan.getExplainPlan().toString().contains("CLIENT PARALLEL 1-WAY RANGE SCAN OVER")); + ExplainPlan plan = queryPlan.getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); viewConn.prepareStatement("DELETE FROM " + tenantView + " WHERE (ID1) IN " + "(('005xx000001Sv6o')," + @@ -903,7 +942,12 @@ private void testPartialPkListPlan(String tenantView) throws Exception { "(('000000000000500')," + "('000000000000400'))"); queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt); - assertTrue(queryPlan.getExplainPlan().toString().contains("CLIENT PARALLEL 1-WAY RANGE SCAN OVER")); + plan = queryPlan.getExplainPlan(); + explainPlanAttributes = plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); viewConn.prepareStatement("DELETE FROM " + tenantView + " WHERE (ID2) IN " + "(('000000000000500')," + @@ -919,7 +963,13 @@ private void testPartialPkPlusNonPkListPlan(String tenantView) throws Exception "(('005xx000001Sv6o', 1)," + "('005xx000001Sv6o', 2))"); QueryPlan queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt); - assertTrue(queryPlan.getExplainPlan().toString().contains("CLIENT PARALLEL 1-WAY RANGE SCAN OVER")); + ExplainPlan plan = queryPlan.getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); viewConn.prepareStatement("DELETE FROM " + tenantView + " WHERE (ID1, ID3) IN " + "(('005xx000001Sv6o', 1)," + @@ -931,7 +981,12 @@ private void testPartialPkPlusNonPkListPlan(String tenantView) throws Exception "(('000000000000500', 1)," + "('000000000000400', 2))"); queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt); - assertTrue(queryPlan.getExplainPlan().toString().contains("CLIENT PARALLEL 1-WAY RANGE SCAN OVER")); + plan = queryPlan.getExplainPlan(); + explainPlanAttributes = plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); viewConn.prepareStatement("DELETE FROM " + tenantView + " WHERE (ID2, ID3) IN " + "(('000000000000500', 1)," + @@ -948,7 +1003,13 @@ private void testNonPkListPlan(String tenantView) throws Exception { "((1, 1)," + "(2, 2))"); QueryPlan queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt); - assertTrue(queryPlan.getExplainPlan().toString().contains("CLIENT PARALLEL 1-WAY RANGE SCAN OVER")); + ExplainPlan plan = queryPlan.getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); viewConn.prepareStatement("DELETE FROM " + tenantView + " WHERE (ID3, ID4) IN " + "((1, 1)," + @@ -1473,18 +1534,27 @@ public void testBaseTableAndIndexTableHaveReversePKOrder() throws Exception { "(2, 2))"); QueryPlan queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt); assertEquals(new Long(2), queryPlan.getEstimatedRowsToScan()); - assertTrue(queryPlan.getExplainPlan().toString().contains(ExplainTable.POINT_LOOKUP_ON_STRING)); + ExplainPlan plan = queryPlan.getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertTrue(explainPlanAttributes.getExplainScanType() + .startsWith(ExplainTable.POINT_LOOKUP_ON_STRING)); preparedStmt = conn.prepareStatement("SELECT * FROM " + view + " WHERE (ID2, ID1) IN ((1, 1),(2, 2))"); queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt); assertEquals(new Long(2), queryPlan.getEstimatedRowsToScan()); - assertTrue(queryPlan.getExplainPlan().toString().contains(ExplainTable.POINT_LOOKUP_ON_STRING)); - + plan = queryPlan.getExplainPlan(); + explainPlanAttributes = plan.getPlanStepsAsAttributes(); + assertTrue(explainPlanAttributes.getExplainScanType() + .startsWith(ExplainTable.POINT_LOOKUP_ON_STRING)); preparedStmt = conn.prepareStatement("SELECT * FROM " + view + " WHERE (ID2, ID1) IN ((1, 1),(2, 2))"); queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt); assertEquals(new Long(2), queryPlan.getEstimatedRowsToScan()); - assertTrue(queryPlan.getExplainPlan().toString().contains(ExplainTable.POINT_LOOKUP_ON_STRING)); + plan = queryPlan.getExplainPlan(); + explainPlanAttributes = plan.getPlanStepsAsAttributes(); + assertTrue(explainPlanAttributes.getExplainScanType() + .startsWith(ExplainTable.POINT_LOOKUP_ON_STRING)); ResultSet rs = stmt.executeQuery("SELECT ID1, ID2, ID5, ID4 FROM " + view + " WHERE (POWER(ID2, 2), ID1) IN " + @@ -1532,20 +1602,29 @@ public void testDeletionFromTenantViewAndViewIndex() throws Exception { "((1, 1)," + "(2, 2))"); QueryPlan queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt); - assertTrue(queryPlan.getExplainPlan().toString().contains("RANGE SCAN")); + ExplainPlan plan = queryPlan.getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); preparedStmt = conn.prepareStatement("SELECT ID1,ID5 FROM " + view + " WHERE (ID1, ID2) IN " + "((1, 1),(2, 2))"); queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt); assertEquals(new Long(2), queryPlan.getEstimatedRowsToScan()); - assertTrue(queryPlan.getExplainPlan().toString().contains(ExplainTable.POINT_LOOKUP_ON_STRING)); + plan = queryPlan.getExplainPlan(); + explainPlanAttributes = plan.getPlanStepsAsAttributes(); + assertTrue(explainPlanAttributes.getExplainScanType() + .startsWith(ExplainTable.POINT_LOOKUP_ON_STRING)); preparedStmt = conn.prepareStatement("SELECT * FROM " + view + " WHERE (ID2, ID1) IN ((1, 1),(2, 2))"); queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt); assertEquals(new Long(2), queryPlan.getEstimatedRowsToScan()); - assertTrue(queryPlan.getExplainPlan().toString().contains(ExplainTable.POINT_LOOKUP_ON_STRING)); - + plan = queryPlan.getExplainPlan(); + explainPlanAttributes = plan.getPlanStepsAsAttributes(); + assertTrue(explainPlanAttributes.getExplainScanType() + .startsWith(ExplainTable.POINT_LOOKUP_ON_STRING)); ResultSet rs = stmt.executeQuery("SELECT ID1, ID2, ID5, ID4 FROM " + view + " WHERE (POWER(ID2, 2), ID1) IN " + "((4.0, 9)," + @@ -1585,7 +1664,11 @@ public void testBaseTableAndIndexTableHaveRightScan() throws Exception { " WHERE (ID2, ID1) IN ((1, 1),(2, 2))"); QueryPlan queryPlan = PhoenixRuntime.getOptimizedQueryPlan(preparedStmt); queryPlan.getTableRef().getTable().getType(); - assertTrue(queryPlan.getExplainPlan().toString().contains(ExplainTable.POINT_LOOKUP_ON_STRING)); + ExplainPlan plan = queryPlan.getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertTrue(explainPlanAttributes.getExplainScanType() + .startsWith(ExplainTable.POINT_LOOKUP_ON_STRING)); } } } diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/IndexToolIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/IndexToolIT.java index e8c8b35de0d..006278d24c1 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/IndexToolIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/IndexToolIT.java @@ -17,6 +17,9 @@ */ package org.apache.phoenix.end2end; +import org.apache.phoenix.compile.ExplainPlan; +import org.apache.phoenix.compile.ExplainPlanAttributes; +import org.apache.phoenix.jdbc.PhoenixPreparedStatement; import org.apache.phoenix.thirdparty.com.google.common.collect.Lists; import org.apache.phoenix.thirdparty.com.google.common.collect.Maps; import org.apache.hadoop.conf.Configuration; @@ -26,10 +29,6 @@ import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.client.Admin; -import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor; -import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; -import org.apache.hadoop.hbase.client.Delete; -import org.apache.hadoop.hbase.client.HBaseAdmin; import org.apache.hadoop.hbase.client.Mutation; import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.client.ResultScanner; @@ -44,7 +43,6 @@ import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.mapreduce.Job; import org.apache.phoenix.mapreduce.index.IndexVerificationOutputRepository; -import org.apache.phoenix.mapreduce.index.IndexVerificationOutputRow; import org.apache.phoenix.mapreduce.index.IndexVerificationResultRepository; import org.apache.phoenix.jdbc.PhoenixConnection; import org.apache.phoenix.mapreduce.index.IndexTool; @@ -56,10 +54,8 @@ import org.apache.phoenix.schema.PTable; import org.apache.phoenix.transaction.PhoenixTransactionProvider.Feature; import org.apache.phoenix.transaction.TransactionFactory; -import org.apache.phoenix.util.EnvironmentEdgeManager; import org.apache.phoenix.util.PhoenixRuntime; import org.apache.phoenix.util.PropertiesUtil; -import org.apache.phoenix.util.QueryUtil; import org.apache.phoenix.util.ReadOnlyProps; import org.apache.phoenix.util.SchemaUtil; import org.apache.phoenix.util.TestUtil; @@ -69,7 +65,6 @@ import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; -import java.io.IOException; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; @@ -83,17 +78,9 @@ import java.util.Map; import java.util.Properties; import java.util.UUID; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import static org.apache.phoenix.mapreduce.PhoenixJobCounters.INPUT_RECORDS; -import static org.apache.phoenix.mapreduce.index.PhoenixIndexToolJobCounters.AFTER_REBUILD_INVALID_INDEX_ROW_COUNT_COZ_EXTRA_CELLS; -import static org.apache.phoenix.mapreduce.index.PhoenixIndexToolJobCounters.AFTER_REBUILD_INVALID_INDEX_ROW_COUNT_COZ_MISSING_CELLS; -import static org.apache.phoenix.mapreduce.index.PhoenixIndexToolJobCounters.AFTER_REBUILD_EXPIRED_INDEX_ROW_COUNT; -import static org.apache.phoenix.mapreduce.index.PhoenixIndexToolJobCounters.AFTER_REBUILD_INVALID_INDEX_ROW_COUNT; -import static org.apache.phoenix.mapreduce.index.PhoenixIndexToolJobCounters.AFTER_REBUILD_MISSING_INDEX_ROW_COUNT; -import static org.apache.phoenix.mapreduce.index.PhoenixIndexToolJobCounters.AFTER_REBUILD_VALID_INDEX_ROW_COUNT; import static org.apache.phoenix.mapreduce.index.PhoenixIndexToolJobCounters.BEFORE_REBUILD_INVALID_INDEX_ROW_COUNT_COZ_EXTRA_CELLS; import static org.apache.phoenix.mapreduce.index.PhoenixIndexToolJobCounters.BEFORE_REBUILD_INVALID_INDEX_ROW_COUNT_COZ_MISSING_CELLS; import static org.apache.phoenix.mapreduce.index.PhoenixIndexToolJobCounters.BEFORE_REBUILD_EXPIRED_INDEX_ROW_COUNT; @@ -103,10 +90,8 @@ import static org.apache.phoenix.mapreduce.index.PhoenixIndexToolJobCounters.REBUILT_INDEX_ROW_COUNT; import static org.apache.phoenix.mapreduce.index.PhoenixIndexToolJobCounters.SCANNED_DATA_ROW_COUNT; import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES; -import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -267,16 +252,23 @@ public void testSecondaryIndex() throws Exception { String.format( "SELECT ID FROM %s WHERE LPAD(UPPER(NAME, 'en_US'),8,'x')||'_xyz' = 'xxUNAME2_xyz'", dataTableFullName); - ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + selectSql); - String actualExplainPlan = QueryUtil.getExplainPlan(rs); // assert we are pulling from data table. - assertEquals(String.format( - "CLIENT PARALLEL 1-WAY FULL SCAN OVER %s\n" - + " SERVER FILTER BY (LPAD(UPPER(NAME, 'en_US'), 8, 'x') || '_xyz') = 'xxUNAME2_xyz'", - dataTableFullName), actualExplainPlan); - - rs = stmt1.executeQuery(selectSql); + ExplainPlan plan = conn.prepareStatement(selectSql) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(dataTableFullName, + explainPlanAttributes.getTableName()); + assertEquals("SERVER FILTER BY (LPAD(UPPER(NAME, 'en_US'), 8, 'x') || '_xyz') = 'xxUNAME2_xyz'", + explainPlanAttributes.getServerWhereFilter()); + + ResultSet rs = stmt1.executeQuery(selectSql); assertTrue(rs.next()); assertEquals(2, rs.getInt(1)); assertFalse(rs.next()); @@ -291,9 +283,20 @@ public void testSecondaryIndex() throws Exception { conn.commit(); // assert we are pulling from index table. - rs = conn.createStatement().executeQuery("EXPLAIN " + selectSql); - actualExplainPlan = QueryUtil.getExplainPlan(rs); - assertExplainPlan(localIndex, actualExplainPlan, dataTableFullName, indexTableFullName); + plan = conn.prepareStatement(selectSql) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + explainPlanAttributes = plan.getPlanStepsAsAttributes(); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + final String expectedTableName; + if (localIndex) { + expectedTableName = dataTableFullName; + } else { + expectedTableName = indexTableFullName; + } + assertEquals(expectedTableName, + explainPlanAttributes.getTableName()); rs = conn.createStatement().executeQuery(selectSql); assertTrue(rs.next()); @@ -456,10 +459,19 @@ public void testIndexToolWithTenantId() throws Exception { tenantId, 0, new String[0]); String selectSql = String.format("SELECT ID FROM %s WHERE NAME='x'", viewTenantName); - ResultSet rs = connTenant.createStatement().executeQuery("EXPLAIN " + selectSql); - String actualExplainPlan = QueryUtil.getExplainPlan(rs); - assertExplainPlan(false, actualExplainPlan, "", viewIndexTableName); - rs = connTenant.createStatement().executeQuery(selectSql); + ExplainPlan plan = connTenant.prepareStatement(selectSql) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(viewIndexTableName, + explainPlanAttributes.getTableName()); + + ResultSet rs = connTenant.createStatement().executeQuery(selectSql); assertTrue(rs.next()); assertEquals(1, rs.getInt(1)); assertFalse(rs.next()); diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/KeyOnlyIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/KeyOnlyIT.java index b682c1aa2b0..a811197e594 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/KeyOnlyIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/KeyOnlyIT.java @@ -32,9 +32,11 @@ import java.util.List; import java.util.Properties; +import org.apache.phoenix.compile.ExplainPlan; +import org.apache.phoenix.compile.ExplainPlanAttributes; +import org.apache.phoenix.jdbc.PhoenixPreparedStatement; import org.apache.phoenix.query.KeyRange; import org.apache.phoenix.util.PropertiesUtil; -import org.apache.phoenix.util.QueryUtil; import org.apache.phoenix.util.TestUtil; import org.junit.Before; import org.junit.Test; @@ -129,7 +131,7 @@ public void testOr() throws Exception { assertEquals(3, rs.getInt(1)); assertFalse(rs.next()); } - + @Test public void testQueryWithLimitAndStats() throws Exception { Properties props = PropertiesUtil.deepCopy(TestUtil.TEST_PROPERTIES); @@ -143,11 +145,20 @@ public void testQueryWithLimitAndStats() throws Exception { assertEquals(0, rs.getInt(1)); assertFalse(rs.next()); - rs = conn.createStatement().executeQuery("EXPLAIN " + query); - assertEquals("CLIENT SERIAL 1-WAY FULL SCAN OVER " + tableName + "\n" + - " SERVER FILTER BY FIRST KEY ONLY\n" + - " SERVER 1 ROW LIMIT\n" + - "CLIENT 1 ROW LIMIT", QueryUtil.getExplainPlan(rs)); + ExplainPlan plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("SERIAL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(tableName, explainPlanAttributes.getTableName()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); + assertEquals(1, explainPlanAttributes.getServerRowLimit().intValue()); + assertEquals(1, explainPlanAttributes.getClientRowLimit().intValue()); } private void initTableValues(Connection conn) throws Exception { diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/LocalIndexSplitMergeIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/LocalIndexSplitMergeIT.java index 0b72f0c6398..57f05da104d 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/LocalIndexSplitMergeIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/LocalIndexSplitMergeIT.java @@ -34,18 +34,19 @@ import org.apache.hadoop.hbase.client.Admin; import org.apache.hadoop.hbase.client.RegionInfo; import org.apache.hadoop.hbase.util.Bytes; +import org.apache.phoenix.compile.ExplainPlan; +import org.apache.phoenix.compile.ExplainPlanAttributes; import org.apache.phoenix.jdbc.PhoenixConnection; +import org.apache.phoenix.jdbc.PhoenixPreparedStatement; import org.apache.phoenix.query.BaseTest; import org.apache.phoenix.query.QueryServices; import org.apache.phoenix.query.QueryServicesOptions; import org.apache.phoenix.util.ByteUtil; import org.apache.phoenix.util.PropertiesUtil; -import org.apache.phoenix.util.QueryUtil; import org.apache.phoenix.util.ReadOnlyProps; import org.apache.phoenix.util.SchemaUtil; import org.apache.phoenix.util.TestUtil; import org.junit.BeforeClass; -import org.junit.Ignore; import org.junit.Test; import org.junit.experimental.categories.Category; @@ -147,20 +148,43 @@ public void testLocalIndexScanAfterRegionSplit() throws Exception { assertEquals(m, k1ColumnValue[m]); } - rs = conn1.createStatement().executeQuery("EXPLAIN " + query); - assertEquals("CLIENT PARALLEL " + (4 + i) + "-WAY RANGE SCAN OVER " - + indexPhysicalTableName + " [1]\n" - + " SERVER FILTER BY FIRST KEY ONLY\n" + "CLIENT MERGE SORT", - QueryUtil.getExplainPlan(rs)); + ExplainPlan plan = conn1.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL " + (4 + i) + "-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(indexPhysicalTableName, + explainPlanAttributes.getTableName()); + assertEquals(" [1]", explainPlanAttributes.getKeyRanges()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); query = "SELECT t_id,k1,k3 FROM " + tableName; - rs = conn1.createStatement().executeQuery("EXPLAIN " + query); - assertEquals( - "CLIENT PARALLEL " - + ((strings[3 * i].compareTo("j") < 0) ? (4 + i) : (4 + i - 1)) - + "-WAY RANGE SCAN OVER " + indexPhysicalTableName + " [2]\n" - + " SERVER FILTER BY FIRST KEY ONLY\n" + "CLIENT MERGE SORT", - QueryUtil.getExplainPlan(rs)); + plan = conn1.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL " + + ((strings[3 * i].compareTo("j") < 0) ? (4 + i) : (4 + i - 1)) + + "-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(indexPhysicalTableName, + explainPlanAttributes.getTableName()); + assertEquals(" [2]", explainPlanAttributes.getKeyRanges()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); + rs = conn1.createStatement().executeQuery(query); Thread.sleep(1000); int[] k3ColumnValue = new int[26]; @@ -238,18 +262,40 @@ public void testLocalIndexScanAfterRegionsMerge() throws Exception { assertEquals(25 - j, rs.getInt("k1")); assertEquals(strings[j], rs.getString("V1")); } - rs = conn1.createStatement().executeQuery("EXPLAIN " + query); - assertEquals( - "CLIENT PARALLEL " + 3 + "-WAY RANGE SCAN OVER " + indexPhysicalTableName + " [1]\n" - + " SERVER FILTER BY FIRST KEY ONLY\n" + "CLIENT MERGE SORT", - QueryUtil.getExplainPlan(rs)); + + ExplainPlan plan = conn1.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 3-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(indexPhysicalTableName, + explainPlanAttributes.getTableName()); + assertEquals(" [1]", explainPlanAttributes.getKeyRanges()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); query = "SELECT t_id,k1,k3 FROM " + tableName; - rs = conn1.createStatement().executeQuery("EXPLAIN " + query); - assertEquals( - "CLIENT PARALLEL " + 3 + "-WAY RANGE SCAN OVER " + indexPhysicalTableName + " [2]\n" - + " SERVER FILTER BY FIRST KEY ONLY\n" + "CLIENT MERGE SORT", - QueryUtil.getExplainPlan(rs)); + plan = conn1.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + explainPlanAttributes = plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 3-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(indexPhysicalTableName, + explainPlanAttributes.getTableName()); + assertEquals(" [2]", explainPlanAttributes.getKeyRanges()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); rs = conn1.createStatement().executeQuery(query); Thread.sleep(1000); diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryWithLimitIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryWithLimitIT.java index 10261f24bd7..3e64fcd9b84 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryWithLimitIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryWithLimitIT.java @@ -31,9 +31,11 @@ import java.util.Properties; import java.util.concurrent.RejectedExecutionException; +import org.apache.phoenix.compile.ExplainPlan; +import org.apache.phoenix.compile.ExplainPlanAttributes; +import org.apache.phoenix.jdbc.PhoenixPreparedStatement; import org.apache.phoenix.query.QueryServices; import org.apache.phoenix.util.PropertiesUtil; -import org.apache.phoenix.util.QueryUtil; import org.apache.phoenix.util.ReadOnlyProps; import org.apache.phoenix.util.TestUtil; import org.junit.Before; @@ -81,11 +83,21 @@ public void testQueryWithLimitAndStats() throws Exception { assertEquals(0, rs.getInt(1)); assertFalse(rs.next()); - rs = conn.createStatement().executeQuery("EXPLAIN " + query); - assertEquals("CLIENT SERIAL 1-WAY FULL SCAN OVER " + tableName + "\n" + - " SERVER FILTER BY FIRST KEY ONLY\n" + - " SERVER 1 ROW LIMIT\n" + - "CLIENT 1 ROW LIMIT", QueryUtil.getExplainPlan(rs)); + ExplainPlan plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("SERIAL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(tableName, + explainPlanAttributes.getTableName()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); + assertEquals(1, explainPlanAttributes.getServerRowLimit().intValue()); + assertEquals(1, explainPlanAttributes.getClientRowLimit().intValue()); } finally { conn.close(); } diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryWithOffsetIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryWithOffsetIT.java index a1d562fe730..902ee54e391 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryWithOffsetIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryWithOffsetIT.java @@ -34,13 +34,11 @@ import java.util.Collection; import java.util.Properties; -import org.apache.hadoop.hbase.util.Bytes; -import org.apache.phoenix.jdbc.PhoenixConnection; +import org.apache.phoenix.compile.ExplainPlan; +import org.apache.phoenix.compile.ExplainPlanAttributes; +import org.apache.phoenix.jdbc.PhoenixPreparedStatement; import org.apache.phoenix.query.QueryServices; -import org.apache.phoenix.schema.stats.GuidePostsInfo; -import org.apache.phoenix.schema.stats.GuidePostsKey; import org.apache.phoenix.util.PropertiesUtil; -import org.apache.phoenix.util.QueryUtil; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -128,30 +126,57 @@ public void testOffsetSerialQueryExecutedOnServer() throws SQLException { initTableValues(conn); updateStatistics(conn); String query = "SELECT t_id from " + tableName + " offset " + offset; - ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + query); - if(!isSalted){ - assertEquals("CLIENT SERIAL 1-WAY FULL SCAN OVER " + tableName + "\n" - + " SERVER FILTER BY FIRST KEY ONLY\n" - + " SERVER OFFSET " + offset, QueryUtil.getExplainPlan(rs)); - }else{ - assertEquals("CLIENT PARALLEL 10-WAY FULL SCAN OVER " + tableName + "\n" - + " SERVER FILTER BY FIRST KEY ONLY\n" - + "CLIENT MERGE SORT\n" + "CLIENT OFFSET " + offset, QueryUtil.getExplainPlan(rs)); + ExplainPlan plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(tableName, + explainPlanAttributes.getTableName()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); + if (!isSalted) { + assertEquals("SERIAL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals(offset, explainPlanAttributes.getServerOffset() + .intValue()); + } else { + assertEquals("PARALLEL 10-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); + assertEquals(offset, explainPlanAttributes.getClientOffset() + .intValue()); } - rs = conn.createStatement().executeQuery(query); + + ResultSet rs = conn.createStatement().executeQuery(query); int i = 0; while (i++ < STRINGS.length - offset) { assertTrue(rs.next()); assertEquals(STRINGS[offset + i - 1], rs.getString(1)); } query = "SELECT t_id from " + tableName + " ORDER BY v1 offset " + offset; - rs = conn.createStatement().executeQuery("EXPLAIN " + query); + plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + explainPlanAttributes = plan.getPlanStepsAsAttributes(); + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(tableName, + explainPlanAttributes.getTableName()); + assertEquals("[C2.V1]", explainPlanAttributes.getServerSortedBy()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); + assertEquals(offset, explainPlanAttributes.getClientOffset() + .intValue()); if (!isSalted) { - assertEquals("CLIENT PARALLEL 5-WAY FULL SCAN OVER " + tableName + "\n" + " SERVER SORTED BY [C2.V1]\n" - + "CLIENT MERGE SORT\n" + "CLIENT OFFSET " + offset, QueryUtil.getExplainPlan(rs)); + assertEquals("PARALLEL 5-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); } else { - assertEquals("CLIENT PARALLEL 10-WAY FULL SCAN OVER " + tableName + "\n" + " SERVER SORTED BY [C2.V1]\n" - + "CLIENT MERGE SORT\n" + "CLIENT OFFSET " + offset, QueryUtil.getExplainPlan(rs)); + assertEquals("PARALLEL 10-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); } conn.close(); } diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryWithTableSampleIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryWithTableSampleIT.java index a7a79528b96..e5e078416e0 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryWithTableSampleIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryWithTableSampleIT.java @@ -25,10 +25,12 @@ import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; -import java.sql.SQLException; import java.util.Properties; +import org.apache.phoenix.compile.ExplainPlan; +import org.apache.phoenix.compile.ExplainPlanAttributes; import org.apache.phoenix.exception.PhoenixParserException; +import org.apache.phoenix.jdbc.PhoenixPreparedStatement; import org.apache.phoenix.util.PropertiesUtil; import org.apache.phoenix.util.QueryUtil; import org.apache.phoenix.util.TestUtil; @@ -204,15 +206,22 @@ public void testSingleQueryWithJoins() throws Exception { @Test public void testExplainSingleQuery() throws Exception { Properties props = PropertiesUtil.deepCopy(TestUtil.TEST_PROPERTIES); - Connection conn = DriverManager.getConnection(getUrl(), props); - try { + try (Connection conn = DriverManager.getConnection(getUrl(), props)) { prepareTableWithValues(conn, 100); - String query = "EXPLAIN SELECT i1, i2 FROM " + tableName +" tablesample (45) "; - ResultSet rs = conn.createStatement().executeQuery(query); - assertEquals("CLIENT PARALLEL 1-WAY 0.45-SAMPLED FULL SCAN OVER " + tableName + "\n" + - " SERVER FILTER BY FIRST KEY ONLY",QueryUtil.getExplainPlan(rs)); - } finally { - conn.close(); + String query = "SELECT i1, i2 FROM " + tableName + " tablesample (45) "; + ExplainPlan plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(0.45D, explainPlanAttributes.getSamplingRate(), 0D); + assertEquals(tableName, explainPlanAttributes.getTableName()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); } } diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ReverseScanIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ReverseScanIT.java index 8ea1876433f..e0a91ddc89b 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ReverseScanIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ReverseScanIT.java @@ -38,12 +38,13 @@ import java.util.Properties; import org.apache.hadoop.hbase.util.Bytes; +import org.apache.phoenix.compile.ExplainPlan; +import org.apache.phoenix.compile.ExplainPlanAttributes; +import org.apache.phoenix.jdbc.PhoenixPreparedStatement; import org.apache.phoenix.util.PropertiesUtil; -import org.apache.phoenix.util.QueryUtil; import org.junit.Test; - public class ReverseScanIT extends ParallelStatsDisabledIT { private static byte[][] getSplitsAtRowKeys(String tenantId) { @@ -59,51 +60,58 @@ public void testReverseRangeScan() throws Exception { String tenantId = getOrganizationId(); String tableName = initATableValues(tenantId, getSplitsAtRowKeys(tenantId), getUrl()); Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); - Connection conn = DriverManager.getConnection(getUrl(), props); - String query = "SELECT entity_id FROM " + tableName + " WHERE entity_id >= '" + ROW3 + "' ORDER BY organization_id DESC, entity_id DESC"; - try { + try (Connection conn = DriverManager.getConnection(getUrl(), props)) { + String query = "SELECT entity_id FROM " + tableName + " WHERE entity_id >= '" + + ROW3 + "' ORDER BY organization_id DESC, entity_id DESC"; Statement stmt = conn.createStatement(); stmt.setFetchSize(2); ResultSet rs = stmt.executeQuery(query); - assertTrue (rs.next()); - assertEquals(ROW9,rs.getString(1)); - assertTrue (rs.next()); - assertEquals(ROW8,rs.getString(1)); - assertTrue (rs.next()); - assertEquals(ROW7,rs.getString(1)); - assertTrue (rs.next()); - assertEquals(ROW6,rs.getString(1)); - assertTrue (rs.next()); - assertEquals(ROW5,rs.getString(1)); - assertTrue (rs.next()); - assertEquals(ROW4,rs.getString(1)); - assertTrue (rs.next()); - assertEquals(ROW3,rs.getString(1)); + assertTrue(rs.next()); + assertEquals(ROW9, rs.getString(1)); + assertTrue(rs.next()); + assertEquals(ROW8, rs.getString(1)); + assertTrue(rs.next()); + assertEquals(ROW7, rs.getString(1)); + assertTrue(rs.next()); + assertEquals(ROW6, rs.getString(1)); + assertTrue(rs.next()); + assertEquals(ROW5, rs.getString(1)); + assertTrue(rs.next()); + assertEquals(ROW4, rs.getString(1)); + assertTrue(rs.next()); + assertEquals(ROW3, rs.getString(1)); assertFalse(rs.next()); - - rs = conn.createStatement().executeQuery("EXPLAIN " + query); - assertEquals( - "CLIENT PARALLEL 1-WAY REVERSE FULL SCAN OVER " + tableName + "\n" + - " SERVER FILTER BY FIRST KEY ONLY AND ENTITY_ID >= '00A323122312312'", - QueryUtil.getExplainPlan(rs)); - - PreparedStatement statement = conn.prepareStatement("SELECT entity_id FROM " + tableName + " WHERE organization_id = ? AND entity_id >= ? ORDER BY organization_id DESC, entity_id DESC"); + + ExplainPlan plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("REVERSE", explainPlanAttributes.getClientSortedBy()); + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(tableName, explainPlanAttributes.getTableName()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY AND ENTITY_ID >= '00A323122312312'", + explainPlanAttributes.getServerWhereFilter()); + + PreparedStatement statement = conn.prepareStatement( + "SELECT entity_id FROM " + tableName + " WHERE organization_id = ? AND entity_id >= ? ORDER BY organization_id DESC, entity_id DESC"); statement.setString(1, tenantId); statement.setString(2, ROW7); rs = statement.executeQuery(); - assertTrue (rs.next()); - assertEquals(ROW9,rs.getString(1)); - assertTrue (rs.next()); - assertEquals(ROW8,rs.getString(1)); - assertTrue (rs.next()); - assertEquals(ROW7,rs.getString(1)); + assertTrue(rs.next()); + assertEquals(ROW9, rs.getString(1)); + assertTrue(rs.next()); + assertEquals(ROW8, rs.getString(1)); + assertTrue(rs.next()); + assertEquals(ROW7, rs.getString(1)); assertFalse(rs.next()); - } finally { - conn.close(); } } @@ -168,26 +176,38 @@ public void testReverseScanIndex() throws Exception { String tableName = initATableValues(tenantId, getSplitsAtRowKeys(tenantId), getUrl()); Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); - Connection conn = DriverManager.getConnection(getUrl(), props); - String ddl = "CREATE INDEX " + indexName + " ON " + tableName + " (a_integer DESC) INCLUDE (" - + " A_STRING, " + " B_STRING, " + " A_DATE)"; - conn.createStatement().execute(ddl); - - String query = - "SELECT a_integer FROM " + tableName + " where a_integer is not null order by a_integer nulls last limit 1"; + try (Connection conn = DriverManager.getConnection(getUrl(), props)) { + String ddl = "CREATE INDEX " + indexName + " ON " + tableName + + " (a_integer DESC) INCLUDE (" + " A_STRING, " + " B_STRING, " + " A_DATE)"; + conn.createStatement().execute(ddl); + + String query = "SELECT a_integer FROM " + tableName + + " where a_integer is not null order by a_integer nulls last limit 1"; + + PreparedStatement statement = conn.prepareStatement(query); + ResultSet rs = statement.executeQuery(); + assertTrue(rs.next()); + assertEquals(1, rs.getInt(1)); + assertFalse(rs.next()); + + ExplainPlan plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("SERIAL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("REVERSE", explainPlanAttributes.getClientSortedBy()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(indexName, explainPlanAttributes.getTableName()); + assertEquals(" [not null]", explainPlanAttributes.getKeyRanges()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); + assertEquals(1, explainPlanAttributes.getServerRowLimit().intValue()); + assertEquals(1, explainPlanAttributes.getClientRowLimit().intValue()); + } - PreparedStatement statement = conn.prepareStatement(query); - ResultSet rs=statement.executeQuery(); - assertTrue(rs.next()); - assertEquals(1,rs.getInt(1)); - assertFalse(rs.next()); - - rs = conn.createStatement().executeQuery("EXPLAIN " + query); - assertEquals( - "CLIENT SERIAL 1-WAY REVERSE RANGE SCAN OVER " + indexName + " [not null]\n" + - " SERVER FILTER BY FIRST KEY ONLY\n" + - " SERVER 1 ROW LIMIT\n" + - "CLIENT 1 ROW LIMIT",QueryUtil.getExplainPlan(rs)); } } \ No newline at end of file diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java index e4b811e4275..7b61b706673 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java @@ -51,11 +51,12 @@ import java.util.List; import java.util.Properties; +import org.apache.phoenix.compile.ExplainPlan; +import org.apache.phoenix.compile.ExplainPlanAttributes; import org.apache.phoenix.jdbc.PhoenixPreparedStatement; import org.apache.phoenix.util.DateUtil; import org.apache.phoenix.util.PhoenixRuntime; import org.apache.phoenix.util.PropertiesUtil; -import org.apache.phoenix.util.QueryUtil; import org.apache.phoenix.util.SchemaUtil; import org.junit.Test; @@ -1225,11 +1226,20 @@ public void testForceSkipScan() throws Exception { assertFalse(rs.next()); - String plan = "CLIENT PARALLEL 4-WAY SKIP SCAN ON 12 KEYS OVER " + tempTableWithCompositePK + " [0,2] - [3,4]\n" + - "CLIENT MERGE SORT"; - String explainQuery = "EXPLAIN " + query; - rs = conn.createStatement().executeQuery(explainQuery); - assertEquals(query, plan, QueryUtil.getExplainPlan(rs)); + ExplainPlan plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 4-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("SKIP SCAN ON 12 KEYS ", + explainPlanAttributes.getExplainScanType()); + assertEquals(tempTableWithCompositePK, + explainPlanAttributes.getTableName()); + assertEquals(" [0,2] - [3,4]", explainPlanAttributes.getKeyRanges()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); } finally { conn.close(); } diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SequenceBulkAllocationIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SequenceBulkAllocationIT.java index f8295e047a8..5c56c4f0a3f 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SequenceBulkAllocationIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SequenceBulkAllocationIT.java @@ -32,9 +32,11 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import org.apache.phoenix.compile.ExplainPlan; +import org.apache.phoenix.compile.ExplainPlanAttributes; import org.apache.phoenix.exception.SQLExceptionCode; +import org.apache.phoenix.jdbc.PhoenixPreparedStatement; import org.apache.phoenix.util.PropertiesUtil; -import org.apache.phoenix.util.QueryUtil; import org.apache.phoenix.util.SchemaUtil; import org.junit.After; import org.junit.Before; @@ -845,12 +847,24 @@ public void testExplainPlanForNextValuesFor() throws Exception { genericConn.close(); // Execute EXPLAIN PLAN which should not change Sequence values - ResultSet rs = conn.createStatement().executeQuery("EXPLAIN SELECT NEXT 1000 VALUES FOR " + sequenceName + " FROM " + tableName); + String query = "SELECT NEXT 1000 VALUES FOR " + sequenceName + + " FROM " + tableName; // Assert output for Explain Plain result is as expected - assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + tableName + "\n" + - " SERVER FILTER BY FIRST KEY ONLY\n" + - "CLIENT RESERVE VALUES FROM 1 SEQUENCE", QueryUtil.getExplainPlan(rs)); + ExplainPlan plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(tableName, explainPlanAttributes.getTableName()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); + assertEquals(1, + explainPlanAttributes.getClientSequenceCount().intValue()); } diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SequenceIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SequenceIT.java index 1a4ee2ede49..06186d07881 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SequenceIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SequenceIT.java @@ -36,8 +36,11 @@ import java.util.List; import java.util.Properties; +import org.apache.phoenix.compile.ExplainPlan; +import org.apache.phoenix.compile.ExplainPlanAttributes; import org.apache.phoenix.exception.SQLExceptionCode; import org.apache.phoenix.jdbc.PhoenixDatabaseMetaData; +import org.apache.phoenix.jdbc.PhoenixPreparedStatement; import org.apache.phoenix.jdbc.PhoenixStatement; import org.apache.phoenix.query.QueryServices; import org.apache.phoenix.query.QueryServicesTestImpl; @@ -48,7 +51,6 @@ import org.apache.phoenix.util.EnvironmentEdgeManager; import org.apache.phoenix.util.PhoenixRuntime; import org.apache.phoenix.util.PropertiesUtil; -import org.apache.phoenix.util.QueryUtil; import org.apache.phoenix.util.SchemaUtil; import org.apache.phoenix.util.SequenceUtil; import org.junit.After; @@ -731,13 +733,25 @@ public void testExplainPlanValidatesSequences() throws Exception { conn.createStatement().execute("CREATE TABLE " + tableName + " (k BIGINT NOT NULL PRIMARY KEY)"); Connection conn2 = DriverManager.getConnection(getUrl(), PropertiesUtil.deepCopy(TEST_PROPERTIES)); - ResultSet rs = conn2.createStatement().executeQuery("EXPLAIN SELECT NEXT VALUE FOR " + sequenceName + " FROM " + tableName); - assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER "+ tableName +"\n" + - " SERVER FILTER BY FIRST KEY ONLY\n" + - "CLIENT RESERVE VALUES FROM 1 SEQUENCE", QueryUtil.getExplainPlan(rs)); - - - rs = conn.createStatement().executeQuery("SELECT sequence_name, current_value FROM \"SYSTEM\".\"SEQUENCE\" WHERE sequence_name='" + sequenceNameWithoutSchema + "'"); + String query = "SELECT NEXT VALUE FOR " + sequenceName + " FROM " + tableName; + ExplainPlan plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(tableName, explainPlanAttributes.getTableName()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); + assertEquals(1, + explainPlanAttributes.getClientSequenceCount().intValue()); + + ResultSet rs = conn.createStatement().executeQuery( + "SELECT sequence_name, current_value FROM \"SYSTEM\".\"SEQUENCE\" WHERE sequence_name='" + + sequenceNameWithoutSchema + "'"); assertTrue(rs.next()); assertEquals(sequenceNameWithoutSchema, rs.getString(1)); assertEquals(1, rs.getInt(2)); @@ -1382,15 +1396,29 @@ public void testNoFromClause() throws Exception { String secondSeqName = alternateSequenceName; conn.createStatement().execute("CREATE SEQUENCE " + seqName + " START WITH 1 INCREMENT BY 1"); conn.createStatement().execute("CREATE SEQUENCE " + secondSeqName + " START WITH 2 INCREMENT BY 3"); - - rs = conn.createStatement().executeQuery("EXPLAIN SELECT NEXT VALUE FOR " + seqName); - assertEquals("CLIENT RESERVE VALUES FROM 1 SEQUENCE", QueryUtil.getExplainPlan(rs)); - rs = conn.createStatement().executeQuery("SELECT NEXT VALUE FOR " + seqName); + + String query = "SELECT NEXT VALUE FOR " + seqName; + ExplainPlan plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals(new Integer(1), + explainPlanAttributes.getClientSequenceCount()); + + rs = conn.createStatement().executeQuery(query); assertTrue(rs.next()); assertEquals(1, rs.getInt(1)); - rs = conn.createStatement().executeQuery("EXPLAIN SELECT CURRENT VALUE FOR " + seqName); - assertEquals("CLIENT RESERVE VALUES FROM 1 SEQUENCE", QueryUtil.getExplainPlan(rs)); - rs = conn.createStatement().executeQuery("SELECT CURRENT VALUE FOR " + seqName); + + query = "SELECT CURRENT VALUE FOR " + seqName; + plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + explainPlanAttributes = plan.getPlanStepsAsAttributes(); + assertEquals(new Integer(1), + explainPlanAttributes.getClientSequenceCount()); + + rs = conn.createStatement().executeQuery(query); assertTrue(rs.next()); assertEquals(1, rs.getInt(1)); rs = conn.createStatement().executeQuery("SELECT NEXT VALUE FOR " + seqName + ", NEXT VALUE FOR " + secondSeqName); diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SpillableGroupByIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SpillableGroupByIT.java index c6692f830f4..ec4526c8fde 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SpillableGroupByIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SpillableGroupByIT.java @@ -33,10 +33,12 @@ import java.util.Map; import java.util.Properties; +import org.apache.phoenix.compile.ExplainPlan; +import org.apache.phoenix.compile.ExplainPlanAttributes; import org.apache.phoenix.exception.SQLExceptionCode; +import org.apache.phoenix.jdbc.PhoenixPreparedStatement; import org.apache.phoenix.query.QueryServices; import org.apache.phoenix.util.PropertiesUtil; -import org.apache.phoenix.util.QueryUtil; import org.apache.phoenix.util.ReadOnlyProps; import org.junit.Assert; import org.junit.BeforeClass; @@ -189,12 +191,19 @@ public void testStatisticsAreNotWritten() throws SQLException { assertFalse(rs.next()); rs.close(); stmt.close(); - rs = conn.createStatement().executeQuery("EXPLAIN SELECT * FROM " + tableName); - String explainPlan = QueryUtil.getExplainPlan(rs); - assertEquals( - "CLIENT 1-CHUNK PARALLEL 1-WAY FULL SCAN OVER " + tableName, - explainPlan); - conn.close(); + String query = "SELECT * FROM " + tableName; + ExplainPlan plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals(new Integer(1), explainPlanAttributes.getSplitsChunk()); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(tableName, explainPlanAttributes.getTableName()); + conn.close(); } @Test diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificViewIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificViewIndexIT.java index 5bf893fd56b..0d41ca821a0 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificViewIndexIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificViewIndexIT.java @@ -22,6 +22,7 @@ import static org.apache.phoenix.util.PhoenixRuntime.TENANT_ID_ATTRIB; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -30,11 +31,12 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.Statement; -import java.util.List; -import java.util.Map; import java.util.Properties; import org.apache.hadoop.hbase.util.Bytes; +import org.apache.phoenix.compile.ExplainPlan; +import org.apache.phoenix.compile.ExplainPlanAttributes; +import org.apache.phoenix.jdbc.PhoenixPreparedStatement; import org.apache.phoenix.query.QueryServices; import org.apache.phoenix.schema.ColumnNotFoundException; import org.apache.phoenix.schema.PNameFactory; @@ -44,10 +46,6 @@ import org.apache.phoenix.util.SchemaUtil; import org.junit.Test; -import org.apache.phoenix.thirdparty.com.google.common.collect.Lists; -import org.apache.phoenix.thirdparty.com.google.common.collect.Maps; - - public class TenantSpecificViewIndexIT extends BaseTenantSpecificViewIndexIT { @Test @@ -189,29 +187,57 @@ private void createViewAndIndexesWithTenantId(String tableName, String viewName, assertEquals("f", rs.getString(2)); assertEquals("g", rs.getString(3)); assertFalse(rs.next()); - rs = conn.createStatement().executeQuery("explain select * from " + viewName); - assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " - + SchemaUtil.getPhysicalTableName(Bytes.toBytes(tableName), isNamespaceMapped) + " ['" - + tenantId + "']", QueryUtil.getExplainPlan(rs)); + + String query = "select * from " + viewName; + ExplainPlan plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(SchemaUtil.getPhysicalTableName( + Bytes.toBytes(tableName), isNamespaceMapped).toString(), + explainPlanAttributes.getTableName()); + assertEquals(" ['" + tenantId + "']", + explainPlanAttributes.getKeyRanges()); rs = conn.createStatement().executeQuery("select pk2,col1 from " + viewName + " where col1='f'"); assertTrue(rs.next()); assertEquals("e", rs.getString(1)); assertEquals("f", rs.getString(2)); assertFalse(rs.next()); - rs = conn.createStatement().executeQuery("explain select pk2,col1 from " + viewName + " where col1='f'"); + query = "select pk2,col1 from " + viewName + " where col1='f'"; + plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); if (localIndex) { - assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " - + SchemaUtil.getPhysicalTableName(Bytes.toBytes(tableName), isNamespaceMapped) + - " [" + Long.toString(1L + indexIdOffset) + ",'" - + tenantId + "','f']\n" + " SERVER FILTER BY FIRST KEY ONLY\n" + "CLIENT MERGE SORT", - QueryUtil.getExplainPlan(rs)); + assertEquals(SchemaUtil.getPhysicalTableName( + Bytes.toBytes(tableName), isNamespaceMapped).toString(), + explainPlanAttributes.getTableName()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); + assertEquals(" [" + (1L + indexIdOffset) + ",'" + + tenantId + "','f']", explainPlanAttributes.getKeyRanges()); } else { - assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " - + Bytes.toString(MetaDataUtil.getViewIndexPhysicalName( - SchemaUtil.getPhysicalTableName(Bytes.toBytes(tableName), isNamespaceMapped).toBytes())) - + " [" + Long.toString(Short.MIN_VALUE + indexIdOffset) + ",'" + tenantId + "','f']\n" + " SERVER FILTER BY FIRST KEY ONLY", - QueryUtil.getExplainPlan(rs)); + assertEquals( + Bytes.toString(MetaDataUtil.getViewIndexPhysicalName( + SchemaUtil.getPhysicalTableName(Bytes.toBytes(tableName), + isNamespaceMapped).toBytes())), + explainPlanAttributes.getTableName()); + assertNull(explainPlanAttributes.getClientSortAlgo()); + assertEquals(" [" + (Short.MIN_VALUE + indexIdOffset) + ",'" + + tenantId + "','f']", explainPlanAttributes.getKeyRanges()); } try { diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/UnionAllIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/UnionAllIT.java index eaeb6e303de..ec717b0ebeb 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/UnionAllIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/UnionAllIT.java @@ -20,6 +20,7 @@ import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -32,7 +33,10 @@ import java.sql.Statement; import java.util.Properties; +import org.apache.phoenix.compile.ExplainPlan; +import org.apache.phoenix.compile.ExplainPlanAttributes; import org.apache.phoenix.exception.SQLExceptionCode; +import org.apache.phoenix.jdbc.PhoenixPreparedStatement; import org.apache.phoenix.util.PropertiesUtil; import org.apache.phoenix.util.QueryUtil; import org.junit.Test; @@ -604,59 +608,122 @@ public void testExplainUnionAll() throws Exception { String tableName1 = generateUniqueName(); String tableName2 = generateUniqueName(); Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); - Connection conn = DriverManager.getConnection(getUrl(), props); - conn.setAutoCommit(false); - try { + try (Connection conn = DriverManager.getConnection(getUrl(), props)) { + conn.setAutoCommit(false); String ddl = "CREATE TABLE " + tableName1 + " " + - " (a_string varchar not null, col1 integer" + - " CONSTRAINT pk PRIMARY KEY (a_string))\n"; + " (a_string varchar not null, col1 integer" + + " CONSTRAINT pk PRIMARY KEY (a_string))\n"; createTestTable(getUrl(), ddl); ddl = "CREATE TABLE " + tableName2 + " " + - " (a_string varchar not null, col1 integer" + - " CONSTRAINT pk PRIMARY KEY (a_string))\n"; + " (a_string varchar not null, col1 integer" + + " CONSTRAINT pk PRIMARY KEY (a_string))\n"; createTestTable(getUrl(), ddl); - ddl = "explain select a_string, col1 from " + tableName1 + " union all select a_string, col1 from " + tableName2 + " order by col1 limit 1"; - ResultSet rs = conn.createStatement().executeQuery(ddl); - assertEquals( - "UNION ALL OVER 2 QUERIES\n" + - " CLIENT PARALLEL 1-WAY FULL SCAN OVER " + tableName1 + "\n" + - " SERVER TOP 1 ROW SORTED BY [COL1]\n" + - " CLIENT MERGE SORT\n" + - " CLIENT LIMIT 1\n" + - " CLIENT PARALLEL 1-WAY FULL SCAN OVER " + tableName2 + "\n" + - " SERVER TOP 1 ROW SORTED BY [COL1]\n" + - " CLIENT MERGE SORT\n" + - " CLIENT LIMIT 1\n" + - "CLIENT MERGE SORT\nCLIENT LIMIT 1", QueryUtil.getExplainPlan(rs)); - - String limitPlan = - "UNION ALL OVER 2 QUERIES\n" + - " CLIENT SERIAL 1-WAY FULL SCAN OVER " + tableName1 + "\n" + - " SERVER 2 ROW LIMIT\n" + - " CLIENT 2 ROW LIMIT\n" + - " CLIENT SERIAL 1-WAY FULL SCAN OVER " + tableName2 + "\n" + - " SERVER 2 ROW LIMIT\n" + - " CLIENT 2 ROW LIMIT\n" + + ddl = "select a_string, col1 from " + tableName1 + + " union all select a_string, col1 from " + tableName2 + + " order by col1 limit 1"; + ExplainPlan plan = conn.prepareStatement(ddl) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("UNION ALL OVER 2 QUERIES", + explainPlanAttributes.getAbstractExplainPlan()); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(tableName1, explainPlanAttributes.getTableName()); + assertEquals("[COL1]", explainPlanAttributes.getServerSortedBy()); + assertEquals(1L, + explainPlanAttributes.getServerRowLimit().longValue()); + assertEquals(1, + explainPlanAttributes.getClientRowLimit().intValue()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); + ExplainPlanAttributes rhsPlanAttributes = + explainPlanAttributes.getRhsJoinQueryExplainPlan(); + assertEquals("PARALLEL 1-WAY", + rhsPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("FULL SCAN ", + rhsPlanAttributes.getExplainScanType()); + assertEquals(tableName2, rhsPlanAttributes.getTableName()); + assertEquals("[COL1]", rhsPlanAttributes.getServerSortedBy()); + assertEquals(1L, + rhsPlanAttributes.getServerRowLimit().longValue()); + assertEquals(1, + rhsPlanAttributes.getClientRowLimit().intValue()); + assertEquals("CLIENT MERGE SORT", + rhsPlanAttributes.getClientSortAlgo()); + + String limitPlan = + "UNION ALL OVER 2 QUERIES\n" + + " CLIENT SERIAL 1-WAY FULL SCAN OVER " + tableName1 + "\n" + + " SERVER 2 ROW LIMIT\n" + + " CLIENT 2 ROW LIMIT\n" + + " CLIENT SERIAL 1-WAY FULL SCAN OVER " + tableName2 + "\n" + + " SERVER 2 ROW LIMIT\n" + + " CLIENT 2 ROW LIMIT\n" + "CLIENT 2 ROW LIMIT"; - ddl = "explain select a_string, col1 from " + tableName1 + " union all select a_string, col1 from " + tableName2; - rs = conn.createStatement().executeQuery(ddl + " limit 2"); - assertEquals(limitPlan, QueryUtil.getExplainPlan(rs)); + + ddl = "select a_string, col1 from " + tableName1 + " union all select a_string, col1 from " + + tableName2 + " limit 2"; + plan = conn.prepareStatement(ddl) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + explainPlanAttributes = plan.getPlanStepsAsAttributes(); + assertEquals("UNION ALL OVER 2 QUERIES", + explainPlanAttributes.getAbstractExplainPlan()); + assertEquals("SERIAL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(tableName1, explainPlanAttributes.getTableName()); + assertNull(explainPlanAttributes.getServerSortedBy()); + assertEquals(2L, + explainPlanAttributes.getServerRowLimit().longValue()); + assertEquals(2, + explainPlanAttributes.getClientRowLimit().intValue()); + rhsPlanAttributes = + explainPlanAttributes.getRhsJoinQueryExplainPlan(); + assertEquals("SERIAL 1-WAY", + rhsPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("FULL SCAN ", + rhsPlanAttributes.getExplainScanType()); + assertEquals(tableName2, rhsPlanAttributes.getTableName()); + assertNull(rhsPlanAttributes.getServerSortedBy()); + assertEquals(2L, + rhsPlanAttributes.getServerRowLimit().longValue()); + assertEquals(2, + rhsPlanAttributes.getClientRowLimit().intValue()); + Statement stmt = conn.createStatement(); stmt.setMaxRows(2); - rs = stmt.executeQuery(ddl); + ResultSet rs = stmt.executeQuery("explain " + ddl); assertEquals(limitPlan, QueryUtil.getExplainPlan(rs)); - - ddl = "explain select a_string, col1 from " + tableName1 + " union all select a_string, col1 from " + tableName2; - rs = conn.createStatement().executeQuery(ddl); - assertEquals( - "UNION ALL OVER 2 QUERIES\n" + - " CLIENT PARALLEL 1-WAY FULL SCAN OVER " + tableName1 + "\n" + - " CLIENT PARALLEL 1-WAY FULL SCAN OVER " + tableName2, QueryUtil.getExplainPlan(rs)); - } finally { - conn.close(); + + ddl = "select a_string, col1 from " + tableName1 + + " union all select a_string, col1 from " + tableName2; + plan = conn.prepareStatement(ddl) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + explainPlanAttributes = plan.getPlanStepsAsAttributes(); + assertEquals("UNION ALL OVER 2 QUERIES", + explainPlanAttributes.getAbstractExplainPlan()); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(tableName1, explainPlanAttributes.getTableName()); + rhsPlanAttributes = + explainPlanAttributes.getRhsJoinQueryExplainPlan(); + assertEquals("PARALLEL 1-WAY", + rhsPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("FULL SCAN ", + rhsPlanAttributes.getExplainScanType()); + assertEquals(tableName2, rhsPlanAttributes.getTableName()); } } diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/UserDefinedFunctionsIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/UserDefinedFunctionsIT.java index 5a9aac145d1..3ee8201fc82 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/UserDefinedFunctionsIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/UserDefinedFunctionsIT.java @@ -57,14 +57,16 @@ import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop.hbase.HBaseTestingUtility; +import org.apache.phoenix.compile.ExplainPlan; +import org.apache.phoenix.compile.ExplainPlanAttributes; import org.apache.phoenix.expression.function.UDFExpression; +import org.apache.phoenix.jdbc.PhoenixPreparedStatement; import org.apache.phoenix.jdbc.PhoenixTestDriver; import org.apache.phoenix.query.QueryServices; import org.apache.phoenix.schema.FunctionAlreadyExistsException; import org.apache.phoenix.schema.FunctionNotFoundException; import org.apache.phoenix.schema.ValueRangeExcpetion; import org.apache.phoenix.util.PhoenixRuntime; -import org.apache.phoenix.util.QueryUtil; import org.apache.phoenix.util.ReadOnlyProps; import org.junit.After; import org.junit.Before; @@ -795,19 +797,43 @@ public void testFunctionalIndexesWithUDFFunction() throws Exception { conn.commit(); stmt.execute("create index idx on t5(myreverse5(lastname_reverse))"); String query = "select myreverse5(lastname_reverse) from t5"; - ResultSet rs = stmt.executeQuery("explain " + query); - assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER IDX\n" - + " SERVER FILTER BY FIRST KEY ONLY", QueryUtil.getExplainPlan(rs)); - rs = stmt.executeQuery(query); + + ExplainPlan plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals("IDX", explainPlanAttributes.getTableName()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); + + ResultSet rs = stmt.executeQuery(query); assertTrue(rs.next()); assertEquals("kcoj", rs.getString(1)); assertFalse(rs.next()); stmt.execute("create local index idx2 on t5(myreverse5(lastname_reverse))"); query = "select k,k1,myreverse5(lastname_reverse) from t5 where myreverse5(lastname_reverse)='kcoj'"; - rs = stmt.executeQuery("explain " + query); - assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER T5 [1,'kcoj']\n" - + " SERVER FILTER BY FIRST KEY ONLY\n" - +"CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs)); + + plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals("T5", explainPlanAttributes.getTableName()); + assertEquals(" [1,'kcoj']", explainPlanAttributes.getKeyRanges()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); + rs = stmt.executeQuery(query); assertTrue(rs.next()); assertEquals(1, rs.getInt(1)); diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewIT.java index e6056f54b1f..370629d0d36 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewIT.java @@ -26,6 +26,7 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -45,8 +46,11 @@ import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.util.Pair; +import org.apache.phoenix.compile.ExplainPlan; +import org.apache.phoenix.compile.ExplainPlanAttributes; import org.apache.phoenix.compile.QueryPlan; import org.apache.phoenix.exception.SQLExceptionCode; +import org.apache.phoenix.jdbc.PhoenixPreparedStatement; import org.apache.phoenix.jdbc.PhoenixStatement; import org.apache.phoenix.query.KeyRange; import org.apache.phoenix.query.QueryServices; @@ -58,7 +62,6 @@ import org.apache.phoenix.util.EnvironmentEdgeManager; import org.apache.phoenix.util.MetaDataUtil; import org.apache.phoenix.util.PhoenixRuntime; -import org.apache.phoenix.util.QueryUtil; import org.apache.phoenix.util.ReadOnlyProps; import org.apache.phoenix.util.SchemaUtil; import org.apache.phoenix.util.TestUtil; @@ -417,23 +420,32 @@ private void testViewUsesTableIndex(boolean localIndex) throws Exception { assertTrue(rs.next()); assertEquals(100, rs.getInt(1)); assertFalse(rs.next()); - rs = stmt.executeQuery("EXPLAIN " + query); - String queryPlan = QueryUtil.getExplainPlan(rs); + + ExplainPlan plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("SKIP SCAN ON 4 KEYS ", + explainPlanAttributes.getExplainScanType()); + assertEquals("SERVER FILTER BY (\"S2\" = 'bas' AND \"S1\" = 'foo')", + explainPlanAttributes.getServerWhereFilter()); + // Assert that in either case (local & global) that index from // physical table used for query on view. if (localIndex) { - assertEquals("CLIENT PARALLEL 1-WAY SKIP SCAN ON 4 KEYS OVER " - + fullTableName - + " [1,1,100] - [1,2,109]\n" - + " SERVER FILTER BY (\"S2\" = 'bas' " - + "AND \"S1\" = 'foo')\n" - + "CLIENT MERGE SORT", queryPlan); + assertEquals(fullTableName, explainPlanAttributes.getTableName()); + assertEquals(" [1,1,100] - [1,2,109]", + explainPlanAttributes.getKeyRanges()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); } else { - assertEquals("CLIENT PARALLEL 1-WAY SKIP SCAN ON 4 KEYS OVER " - + fullIndexName1 - + " [1,100] - [2,109]\n" - + " SERVER FILTER BY (\"S2\" = 'bas' " - + "AND \"S1\" = 'foo')", queryPlan); + assertEquals(fullIndexName1, explainPlanAttributes.getTableName()); + assertEquals(" [1,100] - [2,109]", + explainPlanAttributes.getKeyRanges()); + assertNull(explainPlanAttributes.getClientSortAlgo()); } } } @@ -932,26 +944,44 @@ public static Pair testUpdatableViewIndex( rs.getBigDecimal(3))); assertEquals("bar", rs.getString(4)); assertFalse(rs.next()); - rs = stmt.executeQuery("EXPLAIN " + query); - String queryPlan = QueryUtil.getExplainPlan(rs); + + ExplainPlan plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + if (localIndex) { - assertEquals("CLIENT PARALLEL " - + (saltBuckets == null ? 1 : saltBuckets) - + "-WAY RANGE SCAN OVER " + fullTableName + " [1,51]\n" - + " SERVER FILTER BY FIRST KEY ONLY\n" - + "CLIENT MERGE SORT", queryPlan); + assertEquals("PARALLEL " + + (saltBuckets == null ? 1 : saltBuckets) + "-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals(fullTableName, + explainPlanAttributes.getTableName()); + assertEquals(" [1,51]", explainPlanAttributes.getKeyRanges()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); } else { - assertEquals(saltBuckets == null ? - "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " - + viewIndexPhysicalName - + " [" + Short.MIN_VALUE + ",51]" : - "CLIENT PARALLEL " + saltBuckets - + "-WAY RANGE SCAN OVER " - + viewIndexPhysicalName + " [0," - + Short.MIN_VALUE + ",51] - [" - + (saltBuckets - 1) + "," - + Short.MIN_VALUE + ",51]\nCLIENT MERGE SORT", - queryPlan); + assertEquals(viewIndexPhysicalName, + explainPlanAttributes.getTableName()); + if (saltBuckets == null) { + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals(" [" + Short.MIN_VALUE + ",51]", + explainPlanAttributes.getKeyRanges()); + assertNull(explainPlanAttributes.getClientSortAlgo()); + } else { + assertEquals("PARALLEL " + saltBuckets + "-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals(" [0," + Short.MIN_VALUE + ",51] - [" + + (saltBuckets - 1) + "," + Short.MIN_VALUE + ",51]", + explainPlanAttributes.getKeyRanges()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); + } } String viewIndexName2 = "I_" + generateUniqueName(); @@ -981,37 +1011,43 @@ public static Pair testUpdatableViewIndex( assertEquals(120, rs.getInt(2)); assertEquals("foo", rs.getString(3)); assertFalse(rs.next()); - rs = stmt.executeQuery("EXPLAIN " + query); String physicalTableName; + + plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + explainPlanAttributes = plan.getPlanStepsAsAttributes(); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); if (localIndex) { physicalTableName = fullTableName; - assertEquals("CLIENT PARALLEL " - + (saltBuckets == null ? 1 : saltBuckets) - + "-WAY RANGE SCAN OVER " + fullTableName - + " [" + (2) + ",'foo']\n" - + " SERVER FILTER BY FIRST KEY ONLY\n" - + "CLIENT MERGE SORT", - QueryUtil.getExplainPlan(rs)); + assertEquals("PARALLEL " + (saltBuckets == null ? 1 : saltBuckets) + + "-WAY", explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals(fullTableName, + explainPlanAttributes.getTableName()); + assertEquals(" [" + (2) + ",'foo']", + explainPlanAttributes.getKeyRanges()); } else { physicalTableName = viewIndexPhysicalName; - assertEquals(saltBuckets == null ? - "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " - + viewIndexPhysicalName - + " [" + (Short.MIN_VALUE + 1) - + ",'foo']\n" - + " SERVER FILTER BY FIRST KEY ONLY" - : - "CLIENT PARALLEL " + saltBuckets - + "-WAY RANGE SCAN OVER " - + viewIndexPhysicalName + " [0," - + (Short.MIN_VALUE + 1) - + ",'foo'] - [" + (saltBuckets - 1) - + "," + (Short.MIN_VALUE + 1) - + ",'foo']\n" - + " SERVER FILTER BY FIRST KEY ONLY" - + "\n" - + "CLIENT MERGE SORT", - QueryUtil.getExplainPlan(rs)); + assertEquals(viewIndexPhysicalName, + explainPlanAttributes.getTableName()); + if (saltBuckets == null) { + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals(" [" + (Short.MIN_VALUE + 1) + + ",'foo']", explainPlanAttributes.getKeyRanges()); + assertNull(explainPlanAttributes.getClientSortAlgo()); + } else { + assertEquals("PARALLEL " + saltBuckets + "-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals(" [0," + (Short.MIN_VALUE + 1) + ",'foo'] - [" + + (saltBuckets - 1) + "," + (Short.MIN_VALUE + 1) + + ",'foo']", explainPlanAttributes.getKeyRanges()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); + } } return new Pair<>(physicalTableName, scan); } diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/BaseIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/BaseIndexIT.java index b6b6d40b115..5707a848e79 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/BaseIndexIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/BaseIndexIT.java @@ -42,34 +42,31 @@ import java.util.Properties; import java.util.Random; -import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.Cell; import org.apache.hadoop.hbase.CellScanner; import org.apache.hadoop.hbase.CellUtil; import org.apache.hadoop.hbase.HConstants; -import org.apache.hadoop.hbase.client.Admin; import org.apache.hadoop.hbase.client.HTable; import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.client.ResultScanner; import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.client.Table; -import org.apache.hadoop.hbase.client.TableDescriptor; -import org.apache.hadoop.hbase.ipc.PhoenixRpcSchedulerFactory; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.Pair; import org.apache.phoenix.compile.ColumnResolver; +import org.apache.phoenix.compile.ExplainPlan; +import org.apache.phoenix.compile.ExplainPlanAttributes; import org.apache.phoenix.compile.FromCompiler; import org.apache.phoenix.end2end.ParallelStatsDisabledIT; import org.apache.phoenix.exception.SQLExceptionCode; import org.apache.phoenix.jdbc.PhoenixConnection; -import org.apache.phoenix.jdbc.PhoenixDatabaseMetaData; +import org.apache.phoenix.jdbc.PhoenixPreparedStatement; import org.apache.phoenix.jdbc.PhoenixResultSet; import org.apache.phoenix.jdbc.PhoenixStatement; import org.apache.phoenix.parse.NamedTableNode; import org.apache.phoenix.parse.TableName; import org.apache.phoenix.query.BaseTest; import org.apache.phoenix.query.QueryServices; -import org.apache.phoenix.query.QueryServicesOptions; import org.apache.phoenix.schema.PTable; import org.apache.phoenix.schema.PTableImpl; import org.apache.phoenix.schema.PTableKey; @@ -81,7 +78,6 @@ import org.apache.phoenix.util.PhoenixRuntime; import org.apache.phoenix.util.PropertiesUtil; import org.apache.phoenix.util.QueryUtil; -import org.apache.phoenix.util.ReadOnlyProps; import org.apache.phoenix.util.SchemaUtil; import org.apache.phoenix.util.TestUtil; import org.apache.phoenix.util.TransactionUtil; @@ -148,19 +144,32 @@ public void testIndexWithNullableFixedWithCols() throws Exception { stmt.execute(ddl); String query = "SELECT d.char_col1, int_col1 from " + fullTableName + " as d"; - ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + query); - if(localIndex) { - assertEquals( - "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + fullTableName + " [1]\n" + - " SERVER FILTER BY FIRST KEY ONLY\n" + - "CLIENT MERGE SORT", - QueryUtil.getExplainPlan(rs)); + + ExplainPlan plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); + + if (localIndex) { + assertEquals(fullTableName, explainPlanAttributes.getTableName()); + assertEquals(" [1]", explainPlanAttributes.getKeyRanges()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); } else { - assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + fullIndexName + "\n" - + " SERVER FILTER BY FIRST KEY ONLY", QueryUtil.getExplainPlan(rs)); + assertEquals(fullIndexName, explainPlanAttributes.getTableName()); + assertNull(explainPlanAttributes.getClientSortAlgo()); + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); } - rs = conn.createStatement().executeQuery(query); + ResultSet rs = conn.createStatement().executeQuery(query); assertTrue(rs.next()); assertEquals("chara", rs.getString(1)); assertEquals("chara", rs.getString("char_col1")); @@ -547,17 +556,32 @@ public void testIndexWithNullableDateCol() throws Exception { conn.createStatement().execute(ddl); String query = "SELECT int_pk from " + fullTableName ; - ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + query); + ExplainPlan plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); if (localIndex) { - assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + fullTableName +" [1]\n" - + " SERVER FILTER BY FIRST KEY ONLY\n" - + "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs)); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(fullTableName, + explainPlanAttributes.getTableName()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); + assertEquals(" [1]", explainPlanAttributes.getKeyRanges()); } else { - assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + fullIndexName + "\n" - + " SERVER FILTER BY FIRST KEY ONLY", QueryUtil.getExplainPlan(rs)); + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(fullIndexName, + explainPlanAttributes.getTableName()); + assertNull(explainPlanAttributes.getClientSortAlgo()); } - rs = conn.createStatement().executeQuery(query); + ResultSet rs = conn.createStatement().executeQuery(query); assertTrue(rs.next()); assertEquals(2, rs.getInt(1)); assertTrue(rs.next()); @@ -567,14 +591,26 @@ public void testIndexWithNullableDateCol() throws Exception { assertFalse(rs.next()); query = "SELECT date_col from " + fullTableName + " order by date_col" ; - rs = conn.createStatement().executeQuery("EXPLAIN " + query); + plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + explainPlanAttributes = plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); if (localIndex) { - assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + fullTableName + " [1]\n" - + " SERVER FILTER BY FIRST KEY ONLY\n" - + "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs)); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(fullTableName, explainPlanAttributes.getTableName()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); + assertEquals(" [1]", explainPlanAttributes.getKeyRanges()); } else { - assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + fullIndexName + "\n" - + " SERVER FILTER BY FIRST KEY ONLY", QueryUtil.getExplainPlan(rs)); + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(fullIndexName, explainPlanAttributes.getTableName()); + assertNull(explainPlanAttributes.getClientSortAlgo()); } rs = conn.createStatement().executeQuery(query); @@ -623,11 +659,27 @@ public void testSelectAllAndAliasWithIndex() throws Exception { conn.commit(); query = "SELECT * FROM " + fullTableName; - rs = conn.createStatement().executeQuery("EXPLAIN " + query); - if(localIndex){ - assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + fullTableName+" [1]\nCLIENT MERGE SORT", QueryUtil.getExplainPlan(rs)); + ExplainPlan plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + if (localIndex) { + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(fullTableName, + explainPlanAttributes.getTableName()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); + assertEquals(" [1]", explainPlanAttributes.getKeyRanges()); } else { - assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + fullIndexName, QueryUtil.getExplainPlan(rs)); + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(fullIndexName, + explainPlanAttributes.getTableName()); + assertNull(explainPlanAttributes.getClientSortAlgo()); } rs = conn.createStatement().executeQuery(query); @@ -648,15 +700,24 @@ public void testSelectAllAndAliasWithIndex() throws Exception { assertFalse(rs.next()); query = "SELECT v1 as foo FROM " + fullTableName + " WHERE v2 = '1' ORDER BY foo"; - rs = conn.createStatement().executeQuery("EXPLAIN " + query); - if(localIndex){ - assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " +fullTableName + " [1,~'1']\n" + - " SERVER SORTED BY [\"V1\"]\n" + - "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs)); + plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals("[\"V1\"]", explainPlanAttributes.getServerSortedBy()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); + if (localIndex) { + assertEquals(fullTableName, + explainPlanAttributes.getTableName()); } else { - assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " +fullIndexName + " [~'1']\n" + - " SERVER SORTED BY [\"V1\"]\n" + - "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs)); + assertEquals(fullIndexName, + explainPlanAttributes.getTableName()); } rs = conn.createStatement().executeQuery(query); @@ -703,15 +764,38 @@ public void testSelectCF() throws Exception { conn.commit(); query = "SELECT * FROM " + fullTableName; - rs = conn.createStatement().executeQuery("EXPLAIN " + query); - assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + fullTableName, QueryUtil.getExplainPlan(rs)); + ExplainPlan plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(fullTableName, explainPlanAttributes.getTableName()); query = "SELECT a.* FROM " + fullTableName; - rs = conn.createStatement().executeQuery("EXPLAIN " + query); + plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); if(localIndex) { - assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + fullTableName+" [1]\nCLIENT MERGE SORT", QueryUtil.getExplainPlan(rs)); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(fullTableName, + explainPlanAttributes.getTableName()); + assertEquals(" [1]", explainPlanAttributes.getKeyRanges()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); } else { - assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + fullIndexName, QueryUtil.getExplainPlan(rs)); + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(fullIndexName, + explainPlanAttributes.getTableName()); } rs = conn.createStatement().executeQuery(query); assertTrue(rs.next()); @@ -874,16 +958,30 @@ public void testMultipleUpdatesAcrossRegions() throws Exception { // make sure the index is working as expected query = "SELECT * FROM " + testTable; - rs = conn.createStatement().executeQuery("EXPLAIN " + query); + ExplainPlan plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); if (localIndex) { - assertEquals("CLIENT PARALLEL 2-WAY RANGE SCAN OVER " + testTable+" [1]\n" - + " SERVER FILTER BY FIRST KEY ONLY\n" - + "CLIENT MERGE SORT", - QueryUtil.getExplainPlan(rs)); + assertEquals("PARALLEL 2-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(testTable, explainPlanAttributes.getTableName()); + assertEquals(" [1]", explainPlanAttributes.getKeyRanges()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); } else { - assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + fullIndexName + "\n" - + " SERVER FILTER BY FIRST KEY ONLY", - QueryUtil.getExplainPlan(rs)); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(fullIndexName, + explainPlanAttributes.getTableName()); + assertNull(explainPlanAttributes.getClientSortAlgo()); } // check that the data table matches as expected @@ -940,12 +1038,23 @@ public void testIndexWithCaseSensitiveCols() throws Exception { } query = "SELECT * FROM " + fullTableName + " WHERE \"v2\" = '1'"; - rs = conn.createStatement().executeQuery("EXPLAIN " + query); - if(localIndex){ - assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + fullTableName + " [1,'1']\n" - + "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs)); + ExplainPlan plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + if (localIndex) { + assertEquals(fullTableName, + explainPlanAttributes.getTableName()); + assertEquals(" [1,'1']", explainPlanAttributes.getKeyRanges()); } else { - assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + fullIndexName + " ['1']", QueryUtil.getExplainPlan(rs)); + assertEquals(fullIndexName, + explainPlanAttributes.getTableName()); + assertEquals(" ['1']", explainPlanAttributes.getKeyRanges()); } rs = conn.createStatement().executeQuery(query); @@ -971,12 +1080,26 @@ public void testIndexWithCaseSensitiveCols() throws Exception { } query = "SELECT \"V1\", \"V1\" as foo1, \"v2\" as foo, \"v2\" as \"Foo1\", \"v2\" FROM " + fullTableName + " ORDER BY foo"; - rs = conn.createStatement().executeQuery("EXPLAIN " + query); - if(localIndex){ - assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + fullTableName + " [1]\nCLIENT MERGE SORT", - QueryUtil.getExplainPlan(rs)); + plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + explainPlanAttributes = plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + if (localIndex) { + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(fullTableName, + explainPlanAttributes.getTableName()); + assertEquals(" [1]", explainPlanAttributes.getKeyRanges()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); } else { - assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER "+fullIndexName, QueryUtil.getExplainPlan(rs)); + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(fullIndexName, + explainPlanAttributes.getTableName()); + assertNull(explainPlanAttributes.getClientSortAlgo()); } rs = conn.createStatement().executeQuery(query); @@ -1088,11 +1211,27 @@ private void testIndexWithDecimalCol(boolean enableServerSideUpsert) throws Exce } query = "SELECT decimal_pk, decimal_col1, decimal_col2 from " + fullTableName ; - rs = conn.createStatement().executeQuery("EXPLAIN " + query); - if(localIndex) { - assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + fullTableName+" [1]\nCLIENT MERGE SORT", QueryUtil.getExplainPlan(rs)); + ExplainPlan plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + if (localIndex) { + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(fullTableName, + explainPlanAttributes.getTableName()); + assertEquals(" [1]", explainPlanAttributes.getKeyRanges()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); } else { - assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + fullIndexName, QueryUtil.getExplainPlan(rs)); + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(fullIndexName, + explainPlanAttributes.getTableName()); + assertNull(explainPlanAttributes.getClientSortAlgo()); } rs = conn.createStatement().executeQuery(query); diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/ChildViewsUseParentViewIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/ChildViewsUseParentViewIndexIT.java index 8955653de61..ccc849e33f6 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/ChildViewsUseParentViewIndexIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/ChildViewsUseParentViewIndexIT.java @@ -29,12 +29,12 @@ import java.sql.SQLException; import java.util.Properties; -import org.apache.hadoop.hbase.HConstants; +import org.apache.phoenix.compile.ExplainPlan; +import org.apache.phoenix.compile.ExplainPlanAttributes; import org.apache.phoenix.end2end.ParallelStatsDisabledIT; -import org.apache.phoenix.jdbc.PhoenixConnection; +import org.apache.phoenix.jdbc.PhoenixPreparedStatement; import org.apache.phoenix.schema.PTable; import org.apache.phoenix.util.PhoenixRuntime; -import org.apache.phoenix.util.QueryUtil; import org.junit.Test; public class ChildViewsUseParentViewIndexIT extends ParallelStatsDisabledIT { @@ -158,14 +158,25 @@ public void testParentViewIndexWithSpecializedChildViews() throws Exception { private void assertQueryUsesIndex(final String baseTableName, final String viewName, Connection conn, boolean isChildView) throws SQLException { String sql = "SELECT A0, A1, A2, A4 FROM " + viewName +" WHERE A4 IN ('1', '2', '3') ORDER BY A4, A2"; - ResultSet rs = conn.prepareStatement("EXPLAIN " + sql).executeQuery(); + ExplainPlan plan = conn.prepareStatement(sql) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); + assertEquals("SKIP SCAN ON 3 KEYS ", + explainPlanAttributes.getExplainScanType()); + assertEquals("_IDX_" + baseTableName, + explainPlanAttributes.getTableName()); String childViewScanKey = isChildView ? ",'Y'" : ""; - assertEquals( - "CLIENT PARALLEL 1-WAY SKIP SCAN ON 3 KEYS OVER _IDX_" + baseTableName + " ["+ Short.MIN_VALUE +",'1'" + childViewScanKey + "] - [" + Short.MIN_VALUE + ",'3'" + childViewScanKey + "]\n" + - " SERVER FILTER BY FIRST KEY ONLY", - QueryUtil.getExplainPlan(rs)); - - rs = conn.createStatement().executeQuery(sql); + assertEquals(" [" + Short.MIN_VALUE + ",'1'" + childViewScanKey + "] - [" + + Short.MIN_VALUE + ",'3'" + childViewScanKey + "]", + explainPlanAttributes.getKeyRanges()); + + ResultSet rs = conn.createStatement().executeQuery(sql); assertTrue(rs.next()); assertEquals("1", rs.getString(1)); assertEquals("X", rs.getString(2)); @@ -181,13 +192,21 @@ private void assertQueryUsesIndex(final String baseTableName, final String viewN private void assertQueryUsesBaseTable(final String baseTableName, final String viewName, Connection conn) throws SQLException { String sql = "SELECT A0, A1, A2, A4 FROM " + viewName +" WHERE A4 IN ('1', '2', '3') "; - ResultSet rs = conn.prepareStatement("EXPLAIN " + sql).executeQuery(); - assertEquals( - "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + baseTableName + "\n" + - " SERVER FILTER BY (A4 IN ('1','2','3') AND ((A1 = 'X' AND A2 = 'Y') AND A3 = 'Z'))", - QueryUtil.getExplainPlan(rs)); - - rs = conn.createStatement().executeQuery(sql); + ExplainPlan plan = conn.prepareStatement(sql) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("SERVER FILTER BY (A4 IN ('1','2','3') AND ((A1 = 'X' AND A2 = 'Y') AND A3 = 'Z'))", + explainPlanAttributes.getServerWhereFilter()); + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(baseTableName, + explainPlanAttributes.getTableName()); + + ResultSet rs = conn.createStatement().executeQuery(sql); assertTrue(rs.next()); assertEquals("1", rs.getString(1)); assertEquals("X", rs.getString(2)); @@ -262,15 +281,28 @@ private void assertQueryIndex(String viewName, String baseTableName, Connection " WHERE WO_ID IN ('003xxxxxxxxxxx1', '003xxxxxxxxxxx2', '003xxxxxxxxxxx3', '003xxxxxxxxxxx4', '003xxxxxxxxxxx5') " + " AND (A_DATE > TO_DATE('2016-01-01 06:00:00.0')) " + " ORDER BY WO_ID, A_DATE DESC"; - ResultSet rs = conn.prepareStatement("EXPLAIN " + sql).executeQuery(); - assertEquals( - "CLIENT PARALLEL 1-WAY SKIP SCAN ON 5 RANGES OVER _IDX_" + baseTableName + " [" + Short.MIN_VALUE + ",'00Dxxxxxxxxxxx1','003xxxxxxxxxxx1',*] - [" + Short.MIN_VALUE + ",'00Dxxxxxxxxxxx1','003xxxxxxxxxxx5',~'2016-01-01 06:00:00.000']\n" + - " SERVER FILTER BY FIRST KEY ONLY", - QueryUtil.getExplainPlan(rs)); - - rs = conn.createStatement().executeQuery(sql); - for (int i=0; i 0"; - ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + sql); - String expectedPlan = "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " - + (localIndex ? fullDataTableName + " [1,0] - [1,*]" - : "INDEX_TEST." + indexName + " [0] - [*]") - + "\n SERVER FILTER BY FIRST KEY ONLY\n SERVER DISTINCT PREFIX FILTER OVER [TO_BIGINT(\"(A.INT_COL1 + 1)\")]\n SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [TO_BIGINT(\"(A.INT_COL1 + 1)\")]" - + (localIndex ? "\nCLIENT MERGE SORT" : ""); - assertEquals(expectedPlan, QueryUtil.getExplainPlan(rs)); - rs = conn.createStatement().executeQuery(sql); + String sql = "SELECT distinct int_col1+1 FROM " + fullDataTableName + + " where int_col1+1 > 0"; + + ExplainPlan plan = conn.prepareStatement(sql) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); + assertEquals("SERVER DISTINCT PREFIX FILTER OVER [TO_BIGINT(\"(A.INT_COL1 + 1)\")]", + explainPlanAttributes.getServerDistinctFilter()); + assertEquals("SERVER AGGREGATE INTO ORDERED DISTINCT ROWS BY [TO_BIGINT(\"(A.INT_COL1 + 1)\")]", + explainPlanAttributes.getServerAggregate()); + if (localIndex) { + assertEquals(fullDataTableName, + explainPlanAttributes.getTableName()); + assertEquals(" [1,0] - [1,*]", + explainPlanAttributes.getKeyRanges()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); + } else { + assertEquals("INDEX_TEST." + indexName, + explainPlanAttributes.getTableName()); + assertEquals(" [0] - [*]", + explainPlanAttributes.getKeyRanges()); + assertNull(explainPlanAttributes.getClientSortAlgo()); + } + + ResultSet rs = conn.createStatement().executeQuery(sql); assertTrue(rs.next()); assertEquals(2, rs.getInt(1)); assertTrue(rs.next()); assertEquals(3, rs.getInt(1)); assertFalse(rs.next()); - } finally { - conn.close(); } } @@ -222,26 +267,44 @@ protected void helpTestInClauseWithIndex(boolean mutable, boolean localIndex) th String indexName = generateUniqueName(); Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); - Connection conn = DriverManager.getConnection(getUrl(), props); - try { + try (Connection conn = DriverManager.getConnection(getUrl(), props)) { conn.setAutoCommit(false); createDataTable(conn, fullDataTableName, mutable ? "" : "IMMUTABLE_ROWS=true"); populateDataTable(conn, fullDataTableName); String ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX " + indexName + " ON " + fullDataTableName - + " (int_col1+1)"; + + " (int_col1+1)"; conn.createStatement().execute(ddl); String sql = "SELECT int_col1+1 FROM " + fullDataTableName + " where int_col1+1 IN (2)"; - ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + sql); - assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " - + (localIndex ? fullDataTableName + " [1,2]\n SERVER FILTER BY FIRST KEY ONLY\nCLIENT MERGE SORT" - : "INDEX_TEST." + indexName + " [2]\n SERVER FILTER BY FIRST KEY ONLY"), QueryUtil.getExplainPlan(rs)); - rs = conn.createStatement().executeQuery(sql); + + ExplainPlan plan = conn.prepareStatement(sql) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); + if (localIndex) { + assertEquals(fullDataTableName, + explainPlanAttributes.getTableName()); + assertEquals(" [1,2]", explainPlanAttributes.getKeyRanges()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); + } else { + assertEquals("INDEX_TEST." + indexName, + explainPlanAttributes.getTableName()); + assertEquals(" [2]", explainPlanAttributes.getKeyRanges()); + assertNull(explainPlanAttributes.getClientSortAlgo()); + } + + ResultSet rs = conn.createStatement().executeQuery(sql); assertTrue(rs.next()); assertEquals(2, rs.getInt(1)); assertFalse(rs.next()); - } finally { - conn.close(); } } @@ -271,30 +334,49 @@ protected void helpTestSelectAliasAndOrderByWithIndex(boolean mutable, boolean l String indexName = generateUniqueName(); Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); - Connection conn = DriverManager.getConnection(getUrl(), props); - try { + try (Connection conn = DriverManager.getConnection(getUrl(), props)) { conn.setAutoCommit(false); createDataTable(conn, fullDataTableName, mutable ? "" : "IMMUTABLE_ROWS=true"); populateDataTable(conn, fullDataTableName); - String ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX " + indexName + " ON " + fullDataTableName - + " (int_col1+1)"; + String ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX " + + indexName + " ON " + fullDataTableName + + " (int_col1+1)"; conn.createStatement().execute(ddl); - String sql = "SELECT int_col1+1 AS foo FROM " + fullDataTableName + " ORDER BY foo"; - ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + sql); - assertEquals("CLIENT PARALLEL 1-WAY " - + (localIndex ? "RANGE SCAN OVER " + fullDataTableName - + " [1]\n SERVER FILTER BY FIRST KEY ONLY\nCLIENT MERGE SORT" - : "FULL SCAN OVER INDEX_TEST." + indexName + "\n SERVER FILTER BY FIRST KEY ONLY"), - QueryUtil.getExplainPlan(rs)); - rs = conn.createStatement().executeQuery(sql); + String sql = "SELECT int_col1+1 AS foo FROM " + fullDataTableName + + " ORDER BY foo"; + + ExplainPlan plan = conn.prepareStatement(sql) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); + if (localIndex) { + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(fullDataTableName, + explainPlanAttributes.getTableName()); + assertEquals(" [1]", explainPlanAttributes.getKeyRanges()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); + } else { + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals("INDEX_TEST." + indexName, + explainPlanAttributes.getTableName()); + assertNull(explainPlanAttributes.getClientSortAlgo()); + } + + ResultSet rs = conn.createStatement().executeQuery(sql); assertTrue(rs.next()); assertEquals(2, rs.getInt(1)); assertTrue(rs.next()); assertEquals(3, rs.getInt(1)); assertFalse(rs.next()); - } finally { - conn.close(); } } @@ -323,86 +405,117 @@ protected void helpTestIndexWithCaseSensitiveCols(boolean mutable, boolean local String indexName = generateUniqueName(); Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); - Connection conn = DriverManager.getConnection(getUrl(), props); - try { - conn.createStatement().execute("CREATE TABLE " + dataTableName + " (k VARCHAR NOT NULL PRIMARY KEY, \"cf1\".\"V1\" VARCHAR, \"CF2\".\"v2\" VARCHAR) "+ (mutable ? "IMMUTABLE_ROWS=true" : "")); + try (Connection conn = DriverManager.getConnection(getUrl(), props)) { + conn.createStatement().execute("CREATE TABLE " + dataTableName + + " (k VARCHAR NOT NULL PRIMARY KEY, \"cf1\".\"V1\" VARCHAR, \"CF2\".\"v2\" VARCHAR) " + + (mutable ? "IMMUTABLE_ROWS=true" : "")); String query = "SELECT * FROM " + dataTableName; ResultSet rs = conn.createStatement().executeQuery(query); assertFalse(rs.next()); - String ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX " + indexName + " ON " + dataTableName + " (\"cf1\".\"V1\" || '_' || \"CF2\".\"v2\") INCLUDE (\"V1\",\"v2\")"; + String ddl = "CREATE " + (localIndex ? "LOCAL" : "") + " INDEX " + + indexName + " ON " + dataTableName + + " (\"cf1\".\"V1\" || '_' || \"CF2\".\"v2\") INCLUDE (\"V1\",\"v2\")"; conn.createStatement().execute(ddl); query = "SELECT * FROM " + indexName; rs = conn.createStatement().executeQuery(query); assertFalse(rs.next()); PreparedStatement stmt = conn.prepareStatement("UPSERT INTO " + dataTableName + " VALUES(?,?,?)"); - stmt.setString(1,"a"); + stmt.setString(1, "a"); stmt.setString(2, "x"); stmt.setString(3, "1"); stmt.execute(); - stmt.setString(1,"b"); + stmt.setString(1, "b"); stmt.setString(2, "y"); stmt.setString(3, "2"); stmt.execute(); conn.commit(); query = "SELECT (\"V1\" || '_' || \"v2\"), k, \"V1\", \"v2\" FROM " + dataTableName + " WHERE (\"V1\" || '_' || \"v2\") = 'x_1'"; - rs = conn.createStatement().executeQuery("EXPLAIN " + query); - if(localIndex){ - assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + dataTableName + " [1,'x_1']\n" - + "CLIENT MERGE SORT", QueryUtil.getExplainPlan(rs)); + ExplainPlan plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + if (localIndex) { + assertEquals(dataTableName, + explainPlanAttributes.getTableName()); + assertEquals(" [1,'x_1']", + explainPlanAttributes.getKeyRanges()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); } else { - assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + indexName + " ['x_1']", QueryUtil.getExplainPlan(rs)); + assertEquals(indexName, + explainPlanAttributes.getTableName()); + assertEquals(" ['x_1']", explainPlanAttributes.getKeyRanges()); + assertNull(explainPlanAttributes.getClientSortAlgo()); } rs = conn.createStatement().executeQuery(query); assertTrue(rs.next()); - assertEquals("x_1",rs.getString(1)); - assertEquals("a",rs.getString(2)); - assertEquals("x",rs.getString(3)); - assertEquals("1",rs.getString(4)); + assertEquals("x_1", rs.getString(1)); + assertEquals("a", rs.getString(2)); + assertEquals("x", rs.getString(3)); + assertEquals("1", rs.getString(4)); //TODO figure out why this " " is needed - assertEquals("x_1",rs.getString("\"('cf1'.'V1' || '_' || 'CF2'.'v2')\"")); - assertEquals("a",rs.getString("k")); - assertEquals("x",rs.getString("V1")); - assertEquals("1",rs.getString("v2")); + assertEquals("x_1", rs.getString("\"('cf1'.'V1' || '_' || 'CF2'.'v2')\"")); + assertEquals("a", rs.getString("k")); + assertEquals("x", rs.getString("V1")); + assertEquals("1", rs.getString("v2")); assertFalse(rs.next()); query = "SELECT \"V1\", \"V1\" as foo1, (\"V1\" || '_' || \"v2\") as foo, (\"V1\" || '_' || \"v2\") as \"Foo1\", (\"V1\" || '_' || \"v2\") FROM " + dataTableName + " ORDER BY foo"; - rs = conn.createStatement().executeQuery("EXPLAIN " + query); - if(localIndex){ - assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + dataTableName + " [1]\nCLIENT MERGE SORT", - QueryUtil.getExplainPlan(rs)); + plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + explainPlanAttributes = plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + if (localIndex) { + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(dataTableName, + explainPlanAttributes.getTableName()); + assertEquals(" [1]", + explainPlanAttributes.getKeyRanges()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); } else { - assertEquals("CLIENT PARALLEL 1-WAY FULL SCAN OVER " + indexName, QueryUtil.getExplainPlan(rs)); + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(indexName, + explainPlanAttributes.getTableName()); + assertNull(explainPlanAttributes.getClientSortAlgo()); } rs = conn.createStatement().executeQuery(query); assertTrue(rs.next()); - assertEquals("x",rs.getString(1)); - assertEquals("x",rs.getString("V1")); - assertEquals("x",rs.getString(2)); - assertEquals("x",rs.getString("foo1")); - assertEquals("x_1",rs.getString(3)); - assertEquals("x_1",rs.getString("Foo")); - assertEquals("x_1",rs.getString(4)); - assertEquals("x_1",rs.getString("Foo1")); - assertEquals("x_1",rs.getString(5)); - assertEquals("x_1",rs.getString("\"('cf1'.'V1' || '_' || 'CF2'.'v2')\"")); + assertEquals("x", rs.getString(1)); + assertEquals("x", rs.getString("V1")); + assertEquals("x", rs.getString(2)); + assertEquals("x", rs.getString("foo1")); + assertEquals("x_1", rs.getString(3)); + assertEquals("x_1", rs.getString("Foo")); + assertEquals("x_1", rs.getString(4)); + assertEquals("x_1", rs.getString("Foo1")); + assertEquals("x_1", rs.getString(5)); + assertEquals("x_1", rs.getString("\"('cf1'.'V1' || '_' || 'CF2'.'v2')\"")); assertTrue(rs.next()); - assertEquals("y",rs.getString(1)); - assertEquals("y",rs.getString("V1")); - assertEquals("y",rs.getString(2)); - assertEquals("y",rs.getString("foo1")); - assertEquals("y_2",rs.getString(3)); - assertEquals("y_2",rs.getString("Foo")); - assertEquals("y_2",rs.getString(4)); - assertEquals("y_2",rs.getString("Foo1")); - assertEquals("y_2",rs.getString(5)); - assertEquals("y_2",rs.getString("\"('cf1'.'V1' || '_' || 'CF2'.'v2')\"")); + assertEquals("y", rs.getString(1)); + assertEquals("y", rs.getString("V1")); + assertEquals("y", rs.getString(2)); + assertEquals("y", rs.getString("foo1")); + assertEquals("y_2", rs.getString(3)); + assertEquals("y_2", rs.getString("Foo")); + assertEquals("y_2", rs.getString(4)); + assertEquals("y_2", rs.getString("Foo1")); + assertEquals("y_2", rs.getString(5)); + assertEquals("y_2", rs.getString("\"('cf1'.'V1' || '_' || 'CF2'.'v2')\"")); assertFalse(rs.next()); - } finally { - conn.close(); } } @@ -444,13 +557,36 @@ protected void helpTestSelectColOnlyInDataTable(boolean mutable, boolean localIn conn.setAutoCommit(false); conn.createStatement().execute(ddl); String sql = "SELECT int_col1+1, int_col2 FROM " + fullDataTableName + " WHERE int_col1+1=2"; - ResultSet rs = conn.createStatement().executeQuery("EXPLAIN " + sql); - assertEquals("CLIENT PARALLEL 1-WAY " - + (localIndex ? "RANGE SCAN OVER " + fullDataTableName - + " [1,2]\n SERVER FILTER BY FIRST KEY ONLY\nCLIENT MERGE SORT" : "FULL SCAN OVER " - + fullDataTableName + "\n SERVER FILTER BY (A.INT_COL1 + 1) = 2"), - QueryUtil.getExplainPlan(rs)); - rs = conn.createStatement().executeQuery(sql); + + ExplainPlan plan = conn.prepareStatement(sql) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + if (localIndex) { + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(fullDataTableName, + explainPlanAttributes.getTableName()); + assertEquals(" [1,2]", + explainPlanAttributes.getKeyRanges()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); + } else { + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(fullDataTableName, + explainPlanAttributes.getTableName()); + assertNull(explainPlanAttributes.getClientSortAlgo()); + assertEquals("SERVER FILTER BY (A.INT_COL1 + 1) = 2", + explainPlanAttributes.getServerWhereFilter()); + } + + ResultSet rs = conn.createStatement().executeQuery(sql); assertTrue(rs.next()); assertEquals(2, rs.getInt(1)); assertEquals(1, rs.getInt(2)); @@ -471,64 +607,92 @@ public void testUpdatableViewWithLocalIndex() throws Exception { } private void helpTestUpdatableViewIndex(boolean local) throws Exception { - Connection conn = DriverManager.getConnection(getUrl()); - String dataTableName = generateUniqueName(); - String indexName1 = generateUniqueName(); - String viewName = generateUniqueName(); - String indexName2 = generateUniqueName(); - try { - String ddl = "CREATE TABLE " + dataTableName + " (k1 INTEGER NOT NULL, k2 INTEGER NOT NULL, k3 DECIMAL, s1 VARCHAR, s2 VARCHAR CONSTRAINT pk PRIMARY KEY (k1, k2, k3))"; - conn.createStatement().execute(ddl); - ddl = "CREATE VIEW " + viewName + " AS SELECT * FROM " + dataTableName + " WHERE k1 = 1"; - conn.createStatement().execute(ddl); - conn.createStatement().execute("UPSERT INTO " + viewName + "(k2,s1,s2,k3) VALUES(120,'foo0','bar0',50.0)"); - conn.createStatement().execute("UPSERT INTO " + viewName + "(k2,s1,s2,k3) VALUES(121,'foo1','bar1',51.0)"); - conn.commit(); - - ResultSet rs; - conn.createStatement().execute("CREATE " + (local ? "LOCAL" : "") + " INDEX " + indexName1 + " on " + viewName + "(k1+k2+k3) include (s1, s2)"); - conn.createStatement().execute("UPSERT INTO " + viewName + "(k2,s1,s2,k3) VALUES(120,'foo2','bar2',50.0)"); - conn.commit(); - - String query = "SELECT k1, k2, k3, s1, s2 FROM " + viewName + " WHERE k1+k2+k3 = 173.0"; - rs = conn.createStatement().executeQuery("EXPLAIN " + query); - String queryPlan = QueryUtil.getExplainPlan(rs); - if (local) { - assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + dataTableName + " [1,173]\n" + "CLIENT MERGE SORT", - queryPlan); - } else { - assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER _IDX_" + dataTableName + " [" + Short.MIN_VALUE + ",173]", queryPlan); - } - rs = conn.createStatement().executeQuery(query); - assertTrue(rs.next()); - assertEquals(1, rs.getInt(1)); - assertEquals(121, rs.getInt(2)); - assertTrue(BigDecimal.valueOf(51.0).compareTo(rs.getBigDecimal(3))==0); - assertEquals("foo1", rs.getString(4)); - assertEquals("bar1", rs.getString(5)); - assertFalse(rs.next()); - - conn.createStatement().execute("CREATE " + (local ? "LOCAL" : "") + " INDEX " + indexName2 + " on " + viewName + "(s1||'_'||s2)"); - - query = "SELECT k1, k2, s1||'_'||s2 FROM " + viewName + " WHERE (s1||'_'||s2)='foo2_bar2'"; - rs = conn.createStatement().executeQuery("EXPLAIN " + query); - if (local) { - assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + dataTableName + " [" + (2) - + ",'foo2_bar2']\n" + " SERVER FILTER BY FIRST KEY ONLY\n" + "CLIENT MERGE SORT", - QueryUtil.getExplainPlan(rs)); - } else { - assertEquals("CLIENT PARALLEL 1-WAY RANGE SCAN OVER _IDX_" + dataTableName + " [" + (Short.MIN_VALUE + 1) + ",'foo2_bar2']\n" - + " SERVER FILTER BY FIRST KEY ONLY", QueryUtil.getExplainPlan(rs)); - } - rs = conn.createStatement().executeQuery(query); - assertTrue(rs.next()); - assertEquals(1, rs.getInt(1)); - assertEquals(120, rs.getInt(2)); - assertEquals("foo2_bar2", rs.getString(3)); - assertFalse(rs.next()); - } - finally { - conn.close(); + try (Connection conn = DriverManager.getConnection(getUrl())) { + String dataTableName = generateUniqueName(); + String indexName1 = generateUniqueName(); + String viewName = generateUniqueName(); + String indexName2 = generateUniqueName(); + String ddl = "CREATE TABLE " + dataTableName + " (k1 INTEGER NOT NULL, k2 INTEGER NOT NULL, k3 DECIMAL, s1 VARCHAR, s2 VARCHAR CONSTRAINT pk PRIMARY KEY (k1, k2, k3))"; + conn.createStatement().execute(ddl); + ddl = "CREATE VIEW " + viewName + " AS SELECT * FROM " + dataTableName + " WHERE k1 = 1"; + conn.createStatement().execute(ddl); + conn.createStatement().execute("UPSERT INTO " + viewName + "(k2,s1,s2,k3) VALUES(120,'foo0','bar0',50.0)"); + conn.createStatement().execute("UPSERT INTO " + viewName + "(k2,s1,s2,k3) VALUES(121,'foo1','bar1',51.0)"); + conn.commit(); + + ResultSet rs; + conn.createStatement().execute("CREATE " + (local ? "LOCAL" : "") + " INDEX " + indexName1 + " on " + viewName + "(k1+k2+k3) include (s1, s2)"); + conn.createStatement().execute("UPSERT INTO " + viewName + "(k2,s1,s2,k3) VALUES(120,'foo2','bar2',50.0)"); + conn.commit(); + + String query = "SELECT k1, k2, k3, s1, s2 FROM " + viewName + " WHERE k1+k2+k3 = 173.0"; + ExplainPlan plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + if (local) { + assertEquals(dataTableName, + explainPlanAttributes.getTableName()); + assertEquals(" [1,173]", + explainPlanAttributes.getKeyRanges()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); + } else { + assertEquals("_IDX_" + dataTableName, + explainPlanAttributes.getTableName()); + assertEquals(" [" + Short.MIN_VALUE + ",173]", + explainPlanAttributes.getKeyRanges()); + assertNull(explainPlanAttributes.getClientSortAlgo()); + } + + rs = conn.createStatement().executeQuery(query); + assertTrue(rs.next()); + assertEquals(1, rs.getInt(1)); + assertEquals(121, rs.getInt(2)); + assertTrue(BigDecimal.valueOf(51.0).compareTo(rs.getBigDecimal(3)) == 0); + assertEquals("foo1", rs.getString(4)); + assertEquals("bar1", rs.getString(5)); + assertFalse(rs.next()); + + conn.createStatement().execute("CREATE " + (local ? "LOCAL" : "") + " INDEX " + indexName2 + " on " + viewName + "(s1||'_'||s2)"); + + query = "SELECT k1, k2, s1||'_'||s2 FROM " + viewName + " WHERE (s1||'_'||s2)='foo2_bar2'"; + plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + explainPlanAttributes = plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); + if (local) { + assertEquals(dataTableName, + explainPlanAttributes.getTableName()); + assertEquals(" [" + (2) + ",'foo2_bar2']", + explainPlanAttributes.getKeyRanges()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); + } else { + assertEquals("_IDX_" + dataTableName, + explainPlanAttributes.getTableName()); + assertEquals(" [" + (Short.MIN_VALUE + 1) + ",'foo2_bar2']", + explainPlanAttributes.getKeyRanges()); + assertNull(explainPlanAttributes.getClientSortAlgo()); + } + + rs = conn.createStatement().executeQuery(query); + assertTrue(rs.next()); + assertEquals(1, rs.getInt(1)); + assertEquals(120, rs.getInt(2)); + assertEquals("foo2_bar2", rs.getString(3)); + assertFalse(rs.next()); } } @@ -569,11 +733,23 @@ private void helpTestViewUsesTableIndex(boolean immutable) throws Exception { //i2 should be used since it contains s3||'_'||s4 i String query = "SELECT s2||'_'||s3 FROM " + viewName + " WHERE k2=1 AND (s2||'_'||s3)='abc_cab'"; - rs = conn.createStatement( ).executeQuery("EXPLAIN " + query); - String queryPlan = QueryUtil.getExplainPlan(rs); - assertEquals( - "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + indexName2 + " [1,'abc_cab','foo']\n" + - " SERVER FILTER BY FIRST KEY ONLY", queryPlan); + + ExplainPlan plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals(indexName2, + explainPlanAttributes.getTableName()); + assertEquals(" [1,'abc_cab','foo']", + explainPlanAttributes.getKeyRanges()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); + rs = conn.createStatement().executeQuery(query); assertTrue(rs.next()); assertEquals("abc_cab", rs.getString(1)); @@ -582,7 +758,7 @@ private void helpTestViewUsesTableIndex(boolean immutable) throws Exception { conn.createStatement().execute("ALTER VIEW " + viewName + " DROP COLUMN s4"); //i2 cannot be used since s4 has been dropped from the view, so i1 will be used rs = conn.createStatement().executeQuery("EXPLAIN " + query); - queryPlan = QueryUtil.getExplainPlan(rs); + String queryPlan = QueryUtil.getExplainPlan(rs); assertEquals( "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + indexName1 + " [1]\n" + " SERVER FILTER BY FIRST KEY ONLY AND ((\"S2\" || '_' || \"S3\") = 'abc_cab' AND \"S1\" = 'foo')", queryPlan); @@ -642,53 +818,64 @@ public void testMutableLocalCaseSensitiveFunctionIndex() throws Exception { protected void helpTestCaseSensitiveFunctionIndex(boolean mutable, boolean localIndex) throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); - Connection conn = DriverManager.getConnection(getUrl(), props); - String dataTableName = generateUniqueName(); - String indexName = generateUniqueName(); - try { - conn.createStatement().execute( - "CREATE TABLE " + dataTableName + " (k VARCHAR NOT NULL PRIMARY KEY, v VARCHAR) " - + (!mutable ? "IMMUTABLE_ROWS=true" : "")); - String query = "SELECT * FROM " + dataTableName; - ResultSet rs = conn.createStatement().executeQuery(query); - assertFalse(rs.next()); - String ddl = "CREATE " + (localIndex ? "LOCAL" : "") - + " INDEX " + indexName + " ON " + dataTableName + " (REGEXP_SUBSTR(v,'id:\\\\w+'))"; - conn.createStatement().execute(ddl); - query = "SELECT * FROM " + indexName; - rs = conn.createStatement().executeQuery(query); - assertFalse(rs.next()); - - PreparedStatement stmt = conn.prepareStatement("UPSERT INTO " + dataTableName + " VALUES(?,?)"); - stmt.setString(1, "k1"); - stmt.setString(2, "{id:id1}"); - stmt.execute(); - stmt.setString(1, "k2"); - stmt.setString(2, "{id:id2}"); - stmt.execute(); - conn.commit(); - - query = "SELECT k FROM " + dataTableName + " WHERE REGEXP_SUBSTR(v,'id:\\\\w+') = 'id:id1'"; - rs = conn.createStatement().executeQuery("EXPLAIN " + query); - if (localIndex) { - assertEquals( - "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + dataTableName + " [1,'id:id1']\n" - + " SERVER FILTER BY FIRST KEY ONLY\nCLIENT MERGE SORT", - QueryUtil.getExplainPlan(rs)); - } else { - assertEquals( - "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + indexName + " ['id:id1']\n" - + " SERVER FILTER BY FIRST KEY ONLY", - QueryUtil.getExplainPlan(rs)); - } - - rs = conn.createStatement().executeQuery(query); - assertTrue(rs.next()); - assertEquals("k1", rs.getString(1)); - assertFalse(rs.next()); - } finally { - conn.close(); - } + try (Connection conn = DriverManager.getConnection(getUrl(), props)) { + String dataTableName = generateUniqueName(); + String indexName = generateUniqueName(); + conn.createStatement().execute( + "CREATE TABLE " + dataTableName + " (k VARCHAR NOT NULL PRIMARY KEY, v VARCHAR) " + + (!mutable ? "IMMUTABLE_ROWS=true" : "")); + String query = "SELECT * FROM " + dataTableName; + ResultSet rs = conn.createStatement().executeQuery(query); + assertFalse(rs.next()); + String ddl = "CREATE " + (localIndex ? "LOCAL" : "") + + " INDEX " + indexName + " ON " + dataTableName + " (REGEXP_SUBSTR(v,'id:\\\\w+'))"; + conn.createStatement().execute(ddl); + query = "SELECT * FROM " + indexName; + rs = conn.createStatement().executeQuery(query); + assertFalse(rs.next()); + + PreparedStatement stmt = conn.prepareStatement("UPSERT INTO " + dataTableName + " VALUES(?,?)"); + stmt.setString(1, "k1"); + stmt.setString(2, "{id:id1}"); + stmt.execute(); + stmt.setString(1, "k2"); + stmt.setString(2, "{id:id2}"); + stmt.execute(); + conn.commit(); + + query = "SELECT k FROM " + dataTableName + " WHERE REGEXP_SUBSTR(v,'id:\\\\w+') = 'id:id1'"; + + ExplainPlan plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 1-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("RANGE SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals("SERVER FILTER BY FIRST KEY ONLY", + explainPlanAttributes.getServerWhereFilter()); + if (localIndex) { + assertEquals(dataTableName, + explainPlanAttributes.getTableName()); + assertEquals(" [1,'id:id1']", + explainPlanAttributes.getKeyRanges()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); + } else { + assertEquals(indexName, + explainPlanAttributes.getTableName()); + assertEquals(" ['id:id1']", + explainPlanAttributes.getKeyRanges()); + assertNull(explainPlanAttributes.getClientSortAlgo()); + } + + rs = conn.createStatement().executeQuery(query); + assertTrue(rs.next()); + assertEquals("k1", rs.getString(1)); + assertFalse(rs.next()); + } } @Test diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/MutableIndexFailureIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/MutableIndexFailureIT.java index 4aaa855a5c9..5ef55381516 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/MutableIndexFailureIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/MutableIndexFailureIT.java @@ -49,6 +49,8 @@ import org.apache.hadoop.hbase.coprocessor.SimpleRegionObserver; import org.apache.hadoop.hbase.regionserver.MiniBatchOperationInProgress; import org.apache.hadoop.hbase.util.Bytes; +import org.apache.phoenix.compile.ExplainPlan; +import org.apache.phoenix.compile.ExplainPlanAttributes; import org.apache.phoenix.coprocessor.MetaDataRegionObserver; import org.apache.phoenix.coprocessor.MetaDataRegionObserver.BuildIndexScheduleTask; import org.apache.phoenix.end2end.NeedsOwnMiniClusterTest; @@ -57,6 +59,7 @@ import org.apache.phoenix.index.PhoenixIndexFailurePolicy; import org.apache.phoenix.jdbc.PhoenixConnection; import org.apache.phoenix.jdbc.PhoenixDatabaseMetaData; +import org.apache.phoenix.jdbc.PhoenixPreparedStatement; import org.apache.phoenix.query.BaseTest; import org.apache.phoenix.query.QueryConstants; import org.apache.phoenix.query.QueryServices; @@ -288,10 +291,22 @@ public void testIndexWriteFailure() throws Exception { addRowToTable(conn, fullTableName); query = "SELECT /*+ NO_INDEX */ k,v1 FROM " + fullTableName; - rs = conn.createStatement().executeQuery("EXPLAIN " + query); - String expectedPlan = "CLIENT PARALLEL 2-WAY FULL SCAN OVER " - + SchemaUtil.getPhysicalTableName(fullTableName.getBytes(), isNamespaceMapped)+"\nCLIENT MERGE SORT"; - assertEquals(expectedPlan, QueryUtil.getExplainPlan(rs)); + ExplainPlan plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + ExplainPlanAttributes explainPlanAttributes = + plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 2-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals( + SchemaUtil.getPhysicalTableName(fullTableName.getBytes(), + isNamespaceMapped).toString(), + explainPlanAttributes.getTableName()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); + rs = conn.createStatement().executeQuery(query); assertTrue(rs.next()); assertEquals("a", rs.getString(1)); @@ -330,10 +345,21 @@ public void testIndexWriteFailure() throws Exception { updateTableAgain(conn, false); // Verify previous writes succeeded to data table query = "SELECT /*+ NO_INDEX */ k,v1 FROM " + fullTableName; - rs = conn.createStatement().executeQuery("EXPLAIN " + query); - expectedPlan = "CLIENT PARALLEL 2-WAY FULL SCAN OVER " - + SchemaUtil.getPhysicalTableName(fullTableName.getBytes(), isNamespaceMapped)+"\nCLIENT MERGE SORT"; - assertEquals(expectedPlan, QueryUtil.getExplainPlan(rs)); + plan = conn.prepareStatement(query) + .unwrap(PhoenixPreparedStatement.class).optimizeQuery() + .getExplainPlan(); + explainPlanAttributes = plan.getPlanStepsAsAttributes(); + assertEquals("PARALLEL 2-WAY", + explainPlanAttributes.getIteratorTypeAndScanSize()); + assertEquals("FULL SCAN ", + explainPlanAttributes.getExplainScanType()); + assertEquals( + SchemaUtil.getPhysicalTableName(fullTableName.getBytes(), + isNamespaceMapped).toString(), + explainPlanAttributes.getTableName()); + assertEquals("CLIENT MERGE SORT", + explainPlanAttributes.getClientSortAlgo()); + rs = conn.createStatement().executeQuery(query); assertTrue(rs.next()); assertEquals("a", rs.getString(1));