From e1a0c7e5ee65482fe7b961559903acf4088d478a Mon Sep 17 00:00:00 2001 From: Valera V Harseko Date: Wed, 8 Jul 2026 15:53:25 +0300 Subject: [PATCH] Declare equals/hashCode consistent with compareTo (CodeQL java/inconsistent-compareto-and-equals) Three persistit engine classes implemented Comparable/compareTo but inherited Object.equals, so equals and compareTo could disagree: - BufferPool.BufferHolder (sorted via Arrays.sort by volumeId, page) - JournalManager.TransactionMapItem (held in a TreeSet, ordered by timestamp) - TreeBuilder.Node (keyed in a TreeMap, ordered by tree + key) None of these types is currently compared through equals/hashCode - the sorted collections (TreeMap, TreeSet, Arrays.sort) all use compareTo, and the types are never placed in a hash collection keyed by themselves nor looked up via contains/indexOf/remove(Object). The inconsistency was therefore latent, but CodeQL flags it and it is a real trap for future callers. Add equals and hashCode to each class, defined so that two instances are equal exactly when compareTo returns 0, and hashCode is derived from the same ordering key. Because nothing relied on the inherited identity equals, this is behaviour-preserving for all existing usages. Also add the missing 3A Systems Portions header line to JournalManager.java (TreeBuilder.java and BufferPool.java already carry it). --- .../main/java/com/persistit/BufferPool.java | 16 ++++++++++++++++ .../java/com/persistit/JournalManager.java | 17 +++++++++++++++++ .../main/java/com/persistit/TreeBuilder.java | 18 ++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/persistit/core/src/main/java/com/persistit/BufferPool.java b/persistit/core/src/main/java/com/persistit/BufferPool.java index 8af3ee0c44..ea94e0503b 100644 --- a/persistit/core/src/main/java/com/persistit/BufferPool.java +++ b/persistit/core/src/main/java/com/persistit/BufferPool.java @@ -1338,6 +1338,22 @@ public int compareTo(final BufferHolder buffer) { } + /** + * Equality is defined by the same (volumeId, page) ordering key used by + * {@link #compareTo(BufferHolder)}, so that {@code equals} stays + * consistent with the natural ordering. + */ + @Override + public boolean equals(final Object object) { + return this == object + || (object instanceof BufferHolder && compareTo((BufferHolder) object) == 0); + } + + @Override + public int hashCode() { + return Long.hashCode(_volumeId) * 31 + Long.hashCode(_page); + } + @Override public String toString() { final Buffer buffer = _buffer; diff --git a/persistit/core/src/main/java/com/persistit/JournalManager.java b/persistit/core/src/main/java/com/persistit/JournalManager.java index 0ce109579b..b1e3265fa3 100644 --- a/persistit/core/src/main/java/com/persistit/JournalManager.java +++ b/persistit/core/src/main/java/com/persistit/JournalManager.java @@ -13,6 +13,7 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * Portions Copyrighted 2026 3A Systems, LLC */ package com.persistit; @@ -2158,6 +2159,22 @@ public int compareTo(final TransactionMapItem ts) { } } + /** + * Equality is kept consistent with {@link #compareTo(TransactionMapItem)}: + * two items are equal exactly when they order equally (same commit + * timestamp for committed items, otherwise same start timestamp). + */ + @Override + public boolean equals(final Object object) { + return this == object + || (object instanceof TransactionMapItem && compareTo((TransactionMapItem) object) == 0); + } + + @Override + public int hashCode() { + return Long.hashCode(isCommitted() ? _commitTimestamp : _startTimestamp); + } + final static Comparator TRANSACTION_MAP_ITEM_COMPARATOR = new Comparator() { @Override diff --git a/persistit/core/src/main/java/com/persistit/TreeBuilder.java b/persistit/core/src/main/java/com/persistit/TreeBuilder.java index 6be1c27dff..3a0c5fba12 100644 --- a/persistit/core/src/main/java/com/persistit/TreeBuilder.java +++ b/persistit/core/src/main/java/com/persistit/TreeBuilder.java @@ -247,6 +247,24 @@ public int compareTo(final Node node) { return _key.compareTo(node._key); } + /** + * Equality is kept consistent with {@link #compareTo(Node)}: two nodes + * are equal exactly when they order equally (same tree and key, or both + * with no tree). + */ + @Override + public boolean equals(final Object object) { + return this == object || (object instanceof Node && compareTo((Node) object) == 0); + } + + @Override + public int hashCode() { + // Consistent with compareTo: equal nodes share the same tree + // instance (or both have none), so the tree's identity hash yields + // equal hash codes for equal nodes. + return _tree == null ? 0 : System.identityHashCode(_tree); + } + @Override public String toString() { Node n = this;