Skip to content
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
18 changes: 15 additions & 3 deletions integration_test/myxql/explain_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ defmodule Ecto.Integration.ExplainTest do
explain = TestRepo.explain(:all, from(p in Post, where: p.title == "title"), timeout: 20000)

assert explain =~
"| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |"
"| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |"

assert explain =~ "p0"
assert explain =~ "SIMPLE"
Expand All @@ -24,7 +24,9 @@ defmodule Ecto.Integration.ExplainTest do
end

test "update" do
explain = TestRepo.explain(:update_all, from(p in Post, update: [set: [title: "new title"]]))
explain =
TestRepo.explain(:update_all, from(p in Post, update: [set: [title: "new title"]]))

assert explain =~ "UPDATE"
assert explain =~ "p0"
end
Expand All @@ -37,10 +39,20 @@ defmodule Ecto.Integration.ExplainTest do

test "map format" do
[explain] = TestRepo.explain(:all, Post, format: :map)
keys = explain["query_block"] |> Map.keys
keys = explain["query_block"] |> Map.keys()
assert Enum.member?(keys, "cost_info")
assert Enum.member?(keys, "select_id")
assert Enum.member?(keys, "table")
end

test "explain without rolling back" do
{:ok, {:ok, explain}} =
TestRepo.transaction(fn ->
TestRepo.explain(:delete_all, Post, wrap_in_transaction: false, timeout: 20000)
end)

assert explain =~ "DELETE"
assert explain =~ "p0"
end
end
end
18 changes: 18 additions & 0 deletions integration_test/pg/explain_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,22 @@ defmodule Ecto.Integration.ExplainTest do
assert explain =~ ~r/Node Type:/
assert explain =~ ~r/Relation Name:/
end

test "explain without rolling back" do
TestRepo.insert!(%Post{})
assert [%Post{}] = TestRepo.all(Post)

{:ok, {:ok, explain}} =
TestRepo.transaction(fn ->
TestRepo.explain(:delete_all, Post,
analyze: true,
wrap_in_transaction: false,
timeout: 20000
)
end)

assert explain =~ "Delete on posts p0"
assert explain =~ "cost="
assert TestRepo.all(Post) == []
end
end
18 changes: 16 additions & 2 deletions integration_test/tds/explain_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ defmodule Ecto.Integration.ExplainTest do

describe "explain" do
test "select" do
explain = TestRepo.explain(:all, from(p in Post, where: p.title == "explain_test", limit: 1))
explain =
TestRepo.explain(:all, from(p in Post, where: p.title == "explain_test", limit: 1))

assert explain =~ "| Rows | Executes |"
assert explain =~ "| Parallel | EstimateExecutions |"
assert explain =~ "SELECT TOP(1)"
Expand All @@ -21,7 +23,9 @@ defmodule Ecto.Integration.ExplainTest do
end

test "update" do
explain = TestRepo.explain(:update_all, from(p in Post, update: [set: [title: "new title"]]))
explain =
TestRepo.explain(:update_all, from(p in Post, update: [set: [title: "new title"]]))

assert explain =~ "UPDATE"
assert explain =~ "p0"
assert explain =~ "new title"
Expand All @@ -32,5 +36,15 @@ defmodule Ecto.Integration.ExplainTest do
TestRepo.explain(:all, from(p in "posts", select: p.invalid, where: p.invalid == "title"))
end)
end

test "explain without rolling back" do
{:ok, {:ok, explain}} =
TestRepo.transaction(fn ->
TestRepo.explain(:delete_all, Post, wrap_in_transaction: false, timeout: 20000)
end)

assert explain =~ "DELETE"
assert explain =~ "p0"
end
end
end
18 changes: 14 additions & 4 deletions lib/ecto/adapters/sql.ex
Original file line number Diff line number Diff line change
Expand Up @@ -434,8 +434,8 @@ defmodule Ecto.Adapters.SQL do

Adapter | Supported opts
---------------- | --------------
Postgrex | `analyze`, `verbose`, `costs`, `settings`, `buffers`, `timing`, `summary`, `format`, `plan`
MyXQL | `format`
Postgrex | `analyze`, `verbose`, `costs`, `settings`, `buffers`, `timing`, `summary`, `format`, `plan`, `wrap_in_transaction`
MyXQL | `format`, `wrap_in_transaction`

All options except `format` are boolean valued and default to `false`.

Expand All @@ -447,6 +447,10 @@ defmodule Ecto.Adapters.SQL do
* Postgrex: `:map`, `:yaml` and `:text`
* MyXQL: `:map` and `:text`

The `wrap_in_transaction` option is a boolean that controls whether the command is run inside of a
transaction that is rolled back. This is useful when, for example, you'd like to use `analyze: true`
on an update or delete query without modifying data. Defaults to `true`.

The `:plan` option in Postgrex can take the values `:custom` or `:fallback_generic`. When `:custom`
is specified, the explain plan generated will consider the specific values of the query parameters
that are supplied. When using `:fallback_generic`, the specific values of the query parameters will
Expand Down Expand Up @@ -508,10 +512,11 @@ defmodule Ecto.Adapters.SQL do
def explain(repo, operation, queryable, opts \\ [])

def explain(repo, operation, queryable, opts) when is_atom(repo) or is_pid(repo) do
explain(Ecto.Adapter.lookup_meta(repo), operation, queryable, opts)
wrap_in_transaction? = Keyword.get(opts, :wrap_in_transaction, true)
explain(Ecto.Adapter.lookup_meta(repo), operation, queryable, wrap_in_transaction?, opts)
end

def explain(%{repo: repo} = adapter_meta, operation, queryable, opts) do
def explain(%{repo: repo} = adapter_meta, operation, queryable, true, opts) do
Ecto.Multi.new()
|> Ecto.Multi.run(:explain, fn _, _ ->
{prepared, prepared_params} = to_sql(operation, repo, queryable)
Expand All @@ -528,6 +533,11 @@ defmodule Ecto.Adapters.SQL do
end
end

def explain(%{repo: repo} = adapter_meta, operation, queryable, false, opts) do
{prepared, prepared_params} = to_sql(operation, repo, queryable)
sql_call(adapter_meta, :explain_query, [prepared], prepared_params, opts)
end

@doc @disconnect_all_doc
@spec disconnect_all(
pid | Ecto.Repo.t() | Ecto.Adapter.adapter_meta(),
Expand Down
Loading