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

Reply via email to