PostgreSQL Source Code git master
Loading...
Searching...
No Matches
pgpa_ast.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * pgpa_ast.c
4 * additional supporting code related to plan advice parsing
5 *
6 * Copyright (c) 2016-2026, PostgreSQL Global Development Group
7 *
8 * contrib/pg_plan_advice/pgpa_ast.c
9 *
10 *-------------------------------------------------------------------------
11 */
12
13#include "postgres.h"
14
15#include "pgpa_ast.h"
16
17#include "funcapi.h"
18#include "utils/array.h"
19#include "utils/builtins.h"
20
22 pgpa_advice_target *target,
23 bool *rids_used);
24
25/*
26 * Get a C string that corresponds to the specified advice tag.
27 */
28char *
30{
31 switch (advice_tag)
32 {
34 return "BITMAP_HEAP_SCAN";
36 return "DO_NOT_SCAN";
38 return "FOREIGN_JOIN";
39 case PGPA_TAG_GATHER:
40 return "GATHER";
42 return "GATHER_MERGE";
44 return "HASH_JOIN";
46 return "INDEX_ONLY_SCAN";
48 return "INDEX_SCAN";
50 return "JOIN_ORDER";
52 return "MERGE_JOIN_MATERIALIZE";
54 return "MERGE_JOIN_PLAIN";
56 return "NESTED_LOOP_MATERIALIZE";
58 return "NESTED_LOOP_MEMOIZE";
60 return "NESTED_LOOP_PLAIN";
62 return "NO_GATHER";
64 return "PARTITIONWISE";
66 return "SEMIJOIN_NON_UNIQUE";
68 return "SEMIJOIN_UNIQUE";
70 return "SEQ_SCAN";
72 return "TID_SCAN";
73 }
74
76 return NULL;
77}
78
79/*
80 * Convert an advice tag, formatted as a string that has already been
81 * downcased as appropriate, to a pgpa_advice_tag_type.
82 *
83 * If we succeed, set *fail = false and return the result; if we fail,
84 * set *fail = true and return an arbitrary value.
85 */
87pgpa_parse_advice_tag(const char *tag, bool *fail)
88{
89 *fail = false;
90
91 switch (tag[0])
92 {
93 case 'b':
94 if (strcmp(tag, "bitmap_heap_scan") == 0)
96 break;
97 case 'd':
98 if (strcmp(tag, "do_not_scan") == 0)
100 break;
101 case 'f':
102 if (strcmp(tag, "foreign_join") == 0)
104 break;
105 case 'g':
106 if (strcmp(tag, "gather") == 0)
107 return PGPA_TAG_GATHER;
108 if (strcmp(tag, "gather_merge") == 0)
110 break;
111 case 'h':
112 if (strcmp(tag, "hash_join") == 0)
113 return PGPA_TAG_HASH_JOIN;
114 break;
115 case 'i':
116 if (strcmp(tag, "index_scan") == 0)
117 return PGPA_TAG_INDEX_SCAN;
118 if (strcmp(tag, "index_only_scan") == 0)
120 break;
121 case 'j':
122 if (strcmp(tag, "join_order") == 0)
123 return PGPA_TAG_JOIN_ORDER;
124 break;
125 case 'm':
126 if (strcmp(tag, "merge_join_materialize") == 0)
128 if (strcmp(tag, "merge_join_plain") == 0)
130 break;
131 case 'n':
132 if (strcmp(tag, "nested_loop_materialize") == 0)
134 if (strcmp(tag, "nested_loop_memoize") == 0)
136 if (strcmp(tag, "nested_loop_plain") == 0)
138 if (strcmp(tag, "no_gather") == 0)
139 return PGPA_TAG_NO_GATHER;
140 break;
141 case 'p':
142 if (strcmp(tag, "partitionwise") == 0)
144 break;
145 case 's':
146 if (strcmp(tag, "semijoin_non_unique") == 0)
148 if (strcmp(tag, "semijoin_unique") == 0)
150 if (strcmp(tag, "seq_scan") == 0)
151 return PGPA_TAG_SEQ_SCAN;
152 break;
153 case 't':
154 if (strcmp(tag, "tid_scan") == 0)
155 return PGPA_TAG_TID_SCAN;
156 break;
157 }
158
159 /* didn't work out */
160 *fail = true;
161
162 /* return an arbitrary value to unwind the call stack */
163 return PGPA_TAG_SEQ_SCAN;
164}
165
166/*
167 * Format a pgpa_advice_target as a string and append result to a StringInfo.
168 */
169void
171{
172 if (target->ttype != PGPA_TARGET_IDENTIFIER)
173 {
174 bool first = true;
175 char *delims;
176
177 if (target->ttype == PGPA_TARGET_UNORDERED_LIST)
178 delims = "{}";
179 else
180 delims = "()";
181
184 {
185 if (first)
186 first = false;
187 else
190 }
192 }
193 else
194 {
195 const char *rt_identifier;
196
199 }
200}
201
202/*
203 * Format a pgpa_index_target as a string and append result to a StringInfo.
204 */
205void
213
214/*
215 * Determine whether two pgpa_index_target objects are exactly identical.
216 */
217bool
219{
220 /* indnamespace can be NULL, and two NULL values are equal */
221 if ((i1->indnamespace != NULL || i2->indnamespace != NULL) &&
222 (i1->indnamespace == NULL || i2->indnamespace == NULL ||
223 strcmp(i1->indnamespace, i2->indnamespace) != 0))
224 return false;
225 if (strcmp(i1->indname, i2->indname) != 0)
226 return false;
227
228 return true;
229}
230
231/*
232 * Check whether an identifier matches an any part of an advice target.
233 */
234bool
236{
237 /* For non-identifiers, check all descendants. */
238 if (target->ttype != PGPA_TARGET_IDENTIFIER)
239 {
241 {
243 return true;
244 }
245 return false;
246 }
247
248 /* Straightforward comparisons of alias name and occurrence number. */
249 if (strcmp(rid->alias_name, target->rid.alias_name) != 0)
250 return false;
251 if (rid->occurrence != target->rid.occurrence)
252 return false;
253
254 /*
255 * If a relation identifier mentions a partition name, it should also
256 * specify a partition schema. But the target may leave the schema NULL to
257 * match anything.
258 */
259 Assert(rid->partnsp != NULL || rid->partrel == NULL);
260 if (rid->partnsp != NULL && target->rid.partnsp != NULL &&
261 strcmp(rid->partnsp, target->rid.partnsp) != 0)
262 return false;
263
264 /*
265 * These fields can be NULL on either side, but NULL only matches another
266 * NULL.
267 */
268 if (!strings_equal_or_both_null(rid->partrel, target->rid.partrel))
269 return false;
271 return false;
272
273 return true;
274}
275
276/*
277 * Match identifiers to advice targets and return an enum value indicating
278 * the relationship between the set of keys and the set of targets.
279 *
280 * See the comments for pgpa_itm_type.
281 */
284 pgpa_advice_target *target)
285{
286 bool all_rids_used = true;
287 bool any_rids_used = false;
288 bool all_targets_used;
289 bool *rids_used = palloc0_array(bool, nrids);
290
293
294 for (int i = 0; i < nrids; ++i)
295 {
296 if (rids_used[i])
297 any_rids_used = true;
298 else
299 all_rids_used = false;
300 }
301
302 if (all_rids_used)
303 {
305 return PGPA_ITM_EQUAL;
306 else
308 }
309 else
310 {
313 else if (any_rids_used)
315 else
316 return PGPA_ITM_DISJOINT;
317 }
318}
319
320/*
321 * Returns true if every target or sub-target is matched by at least one
322 * identifier, and otherwise false.
323 *
324 * Also sets rids_used[i] = true for each idenifier that matches at least one
325 * target.
326 */
327static bool
329 pgpa_advice_target *target, bool *rids_used)
330{
331 bool result = false;
332
333 if (target->ttype != PGPA_TARGET_IDENTIFIER)
334 {
335 result = true;
336
338 {
340 rids_used))
341 result = false;
342 }
343 }
344 else
345 {
346 for (int i = 0; i < nrids; ++i)
347 {
348 if (pgpa_identifier_matches_target(&rids[i], target))
349 {
350 rids_used[i] = true;
351 result = true;
352 }
353 }
354 }
355
356 return result;
357}
#define Assert(condition)
Definition c.h:943
#define pg_unreachable()
Definition c.h:367
#define palloc0_array(type, count)
Definition fe_memutils.h:77
const char * str
int i
Definition isn.c:77
#define foreach_ptr(type, var, lst)
Definition pg_list.h:501
char * pgpa_cstring_advice_tag(pgpa_advice_tag_type advice_tag)
Definition pgpa_ast.c:29
bool pgpa_identifier_matches_target(pgpa_identifier *rid, pgpa_advice_target *target)
Definition pgpa_ast.c:235
void pgpa_format_advice_target(StringInfo str, pgpa_advice_target *target)
Definition pgpa_ast.c:170
static bool pgpa_identifiers_cover_target(int nrids, pgpa_identifier *rids, pgpa_advice_target *target, bool *rids_used)
Definition pgpa_ast.c:328
pgpa_advice_tag_type pgpa_parse_advice_tag(const char *tag, bool *fail)
Definition pgpa_ast.c:87
void pgpa_format_index_target(StringInfo str, pgpa_index_target *itarget)
Definition pgpa_ast.c:206
bool pgpa_index_targets_equal(pgpa_index_target *i1, pgpa_index_target *i2)
Definition pgpa_ast.c:218
pgpa_itm_type pgpa_identifiers_match_target(int nrids, pgpa_identifier *rids, pgpa_advice_target *target)
Definition pgpa_ast.c:283
pgpa_advice_tag_type
Definition pgpa_ast.h:81
@ PGPA_TAG_INDEX_SCAN
Definition pgpa_ast.h:89
@ PGPA_TAG_NESTED_LOOP_MATERIALIZE
Definition pgpa_ast.h:93
@ PGPA_TAG_MERGE_JOIN_PLAIN
Definition pgpa_ast.h:92
@ PGPA_TAG_GATHER_MERGE
Definition pgpa_ast.h:86
@ PGPA_TAG_GATHER
Definition pgpa_ast.h:85
@ PGPA_TAG_NESTED_LOOP_MEMOIZE
Definition pgpa_ast.h:94
@ PGPA_TAG_SEMIJOIN_NON_UNIQUE
Definition pgpa_ast.h:98
@ PGPA_TAG_BITMAP_HEAP_SCAN
Definition pgpa_ast.h:82
@ PGPA_TAG_PARTITIONWISE
Definition pgpa_ast.h:97
@ PGPA_TAG_NO_GATHER
Definition pgpa_ast.h:96
@ PGPA_TAG_INDEX_ONLY_SCAN
Definition pgpa_ast.h:88
@ PGPA_TAG_SEQ_SCAN
Definition pgpa_ast.h:100
@ PGPA_TAG_HASH_JOIN
Definition pgpa_ast.h:87
@ PGPA_TAG_SEMIJOIN_UNIQUE
Definition pgpa_ast.h:99
@ PGPA_TAG_DO_NOT_SCAN
Definition pgpa_ast.h:83
@ PGPA_TAG_JOIN_ORDER
Definition pgpa_ast.h:90
@ PGPA_TAG_TID_SCAN
Definition pgpa_ast.h:101
@ PGPA_TAG_FOREIGN_JOIN
Definition pgpa_ast.h:84
@ PGPA_TAG_NESTED_LOOP_PLAIN
Definition pgpa_ast.h:95
@ PGPA_TAG_MERGE_JOIN_MATERIALIZE
Definition pgpa_ast.h:91
@ PGPA_TARGET_UNORDERED_LIST
Definition pgpa_ast.h:29
@ PGPA_TARGET_IDENTIFIER
Definition pgpa_ast.h:27
pgpa_itm_type
Definition pgpa_ast.h:142
@ PGPA_ITM_EQUAL
Definition pgpa_ast.h:143
@ PGPA_ITM_DISJOINT
Definition pgpa_ast.h:147
@ PGPA_ITM_KEYS_ARE_SUBSET
Definition pgpa_ast.h:144
@ PGPA_ITM_TARGETS_ARE_SUBSET
Definition pgpa_ast.h:145
@ PGPA_ITM_INTERSECTING
Definition pgpa_ast.h:146
const char * pgpa_identifier_string(const pgpa_identifier *rid)
static bool strings_equal_or_both_null(const char *a, const char *b)
static int fb(int x)
const char * quote_identifier(const char *ident)
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition stringinfo.c:145
void appendStringInfoString(StringInfo str, const char *s)
Definition stringinfo.c:230
void appendStringInfoChar(StringInfo str, char ch)
Definition stringinfo.c:242
pgpa_identifier rid
Definition pgpa_ast.h:58
pgpa_target_type ttype
Definition pgpa_ast.h:49
const char * alias_name
const char * partnsp
const char * partrel
const char * plan_name
char * indnamespace
Definition pgpa_ast.h:38