Skip to content

Commit 9b015d8

Browse files
committed
Merge branch 'main' into fix/tenant-migrations-should-honor-module-attributes
* main: (23 commits) fix: do not build query filter with or: [] when all records are successfully inserted, for return_skipped_upsert?: true (#659) test: load aggregate with select and sort on from_many relationship (#661) test: add failing test for expr(has_one.function_calc) nil poisoning in batch (#660) test: aggregate with parent ref in relationship filter and sorting on relationship field (#658) test: binding error when loading aggregate (#657) chore(deps): bump ash_sql in the production-dependencies group (#656) test: multi relationship problems (#654) chore: release version v2.6.26 chore: update ash_sql, use new select binding test: add complex calculation tests with filtered aggregates (#652) improvement: add generator to tsvector type (#655) test: add failing test for aggregate filtering on nested first aggregate (#653) chore: update ash_sql and add test docs: Mention :define_ecto_repo? option of use AshPostgres.Repo macro in getting started guide (#648) improvement: verify check constraint attributes at compile time chore: update ash chore: reuse compliance improvement: update deps for bug fixes chore: release version v2.6.25 test: Add failing test for aggregate with parent() + select() + limit() (#644) ...
2 parents 89c913b + 23cc10a commit 9b015d8

File tree

55 files changed

+3379
-58
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+3379
-58
lines changed

CHANGELOG.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,43 @@ See [Conventional Commits](https://www.conventionalcommits.org) for commit guide
1111

1212
<!-- changelog -->
1313

14+
## [v2.6.26](https://github.com/ash-project/ash_postgres/compare/v2.6.25...v2.6.26) (2025-11-23)
15+
16+
17+
18+
19+
### Improvements:
20+
21+
* add generator to tsvector type (#655) by Jesse Williams
22+
23+
* verify check constraint attributes at compile time by Zach Daniel
24+
25+
* update deps for bug fixes by Zach Daniel
26+
27+
## [v2.6.25](https://github.com/ash-project/ash_postgres/compare/v2.6.24...v2.6.25) (2025-11-05)
28+
29+
30+
31+
32+
### Bug Fixes:
33+
34+
* add failing test for exists expansnion inside of calculations by Zach Daniel
35+
36+
## [v2.6.24](https://github.com/ash-project/ash_postgres/compare/v2.6.23...v2.6.24) (2025-10-30)
37+
38+
39+
40+
41+
### Bug Fixes:
42+
43+
* handle results that can't be mapped to the changeset in bulk_create (#638) by Barnabas Jovanovics
44+
45+
* handle results that can't be mapped to the changeset in bulk_create by Barnabas Jovanovics
46+
47+
### Improvements:
48+
49+
* remove unused bulk operation metadata function & update ash by Zach Daniel
50+
1451
## [v2.6.23](https://github.com/ash-project/ash_postgres/compare/v2.6.22...v2.6.23) (2025-10-15)
1552

1653

documentation/tutorials/get-started-with-ash-postgres.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,28 @@ defmodule Helpdesk.Repo do
5656
end
5757
```
5858

59+
If your project already defines its own `Ecto.Repo` (for example, to integrate with third-party libraries such as AppSignal), you can prevent `AshPostgres.Repo` from automatically defining one by setting the `define_ecto_repo?: false` option:
60+
61+
```elixir
62+
# in lib/helpdesk/repo.ex
63+
64+
defmodule Helpdesk.Repo do
65+
use Appsignal.Ecto.Repo,
66+
otp_app: :helpdesk,
67+
adapter: Ecto.Adapters.Postgres
68+
69+
use AshPostgres.Repo,
70+
define_ecto_repo?: false
71+
72+
def installed_extensions do
73+
["ash-functions"]
74+
end
75+
end
76+
```
77+
78+
This lets you keep full control over your existing `Ecto.Repo` definition while still enabling AshPostgres features.
79+
80+
5981
Next we will need to create configuration files for various environments. Run the following to create the configuration files we need.
6082

6183
```bash

lib/data_layer.ex

Lines changed: 45 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,7 @@ defmodule AshPostgres.DataLayer do
417417
verifiers: [
418418
AshPostgres.Verifiers.PreventMultidimensionalArrayAggregates,
419419
AshPostgres.Verifiers.ValidateReferences,
420+
AshPostgres.Verifiers.ValidateCheckConstraints,
420421
AshPostgres.Verifiers.PreventAttributeMultitenancyAndNonFullMatchType,
421422
AshPostgres.Verifiers.EnsureTableOrPolymorphic,
422423
AshPostgres.Verifiers.ValidateIdentityIndexNames
@@ -2058,39 +2059,46 @@ defmodule AshPostgres.DataLayer do
20582059
{Map.take(r, keys), r}
20592060
end)
20602061

2061-
ash_query =
2062-
resource
2063-
|> Ash.Query.do_filter(
2064-
or:
2065-
changesets
2066-
|> Enum.filter(fn changeset ->
2067-
not Map.has_key?(
2068-
results_by_identity,
2069-
Map.take(changeset.attributes, keys)
2070-
)
2071-
end)
2072-
|> Enum.map(fn changeset ->
2073-
changeset.attributes
2074-
|> Map.take(keys)
2075-
|> Keyword.new()
2076-
end)
2077-
)
2078-
|> then(fn
2079-
query when is_nil(identity) or is_nil(identity.where) -> query
2080-
query -> Ash.Query.do_filter(query, identity.where)
2062+
skipped_filter =
2063+
changesets
2064+
|> Enum.filter(fn changeset ->
2065+
not Map.has_key?(
2066+
results_by_identity,
2067+
Map.take(changeset.attributes, keys)
2068+
)
2069+
end)
2070+
|> Enum.map(fn changeset ->
2071+
changeset.attributes
2072+
|> Map.take(keys)
2073+
|> Keyword.new()
20812074
end)
2082-
|> Ash.Query.set_tenant(changeset.tenant)
20832075

20842076
skipped_upserts =
2085-
with {:ok, ecto_query} <- Ash.Query.data_layer_query(ash_query),
2086-
{:ok, results} <- run_query(ecto_query, resource) do
2087-
results
2088-
|> Enum.map(fn result ->
2089-
Ash.Resource.put_metadata(result, :upsert_skipped, true)
2090-
end)
2091-
|> Enum.reduce(%{}, fn r, acc ->
2092-
Map.put(acc, Map.take(r, keys), r)
2093-
end)
2077+
case skipped_filter do
2078+
[] ->
2079+
# No skipped records to query for
2080+
%{}
2081+
2082+
skipped_filter ->
2083+
ash_query =
2084+
resource
2085+
|> Ash.Query.do_filter(or: skipped_filter)
2086+
|> then(fn
2087+
query when is_nil(identity) or is_nil(identity.where) -> query
2088+
query -> Ash.Query.do_filter(query, identity.where)
2089+
end)
2090+
|> Ash.Query.set_tenant(changeset.tenant)
2091+
2092+
with {:ok, ecto_query} <- Ash.Query.data_layer_query(ash_query),
2093+
{:ok, results} <- run_query(ecto_query, resource) do
2094+
results
2095+
|> Enum.map(fn result ->
2096+
Ash.Resource.put_metadata(result, :upsert_skipped, true)
2097+
end)
2098+
|> Enum.reduce(%{}, fn r, acc ->
2099+
Map.put(acc, Map.take(r, keys), r)
2100+
end)
2101+
end
20942102
end
20952103

20962104
results =
@@ -2143,8 +2151,8 @@ defmodule AshPostgres.DataLayer do
21432151
# Compatibility fallback
21442152
Ash.Resource.put_metadata(
21452153
result_for_changeset,
2146-
:bulk_create_index,
2147-
changeset.context[:bulk_create][:index]
2154+
:bulk_action_ref,
2155+
changeset.context[:bulk_create][:ref]
21482156
)
21492157
end
21502158
end)
@@ -2160,8 +2168,8 @@ defmodule AshPostgres.DataLayer do
21602168
# Compatibility fallback
21612169
Ash.Resource.put_metadata(
21622170
result,
2163-
:bulk_create_index,
2164-
changeset.context[:bulk_create][:index]
2171+
:bulk_action_ref,
2172+
changeset.context[:bulk_create][:ref]
21652173
)
21662174
end)
21672175
end
@@ -3455,6 +3463,7 @@ defmodule AshPostgres.DataLayer do
34553463
@impl true
34563464
def select(query, select, _resource) do
34573465
query = maybe_subquery_upgrade(query, {:select, select})
3466+
query = put_in(query.__ash_bindings__[:select], select)
34583467

34593468
if query.__ash_bindings__.context[:data_layer][:combination_query?] ||
34603469
query.__ash_bindings__.context[:data_layer][:combination_of_queries?] do
@@ -3615,6 +3624,8 @@ defmodule AshPostgres.DataLayer do
36153624
:__ash_bindings__,
36163625
&Map.merge(&1, %{
36173626
already_selected: fieldset,
3627+
select: query.__ash_bindings__[:select],
3628+
select_calculations: query.__ash_bindings__[:select_calculations],
36183629
subquery_upgrade?: true,
36193630
sort: query.__ash_bindings__[:sort],
36203631
context: query.__ash_bindings__.context

lib/types/tsvector.ex

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,9 @@ defmodule AshPostgres.Tsvector do
6464
def cast_stored(_, _) do
6565
:error
6666
end
67+
68+
@impl true
69+
def generator(_constraints) do
70+
StreamData.constant([])
71+
end
6772
end
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# SPDX-FileCopyrightText: 2019 ash_postgres contributors <https://github.com/ash-project/ash_postgres/graphs.contributors>
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
defmodule AshPostgres.Verifiers.ValidateCheckConstraints do
6+
@moduledoc false
7+
use Spark.Dsl.Verifier
8+
alias Spark.Dsl.Verifier
9+
10+
def verify(dsl) do
11+
resource = Verifier.get_persisted(dsl, :module)
12+
13+
dsl
14+
|> AshPostgres.DataLayer.Info.check_constraints()
15+
|> Enum.each(fn constraint ->
16+
constraint.attribute
17+
|> List.wrap()
18+
|> Enum.each(fn attribute_name ->
19+
if is_nil(Ash.Resource.Info.attribute(dsl, attribute_name)) do
20+
raise Spark.Error.DslError,
21+
path: [:postgres, :check_constraints, constraint.name],
22+
module: resource,
23+
message: """
24+
Check constraint `#{constraint.name}` references attribute `#{attribute_name}`, but no such attribute exists on resource `#{inspect(resource)}`.
25+
26+
Available attributes: #{dsl |> Ash.Resource.Info.attributes() |> Enum.map(& &1.name) |> inspect()}
27+
""",
28+
location: Spark.Dsl.Transformer.get_section_anno(dsl, [:postgres, :check_constraints])
29+
end
30+
end)
31+
end)
32+
33+
:ok
34+
end
35+
end

mix.exs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ defmodule AshPostgres.MixProject do
99
The PostgreSQL data layer for Ash Framework
1010
"""
1111

12-
@version "2.6.23"
12+
@version "2.6.26"
1313

1414
def project do
1515
[
@@ -177,9 +177,9 @@ defmodule AshPostgres.MixProject do
177177
# Run "mix help deps" to learn about dependencies.
178178
defp deps do
179179
[
180-
{:ash, ash_version("~> 3.7 and >= 3.7.5")},
180+
{:ash, ash_version("~> 3.10")},
181181
{:spark, "~> 2.3 and >= 2.3.4"},
182-
{:ash_sql, ash_sql_version("~> 0.3 and >= 0.3.7")},
182+
{:ash_sql, ash_sql_version("~> 0.3 and >= 0.3.14")},
183183
{:igniter, "~> 0.6 and >= 0.6.29", optional: true},
184184
{:ecto_sql, "~> 3.13"},
185185
{:ecto, "~> 3.13"},
@@ -197,7 +197,8 @@ defmodule AshPostgres.MixProject do
197197
{:credo, ">= 0.0.0", only: [:dev, :test], runtime: false},
198198
{:mix_audit, ">= 0.0.0", only: [:dev, :test], runtime: false},
199199
{:dialyxir, ">= 0.0.0", only: [:dev, :test], runtime: false},
200-
{:sobelow, ">= 0.0.0", only: [:dev, :test], runtime: false}
200+
{:sobelow, ">= 0.0.0", only: [:dev, :test], runtime: false},
201+
{:mix_test_interactive, "~> 5.0", only: [:dev, :test]}
201202
]
202203
end
203204

0 commit comments

Comments
 (0)