-
Notifications
You must be signed in to change notification settings - Fork 54
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
Add Exqlite.TypeExtensions to allow more types to be stored in the database #333
Conversation
Runtime extensions to convert data types into something that can be serialized into the database.
lib/exqlite/extension.ex
Outdated
@doc """ | ||
Takes a value and convers it to data suitable for storage in the database. | ||
""" | ||
@callback convert(value :: term) :: term |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
lib/exqlite/sqlite3.ex
Outdated
defp convert(val) do | ||
convert_with_type_extensions(type_extensions(), val) | ||
end | ||
|
||
defp convert_with_type_extensions([], val), do: val |
There was a problem hiding this comment.
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)
.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See 704c9bd
lib/exqlite/type_extension.ex
Outdated
|
||
Returns a tagged :ok/:error tuple. If the value is not convertable by this | ||
extension, returns nil. | ||
""" | ||
@callback convert(value :: term) :: term | ||
@callback convert(value :: term) :: {:ok, term} | {:error, reason :: term} | nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know it's just an atom, but perhaps :unsupported
is more clear than nil
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason I didn't use a more "expressive" atom is one ends up having to remember all the various names for "this is not something that is happening" return values. nil
is convenient in that it is the least surprising (at least for me) and widely used to mean "nothing here".
If you prefer, I can change it to :unsupported
, but as a library user I lean towards "common and therefore predictable" in these cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No that's a fine justification.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a great start. I think we can make the type extensions be configurable per ecto adapter. The idea that I have is that we store the configuration with the state of the connection later and allow each ecto repo to optionally add a type extension it wants to use so that others aren't "poisoned" by it.
However, I'm not gonna implement that right now. We'll let this one play out and adjust later.
That sounds great. It's a bit more complicated internally (obviously), but would be a nice improvement. Cheers! |
I'll get this cut out in the next version soon. I need to retire an old version of this adapter and update the previous release. |
This PR introduces a runtime extension to convert data types into something that can be serialized into the database.
See elixir-sqlite/ecto_sqlite3#167 and elixir-sqlite/ecto_sqlite3#166
My primary goal is to be able to use Spatialite from Elixir. With this PR and the one against
ecto_sqlite3
, it is 95% of the way there. With these two PRs applied, one can now do things like:.. and it just works, regardless of whether
MyApp.Repo
is point at a PostgreSQL database with PostGIS enabled, or an SQLite3 database with Spatialite loaded.The one remaining annoyance is getting structs back out of the database without going through ecto (which does work with the
ecto_sqlite3
PR!). So, I still have to figure out a nice way for the user to get backGeo
structs from raw queries such asfrom(location in Location, limit: 1, select: MM2.transform(location.geom, 3452))
, but otherwise everything Just Works transparently with this change.