Repository: elixir-ecto/ecto
Pull request #4510 (2024-09-25 03:41:13)

Make Repo.reload respect `source`
`Repo.reload/1` doesn't seem to respect `source` meta key.

<details>
<summary>
Here's a single file reproduction
</summary>

```elixir
Mix.install([
  {:ecto_sql, "~> 3.10"},
  {:postgrex, ">= 0.0.0"}
])

Application.put_env(:foo, Repo, database: "mix_install_examples", host: "127.0.0.1", password: "postgres", username: "postgres")

defmodule Repo do
  use Ecto.Repo, adapter: Ecto.Adapters.Postgres, otp_app: :foo
end

defmodule Migration0 do
  use Ecto.Migration

  def change do
    create table("posts2") do
      add(:title, :string)
      timestamps(type: :utc_datetime_usec)
    end
  end
end

defmodule Post do
  use Ecto.Schema

  schema "posts" do
    field(:title, :string)
    timestamps(type: :utc_datetime_usec)
  end
end

defmodule Main do
  import Ecto.Query, warn: false

  def main do
    Repo.__adapter__().storage_down(Repo.config())
    :ok = Repo.__adapter__().storage_up(Repo.config())
    {:ok, _} = Supervisor.start_link([Repo], strategy: :one_for_one)
    Ecto.Migrator.run(Repo, [{0, Migration0}], :up, all: true, log_migrations_sql: :info)

    post = %Post{
      title: "Post 1"
    }
    |> Ecto.put_meta(source: "posts2")
    |> Repo.insert!()

    Repo.reload(post)
    |> dbg()
  end
end

Main.main()
```

```
elixir ecto_sql.exs

20:38:28.199 [info] == Running 0 Migration0.change/0 forward

20:38:28.200 [info] create table posts2

20:38:28.204 [info] QUERY OK db=2.2ms
CREATE TABLE "posts2" ("id" bigserial, "title" varchar(255), "inserted_at" timestamp NOT NULL, "updated_at" timestamp NOT NULL, PRIMARY KEY ("id")) []

20:38:28.205 [info] == Migrated 0 in 0.0s

20:38:28.224 [debug] QUERY OK source="posts2" db=0.7ms queue=0.6ms idle=40.9ms
INSERT INTO "posts2" ("title","inserted_at","updated_at") VALUES ($1,$2,$3) RETURNING "id" ["Post 1", ~U[2024-09-25 03:38:28.221724Z], ~U[2024-09-25 03:38:28.221724Z]]

20:38:28.232 [debug] QUERY ERROR source="posts" db=0.0ms queue=3.4ms idle=47.7ms
SELECT p0."id", p0."title", p0."inserted_at", p0."updated_at" FROM "posts" AS p0 WHERE (p0."id" = ANY($1)) [[1]]
** (Postgrex.Error) ERROR 42P01 (undefined_table) relation "posts" does not exist

    query: SELECT p0."id", p0."title", p0."inserted_at", p0."updated_at" FROM "posts" AS p0 WHERE (p0."id" = ANY($1))
    (ecto_sql 3.12.0) lib/ecto/adapters/sql.ex:1078: Ecto.Adapters.SQL.raise_sql_call_error/1
    (ecto_sql 3.12.0) lib/ecto/adapters/sql.ex:976: Ecto.Adapters.SQL.execute/6
    (ecto 3.12.3) lib/ecto/repo/queryable.ex:232: Ecto.Repo.Queryable.execute/4
    (ecto 3.12.3) lib/ecto/repo/queryable.ex:19: Ecto.Repo.Queryable.all/3
    (ecto 3.12.3) lib/ecto/repo/queryable.ex:154: Ecto.Repo.Queryable.one/3
    ecto_sql.exs:47: Main.main/0
    ecto_sql.exs:52: (file)
```
</details>

I attempted a quick naive solution, let me know if it's not that simple 😅 


Patch:
diff --git a/lib/ecto/repo/queryable.ex b/lib/ecto/repo/queryable.ex
index 3fce1649..f3caf32d 100644
--- a/lib/ecto/repo/queryable.ex
+++ b/lib/ecto/repo/queryable.ex
@@ -491,12 +491,12 @@ defmodule Ecto.Repo.Queryable do
     assert_structs!(structs)
 
     schema = head.__struct__
-    prefix = head.__meta__.prefix
+    %{prefix: prefix, source: source} = head.__meta__
 
     case schema.__schema__(:primary_key) do
       [pk] ->
         keys = Enum.map(structs, &get_pk!(&1, pk))
-        query = Query.from(x in schema, where: field(x, ^pk) in ^keys)
+        query = Query.from(x in {source, schema}, where: field(x, ^pk) in ^keys)
         %{query | prefix: prefix}
 
       pks ->


Test patch:
diff --git a/test/ecto/repo_test.exs b/test/ecto/repo_test.exs
index e244d7da..d9fb87e7 100644
--- a/test/ecto/repo_test.exs
+++ b/test/ecto/repo_test.exs
@@ -334,6 +334,12 @@ defmodule Ecto.RepoTest do
       TestRepo.reload(struct_with_prefix)
       assert_received {:all, %{prefix: %{key: :another}}}
     end
+
+    test "respects source" do
+      struct_with_custom_source = put_meta(%MySchema{id: 2}, source: "custom_schema")
+      TestRepo.reload(struct_with_custom_source)
+      assert_received {:all, %{from: %{source: {"custom_schema", MySchema}}}}
+    end
   end
 
   defmodule DefaultOptionRepo do