Closed Wigny closed 1 year ago
This is expected behavior. When you define your type, Ecto bypassed all of its type logic so you have full control. In this case, the database believe decimal is a better return and it is being surfaced up all the way to your Ecto type, so you just handle it accordingly!
Got it! I'm just a little bit confused on why it's expected this behaviour on the Repo.aggregate/3
function, but it differs from the Repo.one/2
or Repo.all/2
behaviour... The question is why these last two functions loads a decimal as float when using the MyXQL adapter while the aggregate shouldn't? Shouldn't all these functions be consistent on loading from the database?
The MyXQL adapter has special behaviour to convert decimals to float before it goes to the generic Ecto conversions
MyXQL Adapter:
...
def loaders(:float, type), do: [&float_decode/1, type]
...
defp float_decode(%Decimal{} = decimal), do: {:ok, Decimal.to_float(decimal)}
defp float_decode(x), do: {:ok, x}
Ecto:
defp load_float(term) when is_float(term), do: {:ok, term}
defp load_float(term) when is_integer(term), do: {:ok, :erlang.float(term)}
defp load_float(_), do: :error
Maybe the MyXQL adapter should also pattern match on {:maybe. :float}
. But not 100% sure the history behind these functions.
:maybe
was added to handle exactly aggregations but I am not sure if goes all the way to adapters, does it? If {:maybe, :float}
is a fix, then I am fine with adding it, but perhaps I would rename :maybe
to :aggregate
.
Sorry, but the merged PR still doesn't solve the problem I'm facing, right @greg-rychlewski?
I'm still receiving the same error and when trying to understand the problem, it seems that the Ecto.Type.adapter_load/3
is not prepared to receive a {:maybe, {:parameterized, MyApp.Type.Duration, %{unit: :seconds}}}
type yet passed by the Ecto.Repo.Queryable.load!/5
function. Could you tell me if that's right?
iex(3)> Ecto.Type.adapter_load(Ecto.Adapters.MyXQL, {:parameterized, MyApp.Type.Duration, %{unit: :seconds}}, Decimal.new("282"))
{:ok, #<Duration(PT4M42S)>}
iex(4)> Ecto.Type.adapter_load(Ecto.Adapters.MyXQL, {:maybe, {:parameterized, MyApp.Type.Duration, %{unit: :seconds}}}, Decimal.new("282"))
** (FunctionClauseError) no function clause matching in MyApp.Type.Duration.load/3
The following arguments were given to MyApp.Type.Duration.load/3:
# 1
Decimal.new("282")
# 2
#Function<34.13828884/2 in Ecto.Type.process_loaders/3>
# 3
%{unit: :seconds}
iex:49: MyApp.Type.Duration.load/3
(ecto 3.11.0-dev) lib/ecto/type.ex:607: Ecto.Type.load/3
(ecto 3.11.0-dev) lib/ecto/type.ex:939: Ecto.Type.process_loaders/3
@Wigny Could you try the most up to date main?
It's working! Thank you!
Elixir version
1.15.4
Database and Version
MySQL 5.7
Ecto Versions
3.10.3
Database Adapter and Versions (postgrex, myxql, etc)
myxql 0.6.3
Current behavior
When I created a custom
Ecto.ParameterizedType
for handling ISO 8601 Durations with Timex, I defined it to be of type:float
, later when I tried to apply this custom type on an already existent schema field of type:decimal
, I noticed that loading and dumping into the DB worked fine in normal queries, but it fails when aggregating.For the code above, the
Repo.aggregate/3
function throws:Expected behavior
Since the
MyXQL
adapter already can load decimals as float, it's desirable that the aggregation result be correctly loaded into the:float
type as well: