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

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


The following commit(s) were added to refs/heads/master by this push:
     new 3d558d9bde PHOENIX-7659: Leverage = ANY() instead of big IN list to do 
huge number of point lookups in a single query (#2239)
3d558d9bde is described below

commit 3d558d9bde1478b824b303beaa87447f1da17f5d
Author: sanjeet006py <[email protected]>
AuthorDate: Thu Jul 31 08:45:37 2025 +0530

    PHOENIX-7659: Leverage = ANY() instead of big IN list to do huge number of 
point lookups in a single query (#2239)
    
    ---------
    
    Co-authored-by: Sanjeet Malhotra 
<[email protected]>
---
 .../org/apache/phoenix/compile/WhereOptimizer.java | 149 ++++
 .../end2end/WhereOptimizerForArrayAnyIT.java       | 891 +++++++++++++++++++++
 2 files changed, 1040 insertions(+)

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 52b01d9853..9f8e3f14c0 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
@@ -51,6 +51,8 @@ import org.apache.phoenix.expression.LiteralExpression;
 import org.apache.phoenix.expression.OrExpression;
 import org.apache.phoenix.expression.RowKeyColumnExpression;
 import org.apache.phoenix.expression.RowValueConstructorExpression;
+import org.apache.phoenix.expression.function.ArrayAnyComparisonExpression;
+import org.apache.phoenix.expression.function.ArrayElemRefExpression;
 import 
org.apache.phoenix.expression.function.FunctionExpression.OrderPreserving;
 import org.apache.phoenix.expression.function.ScalarFunction;
 import org.apache.phoenix.expression.visitor.ExpressionVisitor;
@@ -76,6 +78,7 @@ 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.PVarchar;
+import org.apache.phoenix.schema.types.PhoenixArray;
 import org.apache.phoenix.util.ByteUtil;
 import org.apache.phoenix.util.ScanUtil;
 import org.apache.phoenix.util.SchemaUtil;
@@ -1945,6 +1948,152 @@ public class WhereOptimizer {
       return newKeyParts(childSlot, node, new ArrayList<KeyRange>(ranges));
     }
 
+    /**
+     * If {@link ArrayAnyComparisonExpression} is of the form:
+     *
+     * <pre>
+     * COL = ANY(ARR)
+     * </pre>
+     *
+     * then we can extract the scan ranges for the COL, given COL is a PK 
column. This syntactical
+     * pattern can be used as a replacement for a IN expression. So, instead 
of following IN
+     * expression:
+     *
+     * <pre>
+     * COL IN (VAL1, VAL2, ... VALN)
+     * </pre>
+     *
+     * we can use the following ANY expression:
+     *
+     * <pre>
+     * try (Connection conn = DriverManager.getConnection(url)) {
+     *      conn.createArrayOf("CHAR", new String[] {"VAL1", "VAL2", ... 
"VALN"});
+     *      try (PreparedStatement stmt = conn.prepareStatement(
+     *          "SELECT ... FROM TABLE WHERE COL = ANY(?)")) {
+     *          stmt.setArray(1, arr);
+     *          ResultSet rs = stmt.executeQuery();
+     *      }
+     * }
+     * </pre>
+     *
+     * This will help in saving the query parsing time as on using IN list 
query parsing time
+     * increases with the size of IN list but in case of ANY expression it is 
constant. Below we
+     * account for cases where COL is on the LHS or RHS of the comparison 
expression.
+     * @param node           {@link ArrayAnyComparisonExpression} node for 
which scan ranges are to
+     *                       be extracted
+     * @param keyExpressions {@link RowKeyColumnExpression} for the PK column 
for which scan ranges
+     *                       are to be extracted
+     * @return true if the scan ranges can be extracted, false otherwise
+     */
+    private boolean 
shouldExtractKeyRangesForArrayAnyExpr(ArrayAnyComparisonExpression node,
+      List<Expression> keyExpressions) {
+      // {@link ArrayAnyComparisonExpression} has two children, and the second 
child is
+      // comparison expression
+      Expression childExpr = node.getChildren().get(1);
+      if (!(childExpr instanceof ComparisonExpression)) {
+        return false;
+      }
+      ComparisonExpression comparisonExpr = (ComparisonExpression) childExpr;
+
+      // Replacing IN() with =ANY() is only valid if the comparison operator 
is EQUAL
+      if (comparisonExpr.getFilterOp() != CompareOperator.EQUAL) {
+        return false;
+      }
+
+      // {@link ComparisonExpression} will have two children in this case, we 
need to make
+      // sure that one of them is a {@link RowKeyColumnExpression} and the 
other is a {@link
+      // ArrayElemRefExpression}. Further, the first child of {@link 
ArrayElemRefExpression}
+      // must be a {@link LiteralExpression}. The first child of {@link
+      // ArrayElemRefExpression} is same as the first child of {@link
+      // ArrayAnyComparisonExpression}.
+      Expression lhs = comparisonExpr.getChildren().get(0);
+      Expression rhs = comparisonExpr.getChildren().get(1);
+      if (lhs instanceof RowKeyColumnExpression && rhs instanceof 
ArrayElemRefExpression) {
+        ArrayElemRefExpression arrayElemRefExpr = (ArrayElemRefExpression) rhs;
+        if (!(arrayElemRefExpr.getChildren().get(0) instanceof 
LiteralExpression)) {
+          return false;
+        }
+        // Capture {@link RowKeyColumnExpression} for the generation of key 
slots.
+        keyExpressions.add(lhs);
+
+      } else if (lhs instanceof ArrayElemRefExpression && rhs instanceof 
RowKeyColumnExpression) {
+        ArrayElemRefExpression arrayElemRefExpr = (ArrayElemRefExpression) lhs;
+        if (!(arrayElemRefExpr.getChildren().get(0) instanceof 
LiteralExpression)) {
+          return false;
+        }
+        // Capture {@link RowKeyColumnExpression} for the generation of key 
slots.
+        keyExpressions.add(rhs);
+      } else {
+        return false;
+      }
+      return true;
+    }
+
+    @Override
+    public Iterator<Expression> visitEnter(ArrayAnyComparisonExpression node) {
+      ArrayList<Expression> keyExpressions = new ArrayList<>();
+      if (shouldExtractKeyRangesForArrayAnyExpr(node, keyExpressions)) {
+        return keyExpressions.iterator();
+      }
+      // If the scan ranges cannot be extracted, we return an empty iterator
+      return Collections.emptyIterator();
+    }
+
+    @Override
+    public KeySlots visitLeave(ArrayAnyComparisonExpression node, 
List<KeySlots> childParts) {
+      if (childParts == null || childParts.isEmpty()) {
+        return null;
+      }
+      // Doing type casting is safe here as we won't have reached here unless 
the expression
+      // tree is of the form expected by the method 
shouldExtractKeyRangesForArrayAnyExpr.
+      Expression arrayExpr = node.getChildren().get(0);
+      PhoenixArray arr = (PhoenixArray) ((LiteralExpression) 
arrayExpr).getValue();
+      int numElements = arr.getDimensions();
+
+      ComparisonExpression comparisonExpr = (ComparisonExpression) 
node.getChildren().get(1);
+      Expression lhsExpr = comparisonExpr.getChildren().get(0);
+      Expression rhsExpr = comparisonExpr.getChildren().get(1);
+      ArrayElemRefExpression arrayElemRefExpr;
+      if (lhsExpr instanceof ArrayElemRefExpression) {
+        arrayElemRefExpr = (ArrayElemRefExpression) lhsExpr;
+      } else {
+        arrayElemRefExpr = (ArrayElemRefExpression) rhsExpr;
+      }
+
+      KeySlots childSlots = childParts.get(0);
+      KeySlot childSlot = childSlots.getSlots().get(0);
+      KeyPart childPart = childSlot.getKeyPart();
+      PColumn column = childPart.getColumn();
+
+      List<KeyRange> keyRanges = new ArrayList<>();
+      try {
+        Expression coerceExpr = CoerceExpression.create(arrayElemRefExpr, 
column.getDataType(),
+          column.getSortOrder(), column.getMaxLength());
+        for (int i = 1; i <= numElements; i++) {
+          arrayElemRefExpr.setIndex(i);
+          KeyRange keyRange = childPart.getKeyRange(CompareOperator.EQUAL, 
coerceExpr);
+          if (
+            keyRange == null || keyRange == KeyRange.EMPTY_RANGE
+              || keyRange == KeyRange.IS_NULL_RANGE
+          ) {
+            // Skip null range along with empty range as null check is done 
via IS NULL as
+            // per SQL standards
+            continue;
+          }
+          keyRanges.add(keyRange);
+        }
+      } catch (Exception e) {
+        LOGGER.warn(
+          "Failed to wrap ArrayElemRefExpression with CoerceExpression for 
column: {} and type: {}",
+          column.getName().getString(), column.getDataType().getSqlTypeName(), 
e);
+        return super.visitLeave(node, childParts);
+      }
+      if (keyRanges.isEmpty()) {
+        return super.visitLeave(node, childParts);
+      }
+      return newKeyParts(childSlot, node, keyRanges);
+    }
+
     @Override
     public Iterator<Expression> visitEnter(IsNullExpression node) {
       return Iterators.singletonIterator(node.getChildren().get(0));
diff --git 
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/WhereOptimizerForArrayAnyIT.java
 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/WhereOptimizerForArrayAnyIT.java
new file mode 100644
index 0000000000..dd47aefc5b
--- /dev/null
+++ 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/WhereOptimizerForArrayAnyIT.java
@@ -0,0 +1,891 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.math.BigDecimal;
+import java.sql.Array;
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.sql.Types;
+import java.util.HashMap;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.phoenix.compile.ExplainPlan;
+import org.apache.phoenix.compile.ExplainPlanAttributes;
+import org.apache.phoenix.compile.QueryPlan;
+import org.apache.phoenix.jdbc.PhoenixPreparedStatement;
+import org.apache.phoenix.jdbc.PhoenixStatement;
+import org.apache.phoenix.query.BaseTest;
+import org.apache.phoenix.util.ByteUtil;
+import org.apache.phoenix.util.DateUtil;
+import org.apache.phoenix.util.ReadOnlyProps;
+import org.apache.phoenix.util.TestUtil;
+import org.bson.RawBsonDocument;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+@Category(NeedsOwnMiniClusterTest.class)
+public class WhereOptimizerForArrayAnyIT extends BaseTest {
+  @BeforeClass
+  public static void setup() throws Exception {
+    setUpTestDriver(new ReadOnlyProps(new HashMap<String, String>()));
+  }
+
+  @Test
+  public void testArrayAnyComparisonForNonPkColumn() throws Exception {
+    String tableName = generateUniqueName();
+    createTableASCPkColumns(tableName);
+    insertData(tableName, 1, "x", "a");
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      String selectSql = "SELECT * FROM " + tableName + " WHERE col1 = ANY(?)";
+      Array arr = conn.createArrayOf("VARCHAR", new String[] { "a", "b" });
+      try (PreparedStatement stmt = conn.prepareStatement(selectSql)) {
+        stmt.setArray(1, arr);
+        try (ResultSet rs = stmt.executeQuery()) {
+          assertTrue(rs.next());
+          assertEquals(1, rs.getInt(1));
+          assertEquals("x", rs.getString(2));
+          assertEquals("a", rs.getString(3));
+        }
+        assertPointLookupsAreNotGenerated(stmt);
+      }
+    }
+  }
+
+  @Test
+  public void testArrayAnyComparisonWithInequalityOperator() throws Exception {
+    String tableName = generateUniqueName();
+    createTableASCPkColumns(tableName);
+    insertData(tableName, 2, "x", "a");
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      String selectSql = "SELECT * FROM " + tableName + " WHERE pk1 > ANY(?)";
+      Array arr = conn.createArrayOf("INTEGER", new Integer[] { 1, 2, 3 });
+      try (PreparedStatement stmt = conn.prepareStatement(selectSql)) {
+        stmt.setArray(1, arr);
+        try (ResultSet rs = stmt.executeQuery()) {
+          assertTrue(rs.next());
+          assertEquals(2, rs.getInt(1));
+          assertEquals("x", rs.getString(2));
+          assertEquals("a", rs.getString(3));
+        }
+        assertPointLookupsAreNotGenerated(stmt);
+      }
+    }
+  }
+
+  @Test
+  public void testArrayAnyComparsionWithBindVariable() throws Exception {
+    String tableName = generateUniqueName();
+    createTableASCPkColumns(tableName);
+    insertData(tableName, 1, "x", "a");
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      String selectSql = "SELECT * FROM " + tableName + " WHERE pk1 = ANY(?) 
AND pk2 = 'x'";
+      Array arr = conn.createArrayOf("INTEGER", new Integer[] { 1, 2 });
+      try (PreparedStatement stmt = conn.prepareStatement(selectSql)) {
+        stmt.setArray(1, arr);
+        try (ResultSet rs = stmt.executeQuery()) {
+          assertTrue(rs.next());
+          assertEquals(1, rs.getInt(1));
+          assertEquals("x", rs.getString(2));
+          assertEquals("a", rs.getString(3));
+        }
+        assertPointLookupsAreGenerated(stmt, 2);
+      }
+    }
+  }
+
+  @Test
+  public void testArrayAnyComparisonWithLiteralArray() throws Exception {
+    String tableName = generateUniqueName();
+    createTableASCPkColumns(tableName);
+    insertData(tableName, 1, "x", "a");
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      String selectSql =
+        "SELECT * FROM " + tableName + " WHERE pk1 = 1 AND pk2 = 
ANY(ARRAY['x', 'y'])";
+      try (Statement stmt = conn.createStatement()) {
+        try (ResultSet rs = stmt.executeQuery(selectSql)) {
+          assertTrue(rs.next());
+          assertEquals(1, rs.getInt(1));
+          assertEquals("x", rs.getString(2));
+          assertEquals("a", rs.getString(3));
+        }
+        assertPointLookupsAreGenerated(stmt, selectSql, 2);
+      }
+    }
+  }
+
+  @Test
+  public void testArrayAnyComparisonWithDoubleToFloatConversion() throws 
Exception {
+    String tableName = generateUniqueName();
+    String ddl =
+      "CREATE TABLE " + tableName + " (" + "pk1 FLOAT NOT NULL, " + "pk2 
VARCHAR(3) NOT NULL, "
+        + "col1 VARCHAR, " + "CONSTRAINT pk PRIMARY KEY (pk1 DESC, pk2)" + ")";
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      try (Statement stmt = conn.createStatement()) {
+        stmt.execute(ddl);
+        conn.commit();
+        stmt.execute("UPSERT INTO " + tableName + " VALUES (2.2, 'y', 'b')");
+        conn.commit();
+      }
+    }
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      String selectSql = "SELECT * FROM " + tableName + " WHERE pk1 = ANY(?) 
AND pk2 = 'y'";
+      Array arr = conn.createArrayOf("DOUBLE", new Double[] { 4.4d, 2.2d, 0d 
});
+      try (PreparedStatement stmt = conn.prepareStatement(selectSql)) {
+        stmt.setArray(1, arr);
+        try (ResultSet rs = stmt.executeQuery()) {
+          assertTrue(rs.next());
+          assertEquals(2, rs.getInt(1));
+          assertEquals("y", rs.getString(2));
+          assertEquals("b", rs.getString(3));
+        }
+        assertPointLookupsAreGenerated(stmt, 3);
+      }
+    }
+  }
+
+  @Test
+  public void testArrayAnyComparisonWithLongToIntegerConversion() throws 
Exception {
+    String tableName = generateUniqueName();
+    String ddl =
+      "CREATE TABLE " + tableName + " (" + "pk1 INTEGER NOT NULL, " + "pk2 
VARCHAR(3) NOT NULL, "
+        + "col1 VARCHAR, " + "CONSTRAINT pk PRIMARY KEY (pk1 DESC, pk2)" + ")";
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      try (Statement stmt = conn.createStatement()) {
+        stmt.execute(ddl);
+        conn.commit();
+      }
+    }
+    insertData(tableName, 2, "y", "b");
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      String selectSql = "SELECT * FROM " + tableName + " WHERE pk1 = ANY(?) 
AND pk2 = 'y'";
+      Array arr = conn.createArrayOf("BIGINT", new Long[] { 4L, 2L, 0L });
+      try (PreparedStatement stmt = conn.prepareStatement(selectSql)) {
+        stmt.setArray(1, arr);
+        try (ResultSet rs = stmt.executeQuery()) {
+          assertTrue(rs.next());
+          assertEquals(2, rs.getInt(1));
+          assertEquals("y", rs.getString(2));
+          assertEquals("b", rs.getString(3));
+        }
+        assertPointLookupsAreGenerated(stmt, 3);
+      }
+    }
+  }
+
+  @Test
+  public void testArrayAnyComparisonWithNullInArray() throws Exception {
+    String tableName = generateUniqueName();
+    createTableASCPkColumns(tableName);
+    insertData(tableName, 1, null, "a");
+    insertData(tableName, 2, "y", "b");
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      String selectSql = "SELECT * FROM " + tableName + " WHERE pk1 = 1 AND 
pk2 = ANY(?)";
+      Array arr = conn.createArrayOf("VARCHAR", new String[] { "y", "z", null 
});
+      try (PreparedStatement stmt = conn.prepareStatement(selectSql)) {
+        stmt.setArray(1, arr);
+        try (ResultSet rs = stmt.executeQuery()) {
+          assertFalse(rs.next());
+        }
+        // 2 point lookups are generated instead of 3 as the null is not 
considered as a value for
+        // VARCHAR type column
+        assertPointLookupsAreGenerated(stmt, 2);
+      }
+
+      selectSql = "SELECT * FROM " + tableName + " WHERE pk1 = 2 AND pk2 = 
ANY(?)";
+      try (PreparedStatement stmt = conn.prepareStatement(selectSql)) {
+        stmt.setArray(1, arr);
+        try (ResultSet rs = stmt.executeQuery()) {
+          assertTrue(rs.next());
+          assertEquals(2, rs.getInt(1));
+          assertEquals("y", rs.getString(2));
+          assertEquals("b", rs.getString(3));
+        }
+        assertPointLookupsAreGenerated(stmt, 2);
+      }
+    }
+  }
+
+  @Test
+  public void testArrayAnyComparisonWithDescPKAndNullInArray() throws 
Exception {
+    String tableName = generateUniqueName();
+    String ddl = "CREATE TABLE " + tableName + " (" + "pk1 INTEGER NOT NULL, " 
+ "pk2 VARCHAR(3), "
+      + "col1 VARCHAR, " + "CONSTRAINT pk PRIMARY KEY (pk1, pk2 DESC)" + ")";
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      try (Statement stmt = conn.createStatement()) {
+        stmt.execute(ddl);
+        conn.commit();
+      }
+    }
+    insertData(tableName, 1, null, "a");
+    insertData(tableName, 2, "y", "b");
+    insertData(tableName, 3, "z", null);
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      String selectSql = "SELECT * FROM " + tableName + " WHERE pk1 = 1 AND 
pk2 = ANY(?)";
+      Array arr = conn.createArrayOf("VARCHAR", new String[] { "y", "z", null 
});
+      try (PreparedStatement stmt = conn.prepareStatement(selectSql)) {
+        stmt.setArray(1, arr);
+        try (ResultSet rs = stmt.executeQuery()) {
+          assertFalse(rs.next());
+        }
+        // 2 point lookups are generated instead of 3 as the null is not 
considered as a value for
+        // VARCHAR type column
+        assertPointLookupsAreGenerated(stmt, 2);
+      }
+
+      selectSql = "SELECT * FROM " + tableName + " WHERE pk1 = 2 AND pk2 = 
ANY(?)";
+      try (PreparedStatement stmt = conn.prepareStatement(selectSql)) {
+        stmt.setArray(1, arr);
+        try (ResultSet rs = stmt.executeQuery()) {
+          assertTrue(rs.next());
+          assertEquals(2, rs.getInt(1));
+          assertEquals("y", rs.getString(2));
+          assertEquals("b", rs.getString(3));
+        }
+        assertPointLookupsAreGenerated(stmt, 2);
+      }
+    }
+  }
+
+  @Test
+  public void testArrayAnyComparisonForDescCharPKWithPadding() throws 
Exception {
+    String tableName = generateUniqueName();
+    String ddl = "CREATE TABLE " + tableName + " (" + "pk1 CHAR(3) NOT NULL, " 
+ "pk2 VARCHAR(3), "
+      + "col1 VARCHAR, " + "CONSTRAINT pk PRIMARY KEY (pk1 DESC, pk2)" + ")";
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      try (Statement stmt = conn.createStatement()) {
+        stmt.execute(ddl);
+        conn.commit();
+      }
+    }
+    String upsertStmt = "UPSERT INTO " + tableName + " VALUES (?, ?, ?)";
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      try (PreparedStatement stmt = conn.prepareStatement(upsertStmt)) {
+        stmt.setString(1, "a");
+        stmt.setString(2, "b");
+        stmt.setString(3, "c");
+        stmt.executeUpdate();
+        conn.commit();
+      }
+    }
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      String selectSql = "SELECT * FROM " + tableName + " WHERE pk1 = ANY(?) 
AND pk2 = 'b'";
+      Array arr = conn.createArrayOf("VARCHAR", new String[] { "a", "c", null 
});
+      try (PreparedStatement stmt = conn.prepareStatement(selectSql)) {
+        stmt.setArray(1, arr);
+        try (ResultSet rs = stmt.executeQuery()) {
+          assertTrue(rs.next());
+          assertEquals("a", rs.getString(1));
+          assertEquals("b", rs.getString(2));
+          assertEquals("c", rs.getString(3));
+        }
+        // 3 point lookups are generated though one of the array values is 
null as CHAR type pads it
+        // and the value is a string consisting only of pad characters
+        assertPointLookupsAreGenerated(stmt, 3);
+      }
+    }
+  }
+
+  @Test
+  public void testArrayAnyComparisonWithDecimalArray() throws Exception {
+    String tableName = generateUniqueName();
+    String ddl = "CREATE TABLE " + tableName + " (" + "pk1 DECIMAL(10, 2) NOT 
NULL, "
+      + "pk2 VARCHAR(3), " + "col1 VARCHAR, " + "CONSTRAINT pk PRIMARY KEY 
(pk1, pk2)" + ")";
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      try (Statement stmt = conn.createStatement()) {
+        stmt.execute(ddl);
+        conn.commit();
+      }
+    }
+    String upsertStmt = "UPSERT INTO " + tableName + " VALUES (?, ?, ?)";
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      try (PreparedStatement stmt = conn.prepareStatement(upsertStmt)) {
+        stmt.setBigDecimal(1, new BigDecimal("1.23"));
+        stmt.setString(2, "x");
+        stmt.setString(3, "a");
+        stmt.executeUpdate();
+        conn.commit();
+      }
+    }
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      String selectSql = "SELECT * FROM " + tableName + " WHERE pk1 = ANY(?) 
AND pk2 = 'x'";
+      Array arr = conn.createArrayOf("DECIMAL", new BigDecimal[] { new 
BigDecimal("1.230"),
+        new BigDecimal("2.340"), new BigDecimal("3.450") });
+      try (PreparedStatement stmt = conn.prepareStatement(selectSql)) {
+        stmt.setArray(1, arr);
+        try (ResultSet rs = stmt.executeQuery()) {
+          assertTrue(rs.next());
+          assertEquals(new BigDecimal("1.23"), rs.getBigDecimal(1));
+          assertEquals("x", rs.getString(2));
+          assertEquals("a", rs.getString(3));
+        }
+        assertPointLookupsAreGenerated(stmt, 3);
+      }
+    }
+  }
+
+  @Test
+  public void 
testArrayAnyComparisonWithDataTypeAndSortOrderCoercionForDecimalColumn()
+    throws Exception {
+    String tableName = generateUniqueName();
+    String ddl = "CREATE TABLE " + tableName + " (" + "pk1 DECIMAL(10, 2) NOT 
NULL, "
+      + "pk2 VARCHAR(3), " + "col1 VARCHAR, " + "CONSTRAINT pk PRIMARY KEY 
(pk1 DESC, pk2)" + ")";
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      try (Statement stmt = conn.createStatement()) {
+        stmt.execute(ddl);
+        conn.commit();
+      }
+    }
+    String upsertStmt = "UPSERT INTO " + tableName + " VALUES (?, ?, ?)";
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      try (PreparedStatement stmt = conn.prepareStatement(upsertStmt)) {
+        stmt.setBigDecimal(1, new BigDecimal("1.23"));
+        stmt.setString(2, "x");
+        stmt.setString(3, "a");
+        stmt.executeUpdate();
+        conn.commit();
+      }
+    }
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      String selectSql = "SELECT * FROM " + tableName + " WHERE pk1 = ANY(?) 
AND pk2 = 'x'";
+      Array arr = conn.createArrayOf("DOUBLE", new Double[] { 1.230d, 2.340d, 
3.450d });
+      try (PreparedStatement stmt = conn.prepareStatement(selectSql)) {
+        stmt.setArray(1, arr);
+        try (ResultSet rs = stmt.executeQuery()) {
+          assertTrue(rs.next());
+          assertEquals(new BigDecimal("1.23"), rs.getBigDecimal(1));
+          assertEquals("x", rs.getString(2));
+          assertEquals("a", rs.getString(3));
+        }
+        assertPointLookupsAreGenerated(stmt, 3);
+      }
+    }
+  }
+
+  @Test
+  public void testArrayAnyComparisonForDateTimeColumn() throws Exception {
+    String tableName = generateUniqueName();
+    String ddl = "CREATE TABLE " + tableName + " (" + "pk1 TIMESTAMP NOT NULL, 
"
+      + "pk2 TIME NOT NULL, " + "pk3 DATE NOT NULL, " + "col1 VARCHAR, "
+      + "CONSTRAINT pk PRIMARY KEY (pk1, pk2, pk3)" + ")";
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      try (Statement stmt = conn.createStatement()) {
+        stmt.execute(ddl);
+        conn.commit();
+      }
+    }
+    String pk1Value = "2025-07-18 10:00:00";
+    String pk2Value = "2025-07-18 11:00:00";
+    String pk3Value = "2025-07-18 12:00:00";
+    String upsertStmt = "UPSERT INTO " + tableName + " VALUES (?, ?, ?, ?)";
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      try (PreparedStatement stmt = conn.prepareStatement(upsertStmt)) {
+        stmt.setTimestamp(1, DateUtil.parseTimestamp(pk1Value));
+        stmt.setTime(2, DateUtil.parseTime(pk2Value));
+        stmt.setDate(3, DateUtil.parseDate(pk3Value));
+        stmt.setString(4, "a");
+        stmt.executeUpdate();
+        conn.commit();
+      }
+    }
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+
+      // Use arrays as bind variables to test the point lookup optimization.
+      String selectSql = "SELECT * FROM " + tableName + " WHERE pk1 = ANY(?) " 
+ "AND pk2 = ANY(?) "
+        + "AND pk3 = ANY(?)";
+      Array timestampArr = conn.createArrayOf("TIMESTAMP",
+        new Timestamp[] { DateUtil.parseTimestamp(pk1Value),
+          DateUtil.parseTimestamp("2025-07-19 10:00:00"),
+          DateUtil.parseTimestamp("2025-07-17 10:00:00"), });
+      Array timeArr = conn.createArrayOf("TIME", new Time[] { 
DateUtil.parseTime(pk2Value),
+        DateUtil.parseTime("2025-07-19 11:00:00"), 
DateUtil.parseTime("2025-07-17 11:00:00"), });
+      Array dateArr = conn.createArrayOf("DATE", new Date[] { 
DateUtil.parseDate(pk3Value),
+        DateUtil.parseDate("2025-07-19 12:00:00"), 
DateUtil.parseDate("2025-07-17 12:00:00"), });
+      try (PreparedStatement stmt = conn.prepareStatement(selectSql)) {
+        stmt.setArray(1, timestampArr);
+        stmt.setArray(2, timeArr);
+        stmt.setArray(3, dateArr);
+        try (ResultSet rs = stmt.executeQuery()) {
+          assertTrue(rs.next());
+          assertEquals(DateUtil.parseTimestamp(pk1Value), rs.getTimestamp(1));
+          assertEquals(DateUtil.parseTime(pk2Value), rs.getTime(2));
+          assertEquals(DateUtil.parseDate(pk3Value), rs.getDate(3));
+        }
+        assertPointLookupsAreGenerated(stmt, 3 * 3 * 3);
+      }
+
+      // Use literal arrays to test the point lookup optimization.
+      String timestampLiteralArr = "ARRAY[" + "TO_TIMESTAMP('" + pk1Value + 
"'), "
+        + "TO_TIMESTAMP('" + pk2Value + "'), " + "TO_TIMESTAMP('" + pk3Value + 
"')]";
+      String timeLiteralArr = "ARRAY[" + "TO_TIME('" + pk1Value + "'), " + 
"TO_TIME('" + pk2Value
+        + "'), " + "TO_TIME('" + pk3Value + "')]";
+      String dateLiteralArr = "ARRAY[" + "TO_DATE('" + pk1Value + "'), " + 
"TO_DATE('" + pk2Value
+        + "'), " + "TO_DATE('" + pk3Value + "')]";
+      selectSql = "SELECT * FROM " + tableName + " WHERE pk1 = ANY(" + 
timestampLiteralArr + ") "
+        + "AND pk2 = ANY(" + timeLiteralArr + ")" + "AND pk3 = ANY(" + 
dateLiteralArr + ")";
+      try (Statement stmt = conn.createStatement()) {
+        try (ResultSet rs = stmt.executeQuery(selectSql)) {
+          assertTrue(rs.next());
+          assertEquals(DateUtil.parseTimestamp(pk1Value), rs.getTimestamp(1));
+          assertEquals(DateUtil.parseTime(pk2Value), rs.getTime(2));
+          assertEquals(DateUtil.parseDate(pk3Value), rs.getDate(3));
+        }
+        assertPointLookupsAreGenerated(stmt, selectSql, 3 * 3 * 3);
+      }
+    }
+  }
+
+  @Test
+  public void testArrayAnyComparisonWithTimestampToDateCoercion() throws 
Exception {
+    String tableName = generateUniqueName();
+    String ddl =
+      "CREATE TABLE " + tableName + " (" + "pk1 TIMESTAMP NOT NULL, " + "pk2 
DATE NOT NULL, "
+        + "col1 VARCHAR, " + "CONSTRAINT pk PRIMARY KEY (pk1, pk2 DESC)" + ")";
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      try (Statement stmt = conn.createStatement()) {
+        stmt.execute(ddl);
+        conn.commit();
+      }
+    }
+    String pk1Value = "2025-07-18 10:00:00";
+    String pk2Value = "2025-07-18 11:00:00";
+    String pk3Value = "2025-07-18 12:00:00";
+    String upsertStmt = "UPSERT INTO " + tableName + " VALUES (?, ?, ?)";
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      try (PreparedStatement stmt = conn.prepareStatement(upsertStmt)) {
+        stmt.setTimestamp(1, DateUtil.parseTimestamp(pk1Value));
+        stmt.setDate(2, DateUtil.parseDate(pk2Value));
+        stmt.setString(3, "a");
+        stmt.executeUpdate();
+        conn.commit();
+      }
+    }
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      // Use literal arrays to test the point lookup optimization.
+      String timestampLiteralArr = "ARRAY[" + "TO_TIMESTAMP('" + pk1Value + 
"'), "
+        + "TO_TIMESTAMP('" + pk2Value + "'), " + "TO_TIMESTAMP('" + pk3Value + 
"')]";
+      String selectSql = "SELECT * FROM " + tableName + " WHERE pk1 = ANY(" + 
timestampLiteralArr
+        + ") " + "AND pk2 = ANY(" + timestampLiteralArr + ")";
+      try (Statement stmt = conn.createStatement()) {
+        try (ResultSet rs = stmt.executeQuery(selectSql)) {
+          assertTrue(rs.next());
+          assertEquals(DateUtil.parseTimestamp(pk1Value), rs.getTimestamp(1));
+          assertEquals(DateUtil.parseDate(pk2Value), rs.getDate(2));
+          assertEquals("a", rs.getString(3));
+        }
+        assertPointLookupsAreGenerated(stmt, selectSql, 3 * 3);
+      }
+    }
+  }
+
+  @Test
+  public void testArrayAnyComparisonForBinaryColumn() throws Exception {
+    String tableName = generateUniqueName();
+    String ddl = "CREATE TABLE " + tableName + " (" + "pk1 BINARY(3) NOT NULL, 
"
+      + "pk2 VARBINARY_ENCODED(3) NOT NULL, " + "pk3 VARBINARY(3) NOT NULL, " 
+ "col1 VARCHAR, "
+      + "CONSTRAINT pk PRIMARY KEY (pk1, pk2, pk3)" + ")";
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      try (Statement stmt = conn.createStatement()) {
+        stmt.execute(ddl);
+        conn.commit();
+      }
+    }
+    String pk1Value = "a";
+    String pk2Value = "b";
+    String pk3Value = "c";
+    String col1Value = "d";
+    String upsertStmt = "UPSERT INTO " + tableName + " VALUES (?, ?, ?, ?)";
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      try (PreparedStatement stmt = conn.prepareStatement(upsertStmt)) {
+        stmt.setBytes(1, pk1Value.getBytes());
+        stmt.setBytes(2, pk2Value.getBytes());
+        stmt.setBytes(3, pk3Value.getBytes());
+        stmt.setString(4, col1Value);
+        stmt.executeUpdate();
+        conn.commit();
+      }
+    }
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      TestUtil.dumpTable(conn, TableName.valueOf(tableName));
+      String selectSql =
+        "SELECT * FROM " + tableName + " WHERE pk1 = ANY(?) AND pk2 = ANY(?) 
AND pk3 = ANY(?)";
+      byte[][] nativeByteArr =
+        new byte[][] { pk1Value.getBytes(), pk2Value.getBytes(), 
pk3Value.getBytes() };
+      Array binaryArr = conn.createArrayOf("BINARY", nativeByteArr);
+      Array varbinaryArr = conn.createArrayOf("VARBINARY", nativeByteArr);
+      try (PreparedStatement stmt = conn.prepareStatement(selectSql)) {
+        stmt.setArray(1, binaryArr);
+        stmt.setArray(2, varbinaryArr);
+        stmt.setArray(3, varbinaryArr);
+        try (ResultSet rs = stmt.executeQuery()) {
+          assertTrue(rs.next());
+          assertBinaryValue(pk1Value.getBytes(), rs.getBytes(1));
+          assertBinaryValue(pk2Value.getBytes(), rs.getBytes(2));
+          assertBinaryValue(pk3Value.getBytes(), rs.getBytes(3));
+          assertEquals(col1Value, rs.getString(4));
+        }
+        assertPointLookupsAreGenerated(stmt, 3 * 3 * 3);
+      }
+    }
+  }
+
+  @Test
+  public void testArrayAnyComparisonForBinaryColumnWithCoercion() throws 
Exception {
+    String tableName = generateUniqueName();
+    String ddl = "CREATE TABLE " + tableName + " (" + "pk1 BINARY(3) NOT NULL, 
"
+      + "pk2 VARBINARY_ENCODED(3) NOT NULL, " + "pk3 VARBINARY(3) NOT NULL, " 
+ "col1 VARCHAR, "
+      + "CONSTRAINT pk PRIMARY KEY (pk1 DESC, pk2 DESC, pk3)" + ")";
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      try (Statement stmt = conn.createStatement()) {
+        stmt.execute(ddl);
+        conn.commit();
+      }
+    }
+    String pk1Value = "a";
+    String pk2Value = "b";
+    String pk3Value = "c";
+    String col1Value = "d";
+    String upsertStmt = "UPSERT INTO " + tableName + " VALUES (?, ?, ?, ?)";
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      try (PreparedStatement stmt = conn.prepareStatement(upsertStmt)) {
+        stmt.setBytes(1, pk1Value.getBytes());
+        stmt.setBytes(2, pk2Value.getBytes());
+        stmt.setBytes(3, pk3Value.getBytes());
+        stmt.setString(4, col1Value);
+        stmt.executeUpdate();
+        conn.commit();
+      }
+    }
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      TestUtil.dumpTable(conn, TableName.valueOf(tableName));
+      String selectSql =
+        "SELECT * FROM " + tableName + " WHERE pk1 = ANY(?) AND pk2 = ANY(?) 
AND pk3 = ANY(?)";
+      byte[][] nativeByteArr =
+        new byte[][] { pk1Value.getBytes(), pk2Value.getBytes(), 
pk3Value.getBytes() };
+      Array binaryArr = conn.createArrayOf("BINARY", nativeByteArr);
+      try (PreparedStatement stmt = conn.prepareStatement(selectSql)) {
+        stmt.setArray(1, binaryArr);
+        stmt.setArray(2, binaryArr);
+        stmt.setArray(3, binaryArr);
+        try (ResultSet rs = stmt.executeQuery()) {
+          assertTrue(rs.next());
+          assertBinaryValue(pk1Value.getBytes(), rs.getBytes(1));
+          assertBinaryValue(pk2Value.getBytes(), rs.getBytes(2));
+          assertBinaryValue(pk3Value.getBytes(), rs.getBytes(3));
+          assertEquals(col1Value, rs.getString(4));
+        }
+        assertPointLookupsAreGenerated(stmt, 3 * 3 * 3);
+      }
+    }
+  }
+
+  @Test
+  public void testArrayAnyComparisonForBsonColumn() throws Exception {
+    String tableName = generateUniqueName();
+    String ddl = "CREATE TABLE " + tableName + " (" + "pk1 BSON NOT NULL, " + 
"col1 VARCHAR, "
+      + "CONSTRAINT pk PRIMARY KEY (pk1 DESC)" + ")";
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      try (Statement stmt = conn.createStatement()) {
+        stmt.execute(ddl);
+        conn.commit();
+      }
+    }
+    RawBsonDocument pk1Value = getBsonDocument1();
+    String upsertStmt = "UPSERT INTO " + tableName + " VALUES (?, ?)";
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      try (PreparedStatement stmt = conn.prepareStatement(upsertStmt)) {
+        stmt.setObject(1, pk1Value);
+        stmt.setString(2, "a");
+        stmt.executeUpdate();
+        conn.commit();
+      }
+    }
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      TestUtil.dumpTable(conn, TableName.valueOf(tableName));
+      String selectSql = "SELECT * FROM " + tableName + " WHERE pk1 = ANY(?)";
+      Array bsonArr = conn.createArrayOf("VARBINARY",
+        new byte[][] { ByteUtil.toBytes(pk1Value.getByteBuffer().asNIO()),
+          ByteUtil.toBytes(getBsonDocument2().getByteBuffer().asNIO()), });
+      try (PreparedStatement stmt = conn.prepareStatement(selectSql)) {
+        stmt.setArray(1, bsonArr);
+        try (ResultSet rs = stmt.executeQuery()) {
+          assertTrue(rs.next());
+          // ResultSet#getBytes() takes care of inverting the bytes as sort 
order of
+          // column is DESC. Is this a gap in PBson#toObject()?
+          byte[] pk1ValueBytes = rs.getBytes(1);
+          RawBsonDocument actualPk1Value =
+            new RawBsonDocument(pk1ValueBytes, 0, pk1ValueBytes.length);
+          assertEquals(pk1Value, actualPk1Value);
+          assertEquals("a", rs.getString(2));
+        }
+        assertPointLookupsAreGenerated(stmt, 2);
+      }
+    }
+  }
+
+  @Test
+  public void testArrayAnyComparisonInGroupedAggregateQuery() throws Exception 
{
+    String tableName = generateUniqueName();
+    String ddl = "CREATE TABLE " + tableName + " (" + "pk1 INTEGER NOT NULL, "
+      + "pk2 VARCHAR NOT NULL, " + "col1 VARCHAR, " + "CONSTRAINT pk PRIMARY 
KEY (pk1, pk2)" + ")";
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      try (Statement stmt = conn.createStatement()) {
+        stmt.execute(ddl);
+        conn.commit();
+      }
+    }
+    insertData(tableName, 1, "a11", "b11");
+    insertData(tableName, 1, "a12", "b12");
+    insertData(tableName, 2, "a21", "b21");
+    insertData(tableName, 2, "a22", "b22");
+    insertData(tableName, 2, "a23", "b23");
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      String selectSql =
+        "SELECT pk1, COUNT(*) FROM " + tableName + " WHERE pk1 = 2 AND pk2 = 
ANY(?) GROUP BY pk1";
+      Array arr = conn.createArrayOf("VARCHAR", new String[] { "a11", "a21", 
"a23" });
+      try (PreparedStatement stmt = conn.prepareStatement(selectSql)) {
+        stmt.setArray(1, arr);
+        try (ResultSet rs = stmt.executeQuery()) {
+          assertTrue(rs.next());
+          assertEquals(2, rs.getInt(1));
+          assertEquals(2, rs.getInt(2));
+        }
+        assertPointLookupsAreGenerated(stmt, 3);
+      }
+    }
+  }
+
+  @Test
+  public void testArrayAnyComparisonWithIndexPKColumn() throws Exception {
+    String tableName = generateUniqueName();
+    String createTableDdl = "CREATE TABLE " + tableName + " (" + "pk1 INTEGER 
NOT NULL, "
+      + "pk2 VARCHAR(3), " + "col1 VARCHAR, " + "CONSTRAINT pk PRIMARY KEY 
(pk1, pk2)" + ")";
+    String createIndexDdl = "CREATE INDEX idx_pk1 ON " + tableName + " (col1)";
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      try (Statement stmt = conn.createStatement()) {
+        stmt.execute(createTableDdl);
+        conn.commit();
+        stmt.execute(createIndexDdl);
+        conn.commit();
+      }
+    }
+    insertData(tableName, 1, "a", "b");
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      String selectSql = "SELECT * FROM " + tableName + " WHERE col1 = ANY(?)";
+      Array arr = conn.createArrayOf("VARCHAR", new String[] { "a", "b", "c" 
});
+      try (PreparedStatement stmt = conn.prepareStatement(selectSql)) {
+        stmt.setArray(1, arr);
+        try (ResultSet rs = stmt.executeQuery()) {
+          assertTrue(rs.next());
+          assertEquals(1, rs.getInt(1));
+          assertEquals("a", rs.getString(2));
+          assertEquals("b", rs.getString(3));
+        }
+        assertSkipScanIsGenerated(stmt, 3);
+      }
+    }
+  }
+
+  @Test
+  public void testArrayAnyComparisonWithRowKeyPrefix() throws Exception {
+    String tableName = generateUniqueName();
+    String ddl = "CREATE TABLE " + tableName + " (" + "pk1 INTEGER NOT NULL, " 
+ "pk2 VARCHAR(3), "
+      + "col1 VARCHAR, " + "CONSTRAINT pk PRIMARY KEY (pk1, pk2)" + ")";
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      try (Statement stmt = conn.createStatement()) {
+        stmt.execute(ddl);
+        conn.commit();
+      }
+    }
+    insertData(tableName, 1, "a", "b");
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      String selectSql = "SELECT * FROM " + tableName + " WHERE pk1 = ANY(?)";
+      Array arr = conn.createArrayOf("INTEGER", new Integer[] { 1, 2, 3 });
+      try (PreparedStatement stmt = conn.prepareStatement(selectSql)) {
+        stmt.setArray(1, arr);
+        try (ResultSet rs = stmt.executeQuery()) {
+          assertTrue(rs.next());
+          assertEquals(1, rs.getInt(1));
+          assertEquals("a", rs.getString(2));
+          assertEquals("b", rs.getString(3));
+        }
+        assertSkipScanIsGenerated(stmt, 3);
+      }
+    }
+  }
+
+  @Test
+  public void 
testArrayAnyComparisonWhenRowKeyColumnExpressionIsNotTopLevelExpression()
+    throws Exception {
+    String tableName = generateUniqueName();
+    String ddl = "CREATE TABLE " + tableName + " (" + "pk1 INTEGER NOT NULL, " 
+ "pk2 VARCHAR(3), "
+      + "col1 VARCHAR, " + "CONSTRAINT pk PRIMARY KEY (pk1, pk2)" + ")";
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      try (Statement stmt = conn.createStatement()) {
+        stmt.execute(ddl);
+        conn.commit();
+      }
+    }
+    insertData(tableName, 1, "a", "b");
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      String selectSql =
+        "SELECT * FROM " + tableName + " WHERE CAST (pk1 as BIGINT) = ANY(?) 
AND pk2 = 'a'";
+      Array arr = conn.createArrayOf("BIGINT", new Long[] { 1L, 2L, 3L });
+      try (PreparedStatement stmt = conn.prepareStatement(selectSql)) {
+        stmt.setArray(1, arr);
+        try (ResultSet rs = stmt.executeQuery()) {
+          assertTrue(rs.next());
+          assertEquals(1, rs.getInt(1));
+          assertEquals("a", rs.getString(2));
+          assertEquals("b", rs.getString(3));
+        }
+        assertPointLookupsAreNotGenerated(stmt);
+      }
+    }
+  }
+
+  private void assertBinaryValue(byte[] expected, byte[] actual) {
+    int expectedLength = expected.length;
+    for (int i = 0; i < expectedLength; i++) {
+      assertEquals(expected[i], actual[i]);
+    }
+  }
+
+  private void assertPointLookupsAreNotGenerated(PreparedStatement stmt) 
throws SQLException {
+    ExplainPlan explain =
+      
stmt.unwrap(PhoenixPreparedStatement.class).optimizeQuery().getExplainPlan();
+    ExplainPlanAttributes planAttributes = explain.getPlanStepsAsAttributes();
+    assertEquals("FULL SCAN ", planAttributes.getExplainScanType());
+  }
+
+  private void assertPointLookupsAreGenerated(PreparedStatement stmt, int 
noOfPointLookups)
+    throws SQLException {
+    QueryPlan queryPlan = 
stmt.unwrap(PhoenixPreparedStatement.class).optimizeQuery();
+    assertPointLookupsAreGenerated(queryPlan, noOfPointLookups);
+  }
+
+  private void assertPointLookupsAreGenerated(Statement stmt, String selectSql,
+    int noOfPointLookups) throws SQLException {
+    QueryPlan queryPlan = 
stmt.unwrap(PhoenixStatement.class).optimizeQuery(selectSql);
+    assertPointLookupsAreGenerated(queryPlan, noOfPointLookups);
+  }
+
+  private void assertSkipScanIsGenerated(PreparedStatement stmt, int 
skipListSize)
+    throws SQLException {
+    QueryPlan queryPlan = 
stmt.unwrap(PhoenixPreparedStatement.class).optimizeQuery();
+    ExplainPlan explain = queryPlan.getExplainPlan();
+    ExplainPlanAttributes planAttributes = explain.getPlanStepsAsAttributes();
+    String expectedScanType =
+      "SKIP SCAN ON " + skipListSize + " KEY" + (skipListSize > 1 ? "S " : " 
");
+    assertEquals(expectedScanType, planAttributes.getExplainScanType());
+  }
+
+  private void assertPointLookupsAreGenerated(QueryPlan queryPlan, int 
noOfPointLookups)
+    throws SQLException {
+    ExplainPlan explain = queryPlan.getExplainPlan();
+    ExplainPlanAttributes planAttributes = explain.getPlanStepsAsAttributes();
+    String expectedScanType =
+      "POINT LOOKUP ON " + noOfPointLookups + " KEY" + (noOfPointLookups > 1 ? 
"S " : " ");
+    assertEquals(expectedScanType, planAttributes.getExplainScanType());
+  }
+
+  private void createTableASCPkColumns(String tableName) throws SQLException {
+    String ddl = "CREATE TABLE " + tableName + " (" + "pk1 INTEGER NOT NULL, " 
+ "pk2 VARCHAR(3), "
+      + "col1 VARCHAR, " + "CONSTRAINT pk PRIMARY KEY (pk1, pk2)" + ")";
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      try (Statement stmt = conn.createStatement()) {
+        stmt.execute(ddl);
+        conn.commit();
+      }
+    }
+  }
+
+  private void insertData(String tableName, int pk1, String pk2, String col1) 
throws SQLException {
+    String ddl = "UPSERT INTO " + tableName + " VALUES (?, ?, ?)";
+    try (Connection conn = DriverManager.getConnection(getUrl())) {
+      try (PreparedStatement stmt = conn.prepareStatement(ddl)) {
+        stmt.setInt(1, pk1);
+        if (pk2 != null) {
+          stmt.setString(2, pk2);
+        } else {
+          stmt.setNull(2, Types.VARCHAR);
+        }
+        if (col1 != null) {
+          stmt.setString(3, col1);
+        } else {
+          stmt.setNull(3, Types.VARCHAR);
+        }
+        stmt.executeUpdate();
+        conn.commit();
+      }
+    }
+  }
+
+  private static RawBsonDocument getBsonDocument1() {
+    String json = "{\n" + "  \"attr_9\" : {\n" + "    \"$set\" : [ {\n" + "    
  \"$binary\" : {\n"
+      + "        \"base64\" : \"YWJjZA==\",\n" + "        \"subType\" : 
\"00\"\n" + "      }\n"
+      + "    }, {\n" + "      \"$binary\" : {\n" + "        \"base64\" : 
\"c3RyaW5nXzAyMDM=\",\n"
+      + "        \"subType\" : \"00\"\n" + "      }\n" + "    } ]\n" + "  },\n"
+      + "  \"attr_8\" : {\n" + "    \"$set\" : [ 3802.34, -40.667, -4839, 7593 
]\n" + "  },\n"
+      + "  \"attr_7\" : {\n" + "    \"$set\" : [ \"str_set002\", 
\"strset003\", \"strset001\" ]\n"
+      + "  },\n" + "  \"attr_6\" : {\n" + "    \"n_attr_0\" : \"str_val_0\",\n"
+      + "    \"n_attr_1\" : 1295.03,\n" + "    \"n_attr_2\" : {\n" + "      
\"$binary\" : {\n"
+      + "        \"base64\" : \"MjA0OHU1bmJsd2plaVdGR1RIKDRiZjkzMA==\",\n"
+      + "        \"subType\" : \"00\"\n" + "      }\n" + "    },\n" + "    
\"n_attr_3\" : true,\n"
+      + "    \"n_attr_4\" : null\n" + "  },\n" + "  \"attr_5\" : [ 1234, 
\"str001\", {\n"
+      + "    \"$binary\" : {\n" + "      \"base64\" : \"AAECAwQF\",\n"
+      + "      \"subType\" : \"00\"\n" + "    }\n" + "  } ],\n" + "  
\"attr_4\" : null,\n"
+      + "  \"attr_3\" : true,\n" + "  \"attr_2\" : {\n" + "    \"$binary\" : 
{\n"
+      + "      \"base64\" : 
\"cmFuZG9tZTkzaDVvbmVmaHUxbmtyXzE5MzBga2p2LSwhJCVeaWVpZmhiajAzNA==\",\n"
+      + "      \"subType\" : \"00\"\n" + "    }\n" + "  },\n" + "  \"attr_1\" 
: 1295.03,\n"
+      + "  \"attr_0\" : \"str_val_0\"\n" + "}";
+    return RawBsonDocument.parse(json);
+  }
+
+  private static RawBsonDocument getBsonDocument2() {
+    String json = "{\n" + "  \"InPublication\" : false,\n" + "  \"ISBN\" : 
\"111-1111111111\",\n"
+      + "  \"NestedList1\" : [ -485.34, \"1234abcd\", [ \"xyz0123\", {\n"
+      + "    \"InPublication\" : false,\n" + "    \"ISBN\" : 
\"111-1111111111\",\n"
+      + "    \"Title\" : \"Book 101 Title\",\n" + "    \"Id\" : 101.01\n" + "  
} ] ],\n"
+      + "  \"NestedMap1\" : {\n" + "    \"InPublication\" : false,\n"
+      + "    \"ISBN\" : \"111-1111111111\",\n" + "    \"Title\" : \"Book 101 
Title\",\n"
+      + "    \"Id\" : 101.01,\n" + "    \"NList1\" : [ \"NListVal01\", -23.4 
]\n" + "  },\n"
+      + "  \"Id2\" : 101.01,\n" + "  \"attr_6\" : {\n" + "    \"n_attr_0\" : 
\"str_val_0\",\n"
+      + "    \"n_attr_1\" : 1295.03,\n" + "    \"n_attr_2\" : {\n" + "      
\"$binary\" : {\n"
+      + "        \"base64\" : \"MjA0OHU1bmJsd2plaVdGR1RIKDRiZjkzMA==\",\n"
+      + "        \"subType\" : \"00\"\n" + "      }\n" + "    },\n" + "    
\"n_attr_3\" : true,\n"
+      + "    \"n_attr_4\" : null\n" + "  },\n" + "  \"attr_5\" : [ 1234, 
\"str001\", {\n"
+      + "    \"$binary\" : {\n" + "      \"base64\" : \"AAECAwQF\",\n"
+      + "      \"subType\" : \"00\"\n" + "    }\n" + "  } ],\n" + "  \"IdS\" : 
\"101.01\",\n"
+      + "  \"Title\" : \"Book 101 Title\",\n" + "  \"Id\" : 101.01,\n" + "  
\"attr_1\" : 1295.03,\n"
+      + "  \"attr_0\" : \"str_val_0\"\n" + "}";
+    return RawBsonDocument.parse(json);
+  }
+
+}

Reply via email to