···55The format is loosely based on [Keep a Changelog][keepachangelog], and this
66project adheres to [Semantic Versioning][semver].
7788+## Unreleased
99+- changed: Configurable encoding for `:map` and `:array`, allowing usage of SQLite's JSONB storage format.
1010+811## v0.18.1
912- fixed: Support both `Jason` and `JSON`.
1013
+6
lib/ecto/adapters/sqlite3.ex
···5151 * `:uuid_type` - Defaults to `:string`. Determines the type of `:uuid` columns.
5252 Possible values and column types are the same as for
5353 [binary IDs](#module-binary-id-types).
5454+ * `:map_type` - Defaults to `:string`. Determines the type of `:map` columns.
5555+ Set to `:binary` to use the [JSONB](https://sqlite.org/jsonb.html)
5656+ storage format.
5757+ * `:array_type` - Defaults to `:string`. Determines the type of `:array` columns.
5858+ Arrays are serialized using JSON. Set to `:binary` to use the
5959+ [JSONB](https://sqlite.org/jsonb.html) storage format.
5460 * `:datetime_type` - Defaults to `:iso8601`. Determines how datetime fields are
5561 stored in the database. The allowed values are `:iso8601` and `:text_datetime`.
5662 `:iso8601` corresponds to a string of the form `YYYY-MM-DDThh:mm:ss` and
+28-4
lib/ecto/adapters/sqlite3/data_type.ex
···1717 def column_type(:string, _opts), do: "TEXT"
1818 def column_type(:float, _opts), do: "NUMERIC"
1919 def column_type(:binary, _opts), do: "BLOB"
2020- def column_type(:map, _opts), do: "TEXT"
2121- def column_type(:array, _opts), do: "TEXT"
2222- def column_type({:map, _}, _opts), do: "TEXT"
2323- def column_type({:array, _}, _opts), do: "TEXT"
2420 def column_type(:date, _opts), do: "TEXT"
2521 def column_type(:utc_datetime, _opts), do: "TEXT"
2622 def column_type(:utc_datetime_usec, _opts), do: "TEXT"
···4339 end
4440 end
45414242+ def column_type(:array, _opts) do
4343+ case Application.get_env(:ecto_sqlite3, :array_type, :string) do
4444+ :string -> "TEXT"
4545+ :binary -> "BLOB"
4646+ end
4747+ end
4848+4949+ def column_type({:array, _}, _opts) do
5050+ case Application.get_env(:ecto_sqlite3, :array_type, :string) do
5151+ :string -> "TEXT"
5252+ :binary -> "BLOB"
5353+ end
5454+ end
5555+4656 def column_type(:binary_id, _opts) do
4757 case Application.get_env(:ecto_sqlite3, :binary_id_type, :string) do
5858+ :string -> "TEXT"
5959+ :binary -> "BLOB"
6060+ end
6161+ end
6262+6363+ def column_type(:map, _opts) do
6464+ case Application.get_env(:ecto_sqlite3, :map_type, :string) do
6565+ :string -> "TEXT"
6666+ :binary -> "BLOB"
6767+ end
6868+ end
6969+7070+ def column_type({:map, _}, _opts) do
7171+ case Application.get_env(:ecto_sqlite3, :map_type, :string) do
4872 :string -> "TEXT"
4973 :binary -> "BLOB"
5074 end
+24-4
test/ecto/adapters/sqlite3/data_type_test.exs
···44 alias Ecto.Adapters.SQLite3.DataType
5566 setup do
77+ Application.put_env(:ecto_sqlite3, :array_type, :string)
78 Application.put_env(:ecto_sqlite3, :binary_id_type, :string)
99+ Application.put_env(:ecto_sqlite3, :map_type, :string)
810 Application.put_env(:ecto_sqlite3, :uuid_type, :string)
9111012 on_exit(fn ->
1313+ Application.put_env(:ecto_sqlite3, :array_type, :string)
1114 Application.put_env(:ecto_sqlite3, :binary_id_type, :string)
1515+ Application.put_env(:ecto_sqlite3, :map_type, :string)
1216 Application.put_env(:ecto_sqlite3, :uuid_type, :string)
1317 end)
1418 end
···4650 assert DataType.column_type(:uuid, nil) == "BLOB"
4751 end
48524949- test ":map is TEXT" do
5353+ test ":map is TEXT or BLOB" do
5054 assert DataType.column_type(:map, nil) == "TEXT"
5555+5656+ Application.put_env(:ecto_sqlite3, :map_type, :binary)
5757+5858+ assert DataType.column_type(:map, nil) == "BLOB"
5159 end
52605353- test "{:map, _} is TEXT" do
6161+ test "{:map, _} is TEXT or BLOB" do
5462 assert DataType.column_type({:map, %{}}, nil) == "TEXT"
6363+6464+ Application.put_env(:ecto_sqlite3, :map_type, :binary)
6565+6666+ assert DataType.column_type({:map, %{}}, nil) == "BLOB"
5567 end
56685757- test ":array is TEXT" do
6969+ test ":array is TEXT or BLOB" do
5870 assert DataType.column_type(:array, nil) == "TEXT"
7171+7272+ Application.put_env(:ecto_sqlite3, :array_type, :binary)
7373+7474+ assert DataType.column_type(:array, nil) == "BLOB"
5975 end
60766161- test "{:array, _} is TEXT" do
7777+ test "{:array, _} is TEXT or BLOB" do
6278 assert DataType.column_type({:array, []}, nil) == "TEXT"
7979+8080+ Application.put_env(:ecto_sqlite3, :array_type, :binary)
8181+8282+ assert DataType.column_type({:array, []}, nil) == "BLOB"
6383 end
64846585 test ":float is NUMERIC" do
+29-2
test/ecto/integration/json_test.exs
···11defmodule Ecto.Integration.JsonTest do
22- use Ecto.Integration.Case
22+ use Ecto.Integration.Case, async: false
3344 alias Ecto.Adapters.SQL
55 alias Ecto.Integration.TestRepo
···7788 @moduletag :integration
991010- test "serializes json correctly" do
1010+ setup do
1111+ Application.put_env(:ecto_sqlite3, :map_type, :string)
1212+ on_exit(fn -> Application.put_env(:ecto_sqlite3, :map_type, :string) end)
1313+ end
1414+1515+ test "serializes json correctly with string format" do
1616+ # Insert a record purposefully with atoms as the map key. We are going to
1717+ # verify later they were coerced into strings.
1818+ setting =
1919+ %Setting{}
2020+ |> Setting.changeset(%{properties: %{foo: "bar", qux: "baz"}})
2121+ |> TestRepo.insert!()
2222+2323+ # Read the record back using ecto and confirm it
2424+ assert %Setting{properties: %{"foo" => "bar", "qux" => "baz"}} =
2525+ TestRepo.get(Setting, setting.id)
2626+2727+ assert %{num_rows: 1, rows: [["bar"]]} =
2828+ SQL.query!(
2929+ TestRepo,
3030+ "select json_extract(properties, '$.foo') from settings where id = ?1",
3131+ [setting.id]
3232+ )
3333+ end
3434+3535+ test "serializes json correctly with binary format" do
3636+ Application.put_env(:ecto_sqlite3, :map_type, :binary)
3737+1138 # Insert a record purposefully with atoms as the map key. We are going to
1239 # verify later they were coerced into strings.
1340 setting =