mirror of
https://github.com/ash-project/ash_postgres.git
synced 2024-09-20 05:23:18 +12:00
improvement: inform users about postgres incompatibility with multidimensional arrays
This commit is contained in:
parent
f088601cf9
commit
52fcba79ac
5 changed files with 80 additions and 37 deletions
|
@ -416,7 +416,8 @@ defmodule AshPostgres.DataLayer do
|
|||
sections: @sections,
|
||||
transformers: [
|
||||
AshPostgres.Transformers.VerifyRepo,
|
||||
AshPostgres.Transformers.EnsureTableOrPolymorphic
|
||||
AshPostgres.Transformers.EnsureTableOrPolymorphic,
|
||||
AshPostgres.Transformers.PreventMultidimensionalArrayAggregates
|
||||
]
|
||||
|
||||
@doc false
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
defmodule AshPostgres.Transformers.PreventMultidimensionalArrayAggregates do
|
||||
@moduledoc "Prevents at compile time certain aggregates that are unsupported by `AshPostgres`"
|
||||
use Spark.Dsl.Transformer
|
||||
alias Spark.Dsl.Transformer
|
||||
|
||||
def after_compile?, do: true
|
||||
|
||||
def transform(dsl) do
|
||||
resource = Transformer.get_persisted(dsl, :module)
|
||||
|
||||
dsl
|
||||
|> Ash.Resource.Info.aggregates()
|
||||
|> Stream.filter(&(&1.kind in [:list, :first]))
|
||||
|> Stream.filter(& &1.field)
|
||||
|> Enum.each(fn aggregate ->
|
||||
related = Ash.Resource.Info.related(resource, aggregate.relationship_path)
|
||||
type = Ash.Resource.Info.attribute(related, aggregate.field).type
|
||||
|
||||
case type do
|
||||
{:array, _} ->
|
||||
raise Spark.Error.DslError,
|
||||
module: resource,
|
||||
path: [:aggregates, aggregate.name],
|
||||
message: """
|
||||
Aggregate not supported.
|
||||
|
||||
Aggregate #{inspect(resource)}.#{aggregate.name} is not supported, because its type is `#{aggregate.kind}`, and the destination attribute is an array.
|
||||
|
||||
Postgres does not support multidimensional arrays with differing lengths internally. In the future we may be able to remove this restriction
|
||||
for the `:first` type aggregate, but likely never for `:list`. In the meantime, you will have to use a custom calculation to get this data.
|
||||
"""
|
||||
|
||||
_ ->
|
||||
:ok
|
||||
end
|
||||
end)
|
||||
|
||||
repo = Transformer.get_option(dsl, [:postgres], :repo)
|
||||
|
||||
cond do
|
||||
match?({:error, _}, Code.ensure_compiled(repo)) ->
|
||||
{:error, "Could not find repo module #{repo}"}
|
||||
|
||||
repo.__adapter__() != Ecto.Adapters.Postgres ->
|
||||
{:error, "Expected a repo using the postgres adapter `Ecto.Adapters.Postgres`"}
|
||||
|
||||
true ->
|
||||
{:ok, dsl}
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,6 +3,8 @@ defmodule AshPostgres.Transformers.VerifyRepo do
|
|||
use Spark.Dsl.Transformer
|
||||
alias Spark.Dsl.Transformer
|
||||
|
||||
def after_compile?, do: true
|
||||
|
||||
def transform(dsl) do
|
||||
repo = Transformer.get_option(dsl, [:postgres], :repo)
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
defmodule AshPostgres.AggregateTest do
|
||||
use AshPostgres.RepoCase, async: false
|
||||
alias AshPostgres.Test.{Api, Comment, Post, Rating, Author, Profile}
|
||||
alias AshPostgres.Test.{Api, Comment, Post, Rating}
|
||||
|
||||
require Ash.Query
|
||||
|
||||
|
@ -236,43 +236,36 @@ defmodule AshPostgres.AggregateTest do
|
|||
|> Ash.Query.sort(:first_comment)
|
||||
|> Api.read_one!()
|
||||
end
|
||||
end
|
||||
|
||||
test "on an array composite type" do
|
||||
author =
|
||||
Author
|
||||
|> Ash.Changeset.for_create(:create, %{badges: [:author_of_the_year]})
|
||||
|> Api.create!()
|
||||
test "can't define multidimensional array aggregate types" do
|
||||
assert_raise Spark.Error.DslError, ~r/Aggregate not supported/, fn ->
|
||||
defmodule Foo do
|
||||
@moduledoc false
|
||||
use Ash.Resource,
|
||||
data_layer: AshPostgres.DataLayer
|
||||
|
||||
profile =
|
||||
Profile
|
||||
|> Ash.Changeset.for_create(:create)
|
||||
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
postgres do
|
||||
table("profile")
|
||||
repo(AshPostgres.TestRepo)
|
||||
end
|
||||
|
||||
assert %{author_badges: [:author_of_the_year]} =
|
||||
Profile
|
||||
|> Ash.Query.filter(id == ^profile.id)
|
||||
|> Ash.Query.load([:author_badges])
|
||||
|> Api.read_one!()
|
||||
end
|
||||
attributes do
|
||||
uuid_primary_key(:id, writable?: true)
|
||||
end
|
||||
|
||||
test "on an empty array composite type" do
|
||||
author =
|
||||
Author
|
||||
|> Ash.Changeset.for_create(:create, %{badges: []})
|
||||
|> Api.create!()
|
||||
actions do
|
||||
defaults([:create, :read, :update, :destroy])
|
||||
end
|
||||
|
||||
profile =
|
||||
Profile
|
||||
|> Ash.Changeset.for_create(:create)
|
||||
|> Ash.Changeset.manage_relationship(:author, author, type: :append_and_remove)
|
||||
|> Api.create!()
|
||||
relationships do
|
||||
belongs_to(:author, AshPostgres.Test.Author)
|
||||
end
|
||||
|
||||
assert %{author_badges: []} =
|
||||
Profile
|
||||
|> Ash.Query.filter(id == ^profile.id)
|
||||
|> Ash.Query.load([:author_badges])
|
||||
|> Api.read_one!()
|
||||
aggregates do
|
||||
first(:author_badges, :author, :badges)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -21,8 +21,4 @@ defmodule AshPostgres.Test.Profile do
|
|||
relationships do
|
||||
belongs_to(:author, AshPostgres.Test.Author)
|
||||
end
|
||||
|
||||
aggregates do
|
||||
first(:author_badges, :author, :badges)
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue