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

apurtell pushed a commit to branch PHOENIX-7562-feature
in repository https://gitbox.apache.org/repos/asf/phoenix.git

commit 098c7928d4770dbab491b5984732a555fb52fa8f
Author: Viraj Jasani <vjas...@apache.org>
AuthorDate: Wed May 7 18:14:12 2025 -0700

    PHOENIX-7517 Allow BSON as primary key column (#2141)
---
 .../org/apache/phoenix/schema/types/PBson.java     |  10 --
 .../org/apache/phoenix/schema/types/PVarchar.java  |   2 +-
 .../java/org/apache/phoenix/end2end/Bson4IT.java   | 148 ++++++++++++++++++++-
 .../apache/phoenix/schema/types/PDataTypeTest.java |   2 +-
 .../org/apache/phoenix/util/IndexUtilTest.java     |   2 +
 5 files changed, 145 insertions(+), 19 deletions(-)

diff --git 
a/phoenix-core-client/src/main/java/org/apache/phoenix/schema/types/PBson.java 
b/phoenix-core-client/src/main/java/org/apache/phoenix/schema/types/PBson.java
index c1c3274bb2..1beacefa03 100644
--- 
a/phoenix-core-client/src/main/java/org/apache/phoenix/schema/types/PBson.java
+++ 
b/phoenix-core-client/src/main/java/org/apache/phoenix/schema/types/PBson.java
@@ -42,16 +42,6 @@ public class PBson extends PVarbinary {
         super("BSON", PDataType.BSON_TYPE, byte[].class, null, 49);
     }
 
-    @Override
-    public boolean canBePrimaryKey() {
-        return false;
-    }
-
-    @Override
-    public boolean isComparisonSupported() {
-        return false;
-    }
-
     @Override
     public int toBytes(Object object, byte[] bytes, int offset) {
         if (object == null) {
diff --git 
a/phoenix-core-client/src/main/java/org/apache/phoenix/schema/types/PVarchar.java
 
b/phoenix-core-client/src/main/java/org/apache/phoenix/schema/types/PVarchar.java
index 911edf3f4e..451f892ea0 100644
--- 
a/phoenix-core-client/src/main/java/org/apache/phoenix/schema/types/PVarchar.java
+++ 
b/phoenix-core-client/src/main/java/org/apache/phoenix/schema/types/PVarchar.java
@@ -101,7 +101,7 @@ public class PVarchar extends PDataType<String> {
     @Override
     public boolean isCoercibleTo(PDataType targetType) {
         return equalsAny(targetType, this, PChar.INSTANCE, 
PVarbinary.INSTANCE, PBinary.INSTANCE,
-            PVarbinaryEncoded.INSTANCE);
+            PVarbinaryEncoded.INSTANCE, PBson.INSTANCE);
     }
 
     @Override
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/Bson4IT.java 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/Bson4IT.java
index 2296fad4ae..26b2172875 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/Bson4IT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/Bson4IT.java
@@ -27,11 +27,13 @@ import java.sql.DriverManager;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.sql.Statement;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Properties;
 
 import org.apache.hadoop.hbase.util.Pair;
+import org.apache.phoenix.jdbc.PhoenixStatement;
 import org.apache.phoenix.schema.types.PDouble;
 import org.bson.BsonArray;
 import org.bson.BsonBinary;
@@ -167,7 +169,7 @@ public class Bson4IT extends ParallelStatsDisabledIT {
 
       assertFalse(rs.next());
 
-      validateIndexUsed(ps, indexName2);
+      validateExplainPlan(ps, indexName2, "RANGE SCAN ");
 
       ps = conn.prepareStatement("SELECT PK1, COL FROM " + tableName
           + " WHERE BSON_VALUE(COL, 'rather[3].outline.clock', 'VARCHAR') = 
?");
@@ -182,7 +184,7 @@ public class Bson4IT extends ParallelStatsDisabledIT {
 
       assertFalse(rs.next());
 
-      validateIndexUsed(ps, indexName1);
+      validateExplainPlan(ps, indexName1, "RANGE SCAN ");
 
       BsonDocument updateExp = new BsonDocument()
               .append("$ADD", new BsonDocument()
@@ -230,7 +232,7 @@ public class Bson4IT extends ParallelStatsDisabledIT {
       rs = ps.executeQuery();
       assertFalse(rs.next());
 
-      validateIndexUsed(ps, indexName1);
+      validateExplainPlan(ps, indexName1, "RANGE SCAN ");
 
       ps = conn.prepareStatement("SELECT PK1, COL FROM " + tableName
           + " WHERE BSON_VALUE(COL, 
'result[1].location.coordinates.longitude', 'DOUBLE') = ?");
@@ -239,7 +241,7 @@ public class Bson4IT extends ParallelStatsDisabledIT {
       rs = ps.executeQuery();
       assertFalse(rs.next());
 
-      validateIndexUsed(ps, indexName2);
+      validateExplainPlan(ps, indexName2, "RANGE SCAN ");
     }
   }
 
@@ -426,6 +428,127 @@ public class Bson4IT extends ParallelStatsDisabledIT {
     }
   }
 
+  @Test
+  public void testBsonPk() throws Exception {
+    Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+    String tableName = generateUniqueName();
+    try (Connection conn = DriverManager.getConnection(getUrl(), props)) {
+      conn.setAutoCommit(true);
+      String ddl = "CREATE TABLE " + tableName
+              + " (PK1 BSON NOT NULL, C1 VARCHAR"
+              + " CONSTRAINT pk PRIMARY KEY(PK1))";
+      conn.createStatement().execute(ddl);
+
+      String sample1 = getJsonString("json/sample_01.json");
+      String sample2 = getJsonString("json/sample_02.json");
+      String sample3 = getJsonString("json/sample_03.json");
+      BsonDocument bsonDocument1 = RawBsonDocument.parse(sample1);
+      BsonDocument bsonDocument2 = RawBsonDocument.parse(sample2);
+      BsonDocument bsonDocument3 = RawBsonDocument.parse(sample3);
+
+      upsertRowsWithBsonPkCol(conn, tableName, bsonDocument1, bsonDocument2, 
bsonDocument3);
+
+      String conditionExpression =
+              "press = :press AND track[0].shot[2][0].city.standard[5] = 
:softly";
+
+      BsonDocument conditionDoc = new BsonDocument();
+      conditionDoc.put("$EXPR", new BsonString(conditionExpression));
+      conditionDoc.put("$VAL", new BsonDocument()
+              .append(":press", new BsonString("beat"))
+              .append(":softly", new BsonString("softly")));
+
+      String query = "SELECT * FROM " + tableName +
+              " WHERE PK1 = ?";
+      PreparedStatement pst = conn.prepareStatement(query);
+      pst.setObject(1, bsonDocument1);
+      ResultSet rs = pst.executeQuery(query);
+
+      assertTrue(rs.next());
+      assertEquals("0002", rs.getString(2));
+      BsonDocument document1 = (BsonDocument) rs.getObject(1);
+      assertEquals(bsonDocument1, document1);
+
+      assertFalse(rs.next());
+
+      validateExplainPlan(pst, tableName, "POINT LOOKUP ON 1 KEY ");
+
+      query = "SELECT * FROM " + tableName +
+              " WHERE PK1 = CAST('" + sample2 + "' AS BSON)";
+      Statement stmt = conn.createStatement();
+      rs = stmt.executeQuery(query);
+
+      assertTrue(rs.next());
+      assertEquals("1010", rs.getString(2));
+      BsonDocument document2 = (BsonDocument) rs.getObject(1);
+      assertEquals(bsonDocument2, document2);
+
+      assertFalse(rs.next());
+
+      // TODO : Fix this separately, using CAST with PK column results into 
full table scan due
+      //  to bug.
+      // validateExplainPlan(stmt, query, tableName, "POINT LOOKUP ON 1 KEY ");
+
+      query = "SELECT * FROM " + tableName +
+              " WHERE PK1 != CAST('" + sample1 + "' AS BSON)";
+      stmt = conn.createStatement();
+      rs = stmt.executeQuery(query);
+
+      assertTrue(rs.next());
+      assertEquals("1011", rs.getString(2));
+      document2 = (BsonDocument) rs.getObject(1);
+      assertEquals(bsonDocument3, document2);
+
+      assertTrue(rs.next());
+      assertEquals("1010", rs.getString(2));
+      BsonDocument document3 = (BsonDocument) rs.getObject(1);
+      assertEquals(bsonDocument2, document3);
+
+      assertFalse(rs.next());
+
+      validateExplainPlan(stmt, query, tableName, "FULL SCAN ");
+
+      query = "SELECT * FROM " + tableName;
+      rs = conn.createStatement().executeQuery(query);
+
+      assertTrue(rs.next());
+      assertEquals("0002", rs.getString(2));
+      document1 = (BsonDocument) rs.getObject(1);
+      assertEquals(bsonDocument1, document1);
+
+      assertTrue(rs.next());
+      assertEquals("1011", rs.getString(2));
+      document2 = (BsonDocument) rs.getObject(1);
+      assertEquals(bsonDocument3, document2);
+
+      assertTrue(rs.next());
+      assertEquals("1010", rs.getString(2));
+      document3 = (BsonDocument) rs.getObject(1);
+      assertEquals(bsonDocument2, document3);
+
+      assertFalse(rs.next());
+    }
+  }
+
+  private static void upsertRowsWithBsonPkCol(Connection conn, String 
tableName,
+                                              BsonDocument bsonDocument1,
+                                              BsonDocument bsonDocument2,
+                                              BsonDocument bsonDocument3)
+          throws SQLException {
+    PreparedStatement stmt =
+            conn.prepareStatement("UPSERT INTO " + tableName + " VALUES 
(?,?)");
+    stmt.setString(2, "0002");
+    stmt.setObject(1, bsonDocument1);
+    stmt.executeUpdate();
+
+    stmt.setString(2, "1010");
+    stmt.setObject(1, bsonDocument2);
+    stmt.executeUpdate();
+
+    stmt.setString(2, "1011");
+    stmt.setObject(1, bsonDocument3);
+    stmt.executeUpdate();
+  }
+
   private static void upsertRows(Connection conn, String tableName, 
BsonDocument bsonDocument1,
                                 BsonDocument bsonDocument2, BsonDocument 
bsonDocument3)
           throws SQLException {
@@ -495,13 +618,24 @@ public class Bson4IT extends ParallelStatsDisabledIT {
     assertEquals(RawBsonDocument.parse(getJsonString(jsonPath)), 
resultSet.getObject(3));
   }
 
-  private static void validateIndexUsed(PreparedStatement ps, String indexName)
+  private static void validateExplainPlan(PreparedStatement ps, String 
tableName, String scanType)
       throws SQLException {
     ExplainPlan plan = 
ps.unwrap(PhoenixPreparedStatement.class).optimizeQuery().getExplainPlan();
+    validatePlan(tableName, scanType, plan);
+  }
+
+  private static void validateExplainPlan(Statement stmt, String query, String 
tableName,
+                                          String scanType)
+          throws SQLException {
+    ExplainPlan plan = 
stmt.unwrap(PhoenixStatement.class).optimizeQuery(query).getExplainPlan();
+    validatePlan(tableName, scanType, plan);
+  }
+
+  private static void validatePlan(String tableName, String scanType, 
ExplainPlan plan) {
     ExplainPlanAttributes explainPlanAttributes = 
plan.getPlanStepsAsAttributes();
-    assertEquals(indexName, explainPlanAttributes.getTableName());
+    assertEquals(tableName, explainPlanAttributes.getTableName());
     assertEquals("PARALLEL 1-WAY", 
explainPlanAttributes.getIteratorTypeAndScanSize());
-    assertEquals("RANGE SCAN ", explainPlanAttributes.getExplainScanType());
+    assertEquals(scanType, explainPlanAttributes.getExplainScanType());
   }
 
 }
\ No newline at end of file
diff --git 
a/phoenix-core/src/test/java/org/apache/phoenix/schema/types/PDataTypeTest.java 
b/phoenix-core/src/test/java/org/apache/phoenix/schema/types/PDataTypeTest.java
index 31b9505d7e..66d8bb7cf1 100644
--- 
a/phoenix-core/src/test/java/org/apache/phoenix/schema/types/PDataTypeTest.java
+++ 
b/phoenix-core/src/test/java/org/apache/phoenix/schema/types/PDataTypeTest.java
@@ -1790,7 +1790,7 @@ public class PDataTypeTest {
              + "VARBINARY=[BINARY, VARBINARY, VARBINARY_ENCODED], "
              + "VARBINARY ARRAY=[BINARY ARRAY, VARBINARY ARRAY], "
              + "VARBINARY_ENCODED=[BINARY, VARBINARY, VARBINARY_ENCODED], "
-             + "VARCHAR=[BINARY, CHAR, VARBINARY, VARBINARY_ENCODED, VARCHAR], 
"
+             + "VARCHAR=[BINARY, BSON, CHAR, VARBINARY, VARBINARY_ENCODED, 
VARCHAR], "
              + "VARCHAR ARRAY=[BINARY ARRAY, CHAR ARRAY, VARBINARY ARRAY, 
VARCHAR ARRAY]}", 
              coercibleToMap.toString());
     }
diff --git 
a/phoenix-core/src/test/java/org/apache/phoenix/util/IndexUtilTest.java 
b/phoenix-core/src/test/java/org/apache/phoenix/util/IndexUtilTest.java
index 77a3b7b325..fb0cde51bf 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/util/IndexUtilTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/util/IndexUtilTest.java
@@ -38,6 +38,7 @@ public class IndexUtilTest {
              + "BINARY ARRAY=BINARY ARRAY, "
              + "BOOLEAN=BOOLEAN, "
              + "BOOLEAN ARRAY=BOOLEAN ARRAY, "
+             + "BSON=BSON, "
              + "CHAR=CHAR, "
              + "CHAR ARRAY=CHAR ARRAY, "
              + "DATE=DATE, "
@@ -92,6 +93,7 @@ public class IndexUtilTest {
              + "BINARY ARRAY=BINARY ARRAY, "
              + "BOOLEAN=DECIMAL, "
              + "BOOLEAN ARRAY=BOOLEAN ARRAY, "
+             + "BSON=BSON, "
              + "CHAR=VARCHAR, "
              + "CHAR ARRAY=CHAR ARRAY, "
              + "DATE=DECIMAL, "

Reply via email to