Skip to content
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 50 additions & 12 deletions docs/other-topics/upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -1046,28 +1046,65 @@ You can now write the following:
sql.where(sql.attribute('firstName'), Op.like, 'foo');
```

### Changes to empty `OR` & `NOT` operators
### Empty `OR` & `NOT` operators now throw

_Pull Request [#15598]_
_Pull Requests [#15598] & [#18286]_

In Sequelize 6, both `Op.or` and `Op.not` produced `'0=1'` if their object or array was empty.
Sequelize 7 alphas `7.0.0-alpha.24` through `7.0.0-alpha.48` ignored them entirely instead, producing no condition at all.

Both behaviors could go wrong quietly: `0=1` silently matched no rows, while ignoring the operator silently matched
_every_ row. The latter is the more dangerous of the two, because it also applies to `Model.update` and `Model.destroy`,
where it defeated the safeguard that refuses to run those methods without a `where` option.

Both `Op.or` and `Op.not` used to produce `'0=1'` if their object or array was empty. Both of them are now completely ignored instead:
Starting with Sequelize 7, an empty `Op.or` or `Op.not` throws instead:

```ts
User.findAll({
where: or([]),
});
// All of the following throw:
User.findAll({ where: { [Op.or]: [] } });
User.findAll({ where: { [Op.or]: {} } });
User.findAll({ where: { [Op.not]: [] } });
User.findAll({ where: { [Op.not]: {} } });
User.findAll({ where: or([]) });
User.findAll({ where: or({}) });

// Including when the operator is used on an attribute:
User.findAll({ where: { firstName: { [Op.or]: [] } } });

// And when the empty operator is nested inside another condition:
User.findAll({ where: { [Op.and]: [{ firstName: 'zoe' }, { [Op.or]: [] }] } });
```

User.findAll({
where: not({}),
});
If you were relying on either of the previous behaviors, say which one you meant:

```ts
// To match no rows:
User.findAll({ where: sql`1 = 0` });

// To apply no condition at all: omit the operator entirely.
User.findAll();
```

Both produce the following query:
This most often comes up when the operand is built from a list that turned out to be empty.
Check the list before building the `where` option:

```sql
SELECT * FROM "users"
```ts
if (tenantIds.length === 0) {
return [];
}

const users = await User.findAll({
where: { [Op.or]: tenantIds.map(tenantId => ({ tenantId })) },
});
```

:::info

`Op.and` is not affected. An empty conjunction is vacuously true, so `{ [Op.and]: [] }` still produces no condition,
which is the correct SQL translation. Only `Op.or` and `Op.not` are ambiguous when empty.

:::

### Removed support for raw SQL in `json()`

_Pull Request [#15598]_
Expand Down Expand Up @@ -1186,3 +1223,4 @@ stop working in a future major release.
[#15292]: https://github.com/sequelize/sequelize/pull/15292
[#15598]: https://github.com/sequelize/sequelize/pull/15598
[#16514]: https://github.com/sequelize/sequelize/pull/16514
[#18286]: https://github.com/sequelize/sequelize/pull/18286
Loading