File postgrex-0.13.5-git.patch of Package postgrex
diff --git a/lib/postgrex.ex b/lib/postgrex.ex
index c739950..4a18648 100644
--- a/lib/postgrex.ex
+++ b/lib/postgrex.ex
@@ -62,8 +62,10 @@ defmodule Postgrex do
options, this option must be included with all requests contacting the pool
if not `DBConnection.Connection` (default: `DBConnection.Connection`);
* `:types` - The types module to use, see `Postgrex.TypeModule`, this
- option is only required when using custom encoding or decoding (default:
- `Postgrex.DefaultTypes`);
+ option is only required when using custom encoding or decoding (default:
+ `Postgrex.DefaultTypes`);
+ * `:disconnect_on_error_codes` - List of error code atoms that when encountered
+ will disconnect the connection (default: `[]`);
`Postgrex` uses the `DBConnection` framework and supports all `DBConnection`
options like `:idle`, `:after_connect` etc.
diff --git a/lib/postgrex/protocol.ex b/lib/postgrex/protocol.ex
index b56bf9a..9c6c29f 100644
--- a/lib/postgrex/protocol.ex
+++ b/lib/postgrex/protocol.ex
@@ -20,7 +20,7 @@ defmodule Postgrex.Protocol do
defstruct [sock: nil, connection_id: nil, connection_key: nil, peer: nil,
types: nil, null: nil, timeout: nil, parameters: %{}, queries: nil,
- postgres: :idle, transactions: :naive, buffer: nil]
+ postgres: :idle, transactions: :naive, buffer: nil, disconnect_on_error_codes: []]
@type state :: %__MODULE__{sock: {module, any},
connection_id: nil | pos_integer,
@@ -33,7 +33,8 @@ defmodule Postgrex.Protocol do
queries: nil | :ets.tid,
postgres: :idle | :transaction | :failed,
transactions: :strict | :naive,
- buffer: nil | binary | :active_once}
+ buffer: nil | binary | :active_once,
+ disconnect_on_error_codes: [atom()]}
@type notify :: ((binary, binary) -> any)
@reserved_prefix "POSTGREX_"
@@ -92,8 +93,14 @@ defmodule Postgrex.Protocol do
:unnamed -> :unnamed
end
- s = %__MODULE__{timeout: timeout, postgres: :idle,
- transactions: transactions}
+ disconnect_on_error_codes = opts[:disconnect_on_error_codes] || []
+
+ s = %__MODULE__{
+ timeout: timeout,
+ postgres: :idle,
+ transactions: transactions,
+ disconnect_on_error_codes: disconnect_on_error_codes
+ }
types_key = if types_mod, do: {host, port, Keyword.fetch!(opts, :database)}
status = %{opts: opts, types_mod: types_mod, types_key: types_key,
@@ -218,7 +225,7 @@ defmodule Postgrex.Protocol do
execute when is_function(execute, 4) ->
%{buffer: buffer} = s
s = %{s | buffer: nil}
- execute.(s, status, params, buffer)
+ execute.(s, status, params, buffer) |> maybe_disconnect()
{kind, _, _} = error when kind in [:error, :disconnect] ->
error
end
@@ -1187,6 +1194,7 @@ defmodule Postgrex.Protocol do
lock_error(s, :bind, query)
end
end
+
defp bind(s, _, %Query{name: @reserved_prefix <> _} = query, _, _) do
reserved_error(query, s)
end
@@ -1262,6 +1270,21 @@ defmodule Postgrex.Protocol do
end
end
+ defp maybe_disconnect({:error, _, %{disconnect_on_error_codes: []}} = result), do: result
+
+ defp maybe_disconnect({:error,
+ %Postgrex.Error{postgres: %{code: code}} = error,
+ %{disconnect_on_error_codes: codes} = state
+ } = result) do
+ if code in codes do
+ {:disconnect, error, state}
+ else
+ result
+ end
+ end
+
+ defp maybe_disconnect(other), do: other
+
defp savepoint_msgs(s, :sync, msgs) do
savepoint = transaction_msgs(s, ["SAVEPOINT postgrex_query"])
release = transaction_msgs(s, ["RELEASE SAVEPOINT postgrex_query", :sync])
diff --git a/mix.lock b/mix.lock
index d3f4988..dd8b540 100644
--- a/mix.lock
+++ b/mix.lock
@@ -2,6 +2,9 @@
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm"},
"db_connection": {:hex, :db_connection, "1.1.0", "b2b88db6d7d12f99997b584d09fad98e560b817a20dab6a526830e339f54cdb3", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
"decimal": {:hex, :decimal, "1.3.1", "157b3cedb2bfcb5359372a7766dd7a41091ad34578296e951f58a946fcab49c6", [:mix], [], "hexpm"},
- "earmark": {:hex, :earmark, "1.2.4", "99b637c62a4d65a20a9fb674b8cffb8baa771c04605a80c911c4418c69b75439", [:mix], [], "hexpm"},
- "ex_doc": {:hex, :ex_doc, "0.18.1", "37c69d2ef62f24928c1f4fdc7c724ea04aecfdf500c4329185f8e3649c915baf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"},
+ "earmark": {:hex, :earmark, "1.2.6", "b6da42b3831458d3ecc57314dff3051b080b9b2be88c2e5aa41cd642a5b044ed", [:mix], [], "hexpm"},
+ "ex_doc": {:hex, :ex_doc, "0.19.1", "519bb9c19526ca51d326c060cb1778d4a9056b190086a8c6c115828eaccea6cf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.7", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"},
+ "makeup": {:hex, :makeup, "0.5.5", "9e08dfc45280c5684d771ad58159f718a7b5788596099bdfb0284597d368a882", [:mix], [{:nimble_parsec, "~> 0.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},
+ "makeup_elixir": {:hex, :makeup_elixir, "0.10.0", "0f09c2ddf352887a956d84f8f7e702111122ca32fbbc84c2f0569b8b65cbf7fa", [:mix], [{:makeup, "~> 0.5.5", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"},
+ "nimble_parsec": {:hex, :nimble_parsec, "0.4.0", "ee261bb53214943679422be70f1658fff573c5d0b0a1ecd0f18738944f818efe", [:mix], [], "hexpm"},
}
diff --git a/test/transaction_test.exs b/test/transaction_test.exs
index 2dff0fc..0007a6b 100644
--- a/test/transaction_test.exs
+++ b/test/transaction_test.exs
@@ -10,8 +10,16 @@ defmodule TransactionTest do
:transaction -> :strict
:savepoint -> :naive
end
- opts = [ database: "postgrex_test", transactions: transactions, idle: :active,
- backoff_type: :stop, prepare: context[:prepare] || :named]
+
+ opts = [
+ database: "postgrex_test",
+ transactions: transactions,
+ idle: :active,
+ backoff_type: :stop,
+ prepare: context[:prepare] || :named,
+ disconnect_on_error_codes: context[:disconnect_on_error_codes] || []
+ ]
+
{:ok, pid} = P.start_link(opts)
{:ok, [pid: pid]}
end
@@ -123,6 +131,44 @@ defmodule TransactionTest do
assert query("SELECT 42", []) == [[42]]
end
+ @tag mode: :transaction
+ @tag disconnect_on_error_codes: [:read_only_sql_transaction]
+ test "transaction read-only only error disconnects with prepare and execute", context do
+ Process.flag(:trap_exit, true)
+
+ assert transaction(fn conn ->
+ P.query!(conn, "SET TRANSACTION READ ONLY", []).connection_id
+
+ {:ok, query} = P.prepare(conn, "query_1", "insert into uniques values (1);", [])
+
+ assert capture_log(fn ->
+ {:error, %Postgrex.Error{postgres: %{code: :read_only_sql_transaction}}} =
+ P.execute(conn, query, [])
+
+ pid = context[:pid]
+ assert_receive {:EXIT, ^pid, {:shutdown, _}}
+ end) =~ "disconnected: ** (Postgrex.Error) ERROR 25006 (read_only_sql_transaction)"
+ end)
+ end
+
+ @tag mode: :transaction
+ @tag disconnect_on_error_codes: [:read_only_sql_transaction]
+ test "transaction read-only only error disconnects with prepare, execute, and close", context do
+ Process.flag(:trap_exit, true)
+
+ assert transaction(fn conn ->
+ P.query!(conn, "SET TRANSACTION READ ONLY", []).connection_id
+
+ assert capture_log(fn ->
+ {:error, %Postgrex.Error{postgres: %{code: :read_only_sql_transaction}}} =
+ P.query(conn, "insert into uniques values (1);", [])
+
+ pid = context[:pid]
+ assert_receive {:EXIT, ^pid, {:shutdown, _}}
+ end) =~ "disconnected: ** (Postgrex.Error) ERROR 25006 (read_only_sql_transaction)"
+ end)
+ end
+
@tag mode: :savepoint
test "savepoint transaction releases savepoint", context do
:ok = query("BEGIN", [])