Typecase to `integer`, not `bigint` with ecto postgres

I have a int4range, which I want to query. In this example, the column is named from_postal_code I’d like to do something like this:

from r in Route, where: fragment("? @> ?", r.from_postal_code, type(^value, :integer)

But this doesn’t actually work, because ecto does not cast value to ::integer, but rather to ::bigint, resulting in the following error:

(Postgrex.Error) ERROR 42883 (undefined_function) operator does not exist: int4range @> bigint

I could switch to using a int8range, which is queryable by a bigint, but this seems… silly. Surely there must be some way to actually cast to a normal integer? As best as I can tell, this switcheroo of types is implemented at the adapter level, I think it might be this. And this seems tricky to bypass, but I’m not very familiar with ecto internals. Is there some way to bypass this to cast to a normal integer which is not a bigint?

2 Likes

PG accepts int4 as a synonym for integer, which also has a nice symmetry with declaring the column as int4range.

Looks like that tagged_to_db has a catchall if you pass a type it doesn’t understand, so type(^value, :int4) may do what you want.

1 Like

Unfortunately this doesn’t quite work:

 function :int4.cast/1 is undefined (module :int4 is not available)

But this gave me an idea! I can define a custom type like this:

  defmodule Int4 do
    @behaviour Ecto.Type
    def type, do: :int4
    def cast(value), do: {:ok, value}
    def load(value), do: {:ok, value}
    def dump(value), do: {:ok, value}
  end

And now type(^value, Int4) actually does this trick!

2 Likes