You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@calcite.apache.org by "zzwqqq (Jira)" <ji...@apache.org> on 2026/05/23 02:13:00 UTC
[jira] [Updated] (CALCITE-7529) Casts between literals and TIME/TIMESTAMP can lose precision beyond milliseconds
[ https://issues.apache.org/jira/browse/CALCITE-7529?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
zzwqqq updated CALCITE-7529:
----------------------------
Description:
When a custom RelDataTypeSystem allows datetime precision greater than Calcite's default maximum precision, RexExecutor constant reduction can lose sub-millisecond precision for TIME and TIMESTAMP values.
Calcite's default RelDataTypeSystem limits TIME/TIMESTAMP precision to 3, but users can override RelDataTypeSystem#getMaxPrecision. With such a type system, RexBuilder can be configured to create TIME(6) and TIMESTAMP(6) literals backed by TimeString/TimestampString.
However, RexExecutorImpl reduces constant expressions through generated Java code, where TIME and TIMESTAMP values are represented using millisecond-based runtime values. When RexExecutable.reduce rebuilds a RexLiteral from those values, digits beyond milliseconds are lost.
Minimal reproduction:
{code:java}
@Test void testReduceTimeCastWithMicros() {
final RelDataTypeFactory typeFactory =
new JavaTypeFactoryImpl(
new RelDataTypeSystemImpl() {
@Override public int getMaxPrecision(SqlTypeName typeName) {
switch (typeName) {
case TIME:
case TIMESTAMP:
return 6;
default:
return super.getMaxPrecision(typeName);
}
}
});
final RexBuilder rexBuilder = new RexBuilder(typeFactory);
final RexExecutorImpl executor =
new RexExecutorImpl(
DataContexts.of(
ImmutableMap.of(
DataContext.Variable.TIME_ZONE.camelName, TimeZone.getTimeZone("GMT"),
DataContext.Variable.LOCALE.camelName, Locale.US)));
final RexNode cast =
rexBuilder.makeCast(
typeFactory.createSqlType(SqlTypeName.TIME, 6),
rexBuilder.makeLiteral("12:34:56.123456"));
final List<RexNode> reducedValues = new ArrayList<>();
executor.reduce(rexBuilder, ImmutableList.of(cast), reducedValues);
assertThat(
((RexLiteral) reducedValues.get(0)).getValueAs(TimeString.class).toString(6),
equalTo("12:34:56.123456"));
}
{code}
Expected:
{code}
12:34:56.123456
{code}
Actual before the fix:
{code}
12:34:56.123000
{code}
The same issue can occur for TIMESTAMP(6). It can also affect already-created high precision TimeString/TimestampString RexLiterals if they are passed through RexExecutorImpl.reduce.
A possible fix is to preserve high-precision temporal literal casts as Rex literals when they can be represented directly by TimeString/TimestampString, and to let RexExecutorImpl return existing RexLiteral values without compiling and executing them again.
Note: After discussion, this issue is narrowed to casts involving temporal literals. Broader TIME/TIMESTAMP constant-expression handling likely requires an evaluator-level change and may be covered by other issues.
was:
When a custom RelDataTypeSystem allows datetime precision greater than Calcite's default maximum precision, RexExecutor constant reduction can lose sub-millisecond precision for TIME and TIMESTAMP values.
Calcite's default RelDataTypeSystem limits TIME/TIMESTAMP precision to 3, but users can override RelDataTypeSystem#getMaxPrecision. With such a type system, RexBuilder can be configured to create TIME(6) and TIMESTAMP(6) literals backed by TimeString/TimestampString.
However, RexExecutorImpl reduces constant expressions through generated Java code, where TIME and TIMESTAMP values are represented using millisecond-based runtime values. When RexExecutable.reduce rebuilds a RexLiteral from those values, digits beyond milliseconds are lost.
Minimal reproduction:
{code:java}
@Test void testReduceTimeCastWithMicros() {
final RelDataTypeFactory typeFactory =
new JavaTypeFactoryImpl(
new RelDataTypeSystemImpl() {
@Override public int getMaxPrecision(SqlTypeName typeName) {
switch (typeName) {
case TIME:
case TIMESTAMP:
return 6;
default:
return super.getMaxPrecision(typeName);
}
}
});
final RexBuilder rexBuilder = new RexBuilder(typeFactory);
final RexExecutorImpl executor =
new RexExecutorImpl(
DataContexts.of(
ImmutableMap.of(
DataContext.Variable.TIME_ZONE.camelName, TimeZone.getTimeZone("GMT"),
DataContext.Variable.LOCALE.camelName, Locale.US)));
final RexNode cast =
rexBuilder.makeCast(
typeFactory.createSqlType(SqlTypeName.TIME, 6),
rexBuilder.makeLiteral("12:34:56.123456"));
final List<RexNode> reducedValues = new ArrayList<>();
executor.reduce(rexBuilder, ImmutableList.of(cast), reducedValues);
assertThat(
((RexLiteral) reducedValues.get(0)).getValueAs(TimeString.class).toString(6),
equalTo("12:34:56.123456"));
}
{code}
Expected:
{code}
12:34:56.123456
{code}
Actual before the fix:
{code}
12:34:56.123000
{code}
The same issue can occur for TIMESTAMP(6). It can also affect already-created high precision TimeString/TimestampString RexLiterals if they are passed through RexExecutorImpl.reduce.
A possible fix is to preserve high-precision temporal literal casts as Rex literals when they can be represented directly by TimeString/TimestampString, and to let RexExecutorImpl return existing RexLiteral values without compiling and executing them again.
> Casts between literals and TIME/TIMESTAMP can lose precision beyond milliseconds
> --------------------------------------------------------------------------------
>
> Key: CALCITE-7529
> URL: https://issues.apache.org/jira/browse/CALCITE-7529
> Project: Calcite
> Issue Type: Bug
> Components: core
> Reporter: zzwqqq
> Priority: Major
> Labels: pull-request-available
>
> When a custom RelDataTypeSystem allows datetime precision greater than Calcite's default maximum precision, RexExecutor constant reduction can lose sub-millisecond precision for TIME and TIMESTAMP values.
> Calcite's default RelDataTypeSystem limits TIME/TIMESTAMP precision to 3, but users can override RelDataTypeSystem#getMaxPrecision. With such a type system, RexBuilder can be configured to create TIME(6) and TIMESTAMP(6) literals backed by TimeString/TimestampString.
> However, RexExecutorImpl reduces constant expressions through generated Java code, where TIME and TIMESTAMP values are represented using millisecond-based runtime values. When RexExecutable.reduce rebuilds a RexLiteral from those values, digits beyond milliseconds are lost.
> Minimal reproduction:
> {code:java}
> @Test void testReduceTimeCastWithMicros() {
> final RelDataTypeFactory typeFactory =
> new JavaTypeFactoryImpl(
> new RelDataTypeSystemImpl() {
> @Override public int getMaxPrecision(SqlTypeName typeName) {
> switch (typeName) {
> case TIME:
> case TIMESTAMP:
> return 6;
> default:
> return super.getMaxPrecision(typeName);
> }
> }
> });
> final RexBuilder rexBuilder = new RexBuilder(typeFactory);
> final RexExecutorImpl executor =
> new RexExecutorImpl(
> DataContexts.of(
> ImmutableMap.of(
> DataContext.Variable.TIME_ZONE.camelName, TimeZone.getTimeZone("GMT"),
> DataContext.Variable.LOCALE.camelName, Locale.US)));
> final RexNode cast =
> rexBuilder.makeCast(
> typeFactory.createSqlType(SqlTypeName.TIME, 6),
> rexBuilder.makeLiteral("12:34:56.123456"));
> final List<RexNode> reducedValues = new ArrayList<>();
> executor.reduce(rexBuilder, ImmutableList.of(cast), reducedValues);
> assertThat(
> ((RexLiteral) reducedValues.get(0)).getValueAs(TimeString.class).toString(6),
> equalTo("12:34:56.123456"));
> }
> {code}
> Expected:
> {code}
> 12:34:56.123456
> {code}
> Actual before the fix:
> {code}
> 12:34:56.123000
> {code}
> The same issue can occur for TIMESTAMP(6). It can also affect already-created high precision TimeString/TimestampString RexLiterals if they are passed through RexExecutorImpl.reduce.
> A possible fix is to preserve high-precision temporal literal casts as Rex literals when they can be represented directly by TimeString/TimestampString, and to let RexExecutorImpl return existing RexLiteral values without compiling and executing them again.
> Note: After discussion, this issue is narrowed to casts involving temporal literals. Broader TIME/TIMESTAMP constant-expression handling likely requires an evaluator-level change and may be covered by other issues.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)