Ruiqi Dong created CALCITE-7553:
-----------------------------------
Summary: TimestampWithTimeZoneString.compareTo() makes
TreeSet/TreeMap drop distinct timestamp-with-zone values
Key: CALCITE-7553
URL: https://issues.apache.org/jira/browse/CALCITE-7553
Project: Calcite
Issue Type: Bug
Components: core
Affects Versions: 1.41.0
Reporter: Ruiqi Dong
*Summary*
TimestampWithTimeZoneString exposes a natural ordering that can silently drop
distinct values from TreeSet and TreeMap. equals() and hashCode() use the
canonical string form, including the local timestamp text and the zone ID.
compareTo(), however, compares only the parsed Calendar instant. As a result,
two distinct values such as: * 1969-07-21 02:56:15 GMT-08:00
* 1969-07-21 10:56:15 GMT
are not equal as objects, but compare as equal in the natural ordering. Any
sorted collection keyed on this class can therefore silently discard one of
them.
*Affected code*
File:
core/src/main/java/org/apache/calcite/util/TimestampWithTimeZoneString.java
{code:java}
@Override public boolean equals(@Nullable Object o) {
return o == this
|| o instanceof TimestampWithTimeZoneString
&& ((TimestampWithTimeZoneString) o).v.equals(v);
}
@Override public int hashCode() {
return v.hashCode();
}
@Override public int compareTo(TimestampWithTimeZoneString o) {
return this.pt.getCalendar().compareTo(o.pt.getCalendar());
} {code}
Reproducer
Add the following test to:
core/src/test/java/org/apache/calcite/util/MtClawCalciteBugTest.java
{code:java}
@Test void testTimestampWithTimeZoneStringNaturalOrderingKeepsDistinctValues() {
final TimestampWithTimeZoneString pst =
new TimestampWithTimeZoneString("1969-07-21 02:56:15 GMT-08:00");
final TimestampWithTimeZoneString gmt =
new TimestampWithTimeZoneString("1969-07-21 10:56:15 GMT");
assertFalse(pst.equals(gmt));
final TreeSet<TimestampWithTimeZoneString> values = new TreeSet<>();
values.add(pst);
values.add(gmt);
assertThat(values, hasSize(2));
} {code}
Run:
{code:java}
./gradlew :core:test \
--tests
org.apache.calcite.util.MtClawCalciteBugTest.testTimestampWithTimeZoneStringNaturalOrderingKeepsDistinctValues
{code}
Observed behavior:
The test fails because the second distinct value is dropped by the natural
ordering
{code:java}
Expected: a collection with size <2>
but: collection size was <1> {code}
Expected behavior:
If two TimestampWithTimeZoneString instances are not equal as values, the
natural ordering should not collapse them into a single sorted-set or
sorted-map key.
Even if ordering by instant is intentional, exposing it as the class's natural
ordering while equals() preserves zone-qualified text is unsafe. In practice,
sorted collections keyed on this class deduplicate distinct timestamp-with-zone
literals that happen to denote the same instant.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)