From 4474c163281b10485a8ebd9fd8e508dce5287642 Mon Sep 17 00:00:00 2001 From: Greg Rychlewski Date: Thu, 2 Jan 2025 18:15:05 -0500 Subject: [PATCH 01/12] allow prepare option to be specified per operation --- integration_test/myxql/prepare_test.exs | 19 +++++++++++++++++++ lib/ecto/adapters/myxql.ex | 19 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 integration_test/myxql/prepare_test.exs diff --git a/integration_test/myxql/prepare_test.exs b/integration_test/myxql/prepare_test.exs new file mode 100644 index 00000000..ea502053 --- /dev/null +++ b/integration_test/myxql/prepare_test.exs @@ -0,0 +1,19 @@ +defmodule Ecto.Integration.PrepareTest do + use Ecto.Integration.Case, async: true + + alias Ecto.Integration.TestRepo + alias Ecto.Integration.Post + + test "prepare option" do + one = TestRepo.insert!(%Post{title: "one"}) + two = TestRepo.insert!(%Post{title: "two"}) + + # Uncached + assert TestRepo.all(Post, prepare: :unnamed) == [one, two] + assert TestRepo.all(Post, prepare: :named) == [one, two] + + # Cached + assert TestRepo.all(Post, prepare: :unnamed) == [one, two] + assert TestRepo.all(Post, prepare: :named) == [one, two] + end +end diff --git a/lib/ecto/adapters/myxql.ex b/lib/ecto/adapters/myxql.ex index 1c589aeb..73ae9cde 100644 --- a/lib/ecto/adapters/myxql.ex +++ b/lib/ecto/adapters/myxql.ex @@ -147,6 +147,8 @@ defmodule Ecto.Adapters.MyXQL do @behaviour Ecto.Adapter.Storage @behaviour Ecto.Adapter.Structure + @default_prepare_opt :named + ## Custom MySQL types @impl true @@ -171,6 +173,23 @@ defmodule Ecto.Adapters.MyXQL do defp json_decode(x) when is_binary(x), do: {:ok, MyXQL.json_library().decode!(x)} defp json_decode(x), do: {:ok, x} + ## Query API + + @impl Ecto.Adapter.Queryable + def execute(adapter_meta, query_meta, query, params, opts) do + prepare = Keyword.get(opts, :prepare, @default_prepare_opt) + + unless valid_prepare?(prepare) do + raise ArgumentError, + "expected option `:prepare` to be either `:named` or `:unnamed`, got: #{inspect(prepare)}" + end + + Ecto.Adapters.SQL.execute(prepare, adapter_meta, query_meta, query, params, opts) + end + + defp valid_prepare?(prepare) when prepare in [:named, :unnamed], do: true + defp valid_prepare?(_), do: false + ## Storage API @impl true From cc5540b89c11e34a9b85cb25874ad5506df9d525 Mon Sep 17 00:00:00 2001 From: Greg Rychlewski Date: Thu, 2 Jan 2025 18:17:12 -0500 Subject: [PATCH 02/12] docs --- lib/ecto/adapters/myxql.ex | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/ecto/adapters/myxql.ex b/lib/ecto/adapters/myxql.ex index 73ae9cde..439da44f 100644 --- a/lib/ecto/adapters/myxql.ex +++ b/lib/ecto/adapters/myxql.ex @@ -10,6 +10,13 @@ defmodule Ecto.Adapters.MyXQL do below. All options can be given via the repository configuration: + config :your_app, YourApp.Repo, + ... + + The `:prepare` option may be specified per operation: + + YourApp.Repo.all(Queryable, prepare: :unnamed) + ### Connection options * `:protocol` - Set to `:socket` for using UNIX domain socket, or `:tcp` for TCP From 930582b33669e10200aaf2cbf25716f036012a18 Mon Sep 17 00:00:00 2001 From: Greg Rychlewski Date: Thu, 2 Jan 2025 18:53:41 -0500 Subject: [PATCH 03/12] improve test --- integration_test/pg/prepare_test.exs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/integration_test/pg/prepare_test.exs b/integration_test/pg/prepare_test.exs index ea502053..256720f4 100644 --- a/integration_test/pg/prepare_test.exs +++ b/integration_test/pg/prepare_test.exs @@ -8,12 +8,18 @@ defmodule Ecto.Integration.PrepareTest do one = TestRepo.insert!(%Post{title: "one"}) two = TestRepo.insert!(%Post{title: "two"}) + stmt_count_query = "SHOW GLOBAL STATUS LIKE '%prepared_stmt_count%'" + # Uncached assert TestRepo.all(Post, prepare: :unnamed) == [one, two] + assert %{rows: [[0]]} = TestRepo.query(stmt_count_query, []) assert TestRepo.all(Post, prepare: :named) == [one, two] + assert %{rows: [[1]]} = TestRepo.query(stmt_count_query, []) # Cached assert TestRepo.all(Post, prepare: :unnamed) == [one, two] + assert %{rows: [[1]]} = TestRepo.query(stmt_count_query, []) assert TestRepo.all(Post, prepare: :named) == [one, two] + assert %{rows: [[1]]} = TestRepo.query(stmt_count_query, []) end end From ec3472abf72f0914ea8aa41c02b10c0ed83148f2 Mon Sep 17 00:00:00 2001 From: Greg Rychlewski Date: Thu, 2 Jan 2025 18:54:51 -0500 Subject: [PATCH 04/12] oops --- integration_test/myxql/prepare_test.exs | 6 ++++++ integration_test/pg/prepare_test.exs | 6 ------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/integration_test/myxql/prepare_test.exs b/integration_test/myxql/prepare_test.exs index ea502053..256720f4 100644 --- a/integration_test/myxql/prepare_test.exs +++ b/integration_test/myxql/prepare_test.exs @@ -8,12 +8,18 @@ defmodule Ecto.Integration.PrepareTest do one = TestRepo.insert!(%Post{title: "one"}) two = TestRepo.insert!(%Post{title: "two"}) + stmt_count_query = "SHOW GLOBAL STATUS LIKE '%prepared_stmt_count%'" + # Uncached assert TestRepo.all(Post, prepare: :unnamed) == [one, two] + assert %{rows: [[0]]} = TestRepo.query(stmt_count_query, []) assert TestRepo.all(Post, prepare: :named) == [one, two] + assert %{rows: [[1]]} = TestRepo.query(stmt_count_query, []) # Cached assert TestRepo.all(Post, prepare: :unnamed) == [one, two] + assert %{rows: [[1]]} = TestRepo.query(stmt_count_query, []) assert TestRepo.all(Post, prepare: :named) == [one, two] + assert %{rows: [[1]]} = TestRepo.query(stmt_count_query, []) end end diff --git a/integration_test/pg/prepare_test.exs b/integration_test/pg/prepare_test.exs index 256720f4..ea502053 100644 --- a/integration_test/pg/prepare_test.exs +++ b/integration_test/pg/prepare_test.exs @@ -8,18 +8,12 @@ defmodule Ecto.Integration.PrepareTest do one = TestRepo.insert!(%Post{title: "one"}) two = TestRepo.insert!(%Post{title: "two"}) - stmt_count_query = "SHOW GLOBAL STATUS LIKE '%prepared_stmt_count%'" - # Uncached assert TestRepo.all(Post, prepare: :unnamed) == [one, two] - assert %{rows: [[0]]} = TestRepo.query(stmt_count_query, []) assert TestRepo.all(Post, prepare: :named) == [one, two] - assert %{rows: [[1]]} = TestRepo.query(stmt_count_query, []) # Cached assert TestRepo.all(Post, prepare: :unnamed) == [one, two] - assert %{rows: [[1]]} = TestRepo.query(stmt_count_query, []) assert TestRepo.all(Post, prepare: :named) == [one, two] - assert %{rows: [[1]]} = TestRepo.query(stmt_count_query, []) end end From 347059c118e21db95cfef816723340db68cce179 Mon Sep 17 00:00:00 2001 From: Greg Rychlewski Date: Thu, 2 Jan 2025 19:01:14 -0500 Subject: [PATCH 05/12] oops --- integration_test/myxql/prepare_test.exs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/integration_test/myxql/prepare_test.exs b/integration_test/myxql/prepare_test.exs index 256720f4..1c98e7f3 100644 --- a/integration_test/myxql/prepare_test.exs +++ b/integration_test/myxql/prepare_test.exs @@ -1,5 +1,5 @@ defmodule Ecto.Integration.PrepareTest do - use Ecto.Integration.Case, async: true + use Ecto.Integration.Case, async: false alias Ecto.Integration.TestRepo alias Ecto.Integration.Post @@ -9,17 +9,22 @@ defmodule Ecto.Integration.PrepareTest do two = TestRepo.insert!(%Post{title: "two"}) stmt_count_query = "SHOW GLOBAL STATUS LIKE '%prepared_stmt_count%'" + assert %{rows: [[orig_count]]} = TestRepo.query(stmt_count_query, []) # Uncached assert TestRepo.all(Post, prepare: :unnamed) == [one, two] - assert %{rows: [[0]]} = TestRepo.query(stmt_count_query, []) + %{rows: [[new_count]]} = TestRepo.query(stmt_count_query, []) + assert new_count == orig_count assert TestRepo.all(Post, prepare: :named) == [one, two] - assert %{rows: [[1]]} = TestRepo.query(stmt_count_query, []) + assert %{rows: [[new_count]]} = TestRepo.query(stmt_count_query, []) + assert new_count == orig_count + 1 # Cached assert TestRepo.all(Post, prepare: :unnamed) == [one, two] - assert %{rows: [[1]]} = TestRepo.query(stmt_count_query, []) + assert %{rows: [[new_count]]} = TestRepo.query(stmt_count_query, []) + assert new_count == orig_count + 1 assert TestRepo.all(Post, prepare: :named) == [one, two] - assert %{rows: [[1]]} = TestRepo.query(stmt_count_query, []) + assert %{rows: [[new_count]]} = TestRepo.query(stmt_count_query, []) + assert new_count == orig_count + 1 end end From 5691acb219e308328ae0b9e56d2ba5cba3adb012 Mon Sep 17 00:00:00 2001 From: Greg Rychlewski Date: Thu, 2 Jan 2025 19:06:14 -0500 Subject: [PATCH 06/12] oops --- integration_test/myxql/prepare_test.exs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/integration_test/myxql/prepare_test.exs b/integration_test/myxql/prepare_test.exs index 1c98e7f3..ef805c4f 100644 --- a/integration_test/myxql/prepare_test.exs +++ b/integration_test/myxql/prepare_test.exs @@ -9,22 +9,22 @@ defmodule Ecto.Integration.PrepareTest do two = TestRepo.insert!(%Post{title: "two"}) stmt_count_query = "SHOW GLOBAL STATUS LIKE '%prepared_stmt_count%'" - assert %{rows: [[orig_count]]} = TestRepo.query(stmt_count_query, []) + assert %{rows: [[orig_count]]} = TestRepo.query!(stmt_count_query, []) # Uncached assert TestRepo.all(Post, prepare: :unnamed) == [one, two] - %{rows: [[new_count]]} = TestRepo.query(stmt_count_query, []) + %{rows: [[new_count]]} = TestRepo.query!(stmt_count_query, []) assert new_count == orig_count assert TestRepo.all(Post, prepare: :named) == [one, two] - assert %{rows: [[new_count]]} = TestRepo.query(stmt_count_query, []) + assert %{rows: [[new_count]]} = TestRepo.query!(stmt_count_query, []) assert new_count == orig_count + 1 # Cached assert TestRepo.all(Post, prepare: :unnamed) == [one, two] - assert %{rows: [[new_count]]} = TestRepo.query(stmt_count_query, []) + assert %{rows: [[new_count]]} = TestRepo.query!(stmt_count_query, []) assert new_count == orig_count + 1 assert TestRepo.all(Post, prepare: :named) == [one, two] - assert %{rows: [[new_count]]} = TestRepo.query(stmt_count_query, []) + assert %{rows: [[new_count]]} = TestRepo.query!(stmt_count_query, []) assert new_count == orig_count + 1 end end From b329d4870cc3d43f85b3de32955b2fd0703e24b3 Mon Sep 17 00:00:00 2001 From: Greg Rychlewski Date: Thu, 2 Jan 2025 19:10:51 -0500 Subject: [PATCH 07/12] oops --- integration_test/myxql/prepare_test.exs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/integration_test/myxql/prepare_test.exs b/integration_test/myxql/prepare_test.exs index ef805c4f..5099b8fc 100644 --- a/integration_test/myxql/prepare_test.exs +++ b/integration_test/myxql/prepare_test.exs @@ -9,22 +9,22 @@ defmodule Ecto.Integration.PrepareTest do two = TestRepo.insert!(%Post{title: "two"}) stmt_count_query = "SHOW GLOBAL STATUS LIKE '%prepared_stmt_count%'" - assert %{rows: [[orig_count]]} = TestRepo.query!(stmt_count_query, []) + assert %{rows: [[_, orig_count]]} = TestRepo.query!(stmt_count_query, []) # Uncached assert TestRepo.all(Post, prepare: :unnamed) == [one, two] - %{rows: [[new_count]]} = TestRepo.query!(stmt_count_query, []) + %{rows: [[_, new_count]]} = TestRepo.query!(stmt_count_query, []) assert new_count == orig_count assert TestRepo.all(Post, prepare: :named) == [one, two] - assert %{rows: [[new_count]]} = TestRepo.query!(stmt_count_query, []) + assert %{rows: [[_, new_count]]} = TestRepo.query!(stmt_count_query, []) assert new_count == orig_count + 1 # Cached assert TestRepo.all(Post, prepare: :unnamed) == [one, two] - assert %{rows: [[new_count]]} = TestRepo.query!(stmt_count_query, []) + assert %{rows: [[_, new_count]]} = TestRepo.query!(stmt_count_query, []) assert new_count == orig_count + 1 assert TestRepo.all(Post, prepare: :named) == [one, two] - assert %{rows: [[new_count]]} = TestRepo.query!(stmt_count_query, []) + assert %{rows: [[_, new_count]]} = TestRepo.query!(stmt_count_query, []) assert new_count == orig_count + 1 end end From c18023305e85676c30289e1da9e9d050516ffb97 Mon Sep 17 00:00:00 2001 From: Greg Rychlewski Date: Thu, 2 Jan 2025 19:15:59 -0500 Subject: [PATCH 08/12] oops --- integration_test/myxql/prepare_test.exs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/integration_test/myxql/prepare_test.exs b/integration_test/myxql/prepare_test.exs index 5099b8fc..500997be 100644 --- a/integration_test/myxql/prepare_test.exs +++ b/integration_test/myxql/prepare_test.exs @@ -10,21 +10,22 @@ defmodule Ecto.Integration.PrepareTest do stmt_count_query = "SHOW GLOBAL STATUS LIKE '%prepared_stmt_count%'" assert %{rows: [[_, orig_count]]} = TestRepo.query!(stmt_count_query, []) + orig_count = String.to_integer(orig_count) # Uncached assert TestRepo.all(Post, prepare: :unnamed) == [one, two] %{rows: [[_, new_count]]} = TestRepo.query!(stmt_count_query, []) - assert new_count == orig_count + assert String.to_integer(new_count) == orig_count assert TestRepo.all(Post, prepare: :named) == [one, two] assert %{rows: [[_, new_count]]} = TestRepo.query!(stmt_count_query, []) - assert new_count == orig_count + 1 + assert String.to_integer(new_count) == orig_count + 1 # Cached assert TestRepo.all(Post, prepare: :unnamed) == [one, two] assert %{rows: [[_, new_count]]} = TestRepo.query!(stmt_count_query, []) - assert new_count == orig_count + 1 + assert String.to_integer(new_count) == orig_count + 1 assert TestRepo.all(Post, prepare: :named) == [one, two] assert %{rows: [[_, new_count]]} = TestRepo.query!(stmt_count_query, []) - assert new_count == orig_count + 1 + assert String.to_integer(new_count) == orig_count + 1 end end From 04d6464221ed44a538a5b8846ea67948da78f7d7 Mon Sep 17 00:00:00 2001 From: Greg Rychlewski Date: Thu, 2 Jan 2025 19:23:20 -0500 Subject: [PATCH 09/12] oops --- integration_test/myxql/prepare_test.exs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/integration_test/myxql/prepare_test.exs b/integration_test/myxql/prepare_test.exs index 500997be..5f9fe10a 100644 --- a/integration_test/myxql/prepare_test.exs +++ b/integration_test/myxql/prepare_test.exs @@ -12,19 +12,23 @@ defmodule Ecto.Integration.PrepareTest do assert %{rows: [[_, orig_count]]} = TestRepo.query!(stmt_count_query, []) orig_count = String.to_integer(orig_count) + query = from p in Post, select: fragment("'mxql test prepare option'") + # Uncached - assert TestRepo.all(Post, prepare: :unnamed) == [one, two] + assert TestRepo.all(query, prepare: :unnamed) == [one, two] %{rows: [[_, new_count]]} = TestRepo.query!(stmt_count_query, []) assert String.to_integer(new_count) == orig_count - assert TestRepo.all(Post, prepare: :named) == [one, two] + + assert TestRepo.all(query, prepare: :named) == [one, two] assert %{rows: [[_, new_count]]} = TestRepo.query!(stmt_count_query, []) assert String.to_integer(new_count) == orig_count + 1 # Cached - assert TestRepo.all(Post, prepare: :unnamed) == [one, two] + assert TestRepo.all(query, prepare: :unnamed) == [one, two] assert %{rows: [[_, new_count]]} = TestRepo.query!(stmt_count_query, []) assert String.to_integer(new_count) == orig_count + 1 - assert TestRepo.all(Post, prepare: :named) == [one, two] + + assert TestRepo.all(query, prepare: :named) == [one, two] assert %{rows: [[_, new_count]]} = TestRepo.query!(stmt_count_query, []) assert String.to_integer(new_count) == orig_count + 1 end From 0441860a72a8259063d03c5908776500e434c25b Mon Sep 17 00:00:00 2001 From: Greg Rychlewski Date: Thu, 2 Jan 2025 19:23:46 -0500 Subject: [PATCH 10/12] oops --- integration_test/myxql/prepare_test.exs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/integration_test/myxql/prepare_test.exs b/integration_test/myxql/prepare_test.exs index 5f9fe10a..98e9e4ac 100644 --- a/integration_test/myxql/prepare_test.exs +++ b/integration_test/myxql/prepare_test.exs @@ -1,6 +1,8 @@ defmodule Ecto.Integration.PrepareTest do use Ecto.Integration.Case, async: false + import Ecto.Query, only: [from: 2] + alias Ecto.Integration.TestRepo alias Ecto.Integration.Post From a501347bb95b34abf52f2cb85192be1e8856efeb Mon Sep 17 00:00:00 2001 From: Greg Rychlewski Date: Thu, 2 Jan 2025 19:27:13 -0500 Subject: [PATCH 11/12] oops --- integration_test/myxql/prepare_test.exs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/integration_test/myxql/prepare_test.exs b/integration_test/myxql/prepare_test.exs index 98e9e4ac..543a04dc 100644 --- a/integration_test/myxql/prepare_test.exs +++ b/integration_test/myxql/prepare_test.exs @@ -7,8 +7,7 @@ defmodule Ecto.Integration.PrepareTest do alias Ecto.Integration.Post test "prepare option" do - one = TestRepo.insert!(%Post{title: "one"}) - two = TestRepo.insert!(%Post{title: "two"}) + TestRepo.insert!(%Post{title: "one"}) stmt_count_query = "SHOW GLOBAL STATUS LIKE '%prepared_stmt_count%'" assert %{rows: [[_, orig_count]]} = TestRepo.query!(stmt_count_query, []) @@ -17,20 +16,20 @@ defmodule Ecto.Integration.PrepareTest do query = from p in Post, select: fragment("'mxql test prepare option'") # Uncached - assert TestRepo.all(query, prepare: :unnamed) == [one, two] + assert TestRepo.all(query, prepare: :unnamed) == ["mxql test prepare option"] %{rows: [[_, new_count]]} = TestRepo.query!(stmt_count_query, []) assert String.to_integer(new_count) == orig_count - assert TestRepo.all(query, prepare: :named) == [one, two] + assert TestRepo.all(query, prepare: :named) == ["mxql test prepare option"] assert %{rows: [[_, new_count]]} = TestRepo.query!(stmt_count_query, []) assert String.to_integer(new_count) == orig_count + 1 # Cached - assert TestRepo.all(query, prepare: :unnamed) == [one, two] + assert TestRepo.all(query, prepare: :unnamed) == ["mxql test prepare option"] assert %{rows: [[_, new_count]]} = TestRepo.query!(stmt_count_query, []) assert String.to_integer(new_count) == orig_count + 1 - assert TestRepo.all(query, prepare: :named) == [one, two] + assert TestRepo.all(query, prepare: :named) == ["mxql test prepare option"] assert %{rows: [[_, new_count]]} = TestRepo.query!(stmt_count_query, []) assert String.to_integer(new_count) == orig_count + 1 end From 30a2d29971e329dcff2594a0345bb1398c916fd1 Mon Sep 17 00:00:00 2001 From: Greg Rychlewski Date: Thu, 2 Jan 2025 19:39:03 -0500 Subject: [PATCH 12/12] small cleanup --- integration_test/myxql/prepare_test.exs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/integration_test/myxql/prepare_test.exs b/integration_test/myxql/prepare_test.exs index 543a04dc..0370f7b3 100644 --- a/integration_test/myxql/prepare_test.exs +++ b/integration_test/myxql/prepare_test.exs @@ -9,11 +9,11 @@ defmodule Ecto.Integration.PrepareTest do test "prepare option" do TestRepo.insert!(%Post{title: "one"}) + query = from p in Post, select: fragment("'mxql test prepare option'") stmt_count_query = "SHOW GLOBAL STATUS LIKE '%prepared_stmt_count%'" - assert %{rows: [[_, orig_count]]} = TestRepo.query!(stmt_count_query, []) - orig_count = String.to_integer(orig_count) - query = from p in Post, select: fragment("'mxql test prepare option'") + %{rows: [[_, orig_count]]} = TestRepo.query!(stmt_count_query, []) + orig_count = String.to_integer(orig_count) # Uncached assert TestRepo.all(query, prepare: :unnamed) == ["mxql test prepare option"]