Skip to content
Merged
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
74 changes: 74 additions & 0 deletions priv/resource_snapshots/test_repo/chats/20251030040520.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
{
"attributes": [
{
"allow_nil?": false,
"default": "fragment(\"gen_random_uuid()\")",
"generated?": false,
"precision": null,
"primary_key?": true,
"references": null,
"scale": null,
"size": null,
"source": "id",
"type": "uuid"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"precision": null,
"primary_key?": false,
"references": null,
"scale": null,
"size": null,
"source": "name",
"type": "text"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"precision": null,
"primary_key?": false,
"references": {
"deferrable": false,
"destination_attribute": "id",
"destination_attribute_default": null,
"destination_attribute_generated": null,
"index?": false,
"match_type": null,
"match_with": null,
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"name": "chats_last_read_message_id_fkey",
"on_delete": null,
"on_update": null,
"primary_key?": true,
"schema": "public",
"table": "messages"
},
"scale": null,
"size": null,
"source": "last_read_message_id",
"type": "uuid"
}
],
"base_filter": null,
"check_constraints": [],
"custom_indexes": [],
"custom_statements": [],
"has_create_action": true,
"hash": "78989357AB74584B9A18249E307C842D17AA96B7DD790EAADC9FA1C6422388B5",
"identities": [],
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"repo": "Elixir.AshPostgres.TestRepo",
"schema": null,
"table": "chats"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
defmodule AshPostgres.TestRepo.Migrations.AddLastReadMessageToChats do
@moduledoc """
Updates resources based on their most recent snapshots.

This file was autogenerated with `mix ash_postgres.generate_migrations`
"""

use Ecto.Migration

def up do
alter table(:chats) do
add(
:last_read_message_id,
references(:messages,
column: :id,
name: "chats_last_read_message_id_fkey",
type: :uuid,
prefix: "public"
)
)
end
end

def down do
drop(constraint(:chats, "chats_last_read_message_id_fkey"))

alter table(:chats) do
remove(:last_read_message_id)
end
end
end
61 changes: 60 additions & 1 deletion test/aggregate_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
defmodule AshSql.AggregateTest do
use AshPostgres.RepoCase, async: false
import ExUnit.CaptureIO
alias AshPostgres.Test.{Author, Comment, Organization, Post, Rating, User}
alias AshPostgres.Test.{Author, Chat, Comment, Organization, Post, Rating, User}

require Ash.Query
import Ash.Expr
Expand Down Expand Up @@ -1860,4 +1860,63 @@
assert Enum.at(results, 0).count_of_comments == 3
assert Enum.at(results, 1).count_of_comments == 2
end

describe "aggregate with parent filter and limited select" do
test "FAILS when combining select() + limit() with aggregate using parent() in filter" do

Check failure on line 1865 in test/aggregate_test.exs

View workflow job for this annotation

GitHub Actions / ash-ci (16) / mix test

test aggregate with parent filter and limited select FAILS when combining select() + limit() with aggregate using parent() in filter (AshSql.AggregateTest)

Check failure on line 1865 in test/aggregate_test.exs

View workflow job for this annotation

GitHub Actions / ash-ci (15) / mix test

test aggregate with parent filter and limited select FAILS when combining select() + limit() with aggregate using parent() in filter (AshSql.AggregateTest)

Check failure on line 1865 in test/aggregate_test.exs

View workflow job for this annotation

GitHub Actions / ash-ci (14) / mix test

test aggregate with parent filter and limited select FAILS when combining select() + limit() with aggregate using parent() in filter (AshSql.AggregateTest)
# BUG: When using select() + limit() with an aggregate that uses parent()
# in its filter, the query generation creates a subquery that's missing the parent
# fields, causing a SQL error.
#
# This bug was found in ash_graphql where GraphQL list queries with pagination
# would fail when loading aggregates that use parent() in filters.
#
# The bug requires BOTH conditions:
# 1. select() limits which fields are included (e.g., only :id)
# 2. limit() causes a subquery to be generated
# 3. An aggregate filter references parent() fields that aren't in select()
#
# Without BOTH select() and limit(), the query works fine (see tests below).
#
# Current error:
# ERROR 42703 (undefined_column) column s0.last_read_message_id does not exist
#
# Generated query:
# SELECT s0."id", coalesce(s1."unread_message_count"::bigint, ...)
# FROM (SELECT sc0."id" AS "id" FROM "chats" AS sc0 LIMIT 10) AS s0
# LEFT OUTER JOIN LATERAL (
# SELECT ... FROM "messages" WHERE ... s0."last_read_message_id" ... # <- field not in subquery!
# ) AS s1 ON TRUE
#
# Expected fix: Ash should automatically include parent() referenced fields
# (like last_read_message_id) in the subquery even if not explicitly selected.

Chat
|> Ash.Query.select(:id)
|> Ash.Query.load(:unread_message_count)
|> Ash.Query.limit(10)
|> Ash.read!()
end

test "works WITHOUT select() - limit alone doesn't cause the bug" do
Chat
|> Ash.Query.load(:unread_message_count)
|> Ash.Query.limit(10)
|> Ash.read!()
end

test "works WITHOUT limit() - select alone doesn't cause the bug" do
Chat
|> Ash.Query.select(:id)
|> Ash.Query.load(:unread_message_count)
|> Ash.read!()
end

test "works when selecting the parent() referenced field explicitly (workaround)" do
Chat
|> Ash.Query.select([:id, :last_read_message_id])
|> Ash.Query.load(:unread_message_count)
|> Ash.Query.limit(10)
|> Ash.read!()
end
end
end
13 changes: 13 additions & 0 deletions test/support/resources/chat.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ defmodule AshPostgres.Test.Chat do
end

relationships do
belongs_to :last_read_message, AshPostgres.Test.Message do
allow_nil?(true)
public?(true)
attribute_writable?(true)
end

has_many :messages, AshPostgres.Test.Message do
public?(true)
end
Expand All @@ -41,4 +47,11 @@ defmodule AshPostgres.Test.Chat do
sort(sent_at: :desc)
end
end

aggregates do
count :unread_message_count, :messages do
public?(true)
filter(expr(is_nil(parent(last_read_message_id)) or id > parent(last_read_message_id)))
end
end
end
Loading