From 67213cb7a12156613a237d8ead362252e97ee86a Mon Sep 17 00:00:00 2001 From: ruslandoga Date: Thu, 19 Jun 2025 22:53:58 +0300 Subject: [PATCH 1/9] update Ecto to v3.13 --- mix.exs | 2 +- mix.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index dd68ed1..c1a02b4 100644 --- a/mix.exs +++ b/mix.exs @@ -36,7 +36,7 @@ defmodule EctoSQLite3.MixProject do [ {:decimal, "~> 1.6 or ~> 2.0"}, {:ecto_sql, "~> 3.12"}, - {:ecto, "~> 3.12"}, + {:ecto, "~> 3.13.0"}, {:exqlite, "~> 0.22"}, {:ex_doc, "~> 0.27", only: [:dev], runtime: false}, {:jason, ">= 0.0.0", only: [:dev, :test, :docs]}, diff --git a/mix.lock b/mix.lock index e082e75..0f5ac4f 100644 --- a/mix.lock +++ b/mix.lock @@ -8,7 +8,7 @@ "decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"}, "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, - "ecto": {:hex, :ecto, "3.12.5", "4a312960ce612e17337e7cefcf9be45b95a3be6b36b6f94dfb3d8c361d631866", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6eb18e80bef8bb57e17f5a7f068a1719fbda384d40fc37acb8eb8aeca493b6ea"}, + "ecto": {:hex, :ecto, "3.13.1", "ebb11c2f0307ff62e8aaba57def59ad920a3cbd89d002b1118944cbf598c13c7", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d9ea5075a6f3af9cd2cdbabe8a0759eb73b485e981fd7c03014f79479ac85340"}, "ecto_sql": {:hex, :ecto_sql, "3.12.1", "c0d0d60e85d9ff4631f12bafa454bc392ce8b9ec83531a412c12a0d415a3a4d0", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.12", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "aff5b958a899762c5f09028c847569f7dfb9cc9d63bdb8133bff8a5546de6bf5"}, "elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"}, "ex_doc": {:hex, :ex_doc, "0.37.3", "f7816881a443cd77872b7d6118e8a55f547f49903aef8747dbcb345a75b462f9", [:mix], [{:earmark_parser, "~> 1.4.42", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "e6aebca7156e7c29b5da4daa17f6361205b2ae5f26e5c7d8ca0d3f7e18972233"}, From 173d235b818b452e2fc438b051852f861e28e9ef Mon Sep 17 00:00:00 2001 From: ruslandoga Date: Thu, 19 Jun 2025 22:55:17 +0300 Subject: [PATCH 2/9] add identifier/1 support --- lib/ecto/adapters/sqlite3/connection.ex | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/ecto/adapters/sqlite3/connection.ex b/lib/ecto/adapters/sqlite3/connection.ex index c2e27ec..d809587 100644 --- a/lib/ecto/adapters/sqlite3/connection.ex +++ b/lib/ecto/adapters/sqlite3/connection.ex @@ -1376,6 +1376,10 @@ defmodule Ecto.Adapters.SQLite3.Connection do quote_name(literal) end + defp expr({:identifier, _, [literal]}, _sources, _query) do + quote_name(literal) + end + defp expr({:splice, _, [{:^, _, [_, length]}]}, _sources, _query) do Enum.intersperse(List.duplicate(??, length), ?,) end From e3601842621d9732c5ea9d864a6cbcfc94fbc33e Mon Sep 17 00:00:00 2001 From: ruslandoga Date: Thu, 19 Jun 2025 22:56:15 +0300 Subject: [PATCH 3/9] switch to identifier/1 in tests to avoid warnings --- test/ecto/adapters/sqlite3/connection/select_test.exs | 4 ++-- test/ecto/integration/crud_test.exs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/ecto/adapters/sqlite3/connection/select_test.exs b/test/ecto/adapters/sqlite3/connection/select_test.exs index 4707a49..4d206d2 100644 --- a/test/ecto/adapters/sqlite3/connection/select_test.exs +++ b/test/ecto/adapters/sqlite3/connection/select_test.exs @@ -157,10 +157,10 @@ defmodule Ecto.Adapters.SQLite3.Connection.SelectTest do assert ~s{SELECT downcase(s0."x") FROM "schema" AS s0} == all(query) end - test "collating with literal" do + test "collating with identifier" do query = Schema - |> select([r], fragment("? COLLATE ?", r.x, literal(^"es_ES"))) + |> select([r], fragment("? COLLATE ?", r.x, identifier(^"es_ES"))) |> plan() assert ~s{SELECT s0."x" COLLATE "es_ES" FROM "schema" AS s0} == all(query) diff --git a/test/ecto/integration/crud_test.exs b/test/ecto/integration/crud_test.exs index a5f92cb..a8bad3c 100644 --- a/test/ecto/integration/crud_test.exs +++ b/test/ecto/integration/crud_test.exs @@ -272,11 +272,11 @@ defmodule Ecto.Integration.CrudTest do assert [_] = TestRepo.all(from(a in Account, as: :user, where: exists(subquery))) end - test "can handle fragment literal" do + test "can handle fragment identifier" do account1 = TestRepo.insert!(%Account{name: "Main"}) name = "name" - query = from(a in Account, where: fragment("? = ?", literal(^name), "Main")) + query = from(a in Account, where: fragment("? = ?", identifier(^name), "Main")) assert [account] = TestRepo.all(query) assert account.id == account1.id From 5d19cb47bb2b35d067d68c48c2de58c62cecf5ac Mon Sep 17 00:00:00 2001 From: ruslandoga Date: Thu, 19 Jun 2025 22:57:25 +0300 Subject: [PATCH 4/9] changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 15d6639..d15e7e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ The format is loosely based on [Keep a Changelog][keepachangelog], and this project adheres to [Semantic Versioning][semver]. ## Unreleased +- added: Ecto `identifier/1` support +- changed: Bump (and restrict) Ecto to `3.13.0` ## v0.19.0 - changed: Configurable encoding for `:map` and `:array`, allowing usage of SQLite's JSONB storage format. From fa3f71e3285b41c7d43bcc05325ef0759835705e Mon Sep 17 00:00:00 2001 From: ruslandoga Date: Thu, 19 Jun 2025 22:59:39 +0300 Subject: [PATCH 5/9] link identifier/1 in changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d15e7e1..d1658cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ The format is loosely based on [Keep a Changelog][keepachangelog], and this project adheres to [Semantic Versioning][semver]. ## Unreleased -- added: Ecto `identifier/1` support +- added: Ecto [`identifier/1`](https://hexdocs.pm/ecto/Ecto.Query.API.html#identifier/1) support - changed: Bump (and restrict) Ecto to `3.13.0` ## v0.19.0 From 6fa9a3fc2db77960901d4bdb8be7b56fe4147895 Mon Sep 17 00:00:00 2001 From: ruslandoga Date: Thu, 19 Jun 2025 23:07:55 +0300 Subject: [PATCH 6/9] forgot to update ecto_sql for new migrations --- mix.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.lock b/mix.lock index 0f5ac4f..c45da7e 100644 --- a/mix.lock +++ b/mix.lock @@ -9,7 +9,7 @@ "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"}, "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, "ecto": {:hex, :ecto, "3.13.1", "ebb11c2f0307ff62e8aaba57def59ad920a3cbd89d002b1118944cbf598c13c7", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d9ea5075a6f3af9cd2cdbabe8a0759eb73b485e981fd7c03014f79479ac85340"}, - "ecto_sql": {:hex, :ecto_sql, "3.12.1", "c0d0d60e85d9ff4631f12bafa454bc392ce8b9ec83531a412c12a0d415a3a4d0", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.12", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "aff5b958a899762c5f09028c847569f7dfb9cc9d63bdb8133bff8a5546de6bf5"}, + "ecto_sql": {:hex, :ecto_sql, "3.13.0", "a732428f38ce86612a2c34a1ea5d0a9642a5a71f044052007fd2f2e815707990", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.13.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5ce13085122a0871d93ea9ba1a886447d89c07f3b563e19e0b3dcdf201ed9fe9"}, "elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"}, "ex_doc": {:hex, :ex_doc, "0.37.3", "f7816881a443cd77872b7d6118e8a55f547f49903aef8747dbcb345a75b462f9", [:mix], [{:earmark_parser, "~> 1.4.42", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "e6aebca7156e7c29b5da4daa17f6361205b2ae5f26e5c7d8ca0d3f7e18972233"}, "exqlite": {:hex, :exqlite, "0.30.0", "92cd991fc7fcdf688718452bca5012ffa9015d333845d7661ee00c8256e80423", [:make, :mix], [{:cc_precompiler, "~> 0.1", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.8", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "3ed5800d1ccafc220d4a2e0bc7cf2c0e2f93456f03f73856158b474e010c792c"}, From ce6c452744e8f37bbb5ba37584c3dbe19b41e863 Mon Sep 17 00:00:00 2001 From: ruslandoga Date: Thu, 19 Jun 2025 23:08:28 +0300 Subject: [PATCH 7/9] also pin ecto_sql to ~> 0.13.0 --- mix.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mix.exs b/mix.exs index c1a02b4..b2b024d 100644 --- a/mix.exs +++ b/mix.exs @@ -35,7 +35,7 @@ defmodule EctoSQLite3.MixProject do defp deps do [ {:decimal, "~> 1.6 or ~> 2.0"}, - {:ecto_sql, "~> 3.12"}, + {:ecto_sql, "~> 3.13.0"}, {:ecto, "~> 3.13.0"}, {:exqlite, "~> 0.22"}, {:ex_doc, "~> 0.27", only: [:dev], runtime: false}, From f70c79c0088ea841a028c245c725e39294ca0b12 Mon Sep 17 00:00:00 2001 From: ruslandoga Date: Thu, 19 Jun 2025 23:15:24 +0300 Subject: [PATCH 8/9] drop :literal clause --- lib/ecto/adapters/sqlite3/connection.ex | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/ecto/adapters/sqlite3/connection.ex b/lib/ecto/adapters/sqlite3/connection.ex index d809587..d29401c 100644 --- a/lib/ecto/adapters/sqlite3/connection.ex +++ b/lib/ecto/adapters/sqlite3/connection.ex @@ -1372,10 +1372,6 @@ defmodule Ecto.Adapters.SQLite3.Connection do raise ArgumentError, "SQLite3 adapter does not support values lists" end - defp expr({:literal, _, [literal]}, _sources, _query) do - quote_name(literal) - end - defp expr({:identifier, _, [literal]}, _sources, _query) do quote_name(literal) end From 14ee96bb24ab6ea7bcdad487263a393b9d054a43 Mon Sep 17 00:00:00 2001 From: ruslandoga Date: Thu, 19 Jun 2025 23:17:58 +0300 Subject: [PATCH 9/9] return literal/1 tests --- test/ecto/adapters/sqlite3/connection/select_test.exs | 9 +++++++++ test/ecto/integration/crud_test.exs | 10 ++++++++++ 2 files changed, 19 insertions(+) diff --git a/test/ecto/adapters/sqlite3/connection/select_test.exs b/test/ecto/adapters/sqlite3/connection/select_test.exs index 4d206d2..358e4b6 100644 --- a/test/ecto/adapters/sqlite3/connection/select_test.exs +++ b/test/ecto/adapters/sqlite3/connection/select_test.exs @@ -157,6 +157,15 @@ defmodule Ecto.Adapters.SQLite3.Connection.SelectTest do assert ~s{SELECT downcase(s0."x") FROM "schema" AS s0} == all(query) end + test "collating with literal" do + query = + Schema + |> select([r], fragment("? COLLATE ?", r.x, literal(^"es_ES"))) + |> plan() + + assert ~s{SELECT s0."x" COLLATE "es_ES" FROM "schema" AS s0} == all(query) + end + test "collating with identifier" do query = Schema diff --git a/test/ecto/integration/crud_test.exs b/test/ecto/integration/crud_test.exs index a8bad3c..8686dfa 100644 --- a/test/ecto/integration/crud_test.exs +++ b/test/ecto/integration/crud_test.exs @@ -272,6 +272,16 @@ defmodule Ecto.Integration.CrudTest do assert [_] = TestRepo.all(from(a in Account, as: :user, where: exists(subquery))) end + test "can handle fragment literal" do + account1 = TestRepo.insert!(%Account{name: "Main"}) + + name = "name" + query = from(a in Account, where: fragment("? = ?", literal(^name), "Main")) + + assert [account] = TestRepo.all(query) + assert account.id == account1.id + end + test "can handle fragment identifier" do account1 = TestRepo.insert!(%Account{name: "Main"})