Skip to content
Closed
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
39 changes: 33 additions & 6 deletions pg_track_optimizer.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "commands/explain_state.h"
#endif
#include "executor/executor.h"
#include "executor/instrument.h"
#include "funcapi.h"
#include "lib/dshash.h"
#include "miscadmin.h"
Expand Down Expand Up @@ -272,7 +273,20 @@ explain_ExecutorStart(QueryDesc *queryDesc, int eflags)
track_attach_shmem();

if (track_optimizer_enabled(queryDesc, eflags))
{
queryDesc->instrument_options |= INSTRUMENT_TIMER | INSTRUMENT_ROWS | INSTRUMENT_BUFFERS;
#if PG_VERSION_NUM >= 190000
/*
* Ask core to set up query-level instrumentation for us. This MUST
* be done before standard_ExecutorStart: core inspects
* query_instr_options there and allocates query_instr only if it is
* non-zero (see standard_ExecutorStart in execMain.c). Setting the
* options later has no effect, and plan_error() needs both timing
* and buffer usage from query_instr.
*/
queryDesc->query_instr_options |= INSTRUMENT_TIMER | INSTRUMENT_BUFFERS;
#endif
}

if (prev_ExecutorStart)
prev_ExecutorStart(queryDesc, eflags);
Expand All @@ -282,10 +296,10 @@ explain_ExecutorStart(QueryDesc *queryDesc, int eflags)
if (!track_optimizer_enabled(queryDesc, eflags))
return;

#if PG_VERSION_NUM < 190000
/*
* Set up to track total elapsed time in ExecutorRun. Make sure the
* space is allocated in the per-query context so it will go away at
* ExecutorEnd.
* Pre-19 PostgreSQL has no query_instr; allocate totaltime ourselves in
* the per-query context so it goes away at ExecutorEnd.
*/
if (queryDesc->totaltime == NULL)
{
Expand All @@ -295,6 +309,7 @@ explain_ExecutorStart(QueryDesc *queryDesc, int eflags)
queryDesc->totaltime = InstrAlloc(1, INSTRUMENT_ALL, false);
MemoryContextSwitchTo(oldcxt);
}
#endif
}

/*
Expand All @@ -309,7 +324,7 @@ _explain_statement(QueryDesc *queryDesc, double normalized_error)
if (log_min_error < 0. || normalized_error < log_min_error)
return;
#if PG_VERSION_NUM >= 190000
msec = INSTR_TIME_GET_MILLISEC(queryDesc->totaltime->total);
msec = INSTR_TIME_GET_MILLISEC(queryDesc->query_instr->total);
#else
msec = queryDesc->totaltime->total * 1000.0;
#endif
Expand Down Expand Up @@ -485,8 +500,12 @@ track_ExecutorEnd(QueryDesc *queryDesc)
PlanEstimatorContext ctx;

track_attach_shmem();

#if PG_VERSION_NUM >= 190000
if (!queryDesc->query_instr ||
!(queryDesc->query_instr_options & INSTRUMENT_TIMER) ||
#else
if (!queryDesc->totaltime ||
#endif
!track_optimizer_enabled(queryDesc, queryDesc->estate->es_top_eflags) ||
queryDesc->plannedstmt->queryId == 0)
/*
Expand All @@ -510,8 +529,16 @@ track_ExecutorEnd(QueryDesc *queryDesc)
/*
* Make sure stats accumulation is done. (Note: it's okay if several
* levels of hook all do this.)
*
* On PG >= 19 the query-level Instrumentation is updated directly by
* core's InstrStart/InstrStop pair around ExecutorRun (InstrStop folds
* elapsed time straight into ->total), so there is no end-loop step
* here. Pre-19 totaltime was a NodeInstrumentation and did need the
* end-loop call.
*/
#if PG_VERSION_NUM < 190000
InstrEndLoop(queryDesc->totaltime);
#endif

/*
* Check that the plan was actually executed (not just a cursor declared
Expand All @@ -522,7 +549,7 @@ track_ExecutorEnd(QueryDesc *queryDesc)
if ((queryDesc->planstate->instrument->running ||
queryDesc->planstate->instrument->nloops > 0) &&
#if PG_VERSION_NUM >= 190000
!INSTR_TIME_IS_ZERO(queryDesc->totaltime->total)
!INSTR_TIME_IS_ZERO(queryDesc->query_instr->total)
#else
queryDesc->totaltime->total > 0.0
#endif
Expand Down
39 changes: 29 additions & 10 deletions plan_error.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,14 @@ prediction_walker(PlanState *pstate, void *context)
*/
foreach_node(SubPlanState, sps, pstate->subPlan)
{
Instrumentation *instr = sps->planstate->instrument;
double subplan_time;
double loop_factor;
double cost_factor;
#if PG_VERSION_NUM >= 190000
NodeInstrumentation *instr = sps->planstate->instrument;
#else
Instrumentation *instr = sps->planstate->instrument;
#endif
double subplan_time;
double loop_factor;
double cost_factor;

/*
* TODO:
Expand All @@ -76,7 +80,7 @@ prediction_walker(PlanState *pstate, void *context)

nloops = instr->nloops;
#if PG_VERSION_NUM >= 190000
subplan_time = INSTR_TIME_GET_MILLISEC(instr->total);
subplan_time = INSTR_TIME_GET_MILLISEC(instr->instr.total);
#else
subplan_time = instr->total * 1000.;
#endif
Expand Down Expand Up @@ -116,8 +120,8 @@ prediction_walker(PlanState *pstate, void *context)
InstrEndLoop(pstate->instrument);
nloops = pstate->instrument->nloops;
#if PG_VERSION_NUM >= 190000
node_time = INSTR_TIME_IS_ZERO(pstate->instrument->total) ? 0.0 :
INSTR_TIME_GET_MILLISEC(pstate->instrument->total);
node_time = INSTR_TIME_IS_ZERO(pstate->instrument->instr.total) ? 0.0 :
INSTR_TIME_GET_MILLISEC(pstate->instrument->instr.total);
#else
node_time = pstate->instrument->total * 1000.;
#endif
Expand Down Expand Up @@ -178,8 +182,11 @@ prediction_walker(PlanState *pstate, void *context)
*/
for (i = 0; i < pstate->worker_instrument->num_workers; i++)
{
Instrumentation *instr = &pstate->worker_instrument->instrument[i];

#if PG_VERSION_NUM >= 190000
NodeInstrumentation *instr = &pstate->worker_instrument->instrument[i];
#else
Instrumentation *instr = &pstate->worker_instrument->instrument[i];
#endif
if (instr->nloops <= 0.0)
{
/*
Expand Down Expand Up @@ -359,7 +366,7 @@ plan_error(QueryDesc *queryDesc, PlanEstimatorContext *ctx)
ctx->twa_error = 0.;
ctx->wca_error = 0.;
#if PG_VERSION_NUM >= 190000
ctx->totaltime = INSTR_TIME_GET_MILLISEC(queryDesc->totaltime->total);
ctx->totaltime = INSTR_TIME_GET_MILLISEC(queryDesc->query_instr->total);
#else
ctx->totaltime = queryDesc->totaltime->total * 1000.;
#endif
Expand All @@ -382,18 +389,30 @@ plan_error(QueryDesc *queryDesc, PlanEstimatorContext *ctx)
* For the sake of optimisation preciseness we don't differ blocks found in
* memory and fetched from the disk - the optimiser doesn't predict that.
*/
#if PG_VERSION_NUM >= 190000
ctx->blks_accessed = queryDesc->query_instr->bufusage.shared_blks_hit +
queryDesc->query_instr->bufusage.shared_blks_read +
queryDesc->query_instr->bufusage.local_blks_hit +
queryDesc->query_instr->bufusage.local_blks_read +
queryDesc->query_instr->bufusage.temp_blks_read;
#else
ctx->blks_accessed = queryDesc->totaltime->bufusage.shared_blks_hit +
queryDesc->totaltime->bufusage.shared_blks_read +
queryDesc->totaltime->bufusage.local_blks_hit +
queryDesc->totaltime->bufusage.local_blks_read +
queryDesc->totaltime->bufusage.temp_blks_read;
#endif

/*
* Collect temp blocks written separately to help identify work_mem
* issues. Temp blocks written indicate sorts/hash joins spilling to disk,
* suggesting insufficient work_mem rather than optimization errors.
*/
#if PG_VERSION_NUM >= 190000
ctx->temp_blks = queryDesc->query_instr->bufusage.temp_blks_written;
#else
ctx->temp_blks = queryDesc->totaltime->bufusage.temp_blks_written;
#endif

/* Initialize JOIN filtering statistics */
ctx->f_join_filter = 0.;
Expand Down
Loading