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

caicancai 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 410030390a [CALCITE-7580] Remove Gandiva dependency from Arrow adapter
410030390a is described below

commit 410030390a3b97de927d30266c37677bc2896596
Author: Cancai Cai <[email protected]>
AuthorDate: Fri Jun 19 17:23:35 2026 +0800

    [CALCITE-7580] Remove Gandiva dependency from Arrow adapter
---
 arrow/build.gradle.kts                             |   1 -
 .../adapter/arrow/AbstractArrowEnumerator.java     |  30 ++-
 .../adapter/arrow/ArrowDirectEnumerator.java       |  31 +--
 .../calcite/adapter/arrow/ArrowEnumerable.java     |  25 +--
 .../adapter/arrow/ArrowFilterEnumerator.java       | 238 ++++++++++++++++-----
 .../adapter/arrow/ArrowProjectEnumerator.java      |  80 -------
 .../apache/calcite/adapter/arrow/ArrowRules.java   |   2 +-
 .../apache/calcite/adapter/arrow/ArrowTable.java   | 143 +------------
 .../calcite/adapter/arrow/ArrowTranslator.java     |  69 ++++--
 .../calcite/adapter/arrow/ConditionToken.java      |  51 ++++-
 .../calcite/adapter/arrow/ArrowAdapterTest.java    |  50 +++++
 .../calcite/adapter/arrow/ArrowDataTest.java       |  29 +++
 .../calcite/adapter/arrow/ArrowExtension.java      |  23 +-
 bom/build.gradle.kts                               |   1 -
 gradle.properties                                  |   1 -
 site/_docs/history.md                              |   5 +
 16 files changed, 397 insertions(+), 382 deletions(-)

diff --git a/arrow/build.gradle.kts b/arrow/build.gradle.kts
index 598aa8a879..c75a8b5f67 100644
--- a/arrow/build.gradle.kts
+++ b/arrow/build.gradle.kts
@@ -23,7 +23,6 @@
     implementation("com.google.guava:guava")
     implementation("org.apache.arrow:arrow-memory-netty")
     implementation("org.apache.arrow:arrow-vector")
-    implementation("org.apache.arrow.gandiva:arrow-gandiva")
     annotationProcessor("org.immutables:value")
     compileOnly("org.immutables:value-annotations")
 
diff --git 
a/arrow/src/main/java/org/apache/calcite/adapter/arrow/AbstractArrowEnumerator.java
 
b/arrow/src/main/java/org/apache/calcite/adapter/arrow/AbstractArrowEnumerator.java
index e188757b0d..486e3f60bd 100644
--- 
a/arrow/src/main/java/org/apache/calcite/adapter/arrow/AbstractArrowEnumerator.java
+++ 
b/arrow/src/main/java/org/apache/calcite/adapter/arrow/AbstractArrowEnumerator.java
@@ -24,9 +24,7 @@
 import org.apache.arrow.vector.TimeStampVector;
 import org.apache.arrow.vector.ValueVector;
 import org.apache.arrow.vector.VectorSchemaRoot;
-import org.apache.arrow.vector.VectorUnloader;
 import org.apache.arrow.vector.ipc.ArrowFileReader;
-import org.apache.arrow.vector.ipc.message.ArrowRecordBatch;
 import org.apache.arrow.vector.types.TimeUnit;
 import org.apache.arrow.vector.types.pojo.ArrowType;
 
@@ -51,8 +49,6 @@ abstract class AbstractArrowEnumerator implements 
Enumerator<Object> {
     this.currRowIndex = -1;
   }
 
-  abstract void evaluateOperator(ArrowRecordBatch arrowRecordBatch);
-
   protected void loadNextArrowBatch() {
     try {
       final VectorSchemaRoot vsr = arrowFileReader.getVectorSchemaRoot();
@@ -60,14 +56,32 @@ protected void loadNextArrowBatch() {
         this.valueVectors.add(vsr.getVector(i));
       }
       this.rowCount = vsr.getRowCount();
-      VectorUnloader vectorUnloader = new VectorUnloader(vsr);
-      ArrowRecordBatch arrowRecordBatch = vectorUnloader.getRecordBatch();
-      evaluateOperator(arrowRecordBatch);
     } catch (IOException e) {
       throw Util.toUnchecked(e);
     }
   }
 
+  /** Loads the next non-empty Arrow batch. */
+  protected boolean loadNextNonEmptyArrowBatch() {
+    while (true) {
+      final boolean hasNextBatch;
+      try {
+        hasNextBatch = arrowFileReader.loadNextBatch();
+      } catch (IOException e) {
+        throw Util.toUnchecked(e);
+      }
+      if (!hasNextBatch) {
+        return false;
+      }
+      currRowIndex = -1;
+      valueVectors.clear();
+      loadNextArrowBatch();
+      if (rowCount > 0) {
+        return true;
+      }
+    }
+  }
+
   @Override public Object current() {
     if (fields.size() == 1) {
       return getValue(this.valueVectors.get(0), currRowIndex);
@@ -85,7 +99,7 @@ protected void loadNextArrowBatch() {
    * <p>For {@link TimeStampVector}, converts the raw value to
    * milliseconds since epoch, which is the representation used by
    * Calcite's Enumerable runtime for TIMESTAMP types. */
-  private static Object getValue(ValueVector vector, int index) {
+  protected static Object getValue(ValueVector vector, int index) {
     if (vector instanceof TimeStampVector) {
       if (vector.isNull(index)) {
         return null;
diff --git 
a/arrow/src/main/java/org/apache/calcite/adapter/arrow/ArrowDirectEnumerator.java
 
b/arrow/src/main/java/org/apache/calcite/adapter/arrow/ArrowDirectEnumerator.java
index 0cdec7baeb..787ffd88d9 100644
--- 
a/arrow/src/main/java/org/apache/calcite/adapter/arrow/ArrowDirectEnumerator.java
+++ 
b/arrow/src/main/java/org/apache/calcite/adapter/arrow/ArrowDirectEnumerator.java
@@ -17,19 +17,11 @@
 package org.apache.calcite.adapter.arrow;
 
 import org.apache.calcite.util.ImmutableIntList;
-import org.apache.calcite.util.Util;
 
 import org.apache.arrow.vector.ipc.ArrowFileReader;
-import org.apache.arrow.vector.ipc.message.ArrowRecordBatch;
-
-import java.io.IOException;
 
 /**
  * Enumerator that reads projected Arrow value-vectors directly.
- *
- * <p>This path is used for identity projections that Gandiva cannot project
- * through the existing {@code Projector} path, such as Arrow binary vectors.
- * It is not a replacement for Gandiva expression evaluation.
  */
 class ArrowDirectEnumerator extends AbstractArrowEnumerator {
   private final Runnable onClose;
@@ -40,27 +32,14 @@ class ArrowDirectEnumerator extends AbstractArrowEnumerator 
{
     this.onClose = onClose;
   }
 
-  @Override protected void evaluateOperator(ArrowRecordBatch arrowRecordBatch) 
{
-  }
-
   @Override public boolean moveNext() {
-    if (currRowIndex >= rowCount - 1) {
-      final boolean hasNextBatch;
-      try {
-        hasNextBatch = arrowFileReader.loadNextBatch();
-      } catch (IOException e) {
-        throw Util.toUnchecked(e);
-      }
-      if (hasNextBatch) {
-        currRowIndex = 0;
-        this.valueVectors.clear();
-        loadNextArrowBatch();
+    while (currRowIndex >= rowCount - 1) {
+      if (!loadNextNonEmptyArrowBatch()) {
+        return false;
       }
-      return hasNextBatch;
-    } else {
-      currRowIndex++;
-      return true;
     }
+    currRowIndex++;
+    return true;
   }
 
   @Override public void close() {
diff --git 
a/arrow/src/main/java/org/apache/calcite/adapter/arrow/ArrowEnumerable.java 
b/arrow/src/main/java/org/apache/calcite/adapter/arrow/ArrowEnumerable.java
index 84ed5997aa..b9c0c4171e 100644
--- a/arrow/src/main/java/org/apache/calcite/adapter/arrow/ArrowEnumerable.java
+++ b/arrow/src/main/java/org/apache/calcite/adapter/arrow/ArrowEnumerable.java
@@ -21,11 +21,10 @@
 import org.apache.calcite.util.ImmutableIntList;
 import org.apache.calcite.util.Util;
 
-import org.apache.arrow.gandiva.evaluator.Filter;
-import org.apache.arrow.gandiva.evaluator.Projector;
 import org.apache.arrow.vector.ipc.ArrowFileReader;
+import org.apache.arrow.vector.types.pojo.Schema;
 
-import org.checkerframework.checker.nullness.qual.Nullable;
+import java.util.List;
 
 /**
  * Enumerable that reads from Arrow value-vectors.
@@ -33,28 +32,24 @@
 class ArrowEnumerable extends AbstractEnumerable<Object> {
   private final ArrowFileReader arrowFileReader;
   private final ImmutableIntList fields;
-  private final @Nullable Projector projector;
-  private final @Nullable Filter filter;
+  private final List<List<List<String>>> conditions;
+  private final Schema schema;
   private final Runnable onClose;
 
   ArrowEnumerable(ArrowFileReader arrowFileReader, ImmutableIntList fields,
-      @Nullable Projector projector, @Nullable Filter filter,
-      Runnable onClose) {
+      List<List<List<String>>> conditions, Schema schema, Runnable onClose) {
     this.arrowFileReader = arrowFileReader;
-    this.projector = projector;
-    this.filter = filter;
+    this.conditions = conditions;
+    this.schema = schema;
     this.fields = fields;
     this.onClose = onClose;
   }
 
   @Override public Enumerator<Object> enumerator() {
     try {
-      if (projector != null) {
-        return new ArrowProjectEnumerator(arrowFileReader, fields, projector,
-            onClose);
-      } else if (filter != null) {
-        return new ArrowFilterEnumerator(arrowFileReader, fields, filter,
-            onClose);
+      if (!conditions.isEmpty()) {
+        return new ArrowFilterEnumerator(arrowFileReader, fields,
+            conditions, schema, onClose);
       }
       // No projector and no filter means the query is an identity projection
       // that should read selected value-vectors directly.
diff --git 
a/arrow/src/main/java/org/apache/calcite/adapter/arrow/ArrowFilterEnumerator.java
 
b/arrow/src/main/java/org/apache/calcite/adapter/arrow/ArrowFilterEnumerator.java
index 5eddec2249..2f154cc6ef 100644
--- 
a/arrow/src/main/java/org/apache/calcite/adapter/arrow/ArrowFilterEnumerator.java
+++ 
b/arrow/src/main/java/org/apache/calcite/adapter/arrow/ArrowFilterEnumerator.java
@@ -19,91 +19,215 @@
 import org.apache.calcite.util.ImmutableIntList;
 import org.apache.calcite.util.Util;
 
-import org.apache.arrow.gandiva.evaluator.Filter;
-import org.apache.arrow.gandiva.evaluator.SelectionVector;
-import org.apache.arrow.gandiva.evaluator.SelectionVectorInt16;
-import org.apache.arrow.gandiva.exceptions.GandivaException;
-import org.apache.arrow.memory.ArrowBuf;
-import org.apache.arrow.memory.BufferAllocator;
-import org.apache.arrow.memory.RootAllocator;
+import org.apache.arrow.vector.ValueVector;
+import org.apache.arrow.vector.VectorSchemaRoot;
 import org.apache.arrow.vector.ipc.ArrowFileReader;
-import org.apache.arrow.vector.ipc.message.ArrowRecordBatch;
-
-import org.checkerframework.checker.nullness.qual.Nullable;
+import org.apache.arrow.vector.types.pojo.Field;
+import org.apache.arrow.vector.types.pojo.Schema;
 
 import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
 
 import static java.util.Objects.requireNonNull;
 
 /**
- * Enumerator that reads from a filtered collection of Arrow value-vectors.
+ * Enumerator that evaluates Arrow filter tokens in Java.
  */
 class ArrowFilterEnumerator extends AbstractArrowEnumerator {
-  private final BufferAllocator allocator;
-  private final Filter filter;
-  private @Nullable ArrowBuf buf;
-  private @Nullable SelectionVector selectionVector;
-  private int selectionVectorIndex;
-
+  private final List<List<ConditionToken>> conditions;
+  private final Schema schema;
   private final Runnable onClose;
+  private final List<ValueVector> filterVectors;
+  private final Map<String, Pattern> likePatterns;
 
-  ArrowFilterEnumerator(ArrowFileReader arrowFileReader, ImmutableIntList 
fields,
-      Filter filter, Runnable onClose) {
+  ArrowFilterEnumerator(ArrowFileReader arrowFileReader,
+      ImmutableIntList fields, List<List<List<String>>> conditions,
+      Schema schema, Runnable onClose) {
     super(arrowFileReader, fields);
-    this.allocator = new RootAllocator(Long.MAX_VALUE);
-    this.filter = filter;
+    this.conditions = toConditionTokens(conditions);
+    this.schema = schema;
     this.onClose = onClose;
+    this.filterVectors = new ArrayList<>(schema.getFields().size());
+    this.likePatterns = new HashMap<>();
   }
 
-  @Override void evaluateOperator(ArrowRecordBatch arrowRecordBatch) {
+  @Override protected void loadNextArrowBatch() {
+    super.loadNextArrowBatch();
+    final VectorSchemaRoot root;
     try {
-      this.buf = this.allocator.buffer((long) rowCount * 2);
-      this.selectionVector = new SelectionVectorInt16(buf);
-      filter.evaluate(arrowRecordBatch, selectionVector);
-    } catch (GandivaException e) {
+      root = arrowFileReader.getVectorSchemaRoot();
+    } catch (IOException e) {
       throw Util.toUnchecked(e);
     }
+    filterVectors.clear();
+    for (int i = 0; i < schema.getFields().size(); i++) {
+      filterVectors.add(root.getVector(i));
+    }
   }
 
   @Override public boolean moveNext() {
-    if (selectionVector == null
-        || selectionVectorIndex >= selectionVector.getRecordCount()) {
-      boolean hasNextBatch;
-      while (true) {
-        try {
-          hasNextBatch = arrowFileReader.loadNextBatch();
-        } catch (IOException e) {
-          throw Util.toUnchecked(e);
+    while (true) {
+      if (currRowIndex >= rowCount - 1) {
+        if (!loadNextNonEmptyArrowBatch()) {
+          return false;
         }
-        if (hasNextBatch) {
-          selectionVectorIndex = 0;
-          this.valueVectors.clear();
-          loadNextArrowBatch();
-          requireNonNull(selectionVector, "selectionVector");
-          if (selectionVectorIndex >= selectionVector.getRecordCount()) {
-            // the "filtered" batch is empty, but there may be more batches to 
fetch
-            continue;
-          }
-          currRowIndex = selectionVector.getIndex(selectionVectorIndex++);
+      }
+      currRowIndex++;
+      if (matches(currRowIndex)) {
+        return true;
+      }
+    }
+  }
+
+  private boolean matches(int rowIndex) {
+    for (List<ConditionToken> orGroup : conditions) {
+      boolean any = false;
+      for (ConditionToken token : orGroup) {
+        if (matches(token, rowIndex)) {
+          any = true;
+          break;
         }
-        return hasNextBatch;
       }
-    } else {
-      currRowIndex = selectionVector.getIndex(selectionVectorIndex++);
-      return true;
+      if (!any) {
+        return false;
+      }
     }
+    return true;
   }
 
-  @Override public void close() {
-    try {
-      if (buf != null) {
-        buf.close();
+  private boolean matches(ConditionToken token, int rowIndex) {
+    final Object value = getValue(fieldVector(token.fieldName), rowIndex);
+    switch (token.operator) {
+    case IS_NULL:
+      return value == null;
+    case IS_NOT_NULL:
+      return value != null;
+    case IS_TRUE:
+      return Boolean.TRUE.equals(value);
+    case IS_FALSE:
+      return Boolean.FALSE.equals(value);
+    case IS_NOT_TRUE:
+      return !Boolean.TRUE.equals(value);
+    case IS_NOT_FALSE:
+      return !Boolean.FALSE.equals(value);
+    case EQUAL:
+      return value != null && compare(value, literal(token)) == 0;
+    case NOT_EQUAL:
+      return value != null && compare(value, literal(token)) != 0;
+    case LESS_THAN:
+      return value != null && compare(value, literal(token)) < 0;
+    case LESS_THAN_OR_EQUAL:
+      return value != null && compare(value, literal(token)) <= 0;
+    case GREATER_THAN:
+      return value != null && compare(value, literal(token)) > 0;
+    case GREATER_THAN_OR_EQUAL:
+      return value != null && compare(value, literal(token)) >= 0;
+    case LIKE:
+      return value != null
+          && like(value.toString(), requireNonNull(token.value, "value"));
+    default:
+      throw new AssertionError("Unhandled Arrow filter operator: " + 
token.operator);
+    }
+  }
+
+  private ValueVector fieldVector(String fieldName) {
+    final Field field = schema.findField(fieldName);
+    final int index = schema.getFields().indexOf(field);
+    if (index < 0) {
+      throw new IllegalArgumentException("Unknown Arrow field: " + fieldName);
+    }
+    return filterVectors.get(index);
+  }
+
+  private static Object literal(ConditionToken token) {
+    final String type = requireNonNull(token.valueType, "valueType");
+    final String value = requireNonNull(token.value, "value");
+    if (type.startsWith("decimal")) {
+      return new BigDecimal(value);
+    } else if (type.equals("integer")) {
+      return Integer.valueOf(value);
+    } else if (type.equals("long")) {
+      return Long.valueOf(value);
+    } else if (type.equals("float")) {
+      return Float.valueOf(value);
+    } else if (type.equals("double")) {
+      return Double.valueOf(value);
+    } else if (type.equals("string")) {
+      return unquote(value);
+    }
+    throw new UnsupportedOperationException("Unsupported literal type: " + 
type);
+  }
+
+  private static int compare(Object left, Object right) {
+    if (left instanceof BigDecimal || right instanceof BigDecimal) {
+      return toBigDecimal(left).compareTo(toBigDecimal(right));
+    }
+    if (left instanceof Number && right instanceof Number) {
+      return Double.compare(((Number) left).doubleValue(),
+          ((Number) right).doubleValue());
+    }
+    return left.toString().compareTo(right.toString());
+  }
+
+  private static BigDecimal toBigDecimal(Object value) {
+    if (value instanceof BigDecimal) {
+      return (BigDecimal) value;
+    }
+    return new BigDecimal(value.toString());
+  }
+
+  private boolean like(String value, String pattern) {
+    final String unquotedPattern = unquote(pattern);
+    final Pattern compiledPattern =
+        likePatterns.computeIfAbsent(unquotedPattern, p -> {
+          return Pattern.compile(toRegex(p), Pattern.DOTALL);
+        });
+    return compiledPattern.matcher(value).matches();
+  }
+
+  private static String toRegex(String pattern) {
+    final StringBuilder builder = new StringBuilder();
+    for (int i = 0; i < pattern.length(); i++) {
+      final char c = pattern.charAt(i);
+      if (c == '%') {
+        builder.append(".*");
+      } else if (c == '_') {
+        builder.append('.');
+      } else {
+        builder.append(Pattern.quote(String.valueOf(c)));
       }
-      filter.close();
-    } catch (GandivaException e) {
-      throw Util.toUnchecked(e);
-    } finally {
-      onClose.run();
     }
+    return builder.toString();
+  }
+
+  private static String unquote(String value) {
+    if (value.length() >= 2 && value.charAt(0) == '\''
+        && value.charAt(value.length() - 1) == '\'') {
+      return value.substring(1, value.length() - 1).replace("''", "'");
+    }
+    return value;
+  }
+
+  private static List<List<ConditionToken>> toConditionTokens(
+      List<List<List<String>>> conditions) {
+    final List<List<ConditionToken>> result =
+        new ArrayList<>(conditions.size());
+    for (List<List<String>> orGroup : conditions) {
+      final List<ConditionToken> tokens = new ArrayList<>(orGroup.size());
+      for (List<String> token : orGroup) {
+        tokens.add(ConditionToken.fromTokenList(token));
+      }
+      result.add(tokens);
+    }
+    return result;
+  }
+
+  @Override public void close() {
+    onClose.run();
   }
 }
diff --git 
a/arrow/src/main/java/org/apache/calcite/adapter/arrow/ArrowProjectEnumerator.java
 
b/arrow/src/main/java/org/apache/calcite/adapter/arrow/ArrowProjectEnumerator.java
deleted file mode 100644
index 0895f36cf1..0000000000
--- 
a/arrow/src/main/java/org/apache/calcite/adapter/arrow/ArrowProjectEnumerator.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * 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.calcite.adapter.arrow;
-
-import org.apache.calcite.util.ImmutableIntList;
-import org.apache.calcite.util.Util;
-
-import org.apache.arrow.gandiva.evaluator.Projector;
-import org.apache.arrow.gandiva.exceptions.GandivaException;
-import org.apache.arrow.vector.ipc.ArrowFileReader;
-import org.apache.arrow.vector.ipc.message.ArrowRecordBatch;
-
-import java.io.IOException;
-
-/**
- * Enumerator that reads from a projected collection of Arrow value-vectors.
- */
-class ArrowProjectEnumerator extends AbstractArrowEnumerator {
-  private final Projector projector;
-  private final Runnable onClose;
-
-  ArrowProjectEnumerator(ArrowFileReader arrowFileReader, ImmutableIntList 
fields,
-      Projector projector, Runnable onClose) {
-    super(arrowFileReader, fields);
-    this.projector = projector;
-    this.onClose = onClose;
-  }
-
-  @Override protected void evaluateOperator(ArrowRecordBatch arrowRecordBatch) 
{
-    try {
-      projector.evaluate(arrowRecordBatch, valueVectors);
-    } catch (GandivaException e) {
-      throw Util.toUnchecked(e);
-    }
-  }
-
-  @Override public boolean moveNext() {
-    if (currRowIndex >= rowCount - 1) {
-      final boolean hasNextBatch;
-      try {
-        hasNextBatch = arrowFileReader.loadNextBatch();
-      } catch (IOException e) {
-        throw Util.toUnchecked(e);
-      }
-      if (hasNextBatch) {
-        currRowIndex = 0;
-        this.valueVectors.clear();
-        loadNextArrowBatch();
-      }
-      return hasNextBatch;
-    } else {
-      currRowIndex++;
-      return true;
-    }
-  }
-
-  @Override public void close() {
-    try {
-      projector.close();
-    } catch (GandivaException e) {
-      throw Util.toUnchecked(e);
-    } finally {
-      onClose.run();
-    }
-  }
-}
diff --git 
a/arrow/src/main/java/org/apache/calcite/adapter/arrow/ArrowRules.java 
b/arrow/src/main/java/org/apache/calcite/adapter/arrow/ArrowRules.java
index 6e268d6469..3da1527f10 100644
--- a/arrow/src/main/java/org/apache/calcite/adapter/arrow/ArrowRules.java
+++ b/arrow/src/main/java/org/apache/calcite/adapter/arrow/ArrowRules.java
@@ -100,7 +100,7 @@ RelNode convert(Filter filter) {
       final RelTraitSet traitSet =
           filter.getTraitSet().replace(ArrowRel.CONVENTION);
       // Expand SEARCH (e.g. IN, BETWEEN) before pushing to Arrow,
-      // since Gandiva does not support SEARCH natively.
+      // since the Arrow adapter does not support SEARCH natively.
       final RexNode condition =
           RexUtil.expandSearch(filter.getCluster().getRexBuilder(), null, 
filter.getCondition());
       return new ArrowFilter(filter.getCluster(), traitSet,
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 74438efe2a..2585f5d156 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
@@ -38,17 +38,9 @@
 import org.apache.calcite.util.ImmutableIntList;
 import org.apache.calcite.util.Util;
 
-import org.apache.arrow.gandiva.evaluator.Filter;
-import org.apache.arrow.gandiva.evaluator.Projector;
-import org.apache.arrow.gandiva.exceptions.GandivaException;
-import org.apache.arrow.gandiva.expression.Condition;
-import org.apache.arrow.gandiva.expression.ExpressionTree;
-import org.apache.arrow.gandiva.expression.TreeBuilder;
-import org.apache.arrow.gandiva.expression.TreeNode;
 import org.apache.arrow.memory.RootAllocator;
 import org.apache.arrow.vector.ipc.ArrowFileReader;
 import org.apache.arrow.vector.ipc.SeekableReadChannel;
-import org.apache.arrow.vector.types.pojo.ArrowType;
 import org.apache.arrow.vector.types.pojo.Field;
 import org.apache.arrow.vector.types.pojo.Schema;
 
@@ -58,20 +50,15 @@
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.lang.reflect.Type;
-import java.util.ArrayList;
 import java.util.List;
 
-import static java.lang.Double.parseDouble;
-import static java.lang.Float.parseFloat;
-import static java.lang.Integer.parseInt;
-import static java.lang.Long.parseLong;
 import static java.util.Objects.requireNonNull;
 
 /**
  * Table backed by an Apache Arrow file.
  *
- * <p>Reads data from an Arrow IPC file on disk and supports projection
- * and filter push-down via the Gandiva expression compiler.
+ * <p>Reads data from an Arrow IPC file on disk. Projections and filters read
+ * directly from Arrow value-vectors.
  *
  * <p>Implements {@link TranslatableTable} so that it can be converted into
  * an {@link ArrowTableScan} for query planning, and {@link QueryableTable}
@@ -116,43 +103,6 @@ public class ArrowTable extends AbstractTable
   public Enumerable<Object> query(DataContext root, ImmutableIntList fields,
       List<List<List<String>>> conditions) {
     requireNonNull(fields, "fields");
-    final Projector projector;
-    final Filter filter;
-
-    if (conditions.isEmpty()) {
-      filter = null;
-      projector = makeProjector(fields);
-    } else {
-      projector = null;
-
-      final List<TreeNode> conjuncts = new ArrayList<>(conditions.size());
-      for (List<List<String>> orGroup : conditions) {
-        final List<TreeNode> disjuncts = new ArrayList<>(orGroup.size());
-        for (List<String> conditionParts : orGroup) {
-          disjuncts.add(
-              convertConditionToGandiva(
-                  ConditionToken.fromTokenList(conditionParts)));
-        }
-        if (disjuncts.size() == 1) {
-          conjuncts.add(disjuncts.get(0));
-        } else {
-          conjuncts.add(TreeBuilder.makeOr(disjuncts));
-        }
-      }
-      final Condition filterCondition;
-      if (conjuncts.size() == 1) {
-        filterCondition = TreeBuilder.makeCondition(conjuncts.get(0));
-      } else {
-        filterCondition =
-            TreeBuilder.makeCondition(TreeBuilder.makeAnd(conjuncts));
-      }
-
-      try {
-        filter = Filter.make(schema, filterCondition);
-      } catch (GandivaException e) {
-        throw Util.toUnchecked(e);
-      }
-    }
 
     FileInputStream fis = null;
     try {
@@ -163,7 +113,7 @@ public Enumerable<Object> query(DataContext root, 
ImmutableIntList fields,
       final FileInputStream fisRef = fis;
       final Runnable onClose = () -> closeSilently(fisRef);
       fis = null; // ownership transferred to onClose
-      return new ArrowEnumerable(reader, fields, projector, filter, onClose);
+      return new ArrowEnumerable(reader, fields, conditions, schema, onClose);
     } catch (IOException e) {
       throw Util.toUnchecked(e);
     } finally {
@@ -202,70 +152,6 @@ private static RelDataType deduceRowType(Schema schema,
     return builder.build();
   }
 
-  private @Nullable Projector makeProjector(ImmutableIntList fields) {
-    if (requiresDirectVectorProjection(fields)) {
-      // Returning null selects ArrowEnumerable's direct vector-read path.
-      // Use that path because Gandiva does not support identity projection
-      // expressions over Arrow List and binary vectors.
-      return null;
-    }
-
-    final List<ExpressionTree> expressionTrees = new ArrayList<>();
-    for (int fieldOrdinal : fields) {
-      Field field = schema.getFields().get(fieldOrdinal);
-      TreeNode node = TreeBuilder.makeField(field);
-      expressionTrees.add(TreeBuilder.makeExpression(node, field));
-    }
-    try {
-      return Projector.make(schema, expressionTrees);
-    } catch (GandivaException e) {
-      throw Util.toUnchecked(e);
-    }
-  }
-
-  /** Returns whether selected fields should be projected by reading Arrow
-   * value-vectors directly rather than by creating a Gandiva projector.
-   *
-   * <p>CALCITE-7541 extends this direct projection path for Arrow binary 
vector
-   * families because Gandiva cannot project them through the existing identity
-   * projection path. Queries with filters still use Gandiva filters; this 
direct
-   * path only applies to no-filter projections.
-   */
-  private boolean requiresDirectVectorProjection(ImmutableIntList fields) {
-    for (int fieldOrdinal : fields) {
-      switch (schema.getFields().get(fieldOrdinal).getType().getTypeID()) {
-      case List:
-      case Binary:
-      case LargeBinary:
-      case FixedSizeBinary:
-        return true;
-      default:
-        break;
-      }
-    }
-    return false;
-  }
-
-  /** Converts a single {@link ConditionToken} into a Gandiva {@link 
TreeNode}. */
-  private TreeNode convertConditionToGandiva(ConditionToken token) {
-    final List<TreeNode> treeNodes = new ArrayList<>(2);
-    treeNodes.add(
-        TreeBuilder.makeField(schema.getFields()
-            .get(
-                schema.getFields().indexOf(
-                schema.findField(token.fieldName)))));
-
-    if (token.isBinary()) {
-      treeNodes.add(
-          makeLiteralNode(
-              requireNonNull(token.value, "value"),
-              requireNonNull(token.valueType, "valueType")));
-    }
-
-    return TreeBuilder.makeFunction(
-        token.operator, treeNodes, new ArrowType.Bool());
-  }
-
   /** Closes an {@link AutoCloseable} without throwing. */
   private static void closeSilently(AutoCloseable closeable) {
     try {
@@ -275,29 +161,6 @@ private static void closeSilently(AutoCloseable closeable) 
{
     }
   }
 
-  private static TreeNode makeLiteralNode(String literal, String type) {
-    if (type.startsWith("decimal")) {
-      String[] typeParts =
-          type.substring(type.indexOf('(') + 1, type.indexOf(')')).split(",");
-      int precision = parseInt(typeParts[0]);
-      int scale = parseInt(typeParts[1]);
-      return TreeBuilder.makeDecimalLiteral(literal, precision, scale);
-    } else if (type.equals("integer")) {
-      return TreeBuilder.makeLiteral(parseInt(literal));
-    } else if (type.equals("long")) {
-      return TreeBuilder.makeLiteral(parseLong(literal));
-    } else if (type.equals("float")) {
-      return TreeBuilder.makeLiteral(parseFloat(literal));
-    } else if (type.equals("double")) {
-      return TreeBuilder.makeLiteral(parseDouble(literal));
-    } else if (type.equals("string")) {
-      return TreeBuilder.makeStringLiteral(literal.substring(1, 
literal.length() - 1));
-    } else {
-      throw new IllegalArgumentException("Invalid literal " + literal
-          + ", type " + type);
-    }
-  }
-
   /**
    * Implementation of {@link Queryable} based on a {@link ArrowTable}.
    *
diff --git 
a/arrow/src/main/java/org/apache/calcite/adapter/arrow/ArrowTranslator.java 
b/arrow/src/main/java/org/apache/calcite/adapter/arrow/ArrowTranslator.java
index 0ec6804052..2b42295981 100644
--- a/arrow/src/main/java/org/apache/calcite/adapter/arrow/ArrowTranslator.java
+++ b/arrow/src/main/java/org/apache/calcite/adapter/arrow/ArrowTranslator.java
@@ -35,13 +35,26 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import static org.apache.calcite.adapter.arrow.ConditionToken.Operator.EQUAL;
+import static 
org.apache.calcite.adapter.arrow.ConditionToken.Operator.GREATER_THAN;
+import static 
org.apache.calcite.adapter.arrow.ConditionToken.Operator.GREATER_THAN_OR_EQUAL;
+import static 
org.apache.calcite.adapter.arrow.ConditionToken.Operator.IS_FALSE;
+import static 
org.apache.calcite.adapter.arrow.ConditionToken.Operator.IS_NOT_FALSE;
+import static 
org.apache.calcite.adapter.arrow.ConditionToken.Operator.IS_NOT_NULL;
+import static 
org.apache.calcite.adapter.arrow.ConditionToken.Operator.IS_NOT_TRUE;
+import static org.apache.calcite.adapter.arrow.ConditionToken.Operator.IS_NULL;
+import static org.apache.calcite.adapter.arrow.ConditionToken.Operator.IS_TRUE;
+import static 
org.apache.calcite.adapter.arrow.ConditionToken.Operator.LESS_THAN;
+import static 
org.apache.calcite.adapter.arrow.ConditionToken.Operator.LESS_THAN_OR_EQUAL;
+import static org.apache.calcite.adapter.arrow.ConditionToken.Operator.LIKE;
+import static 
org.apache.calcite.adapter.arrow.ConditionToken.Operator.NOT_EQUAL;
 import static 
org.apache.calcite.util.DateTimeStringUtils.ISO_DATETIME_FRACTIONAL_SECOND_FORMAT;
 import static org.apache.calcite.util.DateTimeStringUtils.getDateFormatter;
 
 import static java.util.Objects.requireNonNull;
 
 /**
- * Translates a {@link RexNode} expression to Gandiva predicate tokens.
+ * Translates a {@link RexNode} expression to Arrow predicate tokens.
  */
 class ArrowTranslator {
   final RexBuilder rexBuilder;
@@ -65,7 +78,7 @@ public static ArrowTranslator create(RexBuilder rexBuilder,
    *
    * <p>If exceeded, {@link RexUtil#toCnf(RexBuilder, int, RexNode)} returns
    * the original expression unchanged, which may cause the subsequent
-   * translation to Gandiva predicates to fail with an
+   * translation to Arrow predicates to fail with an
    * {@link UnsupportedOperationException}. When invoked by the Arrow adapter
    * module, the exception is caught and the plan falls back to
    * an Enumerable convention. */
@@ -120,32 +133,32 @@ private static Object literalValue(RexLiteral literal) {
   private ConditionToken translateMatch2(RexNode node) {
     switch (node.getKind()) {
     case EQUALS:
-      return translateBinary("equal", "=", (RexCall) node);
+      return translateBinary(EQUAL, EQUAL, (RexCall) node);
     case NOT_EQUALS:
-      return translateBinary("not_equal", "<>", (RexCall) node);
+      return translateBinary(NOT_EQUAL, NOT_EQUAL, (RexCall) node);
     case LESS_THAN:
-      return translateBinary("less_than", ">", (RexCall) node);
+      return translateBinary(LESS_THAN, GREATER_THAN, (RexCall) node);
     case LESS_THAN_OR_EQUAL:
-      return translateBinary("less_than_or_equal_to", ">=", (RexCall) node);
+      return translateBinary(LESS_THAN_OR_EQUAL, GREATER_THAN_OR_EQUAL, 
(RexCall) node);
     case GREATER_THAN:
-      return translateBinary("greater_than", "<", (RexCall) node);
+      return translateBinary(GREATER_THAN, LESS_THAN, (RexCall) node);
     case GREATER_THAN_OR_EQUAL:
-      return translateBinary("greater_than_or_equal_to", "<=", (RexCall) node);
+      return translateBinary(GREATER_THAN_OR_EQUAL, LESS_THAN_OR_EQUAL, 
(RexCall) node);
     case IS_NULL:
-      return translateUnary("isnull", (RexCall) node);
+      return translateUnary(IS_NULL, (RexCall) node);
     case IS_NOT_NULL:
-      return translateUnary("isnotnull", (RexCall) node);
+      return translateUnary(IS_NOT_NULL, (RexCall) node);
     case IS_NOT_TRUE:
-      return translateUnary("isnottrue", (RexCall) node);
+      return translateUnary(IS_NOT_TRUE, (RexCall) node);
     case IS_NOT_FALSE:
-      return translateUnary("isnotfalse", (RexCall) node);
+      return translateUnary(IS_NOT_FALSE, (RexCall) node);
     case INPUT_REF:
       final RexInputRef inputRef = (RexInputRef) node;
-      return ConditionToken.unary(fieldNames.get(inputRef.getIndex()), 
"istrue");
+      return ConditionToken.unary(fieldNames.get(inputRef.getIndex()), 
IS_TRUE);
     case NOT:
-      return translateUnary("isfalse", (RexCall) node);
+      return translateUnary(IS_FALSE, (RexCall) node);
     case LIKE:
-      return translateBinary("like", null, (RexCall) node);
+      return translateBinaryNoReverse(LIKE, (RexCall) node);
     default:
       throw new UnsupportedOperationException("Unsupported operator " + node);
     }
@@ -155,7 +168,8 @@ private ConditionToken translateMatch2(RexNode node) {
    * Translates a call to a binary operator, reversing arguments if
    * necessary.
    */
-  private ConditionToken translateBinary(String op, String rop, RexCall call) {
+  private ConditionToken translateBinary(ConditionToken.Operator op,
+      ConditionToken.Operator rop, RexCall call) {
     final RexNode left = call.operands.get(0);
     final RexNode right = call.operands.get(1);
     @Nullable ConditionToken expression = translateBinary2(op, left, right);
@@ -169,9 +183,21 @@ private ConditionToken translateBinary(String op, String 
rop, RexCall call) {
     throw new UnsupportedOperationException("Unsupported binary operator " + 
call);
   }
 
+  /** Translates a call to a binary operator without reversing arguments. */
+  private ConditionToken translateBinaryNoReverse(ConditionToken.Operator op,
+      RexCall call) {
+    final RexNode left = call.operands.get(0);
+    final RexNode right = call.operands.get(1);
+    @Nullable ConditionToken expression = translateBinary2(op, left, right);
+    if (expression != null) {
+      return expression;
+    }
+    throw new UnsupportedOperationException("Unsupported binary operator " + 
call);
+  }
+
   /** Translates a call to a binary operator. Returns null on failure. */
-  private @Nullable ConditionToken translateBinary2(String op, RexNode left,
-      RexNode right) {
+  private @Nullable ConditionToken translateBinary2(
+      ConditionToken.Operator op, RexNode left, RexNode right) {
     if (right.getKind() != SqlKind.LITERAL) {
       return null;
     }
@@ -191,7 +217,7 @@ private ConditionToken translateBinary(String op, String 
rop, RexCall call) {
 
   /** Combines a field name, operator, and literal to produce a binary
    * condition token. */
-  private ConditionToken translateOp2(String op, String name,
+  private ConditionToken translateOp2(ConditionToken.Operator op, String name,
       RexLiteral right) {
     Object value = literalValue(right);
     String valueString = value.toString();
@@ -209,7 +235,7 @@ private ConditionToken translateOp2(String op, String name,
   }
 
   /** Translates a call to a unary operator. */
-  private ConditionToken translateUnary(String op, RexCall call) {
+  private ConditionToken translateUnary(ConditionToken.Operator op, RexCall 
call) {
     final RexNode opNode = call.operands.get(0);
     @Nullable ConditionToken expression = translateUnary2(op, opNode);
 
@@ -221,7 +247,8 @@ private ConditionToken translateUnary(String op, RexCall 
call) {
   }
 
   /** Translates a call to a unary operator. Returns null on failure. */
-  private @Nullable ConditionToken translateUnary2(String op, RexNode opNode) {
+  private @Nullable ConditionToken translateUnary2(ConditionToken.Operator op,
+      RexNode opNode) {
     if (opNode.getKind() == SqlKind.INPUT_REF) {
       final RexInputRef inputRef = (RexInputRef) opNode;
       final String name = fieldNames.get(inputRef.getIndex());
diff --git 
a/arrow/src/main/java/org/apache/calcite/adapter/arrow/ConditionToken.java 
b/arrow/src/main/java/org/apache/calcite/adapter/arrow/ConditionToken.java
index 44d3facea7..c5b690840a 100644
--- a/arrow/src/main/java/org/apache/calcite/adapter/arrow/ConditionToken.java
+++ b/arrow/src/main/java/org/apache/calcite/adapter/arrow/ConditionToken.java
@@ -25,7 +25,7 @@
 import static java.util.Objects.requireNonNull;
 
 /**
- * A structured representation of a single Gandiva predicate condition.
+ * A structured representation of a single Arrow predicate condition.
  *
  * <p>A condition is either unary (e.g. {@code IS NULL}) or binary
  * (e.g. {@code =}, {@code <}). Unary conditions have a field name
@@ -36,11 +36,11 @@
  */
 class ConditionToken {
   final String fieldName;
-  final String operator;
+  final Operator operator;
   final @Nullable String value;
   final @Nullable String valueType;
 
-  private ConditionToken(String fieldName, String operator,
+  private ConditionToken(String fieldName, Operator operator,
       @Nullable String value, @Nullable String valueType) {
     this.fieldName = requireNonNull(fieldName, "fieldName");
     this.operator = requireNonNull(operator, "operator");
@@ -50,7 +50,7 @@ private ConditionToken(String fieldName, String operator,
 
   /** Creates a binary condition token
    * (e.g. {@code intField equal 12 integer}). */
-  static ConditionToken binary(String fieldName, String operator,
+  static ConditionToken binary(String fieldName, Operator operator,
       String value, String valueType) {
     return new ConditionToken(fieldName, operator,
         requireNonNull(value, "value"),
@@ -59,7 +59,7 @@ static ConditionToken binary(String fieldName, String 
operator,
 
   /** Creates a unary condition token
    * (e.g. {@code intField isnull}). */
-  static ConditionToken unary(String fieldName, String operator) {
+  static ConditionToken unary(String fieldName, Operator operator) {
     return new ConditionToken(fieldName, operator, null, null);
   }
 
@@ -76,22 +76,55 @@ boolean isBinary() {
    * binary conditions. */
   List<String> toTokenList() {
     if (isBinary()) {
-      return ImmutableList.of(fieldName, operator,
+      return ImmutableList.of(fieldName, operator.token,
           requireNonNull(value, "value"),
           requireNonNull(valueType, "valueType"));
     }
-    return ImmutableList.of(fieldName, operator);
+    return ImmutableList.of(fieldName, operator.token);
   }
 
   /** Creates a {@code ConditionToken} from a serialized string list. */
   static ConditionToken fromTokenList(List<String> tokens) {
     final int size = tokens.size();
     if (size == 4) {
-      return binary(tokens.get(0), tokens.get(1),
+      return binary(tokens.get(0), Operator.of(tokens.get(1)),
           tokens.get(2), tokens.get(3));
     } else if (size == 2) {
-      return unary(tokens.get(0), tokens.get(1));
+      return unary(tokens.get(0), Operator.of(tokens.get(1)));
     }
     throw new IllegalArgumentException("Invalid condition tokens: " + tokens);
   }
+
+  /** Operators supported by the Arrow adapter filter representation. */
+  enum Operator {
+    IS_NULL("isnull"),
+    IS_NOT_NULL("isnotnull"),
+    IS_TRUE("istrue"),
+    IS_FALSE("isfalse"),
+    IS_NOT_TRUE("isnottrue"),
+    IS_NOT_FALSE("isnotfalse"),
+    EQUAL("equal"),
+    NOT_EQUAL("not_equal"),
+    LESS_THAN("less_than"),
+    LESS_THAN_OR_EQUAL("less_than_or_equal_to"),
+    GREATER_THAN("greater_than"),
+    GREATER_THAN_OR_EQUAL("greater_than_or_equal_to"),
+    LIKE("like");
+
+    final String token;
+
+    Operator(String token) {
+      this.token = token;
+    }
+
+    static Operator of(String token) {
+      for (Operator operator : values()) {
+        if (operator.token.equals(token)) {
+          return operator;
+        }
+      }
+      throw new UnsupportedOperationException(
+          "Unsupported Arrow filter operator: " + token);
+    }
+  }
 }
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 275bdd0be7..8c9fda7f08 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
@@ -82,6 +82,11 @@ static void initializeArrowState(@TempDir Path sharedTempDir)
     arrowDataGenerator.writeArrowData(dataLocationFile);
     arrowDataGenerator.writeScottEmpData(arrowFilesDirectory);
 
+    File emptyBatchDataLocationFile =
+        arrowFilesDirectory.resolve("arrowemptybatch.arrow").toFile();
+    ArrowDataTest emptyBatchDataGenerator = new ArrowDataTest();
+    
emptyBatchDataGenerator.writeArrowDataWithEmptyBatch(emptyBatchDataLocationFile);
+
     File datatypeLocationFile = 
arrowFilesDirectory.resolve("arrowdatatype.arrow").toFile();
     ArrowDataTest arrowtypeDataGenerator = new ArrowDataTest();
     arrowtypeDataGenerator.writeArrowDataType(datatypeLocationFile);
@@ -260,6 +265,51 @@ static void initializeArrowState(@TempDir Path 
sharedTempDir)
         .explainContains(plan);
   }
 
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7580";>[CALCITE-7580]
+   * Remove Gandiva dependency from Arrow adapter</a>. */
+  @Test void testArrowProjectSkipsEmptyBatch() {
+    String sql = "select \"intField\", \"stringField\" from arrowemptybatch\n";
+    String result = "intField=0; stringField=0\n"
+        + "intField=1; stringField=1\n"
+        + "intField=2; stringField=2\n";
+
+    CalciteAssert.that()
+        .with(arrow)
+        .query(sql)
+        .returns(result);
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7580";>[CALCITE-7580]
+   * Remove Gandiva dependency from Arrow adapter</a>. */
+  @Test void testArrowFilterSkipsEmptyBatch() {
+    String sql = "select \"intField\", \"stringField\"\n"
+        + "from arrowemptybatch\n"
+        + "where \"intField\" > 0";
+    String result = "intField=1; stringField=1\n"
+        + "intField=2; stringField=2\n";
+
+    CalciteAssert.that()
+        .with(arrow)
+        .query(sql)
+        .returns(result);
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-7580";>[CALCITE-7580]
+   * Remove Gandiva dependency from Arrow adapter</a>. */
+  @Test void testArrowFilterSkipsEmptyBatchWithNoMatches() {
+    String sql = "select \"intField\", \"stringField\"\n"
+        + "from arrowemptybatch\n"
+        + "where \"intField\" < 0";
+
+    CalciteAssert.that()
+        .with(arrow)
+        .query(sql)
+        .returns("");
+  }
+
   @Test void testArrowProjectFieldsWithIntegerFilter() {
     String sql = "select \"intField\", \"stringField\"\n"
         + "from arrowdata\n"
diff --git 
a/arrow/src/test/java/org/apache/calcite/adapter/arrow/ArrowDataTest.java 
b/arrow/src/test/java/org/apache/calcite/adapter/arrow/ArrowDataTest.java
index a53cc1231a..4b9af441aa 100644
--- a/arrow/src/test/java/org/apache/calcite/adapter/arrow/ArrowDataTest.java
+++ b/arrow/src/test/java/org/apache/calcite/adapter/arrow/ArrowDataTest.java
@@ -301,6 +301,35 @@ public void writeArrowBinaryData(File file) throws 
IOException {
     fileOutputStream.close();
   }
 
+  public void writeArrowDataWithEmptyBatch(File file) throws IOException {
+    Schema arrowSchema = makeArrowSchema();
+    try (RootAllocator allocator = new RootAllocator(Integer.MAX_VALUE);
+         VectorSchemaRoot vectorSchemaRoot =
+             VectorSchemaRoot.create(arrowSchema, allocator);
+         FileOutputStream fileOutputStream = new FileOutputStream(file);
+         ArrowFileWriter arrowFileWriter =
+             new ArrowFileWriter(vectorSchemaRoot, null,
+                 fileOutputStream.getChannel())) {
+      arrowFileWriter.start();
+
+      vectorSchemaRoot.setRowCount(0);
+      for (Field field : vectorSchemaRoot.getSchema().getFields()) {
+        vectorSchemaRoot.getVector(field.getName()).setValueCount(0);
+      }
+      arrowFileWriter.writeBatch();
+
+      int rowCount = 3;
+      vectorSchemaRoot.setRowCount(rowCount);
+      intField(vectorSchemaRoot.getVector("intField"), rowCount);
+      varCharField(vectorSchemaRoot.getVector("stringField"), rowCount);
+      floatField(vectorSchemaRoot.getVector("floatField"), rowCount);
+      longField(vectorSchemaRoot.getVector("longField"), rowCount);
+      arrowFileWriter.writeBatch();
+
+      arrowFileWriter.end();
+    }
+  }
+
   public void writeArrowDataType(File file) throws IOException {
     FileOutputStream fileOutputStream = new FileOutputStream(file);
     Schema arrowSchema = makeArrowDateTypeSchema();
diff --git 
a/arrow/src/test/java/org/apache/calcite/adapter/arrow/ArrowExtension.java 
b/arrow/src/test/java/org/apache/calcite/adapter/arrow/ArrowExtension.java
index ab1f5c2a88..4600dab100 100644
--- a/arrow/src/test/java/org/apache/calcite/adapter/arrow/ArrowExtension.java
+++ b/arrow/src/test/java/org/apache/calcite/adapter/arrow/ArrowExtension.java
@@ -18,22 +18,12 @@
 
 import org.apache.calcite.config.CalciteSystemProperty;
 
-import org.apache.arrow.gandiva.evaluator.Projector;
-import org.apache.arrow.gandiva.exceptions.GandivaException;
-import org.apache.arrow.gandiva.expression.ExpressionTree;
-import org.apache.arrow.vector.types.pojo.Schema;
-
 import org.junit.jupiter.api.extension.ConditionEvaluationResult;
 import org.junit.jupiter.api.extension.ExecutionCondition;
 import org.junit.jupiter.api.extension.ExtensionContext;
 
-import java.util.ArrayList;
-import java.util.List;
-
 /**
  * JUnit5 extension to handle Arrow tests.
- *
- * <p>Tests will be skipped if the Gandiva library cannot be loaded on the 
given platform.
  */
 class ArrowExtension implements ExecutionCondition {
 
@@ -41,8 +31,7 @@ class ArrowExtension implements ExecutionCondition {
    * Whether to run this test.
    *
    * <p>Enabled by default, unless explicitly disabled from command line
-   * ({@code -Dcalcite.test.arrow=false}) or if Gandiva library, used to 
implement arrow
-   * filtering/projection, cannot be loaded.
+   * ({@code -Dcalcite.test.arrow=false}).
    *
    * @return {@code true} if the test is enabled and can run in the current 
environment,
    *         {@code false} otherwise
@@ -51,16 +40,6 @@ class ArrowExtension implements ExecutionCondition {
       final ExtensionContext context) {
 
     boolean enabled = CalciteSystemProperty.TEST_ARROW.value();
-    try {
-      Schema emptySchema = new Schema(new ArrayList<>(), null);
-      List<ExpressionTree> expressions = new ArrayList<>();
-      Projector.make(emptySchema, expressions);
-    } catch (GandivaException e) {
-      // this exception comes from using an empty expression,
-      // but the JNI library was loaded properly
-    } catch (UnsatisfiedLinkError e) {
-      enabled = false;
-    }
 
     if (enabled) {
       return ConditionEvaluationResult.enabled("Arrow tests enabled");
diff --git a/bom/build.gradle.kts b/bom/build.gradle.kts
index f00ed7d8e5..64cb532afd 100644
--- a/bom/build.gradle.kts
+++ b/bom/build.gradle.kts
@@ -101,7 +101,6 @@ fun DependencyConstraintHandlerScope.runtimev(
         apiv("org.apache.arrow:arrow-memory-netty", "arrow")
         apiv("org.apache.arrow:arrow-vector", "arrow")
         apiv("org.apache.arrow:arrow-jdbc", "arrow")
-        apiv("org.apache.arrow.gandiva:arrow-gandiva", "arrow-gandiva")
         apiv("org.apache.calcite.avatica:avatica-core", "calcite.avatica")
         apiv("org.apache.calcite.avatica:avatica-server", "calcite.avatica")
         apiv("org.apache.cassandra:cassandra-all")
diff --git a/gradle.properties b/gradle.properties
index eb0bd778ae..fd82318264 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -81,7 +81,6 @@ jandex.version=3.5.3
 # elasticsearch does not like asm:6.2.1+
 aggdesigner-algorithm.version=6.1
 apiguardian-api.version=1.1.2
-arrow-gandiva.version=15.0.0
 arrow.version=15.0.0
 asm.version=9.9.1
 byte-buddy.version=1.18.8
diff --git a/site/_docs/history.md b/site/_docs/history.md
index 15e2561d39..c72d6f78d9 100644
--- a/site/_docs/history.md
+++ b/site/_docs/history.md
@@ -49,6 +49,11 @@ ## <a 
href="https://github.com/apache/calcite/releases/tag/calcite-1.43.0";>1.43.
 #### Breaking Changes
 {: #breaking-1-43-0}
 
+* [<a 
href="https://issues.apache.org/jira/browse/CALCITE-7580";>CALCITE-7580</a>]
+  Remove Gandiva dependency from Arrow adapter. Arrow adapter projection and
+  filter evaluation now run in Java, and the `arrow-gandiva` dependency is no
+  longer included in the Arrow module or BOM.
+
 #### New features
 {: #new-features-1-43-0}
 

Reply via email to