diff --git a/integration_test/myxql/prepare_test.exs b/integration_test/myxql/prepare_test.exs new file mode 100644 index 00000000..0370f7b3 --- /dev/null +++ b/integration_test/myxql/prepare_test.exs @@ -0,0 +1,36 @@ +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 + + 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%'" + + %{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"] + %{rows: [[_, new_count]]} = TestRepo.query!(stmt_count_query, []) + assert String.to_integer(new_count) == orig_count + + 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) == ["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) == ["mxql test prepare option"] + assert %{rows: [[_, new_count]]} = TestRepo.query!(stmt_count_query, []) + assert String.to_integer(new_count) == orig_count + 1 + end +end diff --git a/lib/ecto/adapters/myxql.ex b/lib/ecto/adapters/myxql.ex index 1c589aeb..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 @@ -147,6 +154,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 +180,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