cloud-fan commented on code in PR #59135:
URL: https://github.com/apache/spark/pull/59135#discussion_r4281264122
##########
docs/sql-migration-guide.md:
##########
@@ -48,6 +48,7 @@ license: |
- Since Spark 4.4,
`spark.sql.sources.v2.bucketing.partitionKeyOrdering.enabled` defaults to
`true`. A V2 scan that reports a keyed partitioning but no explicit ordering
now also reports itself sorted by its partition key expressions, because every
row of such a partition evaluates them to the same value. Partition transforms
such as `days(ts)` or `bucket(8, id)` are left out of that ordering. This lets
Spark drop a `Sort` it would otherwise place above the scan. An aggregate that
groups by a leading run of the other key expressions, in key order, may also be
planned as a sort aggregate rather than a hash aggregate. This is because
`spark.sql.execution.replaceHashWithSortAgg` keys off the reported ordering. To
restore the previous behavior, set
`spark.sql.sources.v2.bucketing.partitionKeyOrdering.enabled` to `false`.
- Since Spark 4.4,
`spark.sql.sources.v2.bucketing.preserveKeyOrderingOnCoalesce.enabled` defaults
to `true`. When `GroupPartitionsExec` merges several input partitions that
share one partition key value into a single output partition, it now keeps sort
orders over the partition key expressions in the ordering it reports, since
those expressions are constant within the merged partition. Sort orders over
partition transforms such as `days(ts)` or `bucket(8, id)` are left out. Orders
over other columns are still dropped, as the concatenation invalidates them,
and a join that reduced the partition keys onto a common key space (see
`spark.sql.sources.v2.bucketing.allowCompatibleTransforms.enabled`) reports no
order, since the merged partitions then share only the reduced key. This can
remove a `Sort` downstream of the merge. To restore the previous behavior, set
`spark.sql.sources.v2.bucketing.preserveKeyOrderingOnCoalesce.enabled` to
`false`.
- Since Spark 4.4, the value columns of `UNPIVOT` and `Dataset.unpivot` merge
the nested nullability (array element, map value, and struct field nullability)
of all unpivoted values. Previously, a value column took the nested nullability
of the first listed value, so a `NULL` array element, map value, or struct
field of a later value could be returned as a non-`NULL` value such as `0`, and
`IS NULL` on it returned `false`. A view created by an earlier Spark version
over such an `UNPIVOT` may have recorded the non-nullable nested type in its
schema and can fail to read; recreate it with `CREATE OR REPLACE VIEW`. To
restore the previous behavior, set
`spark.sql.legacy.unpivot.useFirstValueNestedNullability` to `true`.
+- Since Spark 4.4, `date_add`, `date_sub`, `dateadd`, and adding or
subtracting an integer number of days to or from a date (for example `date + 1`
or `date - 1`) fail with `DATETIME_OVERFLOW` when the number of days since
1970-01-01 in the result does not fit in an `INT`, regardless of
`spark.sql.ansi.enabled`. Previously, the result silently wrapped around; for
example, `date_add(DATE'2020-01-01', 2147483647)` returned a date in year
-5877591. `try_add` and `try_subtract` on dates now return `NULL` in this case
instead of the wrapped date.
Review Comment:
**Nit (P3):** This entry lists `date_add`, `date_sub`, `dateadd`, and `date
+ 1` / `date - 1`. The check lives in `DateAdd`, though, and
`WindowEvaluatorFactoryBase.createBoundOrdering` also builds `DateAdd(expr,
boundOffset)` for a `RANGE` frame ordered by a DATE column with an integer
offset. Take this query:
```sql
SELECT d, count(*) OVER (ORDER BY d RANGE BETWEEN CURRENT ROW AND 2147483647
FOLLOWING)
FROM VALUES (DATE'2020-01-01') AS t(d)
```
It used to compute a wrapped, wrong frame bound. It now fails with
`DATETIME_OVERFLOW`, regardless of `spark.sql.ansi.enabled`. Someone upgrading
wouldn't connect that failure to this entry, since they never wrote `date + n`
themselves. Could you add a clause for DATE-ordered RANGE frames with integer
offsets? Day-interval forms (`date + INTERVAL n DAY` and DAY-interval frame
offsets) don't need a mention. A DAY interval is at most 106751991 days, so
they can't overflow for dates in the documented range.
**Verification:**
- **Inspection:** The rendered migration entry explicitly mentions RANGE
window frames over DATE columns with integer offsets. Its remaining claims
still match the implementation: it fails only on Int day-count overflow,
regardless of ANSI mode, and try_add/try_subtract return NULL.
##########
sql/catalyst/src/test/scala/org/apache/spark/sql/catalyst/expressions/DateExpressionsSuite.scala:
##########
@@ -510,7 +510,20 @@ class DateExpressionsSuite extends SparkFunSuite with
ExpressionEvalHelper {
DateAdd(Literal(Date.valueOf("2016-02-28")), negativeIntLit), -15910)
checkConsistencyBetweenInterpretedAndCodegen(DateAdd, DateType, ByteType)
checkConsistencyBetweenInterpretedAndCodegen(DateAdd, DateType, ShortType)
- checkConsistencyBetweenInterpretedAndCodegen(DateAdd, DateType,
IntegerType)
+ checkConsistencyBetweenInterpretedAndCodegenAllowingException(DateAdd,
DateType, IntegerType)
+ }
+
+ test("SPARK-59859: date_add fails on overflow instead of wrapping around") {
Review Comment:
**Nit (P3):** These tests cover `DateAdd`/`DateSub` directly. The PR also
says `try_add`/`try_subtract` on a date now return NULL on overflow, and that
depends on a separate composition. `TryAdd`'s non-numeric branch builds
`TryEval(Add(..., EvalMode.ANSI))`, the resolver lowers the inner `Add` to
`DateAdd`, and `TryEval` turns the exception into NULL. No test runs that path
with an overflowing date, and the existing date cases in `try_arithmetic.sql`
don't overflow. That branch also carries `// TODO: support TRY eval mode on
datetime arithmetic expressions.`. If someone later drops the `TryEval` wrapper
while implementing it, `try_add(DATE'2020-01-01', 2147483647)` would start
throwing and no test would fail. A couple of cases next to the `-- Date +
Integer` block in `try_arithmetic.sql` would pin this down, for example
`try_add(date'2021-01-01', 2147483647)` and `try_subtract(date'1900-01-01',
2147483647)`. Note that `inputs/nonansi/try_arithmetic.sql` imports that file.
That me
ans `results/nonansi/try_arithmetic.sql.out.java21` also has to be
regenerated, on JDK 21. A JDK 17 run leaves it stale, and that has needed
follow-up commits for this file before.
**Verification:**
- **Behavior:** SQL-level try_add and try_subtract on a date with a day
count that overflows the Int range return NULL, while the existing
non-overflowing date cases keep their current results.
- **Regression:** The new cases distinguish the fixed behavior from both
failure modes: a wrapped date (the pre-PR behavior) and a propagated
DATETIME_OVERFLOW (lost TryEval wrapping).
- **Regression:** SQLQueryTestSuite passes for try_arithmetic.sql and
nonansi/try_arithmetic.sql on JDK 21 as well as JDK 17, which shows the .java21
golden file was regenerated with the new cases.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]