@@ -34,6 +34,7 @@ defmodule AshPostgres.DataLayer do
3434]
3535}
3636
37+ alias Ash.DataLayer.Delegate
3738alias Ash.Filter
3839alias Ash.Filter . { Expression , Not , Predicate }
3940alias Ash.Filter.Predicate . { Eq , GreaterThan , In , IsNil , LessThan }
@@ -78,7 +79,21 @@ defmodule AshPostgres.DataLayer do
7879def can? ( _ , :transact ) , do: true
7980def can? ( _ , :composite_primary_key ) , do: true
8081def can? ( _ , :upsert ) , do: true
81- def can? ( _ , :join ) , do: true
82+
83+ def can? ( resource , { :join , other_resource } ) do
84+ other_resource = Delegate . get_delegated ( other_resource )
85+ data_layer = Ash.Resource . data_layer ( resource )
86+ other_data_layer = Ash.Resource . data_layer ( other_resource )
87+ data_layer == other_data_layer and repo ( data_layer ) == repo ( other_data_layer )
88+ end
89+
90+ def can? ( resource , { :lateral_join , other_resource } ) do
91+ other_resource = Delegate . get_delegated ( other_resource )
92+ data_layer = Ash.Resource . data_layer ( resource )
93+ other_data_layer = Ash.Resource . data_layer ( other_resource )
94+ data_layer == other_data_layer and repo ( data_layer ) == repo ( other_data_layer )
95+ end
96+
8297def can? ( _ , :boolean_filter ) , do: true
8398def can? ( _ , { :aggregate , :count } ) , do: true
8499def can? ( _ , :aggregate_filter ) , do: true
@@ -130,6 +145,38 @@ defmodule AshPostgres.DataLayer do
130145{ :ok , repo ( resource ) . all ( query ) }
131146end
132147
148+ @ impl true
149+ def run_query_with_lateral_join (
150+ query ,
151+ root_data ,
152+ source_resource ,
153+ _destination_resource ,
154+ source_field ,
155+ destination_field
156+ ) do
157+ source_values = Enum . map ( root_data , & Map . get ( & 1 , source_field ) )
158+
159+ subquery =
160+ subquery (
161+ from ( destination in query ,
162+ where:
163+ field ( destination , ^ destination_field ) ==
164+ field ( parent_as ( :source_record ) , ^ source_field )
165+ )
166+ )
167+
168+ query =
169+ from ( source in resource_to_query ( source_resource ) ,
170+ as: :source_record ,
171+ where: field ( source , ^ source_field ) in ^ source_values ,
172+ inner_lateral_join: destination in ^ subquery ,
173+ on: field ( source , ^ source_field ) == field ( destination , ^ destination_field ) ,
174+ select: destination
175+ )
176+
177+ { :ok , repo ( source_resource ) . all ( query ) }
178+ end
179+
133180@ impl true
134181def resource_to_query ( resource ) ,
135182do: Ecto.Queryable . to_query ( { table ( resource ) , resource } )
@@ -189,23 +236,23 @@ defmodule AshPostgres.DataLayer do
189236def sort ( query , sort , _resource ) do
190237query = default_bindings ( query )
191238
192- sort_expr =
193- sort
194- |> sanitize_sort ( )
195- |> Enum . map ( fn { order , sort } ->
196- binding =
197- case Map . fetch ( query . __ash_bindings__ . aggregates , sort ) do
198- { :ok , binding } ->
199- binding
200-
201- :error ->
202- 0
203- end
204-
205- { order , { { :. , [ ] , [ { :& , [ ] , [ binding ] } , sort ] } , [ ] , [ ] } }
206- end )
207-
208- { :ok , % { query | order_bys: [ % Ecto.Query.QueryExpr { expr: sort_expr , params: [ ] } ] } }
239+ sort
240+ |> sanitize_sort ( )
241+ |> Enum . reduce ( { :ok , query } , fn { order , sort } , query ->
242+ binding =
243+ case Map . fetch ( query . __ash_bindings__ . aggregates , sort ) do
244+ { :ok , binding } ->
245+ binding
246+
247+ :error ->
248+ 0
249+ end
250+
251+ { :ok ,
252+ from ( [ { ^ binding , row } ] in query ,
253+ order_by: [ { ^ order , field ( row , ^ sort ) } ]
254+ ) }
255+ end )
209256end
210257
211258defp sanitize_sort ( sort ) do
@@ -855,6 +902,6 @@ defmodule AshPostgres.DataLayer do
855902end
856903
857904defp maybe_get_resource_query ( resource ) do
858- { table ( resource ) , resource }
905+ { table ( Delegate . get_delegated ( resource ) ) , resource }
859906end
860907end