Skip to content

[CALCITE-7553] Separate TIMESTAMP_TZ comparison semantics#5007

Open
OldTruckDriver wants to merge 1 commit into
apache:mainfrom
OldTruckDriver:fix/CALCITE-7553_timestamp_tz_comparison
Open

[CALCITE-7553] Separate TIMESTAMP_TZ comparison semantics#5007
OldTruckDriver wants to merge 1 commit into
apache:mainfrom
OldTruckDriver:fix/CALCITE-7553_timestamp_tz_comparison

Conversation

@OldTruckDriver

Copy link
Copy Markdown
Contributor

Fixes CALCITE-7553.

This PR separates TIMESTAMP_TZ's Java natural ordering from SQL value comparison semantics.

TimestampWithTimeZoneString.compareTo now compares by instant first, then by the canonical string value as a tie-breaker. This makes compareTo consistent with equals/hashCode, so Java sorted collections do not collapse distinct TIMESTAMP_TZ values that represent the same instant.

SQL comparison paths now use an explicit Rex SQL value comparison helper. For TIMESTAMP_TZ, the helper compares instants; other values keep their existing natural ordering. This preserves SQL semantics for literal folding and RexInterpreter evaluation.

RexBuilder and RexSimplify now avoid constructing Sarg ranges for TIMESTAMP_TZ values in the affected paths, because Sarg still relies on natural Comparable ordering. This keeps IN/BETWEEN and simplification paths from using the wrong ordering. Full TIMESTAMP_TZ SQL-value comparison support inside Sarg/range handling is left for a follow-up PR.

The TIMESTAMP_TZ epoch-millis conversion in RexLiteral now delegates to TimestampWithTimeZoneString.getMillisSinceEpoch(), using the same instant calculation as the comparison helper.

Tests:

  • ./gradlew :core:test -Djunit.jupiter.execution.parallel.enabled=false --tests org.apache.calcite.rex.RexProgramTest --tests org.apache.calcite.rex.RexBuilderTest
  • ./gradlew :core:check

@sonarqubecloud

sonarqubecloud Bot commented Jun 9, 2026

Copy link
Copy Markdown

@mihaibudiu

Copy link
Copy Markdown
Contributor

I think this deserves a little design discussion in Jira prior to an implementation.
I would expect to see an interface e.g., ComparableSqlValue implemented by all SqlLiterals.
Alternatively, there could be a Comparator interface implementation for SqlLiterals.

@OldTruckDriver

Copy link
Copy Markdown
Contributor Author

Thanks. My intent with this PR was to separate Java natural ordering from SQL value comparison semantics, but the current helper is only a local implementation of that idea.

I will move the design proposal to Jira and wait for agreement there before updating this PR. In particular, I will describe the two options you mentioned: a ComparableSqlValue-style interface, or a central Comparator for SQL literal values.

@OldTruckDriver OldTruckDriver force-pushed the fix/CALCITE-7553_timestamp_tz_comparison branch from 38a5dff to b23b0f3 Compare June 16, 2026 12:25
@OldTruckDriver

Copy link
Copy Markdown
Contributor Author

I updated the local version to introduce a ComparableSqlValue<T> interface and a shared SqlValueComparator. TimestampWithTimeZoneString implements the interface, so its Java natural ordering can remain consistent with equals, while SQL-value comparison uses the explicit comparator path.

package org.apache.calcite.util;

/**
* Value whose SQL comparison semantics differ from its natural ordering.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be defined only for constant values (a concept which includes literals, but also perhaps constructors applied to constant arguments). The term "value" you used is very overloaded - it can be applied to runtime values as well.

}

/** Returns whether two values should use custom SQL-value comparison. */
public boolean hasCustomSqlValueComparison(Comparable v0, Comparable v1) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the two-argument version of this function really necessary?
Is there a case where the one-argument and the two-argument would return different results for the same first argument?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants