diff --git a/pg_track_optimizer.c b/pg_track_optimizer.c index 7007001..1219f37 100644 --- a/pg_track_optimizer.c +++ b/pg_track_optimizer.c @@ -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" @@ -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); @@ -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) { @@ -295,6 +309,7 @@ explain_ExecutorStart(QueryDesc *queryDesc, int eflags) queryDesc->totaltime = InstrAlloc(1, INSTRUMENT_ALL, false); MemoryContextSwitchTo(oldcxt); } +#endif } /* @@ -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 @@ -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) /* @@ -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 @@ -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 diff --git a/plan_error.c b/plan_error.c index 9e505ea..19a92d1 100644 --- a/plan_error.c +++ b/plan_error.c @@ -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: @@ -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 @@ -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 @@ -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) { /* @@ -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 @@ -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.;