This is an automated email from the ASF dual-hosted git repository.

vjasani pushed a commit to branch 5.3
in repository https://gitbox.apache.org/repos/asf/phoenix.git


The following commit(s) were added to refs/heads/5.3 by this push:
     new ec1e01dc41 PHOENIX-7760 Incorrect scan range for RVC with overlapping 
constraints (#2367)
ec1e01dc41 is described below

commit ec1e01dc41fd12560344dc86b0a16e9d402c79c2
Author: Viraj Jasani <[email protected]>
AuthorDate: Mon Feb 9 21:37:04 2026 -0800

    PHOENIX-7760 Incorrect scan range for RVC with overlapping constraints 
(#2367)
---
 .../org/apache/phoenix/compile/WhereOptimizer.java |  16 ++-
 .../phoenix/end2end/RowValueConstructorIT.java     | 108 +++++++++++++++++++++
 2 files changed, 121 insertions(+), 3 deletions(-)

diff --git 
a/phoenix-core-client/src/main/java/org/apache/phoenix/compile/WhereOptimizer.java
 
b/phoenix-core-client/src/main/java/org/apache/phoenix/compile/WhereOptimizer.java
index 9f8e3f14c0..22a59e3487 100644
--- 
a/phoenix-core-client/src/main/java/org/apache/phoenix/compile/WhereOptimizer.java
+++ 
b/phoenix-core-client/src/main/java/org/apache/phoenix/compile/WhereOptimizer.java
@@ -1571,20 +1571,30 @@ public class WhereOptimizer {
       byte[] lowerRange = result.getLowerRange();
       byte[] clippedLowerRange = lowerRange;
       byte[] fullLowerRange = otherRange.getLowerRange();
+      boolean fullLowerRangeUsed = false;
       if (!result.lowerUnbound() && Bytes.startsWith(fullLowerRange, 
clippedLowerRange)) {
         lowerRange = fullLowerRange;
+        fullLowerRangeUsed = true;
       }
       byte[] upperRange = result.getUpperRange();
       byte[] clippedUpperRange = upperRange;
       byte[] fullUpperRange = otherRange.getUpperRange();
-      if (!result.lowerUnbound() && Bytes.startsWith(fullUpperRange, 
clippedUpperRange)) {
+      boolean fullUpperRangeUsed = false;
+      if (!result.upperUnbound() && Bytes.startsWith(fullUpperRange, 
clippedUpperRange)) {
         upperRange = fullUpperRange;
+        fullUpperRangeUsed = true;
       }
       if (lowerRange == clippedLowerRange && upperRange == clippedUpperRange) {
         return result;
       }
-      return KeyRange.getKeyRange(lowerRange, result.isLowerInclusive(), 
upperRange,
-        result.isUpperInclusive());
+      // When we restore the full range bytes from otherRange, we must also use
+      // the inclusivity from otherRange. Otherwise, an exclusive bound would 
incorrectly
+      // become inclusive (PHOENIX-7760).
+      boolean lowerInclusive =
+        fullLowerRangeUsed ? otherRange.isLowerInclusive() : 
result.isLowerInclusive();
+      boolean upperInclusive =
+        fullUpperRangeUsed ? otherRange.isUpperInclusive() : 
result.isUpperInclusive();
+      return KeyRange.getKeyRange(lowerRange, lowerInclusive, upperRange, 
upperInclusive);
     }
 
     /**
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 9997badea7..f2e4a2faa6 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
@@ -2166,4 +2166,112 @@ public class RowValueConstructorIT extends 
ParallelStatsDisabledIT {
     PreparedStatement stmt2 = tenantConn.prepareStatement(tenantViewDdl);
     stmt2.execute();
   }
+
+  @Test
+  public void testRVCOverlappingKeyRange() throws Exception {
+    String tableName = generateUniqueName();
+    Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+
+    try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+      conn.createStatement()
+        .execute("CREATE TABLE " + tableName + " (" + "hk VARCHAR NOT NULL, "
+          + "sk VARCHAR NOT NULL, " + "ihk VARCHAR NOT NULL, " + "isk VARCHAR 
NOT NULL, "
+          + "data VARCHAR " + "CONSTRAINT pk PRIMARY KEY (hk, sk, ihk, isk))");
+
+      PreparedStatement upsert = conn.prepareStatement(
+        "UPSERT INTO " + tableName + " (hk, sk, ihk, isk, data) VALUES (?, ?, 
?, ?, ?)");
+      for (int i = 1; i <= 20; i++) {
+        String pad = String.format("%02d", i);
+        upsert.setString(1, "hk");
+        upsert.setString(2, "sk100");
+        upsert.setString(3, "idx");
+        upsert.setString(4, "isk" + pad);
+        upsert.setString(5, "data" + pad);
+        upsert.execute();
+      }
+      conn.commit();
+
+      String query = "SELECT * FROM " + tableName + " WHERE hk = 'hk' AND "
+        + "(sk, ihk, isk) > ('sk100', 'idx', 'isk11') ORDER BY sk, ihk, isk 
LIMIT 5";
+      assertValues1(conn, query);
+
+      query = "SELECT * FROM " + tableName + " WHERE hk = 'hk' AND sk <= 
'sk200' "
+        + "AND (sk, ihk, isk) > ('sk100', 'idx', 'isk11') ORDER BY sk, ihk, 
isk" + " LIMIT 5";
+      assertValues1(conn, query);
+
+      query =
+        "SELECT * FROM " + tableName + " WHERE hk = 'hk'" + " AND sk <= 
'sk200' AND sk >= 'sk1'"
+          + " AND (sk, ihk, isk) > ('sk100', 'idx', 'isk11')" + " ORDER BY sk, 
ihk, isk LIMIT 5";
+      assertValues1(conn, query);
+
+      query = "SELECT * FROM " + tableName + " WHERE hk = 'hk' AND sk >= 
'sk000' "
+        + "AND (sk, ihk, isk) >= ('sk100', 'idx', 'isk12') "
+        + "AND (sk, ihk, isk) < ('sk100', 'idx', 'isk17') ORDER BY sk, ihk, 
isk";
+      assertValues1(conn, query);
+
+      query = "SELECT * FROM " + tableName + " WHERE hk = 'hk' AND sk >= 
'sk000' "
+        + "AND (sk, ihk, isk) > ('sk100', 'idx', 'isk11') "
+        + "AND (sk, ihk, isk) < ('sk100', 'idx', 'isk17') ORDER BY sk, ihk, 
isk";
+      assertValues1(conn, query);
+
+      query = "SELECT * FROM " + tableName + " WHERE hk = 'hk' AND sk >= 
'sk000' "
+        + "AND (sk, ihk, isk) < ('sk100', 'idx', 'isk17') ORDER BY sk, ihk, 
isk";
+      assertValues2(conn, query);
+
+      query = "SELECT hk, sk, ihk, isk FROM " + tableName
+        + " WHERE hk = 'hk' AND sk <= 'sk200' AND (sk, ihk, isk) < ('sk100', 
'idx', 'isk11')"
+        + " ORDER BY hk ASC, sk DESC, ihk DESC, isk DESC LIMIT 5";
+      assertValues3(conn, query);
+    }
+  }
+
+  private static void assertValues1(Connection conn, String query) throws 
SQLException {
+    try (Statement stmt = conn.createStatement()) {
+      ResultSet rs = stmt.executeQuery(query);
+      List<String> results = Lists.newArrayList();
+      while (rs.next()) {
+        results.add(rs.getString("isk"));
+      }
+      assertEquals(5, results.size());
+      assertFalse("Should not include isk11", results.contains("isk11"));
+      assertEquals("isk12", results.get(0));
+      assertEquals("isk13", results.get(1));
+      assertEquals("isk14", results.get(2));
+      assertEquals("isk15", results.get(3));
+      assertEquals("isk16", results.get(4));
+    }
+  }
+
+  private static void assertValues2(Connection conn, String query) throws 
SQLException {
+    try (Statement stmt = conn.createStatement()) {
+      ResultSet rs = stmt.executeQuery(query);
+      List<String> results = Lists.newArrayList();
+      while (rs.next()) {
+        results.add(rs.getString("isk"));
+      }
+      assertEquals(16, results.size());
+      assertTrue("Should not include isk17-isk20 range", 
!results.contains("isk17")
+        && !results.contains("isk18") && !results.contains("isk19") && 
!results.contains("isk20"));
+      assertEquals("isk01", results.get(0));
+      assertEquals("isk16", results.get(15));
+    }
+  }
+
+  private static void assertValues3(Connection conn, String query) throws 
SQLException {
+    try (Statement stmt = conn.createStatement()) {
+      ResultSet rs = stmt.executeQuery(query);
+      List<String> results = Lists.newArrayList();
+      while (rs.next()) {
+        results.add(rs.getString("isk"));
+      }
+      assertEquals(5, results.size());
+      assertFalse(results.contains("isk11"));
+      assertEquals("isk10", results.get(0));
+      assertEquals("isk09", results.get(1));
+      assertEquals("isk08", results.get(2));
+      assertEquals("isk07", results.get(3));
+      assertEquals("isk06", results.get(4));
+    }
+  }
+
 }

Reply via email to