Skip to content

cond/if calculation in query filters causes DBConnection.EncodeError #767

@diogomrts

Description

@diogomrts

Description

Using a cond or if expression calculation in a query filter causes a DBConnection.EncodeError. The same calculation works correctly when loaded.

Error

** (DBConnection.EncodeError) Postgrex expected a binary, got 3.
Please make sure the value you are passing matches the definition
in your table or in your query or convert the value accordingly.

Steps to reproduce

Define a calculation using cond that returns an integer:

calculate(:score_category, :integer,
  expr(
    cond do
      score > 100 -> 3
      score > 50 -> 2
      score > 0 -> 1
      true -> 0
    end
  )
)

Loading it works:

# OK — generates: THEN 3::bigint
Ash.load!(post, :score_category)

Filtering on it fails:

# ERROR — generates: THEN 3 (no cast)
Post |> Ash.Query.filter(score_category == 2) |> Ash.read!()

Generated SQL

SELECT (works):

(CASE WHEN (score > 100) THEN 3::bigint WHEN ... END)::bigint

WHERE (fails):

(CASE WHEN (score > 100) THEN 3 WHEN ... END)::bigint = 2

The THEN clause literals are not type-cast in the WHERE context, causing Postgrex to default to text encoding for the parameters, which produces the binary-vs-integer mismatch.

Root cause

The bug is in ash_sql (deps/ash_sql/lib/expr.ex).

When Ash converts cond to nested If nodes, the %If{} struct has embedded?: true. The If handler calls do_dynamic_expr for each THEN/ELSE value with the resolved type. However, because embedded? is true, the literal handler clause (default_dynamic_expr/6 at ~line 2990) skips type casting — unlike the embedded? = false clause (~line 3037) which calls parameterized_type + type_expr.

Impact

  • Any cond/if expression calculation returning integers/floats used in Ash.Query.filter will fail
  • Ash policy FilterCheck modules that reference such calculations break authorization on bulk operations (e.g. cascade disable/delete)

Failing test

PR with reproduction: #766

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions