Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
6 changes: 6 additions & 0 deletions lib/postgrex/protocol.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3068,6 +3068,12 @@ defmodule Postgrex.Protocol do
end
end

defp error_ready(s, _status, %Postgrex.Error{postgres: %{severity: severity}} = err, buffer)
when severity in ["FATAL", "PANIC"] do
%{connection_id: connection_id} = s
{:disconnect, %{err | connection_id: connection_id}, %{s | buffer: buffer}}
end

defp error_ready(s, status, %Postgrex.Error{} = err, buffer) do
Comment thread
v0idpwn marked this conversation as resolved.
Outdated
case recv_ready(s, status, buffer) do
{:ok, s} ->
Expand Down
20 changes: 20 additions & 0 deletions test/query_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -1940,6 +1940,26 @@ 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", [])

# Start a long-running query in a separate process so we can terminate the
# backend while it's executing.
task =
Task.async(fn ->
Postgrex.query(pid, "SELECT pg_sleep(10)", [])
end)

Process.sleep(100)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Instead of Process.sleep, you could setup a tracer and watch for Postgrex.query being returned. It should be deterministic. Would you like to give it a try?

@v0idpwn v0idpwn Mar 18, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I pushed a commit using tracer to detect when the query was sent via tracing the recv_bind, but I'm still not really content with how it reads.


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

assert {:error, %Postgrex.Error{postgres: %{code: :admin_shutdown, severity: "FATAL"}}} =
Task.await(task, 5000)
end

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