Repository: phoenix Updated Branches: refs/heads/4.8-HBase-1.0 1afcb2e35 -> a28119d52
PHOENIX-3382 Query using Row Value Constructor comprised of non leading PK columns returns incorrect results Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/a28119d5 Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/a28119d5 Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/a28119d5 Branch: refs/heads/4.8-HBase-1.0 Commit: a28119d52ede8107c8237740678ea1ddd9ceb2c1 Parents: 1afcb2e Author: James Taylor <jamestay...@apache.org> Authored: Sat Oct 15 17:05:21 2016 -0700 Committer: James Taylor <jamestay...@apache.org> Committed: Tue Oct 25 17:21:33 2016 -0700 ---------------------------------------------------------------------- .../org/apache/phoenix/end2end/QueryMoreIT.java | 103 +++++++++++++++++++ .../apache/phoenix/compile/WhereOptimizer.java | 4 + .../phoenix/compile/WhereOptimizerTest.java | 61 +++++++++++ 3 files changed, 168 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/phoenix/blob/a28119d5/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryMoreIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryMoreIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryMoreIT.java index c4e4665..c8eb95a 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryMoreIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/QueryMoreIT.java @@ -368,4 +368,107 @@ public class QueryMoreIT extends BaseHBaseManagedTimeTableReuseIT { assertNull(rs.getBigDecimal(2, 10)); } } + + // FIXME: this repros PHOENIX-3382, but turned up two more issues: + // 1) PHOENIX-3383 Comparison between descending row keys used in RVC is reverse + // 2) PHOENIX-3384 Optimize RVC expressions for non leading row key columns + @Test + public void testRVCOnDescWithLeadingPKEquality() throws Exception { + final Connection conn = DriverManager.getConnection(getUrl()); + String fullTableName = generateRandomString(); + try (Statement stmt = conn.createStatement()) { + stmt.execute("CREATE TABLE " + fullTableName + "(\n" + + " ORGANIZATION_ID CHAR(15) NOT NULL,\n" + + " SCORE DOUBLE NOT NULL,\n" + + " ENTITY_ID CHAR(15) NOT NULL\n" + + " CONSTRAINT PAGE_SNAPSHOT_PK PRIMARY KEY (\n" + + " ORGANIZATION_ID,\n" + + " SCORE DESC,\n" + + " ENTITY_ID DESC\n" + + " )\n" + + ") MULTI_TENANT=TRUE"); + } + + conn.createStatement().execute("UPSERT INTO " + fullTableName + " VALUES ('org1',3,'01')"); + conn.createStatement().execute("UPSERT INTO " + fullTableName + " VALUES ('org1',2,'04')"); + conn.createStatement().execute("UPSERT INTO " + fullTableName + " VALUES ('org1',2,'03')"); + conn.createStatement().execute("UPSERT INTO " + fullTableName + " VALUES ('org1',1,'02')"); + conn.commit(); + + // FIXME: PHOENIX-3383 + // This comparison is really backwards: it should be (score, entity_id) < (2, '04'), + // but because we're matching a descending key, our comparison has to be switched. + try (Statement stmt = conn.createStatement()) { + final ResultSet rs = stmt.executeQuery("SELECT entity_id, score\n" + + "FROM " + fullTableName + "\n" + + "WHERE organization_id = 'org1'\n" + + "AND (score, entity_id) > (2, '04')\n" + + "ORDER BY score DESC, entity_id DESC\n" + + "LIMIT 3"); + assertTrue(rs.next()); + assertEquals("03", rs.getString(1)); + assertEquals(2.0, rs.getDouble(2), 0.001); + assertTrue(rs.next()); + assertEquals("02", rs.getString(1)); + assertEquals(1.0, rs.getDouble(2), 0.001); + assertFalse(rs.next()); + } + // FIXME: PHOENIX-3384 + // It should not be necessary to specify organization_id in this query + try (Statement stmt = conn.createStatement()) { + final ResultSet rs = stmt.executeQuery("SELECT entity_id, score\n" + + "FROM " + fullTableName + "\n" + + "WHERE organization_id = 'org1'\n" + + "AND (organization_id, score, entity_id) > ('org1', 2, '04')\n" + + "ORDER BY score DESC, entity_id DESC\n" + + "LIMIT 3"); + assertTrue(rs.next()); + assertEquals("03", rs.getString(1)); + assertEquals(2.0, rs.getDouble(2), 0.001); + assertTrue(rs.next()); + assertEquals("02", rs.getString(1)); + assertEquals(1.0, rs.getDouble(2), 0.001); + assertFalse(rs.next()); + } + } + + @Test + public void testSingleDescPKColumnComparison() throws Exception { + final Connection conn = DriverManager.getConnection(getUrl()); + String fullTableName = generateRandomString(); + try (Statement stmt = conn.createStatement()) { + stmt.execute("CREATE TABLE " + fullTableName + "(\n" + + " ORGANIZATION_ID CHAR(15) NOT NULL,\n" + + " SCORE DOUBLE NOT NULL,\n" + + " ENTITY_ID CHAR(15) NOT NULL\n" + + " CONSTRAINT PAGE_SNAPSHOT_PK PRIMARY KEY (\n" + + " ORGANIZATION_ID,\n" + + " SCORE DESC,\n" + + " ENTITY_ID DESC\n" + + " )\n" + + ") MULTI_TENANT=TRUE"); + } + + conn.createStatement().execute("UPSERT INTO " + fullTableName + " VALUES ('org1',3,'01')"); + conn.createStatement().execute("UPSERT INTO " + fullTableName + " VALUES ('org1',2,'04')"); + conn.createStatement().execute("UPSERT INTO " + fullTableName + " VALUES ('org1',2,'03')"); + conn.createStatement().execute("UPSERT INTO " + fullTableName + " VALUES ('org1',1,'02')"); + conn.commit(); + + try (Statement stmt = conn.createStatement()) { + // Even though SCORE is descending, the > comparison makes sense logically + // and doesn't have to be reversed as RVC expression comparisons do (which + // is really just a bug - see PHOENIX-3383). + final ResultSet rs = stmt.executeQuery("SELECT entity_id, score\n" + + "FROM " + fullTableName + "\n" + + "WHERE organization_id = 'org1'\n" + + "AND score > 2.0\n" + + "ORDER BY score DESC\n" + + "LIMIT 3"); + assertTrue(rs.next()); + assertEquals("01", rs.getString(1)); + assertEquals(3.0, rs.getDouble(2), 0.001); + assertFalse(rs.next()); + } + } } http://git-wip-us.apache.org/repos/asf/phoenix/blob/a28119d5/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereOptimizer.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereOptimizer.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereOptimizer.java index f49aa52..ba1dc8b 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereOptimizer.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereOptimizer.java @@ -487,6 +487,10 @@ public class WhereOptimizer { int initPosition = (table.getBucketNum() ==null ? 0 : 1) + (this.context.getConnection().getTenantId() != null && table.isMultiTenant() ? 1 : 0) + (table.getViewIndexId() == null ? 0 : 1); if (initPosition == slot.getPKPosition()) { minMaxRange = keyRange; + } else { + // If we cannot set the minMaxRange, then we must not extract the expression since + // we wouldn't be constraining the range at all based on it. + extractNode = null; } } return newKeyParts(slot, extractNode, keyRanges, minMaxRange); http://git-wip-us.apache.org/repos/asf/phoenix/blob/a28119d5/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereOptimizerTest.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereOptimizerTest.java b/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereOptimizerTest.java index c50975b..7275c59 100644 --- a/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereOptimizerTest.java +++ b/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereOptimizerTest.java @@ -62,6 +62,7 @@ import org.apache.phoenix.schema.SortOrder; import org.apache.phoenix.schema.types.PChar; import org.apache.phoenix.schema.types.PDate; import org.apache.phoenix.schema.types.PDecimal; +import org.apache.phoenix.schema.types.PDouble; import org.apache.phoenix.schema.types.PInteger; import org.apache.phoenix.schema.types.PLong; import org.apache.phoenix.schema.types.PUnsignedLong; @@ -2032,4 +2033,64 @@ public class WhereOptimizerTest extends BaseConnectionlessQueryTest { context = compileStatement("select avg(pk1) from test order by count(distinct pk2)"); assertEquals(2, context.getAggregationManager().getAggregators().getAggregatorCount()); } + + @Test + public void testRVCWithLeadingPKEq() throws SQLException { + String tenantId = "o1"; + Connection conn = DriverManager.getConnection(getUrl()); + conn.createStatement().execute("CREATE TABLE COMMUNITIES.TEST (\n" + + " ORGANIZATION_ID CHAR(2) NOT NULL,\n" + + " SCORE DOUBLE NOT NULL,\n" + + " ENTITY_ID CHAR(2) NOT NULL\n" + + " CONSTRAINT PAGE_SNAPSHOT_PK PRIMARY KEY (\n" + + " ORGANIZATION_ID,\n" + + " SCORE,\n" + + " ENTITY_ID\n" + + " )\n" + + ") VERSIONS=1, MULTI_TENANT=TRUE"); + String query = "SELECT entity_id, score\n" + + "FROM communities.test\n" + + "WHERE organization_id = '" + tenantId + "'\n" + + "AND (score, entity_id) > (2.0, '04')\n" + + "ORDER BY score, entity_id"; + Scan scan = compileStatement(query).getScan(); + assertNotNull(scan.getFilter()); + + // See PHOENIX-3384: Optimize RVC expressions for non leading row key columns. + // FIXME: We should be able to optimize this better, taking into account the + // (score, entity_id) > (2.0, '04') to form more of the start/stop row. + assertArrayEquals(PVarchar.INSTANCE.toBytes(tenantId), scan.getStartRow()); + assertArrayEquals(ByteUtil.nextKey(PVarchar.INSTANCE.toBytes(tenantId)), scan.getStopRow()); + } + + @Test + public void testRVCWithCompDescRowKey() throws SQLException { + String tenantId = "o1"; + Connection conn = DriverManager.getConnection(getUrl()); + conn.createStatement().execute("CREATE TABLE COMMUNITIES.TEST (\n" + + " ORGANIZATION_ID CHAR(2) NOT NULL,\n" + + " SCORE DOUBLE NOT NULL,\n" + + " ENTITY_ID CHAR(2) NOT NULL\n" + + " CONSTRAINT PAGE_SNAPSHOT_PK PRIMARY KEY (\n" + + " ORGANIZATION_ID,\n" + + " SCORE DESC,\n" + + " ENTITY_ID DESC\n" + + " )\n" + + ") VERSIONS=1, MULTI_TENANT=TRUE"); + String query = "SELECT entity_id, score\n" + + "FROM communities.test\n" + + "WHERE organization_id = '" + tenantId + "'\n" + + "AND (organization_id, score, entity_id) < ('" + tenantId + "',2.0, '04')\n" + + "ORDER BY score DESC, entity_id DESC"; + Scan scan = compileStatement(query).getScan(); + assertNull(scan.getFilter()); + + // FIXME See PHOENIX-3383: Comparison between descending row keys used in RVC is reverse + // This should set the startRow, but instead it's setting the stopRow + byte[] startRow = PChar.INSTANCE.toBytes(tenantId); + assertArrayEquals(startRow, scan.getStartRow()); + byte[] stopRow = ByteUtil.concat(PChar.INSTANCE.toBytes(tenantId), PDouble.INSTANCE.toBytes(2.0, SortOrder.DESC), PChar.INSTANCE.toBytes("04", SortOrder.DESC)); + assertArrayEquals(stopRow, scan.getStopRow()); + } + }