Skip to content

Commit 702a263

Browse files
committed
fix: has_many with limit and sort not respected in exists query filters
Previously, the relationship limit was not being applied when using the relationship in an `exists` query. After fixing that, then the limit was being applied in the wrong place - it needs to be *inside* the exists check pre-filtering, instead of outside it. The test for this feature is in AshPostgres ash-project/ash_postgres#667 - this code makes that test pass.
1 parent 97530ff commit 702a263

File tree

2 files changed

+45
-2
lines changed

2 files changed

+45
-2
lines changed

lib/expr.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2373,7 +2373,7 @@ defmodule AshSql.Expr do
23732373
AshSql.Join.related_subquery(first_relationship, query,
23742374
filter: filter,
23752375
filter_subquery?: true,
2376-
sort?: Map.get(first_relationship, :from_many?),
2376+
sort?: Map.get(first_relationship, :from_many?) || not is_nil(first_relationship.sort),
23772377
start_bindings_at: 1,
23782378
select_star?: !Map.get(first_relationship, :manual),
23792379
in_group?: true,

lib/join.ex

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,8 @@ defmodule AshSql.Join do
428428
|> Ash.Query.set_context(relationship.context)
429429
|> Ash.Query.do_filter(relationship.filter, parent_stack: parent_resources)
430430
|> then(fn query ->
431-
if Map.get(relationship, :from_many?) && filter_subquery? do
431+
if (Map.get(relationship, :from_many?) || not is_nil(Map.get(relationship, :limit))) &&
432+
filter_subquery? do
432433
query
433434
else
434435
Ash.Query.do_filter(query, filter, parent_stack: parent_resources)
@@ -576,6 +577,48 @@ defmodule AshSql.Join do
576577
end
577578
end
578579

580+
defp limit_from_many(
581+
query,
582+
%{limit: limit, destination: destination},
583+
filter,
584+
filter_subquery?,
585+
opts
586+
)
587+
when is_integer(limit) do
588+
# Check if query has parent expressions - if so, we can't wrap in a non-lateral subquery
589+
# because parent references won't resolve across the subquery boundary
590+
has_parent_expr? = !!query.__ash_bindings__.context[:data_layer][:has_parent_expr?]
591+
592+
if filter_subquery? && !has_parent_expr? do
593+
# Wrap the limited query in a subquery, then apply filter on top
594+
query =
595+
from(row in Ecto.Query.subquery(from(row in query, limit: ^limit)),
596+
as: ^query.__ash_bindings__.root_binding
597+
)
598+
|> Map.put(:__ash_bindings__, query.__ash_bindings__)
599+
|> AshSql.Bindings.default_bindings(
600+
destination,
601+
query.__ash_bindings__.sql_behaviour
602+
)
603+
604+
{:ok, query} = AshSql.Filter.filter(query, filter, query.__ash_bindings__.resource)
605+
606+
if opts[:select_star?] do
607+
from(row in Ecto.Query.exclude(query, :select), select: 1)
608+
else
609+
query
610+
end
611+
else
612+
# When has_parent_expr?, we can't apply the limit in exists subquery
613+
# Fall through to the default clause which just applies select_star if needed
614+
if opts[:select_star?] do
615+
from(row in Ecto.Query.exclude(query, :select), select: 1)
616+
else
617+
query
618+
end
619+
end
620+
end
621+
579622
defp limit_from_many(query, _, _, _, opts) do
580623
if opts[:select_star?] do
581624
from(row in Ecto.Query.exclude(query, :select), select: 1)

0 commit comments

Comments
 (0)