Skip to content

fix: correct prefix name when loading many_to_many relationships#736

Merged
zachdaniel merged 1 commit intoash-project:mainfrom
gcugnet:bugfix/correct-prefix-name-when-loading-many-to-many-relationship-in-different-schema
Apr 14, 2026
Merged

fix: correct prefix name when loading many_to_many relationships#736
zachdaniel merged 1 commit intoash-project:mainfrom
gcugnet:bugfix/correct-prefix-name-when-loading-many-to-many-relationship-in-different-schema

Conversation

@gcugnet
Copy link
Copy Markdown
Contributor

@gcugnet gcugnet commented Apr 14, 2026

Commit 4ddcffe
introduced a bug when loading many_to_many
relationships living in a different schema.
This commit fixes it.

Contributor checklist

Leave anything that you believe does not apply unchecked.

  • I accept the AI Policy, or AI was not used in the creation of this PR.
  • Bug fixes include regression tests
  • Chores
  • Documentation changes
  • Features include unit/acceptance tests
  • Refactoring
  • Update dependencies

Context

Loading many_to_many relationships across different schemas is broken since commit.
See issue "Wrong schema is being used when querying cross-schema many_to_many relationships" #735 for more details on how to reproduce it.

Integration tests in one of my projects indicated me the error. I then searched for the responsible commit, and IA helped me to find the right fix.

Concept

Detail

(from Claude Opus 4.5)

The code in lateral_join_query used through_relationship.source for schema prefix determination, which returned the wrong resource when the join table lived in a different PostgreSQL schema than the source resource. This caused queries to look for the join table in the wrong schema (e.g., public.profile_interests instead of profiles.profile_interests).

Why does through_resource.resource works and through_relationship.source didn’t?

through_relationship.source refers to the source resource of the relationship in the traversal path. For a many-to-many from Profile → Interest through ProfileInterest, the through_relationship is the internal relationship used to traverse the path, and its .source points back to Profile (the original source), not the join table.

through_resource.resource is the resource module from the Ash.Query struct that was explicitly constructed from the join table resource earlier in the code (in relationships.ex). It directly contains the correct resource module (ProfileInterest).

So when Profile is in "profiles" schema and ProfileInterest is also in "profiles" schema:

  • through_relationship.source → Profile → correct by coincidence
  • through_resource.resource → ProfileInterest → correct by design

But when Interest is in "interest" schema and you load interest.profiles:

  • through_relationship.source → Interest (wrong - "interest" schema)
  • through_resource.resource → ProfileInterest (correct - "profiles" schema)

The fix works because through_resource was already built from the right resource, so we just use that directly instead of trying to infer it from the relationship's source.

Tests

I locally runned the tests. 2 are failing, but were already failing before this PR:

1) test has_one through relationships student teacher through classroom active_teacher (AshPostgres.Test.ThroughRelationshipsTest)
     test/through_relationships_test.exs:142
     ** (Ash.Error.Unknown) 
     Bread Crumbs:
       > Error returned from: AshPostgres.Test.Through.Teacher.read
       > Error returned from: AshPostgres.Test.Through.Student.read

     Unknown Error

     * Invalid reference retired_at
         at teacher, filter
       (ash 3.24.0) lib/ash/actions/read/read.ex:88: Ash.Actions.Read.run/3
       (ash 3.24.0) lib/ash.ex:2571: Ash.load/3
       (ash 3.24.0) lib/ash.ex:2525: Ash.load/3
       (ash 3.24.0) lib/ash.ex:2427: Ash.load!/3
       test/through_relationships_test.exs:150: AshPostgres.Test.ThroughRelationshipsTest."test has_one through relationships student teacher through classroom active_teacher"/1
       (ex_unit 1.19.5) lib/ex_unit/runner.ex:528: ExUnit.Runner.exec_test/2
       (ex_unit 1.19.5) lib/ex_unit/capture_log.ex:121: ExUnit.CaptureLog.with_log/2
       (ex_unit 1.19.5) lib/ex_unit/runner.ex:477: anonymous fn/3 in ExUnit.Runner.maybe_capture_log/3
       (stdlib 7.3) timer.erl:599: :timer.tc/2
       (ex_unit 1.19.5) lib/ex_unit/runner.ex:450: anonymous fn/6 in ExUnit.Runner.spawn_test_monitor/4
     code: student_with_teacher = Ash.load!(student_1, :teacher)
     stacktrace:
       (ash 3.24.0) lib/ash/actions/read/read.ex:88: Ash.Actions.Read.run/3
       (ash 3.24.0) lib/ash.ex:2571: Ash.load/3
       (ash 3.24.0) lib/ash.ex:2525: Ash.load/3
       (ash 3.24.0) lib/ash.ex:2427: Ash.load!/3
       test/through_relationships_test.exs:150: (test)
       
2) test aggregates on through relationships students know their active teacher (AshPostgres.Test.ThroughRelationshipsTest)
     test/through_relationships_test.exs:237
     ** (Ash.Error.Unknown) 
     Bread Crumbs:
       > Error returned from: AshPostgres.Test.Through.Teacher.read
       > Error returned from: AshPostgres.Test.Through.Student.read

     Unknown Error

     * Invalid reference retired_at
         at teacher, filter
       (ash 3.24.0) lib/ash/actions/read/read.ex:88: Ash.Actions.Read.run/3
       (ash 3.24.0) lib/ash.ex:2571: Ash.load/3
       (ash 3.24.0) lib/ash.ex:2525: Ash.load/3
       (ash 3.24.0) lib/ash.ex:2427: Ash.load!/3
       test/through_relationships_test.exs:248: AshPostgres.Test.ThroughRelationshipsTest."test aggregates on through relationships students know their active teacher"/1
       (ex_unit 1.19.5) lib/ex_unit/runner.ex:528: ExUnit.Runner.exec_test/2
       (ex_unit 1.19.5) lib/ex_unit/capture_log.ex:121: ExUnit.CaptureLog.with_log/2
       (ex_unit 1.19.5) lib/ex_unit/runner.ex:477: anonymous fn/3 in ExUnit.Runner.maybe_capture_log/3
       (stdlib 7.3) timer.erl:599: :timer.tc/2
       (ex_unit 1.19.5) lib/ex_unit/runner.ex:450: anonymous fn/6 in ExUnit.Runner.spawn_test_monitor/4
     code: student_1 = Ash.load!(student_1, [:teacher, :retired_teacher_count])
     stacktrace:
       (ash 3.24.0) lib/ash/actions/read/read.ex:88: Ash.Actions.Read.run/3
       (ash 3.24.0) lib/ash.ex:2571: Ash.load/3
       (ash 3.24.0) lib/ash.ex:2525: Ash.load/3
       (ash 3.24.0) lib/ash.ex:2427: Ash.load!/3
       test/through_relationships_test.exs:248: (test)
  • the PR adds a regression test which fails if you revert the updated code in data_layer.ex (that will ensure this kind of bug should not happen in the future)
  • the fix also fixed my project's tests, as intended

Commit 4ddcffe
introduced a bug when loading many_to_many
relationships living in a different schema.
This commit fixes it.
@zachdaniel zachdaniel merged commit f8cab09 into ash-project:main Apr 14, 2026
11 of 20 checks passed
@zachdaniel
Copy link
Copy Markdown
Contributor

🚀 Thank you for your contribution! 🚀

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