繁体   English   中英

Elixir:禁止与 nil 比较,因为它不安全

[英]Elixir: comparison with nil is forbidden as it is unsafe

我对Elixir很陌生。 我正面临这个错误:

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

现在这是我的代码:

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

我在这里做错了什么? 该代码早些时候运行良好,但在重新部署后它开始产生问题。

您正在使用nil参数调用函数raw_id_by_name_tenant/2 ,然后使用此处的字段进行检查:

where: l.name == ^name and l.tenant_id == ^tenant_id,

这在 Ecto 中是被禁止的,如果你必须检查 nil 你必须使用is_nil/1

您可以使用dynamic/2重构您的函数,而不是将 nil 传递给您的查询:

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

  defp with_name(nil), do: true
  defp with_name(name), do: dynamic([l], l.name == ^name)

  defp with_tenant_id(nil), do: true
  defp with_tenant_id(tenant_id), do: dynamic([l], l.tenant_id == ^tenant_id)

raw_id_by_name_tenant/2的第二个子句永远不会匹配,因为第一个总是匹配。 例如,您可能忘记在第一个子句中防范nil

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

或将第二个子句向上移动并替换为

defp raw_id_by_name_tenant(name, nil) do

但我认为您可以通过使用Repo.get_by/2来简化此代码,消除对私有函数的需求并使意图更清晰(如果您的 name/tenant_id 对是唯一的):

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM