Skip to content

Commit 595a802

Browse files
committed
[SPARK-56046][SQL] Typed SPJ partition key Reducers
### What changes were proposed in this pull request? This PR adds a new method to SPJ partition key `Reducer`s to return the type of a reduced partition key. ### Why are the changes needed? After the [SPJ refactor](#54330) some Iceberg SPJ tests, that join a `hours` transform partitioned table with a `days` transform partitioned table, started to fail. This is because after the refactor the keys of a `KeyedPartitioning` partitioning are `InternalRowComparableWrapper`s, which include the type of the key, and when the partition keys are reduced the type of the reduced keys are inherited from their original type. - #54330 This means that when `hours` transformed hour keys are reduced to days, the keys actually remain having `IntegerType` type, while the `days` transformed keys have `DateType` type in Iceberg. This type difference causes that the left and right side `InternalRowComparableWrapper`s are not considered equal despite their `InternalRow` raw key data are equal. Before the refactor the type of (possibly reduced) partition keys were not stored in the partitioning. When the left and right side raw keys were compared in `EnsureRequirement` a common comparator was initialized with the type of the left side keys. So in the Iceberg SPJ tests the `IntegerType` keys were forced to be interpreted as `DateType`, or the `DateType` keys were forced to be interpreted as `IntegerType`, depending on the join order of the tables. The reason why this was not causing any issues is that the `PhysicalDataType` of both `DateType` and `IntegerType` logical types is `PhysicalIntegerType`. This PR introduces a new `resultType()` method of `Reducer` to return the correct type of the reduced keys and properly compares the left and right side reduced key types and thorws an error when they are not the same. ### Does this PR introduce _any_ user-facing change? Yes, the reduced key types are now properly compared and incompatibilities are reported to users. ### How was this patch tested? Added new UTs. ### Was this patch authored or co-authored using generative AI tooling? No. Closes #54884 from peter-toth/SPARK-56046-typed-spj-reducers. Authored-by: Peter Toth <peter.toth@gmail.com> Signed-off-by: Peter Toth <peter.toth@gmail.com>
1 parent 3b1d078 commit 595a802

10 files changed

Lines changed: 253 additions & 45 deletions

File tree

common/utils/src/main/resources/error/error-conditions.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6541,6 +6541,14 @@
65416541
],
65426542
"sqlState" : "42601"
65436543
},
6544+
"STORAGE_PARTITION_JOIN_INCOMPATIBLE_REDUCED_TYPES" : {
6545+
"message" : [
6546+
"Storage-partition join partition transforms produced incompatible reduced types,",
6547+
"left reducers: <leftReducers> returned: <leftReducedDataTypes>,",
6548+
"right reducers: <rightReducers> returned: <rightReducedDataTypes>."
6549+
],
6550+
"sqlState" : "42K09"
6551+
},
65446552
"STREAMING_CHECKPOINT_MISSING_METADATA_FILE" : {
65456553
"message" : [
65466554
"Checkpoint location <checkpointLocation> is in an inconsistent state: the metadata file is missing but offset and/or commit logs contain data. Please restore the metadata file or create a new checkpoint directory."

sql/catalyst/src/main/java/org/apache/spark/sql/connector/catalog/functions/Reducer.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.apache.spark.sql.connector.catalog.functions;
1818

1919
import org.apache.spark.annotation.Evolving;
20+
import org.apache.spark.sql.types.DataType;
2021

2122
/**
2223
* A 'reducer' for output of user-defined functions.
@@ -31,9 +32,10 @@
3132
* <li> More generally, there exists reducer functions r1(x) and r2(x) such that
3233
* r1(f_source(x)) = r2(f_target(x)) for all input x. </li>
3334
* </ul>
35+
* where = means both value and data type match.
3436
*
35-
* @param <I> reducer input type
36-
* @param <O> reducer output type
37+
* @param <I> the physical Java type of the input
38+
* @param <O> the physical Java type of the output
3739
* @since 4.0.0
3840
*/
3941
@Evolving
@@ -47,4 +49,11 @@ public interface Reducer<I, O> {
4749
default String displayName() {
4850
return getClass().getSimpleName();
4951
}
52+
53+
/**
54+
* Returns the {@link DataType data type} of values produced by this reducer.
55+
*
56+
* @return the data type of values produced by this reducer.
57+
*/
58+
DataType resultType();
5059
}

sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/plans/physical/partitioning.scala

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -465,10 +465,11 @@ case class KeyedPartitioning(
465465

466466
/**
467467
* Reduces this partitioning's partition keys by applying the given reducers.
468-
* Returns the distinct reduced keys.
468+
* Returns the reduced keys and their data types.
469469
*/
470-
def reduceKeys(reducers: Seq[Option[Reducer[_, _]]]): Seq[InternalRowComparableWrapper] =
471-
KeyedPartitioning.reduceKeys(partitionKeys, expressionDataTypes, reducers).distinct
470+
def reduceKeys(
471+
reducers: Seq[Option[Reducer[_, _]]]): (Seq[DataType], Seq[InternalRowComparableWrapper]) =
472+
KeyedPartitioning.reduceKeys(partitionKeys, expressionDataTypes, reducers)
472473

473474
override def satisfies0(required: Distribution): Boolean = {
474475
nonGroupedSatisfies(required) || groupedSatisfies(required)
@@ -586,17 +587,23 @@ object KeyedPartitioning {
586587
def reduceKeys(
587588
keys: Seq[InternalRowComparableWrapper],
588589
dataTypes: Seq[DataType],
589-
reducers: Seq[Option[Reducer[_, _]]]): Seq[InternalRowComparableWrapper] = {
590+
reducers: Seq[Option[Reducer[_, _]]]): (Seq[DataType], Seq[InternalRowComparableWrapper]) = {
591+
val reducedDataTypes = dataTypes.zip(reducers).map {
592+
case (_, Some(reducer: Reducer[Any, Any])) => reducer.resultType()
593+
case (t, _) => t
594+
}
590595
val comparableKeyWrapperFactory =
591-
InternalRowComparableWrapper.getInternalRowComparableWrapperFactory(dataTypes)
592-
keys.map { key =>
596+
InternalRowComparableWrapper.getInternalRowComparableWrapperFactory(reducedDataTypes)
597+
val reducedKeys = keys.map { key =>
593598
val keyValues = key.row.toSeq(dataTypes)
594599
val reducedKey = keyValues.zip(reducers).map {
595600
case (v, Some(reducer: Reducer[Any, Any])) => reducer.reduce(v)
596601
case (v, _) => v
597602
}.toArray
598603
comparableKeyWrapperFactory(new GenericInternalRow(reducedKey))
599604
}
605+
606+
(reducedDataTypes, reducedKeys)
600607
}
601608
}
602609

sql/catalyst/src/main/scala/org/apache/spark/sql/errors/QueryExecutionErrors.scala

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import org.apache.spark.sql.catalyst.trees.{Origin, TreeNode}
4747
import org.apache.spark.sql.catalyst.util.{sideBySide, CharsetProvider, DateTimeUtils, FailFastMode, IntervalUtils, MapData}
4848
import org.apache.spark.sql.connector.catalog.{CatalogNotFoundException, Table, TableProvider}
4949
import org.apache.spark.sql.connector.catalog.CatalogV2Implicits._
50+
import org.apache.spark.sql.connector.catalog.functions.Reducer
5051
import org.apache.spark.sql.connector.expressions.Transform
5152
import org.apache.spark.sql.internal.SQLConf
5253
import org.apache.spark.sql.internal.StaticSQLConf.GLOBAL_TEMP_DATABASE
@@ -3128,6 +3129,30 @@ private[sql] object QueryExecutionErrors extends QueryErrorsBase with ExecutionE
31283129
)
31293130
}
31303131

3132+
def storagePartitionJoinIncompatibleReducedTypesError(
3133+
leftReducers: Option[Seq[Option[Reducer[_, _]]]],
3134+
leftReducedDataTypes: Seq[DataType],
3135+
rightReducers: Option[Seq[Option[Reducer[_, _]]]],
3136+
rightReducedDataTypes: Seq[DataType]): Throwable = {
3137+
def reducersNames(reducers: Option[Seq[Option[Reducer[_, _]]]]) = {
3138+
reducers.toSeq.flatMap(_.map(_.map(_.displayName()).getOrElse("identity")))
3139+
.mkString("[", ", ", "]")
3140+
}
3141+
3142+
def dataTypeNames(dataTypes: Seq[DataType]) = {
3143+
dataTypes.map(toSQLType).mkString("[", ", ", "]")
3144+
}
3145+
3146+
new SparkException(
3147+
errorClass = "STORAGE_PARTITION_JOIN_INCOMPATIBLE_REDUCED_TYPES",
3148+
messageParameters = Map(
3149+
"leftReducers" -> reducersNames(leftReducers),
3150+
"leftReducedDataTypes" -> dataTypeNames(leftReducedDataTypes),
3151+
"rightReducers" -> reducersNames(rightReducers),
3152+
"rightReducedDataTypes" -> dataTypeNames(rightReducedDataTypes)),
3153+
cause = null)
3154+
}
3155+
31313156
def notAbsolutePathError(path: Path): SparkException = {
31323157
SparkException.internalError(s"$path is not absolute path.")
31333158
}

sql/catalyst/src/test/scala/org/apache/spark/sql/connector/catalog/InMemoryBaseTable.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -203,10 +203,10 @@ abstract class InMemoryBaseTable(
203203
case YearsTransform(ref) =>
204204
extractor(ref.fieldNames, cleanedSchema, row) match {
205205
case (days: Int, DateType) =>
206-
ChronoUnit.YEARS.between(EPOCH_LOCAL_DATE, DateTimeUtils.daysToLocalDate(days))
206+
ChronoUnit.YEARS.between(EPOCH_LOCAL_DATE, DateTimeUtils.daysToLocalDate(days)).toInt
207207
case (micros: Long, TimestampType) =>
208208
val localDate = DateTimeUtils.microsToInstant(micros).atZone(UTC).toLocalDate
209-
ChronoUnit.YEARS.between(EPOCH_LOCAL_DATE, localDate)
209+
ChronoUnit.YEARS.between(EPOCH_LOCAL_DATE, localDate).toInt
210210
case (v, t) =>
211211
throw new IllegalArgumentException(s"Match: unsupported argument(s) type - ($v, $t)")
212212
}
@@ -225,7 +225,7 @@ abstract class InMemoryBaseTable(
225225
case (days, DateType) =>
226226
days
227227
case (micros: Long, TimestampType) =>
228-
ChronoUnit.DAYS.between(Instant.EPOCH, DateTimeUtils.microsToInstant(micros))
228+
ChronoUnit.DAYS.between(Instant.EPOCH, DateTimeUtils.microsToInstant(micros)).toInt
229229
case (v, t) =>
230230
throw new IllegalArgumentException(s"Match: unsupported argument(s) type - ($v, $t)")
231231
}

sql/core/src/main/scala/org/apache/spark/sql/execution/datasources/v2/GroupPartitionsExec.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,15 +141,15 @@ case class GroupPartitionsExec(
141141
)(keyedPartitioning.projectKeys)
142142

143143
// Reduce keys if reducers are specified
144-
val reducedKeys = reducers.fold(projectedKeys)(
144+
val (reducedDataTypes, reducedKeys) = reducers.fold((projectedDataTypes, projectedKeys))(
145145
KeyedPartitioning.reduceKeys(projectedKeys, projectedDataTypes, _))
146146

147147
val keyToPartitionIndices = reducedKeys.zipWithIndex.groupMap(_._1)(_._2)
148148

149149
if (expectedPartitionKeys.isDefined) {
150150
alignToExpectedKeys(keyToPartitionIndices)
151151
} else {
152-
(groupAndSortByKeys(keyToPartitionIndices, projectedDataTypes), true)
152+
(groupAndSortByKeys(keyToPartitionIndices, reducedDataTypes), true)
153153
}
154154
}
155155

sql/core/src/main/scala/org/apache/spark/sql/execution/exchange/EnsureRequirements.scala

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import org.apache.spark.sql.catalyst.plans.physical._
2727
import org.apache.spark.sql.catalyst.rules.Rule
2828
import org.apache.spark.sql.catalyst.util.InternalRowComparableWrapper
2929
import org.apache.spark.sql.connector.catalog.functions.Reducer
30+
import org.apache.spark.sql.errors.QueryExecutionErrors
3031
import org.apache.spark.sql.execution._
3132
import org.apache.spark.sql.execution.datasources.v2.GroupPartitionsExec
3233
import org.apache.spark.sql.execution.joins.{ShuffledHashJoinExec, SortMergeJoinExec}
@@ -509,16 +510,24 @@ case class EnsureRequirements(
509510
// in case of compatible but not identical partition expressions, we apply 'reduce'
510511
// transforms to group one side's partitions as well as the common partition values
511512
val leftReducers = leftSpec.reducers(rightSpec)
512-
val leftReducedKeys =
513-
leftReducers.fold(leftPartitioning.partitionKeys)(leftPartitioning.reduceKeys)
514513
val rightReducers = rightSpec.reducers(leftSpec)
515-
val rightReducedKeys =
516-
rightReducers.fold(rightPartitioning.partitionKeys)(rightPartitioning.reduceKeys)
514+
val (leftReducedDataTypes, leftReducedKeys) = leftReducers.fold(
515+
(leftPartitioning.expressionDataTypes, leftPartitioning.partitionKeys)
516+
)(leftPartitioning.reduceKeys)
517+
val (rightReducedDataTypes, rightReducedKeys) = rightReducers.fold(
518+
(rightPartitioning.expressionDataTypes, rightPartitioning.partitionKeys)
519+
)(rightPartitioning.reduceKeys)
520+
if (leftReducedDataTypes != rightReducedDataTypes) {
521+
throw QueryExecutionErrors.storagePartitionJoinIncompatibleReducedTypesError(
522+
leftReducers = leftReducers,
523+
leftReducedDataTypes = leftReducedDataTypes,
524+
rightReducers = rightReducers,
525+
rightReducedDataTypes = rightReducedDataTypes)
526+
}
517527

518528
// merge values on both sides
519-
var mergedPartitionKeys =
520-
mergePartitions(leftReducedKeys, rightReducedKeys, joinType, leftPartitioning.keyOrdering)
521-
.map((_, 1))
529+
var mergedPartitionKeys = mergeAndDedupPartitions(leftReducedKeys, rightReducedKeys,
530+
joinType, leftPartitioning.keyOrdering).map((_, 1))
522531

523532
logInfo(log"After merging, there are " +
524533
log"${MDC(LogKeys.NUM_PARTITIONS, mergedPartitionKeys.size)} partitions")
@@ -752,36 +761,37 @@ case class EnsureRequirements(
752761
}
753762

754763
/**
755-
* Merge and sort partitions keys for SPJ and optionally enable partition filtering.
764+
* Merge, dedup and sort partitions keys for SPJ and optionally enable partition filtering.
756765
* Both sides must have matching partition expressions.
757766
* @param leftPartitionKeys left side partition keys
758767
* @param rightPartitionKeys right side partition keys
759768
* @param joinType join type for optional partition filtering
760-
* @keyOrdering ordering to sort partition keys
769+
* @param keyOrdering ordering to sort partition keys
761770
* @return merged and sorted partition values
762771
*/
763-
def mergePartitions(
772+
def mergeAndDedupPartitions(
764773
leftPartitionKeys: Seq[InternalRowComparableWrapper],
765774
rightPartitionKeys: Seq[InternalRowComparableWrapper],
766775
joinType: JoinType,
767776
keyOrdering: Ordering[InternalRowComparableWrapper]): Seq[InternalRowComparableWrapper] = {
768777
val merged = if (SQLConf.get.getConf(SQLConf.V2_BUCKETING_PARTITION_FILTER_ENABLED)) {
769778
joinType match {
770-
case Inner => mergePartitionKeys(leftPartitionKeys, rightPartitionKeys, intersect = true)
771-
case LeftOuter => leftPartitionKeys
772-
case RightOuter => rightPartitionKeys
773-
case _ => mergePartitionKeys(leftPartitionKeys, rightPartitionKeys)
779+
case Inner =>
780+
mergeAndDedupPartitionKeys(leftPartitionKeys, rightPartitionKeys, intersect = true)
781+
case LeftOuter => leftPartitionKeys.distinct
782+
case RightOuter => rightPartitionKeys.distinct
783+
case _ => mergeAndDedupPartitionKeys(leftPartitionKeys, rightPartitionKeys)
774784
}
775785
} else {
776-
mergePartitionKeys(leftPartitionKeys, rightPartitionKeys)
786+
mergeAndDedupPartitionKeys(leftPartitionKeys, rightPartitionKeys)
777787
}
778788

779789
// SPARK-41471: We keep to order of partitions to make sure the order of
780790
// partitions is deterministic in different case.
781791
merged.sorted(keyOrdering)
782792
}
783793

784-
private def mergePartitionKeys(
794+
private def mergeAndDedupPartitionKeys(
785795
leftPartitionKeys: Seq[InternalRowComparableWrapper],
786796
rightPartitionKeys: Seq[InternalRowComparableWrapper],
787797
intersect: Boolean = false) = {

sql/core/src/test/scala/org/apache/spark/sql/connector/KeyGroupedPartitioningSuite.scala

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ package org.apache.spark.sql.connector
1919
import java.sql.Timestamp
2020
import java.util.Collections
2121

22-
import org.apache.spark.SparkConf
22+
import org.apache.spark.{SparkConf, SparkException}
2323
import org.apache.spark.sql.{DataFrame, ExplainSuiteHelper, Row}
2424
import org.apache.spark.sql.catalyst.InternalRow
2525
import org.apache.spark.sql.catalyst.expressions.{Literal, TransformExpression}
@@ -75,6 +75,20 @@ class KeyGroupedPartitioningSuite extends DistributionAndOrderingSuiteBase with
7575
Column.create("dept_id", IntegerType),
7676
Column.create("data", StringType))
7777

78+
def withFunction[T](fn: UnboundFunction)(f: => T): T = {
79+
val id = Identifier.of(Array.empty, fn.name())
80+
val oldFn = Option.when(catalog.listFunctions(Array.empty).contains(id)) {
81+
val fn = catalog.loadFunction(id)
82+
catalog.dropFunction(id)
83+
fn
84+
}
85+
catalog.createFunction(id, fn)
86+
try f finally {
87+
catalog.dropFunction(id)
88+
oldFn.foreach(catalog.createFunction(id, _))
89+
}
90+
}
91+
7892
test("clustered distribution: output partitioning should be KeyedPartitioning") {
7993
val partitions: Array[Transform] = Array(Expressions.years("ts"))
8094

@@ -88,7 +102,7 @@ class KeyGroupedPartitioningSuite extends DistributionAndOrderingSuiteBase with
88102
var df = sql(s"SELECT count(*) FROM testcat.ns.$table GROUP BY ts")
89103
val catalystDistribution = physical.ClusteredDistribution(
90104
Seq(TransformExpression(YearsFunction, Seq(attr("ts")))))
91-
val partitionKeys = Seq(50L, 51L, 52L).map(v => InternalRow.fromSeq(Seq(v)))
105+
val partitionKeys = Seq(50, 51, 52).map(v => InternalRow.fromSeq(Seq(v)))
92106

93107
checkQueryPlan(df, catalystDistribution,
94108
physical.KeyedPartitioning(catalystDistribution.clustering, partitionKeys))
@@ -3385,4 +3399,83 @@ class KeyGroupedPartitioningSuite extends DistributionAndOrderingSuiteBase with
33853399
checkKeywordsExistsInExplain(df, FormattedMode, formattedKeyword)
33863400
}
33873401
}
3402+
3403+
test("SPARK-56046: Reducers with same result types") {
3404+
val items_partitions = Array(days("arrive_time"))
3405+
createTable(items, itemsColumns, items_partitions)
3406+
sql(s"INSERT INTO testcat.ns.$items VALUES " +
3407+
s"(0, 'aa', 39.0, cast('2020-01-01' as timestamp)), " +
3408+
s"(1, 'aa', 40.0, cast('2020-01-01' as timestamp)), " +
3409+
s"(2, 'bb', 41.0, cast('2021-01-03' as timestamp)), " +
3410+
s"(3, 'bb', 42.0, cast('2021-01-04' as timestamp))")
3411+
3412+
val purchases_partitions = Array(years("time"))
3413+
createTable(purchases, purchasesColumns, purchases_partitions)
3414+
sql(s"INSERT INTO testcat.ns.$purchases VALUES " +
3415+
s"(1, 42.0, cast('2020-01-01' as timestamp)), " +
3416+
s"(5, 44.0, cast('2020-01-15' as timestamp)), " +
3417+
s"(7, 46.5, cast('2021-02-08' as timestamp))")
3418+
3419+
withSQLConf(
3420+
SQLConf.V2_BUCKETING_PUSH_PART_VALUES_ENABLED.key -> "true",
3421+
SQLConf.V2_BUCKETING_ALLOW_COMPATIBLE_TRANSFORMS.key -> "true") {
3422+
Seq(
3423+
s"testcat.ns.$items i JOIN testcat.ns.$purchases p ON p.time = i.arrive_time",
3424+
s"testcat.ns.$purchases p JOIN testcat.ns.$items i ON i.arrive_time = p.time"
3425+
).foreach { joinString =>
3426+
val df = sql(
3427+
s"""
3428+
|${selectWithMergeJoinHint("i", "p")} id, item_id
3429+
|FROM $joinString
3430+
|ORDER BY id, item_id
3431+
|""".stripMargin)
3432+
3433+
val shuffles = collectShuffles(df.queryExecution.executedPlan)
3434+
assert(shuffles.isEmpty, "should not add shuffle for both sides of the join")
3435+
val groupPartitions = collectGroupPartitions(df.queryExecution.executedPlan)
3436+
assert(groupPartitions.forall(_.outputPartitioning.numPartitions == 2))
3437+
3438+
checkAnswer(df, Seq(Row(0, 1), Row(1, 1)))
3439+
}
3440+
}
3441+
}
3442+
3443+
test("SPARK-56046: Reducers with different result types") {
3444+
withFunction(UnboundDaysFunctionWithIncompatibleResultTypeReducer) {
3445+
val items_partitions = Array(days("arrive_time"))
3446+
createTable(items, itemsColumns, items_partitions)
3447+
sql(s"INSERT INTO testcat.ns.$items VALUES " +
3448+
s"(0, 'aa', 39.0, cast('2020-01-01' as timestamp)), " +
3449+
s"(1, 'aa', 40.0, cast('2020-01-01' as timestamp)), " +
3450+
s"(2, 'bb', 41.0, cast('2021-01-03' as timestamp)), " +
3451+
s"(3, 'bb', 42.0, cast('2021-01-04' as timestamp))")
3452+
3453+
val purchases_partitions = Array(years("time"))
3454+
createTable(purchases, purchasesColumns, purchases_partitions)
3455+
sql(s"INSERT INTO testcat.ns.$purchases VALUES " +
3456+
s"(1, 42.0, cast('2020-01-01' as timestamp)), " +
3457+
s"(5, 44.0, cast('2020-01-15' as timestamp)), " +
3458+
s"(7, 46.5, cast('2021-02-08' as timestamp))")
3459+
3460+
withSQLConf(
3461+
SQLConf.V2_BUCKETING_PUSH_PART_VALUES_ENABLED.key -> "true",
3462+
SQLConf.V2_BUCKETING_ALLOW_COMPATIBLE_TRANSFORMS.key -> "true") {
3463+
Seq(
3464+
s"testcat.ns.$items i JOIN testcat.ns.$purchases p ON p.time = i.arrive_time",
3465+
s"testcat.ns.$purchases p JOIN testcat.ns.$items i ON i.arrive_time = p.time"
3466+
).foreach { joinString =>
3467+
val e = intercept[SparkException] {
3468+
sql(
3469+
s"""
3470+
|${selectWithMergeJoinHint("i", "p")} id, item_id
3471+
|FROM $joinString
3472+
|ORDER BY id, item_id
3473+
|""".stripMargin).collect()
3474+
}
3475+
assert(e.getMessage.contains(
3476+
"Storage-partition join partition transforms produced incompatible reduced types"))
3477+
}
3478+
}
3479+
}
3480+
}
33883481
}

sql/core/src/test/scala/org/apache/spark/sql/connector/WriteDistributionAndOrderingSuite.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1174,7 +1174,7 @@ class WriteDistributionAndOrderingSuite extends DistributionAndOrderingSuiteBase
11741174
Invoke(
11751175
Literal.create(YearsFunction, ObjectType(YearsFunction.getClass)),
11761176
"invoke",
1177-
LongType,
1177+
IntegerType,
11781178
Seq(Cast(attr("day"), TimestampType, Some("America/Los_Angeles"))),
11791179
Seq(TimestampType),
11801180
propagateNull = false),

0 commit comments

Comments
 (0)