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

xuzifu666 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git


The following commit(s) were added to refs/heads/main by this push:
     new cf4ffc1fdf [CALCITE-6291] Support converting ArrowTable to Queryable
cf4ffc1fdf is described below

commit cf4ffc1fdf9b6d971bc703fb31ec97be9b2f42de
Author: Yu Xu <[email protected]>
AuthorDate: Fri Apr 24 15:03:55 2026 +0800

    [CALCITE-6291] Support converting ArrowTable to Queryable
---
 .../apache/calcite/adapter/arrow/ArrowTable.java   | 38 +++++++++++-
 .../calcite/adapter/arrow/ArrowAdapterTest.java    | 70 ++++++++++++++++++++++
 2 files changed, 107 insertions(+), 1 deletion(-)

diff --git 
a/arrow/src/main/java/org/apache/calcite/adapter/arrow/ArrowTable.java 
b/arrow/src/main/java/org/apache/calcite/adapter/arrow/ArrowTable.java
index fa8d59389b..ba1568bcd2 100644
--- a/arrow/src/main/java/org/apache/calcite/adapter/arrow/ArrowTable.java
+++ b/arrow/src/main/java/org/apache/calcite/adapter/arrow/ArrowTable.java
@@ -19,6 +19,7 @@
 import org.apache.calcite.DataContext;
 import org.apache.calcite.adapter.java.JavaTypeFactory;
 import org.apache.calcite.linq4j.Enumerable;
+import org.apache.calcite.linq4j.Enumerator;
 import org.apache.calcite.linq4j.QueryProvider;
 import org.apache.calcite.linq4j.Queryable;
 import org.apache.calcite.linq4j.tree.Expression;
@@ -33,6 +34,7 @@
 import org.apache.calcite.schema.Schemas;
 import org.apache.calcite.schema.TranslatableTable;
 import org.apache.calcite.schema.impl.AbstractTable;
+import org.apache.calcite.schema.impl.AbstractTableQueryable;
 import org.apache.calcite.util.ImmutableIntList;
 import org.apache.calcite.util.Util;
 
@@ -184,7 +186,7 @@ public Enumerable<Object> query(DataContext root, 
ImmutableIntList fields,
 
   @Override public <T> Queryable<T> asQueryable(QueryProvider queryProvider,
       SchemaPlus schema, String tableName) {
-    throw new UnsupportedOperationException();
+    return new ArrowQueryable<>(queryProvider, schema, this, tableName);
   }
 
   @Override public Type getElementType() {
@@ -262,4 +264,38 @@ private static TreeNode makeLiteralNode(String literal, 
String type) {
           + ", type " + type);
     }
   }
+
+  /**
+   * Implementation of {@link Queryable} based on a {@link ArrowTable}.
+   *
+   * @param <T> element type
+   */
+  public static class ArrowQueryable<T> extends AbstractTableQueryable<T> {
+    ArrowQueryable(QueryProvider queryProvider, SchemaPlus schema,
+        ArrowTable table, String tableName) {
+      super(queryProvider, schema, table, tableName);
+    }
+
+    @Override public Enumerator<T> enumerator() {
+      throw new UnsupportedOperationException("enumerator");
+    }
+
+    private ArrowTable getTable() {
+      return (ArrowTable) table;
+    }
+
+    /**
+     * Executes a query with projection and filter conditions.
+     *
+     * @param fields projection fields
+     * @param conditions filter conditions
+     * @return result as enumerable
+     */
+    @SuppressWarnings("UnusedDeclaration")
+    public Enumerable<Object> query(List<Integer> fields,
+        List<List<List<String>>> conditions) {
+      final ImmutableIntList fieldList = ImmutableIntList.copyOf(fields);
+      return getTable().query(null, fieldList, conditions);
+    }
+  }
 }
diff --git 
a/arrow/src/test/java/org/apache/calcite/adapter/arrow/ArrowAdapterTest.java 
b/arrow/src/test/java/org/apache/calcite/adapter/arrow/ArrowAdapterTest.java
index 25212a2083..275bdd0be7 100644
--- a/arrow/src/test/java/org/apache/calcite/adapter/arrow/ArrowAdapterTest.java
+++ b/arrow/src/test/java/org/apache/calcite/adapter/arrow/ArrowAdapterTest.java
@@ -17,6 +17,9 @@
 package org.apache.calcite.adapter.arrow;
 
 import org.apache.calcite.jdbc.JavaTypeFactoryImpl;
+import org.apache.calcite.linq4j.Enumerable;
+import org.apache.calcite.linq4j.Enumerator;
+import org.apache.calcite.linq4j.Queryable;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rel.type.RelDataTypeFactory;
 import org.apache.calcite.rel.type.RelDataTypeSystem;
@@ -39,6 +42,10 @@
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 
 import static org.apache.calcite.test.Matchers.isListOf;
@@ -94,6 +101,69 @@ static void initializeArrowState(@TempDir Path 
sharedTempDir)
         isListOf("intField", "stringField", "floatField", "longField"));
   }
 
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6291";>[CALCITE-6291]
+   * Support converting ArrowTable to Queryable</a>.
+   */
+  @Test void testArrowTableAsQueryable() {
+    ArrowSchema arrowSchema = new ArrowSchema(arrowDataDirectory);
+    Map<String, Table> tableMap = arrowSchema.getTableMap();
+    ArrowTable arrowTable = (ArrowTable) tableMap.get("ARROWDATA");
+
+    // asQueryable can accept null QueryProvider and SchemaPlus for testing
+    Queryable<?> queryable = arrowTable.asQueryable(null, null, "ARROWDATA");
+
+    // Verify that asQueryable returns a non-null Queryable
+    assert queryable != null : "asQueryable should return a non-null 
Queryable";
+
+    // Verify that the Queryable is an instance of ArrowQueryable
+    assert queryable instanceof ArrowTable.ArrowQueryable
+        : "asQueryable should return an instance of ArrowQueryable";
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-6291";>[CALCITE-6291]
+   * Support converting ArrowTable to Queryable</a>.
+   */
+  @Test void testArrowQueryableQueryWithData() {
+    ArrowSchema arrowSchema = new ArrowSchema(arrowDataDirectory);
+    Map<String, Table> tableMap = arrowSchema.getTableMap();
+    ArrowTable arrowTable = (ArrowTable) tableMap.get("ARROWDATA");
+
+    @SuppressWarnings("unchecked")
+    ArrowTable.ArrowQueryable<Object> queryable =
+        (ArrowTable.ArrowQueryable<Object>) arrowTable.asQueryable(null, null, 
"ARROWDATA");
+
+    // Query with projection on all fields (0, 1, 2, 3) and no filter 
conditions
+    List<Integer> fields = Arrays.asList(0, 1, 2, 3);
+    List<List<List<String>>> conditions = Collections.emptyList();
+
+    Enumerable<Object> enumerable = queryable.query(fields, conditions);
+
+    // Fetch the first 6 rows using enumerator
+    List<Object> results = new ArrayList<>();
+    Enumerator<Object> enumerator = enumerable.enumerator();
+    int count = 0;
+    while (enumerator.moveNext() && count < 6) {
+      results.add(enumerator.current());
+      count++;
+    }
+    enumerator.close();
+
+    assert results.size() == 6 : "Expected 6 rows, got " + results.size();
+
+    // Verify each row is an Object array with 4 fields
+    for (int i = 0; i < 6; i++) {
+      Object[] row = (Object[]) results.get(i);
+      assert row.length == 4 : "Expected 4 fields, got " + row.length;
+      assert row[0].equals(i) : "Expected intField=" + i + ", got " + row[0];
+      // stringField may be Text or String type, convert to String for 
comparison
+      String stringValue = row[1].toString();
+      assert stringValue.equals(String.valueOf(i))
+          : "Expected stringField=" + i + ", got " + stringValue;
+    }
+  }
+
   @Test void testArrowProjectAllFields() {
     String sql = "select * from arrowdata\n";
     String plan = "PLAN=ArrowToEnumerableConverter\n"

Reply via email to