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 c8f75adf58 PHOENIX-7770 Incorrect index scan range while using RVC on 
VARBINARY_ENCODED (#2380)
c8f75adf58 is described below

commit c8f75adf58004428cfa100812461565ee7f0b070
Author: Viraj Jasani <[email protected]>
AuthorDate: Thu Feb 19 12:05:40 2026 -0800

    PHOENIX-7770 Incorrect index scan range while using RVC on 
VARBINARY_ENCODED (#2380)
---
 .../org/apache/phoenix/compile/WhereOptimizer.java |  10 +-
 .../phoenix/end2end/RowValueConstructorIT.java     | 357 +++++++++++++++++++++
 2 files changed, 364 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 22a59e3487..ecb71aad52 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
@@ -77,6 +77,7 @@ import org.apache.phoenix.schema.tuple.Tuple;
 import org.apache.phoenix.schema.types.PChar;
 import org.apache.phoenix.schema.types.PDataType;
 import org.apache.phoenix.schema.types.PVarbinary;
+import org.apache.phoenix.schema.types.PVarbinaryEncoded;
 import org.apache.phoenix.schema.types.PVarchar;
 import org.apache.phoenix.schema.types.PhoenixArray;
 import org.apache.phoenix.util.ByteUtil;
@@ -421,9 +422,11 @@ public class WhereOptimizer {
 
   private static KeyRange getTrailingRange(RowKeySchema rowKeySchema, int 
clippedPkPos,
     KeyRange range, KeyRange clippedResult, ImmutableBytesWritable ptr) {
-    // We are interested in the clipped part's Seperator. Since we combined 
first part, we need to
+    // We are interested in the clipped part's Separator. Since we combined 
first part, we need to
     // remove its separator from the trailing parts' start
-    int clippedSepLength = 
rowKeySchema.getField(clippedPkPos).getDataType().isFixedWidth() ? 0 : 1;
+    PDataType clippedType = rowKeySchema.getField(clippedPkPos).getDataType();
+    int clippedSepLength =
+      clippedType.isFixedWidth() ? 0 : clippedType == 
PVarbinaryEncoded.INSTANCE ? 2 : 1;
     byte[] lowerRange = KeyRange.UNBOUND;
     boolean lowerInclusive = false;
     // Lower range of trailing part of RVC must be true, so we can form a new 
range to intersect
@@ -1614,8 +1617,9 @@ public class WhereOptimizer {
       int otherPKPos) {
       RowKeySchema rowKeySchema = table.getRowKeySchema();
       ImmutableBytesWritable ptr = context.getTempPtr();
+      PDataType sepType = table.getPKColumns().get(otherPKPos - 
1).getDataType();
       int separatorLength =
-        table.getPKColumns().get(otherPKPos - 1).getDataType().isFixedWidth() 
? 0 : 1;
+        sepType.isFixedWidth() ? 0 : sepType == PVarbinaryEncoded.INSTANCE ? 2 
: 1;
       boolean lowerInclusive = result.isLowerInclusive();
       byte[] lowerRange = result.getLowerRange();
       ptr.set(lowerRange);
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 f4b03c7c5c..a10cc8745d 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
@@ -2496,6 +2496,17 @@ public class RowValueConstructorIT extends 
ParallelStatsDisabledIT {
     assertEquals(f, rs.getLong("f"));
   }
 
+  private void assertRowBytes(ResultSet rs, byte[] a, long b, byte[] c, int d, 
byte[] e, long f)
+    throws SQLException {
+    assertTrue(rs.next());
+    assertArrayEquals(a, rs.getBytes("a"));
+    assertEquals(b, rs.getLong("b"));
+    assertArrayEquals(c, rs.getBytes("c"));
+    assertEquals(d, rs.getInt("d"));
+    assertArrayEquals(e, rs.getBytes("e"));
+    assertEquals(f, rs.getLong("f"));
+  }
+
   private StringBuilder generateQueryToTest(int numItemsInClause, String 
fullViewName) {
     StringBuilder querySb =
       new StringBuilder("SELECT OBJECT_ID,OBJECT_DATA2,OBJECT_DATA FROM " + 
fullViewName);
@@ -2672,4 +2683,350 @@ public class RowValueConstructorIT extends 
ParallelStatsDisabledIT {
     }
   }
 
+  @Test
+  public void testRVCWithAlternatingFixedVarBinaryEncodedPK() throws Exception 
{
+    Connection conn = nextConnection(getUrl());
+    String tableName = generateUniqueName();
+
+    String ddl = "CREATE TABLE " + tableName + " (" + "a VARBINARY_ENCODED NOT 
NULL, "
+      + "b BIGINT NOT NULL, " + "c VARBINARY_ENCODED NOT NULL, " + "d INTEGER 
NOT NULL, "
+      + "e VARBINARY_ENCODED NOT NULL, " + "f BIGINT NOT NULL, " + "val 
VARCHAR, "
+      + "CONSTRAINT pk PRIMARY KEY (a, b, c, d, e, f))";
+    conn.createStatement().execute(ddl);
+
+    conn = nextConnection(getUrl());
+    PreparedStatement stmt = conn.prepareStatement(
+      "UPSERT INTO " + tableName + " (a, b, c, d, e, f, val) VALUES (?, ?, ?, 
?, ?, ?, ?)");
+    for (int i = 0; i < 5000; i++) {
+      stmt.setBytes(1, new byte[] { 0x00, 0x0A, (byte) (i % 5) });
+      stmt.setLong(2, i);
+      stmt.setBytes(3, new byte[] { 0x0C, (byte) (i % 7) });
+      stmt.setInt(4, i % 100);
+      stmt.setBytes(5, new byte[] { 0x00, 0x0E, (byte) (i % 3) });
+      stmt.setLong(6, i);
+      stmt.setString(7, "val_" + i);
+      stmt.execute();
+      if (i % 1000 == 0) {
+        conn.commit();
+      }
+    }
+    conn.commit();
+
+    byte[] a0 = { 0x00, 0x0A, 0 };
+    byte[] c5 = { 0x0C, 5 };
+    byte[] c3 = { 0x0C, 3 };
+    byte[] c1 = { 0x0C, 1 };
+    byte[] c6 = { 0x0C, 6 };
+    byte[] c4 = { 0x0C, 4 };
+    byte[] e0 = { 0x00, 0x0E, 0 };
+    byte[] e1 = { 0x00, 0x0E, 1 };
+    byte[] e2 = { 0x00, 0x0E, 2 };
+
+    conn = nextConnection(getUrl());
+    PreparedStatement q = conn.prepareStatement("SELECT a, b, c, d, e, f FROM 
" + tableName
+      + " WHERE a = ? AND b >= 2490 AND b <= 2510 ORDER BY a, b, c, d, e, f");
+    q.setBytes(1, a0);
+    ResultSet rs = q.executeQuery();
+    assertRowBytes(rs, a0, 2490, c5, 90, e0, 2490);
+    assertRowBytes(rs, a0, 2495, c3, 95, e2, 2495);
+    assertRowBytes(rs, a0, 2500, c1, 0, e1, 2500);
+    assertRowBytes(rs, a0, 2505, c6, 5, e0, 2505);
+    assertRowBytes(rs, a0, 2510, c4, 10, e2, 2510);
+    assertFalse(rs.next());
+
+    conn = nextConnection(getUrl());
+    q = conn.prepareStatement("SELECT a, b, c, d, e, f FROM " + tableName
+      + " WHERE a = ? AND b <= 2510 AND (b > 2495 OR (b = 2495 AND c > ?))"
+      + " ORDER BY a, b, c, d, e, f LIMIT 100");
+    q.setBytes(1, a0);
+    q.setBytes(2, c3);
+    rs = q.executeQuery();
+    assertRowBytes(rs, a0, 2500, c1, 0, e1, 2500);
+    assertRowBytes(rs, a0, 2505, c6, 5, e0, 2505);
+    assertRowBytes(rs, a0, 2510, c4, 10, e2, 2510);
+    assertFalse(rs.next());
+
+    conn = nextConnection(getUrl());
+    q = conn.prepareStatement("SELECT a, b, c, d, e, f FROM " + tableName
+      + " WHERE a = ? AND b <= 2510 AND (b, c) > (?, ?)" + " ORDER BY a, b, c, 
d, e, f LIMIT 100");
+    q.setBytes(1, a0);
+    q.setLong(2, 2495);
+    q.setBytes(3, c3);
+    rs = q.executeQuery();
+    assertRowBytes(rs, a0, 2500, c1, 0, e1, 2500);
+    assertRowBytes(rs, a0, 2505, c6, 5, e0, 2505);
+    assertRowBytes(rs, a0, 2510, c4, 10, e2, 2510);
+    assertFalse(rs.next());
+
+    conn = nextConnection(getUrl());
+    q = conn.prepareStatement("SELECT a, b, c, d, e, f FROM " + tableName
+      + " WHERE a = ? AND b <= 2510 AND (b, c, d) > (?, ?, ?)"
+      + " ORDER BY a, b, c, d, e, f LIMIT 100");
+    q.setBytes(1, a0);
+    q.setLong(2, 2500);
+    q.setBytes(3, c1);
+    q.setInt(4, 0);
+    rs = q.executeQuery();
+    assertRowBytes(rs, a0, 2505, c6, 5, e0, 2505);
+    assertRowBytes(rs, a0, 2510, c4, 10, e2, 2510);
+    assertFalse(rs.next());
+
+    conn = nextConnection(getUrl());
+    q = conn.prepareStatement("SELECT a, b, c, d, e, f FROM " + tableName
+      + " WHERE a = ? AND b <= 2510 AND (b, c, d, e) > (?, ?, ?, ?)"
+      + " ORDER BY a, b, c, d, e, f LIMIT 100");
+    q.setBytes(1, a0);
+    q.setLong(2, 2500);
+    q.setBytes(3, c1);
+    q.setInt(4, 0);
+    q.setBytes(5, e1);
+    rs = q.executeQuery();
+    assertRowBytes(rs, a0, 2505, c6, 5, e0, 2505);
+    assertRowBytes(rs, a0, 2510, c4, 10, e2, 2510);
+    assertFalse(rs.next());
+
+    conn = nextConnection(getUrl());
+    q = conn.prepareStatement("SELECT a, b, c, d, e, f FROM " + tableName
+      + " WHERE a = ? AND (b, c) <= (?, ?) AND (b, c, d) > (?, ?, ?)"
+      + " ORDER BY a, b, c, d, e, f LIMIT 100");
+    q.setBytes(1, a0);
+    q.setLong(2, 2505);
+    q.setBytes(3, c6);
+    q.setLong(4, 2495);
+    q.setBytes(5, c3);
+    q.setInt(6, 95);
+    rs = q.executeQuery();
+    assertRowBytes(rs, a0, 2500, c1, 0, e1, 2500);
+    assertRowBytes(rs, a0, 2505, c6, 5, e0, 2505);
+    assertFalse(rs.next());
+
+    conn = nextConnection(getUrl());
+    q = conn.prepareStatement("SELECT a, b, c, d, e, f FROM " + tableName
+      + " WHERE a = ? AND (b, c, d) <= (?, ?, ?) AND (b, c, d, e, f) > (?, ?, 
?, ?, ?)"
+      + " ORDER BY a, b, c, d, e, f LIMIT 100");
+    q.setBytes(1, a0);
+    q.setLong(2, 2505);
+    q.setBytes(3, c6);
+    q.setInt(4, 5);
+    q.setLong(5, 2495);
+    q.setBytes(6, c3);
+    q.setInt(7, 95);
+    q.setBytes(8, e2);
+    q.setLong(9, 2495);
+    rs = q.executeQuery();
+    assertRowBytes(rs, a0, 2500, c1, 0, e1, 2500);
+    assertRowBytes(rs, a0, 2505, c6, 5, e0, 2505);
+    assertFalse(rs.next());
+
+    conn = nextConnection(getUrl());
+    q = conn.prepareStatement("SELECT a, b, c, d, e, f FROM " + tableName
+      + " WHERE a = ? AND b >= 2495 AND (b, c, d) < (?, ?, ?)"
+      + " ORDER BY a, b, c, d, e, f LIMIT 100");
+    q.setBytes(1, a0);
+    q.setLong(2, 2510);
+    q.setBytes(3, c4);
+    q.setInt(4, 10);
+    rs = q.executeQuery();
+    assertRowBytes(rs, a0, 2495, c3, 95, e2, 2495);
+    assertRowBytes(rs, a0, 2500, c1, 0, e1, 2500);
+    assertRowBytes(rs, a0, 2505, c6, 5, e0, 2505);
+    assertFalse(rs.next());
+
+    conn = nextConnection(getUrl());
+    q = conn.prepareStatement(
+      "SELECT a, b, c, d, e, f FROM " + tableName + " WHERE a = ? AND b <= 
2510 AND (b, c) > (?, ?)"
+        + " ORDER BY a ASC, b DESC, c DESC, d DESC, e DESC, f DESC LIMIT 100");
+    q.setBytes(1, a0);
+    q.setLong(2, 2495);
+    q.setBytes(3, c3);
+    rs = q.executeQuery();
+    assertRowBytes(rs, a0, 2510, c4, 10, e2, 2510);
+    assertRowBytes(rs, a0, 2505, c6, 5, e0, 2505);
+    assertRowBytes(rs, a0, 2500, c1, 0, e1, 2500);
+    assertFalse(rs.next());
+
+    conn = nextConnection(getUrl());
+    q = conn.prepareStatement("SELECT a, b, c, d, e, f FROM " + tableName
+      + " WHERE a = ? AND (b, c) <= (?, ?) AND (b, c, d) > (?, ?, ?)"
+      + " ORDER BY a ASC, b DESC, c DESC, d DESC, e DESC, f DESC LIMIT 100");
+    q.setBytes(1, a0);
+    q.setLong(2, 2505);
+    q.setBytes(3, c6);
+    q.setLong(4, 2495);
+    q.setBytes(5, c3);
+    q.setInt(6, 95);
+    rs = q.executeQuery();
+    assertRowBytes(rs, a0, 2505, c6, 5, e0, 2505);
+    assertRowBytes(rs, a0, 2500, c1, 0, e1, 2500);
+    assertFalse(rs.next());
+
+    conn = nextConnection(getUrl());
+    q = conn.prepareStatement("SELECT a, b, c, d, e, f FROM " + tableName
+      + " WHERE a = ? AND b >= 2495 AND (b, c, d) < (?, ?, ?)"
+      + " ORDER BY a ASC, b DESC, c DESC, d DESC, e DESC, f DESC LIMIT 100");
+    q.setBytes(1, a0);
+    q.setLong(2, 2510);
+    q.setBytes(3, c4);
+    q.setInt(4, 10);
+    rs = q.executeQuery();
+    assertRowBytes(rs, a0, 2505, c6, 5, e0, 2505);
+    assertRowBytes(rs, a0, 2500, c1, 0, e1, 2500);
+    assertRowBytes(rs, a0, 2495, c3, 95, e2, 2495);
+    assertFalse(rs.next());
+  }
+
+  @Test
+  public void testRVCWithUncoveredIndex1() throws Exception {
+    Connection conn = nextConnection(getUrl());
+    String tableName = generateUniqueName();
+    String indexName = generateUniqueName();
+
+    String ddl = "CREATE TABLE " + tableName + " (HK VARBINARY_ENCODED NOT 
NULL,"
+      + "  SK VARBINARY_ENCODED NOT NULL," + "  GSI_HK VARBINARY_ENCODED,"
+      + "  GSI_SK VARBINARY_ENCODED," + "  DATA VARCHAR," + "  CONSTRAINT pk 
PRIMARY KEY(HK, SK))";
+    conn.createStatement().execute(ddl);
+
+    conn = nextConnection(getUrl());
+    conn.createStatement()
+      .execute("CREATE UNCOVERED INDEX " + indexName + " ON " + tableName + " 
(GSI_HK, GSI_SK)");
+
+    conn = nextConnection(getUrl());
+    PreparedStatement upsert = conn.prepareStatement(
+      "UPSERT INTO " + tableName + " (HK, SK, GSI_HK, GSI_SK, DATA) VALUES (?, 
?, ?, ?, ?)");
+
+    byte[] gsiHk = new byte[] { 0x00, 0x00, 0x00, 0x0A };
+    byte[] gsiSk = "1".getBytes();
+
+    byte[] hk1 = new byte[] { 0x0B, 0x01 };
+    byte[] sk1 = "1".getBytes();
+    upsert.setBytes(1, hk1);
+    upsert.setBytes(2, sk1);
+    upsert.setBytes(3, gsiHk);
+    upsert.setBytes(4, gsiSk);
+    upsert.setString(5, "row1-should-be-excluded");
+    upsert.execute();
+
+    byte[] hk2 = new byte[] { 0x0B, 0x01 };
+    byte[] sk2 = "2".getBytes();
+    upsert.setBytes(1, hk2);
+    upsert.setBytes(2, sk2);
+    upsert.setBytes(3, gsiHk);
+    upsert.setBytes(4, gsiSk);
+    upsert.setString(5, "row2-include");
+    upsert.execute();
+
+    byte[] hk3 = new byte[] { 0x0B, 0x02 };
+    byte[] sk3 = "1".getBytes();
+    upsert.setBytes(1, hk3);
+    upsert.setBytes(2, sk3);
+    upsert.setBytes(3, gsiHk);
+    upsert.setBytes(4, gsiSk);
+    upsert.setString(5, "row3-include");
+    upsert.execute();
+
+    conn.commit();
+
+    conn = nextConnection(getUrl());
+    String query = "SELECT /*+ INDEX(" + tableName + " " + indexName + ") */ "
+      + "HK, SK, DATA FROM " + tableName + " WHERE GSI_HK = ?" + " AND GSI_SK 
= ?"
+      + " AND (GSI_SK, HK, SK) > (?, ?, ?)" + " ORDER BY GSI_SK, HK, SK";
+
+    PreparedStatement stmt = conn.prepareStatement(query);
+    stmt.setBytes(1, gsiHk);
+    stmt.setBytes(2, gsiSk);
+    stmt.setBytes(3, gsiSk);
+    stmt.setBytes(4, hk1);
+    stmt.setBytes(5, sk1);
+    ResultSet rs = stmt.executeQuery();
+
+    assertTrue("Expected row2 to be returned", rs.next());
+    assertArrayEquals(hk2, rs.getBytes("HK"));
+    assertArrayEquals(sk2, rs.getBytes("SK"));
+    assertEquals("row2-include", rs.getString("DATA"));
+
+    assertTrue("Expected row3 to be returned", rs.next());
+    assertArrayEquals(hk3, rs.getBytes("HK"));
+    assertArrayEquals(sk3, rs.getBytes("SK"));
+    assertEquals("row3-include", rs.getString("DATA"));
+
+    assertFalse("Expected only 2 rows but got more", rs.next());
+  }
+
+  @Test
+  public void testRVCWithUncoveredIndex2() throws Exception {
+    Connection conn = nextConnection(getUrl());
+    String tableName = generateUniqueName();
+    String indexName = generateUniqueName();
+
+    String ddl = "CREATE TABLE " + tableName + " (HK VARCHAR NOT NULL," + "  
SK VARCHAR NOT NULL,"
+      + "  GSI_HK VARCHAR," + "  GSI_SK VARCHAR," + "  DATA VARCHAR,"
+      + "  CONSTRAINT pk PRIMARY KEY(HK, SK))";
+    conn.createStatement().execute(ddl);
+
+    conn = nextConnection(getUrl());
+    conn.createStatement()
+      .execute("CREATE UNCOVERED INDEX " + indexName + " ON " + tableName + " 
(GSI_HK, GSI_SK)");
+
+    conn = nextConnection(getUrl());
+    PreparedStatement upsert = conn.prepareStatement(
+      "UPSERT INTO " + tableName + " (HK, SK, GSI_HK, GSI_SK, DATA) VALUES (?, 
?, ?, ?, ?)");
+
+    String gsiHk = "gsi_hk_1";
+    String gsiSk = "gsi_sk_1";
+
+    String hk1 = "hk_bb";
+    String sk1 = "sk_1";
+    upsert.setString(1, hk1);
+    upsert.setString(2, sk1);
+    upsert.setString(3, gsiHk);
+    upsert.setString(4, gsiSk);
+    upsert.setString(5, "row1-should-be-excluded");
+    upsert.execute();
+
+    String hk2 = "hk_bb";
+    String sk2 = "sk_2";
+    upsert.setString(1, hk2);
+    upsert.setString(2, sk2);
+    upsert.setString(3, gsiHk);
+    upsert.setString(4, gsiSk);
+    upsert.setString(5, "row2-include");
+    upsert.execute();
+
+    String hk3 = "hk_cc";
+    String sk3 = "sk_1";
+    upsert.setString(1, hk3);
+    upsert.setString(2, sk3);
+    upsert.setString(3, gsiHk);
+    upsert.setString(4, gsiSk);
+    upsert.setString(5, "row3-include");
+    upsert.execute();
+
+    conn.commit();
+
+    conn = nextConnection(getUrl());
+    String query = "SELECT /*+ INDEX(" + tableName + " " + indexName + ") */ "
+      + "HK, SK, DATA FROM " + tableName + " WHERE GSI_HK = ?" + " AND GSI_SK 
= ?"
+      + " AND (GSI_SK, HK, SK) > (?, ?, ?)" + " ORDER BY GSI_SK, HK, SK";
+
+    PreparedStatement stmt = conn.prepareStatement(query);
+    stmt.setString(1, gsiHk);
+    stmt.setString(2, gsiSk);
+    stmt.setString(3, gsiSk);
+    stmt.setString(4, hk1);
+    stmt.setString(5, sk1);
+    ResultSet rs = stmt.executeQuery();
+
+    assertTrue("Expected row2 to be returned", rs.next());
+    assertEquals(hk2, rs.getString("HK"));
+    assertEquals(sk2, rs.getString("SK"));
+    assertEquals("row2-include", rs.getString("DATA"));
+
+    assertTrue("Expected row3 to be returned", rs.next());
+    assertEquals(hk3, rs.getString("HK"));
+    assertEquals(sk3, rs.getString("SK"));
+    assertEquals("row3-include", rs.getString("DATA"));
+
+    assertFalse("Expected only 2 rows but got more", rs.next());
+  }
+
 }

Reply via email to