fix: incorporate literal values into lookup join key - #19200
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #19200 +/- ##
=============================================
- Coverage 66.63% 38.89% -27.74%
+ Complexity 1423 1422 -1
=============================================
Files 3443 3443
Lines 218663 218709 +46
Branches 34801 34817 +16
=============================================
- Hits 145705 85075 -60630
- Misses 61230 125875 +64645
+ Partials 11728 7759 -3969
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
timothy-e
left a comment
There was a problem hiding this comment.
Thanks for starting on this so fast! I I wasn't sure if you're ready for review or not, but gave it a pass through.
The idea makes sense to me, but have a few small concerns
| // Build a map from right column name to its index in the right schema | ||
| Map<String, Integer> rightColIndex = new HashMap<>(); | ||
| for (int i = 0; i < _rightColumns.length; i++) { | ||
| rightColIndex.put(_rightColumns[i], i); | ||
| } |
| String rightColName = _rightColumns[rightIdx]; | ||
| int pkPos = rightPkColumns.indexOf(rightColName); | ||
| if (pkPos >= 0) { | ||
| _keyColumnLeftIds[pkPos] = -1; // literal mode |
There was a problem hiding this comment.
This could cause a literal to overwrite a equi-join key, e.g. in a scenario like this:
ON fact.currency = dim.currency
AND fact.rate_start_date = dim.rate_start_date
AND dim.currency = 'gbp'The join would only validate dim.currency=gbp, not fact.currency=dim.currency. Can you also add a test for this case?
| // Initialize all PK columns as unmatched (placeholder -1, null literal) | ||
| for (int i = 0; i < _keyColumnCount; i++) { | ||
| _keyColumnLeftIds[i] = -1; | ||
| _keyColumnLiteralValues[i] = null; | ||
| } |
There was a problem hiding this comment.
This uses the same representation for “unassigned PK component” and “literal NULL”, and makes missing PK slots look intentionally mapped to NULL, which changes the behaviour more than what we probably intend to do.
(e.g. right now, what happens when a lookup join doesn't have the full PK specified? we should be careful about changing that)
|
Thanks for picking this up. The root cause in the description is correct. Calcite reads I looked at the change closely and found some problems. I list them here for the record. 1. A constant replaces an equi-join key. The literal pass runs after the equi-join key pass, and it overwrites _keyColumnLeftIds[pkPos] = -1; // drops the equi-join keyTake 2. The literal keeps the type that the planner gave it. 3. An open primary key column gives no error. When no condition fills a position, the key holds a null there and 4. A join key on a column outside the primary key is now dropped. The equi-join key pass skips a column that is 5. The change breaks every existing lookup join test. 6. The new test cannot pass, even after a correct fix. 7. Two smaller points. The file loses its last newline, which the linter reports. There is also no test for the I opened #19210, which covers these cases and corrects the two faults in the test harness. It supersedes |
Description
Fixes a bug in
LookupJoinOperatorwhere a lookup join returns 0 rows when a dimension table primary-key component is supplied as a literal in the join condition.Root Cause
When a join condition like
dim.currency = 'gbp' AND dim.rate_start_date = fact.rate_start_dateis used, Calcite'sanalyzeCondition()classifies thedim.currency = 'gbp'part as a non-equi condition (right-column-to-literal equality, not a left-right column equality). TheLookupJoinOperatorthen builds the lookup key using only the left-key columns (rate_start_date), resulting in an incomplete key that doesn't match the dimension table's composite primary key ([currency, rate_start_date]). The lookup returnsnull, and the join produces 0 rows.Fix
In
LookupJoinOperator's constructor, analyze the non-equi conditions to find=equality expressions where one operand is anInputRef(pointing to a right-side column) and the other is aLiteral. When the right-side column is part of the dimension table's primary key, use the literal value as a key component. This ensures the lookup key is complete and matches the dimension table's primary key.Key changes
LookupJoinOperator.java: Add_keyColumnCount,_keyColumnLeftIds, and_keyColumnLiteralValuesfields. In the constructor, extract literal-based key components from non-equi conditions. UpdategetKey()andfillKey()to build the complete primary key including literals. UpdatebuildJoinedDataBlockSemiandbuildJoinedDataBlockAntito use the full key size.LookupJoin.json: Add a test caselookup_join_literal_keythat reproduces the bug.Test
Expected:
[["gbp", 125]](1 row)Before fix:
[](0 rows)Closes #19188