Age Owner Branch data TLA Line data Source code
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 : :
49 rhaas@postgresql.org 27 :GNC 1 : PG_MODULE_MAGIC;
28 : :
29 : : static bool in_recursion = false;
30 : :
31 : : static char *test_plan_advice_advisor(PlannerGlobal *glob,
32 : : Query *parse,
33 : : const char *query_string,
34 : : int cursorOptions,
35 : : ExplainState *es);
36 : : static DefElem *find_defelem_by_defname(List *deflist, char *defname);
37 : :
38 : : /*
39 : : * Initialize this module.
40 : : */
41 : : void
42 : 1 : _PG_init(void)
43 : : {
44 : : void (*add_advisor_fn) (pg_plan_advice_advisor_hook hook);
45 : :
46 : : /*
47 : : * Ask pg_plan_advice to get advice strings from test_plan_advice_advisor
48 : : */
49 : 1 : add_advisor_fn =
50 : 1 : load_external_function("pg_plan_advice", "pg_plan_advice_add_advisor",
51 : : true, NULL);
52 : :
53 : 1 : (*add_advisor_fn) (test_plan_advice_advisor);
54 : 1 : }
55 : :
56 : : /*
57 : : * Re-plan the given query and return the generated advice string as the
58 : : * supplied advice.
59 : : */
60 : : static char *
61 : 88454 : test_plan_advice_advisor(PlannerGlobal *glob, Query *parse,
62 : : const char *query_string, int cursorOptions,
63 : : ExplainState *es)
64 : : {
65 : : PlannedStmt *pstmt;
66 : 88454 : int save_nestlevel = 0;
67 : : DefElem *pgpa_item;
68 : : DefElem *advice_string_item;
69 : :
70 : : /*
71 : : * Since this function is called from the planner and triggers planning,
72 : : * we need a recursion guard.
73 : : */
74 [ + + ]: 88454 : if (in_recursion)
75 : 44260 : return NULL;
76 : :
77 [ + + ]: 44194 : PG_TRY();
78 : : {
79 : 44194 : 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 : 44194 : save_nestlevel = NewGUCNestLevel();
92 : 44194 : set_config_option("client_min_messages", "error",
93 : : PGC_SUSET, PGC_S_SESSION,
94 : : GUC_ACTION_SAVE, true, 0, false);
95 : 44194 : set_config_option("pg_plan_advice.always_store_advice_details", "true",
96 : : PGC_SUSET, PGC_S_SESSION,
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 : 44194 : pstmt = planner(copyObject(parse), query_string, cursorOptions,
105 : : glob->boundParams, es);
106 : : }
107 : 767 : PG_FINALLY();
108 : : {
109 : 44194 : in_recursion = false;
110 : : }
111 [ + + ]: 44194 : PG_END_TRY();
112 : :
113 : : /* Roll back any GUC changes */
114 [ + - ]: 43427 : if (save_nestlevel > 0)
115 : 43427 : AtEOXact_GUC(false, save_nestlevel);
116 : :
117 : : /* Extract and return the advice string */
118 : 43427 : pgpa_item = find_defelem_by_defname(pstmt->extension_state,
119 : : "pg_plan_advice");
120 [ - + ]: 43427 : if (pgpa_item == NULL)
49 rhaas@postgresql.org 121 [ # # ]:UNC 0 : elog(ERROR, "extension state for pg_plan_advice not found");
49 rhaas@postgresql.org 122 :GNC 43427 : advice_string_item = find_defelem_by_defname((List *) pgpa_item->arg,
123 : : "advice_string");
124 [ - + ]: 43427 : if (advice_string_item == NULL)
49 rhaas@postgresql.org 125 [ # # ]:UNC 0 : elog(ERROR,
126 : : "advice string for pg_plan_advice not found in extension state");
49 rhaas@postgresql.org 127 :GNC 43427 : return strVal(advice_string_item->arg);
128 : : }
129 : :
130 : : /*
131 : : * Search a list of DefElem objects for a given defname.
132 : : */
133 : : static DefElem *
134 : 86854 : find_defelem_by_defname(List *deflist, char *defname)
135 : : {
136 [ + - + - : 86854 : foreach_node(DefElem, item, deflist)
+ - ]
137 : : {
138 [ + - ]: 86854 : if (strcmp(item->defname, defname) == 0)
139 : 86854 : return item;
140 : : }
141 : :
49 rhaas@postgresql.org 142 :UNC 0 : return NULL;
143 : : }
|