I have a custom Ecto type and want to call a SQL function when dumping a value of this type into a virtual table with SQLite.
I’m developing this library to use sqlite-vec with Elixir.
There are 3 vector types in sqlite-vec: float32, int8, and bit.
The most performant way to use sqlite-vec is to create a virtual table.
Then you can create vectors with SQL functions as constructors.
So, in SQL that would be something like this:
CREATE VIRTUAL TABLE vt USING vec0(id INTEGER PRIMARY KEY, embedding int8[2])
INSERT INTO vt(id, embedding) VALUES(1, vec_int8('[1, 2]'))
I’ve created a custom Ecto type for each of the three vector types, e.g. here for int8:
defmodule SqliteVec.Ecto.Int8 do
@moduledoc """
`Ecto.Type` for `SqliteVec.Int8`
"""
use Ecto.Type
def type, do: :binary
def cast(value) do
{:ok, SqliteVec.Int8.new(value)}
end
def load(data) do
{:ok, SqliteVec.Int8.from_binary(data)}
end
def dump(%SqliteVec.Int8{} = vector) do
{:ok, SqliteVec.Int8.to_binary(vector)}
end
def dump(_), do: :error
end
What I want to achieve is inserting vectors with regular Ecto.Repo functions.
For that, I define a schema:
defmodule VT do
use Ecto.Schema
schema "vt" do
field(:embedding, SqliteVec.Ecto.Int8)
end
end
And now I want to insert using
MyApp.Repo.insert(%VT{
embedding: SqliteVec.Int8.new([1, 2])
})
The problem is that this fails as I’m dumping the binary data directly instead of calling the vec_int8 constructor function.
Actually, sqlite-vec interprets plain binary data as float32 (as if I would call the vec_f32 constructor function).
So it works for float32 vectors without constructor function but fails for int8 and bit vectors because the types don’t match.
What I’ve tried:
- parameterized types, I don’t think they add any capabilities that would work here
- changing the type of the custom type from
:binaryto:stringand dump a string of the constructor function with interpolated arguments, e.g. “vec_int8(‘[1, 2]’)”
I also had a look at ecto_sqlite3 as I’m assuming that the correct place for this functionality would be the adapter, and in particular the codec.
I guess I would need a way to define my own codec for the custom types, and call the constructor function there, not sure if that would work though.
Thanks in advance for any input!




















