{"id":31665198,"url":"https://github.com/jechol/ash_sql_sort_breaks_distinct_repro","last_synced_at":"2025-10-07T21:16:17.987Z","repository":{"id":314167754,"uuid":"1052463133","full_name":"jechol/ash_sql_sort_breaks_distinct_repro","owner":"jechol","description":"ash_sql sort breaks distinct repro","archived":false,"fork":false,"pushed_at":"2025-09-08T05:03:35.000Z","size":15,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-23T19:44:00.049Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jechol.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-08T05:03:17.000Z","updated_at":"2025-09-08T05:03:38.000Z","dependencies_parsed_at":"2025-09-11T01:31:08.941Z","dependency_job_id":"277462e0-13d6-46b6-bab6-4c5e3efcd7cf","html_url":"https://github.com/jechol/ash_sql_sort_breaks_distinct_repro","commit_stats":null,"previous_names":["jechol/ash_sql_sort_breaks_distinct_repro"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jechol/ash_sql_sort_breaks_distinct_repro","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jechol%2Fash_sql_sort_breaks_distinct_repro","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jechol%2Fash_sql_sort_breaks_distinct_repro/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jechol%2Fash_sql_sort_breaks_distinct_repro/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jechol%2Fash_sql_sort_breaks_distinct_repro/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jechol","download_url":"https://codeload.github.com/jechol/ash_sql_sort_breaks_distinct_repro/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jechol%2Fash_sql_sort_breaks_distinct_repro/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278848467,"owners_count":26056524,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-07T02:00:06.786Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-10-07T21:16:16.910Z","updated_at":"2025-10-07T21:16:17.952Z","avatar_url":"https://github.com/jechol.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ash_sql sort breaks distinct repro\n\n## Problem Overview\n\n**Issue**: When a related resource in a `has_many` relationship has `prepare build(sort: [:id])` defined, DISTINCT doesn't work properly, causing duplicate records to be returned.\n\n## Bug Details\n\n### Data Structure\n- `Customer` has_many `:purchased_products` (through Product)\n- `Product` has_many `:orders` + **`prepare build(sort: [:id])`** ← Root cause\n- `Order` belongs_to `:customer`, `:product` (acts as join table)\n\n### Expected vs Actual Behavior\n- **Expected**: Even if a Customer orders the same Product multiple times, `purchased_products` should return that Product only once (DISTINCT applied)\n- **Actual**: When Product has sorting, the same Product is returned multiple times\n\n## Root Cause Analysis\n\n### Generated SQL Query\n```sql\nSELECT DISTINCT\n\tc0.\"id\",\n\ts1.\"id\",\n\ts1.\"__order__\"\nFROM\n\t\"public\".\"customers\" AS c0\n\tINNER JOIN LATERAL (\n\t\tSELECT\n\t\t\tsp0.\"id\" AS \"id\",\n\t\t\trow_number() OVER \"order\" AS \"__order__\"\n\t\tFROM\n\t\t\t\"public\".\"products\" AS sp0\n\t\t\tINNER JOIN \"public\".\"orders\" AS so1 ON sp0.\"id\" = so1.\"product_id\"\n\t\tWHERE\n\t\t\t(so1.\"customer_id\"::UUID = c0.\"id\"::UUID::UUID)\n\t\tWINDOW\n\t\t\t\"order\" AS (\n\t\t\t\tORDER BY\n\t\t\t\t\tsp0.\"id\"\n\t\t\t)\n\t\tORDER BY\n\t\t\tsp0.\"id\"\n\t) AS s1 ON TRUE\nWHERE\n\t(c0.\"id\"::UUID = ANY ('{f463c739-5c1b-43d7-8470-be43c36f65de}'::UUID []))\nORDER BY\n\ts1.\"__order__\"\n```\n\n### Issues Identified\n1. **DISTINCT only applied to top-level query**: `SELECT DISTINCT` is only in the outer query\n2. **Duplicates occur inside LATERAL JOIN**: Product's `prepare build(sort: [:id])` generates `ORDER BY` and `row_number()`, but when there are multiple Orders for the same Product, duplicate records are created inside the LATERAL JOIN\n3. **DISTINCT is ineffective**: The outer DISTINCT considers `s1.\"__order__\"` as well, so even for the same Product, different `__order__` values prevent duplicate removal\n\n## Bug Reproduction\n\n### Failing Test Due to Bug\n```bash\nMIX_ENV=test mix ecto.reset\nmix test\n```\n\nThe test `\"purchased_products\"` in `customer_test.exs` **fails** because of this bug.\n\n### Test Scenario That Demonstrates the Bug\n1. Create 1 Customer and 1 Product\n2. Create 2 Orders for the same Customer and Product\n3. Execute `customer |\u003e Ash.load!([:purchased_products])`\n4. **Expected**: 1 Product in `purchased_products`\n5. **Actual**: 2 Products returned (duplicates occur)\n\n### Evidence of the Problem\nThe issue can be confirmed by removing the following from `lib/my_domain/product.ex`:\n```elixir\npreparations do\n  prepare build(sort: [:id])  # ← This line causes the bug\nend\n```\n\nWhen this sorting preparation is removed, the test passes and DISTINCT works correctly. This proves that the sorting preparation is the root cause of the bug.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjechol%2Fash_sql_sort_breaks_distinct_repro","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjechol%2Fash_sql_sort_breaks_distinct_repro","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjechol%2Fash_sql_sort_breaks_distinct_repro/lists"}