{{ message }}
feat: Always add LIMIT 1 to findOne queries (#14549) - #18311
Open
stuartnelson3 wants to merge 1 commit into
Open
feat: Always add LIMIT 1 to findOne queries (#14549)#18311stuartnelson3 wants to merge 1 commit into
findOne queries (#14549)#18311stuartnelson3 wants to merge 1 commit into
Conversation
(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>
Contributor
|
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 |
Author
5 tasks
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.

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.