[ 
https://issues.apache.org/jira/browse/CALCITE-7529?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18082533#comment-18082533
 ] 

zzwqqq edited comment on CALCITE-7529 at 5/21/26 12:06 PM:
-----------------------------------------------------------

The strategy I am trying is to keep the change limited to cases where a 
TIME/TIMESTAMP value is already a literal, or can be represented as a literal 
without going through generated code.

There are two parts:

1. Do not re-evaluate RexLiterals in RexExecutorImpl.reduce.

This is a Rex-level case rather than a simple SQL reproduction. For example:
{code:java}
final RexLiteral literal =
    rexBuilder.makeTimestampLiteral(
        new TimestampString("2020-01-01 12:34:56.123456"), 6);

final List<RexNode> reducedValues = new ArrayList<>();
executor.reduce(rexBuilder, ImmutableList.of(literal), reducedValues);
{code}
The input is already a reduced value. If it is compiled and executed again, 
generated code currently represents TIMESTAMP values at millisecond precision, 
so the rebuilt RexLiteral can lose sub-millisecond digits.

2. Handle directly representable literal casts in RexBuilder.

For example:
{code:sql}
CAST('12:34:56.123456' AS TIME(6))
CAST('2020-01-01 12:34:56.123456' AS TIMESTAMP(6))
CAST(CAST('12:34:56.000456' AS TIME(3)) AS VARCHAR)
{code}
These cases can be handled with TimeString/TimestampString RexLiterals, before 
the cast is reduced through generated code.

This does not try to make all TIME/TIMESTAMP constant expressions 
sub-millisecond aware. Expressions such as temporal comparisons, arithmetic, or 
functions would still need broader runtime/codegen changes. Does this narrower 
scope make sense, or would you prefer a different direction?


was (Author: JIRAUSER311718):
The strategy I am trying is to keep the change limited to cases where a 
TIME/TIMESTAMP value is already a literal, or can be represented as a literal 
without going through generated code.

There are three parts:

1. Do not re-evaluate RexLiterals in RexExecutorImpl.reduce.

This is a Rex-level case rather than a simple SQL reproduction. For example:

{code:java}
final RexLiteral literal =
    rexBuilder.makeTimestampLiteral(
        new TimestampString("2020-01-01 12:34:56.123456"), 6);

final List<RexNode> reducedValues = new ArrayList<>();
executor.reduce(rexBuilder, ImmutableList.of(literal), reducedValues);
{code}

The input is already a reduced value. If it is compiled and executed again, 
generated code currently represents TIMESTAMP values at millisecond precision, 
so the rebuilt RexLiteral can lose sub-millisecond digits.

2. Handle directly representable literal casts in RexBuilder.

For example:

{code:sql}
CAST('12:34:56.123456' AS TIME(6))
CAST('2020-01-01 12:34:56.123456' AS TIMESTAMP(6))
CAST(CAST('12:34:56.000456' AS TIME(3)) AS VARCHAR)
{code}

These cases can be handled with TimeString/TimestampString RexLiterals, before 
the cast is reduced through generated code.

3. Preserve precision in the SqlToRelConverter VALUES path.

For example:

{code:sql}
INSERT INTO t(ts) VALUES ('2020-01-01 12:34:56.123456')
{code}

when `ts` is TIMESTAMP(6), and similarly for TIME(6). This path builds 
LogicalValues from SQL literals, so it may not go through RexExecutor 
reduction. The strategy is to rebuild TIME/TIMESTAMP RexLiterals from 
getValueAs(TimeString.class) / getValueAs(TimestampString.class) using the 
target field precision.

This does not try to make all TIME/TIMESTAMP constant expressions 
sub-millisecond aware. Expressions such as temporal comparisons, arithmetic, or 
functions would still need broader runtime/codegen changes. Does this narrower 
scope make sense, or would you prefer a different direction?

> RexExecutor constant reduction loses sub-millisecond precision for 
> TIME/TIMESTAMP literals
> ------------------------------------------------------------------------------------------
>
>                 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.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to