From: Robert Haas Date: Mon, 1 Dec 2025 17:15:15 +0000 (-0500) Subject: add 'generate_advice_string' to pps, so we always know during planning X-Git-Url: http://git.postgresql.org/gitweb/static/gitweb.js?a=commitdiff_plain;h=f0042c9cd54c096d613ed38193250b702a741b8d;p=users%2Frhaas%2Fpostgres.git add 'generate_advice_string' to pps, so we always know during planning whether we're doing this thing --- diff --git a/contrib/pg_plan_advice/pg_plan_advice.c b/contrib/pg_plan_advice/pg_plan_advice.c index f32e8b7a0d..865931c960 100644 --- a/contrib/pg_plan_advice/pg_plan_advice.c +++ b/contrib/pg_plan_advice/pg_plan_advice.c @@ -217,6 +217,19 @@ pg_plan_advice_dsa_area(void) return pgpa_dsa_area; } +/* + * Was the PLAN_ADVICE option specified and not set to false? + */ +bool +pg_plan_advice_should_explain(ExplainState *es) +{ + bool *plan_advice = NULL; + + if (es != NULL) + plan_advice = GetExplainExtensionState(es, es_extension_id); + return plan_advice != NULL && *plan_advice; +} + /* * Handler for EXPLAIN (PLAN_ADVICE). */ @@ -329,7 +342,7 @@ pg_plan_advice_explain_per_plan_hook(PlannedStmt *plannedstmt, ParamListInfo params, QueryEnvironment *queryEnv) { - bool *plan_advice = GetExplainExtensionState(es, es_extension_id); + bool should_explain; DefElem *pgpa_item; List *pgpa_list; @@ -337,6 +350,9 @@ pg_plan_advice_explain_per_plan_hook(PlannedStmt *plannedstmt, prev_explain_per_plan(plannedstmt, into, es, queryString, params, queryEnv); + /* Should an advice string be part of the EXPLAIN output? */ + should_explain = pg_plan_advice_should_explain(es); + /* Find any data pgpa_planner_shutdown stashed in the PlannedStmt. */ pgpa_item = find_defelem_by_defname(plannedstmt->extension_state, "pg_plan_advice"); @@ -354,7 +370,7 @@ pg_plan_advice_explain_per_plan_hook(PlannedStmt *plannedstmt, * recorded, and therefore this won't be able to show anything. */ if (pgpa_list != NULL && (pg_plan_advice_always_explain_supplied_advice || - (plan_advice != NULL && *plan_advice))) + should_explain)) { DefElem *feedback; @@ -367,10 +383,10 @@ pg_plan_advice_explain_per_plan_hook(PlannedStmt *plannedstmt, * If the PLAN_ADVICE option was specified -- and not sent to FALSE -- * show generated advice. */ - if (plan_advice != NULL && *plan_advice) + if (should_explain) { DefElem *advice_string_item; - char *advice_string; + char *advice_string = NULL; advice_string_item = find_defelem_by_defname(pgpa_list, "advice_string"); @@ -379,22 +395,7 @@ pg_plan_advice_explain_per_plan_hook(PlannedStmt *plannedstmt, /* Advice has already been generated; we can reuse it. */ advice_string = strVal(advice_string_item->arg); } - else - { - pgpa_plan_walker_context walker; - StringInfoData buf; - pgpa_identifier *rt_identifiers; - - /* Advice not yet generated; do that now. */ - pgpa_plan_walker(&walker, plannedstmt); - rt_identifiers = - pgpa_create_identifiers_for_planned_stmt(plannedstmt); - initStringInfo(&buf); - pgpa_output_advice(&buf, &walker, rt_identifiers); - advice_string = buf.data; - } - - if (advice_string[0] != '\0') + if (advice_string != NULL && advice_string[0] != '\0') pg_plan_advice_explain_text_multiline(es, "Generated Plan Advice", advice_string); } diff --git a/contrib/pg_plan_advice/pg_plan_advice.h b/contrib/pg_plan_advice/pg_plan_advice.h index 86efb3b611..02e65a3382 100644 --- a/contrib/pg_plan_advice/pg_plan_advice.h +++ b/contrib/pg_plan_advice/pg_plan_advice.h @@ -12,6 +12,7 @@ #ifndef PG_PLAN_ADVICE_H #define PG_PLAN_ADVICE_H +#include "commands/explain_state.h" #include "nodes/plannodes.h" #include "storage/lwlock.h" #include "utils/dsa.h" @@ -33,5 +34,6 @@ extern char *pg_plan_advice_advice; extern MemoryContext pg_plan_advice_get_mcxt(void); extern pgpa_shared_state *pg_plan_advice_attach(void); extern dsa_area *pg_plan_advice_dsa_area(void); +extern bool pg_plan_advice_should_explain(ExplainState *es); #endif diff --git a/contrib/pg_plan_advice/pgpa_planner.c b/contrib/pg_plan_advice/pgpa_planner.c index 11c8065d15..c75bb779cb 100644 --- a/contrib/pg_plan_advice/pgpa_planner.c +++ b/contrib/pg_plan_advice/pgpa_planner.c @@ -79,6 +79,7 @@ pgpa_ri_checker_compare_key(pgpa_ri_checker_key a, pgpa_ri_checker_key b) typedef struct pgpa_planner_state { ExplainState *explain_state; + bool generate_advice_string; pgpa_trove *trove; MemoryContext trove_cxt; @@ -441,7 +442,7 @@ pgpa_join_path_setup(PlannerInfo *root, RelOptInfo *joinrel, } /* - * Prepare advice for use by a query. + * Carry out whatever setup work we need to do before planning. */ static void pgpa_planner_setup(PlannerGlobal *glob, Query *parse, const char *query_string, @@ -449,8 +450,20 @@ pgpa_planner_setup(PlannerGlobal *glob, Query *parse, const char *query_string, { pgpa_trove *trove = NULL; pgpa_planner_state *pps; + bool generate_advice_string = false; bool needs_pps = false; + /* + * Decide whether we need to generate an advice string. We must do this if + * at least one collector is enabled or if the user has requested it using + * the EXPLAIN (PLAN_ADVICE) option. + */ + generate_advice_string = (pg_plan_advice_local_collection_limit > 0 || + pg_plan_advice_shared_collection_limit > 0 || + pg_plan_advice_should_explain(es)); + if (generate_advice_string) + needs_pps = true; + /* * If any advice was provided, build a trove of advice for use during * planning. @@ -489,11 +502,17 @@ pgpa_planner_setup(PlannerGlobal *glob, Query *parse, const char *query_string, needs_pps = true; #endif - /* Initialize and store private state, if required. */ + /* + * We only create and initialize a private state object if it's needed for + * some purpose. That could be (1) recording that we will need to generate + * an advice string, (2) storing a trove of supplied advice, or (3) + * facilitating debugging cross-checks when asserts are enabled. + */ if (needs_pps) { pps = palloc0_object(pgpa_planner_state); pps->explain_state = es; + pps->generate_advice_string = generate_advice_string; pps->trove = trove; #ifdef USE_ASSERT_CHECKING pps->ri_check_hash = @@ -515,7 +534,7 @@ pgpa_planner_shutdown(PlannerGlobal *glob, Query *parse, ExplainState *es = NULL; pgpa_plan_walker_context walker = {0}; /* placate compiler */ bool do_advice_feedback; - bool do_collect_advice; + bool generate_advice_string = false; List *pgpa_items = NIL; pgpa_identifier *rt_identifiers = NULL; @@ -525,27 +544,26 @@ pgpa_planner_shutdown(PlannerGlobal *glob, Query *parse, { trove = pps->trove; es = pps->explain_state; + generate_advice_string = pps->generate_advice_string; } - /* If at least one collector is enabled, generate advice. */ - do_collect_advice = (pg_plan_advice_local_collection_limit > 0 || - pg_plan_advice_shared_collection_limit > 0); - - /* If we applied advice, generate feedback. */ + /* + * If any advice was specified, and if we're running under EXPLAIN, then + * we should try try to provide advice feedback. + * + * If we're trying to generate an advice string or if we're trying to + * provide advice feedback, then we will need to create range table + * identifiers. + */ do_advice_feedback = (trove != NULL && es != NULL); - - /* If either of the above apply, analyze the resulting PlannedStmt. */ - if (do_collect_advice || do_advice_feedback) + if (generate_advice_string || do_advice_feedback) { pgpa_plan_walker(&walker, pstmt); rt_identifiers = pgpa_create_identifiers_for_planned_stmt(pstmt); } - /* - * If advice collection is enabled, put the advice in string form and send - * it to the collector. - */ - if (do_collect_advice) + /* Generate the advice string, if we need to do so. */ + if (generate_advice_string) { char *advice_string; StringInfoData buf; diff --git a/contrib/pg_plan_advice/pgpa_walker.c b/contrib/pg_plan_advice/pgpa_walker.c index 4ed2229109..db8cc4352c 100644 --- a/contrib/pg_plan_advice/pgpa_walker.c +++ b/contrib/pg_plan_advice/pgpa_walker.c @@ -700,7 +700,7 @@ pgpa_walker_join_order_matches(pgpa_unrolled_join *ujoin, pgpa_advice_target *target, bool toplevel) { - int nchildren = list_length(target->children); + int nchildren = list_length(target->children); Assert(target->ttype == PGPA_TARGET_ORDERED_LIST);