PHOENIX-4734 SQL Query with an RVC expression lexographically higher than all values in an OR clause causes query to blow up
Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/7856a001 Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/7856a001 Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/7856a001 Branch: refs/heads/4.x-cdh5.14 Commit: 7856a0017f5377ab02e882e964b32e93877d5456 Parents: 670e14f Author: Thomas D'Silva <tdsi...@apache.org> Authored: Fri May 11 00:30:36 2018 +0100 Committer: Pedro Boado <pbo...@apache.org> Committed: Sun May 13 10:45:54 2018 +0100 ---------------------------------------------------------------------- .../phoenix/end2end/RowValueConstructorIT.java | 120 +++++++++++++++++++ .../org/apache/phoenix/compile/ScanRanges.java | 5 + 2 files changed, 125 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/phoenix/blob/7856a001/phoenix-core/src/it/java/org/apache/phoenix/end2end/RowValueConstructorIT.java ---------------------------------------------------------------------- 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 df7603a..fb04261 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 @@ -48,6 +48,7 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; +import java.util.List; import java.util.Properties; import org.apache.phoenix.util.DateUtil; @@ -57,6 +58,9 @@ import org.apache.phoenix.util.QueryUtil; import org.apache.phoenix.util.SchemaUtil; import org.junit.Test; +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; + public class RowValueConstructorIT extends ParallelStatsDisabledIT { @@ -1646,4 +1650,120 @@ public class RowValueConstructorIT extends ParallelStatsDisabledIT { assertEquals("value", rs.getString(3)); assertFalse(rs.next()); } + + @Test + /** + * Verifies that a query with a RVC expression lexographically higher than all values in an OR + * clause causes query works see PHOENIX-4734 + */ + public void testRVCWithAndClause() throws Exception { + final int numItemsInClause = 5; + Properties tenantProps = new Properties(); + tenantProps.setProperty(PhoenixRuntime.TENANT_ID_ATTRIB, "tenant1"); + String fullTableName = SchemaUtil.getTableName("S", "T_" + generateUniqueName()); + String fullViewName = SchemaUtil.getTableName("S", "V_" + generateUniqueName()); + try (Connection tenantConn = DriverManager.getConnection(getUrl(), tenantProps)) { + tenantConn.setAutoCommit(false); + createBaseTableAndTenantView(tenantConn, fullTableName, fullViewName); + loadDataIntoTenantView(tenantConn, fullViewName); + List<String> objectIdsList = + selectObjectIdsForInClause(tenantConn, fullViewName, numItemsInClause); + StringBuilder querySb = generateQueryToTest(numItemsInClause, fullViewName); + PreparedStatement ps = tenantConn.prepareStatement(querySb.toString()); + int numbBindVarsSet = 0; + String objectId = null; + for (int i = 0; i < numItemsInClause; i++) { + objectId = objectIdsList.get(i); + ps.setString((i + 1), objectId); + numbBindVarsSet++; + } + assertEquals(numItemsInClause, numbBindVarsSet); + assertEquals("v1000", objectId); + ps.setString(numItemsInClause + 1, "z00"); + ps.setString(numItemsInClause + 2, "v1000"); // This value must match or be + // lexographically higher than the highest + // value in the IN clause + // Query should execute and return 0 results + ResultSet rs = ps.executeQuery(); + assertFalse( + "Query should return no results as IN clause and RVC clause are disjoint sets", + rs.next()); + } + } + + private StringBuilder generateQueryToTest(int numItemsInClause, String fullViewName) { + StringBuilder querySb = + new StringBuilder("SELECT OBJECT_ID,OBJECT_DATA2,OBJECT_DATA FROM " + fullViewName); + querySb.append(" WHERE (("); + List<String> orClauses = Lists.newArrayList(); + for (int i = 1; i < (numItemsInClause + 1); i++) { + orClauses.add("OBJECT_ID = ?"); + } + querySb.append(Joiner.on(" OR ").join(orClauses)); + querySb.append(") AND (KEY_PREFIX,OBJECT_ID) > (?,?)) ORDER BY OBJECT_ID LIMIT 125"); + System.out.println(querySb); + return querySb; + } + + private List<String> selectObjectIdsForInClause(Connection tenantConn, String fullViewName, + int numItemsInClause) throws SQLException { + String sqlForObjIds = + "SELECT OBJECT_ID FROM " + fullViewName + " ORDER BY OBJECT_ID LIMIT " + + numItemsInClause; + PreparedStatement ps = tenantConn.prepareStatement(sqlForObjIds); + ResultSet rs = ps.executeQuery(); + List<String> objectIdsList = Lists.newArrayList(); + System.out.println("ObjectIds: "); + while (rs.next()) { + System.out.println("Object Id: " + rs.getString("OBJECT_ID")); + objectIdsList.add(rs.getString("OBJECT_ID")); + } + assertEquals(numItemsInClause, objectIdsList.size()); + return objectIdsList; + } + + private void loadDataIntoTenantView(Connection tenantConn, String fullViewName) + throws SQLException { + for (int i = 0; i < 2000; i++) { + String objectId = "v" + i; + String upsert = + "UPSERT INTO " + fullViewName + + " (OBJECT_ID, OBJECT_DATA, OBJECT_DATA2) VALUES ('" + objectId + + "', 'data','data2')"; + PreparedStatement ps = tenantConn.prepareStatement(upsert); + ps.executeUpdate(); + } + tenantConn.commit(); + + // Validate Data was loaded correctly + PreparedStatement selectStatement = + tenantConn.prepareStatement( + "SELECT OBJECT_ID FROM " + fullViewName + " ORDER BY OBJECT_ID"); + ResultSet rs = selectStatement.executeQuery(); + int count = 0; + while (rs.next()) { + count++; + } + assertEquals(2000, count); + } + + private void createBaseTableAndTenantView(Connection tenantConn, String fulTableName, + String fullViewName) throws SQLException { + String ddl = + "CREATE TABLE IF NOT EXISTS " + fulTableName + " (TENANT_ID CHAR(15) NOT NULL," + + " KEY_PREFIX CHAR(3) NOT NULL," + " CREATED_DATE DATE," + + " CREATED_BY CHAR(15)," + " SYSTEM_MODSTAMP DATE" + + " CONSTRAINT PK PRIMARY KEY (TENANT_ID,KEY_PREFIX)" + + ") VERSIONS=1, MULTI_TENANT=true, IMMUTABLE_ROWS=TRUE, REPLICATION_SCOPE=1"; + createTestTable(getUrl(), ddl); + + String tenantViewDdl = + "CREATE VIEW IF NOT EXISTS " + fullViewName + " (OBJECT_ID VARCHAR(18) NOT NULL, " + + "OBJECT_DATA VARCHAR(131072), " + "OBJECT_DATA2 VARCHAR(131072), " + + "CONSTRAINT PK PRIMARY KEY (OBJECT_ID)) " + "AS SELECT * FROM " + + fulTableName + " WHERE KEY_PREFIX = 'z00'"; + // Get tenant specific connection + PreparedStatement stmt2 = tenantConn.prepareStatement(tenantViewDdl); + stmt2.execute(); + } } http://git-wip-us.apache.org/repos/asf/phoenix/blob/7856a001/phoenix-core/src/main/java/org/apache/phoenix/compile/ScanRanges.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/ScanRanges.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/ScanRanges.java index 8c71248..019f15d 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/compile/ScanRanges.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/ScanRanges.java @@ -105,6 +105,11 @@ public class ScanRanges { keyRanges.add(KeyRange.getKeyRange(key)); } } + // while doing a point look up if after intersecting with the MinMaxrange there are + // no more keyranges left then just return + if (keyRanges.isEmpty()) { + return NOTHING; + } ranges = Collections.singletonList(keyRanges); useSkipScan = keyRanges.size() > 1; // Treat as binary if descending because we've got a separator byte at the end