Search code examples
elixirecto

Elixir: comparison with nil is forbidden as it is unsafe


I am very new to Elixir. I am facing this error:

2022-07-06 13:52:53.022 [info] pid=<0.31.0> Application mt_consumer exited: MTConsumer.Application.start(:normal, []) returned an error: shutdown: failed to start child: MTConsumer
    ** (EXIT) an exception was raised:
        ** (ArgumentError) comparison with nil is forbidden as it is unsafe. If you want to check if a value is nil, use is_nil/1 instead
            (ecto) lib/ecto/query/builder.ex:551: Ecto.Query.Builder.not_nil!/1
            (mt_model) lib/mt_model/location.ex:36: MTModel.Location.raw_id_by_name_tenant/2
            (mt_consumer) lib/mt_consumer.ex:66: MTConsumer.handle_job/1
            (mt_consumer) lib/mt_consumer.ex:38: MTConsumer.loop/1
            (stdlib) supervisor.erl:365: :supervisor.do_start_child/2
            (stdlib) supervisor.erl:348: :supervisor.start_children/3
            (stdlib) supervisor.erl:314: :supervisor.init_children/2
            (stdlib) gen_server.erl:328: :gen_server.init_it/6

Now here is my code:

def id_by_name_tenant(name, tenant_id) do
    id = raw_id_by_name_tenant(name, tenant_id)

    case id do
      nil -> raw_id_by_name_tenant(@default_name, nil)
      _ -> id
    end
  end

  defp raw_id_by_name_tenant(name, tenant_id) do
    from(
      l in MTModel.Location,
      where: l.name == ^name and l.tenant_id == ^tenant_id,
      limit: 1,
      select: l.id
    ) |> MTModel.Repo.one([])
  end

  defp raw_id_by_name_tenant(name, _) do
    from(
      l in MTModel.Location,
      where: l.name == ^name,
      limit: 1,
      select: l.id
    ) |> MTModel.Repo.one()
  end

what am I doing wrong here? The code worked fine earlier but upon re deploying it started creating problems.


Solution

  • The second clause of raw_id_by_name_tenant/2 will never match, since the first one will always do. You probably forgot to guard against nil in the first clause, for example

    defp raw_id_by_name_tenant(name, tenant_id) when tenant_id != nil do
    

    or move the second clause up and replace it with

    defp raw_id_by_name_tenant(name, nil) do
    

    But I think you might be able to simplify this code by using Repo.get_by/2, removing the need for a private function and making the intent clearer (if your name/tenant_id pairs are unique):

    def id_by_name_tenant(name, tenant_id) do
      query = select(MTModel.Location, [l], l.id)
      case Repo.get_by(query, name: name, tenant_id: tenant_id) do
        nil -> Repo.get_by(query, name: @default_name)
        id -> id
      end
    end