Skip to content

feat (Repo): Raise an error on query-like keyword opts to Repo functions#4717

Merged
josevalim merged 2 commits intoelixir-ecto:masterfrom
s3cur3:ty/validate-opts
Apr 9, 2026
Merged

feat (Repo): Raise an error on query-like keyword opts to Repo functions#4717
josevalim merged 2 commits intoelixir-ecto:masterfrom
s3cur3:ty/validate-opts

Conversation

@s3cur3
Copy link
Copy Markdown
Contributor

@s3cur3 s3cur3 commented Apr 8, 2026

Consider the following, written by a new-to-Elixir developer:

Repo.delete_all(User, where: [account_id: user.account_id])

The developer mistakenly believed that the opts to the Repo function worked like the Ecto.Query DSL, when in fact these keyword options were entirely ignored (making this an unscoped deletion of all users).

With this change, Ecto will raise a runtime error when given opts keys that seem to indicate the user was trying to modify the queryable passed in to:

  • Repo.all/2
  • Repo.all_by/3
  • Repo.delete_all/2
  • Repo.exists?/2
  • Repo.get/3
  • Repo.get_by/3
  • Repo.one/2
  • Repo.stream/2

I've taken the simple approach to the validation (adding a validate_query_opts!/2 call at the top of each function). I had considered bottlenecking this to do the validation only in Ecto.Repo.Queryable.execute/4, but doing so would have required passing execute/4 the name of the function that called it (since right now it only receives the operation, one of :all, :update_all, or :delete_all, even for functions like get or get_by). I'm happy to refactor it to go through execute if that's a desirable tradeoff, though.

Consider the following, written by a new-to-Elixir developer:

```elixir
Repo.delete_all(User, where: [account_id: user.account_id])
```

The developer mistakenly believed that the `opts` to the Repo function worked like the Ecto.Query DSL, when in fact these keyword options were entirely ignored (making this an unscoped deletion of all users).

With this change, Ecto will raise a runtime error when given `opts` keys that seem to indicate the user was trying to modify the queryable passed in to:

- Repo.all/2
- Repo.all_by/3
- Repo.delete_all/2
- Repo.exists?/2
- Repo.get/3
- Repo.get_by/3
- Repo.one/2
- Repo.stream/2
@josevalim josevalim merged commit a9376cc into elixir-ecto:master Apr 9, 2026
@josevalim
Copy link
Copy Markdown
Member

💚 💙 💜 💛 ❤️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants