docs: document the v7 changes to empty and null-bearing operand sets - #841
Draft
WikiRik wants to merge 1 commit into
Draft
docs: document the v7 changes to empty and null-bearing operand sets#841WikiRik wants to merge 1 commit into
WikiRik wants to merge 1 commit into
Conversation
Documents sequelize/sequelize#18324. Rewrites the "Changes to empty OR & NOT operators" section: an empty Op.or or Op.not now produces a false condition rather than being ignored, and an Op.or with an empty *member* is distinguished from one that is empty. Notes that Sequelize 6 produced `0 = 1` for the top-level forms but dropped the attribute-level one, so this is not purely a return to v6 behaviour. Adds three sections the guide never covered, since Op.in and Op.notIn were not mentioned in it at all: - empty IN / NOT IN, which produced `IN (NULL)` in v6 and every v7 alpha - a null inside an IN / NOT IN list, now compared separately - Model.update / destroy / increment / decrement rejecting a `where` that is true for every row on its own Also replaces an example that used `not({})`, which is not exported in v7. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011hNgQwGLn8qFiAmn3ZNVLW
WikiRik
force-pushed
the
docs/empty-op-or-not-throws
branch
from
August 31, 2026 12:06
fff05bf to
3a84805
Compare
Op.or & Op.not now throw in v7
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.
Documents sequelize/sequelize#18324.
Draft — do not merge until that PR lands.
Supersedes the earlier version of this branch, which documented
#18286 — that PR made an empty
Op.or/Op.notthrow. #18324covers the same cases, plus three more, and produces a false condition rather than throwing.
Why
The v7 upgrade guide has one section,
### Changes to empty OR & NOT operators, describing the behaviour introducedby #15598: an empty
Op.ororOp.notis completely ignored,so
where: or([])producesSELECT * FROM "users"with no condition at all.#18324 changes that, and three other things in the same area that the guide never covered.
Op.inandOp.notInarenot mentioned anywhere in
upgrade.mdtoday, so an upgrading user currently has nowhere to read about any of it.What changed
### Empty OR & NOT operators match no rows(was### Changes to empty OR & NOT operators)0 = 1rather than beingignored.
7.0.0-alpha.24through7.0.0-alpha.48) so readers upgrading between alphas can tellwhich behaviour they are on.
Op.orthat is empty and one with an empty member — a member carrying nocondition contributes nothing rather than satisfying the disjunction, so building an
Op.orfrom optional partsstill works. This is the part most likely to trip someone up after reading "empty OR is false".
0 = 1for the top-level forms but dropped the attribute-level one entirely, sothis is not purely a return to v6 behaviour.
:::infonote thatOp.andis deliberately unaffected, because an empty conjunction is the identity ofAND.#18324 pins this with tests; the note is there so it does not get "fixed" for symmetry later.
### Empty IN & NOT IN operators(new){ [Op.in]: [] }producedIN (NULL)—UNKNOWNrather thanFALSE, so negating it matched no rows where it shouldmatch every row. Both operators now produce a constant. Credits #18250 as well as #18324, since the
Op.notInhalflanded there. Includes the note that generated-SQL assertions will differ.
### null inside an IN or NOT IN list(new)x NOT IN (1, NULL)isUNKNOWNfor every row and matched nothing;x IN (1, NULL)never matched aNULL. Anullin a list is now compared separately and means "or is null", consistent with how
{ age: null }already becomesage IS NULL.### Statements that modify rows reject a where that matches every row(new)Model.update/destroy/increment/decrementnow throw when thewhereyou pass is true for every row onits own. Covers what is and is not counted (conditions Sequelize injects, such as a scope or a paranoid model's
deletedAtclause, are not), the escape hatch, and that reads are unaffected. This is the only new throw in #18324and the thing an upgrading user is most likely to hit at runtime.
Link references: added
[#18250]and[#18324], removed[#18286].Drive-by fix
The old example used
not({}). There is nonot()export in v7 —packages/core/src/sequelize.jsexportsand()and
or()only. Replaced with theOp.notobject forms andor({}), which are real.Checks
sequelize@6.37.8install, not from memory — which caughtthe attribute-level exception noted above.
prettier@3.8.2 --check docs/other-topics/upgrade.mdclean (the pinned version).links or components beyond the
:::infoadmonition already used in this file.Created by Opus 5 with Claude Code, supervised by @WikiRik.