Skip to content

Commit 291e134

Browse files
LostKobrakaiBenjamin Milde
and
Benjamin Milde
authored
Make uuid encoding configurable and consistent (#52)
* Make uuid encoding configurable and consistent Co-authored-by: Benjamin Milde <[email protected]>
1 parent 43cc05d commit 291e134

File tree

6 files changed

+97
-15
lines changed

6 files changed

+97
-15
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ All notable changes will be documented in this file.
55
The format is based on [Keep a Changelog][keepachangelog], and this project
66
adheres to [Semantic Versioning][semver].
77

8+
## [Unreleased]
9+
### Changed
10+
- UUID encoding for both `:binary_id` and `:uuid`/`Ecto.UUID` is now configurable
11+
- `:uuid`/`Ecto.UUID` is now encoded as a string by default
12+
13+
814
## [0.6.0] - 2021-08-25
915
### Changed
1016
- `:utc_datetime` handling has been updated to completely remove the `Z` supplied and

lib/ecto/adapters/sqlite3.ex

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,22 @@ defmodule Ecto.Adapters.SQLite3 do
284284
[&Codec.decimal_decode/1, type]
285285
end
286286

287+
@impl Ecto.Adapter
288+
def loaders(:binary_id, type) do
289+
case Application.get_env(:ecto_sqlite3, :binary_id_type, :string) do
290+
:string -> [type]
291+
:binary -> [Ecto.UUID, type]
292+
end
293+
end
294+
295+
@impl Ecto.Adapter
296+
def loaders(:uuid, type) do
297+
case Application.get_env(:ecto_sqlite3, :uuid_type, :string) do
298+
:string -> []
299+
:binary -> [type]
300+
end
301+
end
302+
287303
# when we have an e.g., max(created_date) function
288304
# Ecto does not truly know the return type, hence :maybe
289305
# see Ecto.Query.Planner.collect_fields
@@ -316,6 +332,22 @@ defmodule Ecto.Adapters.SQLite3 do
316332
[type, &Codec.decimal_encode/1]
317333
end
318334

335+
@impl Ecto.Adapter
336+
def dumpers(:binary_id, type) do
337+
case Application.get_env(:ecto_sqlite3, :binary_id_type, :string) do
338+
:string -> [type]
339+
:binary -> [type, Ecto.UUID]
340+
end
341+
end
342+
343+
@impl Ecto.Adapter
344+
def dumpers(:uuid, type) do
345+
case Application.get_env(:ecto_sqlite3, :uuid_type, :string) do
346+
:string -> []
347+
:binary -> [type]
348+
end
349+
end
350+
319351
@impl Ecto.Adapter
320352
def dumpers(:time, type) do
321353
[type, &Codec.time_encode/1]

lib/ecto/adapters/sqlite3/data_type.ex

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,9 @@ defmodule Ecto.Adapters.SQLite3.DataType do
1212
def column_type(:serial, _opts), do: "INTEGER"
1313
def column_type(:bigserial, _opts), do: "INTEGER"
1414
def column_type(:bigint, _opts), do: "INTEGER"
15-
# TODO: We should make this configurable
16-
def column_type(:binary_id, _opts), do: "TEXT"
1715
def column_type(:string, _opts), do: "TEXT"
1816
def column_type(:float, _opts), do: "NUMERIC"
1917
def column_type(:binary, _opts), do: "BLOB"
20-
# TODO: We should make this configurable
21-
# SQLite3 does not support uuid
22-
def column_type(:uuid, _opts), do: "TEXT"
2318
def column_type(:map, _opts), do: "JSON"
2419
def column_type(:array, _opts), do: "JSON"
2520
def column_type({:map, _}, _opts), do: "JSON"
@@ -42,6 +37,20 @@ defmodule Ecto.Adapters.SQLite3.DataType do
4237
end
4338
end
4439

40+
def column_type(:binary_id, _opts) do
41+
case Application.get_env(:ecto_sqlite3, :binary_id_type, :string) do
42+
:string -> "TEXT_UUID"
43+
:binary -> "UUID"
44+
end
45+
end
46+
47+
def column_type(:uuid, _opts) do
48+
case Application.get_env(:ecto_sqlite3, :uuid_type, :string) do
49+
:string -> "TEXT_UUID"
50+
:binary -> "UUID"
51+
end
52+
end
53+
4554
def column_type(type, _) do
4655
type
4756
|> Atom.to_string()

test/ecto/adapters/sqlite3/connection_test.exs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -947,15 +947,13 @@ defmodule Ecto.Adapters.SQLite3.ConnectionTest do
947947
assert all(query) == ~s{SELECT 1 FROM "schema" AS s0 WHERE (s0."foo" = (0 + 123.0))}
948948
end
949949

950-
# TODO: We need to determine what format to store the UUID. Is it Text or binary 16?
951-
# Are we going for readability or for compactness?
952950
test "tagged type" do
953951
query =
954952
Schema
955953
|> select([], type(^"601d74e4-a8d3-4b6e-8365-eddb4c893327", Ecto.UUID))
956954
|> plan()
957955

958-
assert all(query) == ~s{SELECT CAST(? AS TEXT) FROM "schema" AS s0}
956+
assert all(query) == ~s{SELECT CAST(? AS TEXT_UUID) FROM "schema" AS s0}
959957
end
960958

961959
test "string type" do

test/ecto/adapters/sqlite3/data_type_test.exs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
11
defmodule Ecto.Adapters.SQLite3.DataTypeTest do
2-
use ExUnit.Case, async: true
2+
use ExUnit.Case, async: false
33

44
alias Ecto.Adapters.SQLite3.DataType
55

6+
setup do
7+
Application.put_env(:ecto_sqlite3, :binary_id_type, :string)
8+
Application.put_env(:ecto_sqlite3, :uuid_type, :string)
9+
10+
on_exit(fn ->
11+
Application.put_env(:ecto_sqlite3, :binary_id_type, :string)
12+
Application.put_env(:ecto_sqlite3, :uuid_type, :string)
13+
end)
14+
end
15+
616
describe ".column_type/2" do
717
test ":id is INTEGER" do
818
assert DataType.column_type(:id, nil) == "INTEGER"
@@ -16,16 +26,24 @@ defmodule Ecto.Adapters.SQLite3.DataTypeTest do
1626
assert DataType.column_type(:bigserial, nil) == "INTEGER"
1727
end
1828

19-
test ":binary_id is TEXT" do
20-
assert DataType.column_type(:binary_id, nil) == "TEXT"
29+
test ":binary_id is TEXT_UUID OR UUID" do
30+
assert DataType.column_type(:binary_id, nil) == "TEXT_UUID"
31+
32+
Application.put_env(:ecto_sqlite3, :binary_id_type, :binary)
33+
34+
assert DataType.column_type(:binary_id, nil) == "UUID"
2135
end
2236

2337
test ":string is TEXT" do
2438
assert DataType.column_type(:string, nil) == "TEXT"
2539
end
2640

27-
test ":uuid is TEXT" do
28-
assert DataType.column_type(:uuid, nil) == "TEXT"
41+
test ":uuid is TEXT_UUID or UUID" do
42+
assert DataType.column_type(:uuid, nil) == "TEXT_UUID"
43+
44+
Application.put_env(:ecto_sqlite3, :uuid_type, :binary)
45+
46+
assert DataType.column_type(:uuid, nil) == "UUID"
2947
end
3048

3149
test ":map is JSON" do

test/ecto/integration/uuid_test.exs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,29 @@
11
defmodule Ecto.Integration.UUIDTest do
2-
use Ecto.Integration.Case
2+
use Ecto.Integration.Case, async: false
33

44
alias Ecto.Integration.TestRepo
55
alias EctoSQLite3.Integration.Product
66

7-
test "handles uuid serialization and deserialization" do
7+
setup do
8+
Application.put_env(:ecto_sqlite3, :uuid_type, :string)
9+
on_exit(fn -> Application.put_env(:ecto_sqlite3, :uuid_type, :string) end)
10+
end
11+
12+
test "handles uuid serialization and deserialization with string format " do
13+
external_id = Ecto.UUID.generate()
14+
product = TestRepo.insert!(%Product{name: "Pupper Beer", external_id: external_id})
15+
16+
assert product.id
17+
assert product.external_id == external_id
18+
19+
found = TestRepo.get(Product, product.id)
20+
assert found
21+
assert found.external_id == external_id
22+
end
23+
24+
test "handles uuid serialization and deserialization with binary format " do
25+
Application.put_env(:ecto_sqlite3, :uuid_type, :binary)
26+
827
external_id = Ecto.UUID.generate()
928
product = TestRepo.insert!(%Product{name: "Pupper Beer", external_id: external_id})
1029

0 commit comments

Comments
 (0)