Skip to content

[AURON #2624] Support Spark's array_except natively - #2648

Open
zoddtheimmortal wants to merge 4 commits into
apache:masterfrom
zoddtheimmortal:array-except-native
Open

zoddtheimmortal wants to merge 4 commits into
apache:masterfrom
zoddtheimmortal:array-except-native

Conversation

@zoddtheimmortal

Copy link
Copy Markdown

Which issue does this PR close?

Closes #2624

Rationale for this change

Spark's array_except currently falls back to Spark.

What changes are included in this PR?

  • Convert Spark's ArrayExcept to a new native Spark_ArrayExcept function.
  • For each row, seed a hash set with the second array, then keep each element of the first array that the set has not seen. This preserves first-occurrence order and removes duplicates in one pass.
  • Time complexity: O(n + m) expected per row, where n and m are the two array lengths. Each element is hashed once. Only the visible value range of each list is row-encoded, so sliced inputs don't pay for hidden values.
  • Preserve Spark semantics:
    • null arrays give null;
    • a single null element is kept unless the second array contains null;
    • NaN equals NaN;
    • -0.0 and 0.0 stay distinct; Spark 4.2 normalizes them before this function.
  • Move the element-type check from ArrayDistinct into isSupportedSetArrayType, shared by both functions.
  • Move array_distinct's NaN canonicalization into canonicalize_nans, shared by both functions.
  • Add Rust tests for scalar and array inputs, slices, NaN and signed zero, field metadata, and invalid arguments, plus Spark result and native-plan checks.

Are there any user-facing changes?

array_except can execute natively for supported atomic element types. Nested element type keeps the fallback, and so does floating point arrays on Spark 3.0.

How was this patch tested?

cargo test -p datafusion-ext-functions --lib spark_array
cargo clippy -p datafusion-ext-functions --all-targets -- -D warnings
cargo fmt --all -- --check
./dev/reformat --check
./dev/mvn-build-helper/build-native.sh pre
./build/mvn -Ppre -Pspark-3.0 -Pscala-2.12 -pl spark-extension-shims-spark -am test -DskipBuildNative -DskipTests=false -DwildcardSuites=org.apache.auron.AuronFunctionSuite
./build/mvn -Ppre -Pspark-3.5 -Pscala-2.12 -pl spark-extension-shims-spark -am test -DskipBuildNative -DskipTests=false -DwildcardSuites=org.apache.auron.AuronFunctionSuite
./build/mvn -Ppre -Pspark-4.2 -Pscala-2.13 -pl spark-extension-shims-spark -am test -DskipBuildNative -DskipTests=false -DwildcardSuites=org.apache.auron.AuronFunctionSuite

All passed. AuronFunctionSuite: 97 succeeded, 0 failed on each Spark version.

Was this patch authored or co-authored using generative AI tooling?

  • Yes
  • No

Generated-by: Claude Opus 5.5 on GitHub Copilot CLI 1.0.105

ASF guidance: https://www.apache.org/legal/generative-tooling.html

Copilot AI balanced review requested due to automatic review settings October 31, 2026 19:14

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues block approval of the native array_except implementation.

No open issues 🎉

What changed in this PR

Adds native execution of Spark’s array_except in Auron, reducing fallback for supported atomic element types while preserving Spark’s ordering, deduplication, null, and floating-point semantics.

Changes:

  • Connects Spark expression conversion to the new native function, retaining fallback for unsupported types and Spark 3.0 floating-point arrays.
  • Implements hash-based array subtraction and shares type checks and NaN canonicalization with array_distinct.
  • Adds Rust unit tests and Spark result, native-plan, and fallback checks.
File Description
spark-extension/​src/​main/​scala/​org/​apache/​spark/​sql/​auron/​NativeConverters.scala Adds ArrayExcept conversion and shared set-array type checks.
spark-extension-shims-spark/​src/​test/​scala/​org/​apache/​auron/​AuronFunctionSuite.scala Tests Spark-compatible results, native execution, and fallback behavior.
native-engine/​datafusion-ext-functions/​src/​spark_array.rs Implements native array subtraction, extracts NaN handling, and adds unit tests.
native-engine/​datafusion-ext-functions/​src/​lib.rs Registers Spark_ArrayExcept in the native function dispatcher.

Review effort: Balanced

Copilot AI balanced review requested due to automatic review settings November 8, 2026 09:56

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines +41 to +57
// Spark canonicalizes NaNs but keeps signed zeros distinct before Spark 4.2's
// normalization.
fn canonicalize_nans(values: &ArrayRef) -> Result<ArrayRef> {
Ok(match values.data_type() {
DataType::Float32 => Arc::new(Float32Array::from_iter(
downcast_any!(values, Float32Array)?
.iter()
.map(|v| v.map(|v| if v.is_nan() { f32::NAN } else { v })),
)),
DataType::Float64 => Arc::new(Float64Array::from_iter(
downcast_any!(values, Float64Array)?
.iter()
.map(|v| v.map(|v| if v.is_nan() { f64::NAN } else { v })),
)),
_ => Arc::clone(values),
})
}
let mut offsets = Vec::with_capacity(num_rows + 1);
let mut valids = Vec::with_capacity(num_rows);
// Seeded with the right elements, so each left element needs one lookup.
let mut seen = HashSet::new();
if valid {
seen.clear();
let right_start = right.value_offsets()[r] as usize - right_base;
let right_end = right.value_offsets()[r + 1] as usize - right_base;
Comment on lines +120 to +123
private def isSupportedSetArrayType(dataType: DataType): Boolean = dataType match {
// Spark 3.0 has different NaN deduplication behavior.
case ArrayType(FloatType | DoubleType, _) =>
SparkVersionUtil.SPARK_RUNTIME_VERSION >= "3.1"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add native array_except support

2 participants