[
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)