feat: Always add LIMIT 1 to findOne queries (#14549) - #18311
feat: Always add LIMIT 1 to findOne queries (#14549)#18311stuartnelson3 wants to merge 1 commit into
findOne queries (#14549)#18311Conversation
(cherry-pick of 9950b4b to the v6 branch) * fix: always add LIMIT 1 to `findOne` queries * fix: add temp patch for issue 14618 Carries the test removal from the original commit as well as the source change. sequelize#17726 cherry-picked src/model.js only, so the five unit tests that assert the removed behaviour still failed. Co-authored-by: Ross Harrison <rtharrison86@gmail.com> Co-authored-by: Rik Smale <13023439+WikiRik@users.noreply.github.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
44 pass, 7 fail. Four failures are not from this diff, three are. All MSSQL legs pass, so Postgres native on Node 10, four legs. Not this diff. Oracle, three legs. This diff. v7 never met this. Its Oracle dialect declares Do you want |
Pull Request Checklist
npm run testornpm run test-DIALECTpass with this change (including linting)? The unit suite passes, 1387 tests.eslint src/model.js test/unit/model/find-one.test.jsis clean. See below.findOnequeries #14549, findOne does not add 'LIMIT 1' when first column of composite primary key is passed to 'where' option #13479, findOne doesn't generate a corrected SQL with partial primary key #9859.Description Of Change
This is a cherry-pick of 9950b4b, "feat: Always add LIMIT 1 to
findOnequeries" (#14549),onto the
v6branch. It carries both files from the original commit:src/model.jsandthe test removal.
#17726 already cherry-picked the same source change. @WikiRik noted in March 2025 that the
tests removed in 9950b4b are also present on
v6, and that PR touchessrc/model.jsonly,so those tests still fail. This PR closes that gap. I am happy to close this one if you
prefer to keep #17726 and land the test removal there instead.
Why the current behavior is wrong
findOnekeepsLIMIT 1off the query when thewhereclause namesprimaryKeyAttribute.On a composite primary key,
primaryKeyAttributeis only the first column. One value ofthat column matches many rows, so the query returns all of them.
The same block already guards the equivalent case for unique keys. It filters
uniqueKeysdown to
c.fields.length === 1, so a multi-column unique key keeps itsLIMIT. Amulti-column primary key gets no such guard:
sequelize/src/model.js
Lines 1984 to 1994 in cb7f99a
Two earlier reports of this closed without a fix. #9859 (2018) closed with "Sequelize at
this time does not support partial primary keys, we recommend
idas primary key", which nolonger holds. #13479 (2021) described the composite primary key case with an SSCCE, and the
stale bot closed it.
Reproduction on 6.37.8
Case 2 is the proof. Drop the primary key column from the
whereclause and theLIMITcomes back.
What it cost us
We used
findOneas an existence probe on a table with the three-column primary key(run_id, subject_id, subject_type). Thewhereclause namedrun_idand a status, soevery call read every row of the run and built a model instance for each one.
EXPLAIN (ANALYZE, BUFFERS)on a copy of the production table, 180,687 matching rows:count(*)findOne, noLIMITfindOne,limit: 1All three plans take the same index-only scan with
Heap Fetches: 0. No plan flip. Only therow count differs. The probe ran about 147,000 times per nightly job, which added about
1,750 s of database time and 10 minutes of wall-clock to a 613-minute run.
The row count is also why this is more than a wire-format nicety. Those rows became 180,687
model instances per call, and no database metric showed it. Per-call database time rose only
from 17 ms to 29 ms.
Verification
The full unit suite on this branch:
The two remaining cases in
test/unit/model/find-one.test.jsstill pass, because both assertthat
limitis present:Restore the deleted tests and keep the source change, and exactly the five expected cases
fail, which is the gap #17726 leaves open:
grep -rn "uniqueSingleColumns" src/ test/returns nothing after this change, and no othertest on
v6asserts the removed behavior.Notes
limitstill wins. Theif (options.limit === undefined)guard is unchanged,so the temporary workaround for Deleting PK column does not prevent MSSQL from ordering by PK on LIMIT 1 queries. #14618 carries over exactly as written in 9950b4b.
is the known MSSQL interaction, already handled by the guard above.