map_funcs Expression Audits#
Audit notes for expressions in this category that have been audited. Absence of an entry means the expression has not been audited yet, not that it is unsupported. See the user guide Spark Expression Support for current support status.
element_at#
Spark 3.4.3 (audited 2026-05-27): identical to 3.5.8.
Spark 3.5.8 (audited 2026-05-27): baseline.
ElementAt(left, right, defaultValueOutOfBound, failOnError) extends GetMapValueUtil; the parser routeselement_at(<array>, ...)to one overload andelement_at(<map>, ...)to another. Comet routesMapTypeinput through the same nativemap_extractpath used byGetMapValue.Spark 4.0.1 (audited 2026-05-27): adds
nullIntolerant: Booleanfield; semantics unchanged.Spark 4.1.1 (audited 2026-05-27): identical to 4.0.1.
Performance (2026-10-20; #6525, #6845): values are still gathered with one Arrow
take, after which nested results pass through the compaction shared withlist_extract(native/spark-expr/src/nested_compaction.rs, from #6498).takereserves nested child buffers from the average candidate size, andMutableArrayDatazero-fills child validity bitmaps for the full reservation, so a selection of short or mostly NULL values can retain capacity sized from unselected values. Compaction shrinks only buffers holding more than four times their used size plus 64 bytes, and copies an oversized validity bitmap down to its used bits. For 8,192map<int,array<int>>rows with 128-element values and 75% NULL maps that keep their entries, the result shrinks from 4,228,100 to 1,079,300 bytes. In a three-round alternating local release Criterion run against main3355602e2(Arrow 59.3.0, DataFusion 55.2.0), comparing the median of each arm’s three round medians, unchanged flat-string controls moved within ±1.3%. The 70 nestedarray<string>andarray<boolean>leaf cases ranged from −2.7% to +5.3% of main, with 64 within ±3%. The shapes from review (one-element values, scalar key, 75% NULL maps with empty entry ranges) measured −0.5% for Boolean and +0.1% for string. When compaction does fire, it costs CPU: 14–23% on the 128-element, 75%-NULL-map shapes that release about 3 MB per batch, and 8–10% on deeply nested child-null shapes. This covers retained output buffers and expression runtime, not peak RSS. Benchmark:native/spark-expr/benches/map_extract.rs(map_extract_nestedandmap_extract_nested_leaf).
map_contains_key#
Spark 3.4.3 (audited 2026-05-27): identical to 3.5.8.
Spark 3.5.8 (audited 2026-05-27): baseline.
MapContainsKey(left, right) extends RuntimeReplaceable with InheritAnalysisRules; the analyzer rewrites toArrayContains(MapKeys(left), right). Comet routes viaCometMapContainsKeywhich emits the equivalentarray_has(map_keys(map), key).Spark 4.0.1 (audited 2026-05-27): semantics unchanged; minor trait refactors.
Spark 4.1.1 (audited 2026-05-27): identical to 4.0.1.
map_entries#
Spark 3.4.3 (audited 2026-05-27): identical to 3.5.8.
Spark 3.5.8 (audited 2026-05-27): baseline.
MapEntries(child)returns an array of structs<key, value>. Wired to nativemap_entries.Spark 4.0.1 (audited 2026-05-27): semantics unchanged.
Spark 4.1.1 (audited 2026-05-27): identical to 4.0.1.
map_from_arrays#
Spark 3.4.3 (audited 2026-05-27): identical to 3.5.8.
Spark 3.5.8 (audited 2026-05-27): baseline.
MapFromArrays(left, right) extends BinaryExpression with NullIntolerant; Spark usesArrayBasedMapBuilderto detect duplicate keys (subject tospark.sql.mapKeyDedupPolicy) and rejects null keys withRuntimeException("Cannot use null as map key"). CometCometMapFromArraysemits nestedCaseWhen(IsNotNull(left), CaseWhen(IsNotNull(right), map(left, right), null), null)guards so NULL-array inputs return NULL rather than triggering the previously reported native crash (#3327). The outer key guard preserves Spark’s left-before-right evaluation: oneIsNotNull(left) AND IsNotNull(right)predicate can still evaluate the values child for a NULL-key row in a mixed batch. The nativeSparkMapFromArrayswrapper also validates key/value lengths per non-null row before DataFusion constructs the map, so matching flattened totals cannot pair values across row boundaries.Spark 4.0.1 (audited 2026-05-27):
NullIntoleranttrait replaced bynullIntolerant: Boolean.ArrayBasedMapBuildergains thekeyNormalizerdescribed below.Spark 4.1.1 (audited 2026-05-27): identical to 4.0.1.
Floating-point keys (2026-10-14, #6549):
ArrayBasedMapBuilderfinds a duplicateFLOATorDOUBLEkey in aHashMapof boxed values (Double.equals, so NaNs with different bits are one key) and a duplicate struct or array key in aTreeMapin the SQL ordering (-0.0equals0.0). From Spark 4.0 itskeyNormalizeralso normalizes aFLOATorDOUBLEkey before the lookup, unlessspark.sql.legacy.disableMapKeyNormalizationis set. The native builders compare the keys’ bits, so a key type that holds a float at any depth isIncompatibleon every version (MapBuilderKeySupport).map_from_arraysroutes them through the codegen dispatcher.fromreturns its input arrays when no key repeats, so a lone-0.0key stays-0.0there.
map_from_entries#
Spark 3.4.3 (audited 2026-05-27): identical to 3.5.8.
Spark 3.5.8 (audited 2026-05-27): baseline.
MapFromEntries(child) extends UnaryExpression with NullIntolerant; expects an array of structs and produces a map. Wired asCometScalarFunction("map_from_entries").Spark 4.0.1 (audited 2026-05-27): semantics unchanged; trait refactor.
Spark 4.1.1 (audited 2026-05-27): identical to 4.0.1.
Known limitation: input arrays where the struct’s key or value type contains
BinaryTypeare markedIncompatibleand fall back unlessspark.comet.expression.MapFromEntries.allowIncompatible=true.Floating-point keys (2026-10-14, #6549):
ArrayBasedMapBuilderfinds a duplicateFLOATorDOUBLEkey in aHashMapof boxed values (Double.equals, so NaNs with different bits are one key) and a duplicate struct or array key in aTreeMapin the SQL ordering (-0.0equals0.0). From Spark 4.0 itskeyNormalizeralso normalizes aFLOATorDOUBLEkey before the lookup, unlessspark.sql.legacy.disableMapKeyNormalizationis set. The native builders compare the keys’ bits, so a key type that holds a float at any depth isIncompatibleon every version (MapBuilderKeySupport).map_from_entriesroutes them through the codegen dispatcher. It always callsbuild(), so on Spark 4.0+ it stores a-0.0key as0.0.
map_keys#
Spark 3.4.3 (audited 2026-05-27): identical to 3.5.8.
Spark 3.5.8 (audited 2026-05-27): baseline.
MapKeys(child)returns the map’s keys as an array. Wired to nativemap_keys.Spark 4.0.1 (audited 2026-05-27): semantics unchanged.
Spark 4.1.1 (audited 2026-05-27): identical to 4.0.1.
map_sort#
Performance (tuned locally 2026-09-13; PR #5901, related to #5900): reuse per-batch prefix-tuple sorting scratch for multi-entry
Utf8/Int32maps and bulk-fill all-empty offsets, preserving the singleton path from #5887. Against upstream including #5887, matched 2–10-entry forward normalization was about 3x faster; 2–50-entry maps improved 28–39% in the full run and 33–39% in independent paired confirmation. Benchmarks:native/spark-expr/benches/map_sort.rs,hash.rs, andcommon/matched_maps.rs.Performance (tuned locally 2026-09-12; PR #5887): skip Arrow sort dispatch for eligible flat singleton keys, with a batch check and specialized fallback loop for batches without singletons. In the local DataFusion 55.0.0 development cohort, matched singleton normalization measured 19–22x faster in the full run and 18.4x in an independent forward-order confirmation. Benchmarks:
native/spark-expr/benches/map_sort.rs,hash.rs, andcommon/matched_maps.rs; 92 cases cover normalization, hashing, combined execution, nulls, slices, mixed cardinalities, and long Unicode values. Flagged regressions did not remain stable through independent and reversed-order confirmation.
map_values#
Spark 3.4.3 (audited 2026-05-27): identical to 3.5.8.
Spark 3.5.8 (audited 2026-05-27): baseline.
MapValues(child)returns the map’s values as an array. Wired to nativemap_values.Spark 4.0.1 (audited 2026-05-27): semantics unchanged.
Spark 4.1.1 (audited 2026-05-27): identical to 4.0.1.
str_to_map#
Spark 3.4.3 (audited 2026-05-27): identical to 3.5.8.
Spark 3.5.8 (audited 2026-05-27): baseline.
StringToMap(text, pairDelim, keyValueDelim) extends TernaryExpression; splitstextonpairDelim, then each pair onkeyValueDelim(default","and":"). UsesArrayBasedMapBuilderfor duplicate-key handling. Wired asCometScalarFunction("str_to_map").Spark 4.0.1 (audited 2026-05-27):
inputTypeswidened toStringTypeNonCSAICollation; usesCollationAwareUTF8String.splitSQLwith acollationId. Runtime unchanged forUTF8_BINARY.Spark 4.1.1 (audited 2026-05-27): adds the
legacySplitTruncateflag (driven byspark.sql.legacy.truncateForEmptyRegexSplit) to bothsplitSQLcalls. The Comet native impl always behaves as if the flag were false, soCometStrToMapreads the config by string key and reportsIncompatiblewhen it is enabled; theCodegenDispatchFallbacktrait then routes the expression through the JVM codegen dispatcher rather than falling the whole projection back to Spark. Non-UTF8_BINARY collations on the input or the delimiters are handled the same way.