Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 34 additions & 8 deletions lib/aggregate.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2096,6 +2096,25 @@ defmodule AshSql.Aggregate do
defp has_sort?(%{sort: _}), do: true
defp has_sort?(_), do: false

# `array_agg(DISTINCT x ORDER BY y)` is rejected by postgres unless every ORDER BY
# expression also appears in the argument list, so a `uniq?` aggregate can only be
# ordered by the field it aggregates. Sorting by anything else is discarded rather
# than emitted as invalid SQL - it is unsatisfiable either way, since the ordering
# key is exactly what deduplication throws away. This also keeps a `sort` declared
# on the relationship from reaching an aggregate that never asked to be sorted.
defp distinct_safe_sort(sort, aggregate) do

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm...I merged the other tests PR, but looking at the implementation here I'm not so sure. We're basically discarding the sort here in practice. The results are no longer sorted in the way you'd expect them to have been sorted.

I'm not actually sure what SQL formulation here would satisfy it though so it makes me think that we may actually want to reject this formulation entirely somehow, requiring that users set like a sorted? false option that will discard the underlying sort?

if Map.get(aggregate, :uniq?) do
aggregated_field = Map.get(aggregate, :field)

Enum.filter(sort, fn
{field, _direction} -> field == aggregated_field
_ -> false
end)
else
sort
end
end

def add_subquery_aggregate_select(
query,
relationship_path,
Expand Down Expand Up @@ -2327,15 +2346,22 @@ defmodule AshSql.Aggregate do

has_sort? = has_sort?(aggregate.query)

{sorted, include_nil_filter_field, query} =
if has_sort? || (first_relationship && first_relationship.sort not in [nil, []]) do
{sort, binding} =
if has_sort? do
{aggregate.query.sort, binding}
else
{List.wrap(first_relationship.sort), query.__ash_bindings__.root_binding}
end
{sort, binding} =
cond do
has_sort? ->
{aggregate.query.sort, binding}

first_relationship && first_relationship.sort not in [nil, []] ->
{List.wrap(first_relationship.sort), query.__ash_bindings__.root_binding}

true ->
{[], binding}
end

sort = distinct_safe_sort(sort, aggregate)

{sorted, include_nil_filter_field, query} =
if sort != [] do
{:ok, sort_expr, query} =
AshSql.Sort.sort(
query,
Expand Down
3 changes: 1 addition & 2 deletions lib/join.ex
Original file line number Diff line number Diff line change
Expand Up @@ -1189,13 +1189,12 @@ defmodule AshSql.Join do
current_binding
) do
case related_subquery(relationship, query,
sort?: sort?,
apply_filter: apply_filter,
start_bindings_at: 500,
refs_at_path: path,
require_lateral?: require_lateral?,
filter_subquery?: true,
sort?: Map.get(relationship, :from_many?),
sort?: sort? && !!Map.get(relationship, :from_many?),
on_subquery: fn subquery ->
if !Map.get(relationship, :from_many?) || Map.get(relationship, :no_attributes?) do
subquery
Expand Down