spark-test commented on code in PR #59955:
URL: https://github.com/apache/spark/pull/59955#discussion_r4465775753


##########
sql/catalyst/src/main/scala/org/apache/spark/sql/catalyst/util/DateTimeUtils.scala:
##########
@@ -1105,6 +1105,40 @@ object DateTimeUtils extends SparkDateTimeUtils {
     }
   }
 
+  /**
+   * Splits the seconds of a minute with a fraction of 6 digits (microseconds) 
into the whole
+   * seconds and the nanos of the second, as `LocalTime.of` and 
`LocalDateTime.of` take them,
+   * packed into a long, the seconds in the high 32 bits and the nanos in the 
low 32 bits, so
+   * that nothing is allocated per row: `(packed >> 32).toInt` and 
`packed.toInt`.
+   *
+   * The whole seconds are checked to be in the int range and not negative 
before they are
+   * narrowed. A value outside of the int range would overflow when converted 
from long to int,
+   * and could produce an int in the valid seconds range and return a wrong 
value, or a misleading
+   * error message. A negative value is rejected here for a better error 
message: `Math.floorDiv`
+   * gets the next lower integer for it, so the full seconds would not match 
the value given.
+   * The message is the one that `LocalTime.of` and `LocalDateTime.of` throw 
for invalid seconds,
+   * with the seconds given, truncated towards zero, or in full for a value 
between -1 and 0,
+   * which the truncation would show as the valid 0. Any other value, such as 
61 seconds, is
+   * left to them, which check their fields in order, so an invalid date or 
hour is reported
+   * before such seconds, while a negative value or one above `Int.MaxValue` 
is reported first.

Review Comment:
   **Nit (P3):** The order of the checks described here is not exact at two 
boundaries. The `makeTimestampMicros` Javadoc, the `makeTimestampMicros` 
comment that points here, and the order comment in the new 
`DateExpressionsSuite` test rely on the same description:
   
   - The split checks the floored whole seconds, not the value. 2147483647.5 
has whole seconds `Int.MaxValue`, so it passes the split, and 
`make_timestamp(2020, 13, 1, 0, 0, 2147483647.5)` reports `MonthOfYear` first 
even though the value is above `Int.MaxValue`. It takes the same path as the 
`2147483647` row that the new test pins.
   - `makeTimestampMicros` points here for the order of its checks, but it does 
not leave every other value to `LocalDateTime.of`. Its 60-second branch runs 
first, so `make_timestamp(2020, 13, 1, 0, 0, 60.5)` raises 
`INVALID_FRACTION_OF_SECOND` before the month is checked, which also 
contradicts the test comment's "any other out-of-range value after it".
   
   Both inputs already behave this way on the base branch, so only the new 
documentation is off. Still, the PR presents this order as a pinned contract, 
and the new invalid-month rows use only integer seconds, so neither boundary is 
exercised.
   
   One way to fix it: here, in the `makeTimestampMicros` Javadoc and in the 
test comment, say that the split reports seconds first only when they are 
negative or their whole seconds exceed `Int.MaxValue` (2147483648 or more). 
Wherever the order is described for `make_timestamp`, also mention the 
60-second branch: in the test comment, and in the `makeTimestampMicros` 
comments or this Scaladoc. A 2147483647.5 row with month 13 that expects the 
`MonthOfYear` message would pin the corrected boundary.
   
   This extends existing discussions 
[1](https://github.com/apache/spark/pull/59955#discussion_r4424175152), 
[2](https://github.com/apache/spark/pull/59955#discussion_r4424175162).
   
   **Verification:**
   - **Behavior:** With ANSI mode on, make_timestamp with month 13 and seconds 
2147483647.5 raises DATETIME_FIELD_OUT_OF_BOUNDS with the MonthOfYear message, 
while 2147483648 still reports SecondOfMinute first, on both the interpreted 
and the code-generated path, for TIMESTAMP with and without a time zone and for 
TIMESTAMP_NTZ. With ANSI mode off, both return NULL.
   - **Inspection:** Every comment that describes the order of the seconds 
checks speaks of whole seconds above Int.MaxValue rather than a value above it, 
and the make_timestamp description of that order mentions that a value between 
60 and 61 seconds is reported before the other fields. Apart from the added 
test row, only comments change.



-- 
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]

Reply via email to