From b59453ff2ce89c95cff1eacf4ae2be1551d0165f Mon Sep 17 00:00:00 2001 From: Andrew Timberlake Date: Tue, 27 Aug 2024 11:53:11 +0200 Subject: [PATCH] Add `ecto_query: :preload` option to preload query (#4503) --- guides/howtos/Multi tenancy with foreign keys.md | 4 ++-- lib/ecto/repo/preloader.ex | 10 +++++++++- test/ecto/repo_test.exs | 2 +- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/guides/howtos/Multi tenancy with foreign keys.md b/guides/howtos/Multi tenancy with foreign keys.md index c36335509e..cffdde6cce 100644 --- a/guides/howtos/Multi tenancy with foreign keys.md +++ b/guides/howtos/Multi tenancy with foreign keys.md @@ -27,7 +27,7 @@ defmodule MyApp.Repo do @impl true def prepare_query(_operation, query, opts) do cond do - opts[:skip_org_id] || opts[:schema_migration] -> + opts[:skip_org_id] || opts[:ecto_query] in [:schema_migrations, :preload] -> {query, opts} org_id = opts[:org_id] -> @@ -44,7 +44,7 @@ Now we can pass `:org_id` to all READ operations, such as `get`, `get_by`, `prel * if you explicitly set `:skip_org_id` to true, it won't require an `:org_id`. This reduces the odds of a developer forgetting to scope their queries, which can accidentally expose private data to other users - * if the `:schema_migration` option is set. This means the repository operation was issued by Ecto itself when migrating our database and we don't want to apply an `org_id` to them + * if the `:ecto_query` option is set. This means the repository operation was issued by Ecto itself, with value `:schema_migration` when migrating our database, or `:preload` when issuing a preload query, and we don't want to apply an `org_id` to them Still, setting the `org_id` for every operation is cumbersome and error prone. We will be better served if all operations attempt to set an `org_id`. diff --git a/lib/ecto/repo/preloader.ex b/lib/ecto/repo/preloader.ex index 3dc5b37fab..376decc0b5 100644 --- a/lib/ecto/repo/preloader.ex +++ b/lib/ecto/repo/preloader.ex @@ -50,7 +50,15 @@ defmodule Ecto.Repo.Preloader do normalize_and_preload_each([struct], repo_name, preloads, opts[:take], %{}, tuplet) |> hd() end - defp normalize_and_preload_each(structs, repo_name, preloads, take, query_assocs, tuplet) do + defp normalize_and_preload_each( + structs, + repo_name, + preloads, + take, + query_assocs, + {adapter_meta, opts} + ) do + tuplet = {adapter_meta, Keyword.put(opts, :ecto_query, :preload)} preloads = normalize(preloads, take, preloads) preload_each(structs, repo_name, preloads, query_assocs, tuplet) rescue diff --git a/test/ecto/repo_test.exs b/test/ecto/repo_test.exs index 19c3f62067..e244d7dab2 100644 --- a/test/ecto/repo_test.exs +++ b/test/ecto/repo_test.exs @@ -2162,7 +2162,7 @@ defmodule Ecto.RepoTest do test "preload" do PrepareRepo.preload(%MySchemaWithAssoc{parent_id: 1}, :parent, hello: :world) - assert_received {:all, query, _} + assert_received {:all, query, [ecto_query: :preload, hello: :world]} assert query.from.source == {"my_parent", Ecto.RepoTest.MyParent} end