This is an automated email from the ASF dual-hosted git repository.
JingsongLi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git
The following commit(s) were added to refs/heads/master by this push:
new bc493b6cf2 [common] Range-check the digit guard in DateTimeUtils
(#9649)
bc493b6cf2 is described below
commit bc493b6cf29e9fa9017fc6b2f814dbbd7854f761
Author: YangJie <[email protected]>
AuthorDate: Fri Sep 11 03:09:08 2026 -0400
[common] Range-check the digit guard in DateTimeUtils (#9649)
---
.../org/apache/paimon/utils/DateTimeUtils.java | 26 +++++++++++++++++-----
.../org/apache/paimon/utils/DateTimeUtilsTest.java | 26 ++++++++++++++++++++++
2 files changed, 47 insertions(+), 5 deletions(-)
diff --git
a/paimon-common/src/main/java/org/apache/paimon/utils/DateTimeUtils.java
b/paimon-common/src/main/java/org/apache/paimon/utils/DateTimeUtils.java
index d202a5bc47..13f422e8fb 100644
--- a/paimon-common/src/main/java/org/apache/paimon/utils/DateTimeUtils.java
+++ b/paimon-common/src/main/java/org/apache/paimon/utils/DateTimeUtils.java
@@ -349,15 +349,31 @@ public class DateTimeUtils {
+ milli;
}
+ /**
+ * Whether the string is a non-negative decimal integer that fits in an
{@code int}. Callers
+ * hand the string straight to {@link Integer#parseInt}, so the range
matters as much as the
+ * characters.
+ */
private static boolean isInteger(String s) {
- boolean isInt = s.length() > 0;
+ if (s.isEmpty()) {
+ return false;
+ }
+ // Accumulate with overflow checking rather than a digit-count limit:
a zero-padded
+ // component such as "00000002024" is still in range, so what matters
is the value, not
+ // how many characters it took to write. Bailing out the moment the
running value passes
+ // Integer.MAX_VALUE keeps the accumulator itself from overflowing a
long.
+ long value = 0;
for (int i = 0; i < s.length(); i++) {
- if (s.charAt(i) < '0' || s.charAt(i) > '9') {
- isInt = false;
- break;
+ char c = s.charAt(i);
+ if (c < '0' || c > '9') {
+ return false;
+ }
+ value = value * 10 + (c - '0');
+ if (value > Integer.MAX_VALUE) {
+ return false;
}
}
- return isInt;
+ return true;
}
private static boolean isIllegalDate(int y, int m, int d) {
diff --git
a/paimon-common/src/test/java/org/apache/paimon/utils/DateTimeUtilsTest.java
b/paimon-common/src/test/java/org/apache/paimon/utils/DateTimeUtilsTest.java
index 6a0f88a367..a0c86be483 100644
--- a/paimon-common/src/test/java/org/apache/paimon/utils/DateTimeUtilsTest.java
+++ b/paimon-common/src/test/java/org/apache/paimon/utils/DateTimeUtilsTest.java
@@ -33,6 +33,32 @@ import static
org.assertj.core.api.Assertions.assertThatThrownBy;
/** Test for {@link DateTimeUtils}. */
public class DateTimeUtilsTest {
+ @Test
+ public void testParseDateAndTimeOverflowReturnsNull() {
+ // A component too large for an int is an invalid date or time, not a
crash: the
+ // contract of parseDate/parseTime is null for unparseable input.
2147483648 is
+ // Integer.MAX_VALUE + 1, the smallest ten-digit value that does not
fit.
+ assertThat(DateTimeUtils.parseDate("2147483648-01-01")).isNull();
+ assertThat(DateTimeUtils.parseDate("2147483647-01-01"))
+ .isNull(); // in range, but not a year
+ assertThat(DateTimeUtils.parseDate("99999999999-01-01")).isNull();
+ assertThat(DateTimeUtils.parseDate("2024-99999999999-01")).isNull();
+ assertThat(DateTimeUtils.parseDate("2024-01-99999999999")).isNull();
+ assertThat(DateTimeUtils.parseTime("99999999999:00:00")).isNull();
+
+ // Sanity: valid values still parse.
+ assertThat(DateTimeUtils.parseDate("2024-01-15")).isNotNull();
+ assertThat(DateTimeUtils.parseTime("12:30:00")).isNotNull();
+
+ // Zero-padded components whose value still fits an int must keep
parsing: the range
+ // guard has to judge the value, not the digit count, or previously
accepted padded
+ // dates/times would silently turn into NULL.
+ assertThat(DateTimeUtils.parseDate("00000002024-01-15"))
+ .isEqualTo(DateTimeUtils.parseDate("2024-01-15"));
+ assertThat(DateTimeUtils.parseTime("00000000012:30:00"))
+ .isEqualTo(DateTimeUtils.parseTime("12:30:00"));
+ }
+
@Test
public void testFormatLocalDateTime() {
LocalDateTime time = LocalDateTime.of(2023, 8, 30, 12, 30, 59,
999_999_999);