Skip to content

Commit

Permalink
Allow MyXQL to specify :prepare per operation. (#650)
Browse files Browse the repository at this point in the history
  • Loading branch information
greg-rychlewski authored Jan 3, 2025
1 parent 6345716 commit 802914d
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 0 deletions.
36 changes: 36 additions & 0 deletions integration_test/myxql/prepare_test.exs
Original file line number Diff line number Diff line change
@@ -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
26 changes: 26 additions & 0 deletions lib/ecto/adapters/myxql.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down

0 comments on commit 802914d

Please sign in to comment.