From b59453ff2ce89c95cff1eacf4ae2be1551d0165f Mon Sep 17 00:00:00 2001
From: Andrew Timberlake <andrew@andrewtimberlake.com>
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