| From: | torikoshia <torikoshia(at)oss(dot)nttdata(dot)com> |
|---|---|
| To: | Lukas Fittl <lukas(at)fittl(dot)com> |
| Cc: | pgsql-hackers(at)lists(dot)postgresql(dot)org, Atsushi Torikoshi <torikoshia(dot)tech(at)gmail(dot)com>, samimseih(at)gmail(dot)com, destrex271(at)gmail(dot)com, robertmhaas(at)gmail(dot)com |
| Subject: | Re: RFC: Logging plan of the running query |
| Date: | 2026-06-15 13:26:32 |
| Message-ID: | 4372fb202217256483a015f662d2b474@oss.nttdata.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
On 2026-06-08 15:08, Lukas Fittl wrote:
Thanks for your review and update again.
> Hi,
>
> On Mon, Mar 16, 2026 at 8:14 AM torikoshia <torikoshia(at)oss(dot)nttdata(dot)com>
> wrote:
>>
>> Rebased the patch.
>
> I was reminded about this patch at PG DATA in Chicago last week, and
> figured I'd see if I can contribute a review / help move this forward
> in PG20.
>
> First of all, find attached a v54 that is rebased over changes late in
> the PG19 cycle, with other minimal fixes:
>
> - Add a missing call to explain_per_plan_hook in ExplainStringAssemble
> (this hook was newly added after your last patch version)
> - The TAP test number (11) was overlapping with an existing test
> (011_lock_stats) that was recently added, renumbered to
> 014_pg_log_query_plan.pl
> - Add missing reference for the TAP test in Meson builds
> - Adjust auto_explain.c to not include "dynamic_explain.h" (its not
> needed, ExplainStringAssemble is defined in explain.h which is already
> included)
> - pgindent run to fix minor whitespace issues in dynamic_explain.c
Agreed on those changes.
> I've also read through the other thread re: progressive explain, and
> it seems like this infrastructure here to log the current plan
> (without ANALYZE) would be required in any case (and I think the
> overall approach makes sense), and then separately we can solve the
> progressive explain's parallel worker and other design issues around
> execution statistics.
>
> In terms of code review:
>
>> diff --git a/src/backend/access/transam/xact.c
>> b/src/backend/access/transam/xact.c
>> index aafc53e0164..09e51b3d75a 100644
>> --- a/src/backend/access/transam/xact.c
>> +++ b/src/backend/access/transam/xact.c
>> @@ -37,6 +37,7 @@
>> #include "catalog/pg_enum.h"
>> #include "catalog/storage.h"
>> #include "commands/async.h"
>> +#include "commands/dynamic_explain.h"
>> #include "commands/tablecmds.h"
>> #include "commands/trigger.h"
>> #include "common/pg_prng.h"
>> @@ -217,6 +218,7 @@ typedef struct TransactionStateData
>> bool parallelChildXact; /* is any parent transaction
>> parallel? */
>> bool chain; /* start a new block after this one
>> */
>> bool topXidLogged; /* for a subxact: is top-level XID
>> logged? */
>> + QueryDesc *queryDesc; /* my current QueryDesc */
>> struct TransactionStateData *parent; /* back link to parent */
>> } TransactionStateData;
>
> Somehow putting the current QueryDesc into TransactionStateData seemed
> a bit odd to me, and I briefly experimented with putting this into the
> active portal instead - but that doesn't work with subtransactions
> that don't have their own portal, which presumably is how you landed
> on this design. So I think this makes sense as-is, given those
> challenges.
>
>> @@ -2953,6 +2977,9 @@ AbortTransaction(void)
>> /* Reset snapshot export state. */
>> SnapBuildResetExportedSnapshotState();
>>
>> + /* Reset current query plan state used for logging. */
>> + SetCurrentQueryDesc(NULL);
>> +
>> /*
>> * If this xact has started any unfinished parallel operation,
>> clean up
>> * its workers and exit parallel mode. Don't warn about leaked
>> resources.
>> @@ -5352,6 +5379,13 @@ AbortSubTransaction(void)
>> /* Reset logical streaming state. */
>> ResetLogicalStreamingState();
>>
>> + /*
>> + * Reset current query plan state used for logging. Note that
>> even after
>> + * this reset, it's still possible to obtain the parent
>> transaction's
>> + * query plans, since they are preserved in
>> standard_ExecutorRun().
>> + */
>> + SetCurrentQueryDesc(NULL);
>> +
>> /*
>> * No need for SnapBuildResetExportedSnapshotState() here,
>> snapshot
>> * exports are not supported in subtransactions.
>
> It seems better to me if we avoided overly specific code comments in
> xact.c that talk about plan logging behavior, i.e. look at this more
> as a general purpose "what's the current queryDesc for the current
> transaction" facility.
Removed specific comments about plan logging.
>> diff --git a/src/backend/executor/execProcnode.c
>> b/src/backend/executor/execProcnode.c
>> index 7e40b852517..96cb22daf80 100644
>> --- a/src/backend/executor/execProcnode.c
>> +++ b/src/backend/executor/execProcnode.c
>> @@ -441,27 +443,43 @@ ExecSetExecProcNode(PlanState *node,
>> ExecProcNodeMtd function)
>> ...
>> static TupleTableSlot *
>> ExecProcNodeFirst(PlanState *node)
>> {
>> ...
>> + if (LogQueryPlanPending)
>> + LogQueryPlan();
>> +
>> /*
>> * If instrumentation is required, change the wrapper to one that
>> just
>> * does instrumentation. Otherwise we can dispense with all
>> wrappers and
>> * have ExecProcNode() directly call the relevant function from
>> now on.
>> */
>> - if (node->instrument)
>> + else if (node->instrument)
>> node->ExecProcNode = ExecProcNodeInstr;
>> else
>> node->ExecProcNode = node->ExecProcNodeReal;
>
> I'm not sure if this was discussed earlier already, but I find the
> flow here a bit hard to follow in regards to how we execute the actual
> node function again.
>
> Specifically, since this uses "else if" we don't touch ExecProcNode
> when LogQueryPlanPending = true, and since node->ExecProcNode still
> points to ExecProcNodeFirst, the function basically calls itself. On
> the second go around we assume that LogQueryPlanPending is no longer
> set, so we get back to the intended ExecProcNode *if*
> LogQueryPlanPending was set to false (which it is in LogQueryPlan, but
> that's not easy to see from just that function).
>
> Why not do it like this:
>
> if (LogQueryPlanPending)
> LogQueryPlan();
>
> /* Unmodified existing code */
> if (node->instrument)
> node->ExecProcNode = ExecProcNodeInstr;
> else
> node->ExecProcNode = node->ExecProcNodeReal;
>
Make sense. Modified it.
I also fixed some typos in the attached patch.
Thanks,
--
Atsushi Torikoshi
Seconded from NTT DATA CORPORATION to SRA OSS K.K.
| Attachment | Content-Type | Size |
|---|---|---|
| v55-0001-Add-function-to-log-the-plan-of-the-currently-ru.patch | text/x-diff | 38.2 KB |
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Bertrand Drouvot | 2026-06-15 13:40:17 | Allow wal_log_hints to be changed without restart |
| Previous Message | Tatsuya Kawata | 2026-06-15 13:12:08 | Re: [PATCH] Change wait_time column of pg_stat_lock to double precision |