BUG #19877: auto_explain omits a sampled cursor plan after sample_rate changes

From: PG Bug reporting form <noreply(at)postgresql(dot)org>
To: pgsql-bugs(at)lists(dot)postgresql(dot)org
Cc: idrissaajalloh(at)gmail(dot)com
Subject: BUG #19877: auto_explain omits a sampled cursor plan after sample_rate changes
Date: 2026-11-11 12:33:09
Message-ID: 19877-d045e13baa7e9857@postgresql.org
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-bugs

The following bug has been logged on the website:

Bug reference: 19877
Logged by: Wren
Email address: idrissaajalloh(at)gmail(dot)com
PostgreSQL version: 18.6
Operating system: Linux/amd64
Description:

A cursor started while auto_explain.sample_rate is 1 does not emit its plan
if another top-level statement runs with sample_rate set to 0 before the
cursor ends. The cursor was selected and instrumented when it started, so
the later statement should not change whether its plan is emitted at
ExecutorEnd.

How to repeat:

Save the following as case.sql and run it with `psql -X -f case.sql`:

```sql
\set ON_ERROR_STOP on

LOAD 'auto_explain';
SET client_min_messages = notice;
SET auto_explain.log_level = notice;
SET auto_explain.log_min_duration = 0;
SET auto_explain.log_analyze = off;

-- Control: a cursor selected at sample_rate=1 is logged when it ends.
SET auto_explain.sample_rate = 1;
BEGIN;
DECLARE control_cursor CURSOR FOR
SELECT 'control_marker'::text;
FETCH ALL FROM control_cursor;
COMMIT;

-- This cursor is also selected and instrumented at start. A later top-level
-- statement changes auto_explain's backend-global sampling state.
SET auto_explain.sample_rate = 1;
BEGIN;
DECLARE victim_cursor CURSOR FOR
SELECT 'victim_marker'::text;
FETCH ALL FROM victim_cursor;
SET auto_explain.sample_rate = 0;
SELECT 'resampler_marker'::text;
COMMIT;

RESET auto_explain.sample_rate;
RESET auto_explain.log_analyze;
RESET auto_explain.log_min_duration;
RESET auto_explain.log_level;
RESET client_min_messages;
```

The recorded stdout confirms that both cursors ran and returned their rows:

```
BEGIN
DECLARE CURSOR
text
----------------
control_marker
(1 row)

COMMIT
SET
BEGIN
DECLARE CURSOR
text
---------------
victim_marker
(1 row)

SET
text
------------------
resampler_marker
(1 row)

COMMIT
```

However, stderr contains a plan only for the control cursor:

```
psql:case.sql:15: NOTICE: duration: 0.004 ms plan:
Query Text: DECLARE control_cursor CURSOR FOR
SELECT 'control_marker'::text;
Result (cost=0.00..0.01 rows=1 width=32)
```

There is no corresponding NOTICE for victim_cursor. Since both cursors
started with sample_rate=1 and log_min_duration=0, both should emit a plan.
The control cursor also confirms that NOTICE-level auto_explain output is
reaching the client.

This was reproduced on PostgreSQL 18.6 on Linux/amd64 using the packaged
auto_explain module. The evidence suggests that the sampling decision is
kept in backend-global state and consulted again at ExecutorEnd, allowing
the intervening statement to overwrite the decision made for the still-open
cursor. This occurs when auto_explain sampling is changed by a top-level
statement while an instrumented cursor remains open.

Browse pgsql-bugs by date

  From Date Subject
Next Message Dean Rasheed 2026-11-11 12:33:45 Re: BUG #19802: MERGE into a partitioned table fails with "SubPlan found with no parent plan"
Previous Message PG Bug reporting form 2026-11-11 12:16:24 BUG #19876: rhel-9.9-x86_64 yum repository for Postgres 16 is missing repodata