Skip to content
Open
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
9 changes: 7 additions & 2 deletions lib/postgrex/protocol.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3069,13 +3069,18 @@ defmodule Postgrex.Protocol do
end

defp error_ready(s, status, %Postgrex.Error{} = err, buffer) do
%{connection_id: connection_id} = s

case recv_ready(s, status, buffer) do
{:ok, s} ->
%{connection_id: connection_id} = s
{:error, %{err | connection_id: connection_id}, s}

{:disconnect, _, _} = disconnect ->
disconnect
if err.postgres.severity in ["FATAL", "PANIC"] do
{:disconnect, %{err | connection_id: connection_id}, %{s | buffer: buffer}}
else
disconnect
end
Comment on lines 3071 to +3083
Copy link
Member Author

@v0idpwn v0idpwn Mar 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My original implementation just skipped the recv_ready, but I think this would be more correct.

end
end

Expand Down
30 changes: 30 additions & 0 deletions test/query_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -1940,6 +1940,36 @@ defmodule QueryTest do
end) =~ "** (Postgrex.Error) FATAL 57P01 (admin_shutdown)"
end

test "terminate backend during query returns FATAL error", context do
assert {:ok, pid} = P.start_link([idle_interval: 10] ++ context[:options])

%Postgrex.Result{connection_id: connection_id} = Postgrex.query!(pid, "SELECT 42", [])

task =
Task.async(fn ->
receive do
:go -> :ok
end

Postgrex.query(pid, "SELECT pg_sleep(10)", [])
end)

:erlang.trace(task.pid, true, [:call])
:erlang.trace_pattern({Postgrex.Protocol, :recv_bind, :_}, [], [:local])

send(task.pid, :go)

assert_receive {:trace, _, :call, {Postgrex.Protocol, :recv_bind, _}}, 200

assert [[true]] = query("SELECT pg_terminate_backend($1)", [connection_id])

assert {:error, %Postgrex.Error{postgres: %{code: :admin_shutdown, severity: "FATAL"}}} =
Task.await(task, 5000)
after
:erlang.trace_pattern({Postgrex.Protocol, :recv_bind, :_}, false, [])
:erlang.trace(:all, false, [:call])
end

test "terminate backend with socket", context do
Process.flag(:trap_exit, true)
socket = System.get_env("PG_SOCKET_DIR") || "/tmp"
Expand Down
Loading