Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
d0f43af
[SPARK-50593][SQL] Generalize ReducibleFunction reducer API with Redu…
metanil May 6, 2026
c225cc1
[SPARK-50593][SQL] Support truncate transform for Storage Partitioned…
metanil May 7, 2026
f0d8088
[SPARK-50593][SQL] Strengthen test coverage for truncate SPJ and Redu…
metanil May 14, 2026
aeab763
[SPARK-50593][SQL] Harden reducer fallback and tighten SPJ supportsEx…
metanil May 22, 2026
721f576
[SPARK-50593][SQL] Make SPJ compatibility and reducer checks nested-t…
metanil Jun 3, 2026
9a70695
[SPARK-50593][SQL] Restore strict SPJ transform gate, keep recursive …
metanil Jun 4, 2026
28368ca
[SPARK-50593][SQL] Replace ReducibleParameters with V2 Literal[] in t…
metanil Jun 18, 2026
9d92888
[SPARK-50593][SQL] Reject non-scalar literal params in the reducer API
metanil Jun 19, 2026
7ea6de9
[SPARK-50593][SQL] Share childrenMatch helper for reducer layout chec…
metanil Jun 20, 2026
8614b01
[SPARK-50593][SQL] Skip SPJ reduction when a transform's literal para…
metanil Jun 29, 2026
551fe55
[SPARK-50593][SQL] Generalized-first SPJ reducer dispatch with param-…
metanil Jul 8, 2026
6ee8de8
Merge remote-tracking branch 'upstream/master' into SPARK-50593-trunc…
metanil Jul 9, 2026
607612f
[SPARK-50593][SQL] Merge upstream/master and generalize transform ide…
metanil Sep 22, 2026
a0ddf44
[SPARK-50593][SQL] Re-derive the ReducibleFunction version stamp for …
metanil Sep 21, 2026
af0fadb
[SPARK-50593][SQL] Avoid the literal string numBucketsOpt in test com…
metanil Sep 21, 2026
d593c36
[SPARK-50593][SQL] Re-merge upstream/master for SPARK-59688's allowRe…
metanil Sep 22, 2026
4187ba8
[SPARK-50593][SQL] Compare transform identity per argument position, …
metanil Sep 22, 2026
dc0afc9
[SPARK-50593][SQL] Preserve transform literal parameters when rewriti…
metanil Sep 22, 2026
7867a39
[SPARK-50593][SQL] Treat an unreadable argument type as not matching …
metanil Sep 23, 2026
fd7f630
[SPARK-50593][SQL] Resolve any NamedReference in a transform argument…
metanil Sep 23, 2026
b3f9328
[SPARK-50593][SQL] Derive isSameFunction and hasSameReducedKeys from …
metanil Sep 24, 2026
414f96b
[SPARK-50593][SQL] Give TransformExpression one reducer entry point; …
metanil Sep 24, 2026
a7dd4fe
[SPARK-50593][SQL] Share the identity-vs-transform decision and the c…
metanil Sep 24, 2026
e3b43f5
[SPARK-50593][SQL] Match master's inline allowReduce check in isExpre…
metanil Sep 24, 2026
0a2cd6f
[SPARK-50593][SQL] Trim the comments this PR adds
metanil Sep 25, 2026
994391d
[SPARK-50593][SQL] Document when Spark does not call the generalized …
metanil Sep 25, 2026
22cd902
[SPARK-50593][SQL] Offer the deprecated int reducer only a pair of th…
metanil Sep 25, 2026
18f6f8f
[SPARK-50593][SQL] Document the same-function condition for the depre…
metanil Sep 25, 2026
1ae0c48
[SPARK-50593][SQL] Never substitute a literal in alias projection
metanil Oct 1, 2026
03811b1
[SPARK-50593][SQL] Rename ArgumentShape.Column to ColumnRef
metanil Oct 1, 2026
24600bd
[SPARK-50593][SQL] Address review findings on null parameters, functi…
metanil Oct 1, 2026
05184de
[SPARK-50593][SQL] Note that the reducer defaults delegate; fix stale…
metanil Oct 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3048,6 +3048,11 @@
<placement>a</placement>
<head>Note:</head>
</tag>
<tag>
<name>implNote</name>
<placement>a</placement>
<head>Implementation Note:</head>
</tag>
<tag>
<name>group</name>
<placement>X</placement>
Expand Down
1 change: 1 addition & 0 deletions project/SparkBuild.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1815,6 +1815,7 @@ object Unidoc {
"-noqualifier", "java.lang",
"-tag", """example:a:Example\:""",
"-tag", """note:a:Note\:""",
"-tag", """implNote:a:Implementation Note\:""",
"-tag", "group:X",
"-tag", "tparam:X",
"-tag", "constructor:X",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.apache.spark.sql.connector.catalog.functions;

import org.apache.spark.annotation.Evolving;
import org.apache.spark.sql.connector.expressions.Literal;

/**
* Base class for user-defined functions that can be 'reduced' on another function.
Expand Down Expand Up @@ -60,6 +61,55 @@
@Evolving
public interface ReducibleFunction<I, O> {

/**
* Generic reducer for parameterized functions (bucket, truncate, etc.).
*
* If this function is 'reducible' on another function, return the {@link Reducer}.
* <p>
* Each parameter is a {@link Literal} carrying both its value and data type, such as a bucket
* count, a truncate width, or a {@code CalendarInterval}. {@link Literal#value()} is Spark's
* internal representation (e.g. {@code UTF8String} for strings, {@code Decimal} for decimals);
* use {@link Literal#dataType()} to interpret it rather than assuming a JVM type. Spark does not
* call this method for a pair where either side has a null literal, a literal of array, map,
* struct or UDT type, or one of a type other than the function declares; the join shuffles
* instead.
* <p>
* {@code thisParams} and {@code otherParams} hold each side's own literal parameters and may have
* different lengths -- for example a zero-parameter transform reducing onto a one-parameter one.
* Implementations must check each array's length before indexing into it.
* Spark only asks when the two transforms' arguments line up position by position, so
* {@code days(ts)} may be offered {@code truncate(ts, 3)} but never {@code bucket(4, ts)}.
* <p>
* Returning {@code null} means "not reducible for these parameters" and is authoritative:
* Spark consults no other overload. Dispatch order: Spark tries this generalized overload
* first; only if it is not implemented (throws {@link UnsupportedOperationException}), both
* sides are the same function, and each side has a single non-null integer parameter does Spark
* fall back to the deprecated {@code reducer(int, ReducibleFunction, int)} overload. If every
* eligible overload throws {@link UnsupportedOperationException}, Spark logs an "implements no
* reducer" warning; any other exception is logged and the pair is treated as not reducible (the
* join falls back to a shuffle).
* <p>
* Examples:
* <ul>
* <li>bucket(4, x) and bucket(2, x): thisParams = [4], otherParams = [2]</li>
* <li>truncate(x, 3) and truncate(x, 5): thisParams = [3], otherParams = [5]</li>
* <li>hypothetical range_bucket(x, 0L, 100L, 4): thisParams = [0L, 100L, 4]</li>
* </ul>
*
* @param thisParams literal parameters for this function (may differ in length from otherParams)
* @param otherFunction the other parameterized function
* @param otherParams literal parameters for the other function (may differ in length from
* thisParams)
* @return a reduction function if reducible, null otherwise
* @since 4.4.0
*/
default Reducer<I, O> reducer(
Literal<?>[] thisParams,
ReducibleFunction<?, ?> otherFunction,
Literal<?>[] otherParams) {
throw new UnsupportedOperationException();
}

/**
* This method is for the bucket function.
*
Expand All @@ -78,7 +128,12 @@ public interface ReducibleFunction<I, O> {
* @param otherBucketFunction the other parameterized function
* @param otherNumBuckets parameter for the other function
* @return a reduction function if it is reducible, null if not
* @deprecated as of 4.4.0. Please override
* {@link #reducer(Literal[], ReducibleFunction, Literal[])} instead.
* The new overload supports transforms with any number of parameters of any type
* (e.g. truncate width, multi-arg range buckets), not just a single int.
*/
@Deprecated(since = "4.4.0")
default Reducer<I, O> reducer(
int thisNumBuckets,
ReducibleFunction<?, ?> otherBucketFunction,
Expand All @@ -97,10 +152,14 @@ default Reducer<I, O> reducer(
* <li>otherFunction = hours</li>
* </ul>
*
*
* @param otherFunction the other function
* @return a reduction function if it is reducible, null if not.
* @implNote This default delegates to
* {@link #reducer(Literal[], ReducibleFunction, Literal[])}, so an implementation of that
* overload must not call back into this method.
*/
default Reducer<I, O> reducer(ReducibleFunction<?, ?> otherFunction) {
throw new UnsupportedOperationException();
return reducer(new Literal<?>[0], otherFunction, new Literal<?>[0]);

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.

Finding 9. With this delegation the two defaults can call each other. A connector migrating to the generalized overload writes the natural shim for its zero-parameter transforms:

  @Override
  public Reducer<I, O> reducer(
      Literal<?>[] thisParams, ReducibleFunction<?, ?> other, Literal<?>[] otherParams) {
    if (thisParams.length == 0 && otherParams.length == 0) {
      return reducer(other);   // keep the old no-parameter logic
    }
    ...
  }

reducer(other) is not overridden, so it lands back here, which calls the override again. TransformExpression.reducers calls reducer(otherFunction) for exactly the zero-parameter pair, so the recursion is reachable, and the resulting StackOverflowError is a VirtualMachineError: Try's NonFatal does not catch it, so it takes the driver thread down rather than costing a shuffle.

An @implNote is enough, since the direction is not guessable from the signatures:

   * @implNote This default delegates to
   *     {@link #reducer(Literal[], ReducibleFunction, Literal[])}, so an implementation of that
   *     overload must not call back into this method.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Ya, I wanted to avoid adding implementation detail in javadoc, but good idea to add was part of @implNote. Thanks.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

@peter-toth Just a note. Looks like Spark doesn't have @implNote build support. I'll add that one too in the build.
Let me know if we don't want to change build file in this PR.

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.

Thanks for flagging it. That's on me: I suggested the tag without checking that the build doesn't register it. I'd rather not introduce a new Javadoc tag for a single note, so please drop the build changes and keep it as a plain paragraph instead:

   * <p>
   * This default delegates to
   * {@link #reducer(Literal[], ReducibleFunction, Literal[])}, so an implementation of that
   * overload must not call back into this method.

}
}
Loading