From 402e0dba663d1dbcd7b8603d0c52e7dc005ae3a0 Mon Sep 17 00:00:00 2001 From: Giustino Esposito Date: Mon, 23 Feb 2026 12:47:19 +0100 Subject: [PATCH 1/2] fix: replace per-row __id__ clauses with Map lookup The ARM64 JIT backend in OTP 25 (erts-13.2) silently generates incorrect machine code when a function has more than 4096 clauses of integer pattern matching, causing all calls to fall through to the catch-all clause and return nil. Previously, `gen_internal_id/3` generated one `def __id__(N)` clause per CSV row plus a catch-all. For large CSVs (e.g. comuni.csv with 7896 rows) this exceeded the 4096-clause threshold, breaking lookups on ARM64 (Apple Silicon via Docker). Replace the N individual clauses with a single `@__csv_map__` module attribute storing a `%{id => changeset}` map, and a single `def __id__/1` that performs a Map.fetch/2 lookup. --- lib/csv_schema.ex | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/lib/csv_schema.ex b/lib/csv_schema.ex index b6c8b93..e26a000 100644 --- a/lib/csv_schema.ex +++ b/lib/csv_schema.ex @@ -143,13 +143,16 @@ defmodule Csv.Schema do # ## Destination module function generators # - @spec __id__(non_neg_integer) :: t | nil generators = %{ - internal_id: fn id, changeset -> - def __id__(unquote(id)), do: struct!(__MODULE__, unquote(Macro.escape(changeset))) - end, - default_internal_id: fn -> - def __id__(_), do: nil + internal_id: fn csv_map -> + @__csv_map__ csv_map + @spec __id__(non_neg_integer) :: t | nil + def __id__(id) do + case Map.fetch(@__csv_map__, id) do + {:ok, changeset} -> struct!(__MODULE__, changeset) + :error -> nil + end + end end, by: fn translation_map, name -> @spec unquote(:"by_#{name}")(any) :: t | nil @@ -179,6 +182,7 @@ defmodule Csv.Schema do # ## Cleanup # + Module.delete_attribute(__MODULE__, :__csv_map__) Module.delete_attribute(__MODULE__, :separator) Module.delete_attribute(__MODULE__, :struct_fields) Module.delete_attribute(__MODULE__, :fields) @@ -231,16 +235,21 @@ defmodule Csv.Schema do def __gen__(csv_stream, fields, generators) do changesets = get_changesets(csv_stream, fields) - gen_internal_id(changesets, Map.fetch!(generators, :internal_id), Map.fetch!(generators, :default_internal_id)) + gen_internal_id(changesets, Map.fetch!(generators, :internal_id)) gen_by(changesets, fields, Map.fetch!(generators, :by)) gen_filter_by(changesets, fields, Map.fetch!(generators, :filter_by)) gen_get_all(changesets, Map.fetch!(generators, :get_all)) end - @spec gen_internal_id(list(map), function, function) :: :ok - defp gen_internal_id(changesets, internal_id, default_internal_id) do - Enum.each(changesets, &internal_id.(get_id(&1), Map.delete(&1, :__id__))) - default_internal_id.() + @spec gen_internal_id(list(map), function) :: :ok + defp gen_internal_id(changesets, internal_id) do + csv_map = + changesets + |> Enum.into(%{}, fn changeset -> + {get_id(changeset), Map.delete(changeset, :__id__)} + end) + + internal_id.(csv_map) end @spec gen_by(list(map), list(Field.t()), function) :: :ok From 955af7a3507433ee06b3c19364695bcb8a84c34e Mon Sep 17 00:00:00 2001 From: giustinoesposito-prima Date: Mon, 23 Feb 2026 14:30:44 +0100 Subject: [PATCH 2/2] Update lib/csv_schema.ex Co-authored-by: Claudio D'Alicandro --- lib/csv_schema.ex | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/csv_schema.ex b/lib/csv_schema.ex index e26a000..ebde62f 100644 --- a/lib/csv_schema.ex +++ b/lib/csv_schema.ex @@ -244,8 +244,7 @@ defmodule Csv.Schema do @spec gen_internal_id(list(map), function) :: :ok defp gen_internal_id(changesets, internal_id) do csv_map = - changesets - |> Enum.into(%{}, fn changeset -> + Enum.into(changesets, %{}, fn changeset -> {get_id(changeset), Map.delete(changeset, :__id__)} end)