diff --git a/lib/data_layer.ex b/lib/data_layer.ex index 1941398a..a16267ea 100644 --- a/lib/data_layer.ex +++ b/lib/data_layer.ex @@ -605,6 +605,11 @@ defmodule AshPostgres.DataLayer do import Ecto.Query, only: [from: 2, subquery: 1] + @impl true + def prefer_transaction?(resource) do + AshPostgres.DataLayer.Info.repo(resource, :mutate).prefer_transaction?() + end + @impl true def can?(_, :async_engine), do: true def can?(_, :bulk_create), do: true @@ -1773,7 +1778,12 @@ defmodule AshPostgres.DataLayer do |> AshSql.Bindings.default_bindings(resource, AshPostgres.SqlImplementation) upsert_set = - upsert_set(resource, changesets, options) + upsert_set( + resource, + changesets, + options[:upsert_keys] || Ash.Resource.Info.primary_key(resource), + options + ) on_conflict = case AshSql.Atomics.query_with_atomics( @@ -1785,7 +1795,11 @@ defmodule AshPostgres.DataLayer do upsert_set ) do :empty -> - {:replace, options[:upsert_keys] || Ash.Resource.Info.primary_key(resource)} + if options[:return_records?] do + {:replace, options[:upsert_keys] || Ash.Resource.Info.primary_key(resource)} + else + :nothing + end {:ok, query} -> query @@ -1913,7 +1927,7 @@ defmodule AshPostgres.DataLayer do fun.() end - defp upsert_set(resource, changesets, options) do + defp upsert_set(resource, changesets, keys, options) do attributes_changing_anywhere = changesets |> Enum.flat_map(&Map.keys(&1.attributes)) |> Enum.uniq() @@ -1926,7 +1940,7 @@ defmodule AshPostgres.DataLayer do fields_to_upsert = upsert_fields -- - Keyword.keys(Enum.at(changesets, 0).atomics) + Keyword.keys(Enum.at(changesets, 0).atomics) -- keys fields_to_upsert |> Enum.uniq() diff --git a/lib/repo.ex b/lib/repo.ex index b9b2290f..38c33601 100644 --- a/lib/repo.ex +++ b/lib/repo.ex @@ -69,6 +69,9 @@ defmodule AshPostgres.Repo do @doc "The default prefix(postgres schema) to use when building queries" @callback default_prefix() :: String.t() + @doc "Whether or not to explicitly start and close a transaction for each action, even if there are no transaction hooks" + @callback prefer_transaction?() :: boolean + @doc "Allows overriding a given migration type for *all* fields, for example if you wanted to always use :timestamptz for :utc_datetime fields" @callback override_migration_type(atom) :: atom @doc "Should the repo should be created by `mix ash_postgres.create`?" @@ -102,6 +105,9 @@ defmodule AshPostgres.Repo do def create?, do: true def drop?, do: true + # default to false in 4.0 + def prefer_transaction?, do: true + def transaction!(fun) do case fun.() do {:ok, value} -> value @@ -242,6 +248,7 @@ defmodule AshPostgres.Repo do on_transaction_begin: 1, installed_extensions: 0, all_tenants: 0, + prefer_transaction?: 0, tenant_migrations_path: 0, default_prefix: 0, override_migration_type: 1, diff --git a/test/cve/empty_atomic_non_bulk_actions_policy_bypass_test.exs b/test/cve/empty_atomic_non_bulk_actions_policy_bypass_test.exs index 5863f499..8b204f25 100644 --- a/test/cve/empty_atomic_non_bulk_actions_policy_bypass_test.exs +++ b/test/cve/empty_atomic_non_bulk_actions_policy_bypass_test.exs @@ -16,8 +16,6 @@ defmodule AshPostgres.EmptyAtomicNonBulkActionsPolicyBypassTest do |> Ash.Changeset.for_create(:create, %{}) |> Ash.create!() - Logger.configure(level: :debug) - assert_raise Ash.Error.Forbidden, fn -> post |> Ash.Changeset.for_update(:empty_update, %{}, authorize?: true) diff --git a/test/support/test_repo.ex b/test/support/test_repo.ex index c272a2e5..999e928e 100644 --- a/test/support/test_repo.ex +++ b/test/support/test_repo.ex @@ -7,6 +7,8 @@ defmodule AshPostgres.TestRepo do send(self(), data) end + def prefer_transaction?, do: false + def installed_extensions do ["ash-functions", "uuid-ossp", "pg_trgm", "citext", AshPostgres.TestCustomExtension, "ltree"] -- Application.get_env(:ash_postgres, :no_extensions, []) diff --git a/test/test_helper.exs b/test/test_helper.exs index 5b02474b..23858cfd 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -1,4 +1,4 @@ -ExUnit.start(capture_log: true) +ExUnit.start(capture_log: false) exclude_tags = case System.get_env("PG_VERSION") do