fix: = ANY(array) on an indexed column returns no rows#477
Open
earfman wants to merge 2 commits into
Open
Conversation
buildComparison optimized `col = <const>` into a scalar index equality; with an ANY(array) operand the constant is the whole array, so the index lookup matched no rows. Skip that optimization when an operand is ANY/ALL and fall back to the seq scan, which evaluates membership element-wise.
Fails on master, passes with the buildComparison fix on this branch. Exercises the primary-key (index) path for `= ANY(array)` via both ARRAY[] and a string array literal.
Author
Coretexa gate report —
|
| check | result | by | detail |
|---|---|---|---|
repro |
✓ pass | verifier-pgmem |
indexed = ANY(array['A','C']) and = ANY('{A,C}') → [A,C]; PK, explicit index, and no-index all correct |
regression-guard |
✓ pass | verifier-pgmem |
reverted the guard → new test FAILS (Received: []); restored → passes. Genuinely protective. |
full-suite |
✓ pass | verifier-pgmem |
baseline 846 pass / 3 fail / 3 err → fixed 847 pass / 3 fail / 3 err (+1 pass, identical failures) |
no-regression |
✓ pass | verifier-pgmem |
=, IN, = ANY(subquery), !=, scalar comparisons all still correct; 3 pre-existing fails are ORM tests needing a live Postgres |
over-delivery |
✓ pass | verifier-pgmem |
fix also repairs > ANY and != ANY on indexed columns (returned empty/garbage before) |
All required checks have passing evidence: repro, regression-guard, full-suite, no-regression
Verification
--- VERIFIED 2026-07-12 by verifier-pgmem (independent session, not the doer) ---
Gate 1: reproduced with own scripts — indexed = ANY(array/literal/subquery) all correct; non-broken paths intact.
Gate 2: ran full suite baseline (git stash) vs fixed — exactly +1 pass, same 3 fails / 3 errors (pre-existing ORM integration tests).
Gate 3: diff is an 8-line guard at the top of buildComparison, before the isConstant short-circuit and all index EqFilter/IneqFilter paths; comment accurate; isAny is a real IValue property.
Gate 4: reverted only the guard → the new regression test fails; also confirmed > ANY and != ANY on indexed columns are repaired; ALL is a pre-existing unsupported feature (identical both sides), not a gap.
Verified by a second agent · claimer ≠ verifier · coretexa.dev
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
col = ANY(array)silently returned zero rows whencolis indexed (primary key or explicit index). This fixes it and adds a regression test. Fixes #338.This also resolves the mystery in that thread: the reporter's repro used a
SERIAL PRIMARY KEY(indexed → broken path), while the unit test they added — and thename = any(...)case that worked — used non-indexed columns (seq-scan path, correct). The divergence was the index all along.Why
buildComparison(src/transforms/build-filter.ts) optimizescol = <constant>into a scalar index equality. When the right operand isANY(array), the constant is the whole array, so the generatedEqFilterlooks up an index key equal to['A','C']and matches nothing. Without an index, the seq-scan fallback evaluatesANYelement-wise and returns the correct rows — hence the index-only divergence.This is a common query shape (
WHERE id = ANY($1)against a primary key is exactly what Sequelize, Mikro-ORM, etc. generate), and the failure is silent — an empty result set, no error.The fix
Skip the scalar index optimization in
buildComparisonwhen either operand is anANY/ALLexpression, so it falls back to the seq-scan path that already evaluates array membership correctly:Tests
src/tests/any-indexed-column.spec.ts— two cases (ARRAY[]and a string array literal) against aprimary keycolumn. Both fail onmasterand pass with this change.> ANYand!= ANYon indexed columns, which returned empty/incorrect rows before.Scope
Minimal — an 8-line guard in
build-filter.tsplus a standalone regression test. No behavior change for non-indexed columns,IN (...),= ANY(subquery), or scalar comparisons.This change was prepared with AI assistance and independently verified against the project's full test suite before submission (baseline-vs-fixed diff, and the regression test confirmed to fail without the fix).