Skip to content

Commit fc6c7a5

Browse files
authored
Add format option for PostgreSQL explain (#295)
1 parent e28a769 commit fc6c7a5

3 files changed

Lines changed: 45 additions & 4 deletions

File tree

integration_test/pg/explain_test.exs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,27 @@ defmodule Ecto.Integration.ExplainTest do
2626
TestRepo.explain(:all, Post, analyze: "1")
2727
end)
2828
end
29+
30+
test "explain JSON format" do
31+
[explain] = TestRepo.explain(:all, Post, analyze: true, verbose: true, timeout: 20000, format: :json) |> Jason.decode!()
32+
keys = explain["Plan"] |> Map.keys
33+
assert Enum.member?(keys, "Actual Loops")
34+
assert Enum.member?(keys, "Actual Rows")
35+
assert Enum.member?(keys, "Actual Startup Time")
36+
end
37+
38+
test "explain MAP format" do
39+
[explain] = TestRepo.explain(:all, Post, analyze: true, verbose: true, timeout: 20000, format: :map)
40+
keys = explain["Plan"] |> Map.keys
41+
assert Enum.member?(keys, "Actual Loops")
42+
assert Enum.member?(keys, "Actual Rows")
43+
assert Enum.member?(keys, "Actual Startup Time")
44+
end
45+
46+
test "explain YAML format" do
47+
explain = TestRepo.explain(:all, Post, analyze: true, verbose: true, timeout: 20000, format: :yaml)
48+
assert explain =~ ~r/Plan:/
49+
assert explain =~ ~r/Node Type:/
50+
assert explain =~ ~r/Relation Name:/
51+
end
2952
end

lib/ecto/adapters/postgres/connection.ex

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,10 +253,21 @@ if Code.ensure_loaded?(Postgrex) do
253253
@impl true
254254
def explain_query(conn, query, params, opts) do
255255
{explain_opts, opts} =
256-
Keyword.split(opts, ~w[analyze verbose costs settings buffers timing summary]a)
256+
Keyword.split(opts, ~w[analyze verbose costs settings buffers timing summary format]a)
257+
258+
json_format? = {:format, :json} in explain_opts
259+
map_format? = {:format, :map} in explain_opts
257260

258261
case query(conn, build_explain_query(query, explain_opts), params, opts) do
259-
{:ok, %Postgrex.Result{rows: rows}} -> {:ok, Enum.map_join(rows, "\n", & &1)}
262+
{:ok, %Postgrex.Result{rows: rows}} when json_format? ->
263+
case Jason.encode(List.flatten(rows)) do
264+
{:ok, json} -> {:ok, json}
265+
{:error, error} -> error
266+
end
267+
{:ok, %Postgrex.Result{rows: rows}} when map_format? ->
268+
{:ok, List.flatten(rows)}
269+
{:ok, %Postgrex.Result{rows: rows}} ->
270+
{:ok, Enum.map_join(rows, "\n", & &1)}
260271
error -> error
261272
end
262273
end
@@ -289,6 +300,9 @@ if Code.ensure_loaded?(Postgrex) do
289300
{_, nil}, acc ->
290301
acc
291302

303+
{:format, value}, acc ->
304+
[String.upcase("#{format_to_sql(value)}") | acc]
305+
292306
{opt, value}, acc ->
293307
[String.upcase("#{opt} #{quote_boolean(value)}") | acc]
294308
end)
@@ -1229,6 +1243,11 @@ if Code.ensure_loaded?(Postgrex) do
12291243
defp quote_boolean(false), do: "FALSE"
12301244
defp quote_boolean(value), do: error!(nil, "bad boolean value #{value}")
12311245

1246+
defp format_to_sql(:text), do: "FORMAT TEXT"
1247+
defp format_to_sql(:json), do: "FORMAT JSON"
1248+
defp format_to_sql(:map), do: "FORMAT JSON"
1249+
defp format_to_sql(:yaml), do: "FORMAT YAML"
1250+
12321251
defp single_quote(value), do: [?', escape_string(value), ?']
12331252

12341253
defp intersperse_map(list, separator, mapper, acc \\ [])

lib/ecto/adapters/sql.ex

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,8 +281,7 @@ defmodule Ecto.Adapters.SQL do
281281
282282
Also note that:
283283
284-
* `FORMAT` isn't supported at the moment and the only possible output
285-
is a textual format, so you may want to call `IO.puts/1` to display it;
284+
* Currently `:json`, `:map`, `:yaml` and `:text` format options are supported for PostgreSQL
286285
* Any other value passed to `opts` will be forwarded to the underlying
287286
adapter query function, including Repo shared options such as `:timeout`;
288287
* Non built-in adapters may have specific behavior and you should consult

0 commit comments

Comments
 (0)