PostgreSQL Source Code git master
Loading...
Searching...
No Matches
test_plan_advice.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * test_plan_advice.c
4 * Test pg_plan_advice by planning every query with generated advice.
5 *
6 * With this module loaded, every time a query is executed, we end up
7 * planning it twice. The first time we plan it, we generate plan advice,
8 * which we then feed back to pg_plan_advice as the supplied plan advice.
9 * It is then planned a second time using that advice. This hopefully
10 * allows us to detect cases where the advice is incorrect or causes
11 * failures or plan changes for some reason.
12 *
13 * Copyright (c) 2016-2026, PostgreSQL Global Development Group
14 *
15 * src/test/modules/test_plan_advice/test_plan_advice.c
16 *
17 *-------------------------------------------------------------------------
18 */
19#include "postgres.h"
20
21#include "access/xact.h"
22#include "fmgr.h"
23#include "optimizer/optimizer.h"
24#include "pg_plan_advice.h"
25#include "utils/guc.h"
26
28
29static bool in_recursion = false;
30
32 Query *parse,
33 const char *query_string,
34 int cursorOptions,
35 ExplainState *es);
36static DefElem *find_defelem_by_defname(List *deflist, char *defname);
37
38/*
39 * Initialize this module.
40 */
41void
43{
45
46 /*
47 * Ask pg_plan_advice to get advice strings from test_plan_advice_advisor
48 */
50 load_external_function("pg_plan_advice", "pg_plan_advice_add_advisor",
51 true, NULL);
52
53 (*add_advisor_fn) (test_plan_advice_advisor);
54}
55
56/*
57 * Re-plan the given query and return the generated advice string as the
58 * supplied advice.
59 */
60static char *
62 const char *query_string, int cursorOptions,
63 ExplainState *es)
64{
65 PlannedStmt *pstmt;
66 int save_nestlevel = 0;
69
70 /*
71 * Since this function is called from the planner and triggers planning,
72 * we need a recursion guard.
73 */
74 if (in_recursion)
75 return NULL;
76
77 PG_TRY();
78 {
79 in_recursion = true;
80
81 /*
82 * Planning can trigger expression evaluation, which can result in
83 * sending NOTICE messages or other output to the client. To avoid
84 * that, we set client_min_messages = ERROR in the hopes of getting
85 * the same output with and without this module.
86 *
87 * We also need to set pg_plan_advice.always_store_advice_details so
88 * that pg_plan_advice will generate an advice string, since the whole
89 * point of this function is to get access to that.
90 */
91 save_nestlevel = NewGUCNestLevel();
92 set_config_option("client_min_messages", "error",
94 GUC_ACTION_SAVE, true, 0, false);
95 set_config_option("pg_plan_advice.always_store_advice_details", "true",
97 GUC_ACTION_SAVE, true, 0, false);
98
99 /*
100 * Replan. We must copy the Query, because the planner modifies it.
101 * (As noted elsewhere, that's unfortunate; perhaps it will be fixed
102 * some day.)
103 */
104 pstmt = planner(copyObject(parse), query_string, cursorOptions,
105 glob->boundParams, es);
106 }
107 PG_FINALLY();
108 {
109 in_recursion = false;
110 }
111 PG_END_TRY();
112
113 /* Roll back any GUC changes */
114 if (save_nestlevel > 0)
115 AtEOXact_GUC(false, save_nestlevel);
116
117 /* Extract and return the advice string */
119 "pg_plan_advice");
120 if (pgpa_item == NULL)
121 elog(ERROR, "extension state for pg_plan_advice not found");
123 "advice_string");
125 elog(ERROR,
126 "advice string for pg_plan_advice not found in extension state");
127 return strVal(advice_string_item->arg);
128}
129
130/*
131 * Search a list of DefElem objects for a given defname.
132 */
133static DefElem *
135{
137 {
138 if (strcmp(item->defname, defname) == 0)
139 return item;
140 }
141
142 return NULL;
143}
void * load_external_function(const char *filename, const char *funcname, bool signalNotFound, void **filehandle)
Definition dfmgr.c:95
#define PG_TRY(...)
Definition elog.h:372
#define PG_END_TRY(...)
Definition elog.h:397
#define ERROR
Definition elog.h:39
#define elog(elevel,...)
Definition elog.h:226
#define PG_FINALLY(...)
Definition elog.h:389
int NewGUCNestLevel(void)
Definition guc.c:2142
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition guc.c:2169
int set_config_option(const char *name, const char *value, GucContext context, GucSource source, GucAction action, bool changeVal, int elevel, bool is_reload)
Definition guc.c:3248
@ GUC_ACTION_SAVE
Definition guc.h:205
@ PGC_S_SESSION
Definition guc.h:126
@ PGC_SUSET
Definition guc.h:78
void parse(int)
Definition parse.c:49
#define copyObject(obj)
Definition nodes.h:232
#define foreach_node(type, var, lst)
Definition pg_list.h:496
char *(* pg_plan_advice_advisor_hook)(PlannerGlobal *glob, Query *parse, const char *query_string, int cursorOptions, ExplainState *es)
PlannedStmt * planner(Query *parse, const char *query_string, int cursorOptions, ParamListInfo boundParams, ExplainState *es)
Definition planner.c:315
static int fb(int x)
Definition pg_list.h:54
List * extension_state
Definition plannodes.h:163
static char * test_plan_advice_advisor(PlannerGlobal *glob, Query *parse, const char *query_string, int cursorOptions, ExplainState *es)
void _PG_init(void)
static DefElem * find_defelem_by_defname(List *deflist, char *defname)
static bool in_recursion
PG_MODULE_MAGIC
#define strVal(v)
Definition value.h:82