From b00a7e64826657903846ceed120adb3a260a388f Mon Sep 17 00:00:00 2001 From: Zach Daniel Date: Mon, 22 Jul 2024 11:49:59 -0400 Subject: [PATCH] fix: update ash & ash_sql for fixes, test atomic alidations in destroys --- lib/data_layer.ex | 74 +++++++++++++++++++++++----------- mix.exs | 4 +- mix.lock | 13 +++--- test/atomics_test.exs | 5 ++- test/support/resources/post.ex | 11 +++-- 5 files changed, 70 insertions(+), 37 deletions(-) diff --git a/lib/data_layer.ex b/lib/data_layer.ex index b0676b6..aeaff30 100644 --- a/lib/data_layer.ex +++ b/lib/data_layer.ex @@ -2776,42 +2776,68 @@ defmodule AshPostgres.DataLayer do @impl true def destroy(resource, %{data: record} = changeset) do - ecto_changeset = ecto_changeset(record, changeset, :delete) - - try do - repo = AshSql.dynamic_repo(resource, AshPostgres.SqlImplementation, changeset) - - source = resolve_source(resource, changeset) + source = resolve_source(resource, changeset) + query = from(row in source, as: ^0) |> AshSql.Bindings.default_bindings( resource, AshPostgres.SqlImplementation, changeset.context ) - |> filter(changeset.filter, resource) - |> case do - {:ok, query} -> - query - |> pkey_filter(record) - |> repo.delete_all( - AshSql.repo_opts( - repo, - AshPostgres.SqlImplementation, - changeset.timeout, - changeset.tenant, - changeset.resource - ) - ) + |> pkey_filter(record) - :ok + with {:ok, query} <- filter(query, changeset.filter, resource) do + ecto_changeset = + case changeset.data do + %Ash.Changeset.OriginalDataNotAvailable{} -> + changeset.resource.__struct__() + data -> + data + end + |> Map.update!(:__meta__, &Map.put(&1, :source, table(resource, changeset))) + |> ecto_changeset(changeset, :delete, true) + + case bulk_updatable_query( + query, + resource, + [], + [], + changeset.context, + :destroy + ) do {:error, error} -> {:error, error} + + {:ok, query} -> + try do + repo = AshSql.dynamic_repo(resource, AshPostgres.SqlImplementation, changeset) + + repo_opts = + AshSql.repo_opts( + repo, + AshPostgres.SqlImplementation, + changeset.timeout, + changeset.tenant, + changeset.resource + ) + + query = Ecto.Query.exclude(query, :select) + + with_savepoint(repo, query, fn -> + repo.delete_all( + query, + repo_opts + ) + end) + + :ok + rescue + e -> + handle_raised_error(e, __STACKTRACE__, ecto_changeset, resource) + end end - rescue - e -> - handle_raised_error(e, __STACKTRACE__, ecto_changeset, resource) end end diff --git a/mix.exs b/mix.exs index 38934d6..bfe171b 100644 --- a/mix.exs +++ b/mix.exs @@ -162,8 +162,8 @@ defmodule AshPostgres.MixProject do # Run "mix help deps" to learn about dependencies. defp deps do [ - {:ash, ash_version("~> 3.1 and >= 3.2.1")}, - {:ash_sql, ash_sql_version("~> 0.2 and >= 0.2.24")}, + {:ash, ash_version("~> 3.1 and >= 3.2.5")}, + {:ash_sql, ash_sql_version("~> 0.2 and >= 0.2.25")}, {:igniter, "~> 0.3 and >= 0.3.6"}, {:ecto_sql, "~> 3.11 and >= 3.11.3"}, {:ecto, "~> 3.11 and >= 3.11.2"}, diff --git a/mix.lock b/mix.lock index 1d361ab..3e553de 100644 --- a/mix.lock +++ b/mix.lock @@ -1,6 +1,6 @@ %{ - "ash": {:hex, :ash, "3.2.2", "f079fbe7f4f7e3279825c841aa69f6b83333a86267b31435ca97031a805c4b41", [:mix], [{:comparable, "~> 1.0", [hex: :comparable, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8", [hex: :ets, repo: "hexpm", optional: false]}, {:igniter, ">= 0.2.12 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: false]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: true]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:reactor, ">= 0.8.1 and < 1.0.0-0", [hex: :reactor, repo: "hexpm", optional: false]}, {:simple_sat, ">= 0.1.1 and < 1.0.0-0", [hex: :simple_sat, repo: "hexpm", optional: true]}, {:spark, ">= 2.2.8 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:stream_data, "~> 1.0", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b9a9165d1aafec1fa719df6182ddee0b2a29c83f67079a04a9a8060f49cc1e7f"}, - "ash_sql": {:hex, :ash_sql, "0.2.24", "324f3fd65e249ad2e7df56a585a768edf6cc91246b7c8048ac16a684edc1437a", [:mix], [{:ash, ">= 3.1.7 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.9", [hex: :ecto_sql, repo: "hexpm", optional: false]}], "hexpm", "38f3066059be9f567ab5c3a8d99e812417069b881bc12fa33c067b1ea7742145"}, + "ash": {:hex, :ash, "3.2.5", "15702f44075cd34e0bd2756d1e286298c2c6b7b30990794940199885f43d8a44", [:mix], [{:comparable, "~> 1.0", [hex: :comparable, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8", [hex: :ets, repo: "hexpm", optional: false]}, {:igniter, ">= 0.2.12 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: false]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: true]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:reactor, "~> 0.9", [hex: :reactor, repo: "hexpm", optional: false]}, {:simple_sat, ">= 0.1.1 and < 1.0.0-0", [hex: :simple_sat, repo: "hexpm", optional: true]}, {:spark, ">= 2.2.8 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:stream_data, "~> 1.0", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f7790ca7a77ec53d06f9b25820c5219e8ea0e7d05e3c477d783cf9d11428dc9a"}, + "ash_sql": {:hex, :ash_sql, "0.2.25", "a9489f5bd54a3a91e161f59b0c95385d62ca239fad6d4851011f532832f0f5ca", [:mix], [{:ash, ">= 3.1.7 and < 4.0.0-0", [hex: :ash, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.9", [hex: :ecto_sql, repo: "hexpm", optional: false]}], "hexpm", "caeb8c5ea277aca8e9add5583bf9603bf7517c896a6e9d5eb4327a51f1209051"}, "benchee": {:hex, :benchee, "1.3.1", "c786e6a76321121a44229dde3988fc772bca73ea75170a73fd5f4ddf1af95ccf", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "76224c58ea1d0391c8309a8ecbfe27d71062878f59bd41a390266bf4ac1cc56d"}, "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, "comparable": {:hex, :comparable, "1.0.0", "bb669e91cedd14ae9937053e5bcbc3c52bb2f22422611f43b6e38367d94a495f", [:mix], [{:typable, "~> 0.1", [hex: :typable, repo: "hexpm", optional: false]}], "hexpm", "277c11eeb1cd726e7cd41c6c199e7e52fa16ee6830b45ad4cdc62e51f62eb60c"}, @@ -21,8 +21,9 @@ "git_cli": {:hex, :git_cli, "0.3.0", "a5422f9b95c99483385b976f5d43f7e8233283a47cda13533d7c16131cb14df5", [:mix], [], "hexpm", "78cb952f4c86a41f4d3511f1d3ecb28edb268e3a7df278de2faa1bd4672eaf9b"}, "git_ops": {:hex, :git_ops, "2.6.1", "cc7799a68c26cf814d6d1a5121415b4f5bf813de200908f930b27a2f1fe9dad5", [:mix], [{:git_cli, "~> 0.2", [hex: :git_cli, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "ce62d07e41fe993ec22c35d5edb11cf333a21ddaead6f5d9868fcb607d42039e"}, "glob_ex": {:hex, :glob_ex, "0.1.7", "eae6b6377147fb712ac45b360e6dbba00346689a87f996672fe07e97d70597b1", [:mix], [], "hexpm", "decc1c21c0c73df3c9c994412716345c1692477b9470e337f628a7e08da0da6a"}, - "igniter": {:hex, :igniter, "0.3.6", "30951d75604e88d6d893753e7331c9793625fbeb5fb34f06148e61ffcdca590f", [:mix], [{:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:inflex, "~> 2.0", [hex: :inflex, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:owl, "~> 0.9", [hex: :owl, repo: "hexpm", optional: false]}, {:rewrite, "~> 0.9", [hex: :rewrite, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.4", [hex: :sourceror, repo: "hexpm", optional: false]}, {:spitfire, ">= 0.1.3 and < 1.0.0-0", [hex: :spitfire, repo: "hexpm", optional: false]}, {:ucwidth, "~> 0.2", [hex: :ucwidth, repo: "hexpm", optional: false]}], "hexpm", "9fc32f2fc6eff9c24abe1d942a6632f5d50ccaeb031504064c1176f78f892b65"}, + "igniter": {:hex, :igniter, "0.3.8", "e6f423170e90a4547f3aca6b4e1879d742787bf7a577db9beee835c6b3890cc2", [:mix], [{:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:inflex, "~> 2.0", [hex: :inflex, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:owl, "~> 0.9", [hex: :owl, repo: "hexpm", optional: false]}, {:rewrite, "~> 0.9", [hex: :rewrite, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.4", [hex: :sourceror, repo: "hexpm", optional: false]}, {:spitfire, ">= 0.1.3 and < 1.0.0-0", [hex: :spitfire, repo: "hexpm", optional: false]}, {:ucwidth, "~> 0.2", [hex: :ucwidth, repo: "hexpm", optional: false]}], "hexpm", "0850066244fead51ac7f749482889878db550c23c3c8addaf5b0d87956a44c91"}, "inflex": {:hex, :inflex, "2.1.0", "a365cf0821a9dacb65067abd95008ca1b0bb7dcdd85ae59965deef2aa062924c", [:mix], [], "hexpm", "14c17d05db4ee9b6d319b0bff1bdf22aa389a25398d1952c7a0b5f3d93162dd8"}, + "iterex": {:hex, :iterex, "0.1.2", "58f9b9b9a22a55cbfc7b5234a9c9c63eaac26d276b3db80936c0e1c60355a5a6", [:mix], [], "hexpm", "2e103b8bcc81757a9af121f6dc0df312c9a17220f302b1193ef720460d03029d"}, "jason": {:hex, :jason, "1.4.3", "d3f984eeb96fe53b85d20e0b049f03e57d075b5acda3ac8d465c969a2536c17b", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "9a90e868927f7c777689baa16d86f4d0e086d968db5c05d917ccff6d443e58a3"}, "libgraph": {:hex, :libgraph, "0.16.0", "3936f3eca6ef826e08880230f806bfea13193e49bf153f93edcf0239d4fd1d07", [:mix], [], "hexpm", "41ca92240e8a4138c30a7e06466acc709b0cbb795c643e9e17174a178982d6bf"}, "makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"}, @@ -31,14 +32,14 @@ "mix_audit": {:hex, :mix_audit, "2.1.4", "0a23d5b07350cdd69001c13882a4f5fb9f90fbd4cbf2ebc190a2ee0d187ea3e9", [:make, :mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:yaml_elixir, "~> 2.11", [hex: :yaml_elixir, repo: "hexpm", optional: false]}], "hexpm", "fd807653cc8c1cada2911129c7eb9e985e3cc76ebf26f4dd628bb25bbcaa7099"}, "nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, - "owl": {:hex, :owl, "0.9.0", "9b33d64734bd51d3fc1d6ed01b12f8c2ed23e1fbf8c43658a6dfbff62578bd03", [:mix], [{:ucwidth, "~> 0.2", [hex: :ucwidth, repo: "hexpm", optional: true]}], "hexpm", "cd70b55327985f8f24d38cb7de5bf8a8d24040e1b49cca2345508f8119ce81fd"}, + "owl": {:hex, :owl, "0.10.0", "1104390ee3e1b29e2c67c1539ffd7554d9f40e8ef67e4817098e9325684bed30", [:mix], [{:ucwidth, "~> 0.2", [hex: :ucwidth, repo: "hexpm", optional: true]}], "hexpm", "c16e35e00e8dd2bc23527678024e7f491064596d78ad40669d44992cac6afdff"}, "postgrex": {:hex, :postgrex, "0.18.0", "f34664101eaca11ff24481ed4c378492fed2ff416cd9b06c399e90f321867d7e", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "a042989ba1bc1cca7383ebb9e461398e3f89f868c92ce6671feb7ef132a252d1"}, - "reactor": {:hex, :reactor, "0.8.5", "7a621e0392a5975ed97938a4ddbbc92a6a31157fbd87446bc8bc6b1a0f49e56a", [:mix], [{:igniter, "~> 0.2", [hex: :igniter, repo: "hexpm", optional: false]}, {:libgraph, "~> 0.16", [hex: :libgraph, repo: "hexpm", optional: false]}, {:spark, "~> 2.0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "17b1976b9d333e55382dc108779078d5bbdbcd2c3d4033ea6dd52437339fe469"}, + "reactor": {:hex, :reactor, "0.9.0", "f48af9f300454b979a22d5a04b18b59e16959478ffa7f88d50b5e142b5d055dc", [:mix], [{:igniter, "~> 0.2", [hex: :igniter, repo: "hexpm", optional: false]}, {:iterex, "~> 0.1", [hex: :iterex, repo: "hexpm", optional: false]}, {:libgraph, "~> 0.16", [hex: :libgraph, repo: "hexpm", optional: false]}, {:spark, "~> 2.0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4c5ffd700ac669d0992a9e296978abe2110670b23addc0970fca9108d506489c"}, "rewrite": {:hex, :rewrite, "0.10.5", "6afadeae0b9d843b27ac6225e88e165884875e0aed333ef4ad3bf36f9c101bed", [:mix], [{:glob_ex, "~> 0.1", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "51cc347a4269ad3a1e7a2c4122dbac9198302b082f5615964358b4635ebf3d4f"}, "simple_sat": {:hex, :simple_sat, "0.1.3", "f650fc3c184a5fe741868b5ac56dc77fdbb428468f6dbf1978e14d0334497578", [:mix], [], "hexpm", "a54305066a356b7194dc81db2a89232bacdc0b3edaef68ed9aba28dcbc34887b"}, "sobelow": {:hex, :sobelow, "0.13.0", "218afe9075904793f5c64b8837cc356e493d88fddde126a463839351870b8d1e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cd6e9026b85fc35d7529da14f95e85a078d9dd1907a9097b3ba6ac7ebbe34a0d"}, "sourceror": {:hex, :sourceror, "1.4.0", "be87319b1579191e25464005d465713079b3fd7124a3938a1e6cf4def39735a9", [:mix], [], "hexpm", "16751ca55e3895f2228938b703ad399b0b27acfe288eff6c0e629ed3e6ec0358"}, - "spark": {:hex, :spark, "2.2.9", "cc86e39895e1e1b2360e333fe37fa8cdb5624d265d234c0c945b1b1e11b49563", [:mix], [{:igniter, ">= 0.2.6 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.2", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "d855e3971f568527bdd43377a8088ae96a7798a1df732c9b7a631730ba2f705d"}, + "spark": {:hex, :spark, "2.2.10", "834c5f6c6874d019116096b82fe8a3e9bfe92077c3e06ead14b6daff528b69ef", [:mix], [{:igniter, ">= 0.2.6 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.2", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "78972edb0cc1539e56d42f08aabc88b8b2892d64e3e8bd44d58113b7f63622fa"}, "spitfire": {:hex, :spitfire, "0.1.3", "7ea0f544005dfbe48e615ed90250c9a271bfe126914012023fd5e4b6b82b7ec7", [:mix], [], "hexpm", "d53b5107bcff526a05c5bb54c95e77b36834550affd5830c9f58760e8c543657"}, "splode": {:hex, :splode, "0.2.4", "71046334c39605095ca4bed5d008372e56454060997da14f9868534c17b84b53", [:mix], [], "hexpm", "ca3b95f0d8d4b482b5357954fec857abd0fa3ea509d623334c1328e7382044c2"}, "statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"}, diff --git a/test/atomics_test.exs b/test/atomics_test.exs index 418206c..d1c25ec 100644 --- a/test/atomics_test.exs +++ b/test/atomics_test.exs @@ -1,6 +1,7 @@ defmodule AshPostgres.AtomicsTest do - alias AshPostgres.Test.Comment alias AshPostgres.Test.Author + alias AshPostgres.Test.Comment + use AshPostgres.RepoCase, async: false alias AshPostgres.Test.Post @@ -306,7 +307,7 @@ defmodule AshPostgres.AtomicsTest do assert_raise Ash.Error.Invalid, ~r/Can only delete if Post has no comments/, fn -> post |> Ash.Changeset.for_destroy(:destroy_if_no_comments, %{}) - |> Ash.destroy() + |> Ash.destroy!() end end end diff --git a/test/support/resources/post.ex b/test/support/resources/post.ex index a058bce..ca81473 100644 --- a/test/support/resources/post.ex +++ b/test/support/resources/post.ex @@ -14,7 +14,7 @@ defmodule PassIfOriginalDataPresent do end defmodule HasNoComments do - alias Ash.Error.Invalid + @moduledoc false use Ash.Resource.Validation def atomic(_changeset, _opts, _context) do @@ -22,10 +22,15 @@ defmodule HasNoComments do [ {:atomic, [], expr( - list(comments, field: :id) > 0 or + length(list(comments, field: :id)) > 0 or count(comments) > 0 or exists(comments, true) - ), expr(error(^Invalid, %{message: "Can only delete if Post has no comments"}))} + ), + expr( + error(^Ash.Error.Changes.InvalidChanges, %{ + message: "Can only delete if Post has no comments" + }) + )} ] end end