An Ecto SQLite3 adapter.
0

Configure Feed

Select the types of activity you want to include in your feed.

Make map/array encoding configurable (#163)

* Make map encoding configurable
* Make array encoding configurable
* Add changelog entry

authored by

Nassim and committed by
GitHub
(Mar 18, 2025, 9:22 PM -0500) 952f38b0 bbe7ebe3

+90 -10
+3
CHANGELOG.md
··· 5 5 The format is loosely based on [Keep a Changelog][keepachangelog], and this 6 6 project adheres to [Semantic Versioning][semver]. 7 7 8 + ## Unreleased 9 + - changed: Configurable encoding for `:map` and `:array`, allowing usage of SQLite's JSONB storage format. 10 + 8 11 ## v0.18.1 9 12 - fixed: Support both `Jason` and `JSON`. 10 13
+6
lib/ecto/adapters/sqlite3.ex
··· 51 51 * `:uuid_type` - Defaults to `:string`. Determines the type of `:uuid` columns. 52 52 Possible values and column types are the same as for 53 53 [binary IDs](#module-binary-id-types). 54 + * `:map_type` - Defaults to `:string`. Determines the type of `:map` columns. 55 + Set to `:binary` to use the [JSONB](https://sqlite.org/jsonb.html) 56 + storage format. 57 + * `:array_type` - Defaults to `:string`. Determines the type of `:array` columns. 58 + Arrays are serialized using JSON. Set to `:binary` to use the 59 + [JSONB](https://sqlite.org/jsonb.html) storage format. 54 60 * `:datetime_type` - Defaults to `:iso8601`. Determines how datetime fields are 55 61 stored in the database. The allowed values are `:iso8601` and `:text_datetime`. 56 62 `:iso8601` corresponds to a string of the form `YYYY-MM-DDThh:mm:ss` and
+28 -4
lib/ecto/adapters/sqlite3/data_type.ex
··· 17 17 def column_type(:string, _opts), do: "TEXT" 18 18 def column_type(:float, _opts), do: "NUMERIC" 19 19 def column_type(:binary, _opts), do: "BLOB" 20 - def column_type(:map, _opts), do: "TEXT" 21 - def column_type(:array, _opts), do: "TEXT" 22 - def column_type({:map, _}, _opts), do: "TEXT" 23 - def column_type({:array, _}, _opts), do: "TEXT" 24 20 def column_type(:date, _opts), do: "TEXT" 25 21 def column_type(:utc_datetime, _opts), do: "TEXT" 26 22 def column_type(:utc_datetime_usec, _opts), do: "TEXT" ··· 43 39 end 44 40 end 45 41 42 + def column_type(:array, _opts) do 43 + case Application.get_env(:ecto_sqlite3, :array_type, :string) do 44 + :string -> "TEXT" 45 + :binary -> "BLOB" 46 + end 47 + end 48 + 49 + def column_type({:array, _}, _opts) do 50 + case Application.get_env(:ecto_sqlite3, :array_type, :string) do 51 + :string -> "TEXT" 52 + :binary -> "BLOB" 53 + end 54 + end 55 + 46 56 def column_type(:binary_id, _opts) do 47 57 case Application.get_env(:ecto_sqlite3, :binary_id_type, :string) do 58 + :string -> "TEXT" 59 + :binary -> "BLOB" 60 + end 61 + end 62 + 63 + def column_type(:map, _opts) do 64 + case Application.get_env(:ecto_sqlite3, :map_type, :string) do 65 + :string -> "TEXT" 66 + :binary -> "BLOB" 67 + end 68 + end 69 + 70 + def column_type({:map, _}, _opts) do 71 + case Application.get_env(:ecto_sqlite3, :map_type, :string) do 48 72 :string -> "TEXT" 49 73 :binary -> "BLOB" 50 74 end
+24 -4
test/ecto/adapters/sqlite3/data_type_test.exs
··· 4 4 alias Ecto.Adapters.SQLite3.DataType 5 5 6 6 setup do 7 + Application.put_env(:ecto_sqlite3, :array_type, :string) 7 8 Application.put_env(:ecto_sqlite3, :binary_id_type, :string) 9 + Application.put_env(:ecto_sqlite3, :map_type, :string) 8 10 Application.put_env(:ecto_sqlite3, :uuid_type, :string) 9 11 10 12 on_exit(fn -> 13 + Application.put_env(:ecto_sqlite3, :array_type, :string) 11 14 Application.put_env(:ecto_sqlite3, :binary_id_type, :string) 15 + Application.put_env(:ecto_sqlite3, :map_type, :string) 12 16 Application.put_env(:ecto_sqlite3, :uuid_type, :string) 13 17 end) 14 18 end ··· 46 50 assert DataType.column_type(:uuid, nil) == "BLOB" 47 51 end 48 52 49 - test ":map is TEXT" do 53 + test ":map is TEXT or BLOB" do 50 54 assert DataType.column_type(:map, nil) == "TEXT" 55 + 56 + Application.put_env(:ecto_sqlite3, :map_type, :binary) 57 + 58 + assert DataType.column_type(:map, nil) == "BLOB" 51 59 end 52 60 53 - test "{:map, _} is TEXT" do 61 + test "{:map, _} is TEXT or BLOB" do 54 62 assert DataType.column_type({:map, %{}}, nil) == "TEXT" 63 + 64 + Application.put_env(:ecto_sqlite3, :map_type, :binary) 65 + 66 + assert DataType.column_type({:map, %{}}, nil) == "BLOB" 55 67 end 56 68 57 - test ":array is TEXT" do 69 + test ":array is TEXT or BLOB" do 58 70 assert DataType.column_type(:array, nil) == "TEXT" 71 + 72 + Application.put_env(:ecto_sqlite3, :array_type, :binary) 73 + 74 + assert DataType.column_type(:array, nil) == "BLOB" 59 75 end 60 76 61 - test "{:array, _} is TEXT" do 77 + test "{:array, _} is TEXT or BLOB" do 62 78 assert DataType.column_type({:array, []}, nil) == "TEXT" 79 + 80 + Application.put_env(:ecto_sqlite3, :array_type, :binary) 81 + 82 + assert DataType.column_type({:array, []}, nil) == "BLOB" 63 83 end 64 84 65 85 test ":float is NUMERIC" do
+29 -2
test/ecto/integration/json_test.exs
··· 1 1 defmodule Ecto.Integration.JsonTest do 2 - use Ecto.Integration.Case 2 + use Ecto.Integration.Case, async: false 3 3 4 4 alias Ecto.Adapters.SQL 5 5 alias Ecto.Integration.TestRepo ··· 7 7 8 8 @moduletag :integration 9 9 10 - test "serializes json correctly" do 10 + setup do 11 + Application.put_env(:ecto_sqlite3, :map_type, :string) 12 + on_exit(fn -> Application.put_env(:ecto_sqlite3, :map_type, :string) end) 13 + end 14 + 15 + test "serializes json correctly with string format" do 16 + # Insert a record purposefully with atoms as the map key. We are going to 17 + # verify later they were coerced into strings. 18 + setting = 19 + %Setting{} 20 + |> Setting.changeset(%{properties: %{foo: "bar", qux: "baz"}}) 21 + |> TestRepo.insert!() 22 + 23 + # Read the record back using ecto and confirm it 24 + assert %Setting{properties: %{"foo" => "bar", "qux" => "baz"}} = 25 + TestRepo.get(Setting, setting.id) 26 + 27 + assert %{num_rows: 1, rows: [["bar"]]} = 28 + SQL.query!( 29 + TestRepo, 30 + "select json_extract(properties, '$.foo') from settings where id = ?1", 31 + [setting.id] 32 + ) 33 + end 34 + 35 + test "serializes json correctly with binary format" do 36 + Application.put_env(:ecto_sqlite3, :map_type, :binary) 37 + 11 38 # Insert a record purposefully with atoms as the map key. We are going to 12 39 # verify later they were coerced into strings. 13 40 setting =