fix(#4468): expand primitive-array IN params through indexed path#4469
Conversation
FetchFromIndexStep.cartesianProduct() only expanded values that implement Iterable<?>. JSON deserialization via toMap(true) in PostCommandHandler converts numeric JSON arrays to long[]/double[] (primitive arrays), which are not Iterable. The whole array was passed as a single index key, returning no rows. Extended the predicate to also match arrays and wrap them with IterableObjectArray so every element is looked up individually, matching the behaviour of the non-indexed IN evaluator.
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 0 |
🟢 Coverage 100.00% diff coverage · -7.36% coverage variation
Metric Results Coverage variation ✅ -7.36% coverage variation Diff coverage ✅ 100.00% diff coverage Coverage variation details
Coverable lines Covered lines Coverage Common ancestor commit (716cfa5) 128545 94533 73.54% Head commit (ac59992) 160233 (+31688) 106038 (+11505) 66.18% (-7.36%) Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch:
<coverage of head commit> - <coverage of common ancestor commit>Diff coverage details
Coverable lines Covered lines Diff coverage Pull request (#4469) 2 2 100.00% Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified:
<covered lines added or modified>/<coverable lines added or modified> * 100%
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
Code ReviewOverviewThis PR fixes a real bug where Fix (
|
| Area | Status |
|---|---|
| Root cause analysis | Correct |
| Core fix | Works, but MultiValue reuse preferred for consistency |
| Test coverage | Good - consider adding int[]/double[] cases |
| Test style | Minor: remove issue-reference comments from method bodies |
| Docs file | Recommend removing - not a project convention |
The fix is sound. The main suggestions are using the pre-existing MultiValue utility for consistency, and dropping the docs file.
There was a problem hiding this comment.
Code Review
This pull request addresses issue #4468, where SQL queries using IN :param with a collection parameter returned no rows when an index was used. This occurred because primitive arrays resulting from JSON deserialization are not instances of Iterable<?> in Java, causing the index lookup to fail. The fix extends FetchFromIndexStep.cartesianProduct() to also match arrays and wrap them using IterableObjectArray. A comprehensive set of regression tests has been added in InParamIndexExpansionTest. The review feedback suggests simplifying the conditional check in FetchFromIndexStep.java by hoisting the null check for value to the beginning of the expression.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| final Expression nextElementInKey = key.getExpressions().getFirst(); | ||
| final Object value = nextElementInKey.execute(new ResultInternal(context.getDatabase()), context); | ||
| if (value instanceof Iterable<?> iterable && !(value instanceof Identifiable)) { | ||
| if (!(value instanceof Identifiable) && (value instanceof Iterable<?> || (value != null && value.getClass().isArray()))) { |
There was a problem hiding this comment.
The null check for value can be hoisted to the beginning of the condition. This simplifies the expression by removing the nested value != null check and makes it clearer that the subsequent checks are only performed on non-null values.
| if (!(value instanceof Identifiable) && (value instanceof Iterable<?> || (value != null && value.getClass().isArray()))) { | |
| if (value != null && !(value instanceof Identifiable) && (value instanceof Iterable<?> || value.getClass().isArray())) { |
There was a problem hiding this comment.
Resolved in 342fd2b. The condition now uses MultiValue.isMultiValue(value) (per the parallel review), which is internally null-safe (isMultiValue(null) returns false), so the explicit value != null guard is no longer needed at all.
- Replace IterableObjectArray with the already-imported MultiValue utility (isMultiValue + getMultiValueIterable) in cartesianProduct, unifying multi-value detection with the sibling processInCondition path. MultiValue.isMultiValue(null) is internally null-safe so no explicit null guard is needed. - Add int[] and double[] primitive-array regression cases (double coerces to the INTEGER index key). - Remove issue-reference comments and em-dashes from test method bodies per project conventions.
|
Addressed in 342fd2b:
On the docs file: keeping it. |
Code ReviewOverviewThis PR fixes a genuine bug: The Core FixCorrectness: solid. // Before
if (value instanceof Iterable<?> iterable && !(value instanceof Identifiable)) {
for (final Object elemInKey : iterable) {
// After
if (!(value instanceof Identifiable) && MultiValue.isMultiValue(value)) {
for (final Object elemInKey : MultiValue.getMultiValueIterable(value)) {
One implicit behavior change worth noting: Test CoverageThe regression suite is thorough:
One gap: no test for a Minor Points
SummaryThis is a clean, minimal, well-targeted bug fix. The two-line production change is correct, null-safe, and makes Approved with minor suggestions. |
Add a behavioral comment clarifying that the MultiValue-based key expansion intentionally covers every parameter shape (collections, primitive arrays, maps), consistent with processInCondition().
|
Addressed in ac59992:
Two suggestions I'm not applying, with reasons:
|
Code ReviewThis is a clean, well-targeted fix. The root cause analysis is accurate, and the implementation is consistent with the existing codebase. Fix (
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4469 +/- ##
============================================
+ Coverage 64.47% 64.48% +0.01%
- Complexity 463 466 +3
============================================
Files 1650 1650
Lines 128545 128545
Branches 27571 27571
============================================
+ Hits 82874 82897 +23
+ Misses 34045 34026 -19
+ Partials 11626 11622 -4 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Summary
Closes #4468
WHERE field IN :paramreturned no rows whenfieldhad an index and the parameter value was a collection. The non-indexed path (WHERE (field + 0) IN :param) and the literal form (WHERE field IN [1, 2, 3]) worked correctly.Root cause:
PostCommandHandlerusesjson.toMap(true)to deserialize the HTTP request body. The optimized flag converts JSON numeric arrays ([1, 2, 3]) tolong[]/double[]primitive arrays to avoid boxing allocations. Primitive arrays are notIterable<?>in Java.FetchFromIndexStep.cartesianProduct()expanded collection parameters into one index lookup per element using:A
long[]parameter failed this check, so the entire array was passed as a single index key - matching nothing.Fix: Extended the predicate in
cartesianProduct()to also handle arrays, wrapping them with the existingIterableObjectArrayutility:Test plan
InParamIndexExpansionTest(9 test methods) covers:List<Long>parameter viaIN :codeswith an indexed fieldlong[]parameter (simulates HTTP JSON deserialization viatoMap(true))Object[]parameterList<Long>parameter viaIN ?