Skip to content

[CALCITE-7206] Avoid duplicate compare(Object, Object) bridge method in the Enumerable merge join comparator#5080

Open
anxkhn wants to merge 1 commit into
apache:mainfrom
anxkhn:CALCITE-7206
Open

[CALCITE-7206] Avoid duplicate compare(Object, Object) bridge method in the Enumerable merge join comparator#5080
anxkhn wants to merge 1 commit into
apache:mainfrom
anxkhn:CALCITE-7206

Conversation

@anxkhn

@anxkhn anxkhn commented Jul 5, 2026

Copy link
Copy Markdown

Jira Link

CALCITE-7206

Changes Proposed

PhysTypeImpl.generateComparator always appends a bridge
compare(Object, Object) method to the generated Comparator when
EnumerableRules.BRIDGE_METHODS is enabled (the default). The bridge exists so
the erased Comparator.compare(Object, Object) interface method delegates to the
strongly typed primary compare(rowClass, rowClass) method.

When the row is a single column whose Java class is already Object (for
example a merge join whose key has type ANY, materialized as a bare Object),
the primary method's boxed signature is compare(Object, Object), which is
identical to the bridge. The emitted Comparator then declares two
compare(Object, Object) methods and Janino fails to compile it with Error while compiling generated Java code. This reproduces on an EnumerableMergeJoin
with an Object-typed join key, as reported on the Jira.

This change guards the bridge block with javaRowClass != Object.class: when the
boxed row class is already Object, the primary compare method already
satisfies the Comparator contract, so no bridge is needed and none is emitted.
For every other row class (the common Object[] row and all primitive-boxed
rows) the boxed class differs from Object, so the bridge is still generated and
behavior is unchanged.

The same private generateComparator also backs the comparators used by
EnumerableWindow, EnumerableAsofJoin, and EnumerableSortedAggregate, so
each of those paths would have hit the identical duplicate-method failure on an
Object row and is fixed by the same guard.

Added a focused unit test in PhysTypeTest,
testMergeJoinComparatorWithObjectRowHasNoDuplicateBridge, that builds the
reported shape (a single-column ANY struct with JavaRowFormat.SCALAR, so the
row Java class is Object), calls generateMergeJoinComparator, and asserts the
generated source keeps the primary compare(Object v0, Object v1) method but no
longer contains the colliding bridge parameters. The test fails on main with
the duplicate bridge method and passes with this change.

./gradlew :core:test --tests "org.apache.calcite.adapter.enumerable.PhysTypeTest" and ./gradlew :core:test --tests "org.apache.calcite.test.enumerable.EnumerableJoinTest" pass, and
autostyleCheck / checkstyleMain / checkstyleTest are clean.

…in the Enumerable merge join comparator

PhysTypeImpl.generateComparator always appends a bridge
compare(Object, Object) method when EnumerableRules.BRIDGE_METHODS is
enabled. When the boxed row Java class is already Object (for example a
merge join key of type ANY, represented as a bare Object), the primary
compare method already has the signature compare(Object, Object), so the
bridge collapses to the same signature and the generated Comparator
declares two identical methods, which fails to compile with "Error while
compiling generated Java code".

Skip the bridge method when javaRowClass is Object, since the primary
method already satisfies the Comparator contract. The normal Object[] row
case is unchanged.
@sonarqubecloud

sonarqubecloud Bot commented Jul 5, 2026

Copy link
Copy Markdown

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.

1 participant