I think I found a bug in Ash when trying to forcefully updating an attribute that is currently nil
in the DB.
In my resource, I have this field:
attribute :adjust_rate, :decimal do
allow_nil? true
writable? false
end
This field is updated from an :update
action that has a change that will calculate its value and update it with force_change_attribute
:
changeset
|> Ash.Changeset.force_change_attribute(:adjust_rate, adjust_rate)
If the field, in the DB has a nil
value and I’m running the above change to update it, I will get the following error:
** (Ash.Error.Unknown) Unknown Error
* %FunctionClauseError{module: Decimal, function: :decimal, arity: 1, kind: nil, args: nil, clauses: nil}
(decimal 2.1.1) Decimal.decimal(nil)
(decimal 2.1.1) lib/decimal.ex:386: Decimal.compare/2
(decimal 2.1.1) lib/decimal.ex:447: Decimal.eq?/2
(ash 3.4.34) lib/ash/changeset/changeset.ex:5302: Ash.Changeset.force_change_attribute/3
(core 1.115.0) lib/core/marketplace/invoices/pro_forma/actions/common/changes/calculate_adjust_rate.ex:39: Core.Marketplace.Invoices.ProForma.Actions.Common.Changes.CalculateAdjustRate.change/1
(ash 3.4.34) lib/ash/changeset/changeset.ex:3718: anonymous fn/2 in Ash.Changeset.run_before_actions/1
(elixir 1.17.2) lib/enum.ex:4858: Enumerable.List.reduce/3
(elixir 1.17.2) lib/enum.ex:2585: Enum.reduce_while/3
(ash 3.4.34) lib/ash/changeset/changeset.ex:3693: Ash.Changeset.run_before_actions/1
(ash 3.4.34) lib/ash/changeset/changeset.ex:3833: Ash.Changeset.run_around_actions/2
(ash 3.4.34) lib/ash/changeset/changeset.ex:3385: anonymous fn/3 in Ash.Changeset.with_hooks/3
(ecto_sql 3.12.0) lib/ecto/adapters/sql.ex:1382: anonymous fn/3 in Ecto.Adapters.SQL.checkout_or_transaction/4
(db_connection 2.7.0) lib/db_connection.ex:1756: DBConnection.run_transaction/4
(ash 3.4.34) lib/ash/changeset/changeset.ex:3383: anonymous fn/3 in Ash.Changeset.with_hooks/3
(ash 3.4.34) lib/ash/changeset/changeset.ex:3527: anonymous fn/2 in Ash.Changeset.transaction_hooks/2
(ash 3.4.34) lib/ash/changeset/changeset.ex:3364: Ash.Changeset.with_hooks/3
(ash 3.4.34) lib/ash/actions/update/update.ex:403: Ash.Actions.Update.commit/3
(ash 3.4.34) lib/ash/actions/update/update.ex:283: Ash.Actions.Update.do_run/4
(ash 3.4.34) lib/ash/actions/update/update.ex:240: Ash.Actions.Update.run/4
(ash 3.4.34) lib/ash.ex:2665: Ash.update/3
(ash 3.4.34) lib/ash.ex:2605: Ash.update!/3
(elixir 1.17.2) src/elixir.erl:386: :elixir.eval_external_handler/3
(stdlib 6.1.1) erl_eval.erl:904: :erl_eval.do_apply/7
(stdlib 6.1.1) erl_eval.erl:648: :erl_eval.expr/6
(elixir 1.17.2) src/elixir.erl:364: :elixir.eval_forms/4
(elixir 1.17.2) lib/module/parallel_checker.ex:112: Module.ParallelChecker.verify/1
(iex 1.17.2) lib/iex/evaluator.ex:332: IEx.Evaluator.eval_and_inspect/3
(iex 1.17.2) lib/iex/evaluator.ex:306: IEx.Evaluator.eval_and_inspect_parsed/3
(iex 1.17.2) lib/iex/evaluator.ex:295: IEx.Evaluator.parse_eval_inspect/4
(iex 1.17.2) lib/iex/evaluator.ex:187: IEx.Evaluator.loop/1
(iex 1.17.2) lib/iex/evaluator.ex:32: IEx.Evaluator.init/5
(stdlib 6.1.1) proc_lib.erl:329: :proc_lib.init_p_do_apply/3
(decimal 2.1.1) Decimal.decimal(nil)
(decimal 2.1.1) lib/decimal.ex:386: Decimal.compare/2
(decimal 2.1.1) lib/decimal.ex:447: Decimal.eq?/2
(ash 3.4.34) lib/ash/changeset/changeset.ex:5302: Ash.Changeset.force_change_attribute/3
(core 1.115.0) lib/core/marketplace/invoices/pro_forma/actions/common/changes/calculate_adjust_rate.ex:39: Core.Marketplace.Invoices.ProForma.Actions.Common.Changes.CalculateAdjustRate.change/1
(ash 3.4.34) lib/ash/changeset/changeset.ex:3718: anonymous fn/2 in Ash.Changeset.run_before_actions/1
(elixir 1.17.2) lib/enum.ex:4858: Enumerable.List.reduce/3
(elixir 1.17.2) lib/enum.ex:2585: Enum.reduce_while/3
(ash 3.4.34) lib/ash/changeset/changeset.ex:3693: Ash.Changeset.run_before_actions/1
(ash 3.4.34) lib/ash/changeset/changeset.ex:3833: Ash.Changeset.run_around_actions/2
(ash 3.4.34) lib/ash/changeset/changeset.ex:3385: anonymous fn/3 in Ash.Changeset.with_hooks/3
(ecto_sql 3.12.0) lib/ecto/adapters/sql.ex:1382: anonymous fn/3 in Ecto.Adapters.SQL.checkout_or_transaction/4
(db_connection 2.7.0) lib/db_connection.ex:1756: DBConnection.run_transaction/4
(ash 3.4.34) lib/ash/changeset/changeset.ex:3383: anonymous fn/3 in Ash.Changeset.with_hooks/3
(ash 3.4.34) lib/ash/changeset/changeset.ex:3527: anonymous fn/2 in Ash.Changeset.transaction_hooks/2
(ash 3.4.34) lib/ash/changeset/changeset.ex:3364: Ash.Changeset.with_hooks/3
(ash 3.4.34) lib/ash/actions/update/update.ex:403: Ash.Actions.Update.commit/3
(ash 3.4.34) lib/ash/actions/update/update.ex:283: Ash.Actions.Update.do_run/4
(ash 3.4.34) lib/ash/actions/update/update.ex:240: Ash.Actions.Update.run/4
(ash 3.4.34) lib/ash.ex:2665: Ash.update/3
(ash 3.4.34) lib/ash.ex:2605: Ash.update!/3
iex:13: (file)
It seems to fail because in Ash changeset.ex:5302, it will try to compare the decimal values (the old value and new value) but the old value is nil
so Decimal.compare/2
will fail.