Skip to content

Add Exqlite.TypeExtensions to allow more types to be stored in the database #333

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 20, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,17 @@ end
### Runtime Configuration

```elixir
config :exqlite, default_chunk_size: 100
config :exqlite,
default_chunk_size: 100,
type_extensions: [MyApp.TypeExtension]
```

* `default_chunk_size` - The chunk size that is used when multi-stepping when
not specifying the chunk size explicitly.

* `type_extensions`: An optional list of modules that implement the
Exqlite.TypeExtension behaviour, allowing types beyond the default set that
can be stored and retrieved from the database.

### Compile-time Configuration

In `config/config.exs`,
Expand Down
11 changes: 11 additions & 0 deletions lib/exqlite/extension.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
defmodule Exqlite.TypeExtension do
@moduledoc """
A behaviour that defines the API for extensions providing custom data loaders and dumpers
for Ecto schemas.
"""

@doc """
Takes a value and convers it to data suitable for storage in the database.
"""
@callback convert(value :: term) :: term
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this common for these to simply be term that only return nil when it can't be converted? Or is a {:ok, term()} | :error more useful?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could do a tagged tuple, yes. See 7b5d2ee

This also has the nice effect of making it more like the Ecto usage.

end
17 changes: 16 additions & 1 deletion lib/exqlite/sqlite3.ex
Original file line number Diff line number Diff line change
Expand Up @@ -481,5 +481,20 @@ defmodule Exqlite.Sqlite3 do
raise ArgumentError, "#{inspect(datetime)} is not in UTC"
end

defp convert(val), do: val
defp convert(val) do
convert_with_type_extensions(type_extensions(), val)
end

defp convert_with_type_extensions([], val), do: val
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going back and fourth on allowing type_extensions to be nil.

On one hand, letting it default to nil, will make the pattern match be faster than matching with an empty list.

defp convert_with_type_extensions(nil, val), do: val
defp convert_with_type_extensions([], val), do: val

And then changing Application.get_env(:exqlite, :type_extensions, []) to be Application.get_env(:exqlite, :type_extensions).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went back and forth as well .. I'm not sure the speed gain is really going to be noticeable, but it's easy enough to do.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See 704c9bd


defp convert_with_type_extensions([extension | other_extensions], val) do
case extension.convert(val) do
nil -> convert_with_type_extensions(other_extensions, val)
convert -> convert
end
end

defp type_extensions() do
Application.get_env(:exqlite, :type_extensions, [])
end
end