Elixir Ecto报comparison with nil is forbidden错误排查
问题原因
报错触发的核心逻辑有两点:
- 新版本Ecto新增了强制校验:禁止在查询条件中用
==和nil做比较,判断字段为nil必须用is_nil/1语法,否则直接抛出comparison with nil is forbidden的错误,之前部署能运行是因为旧版本Ecto没有加这个强制拦截。 - 你写的两个
raw_id_by_name_tenant/2函数匹配逻辑有漏洞:Elixir函数匹配是从上到下按优先级匹配,你写的第一个函数头defp raw_id_by_name_tenant(name, tenant_id)没有加任何守卫限制,会匹配所有第二个参数的输入(包括nil),第二个写的兜底函数defp raw_id_by_name_tenant(name, _)永远不会被触发。当你在case分支里调用raw_id_by_name_tenant(@default_name, nil)时,实际命中的是第一个带租户过滤的函数,生成了l.tenant_id == nil的非法查询条件,直接触发报错。
修复方法
调整两个私有函数的匹配规则,明确区分tenant_id为nil和非nil的场景,从根源避免nil传入带租户过滤的查询:
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 # 第二个参数为nil时,走无租户ID过滤的查询 defp raw_id_by_name_tenant(name, nil) do from( l in MTModel.Location, where: l.name == ^name, limit: 1, select: l.id ) |> MTModel.Repo.one() end # 加非nil守卫,确保只有tenant_id合法时才走租户过滤逻辑 defp raw_id_by_name_tenant(name, tenant_id) when not is_nil(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
修复后逻辑:
- 传入
tenant_id为nil时,会精准匹配到无租户过滤的查询函数,不会生成非法的nil比较条件 - 非nil的
tenant_id才会进入带租户过滤的查询,完全符合最初的代码设计预期 - 移除了原代码中
Repo.one([])里多余的空列表参数,符合Ecto的常规调用写法
内容的提问来源于stack exchange,提问作者Saani
相关产品推荐
相关产品推荐

