Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow MyXQL to specify :prepare per operation. #650

Merged
merged 12 commits into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading