Skip to content

Commit f8cab09

Browse files
authored
fix: correct prefix name when loading many_to_many relationships (#736)
Commit 4ddcffe introduced a bug when loading many_to_many relationships living in a different schema. This commit fixes it.
1 parent 1a849bf commit f8cab09

7 files changed

Lines changed: 189 additions & 4 deletions

File tree

lib/data_layer.ex

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1323,7 +1323,7 @@ defmodule AshPostgres.DataLayer do
13231323
through_query.limit != nil || through_query.order_bys != [] ||
13241324
(through_query.joins && through_query.joins != []) ||
13251325
has_subqueries_in_wheres? ||
1326-
(Ash.Resource.Info.multitenancy_strategy(through_relationship.source) ==
1326+
(Ash.Resource.Info.multitenancy_strategy(through_resource.resource) ==
13271327
:context &&
13281328
source_query.tenant)
13291329

@@ -1340,11 +1340,11 @@ defmodule AshPostgres.DataLayer do
13401340
set_subquery_prefix(
13411341
through_query,
13421342
source_query,
1343-
through_relationship.source
1343+
through_resource.resource
13441344
)
13451345
)
13461346
else
1347-
set_subquery_prefix(through_query, source_query, through_relationship.source)
1347+
set_subquery_prefix(through_query, source_query, through_resource.resource)
13481348
end
13491349

13501350
if query.__ash_bindings__[:__order__?] do
@@ -2536,7 +2536,7 @@ defmodule AshPostgres.DataLayer do
25362536

25372537
fields_to_upsert =
25382538
upsert_fields --
2539-
Keyword.keys(Enum.at(changesets, 0).atomics) -- keys
2539+
(Keyword.keys(Enum.at(changesets, 0).atomics) -- keys)
25402540

25412541
fields_to_upsert =
25422542
case fields_to_upsert do
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
defmodule AshPostgres.TestRepo.Migrations.AddCrossSchemaM2MTest do
2+
@moduledoc """
3+
Migration for cross-schema many_to_many regression test.
4+
5+
Creates:
6+
- "interest" schema
7+
- interests table in "interest" schema
8+
- profile_interests table in "profiles" schema (join table)
9+
10+
This tests true cross-schema many_to_many between two custom schemas:
11+
- Interest in "interest" schema
12+
- Profile in "profiles" schema
13+
- ProfileInterest join table in "profiles" schema
14+
"""
15+
use Ecto.Migration
16+
17+
def up do
18+
# Create the "interest" schema
19+
execute "CREATE SCHEMA IF NOT EXISTS interest"
20+
21+
# Interest table in "interest" schema
22+
create table(:interests, primary_key: false, prefix: "interest") do
23+
add :id, :uuid, null: false, primary_key: true
24+
add :name, :text
25+
end
26+
27+
# ProfileInterest join table in "profiles" schema
28+
create table(:profile_interests, primary_key: false, prefix: "profiles") do
29+
add :id, :uuid, null: false, primary_key: true
30+
31+
add :profile_id,
32+
references(:profile,
33+
column: :id,
34+
name: "profile_interests_profile_id_fkey",
35+
type: :uuid,
36+
prefix: "profiles"
37+
),
38+
null: false
39+
40+
add :interest_id,
41+
references(:interests,
42+
column: :id,
43+
name: "profile_interests_interest_id_fkey",
44+
type: :uuid,
45+
prefix: "interest"
46+
),
47+
null: false
48+
end
49+
50+
create unique_index(:profile_interests, [:profile_id, :interest_id], prefix: "profiles")
51+
end
52+
53+
def down do
54+
drop table(:profile_interests, prefix: "profiles")
55+
drop table(:interests, prefix: "interest")
56+
execute "DROP SCHEMA IF EXISTS interest"
57+
end
58+
end
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# SPDX-FileCopyrightText: 2019 ash_postgres contributors <https://github.com/ash-project/ash_postgres/graphs/contributors>
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
defmodule AshPostgres.CrossSchemaManyToManyTest do
6+
@moduledoc """
7+
Regression test for cross-schema many_to_many relationships.
8+
9+
Test setup (two custom schemas):
10+
- Profile lives in "profiles" schema
11+
- Interest lives in "interest" schema
12+
- ProfileInterest (join table) lives in "profiles" schema
13+
"""
14+
use AshPostgres.RepoCase, async: false
15+
16+
alias AshPostgres.Test.{Interest, Profile, ProfileInterest}
17+
18+
test "load many_to_many across custom schemas" do
19+
profile = Ash.create!(Profile, %{description: "Test"})
20+
interest = Ash.create!(Interest, %{name: "eating the dogs"})
21+
22+
Ash.create!(ProfileInterest, %{profile_id: profile.id, interest_id: interest.id})
23+
24+
assert [%{description: "Test"}] = Ash.load!(interest, :profiles).profiles
25+
end
26+
end

test/support/domain.ex

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ defmodule AshPostgres.Test.Domain do
7272
resource(AshPostgres.Test.Through.Teacher)
7373
resource(AshPostgres.Test.Through.ClassroomTeacher)
7474
resource(AshPostgres.Test.Through.Student)
75+
resource(AshPostgres.Test.Interest)
76+
resource(AshPostgres.Test.ProfileInterest)
7577
end
7678

7779
authorization do

test/support/resources/interest.ex

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# SPDX-FileCopyrightText: 2019 ash_postgres contributors <https://github.com/ash-project/ash_postgres/graphs/contributors>
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
defmodule AshPostgres.Test.Interest do
6+
@moduledoc """
7+
An interest resource in the "interest" schema (e.g., "sports", "music", "coding").
8+
9+
Used to test cross-schema many_to_many relationships:
10+
- Interest lives in "interest" schema
11+
- Profile lives in "profiles" schema
12+
- ProfileInterest (join table) lives in "profiles" schema
13+
"""
14+
use Ash.Resource,
15+
domain: AshPostgres.Test.Domain,
16+
data_layer: AshPostgres.DataLayer
17+
18+
postgres do
19+
table "interests"
20+
schema "interest"
21+
repo AshPostgres.TestRepo
22+
end
23+
24+
attributes do
25+
uuid_primary_key(:id)
26+
attribute(:name, :string, public?: true)
27+
end
28+
29+
actions do
30+
default_accept(:*)
31+
defaults([:read, :destroy, create: :*, update: :*])
32+
end
33+
34+
relationships do
35+
many_to_many :profiles, AshPostgres.Test.Profile do
36+
public?(true)
37+
through(AshPostgres.Test.ProfileInterest)
38+
source_attribute_on_join_resource(:interest_id)
39+
destination_attribute_on_join_resource(:profile_id)
40+
end
41+
end
42+
end

test/support/resources/profile.ex

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,14 @@ defmodule AshPostgres.Test.Profile do
5353
belongs_to(:author, AshPostgres.Test.Author) do
5454
public?(true)
5555
end
56+
57+
# Cross-schema many_to_many: Profile (profiles schema) -> Interest (public schema)
58+
# through ProfileInterest (profiles schema)
59+
many_to_many :interests, AshPostgres.Test.Interest do
60+
public?(true)
61+
through(AshPostgres.Test.ProfileInterest)
62+
source_attribute_on_join_resource(:profile_id)
63+
destination_attribute_on_join_resource(:interest_id)
64+
end
5665
end
5766
end
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# SPDX-FileCopyrightText: 2019 ash_postgres contributors <https://github.com/ash-project/ash_postgres/graphs/contributors>
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
defmodule AshPostgres.Test.ProfileInterest do
6+
@moduledoc """
7+
A join table in the "profiles" schema linking Profile to Interest.
8+
9+
This tests cross-schema many_to_many relationships between two custom schemas:
10+
- Profile is in "profiles" schema
11+
- Interest is in "interest" schema
12+
- ProfileInterest (this resource) is in "profiles" schema
13+
"""
14+
use Ash.Resource,
15+
domain: AshPostgres.Test.Domain,
16+
data_layer: AshPostgres.DataLayer
17+
18+
postgres do
19+
table "profile_interests"
20+
schema "profiles"
21+
repo AshPostgres.TestRepo
22+
end
23+
24+
attributes do
25+
uuid_primary_key(:id)
26+
end
27+
28+
actions do
29+
default_accept(:*)
30+
defaults([:read, :destroy, create: :*, update: :*])
31+
end
32+
33+
relationships do
34+
belongs_to :profile, AshPostgres.Test.Profile do
35+
public?(true)
36+
allow_nil?(false)
37+
end
38+
39+
belongs_to :interest, AshPostgres.Test.Interest do
40+
public?(true)
41+
allow_nil?(false)
42+
end
43+
end
44+
45+
identities do
46+
identity(:unique_profile_interest, [:profile_id, :interest_id])
47+
end
48+
end

0 commit comments

Comments
 (0)