On 06/07/2026 08:21, torikoshia wrote:
> On Wed, Jul 1, 2026 at 7:42 PM Andrei Lepikhov <[email protected]> wrote:
>
>> I’d
>> propose introducing a tiny hook (register callback?) into ExecProcNode() and
>> just setting it in ProcessLogQueryPlanInterrupt.
>
> This approach would also be possible, but we are concerned that
> checking the hook on every ExecProcNode() call would add too much
> overhead and could have a significant performance impact.
I doubt the performance impact, but generally agree: having
ExecSetExecProcNode/ExecProcNodeFirst trick with a one-time callback inside
(right now it is the only hard-wired LogQueryPlan call) may be enough for the
implementation.
So, the extendable part here may be an ExecProcNodeFirst callback that an
extension module can register once (for example, before the start of an EXPLAIN
ANALYZE) or re-arm on each call of such 'execution state probes'.
>> Maybe the next call to ExecProcNode() will be made
>> from an external query (or from a deeper one).
>> The current implementation will
>> not produce any EXPLAIN in this case, but query still executes.
>
> That said, I think a similar situation can still occur if execution
> moves to an external or deeper queryDesc after the individual plan
> nodes have been wrapped. In that case, the plan does not be logged
> even though the query continues executing.
That's the problem. It makes tests unstable as well as user experience
unpredictable. How to script (automatise) this feature if you have no
guarantees? Is anywhere in Postgres any other examples of such behaviour? To
close the gap, ExecutorStart routine could check a global 'pending report' flag
and wrap planstate nodes if needed.
Also, there are kinds of query feedback that might be, reporting the ‘pending
reports’ like queryid to the pg_stat_activity. That solves the automation
problem for the async feature.
In addition, I wonder why this code doesn't use standard planstate walker. It
seems to me that something like the following should work:
static bool
rearm_execprocnode_walker(PlanState *node, void *context)
{
if (node == NULL)
return false;
ExecSetExecProcNode(node, node->ExecProcNodeReal);
return planstate_tree_walker(node, rearm_execprocnode_walker, context);
}
/* trigger point */
(void) rearm_execprocnode_walker(queryDesc->planstate, NULL);
--
regards, Andrei Lepikhov,
pgEdge