This is an automated email from the ASF dual-hosted git repository.
ppa pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new 4c998d7bf04 IGNITE-25317 Sql. Improved Cast with Format validation
(#5977)
4c998d7bf04 is described below
commit 4c998d7bf043538ea42090f2ca4018f50978183d
Author: Max Zhuravkov <[email protected]>
AuthorDate: Thu Jun 12 10:44:58 2025 +0300
IGNITE-25317 Sql. Improved Cast with Format validation (#5977)
---
.../engine/datatypes/ItDateTimeCastFormatTest.java | 249 ++++++++-------------
.../sql/group1/cast/test_cast_format.test | 25 ++-
.../group1/types/date/test_incorrect_dates.test | 3 +-
.../types/timestamp/test_incorrect_timestamp.test | 3 +-
.../sql/engine/exec/exp/IgniteSqlFunctions.java | 48 +++-
.../sql/engine/exec/exp/RexToLixTranslator.java | 24 +-
.../internal/sql/engine/util/IgniteMethod.java | 17 +-
.../engine/exec/exp/IgniteSqlFunctionsTest.java | 63 +++++-
8 files changed, 233 insertions(+), 199 deletions(-)
diff --git
a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/ItDateTimeCastFormatTest.java
b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/ItDateTimeCastFormatTest.java
index aacf7077253..ab95de174e9 100644
---
a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/ItDateTimeCastFormatTest.java
+++
b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/ItDateTimeCastFormatTest.java
@@ -28,6 +28,7 @@ import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
+import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@@ -126,37 +127,32 @@ public class ItDateTimeCastFormatTest extends
BaseSqlIntegrationTest {
private static Stream<DateTimeArgs<LocalDate>> date() {
return Stream.of(
dateTime("2000-01-01", "yyyy-MM-dd", LocalDate.of(2000, 1, 1),
null),
- dateTime("2-01-01", "y-MM-dd", LocalDate.of(2, 1, 1), null),
- dateTime("02-01-01", "y-MM-dd", LocalDate.of(2002, 1, 1),
null),
+ dateTime("2-01-01", "y-MM-dd", LocalDate.of(2022, 1, 1), null),
+ dateTime("02-01-01", "y-MM-dd", null, "Invalid format.
Expected literal <-> but got"),
dateTime("20-01-01", "yy-MM-dd", LocalDate.of(2020, 1, 1),
null),
- dateTime("020-01-01", "yyy-MM-dd", LocalDate.of(20, 1, 1),
null),
- dateTime("002-01-01", "yyy-MM-dd", LocalDate.of(2, 1, 1),
null),
- dateTime("200-01-01", "yyy-MM-dd", LocalDate.of(200, 1, 1),
null),
+ dateTime("020-01-01", "yyy-MM-dd", LocalDate.of(2020, 1, 1),
null),
+ dateTime("002-01-01", "yyy-MM-dd", LocalDate.of(2002, 1, 1),
null),
+ dateTime("200-01-01", "yyy-MM-dd", LocalDate.of(2200, 1, 1),
null),
dateTime("20-01-01", "yyyy-MM-dd", LocalDate.of(20, 1, 1),
null),
dateTime("9999-01-01", "yyyy-MM-dd", LocalDate.of(9999, 1, 1),
null),
dateTime("2000/01-01", "yyyy/MM-dd", LocalDate.of(2000, 1, 1),
null),
- dateTime("10000-01-01", "yyyy-MM-dd", null, "DATE out of
range"),
- dateTime("10000000-01-01", "yyyy-MM-dd", null, "DATE out of
range"),
+ dateTime("10000-01-01", "yyyy-MM-dd", null, "Invalid format.
Expected literal <-> but got"),
+ dateTime("10000000-01-01", "yyyy-MM-dd", null, "Invalid
format. Expected literal <-> but got"),
- // TODO https://issues.apache.org/jira/browse/IGNITE-25339
Incorrect parsing of RR/RRRR
- /*
dateTime("01-01-01", "RR-MM-dd", LocalDate.of(2001, 1, 1),
null),
dateTime("33-01-01", "RR-MM-dd", LocalDate.of(2033, 1, 1),
null),
dateTime("49-01-01", "RR-MM-dd", LocalDate.of(2049, 1, 1),
null),
- */
+
dateTime("51-01-01", "RR-MM-dd", LocalDate.of(1951, 1, 1),
null),
dateTime("77-01-01", "RR-MM-dd", LocalDate.of(1977, 1, 1),
null),
- // TODO https://issues.apache.org/jira/browse/IGNITE-25339
Incorrect parsing of RR/RRRR
- /*
dateTime("01-01-01", "RRRR-MM-dd", LocalDate.of(2001, 1, 1),
null),
dateTime("33-01-01", "RRRR-MM-dd", LocalDate.of(2033, 1, 1),
null),
dateTime("49-01-01", "RRRR-MM-dd", LocalDate.of(2049, 1, 1),
null),
dateTime("51-01-01", "RRRR-MM-dd", LocalDate.of(1951, 1, 1),
null),
dateTime("77-01-01", "RRRR-MM-dd", LocalDate.of(1977, 1, 1),
null),
- */
dateTime("2001-01-01", "RRRR-MM-dd", LocalDate.of(2001, 1, 1),
null),
dateTime("2033-01-01", "RRRR-MM-dd", LocalDate.of(2033, 1, 1),
null),
@@ -164,43 +160,34 @@ public class ItDateTimeCastFormatTest extends
BaseSqlIntegrationTest {
dateTime("2051-01-01", "RRRR-MM-dd", LocalDate.of(2051, 1, 1),
null),
dateTime("1951-01-01", "RRRR-MM-dd", LocalDate.of(1951, 1, 1),
null),
- // TODO https://issues.apache.org/jira/browse/IGNITE-25339
Incorrect parsing of RR/RRRR
- /*
dateTime("001-01-01", "RRRR-MM-dd", LocalDate.of(2001, 1, 1),
null),
dateTime("1-01-01", "RRRR-MM-dd", LocalDate.of(2001, 1, 1),
null),
dateTime("33-01-01", "RRRR-MM-dd", LocalDate.of(2033, 1, 1),
null),
dateTime("033-01-01", "RRRR-MM-dd", LocalDate.of(2033, 1, 1),
null),
dateTime("51-01-01", "RRRR-MM-dd", LocalDate.of(1951, 1, 1),
null),
dateTime("051-01-01", "RRRR-MM-dd", LocalDate.of(1951, 1, 1),
null),
- dateTime("077-01-01", "RRRR-MM-dd", LocalDate.of(1973, 1, 1),
null),
- */
+ dateTime("077-01-01", "RRRR-MM-dd", LocalDate.of(1977, 1, 1),
null),
dateTime("151-01-01", "RRRR-MM-dd", LocalDate.of(151, 1, 1),
null),
- dateTime("177-01-01", "RRRR-MM-dd", LocalDate.of(177, 1, 1),
null)
- );
+ dateTime("177-01-01", "RRRR-MM-dd", LocalDate.of(177, 1, 1),
null),
- // TODO https://issues.apache.org/jira/browse/IGNITE-25319 parse
accepts incorrect year, month, day fields.
- /*
- dateTime("2000-05-07", "yyyy-MM-dddd", null, "Invalid format"),
- dateTime("2000-05-07", "yyyy-MM-dddd", null, "Invalid format"),
- dateTime("2000-5-07", "yyyyy-M-dd", null, "Invalid format"),
- dateTime("2000-005-07", "yyyyy-MMM-dddd", null, "Invalid format"),
+ dateTime("2000-05-07", "yyyy-MM-dddd", null, "Unexpected
element <D> in pattern"),
+ dateTime("2000-05-07", "yyyy-MM-dddd", null, "Unexpected
element <D> in pattern"),
+ dateTime("2000-5-07", "yyyyy-M-dd", null, "Element is already
present: YEAR"),
+ dateTime("2000-005-07", "yyyyy-MMM-dddd", null, "Element is
already present: YEAR"),
- dateTime("100-05-07", "R-MM-dd", null, "Invalid format"),
- dateTime("100-05-07", "RRR-MM-dd", null, "Invalid format"),
- dateTime("100-05-07", "RRRRR-MM-dd", null, "Invalid format"),
- dateTime("201-01-01", "RR-MM-dd", null, "Invalid format"),
+ dateTime("100-05-07", "R-MM-dd", null, "Unexpected element <R>
in pattern"),
+ dateTime("100-05-07", "RRR-MM-dd", null, "Unexpected element
<R> in pattern"),
+ dateTime("100-05-07", "RRRRR-MM-dd", null, "Unexpected element
<R> in pattern "),
+ dateTime("201-01-01", "RR-MM-dd", null, "Invalid format.
Expected literal <-> but got"),
- // Different error for in combination with ff/ff0 TIMESTAMP/ TIMESTAMP
LTZ: Illegal pattern character 'f'
- dateTime("2000-005-07", "yyyy-MMM-dd", null, "Invalid format")
- */
+ // Different error combination with ff/ff0 TIMESTAMP/
TIMESTAMP LTZ: Illegal pattern character 'f'
+ dateTime("2000-005-07", "yyyy-MMM-dd", null, "Unexpected
element <M> in pattern"),
- // TODO https://issues.apache.org/jira/browse/IGNITE-25010
- /*
- dateTime("0-0-0", "yyyy-MM-dd", null, "DATE out of range"),
- dateTime("0000-01-01", "yyyy-MM-dd", null, "DATE out of range"),
- dateTime("-01-01-01", "yyyy-MM-dd", null, "DATE out of range")
- */
+ dateTime("0-0-0", "yyyy-MM-dd", null, "Invalid value for
Year"),
+ dateTime("0000-01-01", "yyyy-MM-dd", null, "Invalid value for
Year"),
+ dateTime("-01-01-01", "yyyy-MM-dd", null, "Expected field YYYY
but got")
+ );
}
// TIME
@@ -208,24 +195,14 @@ public class ItDateTimeCastFormatTest extends
BaseSqlIntegrationTest {
@ParameterizedTest
@MethodSource("time")
public void timeLiterals(DateTimeArgs<LocalTime> args) {
- String sqlCast = format("SELECT CAST('{}' AS TIME FORMAT '{}')",
args.str, args.format);
-
- // TIME has 0 precision by default
- boolean subMillis = args.value != null && args.value.getNano() != 0 &&
args.format.contains("ff");
- Assumptions.assumeFalse(subMillis);
-
- checkQuery(sqlCast, args.value, args.error);
+ String sqlCast = format("SELECT CAST('{}' AS TIME(3) FORMAT '{}')",
args.str, args.format);
+ checkQuery(sqlCast, args.value, args.error);
}
@ParameterizedTest
@MethodSource("time")
public void timeDynamicParams(DateTimeArgs<LocalTime> args) {
- String sqlCast = format("SELECT CAST(? AS TIME FORMAT '{}')",
args.format);
-
- // TODO https://issues.apache.org/jira/browse/IGNITE-25313
- // Nothing but ff3 works due to SimpleDateFormat
- Assumptions.assumeTrue(!args.format.contains("ff") ||
args.format.contains("ff3"));
-
+ String sqlCast = format("SELECT CAST(? AS TIME(3) FORMAT '{}')",
args.format);
checkQuery(sqlCast, args.value, args.error, args.str);
}
@@ -233,16 +210,12 @@ public class ItDateTimeCastFormatTest extends
BaseSqlIntegrationTest {
@MethodSource("time")
public void timeUpdateFromLiteral(DateTimeArgs<LocalTime> args) {
String sqlCast = format(
- "UPDATE datetime_cols SET time0_col=CAST(? AS TIME FORMAT
'{}') WHERE id = 1",
+ "UPDATE datetime_cols SET time0_col=CAST(? AS TIME(4) FORMAT
'{}') WHERE id = 1",
args.format
);
checkDml(sqlCast, args.error, args.str);
- // TODO https://issues.apache.org/jira/browse/IGNITE-25313
- // Nothing but ff3 works due to SimpleDateFormat
- Assumptions.assumeTrue(!args.format.contains("ff") ||
args.format.contains("ff3"));
-
if (args.value != null) {
assertQuery("SELECT time0_col FROM datetime_cols WHERE id = 1")
.returns(args.value)
@@ -254,12 +227,13 @@ public class ItDateTimeCastFormatTest extends
BaseSqlIntegrationTest {
@MethodSource("time")
public void timeUpdateFromDynamicParam(DateTimeArgs<LocalTime> args) {
String sqlCast = format(
- "UPDATE datetime_cols SET time0_col=CAST('{}' AS TIME FORMAT
'{}') WHERE id = 1",
+ "UPDATE datetime_cols SET time0_col=CAST('{}' AS TIME(3)
FORMAT '{}') WHERE id = 1",
args.str,
args.format
);
- // TODO https://issues.apache.org/jira/browse/IGNITE-25045
Insert/Update RexToLix cast translation bug
+ // TODO https://issues.apache.org/jira/browse/IGNITE-25045
+ // Cast to TIME/TIMESTAMP/TIMESTAMP_LTZ ignores target type's precision
Assumptions.assumeTrue(args.value == null || args.value.getNano() ==
0);
checkDml(sqlCast, args.error);
@@ -273,19 +247,15 @@ public class ItDateTimeCastFormatTest extends
BaseSqlIntegrationTest {
private static Stream<DateTimeArgs<LocalTime>> time() {
return Stream.of(
- // TODO https://issues.apache.org/jira/browse/IGNITE-25314
- // Sql. Cast FORMAT. Parsing of hh/h12 format is incorrect
- /*
dateTime("05:02 a.m.", "hh12:mi a.m.", LocalTime.of(5, 2),
null),
dateTime("11:02 a.m.", "hh12:mi a.m.", LocalTime.of(11, 2),
null),
dateTime("12:02 a.m.", "hh12:mi a.m.", LocalTime.of(0, 2),
null),
- dateTime("13:02 a.m.", "hh12:mi a.m.", null, "TIME out of
range"),
+ dateTime("13:02 a.m.", "hh12:mi a.m.", null, "Invalid value
for HourAmPm"),
dateTime("05:02 p.m.", "hh12:mi p.m.", LocalTime.of(17, 2),
null),
dateTime("11:02 p.m.", "hh12:mi p.m.", LocalTime.of(23, 2),
null),
dateTime("12:02 p.m.", "hh12:mi p.m.", LocalTime.of(12, 2),
null),
- dateTime("13:02 p.m.", "hh12:mi p.m.", null, "TIME out of
range"),
- */
+ dateTime("13:02 p.m.", "hh12:mi p.m.", null, "Invalid value
for HourAmPm"),
// hh24
dateTime("12:02:03", "hh24:mi:ss", LocalTime.of(12, 2, 3),
null),
@@ -297,38 +267,29 @@ public class ItDateTimeCastFormatTest extends
BaseSqlIntegrationTest {
dateTime("23:02:03.999", "hh24:mi:ss.ff3", LocalTime.of(23, 2,
3, 999_000_000), null),
dateTime("23:02:03.123", "hh24:mi:ss.ff3", LocalTime.of(23, 2,
3, 123_000_000), null),
- // TODO https://issues.apache.org/jira/browse/IGNITE-25317
Sub-millsecond fractional format is broken (SimpleDateFormat)
- /*
- dateTime("23:02:03.1234", "hh24:mi:ss.ff3", LocalTime.of(23,
2, 3, 123_400_000), null),
- dateTime("23:02:03.1234", "hh24:mi:ss.ff4", LocalTime.of(23,
2, 3, 123_400_000), null),
- */
-
- // TODO https://issues.apache.org/jira/browse/IGNITE-25313
(SimpleDateFormat)
- /*
- dateTime("24:02:03", "hh24:mi:ss", null, "TIME out of range"),
- dateTime("123:02:03", "hh24:mi:ss", null, "TIME out of range"),
- dateTime("23:60:03", "hh24:mi:ss", null, "TIME out of range"),
- dateTime("23:123:03", "hh24:mi:ss", null, "TIME out of range"),
- dateTime("23:02:60", "hh24:mi:ss", null, "TIME out of range"),
- dateTime("23:02:123", "hh24:mi:ss", null, "TIME out of range"),
- */
-
- dateTime("22:02:03", "hX:mi:ss", null, "Invalid format"),
- dateTime("22:02:03", "hh:mX:ss", null, "Invalid format"),
- dateTime("22:02:03", "hh:mm:sX", null, "Invalid format"),
-
- dateTime("22:02:03", "hh:mm:ss.ff", null, "Illegal pattern
character 'f'"),
- dateTime("22:02:03", "hh:mm:ss.ff0", null, "Illegal pattern
character 'f'"),
- dateTime("22:02:03", "hh:mm:ss.ff10", null, "Invalid format"),
-
- dateTime("23:02:03.123", "hh24:mi:ss", null, "Invalid format")
- );
+ dateTime("23:02:03.1234", "hh24:mi:ss.ff3", null, "Unexpected
trailing characters after field FF3"),
+ dateTime("23:02:03.1234", "hh24:mi:ss.ff4", LocalTime.of(23,
2, 3, 123_000_000), null),
+
+ dateTime("24:02:03", "hh24:mi:ss", null, "Invalid value for
HourOfDay"),
+ dateTime("123:02:03", "hh24:mi:ss", null, "Invalid format.
Expected literal <:> but got <3>"),
+ dateTime("23:60:03", "hh24:mi:ss", null, "Invalid value for
MinuteOfHour"),
+ dateTime("23:123:03", "hh24:mi:ss", null, "Invalid format.
Expected literal <:> but got <3>"),
+ dateTime("23:02:60", "hh24:mi:ss", null, "Invalid value for
SecondOfMinute"),
+ dateTime("23:02:123", "hh24:mi:ss", null, "Unexpected trailing
characters after field SS"),
- // TODO https://issues.apache.org/jira/browse/IGNITE-25315 Max length
of a fractional part is ignored
- /*
- dateTime("23:02:03.12", "hh24:mi:ss.ff3", null, "Invalid format"),
- dateTime("23:02:03.1234", "hh24:mi:ss.ff3", null, "Invalid format")
- */
+ dateTime("22:02:03", "hX:mi:ss", null, "Unexpected element
<HX> in pattern"),
+ dateTime("22:02:03", "hh:mX:ss", null, "Unexpected element
<MX> in pattern"),
+ dateTime("22:02:03", "hh:mi:sX", null, "Unexpected element
<SX> in pattern"),
+ dateTime("22:02:03", "hh:mm:ss", null, "Illegal field <MM> for
format TIME"),
+
+ dateTime("22:02:03", "hh:mi:ss.ff", null, "Unexpected element
<FF> in pattern"),
+ dateTime("22:02:03", "hh:mi:ss.ff0", null, "Unexpected element
<FF0> in pattern"),
+ dateTime("22:02:03", "hh:mi:ss.ff10", null, "Unexpected
character <0> in pattern"),
+
+ dateTime("23:02:03.123", "hh24:mi:ss", null, "Unexpected
trailing characters after field SS"),
+ dateTime("23:02:03.12", "hh24:mi:ss.ff3", LocalTime.of(23, 2,
3, 120_000_000), null),
+ dateTime("23:02:03.1234", "hh24:mi:ss.ff3", null, "Unexpected
trailing characters after field FF3")
+ );
}
@ParameterizedTest
@@ -336,7 +297,7 @@ public class ItDateTimeCastFormatTest extends
BaseSqlIntegrationTest {
public void timeWithPrecisionLiterals(int precision,
DateTimeArgs<LocalTime> args) {
String sqlCast = format("SELECT CAST('{}' AS TIME({}) FORMAT '{}')",
args.str, precision, args.format);
- checkQuery(sqlCast, args.value, args.error);
+ checkQuery(sqlCast, args.value, args.error);
}
// Cast to TIME/TIMESTAMP/TIMESTAMP_LTZ ignores target type's precision
@@ -355,7 +316,7 @@ public class ItDateTimeCastFormatTest extends
BaseSqlIntegrationTest {
String col = format("time{}_col", precision);
String sqlCast = format(
- "UPDATE datetime_cols SET {}=CAST(? AS TIME FORMAT '{}') WHERE
id = 1",
+ "UPDATE datetime_cols SET {}=CAST(? AS TIME(3) FORMAT '{}')
WHERE id = 1",
col, args.format
);
@@ -377,16 +338,17 @@ public class ItDateTimeCastFormatTest extends
BaseSqlIntegrationTest {
public void timeWithPrecisionUpdateFromDynamicParam(int precision,
DateTimeArgs<LocalTime> args) {
String col = format("time{}_col", precision);
+ // Use TIME(3) to preserve fractional part
String sqlCast = format(
- "UPDATE datetime_cols SET {}=CAST('{}' AS TIME FORMAT '{}')
WHERE id = 1",
+ "UPDATE datetime_cols SET {}=CAST('{}' AS TIME(3) FORMAT '{}')
WHERE id = 1",
col,
args.str,
args.format
);
- // TODO https://issues.apache.org/jira/browse/IGNITE-25313 confusing
submillis parsing
- boolean subMillis = args.value != null && args.value.getNano() != 0 &&
args.format.contains("ff");
- Assumptions.assumeFalse(subMillis);
+ // TODO https://issues.apache.org/jira/browse/IGNITE-25045
+ // Cast to TIME/TIMESTAMP/TIMESTAMP_LTZ ignores target type's precision
+ Assumptions.assumeTrue(precision == 3);
checkDml(sqlCast, args.error);
@@ -399,7 +361,6 @@ public class ItDateTimeCastFormatTest extends
BaseSqlIntegrationTest {
private static Stream<Arguments> timeWithPrecision() {
return Stream.of(
- /* TODO https://issues.apache.org/jira/browse/IGNITE-25313
CAST FORMAT confusing behaviour when translating milliseconds
// FF1
Arguments.of(0,
@@ -421,7 +382,6 @@ public class ItDateTimeCastFormatTest extends
BaseSqlIntegrationTest {
dateTime("15:32:17.12", "hh24:mi:ss.ff2",
LocalTime.of(15, 32, 17).withNano(120_000_000), null)),
Arguments.of(3,
dateTime("15:32:17.12", "hh24:mi:ss.ff2",
LocalTime.of(15, 32, 17).withNano(120_000_000), null)),
- */
// FF3
Arguments.of(0,
@@ -451,7 +411,7 @@ public class ItDateTimeCastFormatTest extends
BaseSqlIntegrationTest {
dateTime("15:32:17.500", "hh24:mi:ss.ff3",
LocalTime.of(15, 32, 17).withNano(500_000_000), null)),
Arguments.of(3,
dateTime("15:32:17.999", "hh24:mi:ss.ff3",
LocalTime.of(15, 32, 17).withNano(999_000_000), null))
- );
+ );
}
// TIMESTAMP
@@ -461,10 +421,6 @@ public class ItDateTimeCastFormatTest extends
BaseSqlIntegrationTest {
public void timestampLiterals(DateTimeArgs<LocalDateTime> args) {
String sqlCast = format("SELECT CAST('{}' AS TIMESTAMP FORMAT '{}')",
args.str, args.format);
- // TODO https://issues.apache.org/jira/browse/IGNITE-25313 confusing
submills parsing
- boolean subMillis = args.value != null && args.value.getNano() != 0 &&
args.format.contains("ff");
- Assumptions.assumeFalse(subMillis);
-
checkQuery(sqlCast, args.value, args.error);
}
@@ -482,22 +438,19 @@ public class ItDateTimeCastFormatTest extends
BaseSqlIntegrationTest {
if (d.value != null && t.value != null) {
LocalDateTime tsExpected = LocalDateTime.of(d.value,
t.value);
result.add(dateTime(tsStr, tsFmt, tsExpected, null));
- } else if (isFormatError(d.error)) {
- result.add(dateTime(tsStr, tsFmt, null, d.error));
- } else if (isFormatError(t.error)) {
- result.add(dateTime(tsStr, tsFmt, null, t.error));
} else {
- // TIMESTAMP out of range
- result.add(dateTime(tsStr, tsFmt, null, "TIMESTAMP out of
range"));
+ result.add(dateTime(tsStr, tsFmt, null, " "));
}
}
}
- return result.stream();
- }
+ result.add(new DateTimeArgs<>("2025-10-02 22:15 +02:30", "YYYY-MM-DD
HH24:MI TZH:TZM",
+ LocalDateTime.of(2025, 10, 2, 19, 45), null));
- private static boolean isFormatError(@Nullable String message) {
- return message != null && (message.contains("Invalid format") ||
message.contains("Illegal pattern character"));
+ result.add(new DateTimeArgs<>("2025-10-02 22:15 -02:30", "YYYY-MM-DD
HH24:MI TZH:TZM",
+ LocalDateTime.of(2025, 10, 3, 0, 45), null));
+
+ return result.stream();
}
@ParameterizedTest
@@ -508,14 +461,17 @@ public class ItDateTimeCastFormatTest extends
BaseSqlIntegrationTest {
args.format
);
- // TODO https://issues.apache.org/jira/browse/IGNITE-25313 confusing
submills parsing
- Assumptions.assumeTrue(args.value == null ||
!args.format.contains("ff2"));
+ // TODO https://issues.apache.org/jira/browse/IGNITE-25045
+ // Cast to TIME/TIMESTAMP/TIMESTAMP_LTZ ignores target type's precision
+ boolean subMillis = args.value != null && args.value.getNano() != 0 &&
args.format.contains("ff");
+ Assumptions.assumeFalse(subMillis);
checkDml(sqlCast, args.error, args.str);
if (args.value != null) {
assertQuery("SELECT timestamp0_col FROM datetime_cols WHERE id =
1")
- .returns(args.value)
+ // We are writing to timestamp_0, so we should expect a
timestamp w/o a fractional part.
+ .returns(args.value.withNano(0))
.check();
}
}
@@ -528,17 +484,12 @@ public class ItDateTimeCastFormatTest extends
BaseSqlIntegrationTest {
args.str, args.format
);
- // TODO https://issues.apache.org/jira/browse/IGNITE-25313
- // Nothing but ff3 works due to SimpleDateFormat
- Assumptions.assumeTrue(!args.format.contains("ff"));
- // TODO https://issues.apache.org/jira/browse/IGNITE-24889
- // FF3 does not work due to cast transaction error
-
checkDml(sqlCast, args.error);
if (args.value != null) {
assertQuery("SELECT timestamp0_col FROM datetime_cols WHERE id =
1")
- .returns(args.value)
+ // We are writing to timestamp_0, so we should expect a
timestamp w/o a fractional part.
+ .returns(args.value.withNano(0))
.check();
}
}
@@ -551,9 +502,8 @@ public class ItDateTimeCastFormatTest extends
BaseSqlIntegrationTest {
checkQuery(sqlCast, args.value, args.error);
}
- // TODO https://issues.apache.org/jira/browse/IGNITE-25313
SimpleDateFormat problems
- // Cast translation errors
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-24889")
+ // Cast to TIME/TIMESTAMP/TIMESTAMP_LTZ ignores target type's precision
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-25045")
@ParameterizedTest
@MethodSource("timestampWithPrecision")
public void timestampWithPrecisionUpdateFromLiteral(int precision,
DateTimeArgs<LocalDateTime> args) {
@@ -595,7 +545,6 @@ public class ItDateTimeCastFormatTest extends
BaseSqlIntegrationTest {
private static Stream<Arguments> timestampWithPrecision() {
LocalDate date = LocalDate.of(2020, 2, 5);
return Stream.of(
- /* TODO https://issues.apache.org/jira/browse/IGNITE-25313
CAST FORMAT confusing behaviour when translating milliseconds
// FF1
Arguments.of(0,
dateTime("2020-02-05 15:32:17.1", "yyyy-MM-dd
hh24:mi:ss.ff1",
@@ -625,8 +574,6 @@ public class ItDateTimeCastFormatTest extends
BaseSqlIntegrationTest {
dateTime("2020-02-05 15:32:17.12", "yyyy-MM-dd
hh24:mi:ss.ff2",
LocalDateTime.of(date, LocalTime.of(15, 32,
17).withNano(120_000_000)), null)),
- */
-
// FF3
Arguments.of(0,
@@ -681,10 +628,6 @@ public class ItDateTimeCastFormatTest extends
BaseSqlIntegrationTest {
args.str, args.format
);
- // TODO https://issues.apache.org/jira/browse/IGNITE-25313 confusing
submillis parsing
- boolean subMillis = args.value != null && args.value.getNano() != 0 &&
args.format.contains("ff");
- Assumptions.assumeFalse(subMillis);
-
checkQuery(sqlCast, args.value, args.error);
}
@@ -697,15 +640,11 @@ public class ItDateTimeCastFormatTest extends
BaseSqlIntegrationTest {
args.format
);
- // TODO https://issues.apache.org/jira/browse/IGNITE-25313 confusing
submillis parsing
- boolean subMillis = args.value != null && args.value.getNano() != 0 &&
args.format.contains("ff");
- Assumptions.assumeFalse(subMillis);
-
checkDml(sqlCast, args.error, args.str);
if (args.value != null) {
assertQuery("SELECT timestamp_with_local_time_zone0_col FROM
datetime_cols WHERE id = 1")
- .returns(args.value)
+ .returns(args.value.truncatedTo(ChronoUnit.MILLIS))
.check();
}
}
@@ -719,15 +658,11 @@ public class ItDateTimeCastFormatTest extends
BaseSqlIntegrationTest {
args.str, args.format
);
- // TODO https://issues.apache.org/jira/browse/IGNITE-25313 confusing
submillis parsing
- boolean subMillis = args.value != null && args.value.getNano() != 0 &&
args.format.contains("ff");
- Assumptions.assumeFalse(subMillis);
-
checkDml(sqlCast, args.error);
if (args.value != null) {
assertQuery("SELECT timestamp_with_local_time_zone0_col FROM
datetime_cols WHERE id = 1")
- .returns(args.value)
+ .returns(args.value.truncatedTo(ChronoUnit.MILLIS))
.check();
}
}
@@ -737,17 +672,14 @@ public class ItDateTimeCastFormatTest extends
BaseSqlIntegrationTest {
if (dt.value != null) {
Instant expectedInstant = ZonedDateTime.of(dt.value,
TIME_ZONE_ID).toInstant();
return dateTime(dt.str, dt.format, expectedInstant, null);
- } else if (dt.error != null && dt.error.contains("out of range")) {
- // TIMESTAMP_WITH_LOCAL_TIME_ZONE out of range
- return dateTime(dt.str, dt.format, null,
"TIMESTAMP_WITH_LOCAL_TIME_ZONE out of range");
} else {
- return dateTime(dt.str, dt.format, null, dt.error);
+ return dateTime(dt.str, dt.format, null, " ");
}
});
}
- // CAST FORMAT confusing behaviour when translating milliseconds
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-25313")
+ // Cast to TIME/TIMESTAMP/TIMESTAMP_LTZ ignores target type's precision
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-25045")
@ParameterizedTest
@MethodSource("timestampLtzWithPrecision")
public void timestampLtzWithPrecisionLiterals(int precision,
DateTimeArgs<Instant> args) {
@@ -759,8 +691,8 @@ public class ItDateTimeCastFormatTest extends
BaseSqlIntegrationTest {
checkQuery(sqlCast, args.value, args.error);
}
- // CAST FORMAT confusing behaviour when translating milliseconds
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-25313")
+ // Cast to TIME/TIMESTAMP/TIMESTAMP_LTZ ignores target type's precision
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-25045")
@ParameterizedTest
@MethodSource("timestampLtzWithPrecision")
public void timestampLtzWithPrecisionUpdateFromLiteral(int precision,
DateTimeArgs<Instant> args) {
@@ -781,9 +713,8 @@ public class ItDateTimeCastFormatTest extends
BaseSqlIntegrationTest {
}
}
- // CAST FORMAT confusing behaviour when translating milliseconds
- // TODO https://issues.apache.org/jira/browse/IGNITE-25045
- @Disabled("https://issues.apache.org/jira/browse/IGNITE-25313")
+ // Cast to TIME/TIMESTAMP/TIMESTAMP_LTZ ignores target type's precision
+ @Disabled("https://issues.apache.org/jira/browse/IGNITE-25045")
@ParameterizedTest
@MethodSource("timestampLtzWithPrecision")
public void timestampLtzWithPrecisionUpdateFromDynamicParam(int precision,
DateTimeArgs<Instant> args) {
diff --git
a/modules/sql-engine/src/integrationTest/sql/group1/cast/test_cast_format.test
b/modules/sql-engine/src/integrationTest/sql/group1/cast/test_cast_format.test
index 77810266afb..7553fee7c0e 100644
---
a/modules/sql-engine/src/integrationTest/sql/group1/cast/test_cast_format.test
+++
b/modules/sql-engine/src/integrationTest/sql/group1/cast/test_cast_format.test
@@ -162,15 +162,21 @@ SELECT cast('18-12-03' as date format 'YY-MM-DD')
----
2018-12-03
-query T
+statement error: Unexpected element <MON>
SELECT cast('JUN 30, 2018' as date format 'MON DD, YYYY')
+
+statement error: Invalid format. Expected both fields: 12-hour / am pm
+SELECT cast('17:30' as time format 'HH12:MI')
+
+query T
+SELECT cast('07:30 A.M.' as time format 'HH12:MI A.M.')
----
-2018-06-30
+07:30:00
query T
-SELECT cast('17:30' as time format 'HH12:MI')
+SELECT cast('07:30 P.M.' as time format 'HH12:MI P.M.')
----
-17:30:00
+19:30:00
query T
SELECT cast('01:05:07.161' as time format 'HH24:MI:SS.FF4')
@@ -183,17 +189,14 @@ SELECT cast('2017-05-12' as timestamp format 'YYYY-MM-DD')
2017-05-12 00:00:00
query T
-SELECT cast('2020.06.03 12:42:53' as timestamp format 'YYYY.MM.DD HH:MI:SS')
+SELECT cast('2020.06.03 12:42:53' as timestamp format 'YYYY.MM.DD HH24:MI:SS')
----
2020-06-03 12:42:53
-# Seems will be supported after resolve CALCITE_6367
-statement error: Invalid format: 'YYYY.MM.DD HH24:MI:SSTZH:TZM' for datetime
string: '2020.06.03 00:00:53+06:30'
+query T
SELECT cast('2020.06.03 00:00:53+06:30' as timestamp format 'YYYY.MM.DD
HH24:MI:SSTZH:TZM')
-
-# Seems will be supported after resolve CALCITE_6367
-statement error: Invalid format: 'HH:MI P.M.' for datetime string: '03:30 P.M.'
-SELECT cast('03:30 P.M.' as time format 'HH:MI P.M.')
+----
+2020-06-02 17:30:53
# Seems will be supported after resolve CALCITE_6270
statement error: Cast function cannot convert value of type BINARY(5) to type
VARCHAR
diff --git
a/modules/sql-engine/src/integrationTest/sql/group1/types/date/test_incorrect_dates.test
b/modules/sql-engine/src/integrationTest/sql/group1/types/date/test_incorrect_dates.test
index e4190a390c3..df927b9a9c2 100644
---
a/modules/sql-engine/src/integrationTest/sql/group1/types/date/test_incorrect_dates.test
+++
b/modules/sql-engine/src/integrationTest/sql/group1/types/date/test_incorrect_dates.test
@@ -100,8 +100,7 @@ INSERT INTO dates VALUES (date '10000-01-01')
statement error: Invalid DATE value, '10000-00-01'
INSERT INTO dates VALUES ('10000-00-01'::DATE)
-# TODO https://issues.apache.org/jira/browse/IGNITE-25010 This should be
parser error
-statement error: DATE out of range
+statement error: Invalid value for MonthOfYear
INSERT INTO dates VALUES (CAST('9999-99-99' AS DATE FORMAT 'YYYY-MM-DD'))
# The value is less than the minimum allowed
diff --git
a/modules/sql-engine/src/integrationTest/sql/group1/types/timestamp/test_incorrect_timestamp.test
b/modules/sql-engine/src/integrationTest/sql/group1/types/timestamp/test_incorrect_timestamp.test
index f4ce68f0b4e..7d2f3c16f39 100644
---
a/modules/sql-engine/src/integrationTest/sql/group1/types/timestamp/test_incorrect_timestamp.test
+++
b/modules/sql-engine/src/integrationTest/sql/group1/types/timestamp/test_incorrect_timestamp.test
@@ -106,8 +106,7 @@ INSERT INTO timestamp_t VALUES (timestamp '10000-01-01
01:01:01')
statement error: Timestamp literal 'TIMESTAMP '10000-01-01'' out of range.
INSERT INTO timestamp_t VALUES (timestamp '10000-01-01')
-# TODO https://issues.apache.org/jira/browse/IGNITE-25010 This should be
parser error
-statement error: TIMESTAMP out of range
+statement error: Invalid value for MonthOfYear
INSERT INTO timestamp_t VALUES (CAST('9999-99-99' AS TIMESTAMP FORMAT
'YYYY-MM-DD'))
statement error: Invalid DATE value, '10000-00-01 01:01:01'
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteSqlFunctions.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteSqlFunctions.java
index 7fbeeb7a0b8..9dae4b05b17 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteSqlFunctions.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteSqlFunctions.java
@@ -25,7 +25,11 @@ import static
org.apache.ignite.lang.ErrorGroups.Sql.RUNTIME_ERR;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
+import java.sql.Date;
import java.time.Instant;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.time.zone.ZoneRules;
@@ -35,12 +39,12 @@ import java.util.UUID;
import org.apache.calcite.avatica.util.ByteString;
import org.apache.calcite.linq4j.function.NonDeterministic;
import org.apache.calcite.runtime.SqlFunctions;
-import org.apache.calcite.runtime.SqlFunctions.DateParseFunction;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.ignite.internal.schema.SchemaUtils;
import org.apache.ignite.internal.sql.engine.util.Commons;
import org.apache.ignite.internal.sql.engine.util.IgniteMath;
import org.apache.ignite.internal.sql.engine.util.TypeUtils;
+import org.apache.ignite.internal.sql.engine.util.format.SqlDateTimeParser;
import org.apache.ignite.sql.ColumnType;
import org.apache.ignite.sql.SqlException;
import org.jetbrains.annotations.Nullable;
@@ -57,6 +61,8 @@ public class IgniteSqlFunctions {
private static final int DATE_MIN_INTERNAL = (int)
TypeUtils.toInternal(SchemaUtils.DATE_MIN, ColumnType.DATE);
private static final int DATE_MAX_INTERNAL = (int)
TypeUtils.toInternal(SchemaUtils.DATE_MAX, ColumnType.DATE);
+ /** java.sql.Time is stored as the number of milliseconds since 1970/01/01
*/
+ private static final LocalDate JAVA_SQL_TIME_DATE = LocalDate.of(1970, 1,
1);
/**
* Default constructor.
@@ -560,9 +566,8 @@ public class IgniteSqlFunctions {
Objects.requireNonNull(timeZone, "timeZone");
// TODO https://issues.apache.org/jira/browse/IGNITE-25320 reuse to
improve performance.
- DateParseFunction function = new DateParseFunction();
- long ts = function.parseTimestamp(format, v);
- Instant instant = Instant.ofEpochMilli(ts);
+ LocalDateTime dateTime =
SqlDateTimeParser.timestampFormatter(format).parseTimestamp(v);
+ Instant instant = dateTime.toInstant(ZoneOffset.UTC);
// Adjust instant millis
ZoneRules rules = timeZone.toZoneId().getRules();
@@ -571,6 +576,41 @@ public class IgniteSqlFunctions {
return adjusted.toEpochMilli();
}
+ /** Converts a date string into a date value. */
+ public static @Nullable Integer toDate(@Nullable String v, String format) {
+ if (v == null) {
+ return null;
+ }
+
+ // TODO https://issues.apache.org/jira/browse/IGNITE-25320 reuse to
improve performance.
+ LocalDate date = SqlDateTimeParser.dateFormatter(format).parseDate(v);
+ return SqlFunctions.toInt(Date.valueOf(date));
+ }
+
+ /** Converts a time string into a time value. */
+ public static @Nullable Integer toTime(@Nullable String v, String format) {
+ if (v == null) {
+ return null;
+ }
+
+ // TODO https://issues.apache.org/jira/browse/IGNITE-25320 reuse to
improve performance.
+ LocalTime time = SqlDateTimeParser.timeFormatter(format).parseTime(v);
+ Instant instant =
time.atDate(JAVA_SQL_TIME_DATE).toInstant(ZoneOffset.UTC);
+ return (int) instant.toEpochMilli();
+ }
+
+ /** Converts a timestamp string into a timestamp value. */
+ public static @Nullable Long toTimestamp(@Nullable String v, String
format) {
+ if (v == null) {
+ return null;
+ }
+
+ // TODO https://issues.apache.org/jira/browse/IGNITE-25320 reuse to
improve performance.
+ LocalDateTime ts =
SqlDateTimeParser.timestampFormatter(format).parseTimestamp(v);
+ Instant instant = ts.toInstant(ZoneOffset.UTC);
+ return instant.toEpochMilli();
+ }
+
private static @Nullable Object leastOrGreatest(boolean least, Object
arg0, Object arg1) {
if (arg0 == null || arg1 == null) {
return null;
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RexToLixTranslator.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RexToLixTranslator.java
index 395347a4bce..5e7faeba382 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RexToLixTranslator.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/exp/RexToLixTranslator.java
@@ -117,6 +117,8 @@ import org.locationtech.jts.geom.Geometry;
* Removed original casts to numeric types and used own
ConverterUtils.convert
* Added pad-truncate from CHARACTER to INTERVAL types
* Added time-zone dependency for cast from CHARACTER types to TIMESTAMP
WITH LOCAL TIMEZONE (see point 3)
+ * Cast VARCHAR to TIME is updated to use our implementation (see
IgniteMethod.TIME_STRING_TO_TIME).
+ * Cast VARCHAR to DATE is updated to use our implementation (see
IgniteMethod.DATE_STRING_TO_DATE).
* Cast TIMESTAMP to TIMESTAMP WITH LOCAL TIMEZONE use our
implementation, see IgniteMethod.UNIX_TIMESTAMP_TO_STRING_PRECISION_AWARE
* Cast TIMESTAMP LTZ accepts FORMAT. (See
IgniteMethod.TIMESTAMP_STRING_TO_TIMESTAMP_WITH_LOCAL_TIME_ZONE).
* 6. Translate literals changes:
@@ -612,16 +614,7 @@ public class RexToLixTranslator implements
RexVisitor<RexToLixTranslator.Result>
// If format string is supplied, parse formatted string into date
return Expressions.isConstantNull(format)
? Expressions.call(BuiltInMethod.STRING_TO_DATE.method, operand)
- : Expressions.call(
- // TODO
https://issues.apache.org/jira/browse/IGNITE-25010 Remove redundant call to
TO_DATE_EXACT
- IgniteMethod.TO_DATE_EXACT.method(),
- Expressions.call(
-
Expressions.new_(BuiltInMethod.PARSE_DATE.method.getDeclaringClass()),
- BuiltInMethod.PARSE_DATE.method,
- format,
- operand
- )
- );
+ : Expressions.call(IgniteMethod.DATE_STRING_TO_DATE.method(),
operand, format);
case TIMESTAMP:
return
@@ -652,8 +645,7 @@ public class RexToLixTranslator implements
RexVisitor<RexToLixTranslator.Result>
// If format string is supplied, parse formatted string into time
return Expressions.isConstantNull(format)
? Expressions.call(IgniteMethod.STRING_TO_TIME.method(), operand)
- :
Expressions.call(Expressions.new_(BuiltInMethod.PARSE_TIME.method.getDeclaringClass()),
- BuiltInMethod.PARSE_TIME.method, format, operand);
+ : Expressions.call(IgniteMethod.TIME_STRING_TO_TIME.method(),
operand, format);
case TIME_WITH_LOCAL_TIME_ZONE:
return
@@ -733,13 +725,7 @@ public class RexToLixTranslator implements
RexVisitor<RexToLixTranslator.Result>
// If format string is supplied, parse formatted string into timestamp
return Expressions.isConstantNull(format)
? Expressions.call(IgniteMethod.TO_TIMESTAMP_EXACT.method(),
Expressions.call(IgniteMethod.STRING_TO_TIMESTAMP.method(), operand))
- : Expressions.call(
- IgniteMethod.TO_TIMESTAMP_EXACT.method(),
- Expressions.call(
-
Expressions.new_(BuiltInMethod.PARSE_TIMESTAMP.method.getDeclaringClass()),
- BuiltInMethod.PARSE_TIMESTAMP.method, format, operand
- )
- );
+ :
Expressions.call(IgniteMethod.TIMESTAMP_STRING_TO_TIMESTAMP.method(), operand,
format);
case DATE:
return
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteMethod.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteMethod.java
index 831882cd3b3..b2b92e1d458 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteMethod.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteMethod.java
@@ -151,7 +151,22 @@ public enum IgniteMethod {
CURRENT_DATE(IgniteSqlDateTimeUtils.class, "currentDate",
DataContext.class),
/**
- * SQL CAST(? AS TIMESTAMP WITH LOCAL TIME ZONE FORMAT {@code format}).
The same as
+ * SQL CAST({@code varchar} AS TIME FORMAT {@code format}).
+ */
+ TIME_STRING_TO_TIME(IgniteSqlFunctions.class, "toTime", String.class,
String.class),
+
+ /**
+ * SQL CAST({@code varchar} AS DATE FORMAT {@code format}).
+ */
+ DATE_STRING_TO_DATE(IgniteSqlFunctions.class, "toDate", String.class,
String.class),
+
+ /**
+ * SQL CAST({@code varchar} AS TIMESTAMP FORMAT {@code format}).
+ */
+ TIMESTAMP_STRING_TO_TIMESTAMP(IgniteSqlFunctions.class, "toTimestamp",
String.class, String.class),
+
+ /**
+ * SQL CAST({@code varchar} AS TIMESTAMP WITH LOCAL TIME ZONE FORMAT
{@code format}). The same as
* {@link SqlFunctions#timeWithLocalTimeZoneToTimestampWithLocalTimeZone}
but accepts date format literal.
*/
TIMESTAMP_STRING_TO_TIMESTAMP_WITH_LOCAL_TIME_ZONE(IgniteSqlFunctions.class,
diff --git
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteSqlFunctionsTest.java
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteSqlFunctionsTest.java
index c74f8facbfc..0936c2c35f9 100644
---
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteSqlFunctionsTest.java
+++
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/exec/exp/IgniteSqlFunctionsTest.java
@@ -33,6 +33,7 @@ import java.util.TimeZone;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.apache.calcite.runtime.SqlFunctions;
+import org.apache.calcite.runtime.SqlFunctions.DateParseFunction;
import org.apache.ignite.internal.sql.engine.util.IgniteMath;
import org.apache.ignite.lang.ErrorGroups.Sql;
import org.jetbrains.annotations.Nullable;
@@ -515,7 +516,7 @@ public class IgniteSqlFunctionsTest {
TimeZone timeZone = TimeZone.getTimeZone(zoneId);
long calciteTsLtz = SqlFunctions.toTimestampWithLocalTimeZone(v,
timeZone);
- long tsLtz = IgniteSqlFunctions.toTimestampWithLocalTimeZone(v,
"yyyy-MM-dd hh:mi:ss.ff3", timeZone);
+ long tsLtz = IgniteSqlFunctions.toTimestampWithLocalTimeZone(v,
"yyyy-MM-dd hh24:mi:ss.ff3", timeZone);
assertEquals(Instant.ofEpochMilli(calciteTsLtz),
Instant.ofEpochMilli(tsLtz));
}
@@ -548,4 +549,64 @@ public class IgniteSqlFunctionsTest {
return zones.stream().flatMap(z -> times.stream().map(t ->
Arguments.of(z, t)));
}
+
+ @ParameterizedTest
+ @MethodSource("timeValues")
+ public void testToTime(String timeStr, int expectedMillis) {
+
+ DateParseFunction f = new DateParseFunction();
+ int millis = f.parseTime("HH:mi:SS", timeStr);
+ int time2 = IgniteSqlFunctions.toTime(timeStr, "HH24:MI:SS");
+
+ assertEquals(expectedMillis, millis);
+ assertEquals(millis, time2);
+ }
+
+ private static Stream<Arguments> timeValues() {
+ return Stream.of(
+ Arguments.of("00:00:00", 0),
+ Arguments.of("01:01:43", 3703000),
+ Arguments.of("07:37:59", 27479000),
+ Arguments.of("19:01:32", 68492000),
+ Arguments.of("23:59:59", 86399000)
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("dateValues")
+ public void testToDate(String timeStr, int expectedDays) {
+
+ DateParseFunction f = new DateParseFunction();
+ int days = f.parseDate("YYYY-MM-DD", timeStr);
+ int days2 = IgniteSqlFunctions.toDate(timeStr, "YYYY-MM-DD");
+
+ assertEquals(expectedDays, days);
+ assertEquals(days, days2);
+ }
+
+ private static Stream<Arguments> dateValues() {
+ return Stream.of(
+ Arguments.of("1970-01-01", 0),
+ Arguments.of("2025-01-01", 20089)
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("timestampValues")
+ public void testToTimestamp(String timeStr, long expectedTs) {
+
+ DateParseFunction f = new DateParseFunction();
+ long ts = f.parseTimestamp("YYYY-MM-DD", timeStr);
+ Long ts2 = IgniteSqlFunctions.toTimestamp(timeStr, "YYYY-MM-DD");
+
+ assertEquals(expectedTs, ts);
+ assertEquals(ts, ts2);
+ }
+
+ private static Stream<Arguments> timestampValues() {
+ return Stream.of(
+ Arguments.of("1970-01-01", 0),
+ Arguments.of("2025-01-01", 1735689600000L)
+ );
+ }
}