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 "FOREIGN_JOIN";
37 case PGPA_TAG_GATHER:
38 return "GATHER";
40 return "GATHER_MERGE";
42 return "HASH_JOIN";
44 return "INDEX_ONLY_SCAN";
46 return "INDEX_SCAN";
48 return "JOIN_ORDER";
50 return "MERGE_JOIN_MATERIALIZE";
52 return "MERGE_JOIN_PLAIN";
54 return "NESTED_LOOP_MATERIALIZE";
56 return "NESTED_LOOP_MEMOIZE";
58 return "NESTED_LOOP_PLAIN";
60 return "NO_GATHER";
62 return "PARTITIONWISE";
64 return "SEMIJOIN_NON_UNIQUE";
66 return "SEMIJOIN_UNIQUE";
68 return "SEQ_SCAN";
70 return "TID_SCAN";
71 }
72
74 return NULL;
75}
76
77/*
78 * Convert an advice tag, formatted as a string that has already been
79 * downcased as appropriate, to a pgpa_advice_tag_type.
80 *
81 * If we succeed, set *fail = false and return the result; if we fail,
82 * set *fail = true and return an arbitrary value.
83 */
85pgpa_parse_advice_tag(const char *tag, bool *fail)
86{
87 *fail = false;
88
89 switch (tag[0])
90 {
91 case 'b':
92 if (strcmp(tag, "bitmap_heap_scan") == 0)
94 break;
95 case 'f':
96 if (strcmp(tag, "foreign_join") == 0)
98 break;
99 case 'g':
100 if (strcmp(tag, "gather") == 0)
101 return PGPA_TAG_GATHER;
102 if (strcmp(tag, "gather_merge") == 0)
104 break;
105 case 'h':
106 if (strcmp(tag, "hash_join") == 0)
107 return PGPA_TAG_HASH_JOIN;
108 break;
109 case 'i':
110 if (strcmp(tag, "index_scan") == 0)
111 return PGPA_TAG_INDEX_SCAN;
112 if (strcmp(tag, "index_only_scan") == 0)
114 break;
115 case 'j':
116 if (strcmp(tag, "join_order") == 0)
117 return PGPA_TAG_JOIN_ORDER;
118 break;
119 case 'm':
120 if (strcmp(tag, "merge_join_materialize") == 0)
122 if (strcmp(tag, "merge_join_plain") == 0)
124 break;
125 case 'n':
126 if (strcmp(tag, "nested_loop_materialize") == 0)
128 if (strcmp(tag, "nested_loop_memoize") == 0)
130 if (strcmp(tag, "nested_loop_plain") == 0)
132 if (strcmp(tag, "no_gather") == 0)
133 return PGPA_TAG_NO_GATHER;
134 break;
135 case 'p':
136 if (strcmp(tag, "partitionwise") == 0)
138 break;
139 case 's':
140 if (strcmp(tag, "semijoin_non_unique") == 0)
142 if (strcmp(tag, "semijoin_unique") == 0)
144 if (strcmp(tag, "seq_scan") == 0)
145 return PGPA_TAG_SEQ_SCAN;
146 break;
147 case 't':
148 if (strcmp(tag, "tid_scan") == 0)
149 return PGPA_TAG_TID_SCAN;
150 break;
151 }
152
153 /* didn't work out */
154 *fail = true;
155
156 /* return an arbitrary value to unwind the call stack */
157 return PGPA_TAG_SEQ_SCAN;
158}
159
160/*
161 * Format a pgpa_advice_target as a string and append result to a StringInfo.
162 */
163void
165{
166 if (target->ttype != PGPA_TARGET_IDENTIFIER)
167 {
168 bool first = true;
169 char *delims;
170
171 if (target->ttype == PGPA_TARGET_UNORDERED_LIST)
172 delims = "{}";
173 else
174 delims = "()";
175
178 {
179 if (first)
180 first = false;
181 else
184 }
186 }
187 else
188 {
189 const char *rt_identifier;
190
193 }
194}
195
196/*
197 * Format a pgpa_index_target as a string and append result to a StringInfo.
198 */
199void
207
208/*
209 * Determine whether two pgpa_index_target objects are exactly identical.
210 */
211bool
213{
214 /* indnamespace can be NULL, and two NULL values are equal */
215 if ((i1->indnamespace != NULL || i2->indnamespace != NULL) &&
216 (i1->indnamespace == NULL || i2->indnamespace == NULL ||
217 strcmp(i1->indnamespace, i2->indnamespace) != 0))
218 return false;
219 if (strcmp(i1->indname, i2->indname) != 0)
220 return false;
221
222 return true;
223}
224
225/*
226 * Check whether an identifier matches an any part of an advice target.
227 */
228bool
230{
231 /* For non-identifiers, check all descendants. */
232 if (target->ttype != PGPA_TARGET_IDENTIFIER)
233 {
235 {
237 return true;
238 }
239 return false;
240 }
241
242 /* Straightforward comparisons of alias name and occurrence number. */
243 if (strcmp(rid->alias_name, target->rid.alias_name) != 0)
244 return false;
245 if (rid->occurrence != target->rid.occurrence)
246 return false;
247
248 /*
249 * If a relation identifier mentions a partition name, it should also
250 * specify a partition schema. But the target may leave the schema NULL to
251 * match anything.
252 */
253 Assert(rid->partnsp != NULL || rid->partrel == NULL);
254 if (rid->partnsp != NULL && target->rid.partnsp != NULL &&
255 strcmp(rid->partnsp, target->rid.partnsp) != 0)
256 return false;
257
258 /*
259 * These fields can be NULL on either side, but NULL only matches another
260 * NULL.
261 */
262 if (!strings_equal_or_both_null(rid->partrel, target->rid.partrel))
263 return false;
265 return false;
266
267 return true;
268}
269
270/*
271 * Match identifiers to advice targets and return an enum value indicating
272 * the relationship between the set of keys and the set of targets.
273 *
274 * See the comments for pgpa_itm_type.
275 */
278 pgpa_advice_target *target)
279{
280 bool all_rids_used = true;
281 bool any_rids_used = false;
282 bool all_targets_used;
283 bool *rids_used = palloc0_array(bool, nrids);
284
287
288 for (int i = 0; i < nrids; ++i)
289 {
290 if (rids_used[i])
291 any_rids_used = true;
292 else
293 all_rids_used = false;
294 }
295
296 if (all_rids_used)
297 {
299 return PGPA_ITM_EQUAL;
300 else
302 }
303 else
304 {
307 else if (any_rids_used)
309 else
310 return PGPA_ITM_DISJOINT;
311 }
312}
313
314/*
315 * Returns true if every target or sub-target is matched by at least one
316 * identifier, and otherwise false.
317 *
318 * Also sets rids_used[i] = true for each idenifier that matches at least one
319 * target.
320 */
321static bool
323 pgpa_advice_target *target, bool *rids_used)
324{
325 bool result = false;
326
327 if (target->ttype != PGPA_TARGET_IDENTIFIER)
328 {
329 result = true;
330
332 {
334 rids_used))
335 result = false;
336 }
337 }
338 else
339 {
340 for (int i = 0; i < nrids; ++i)
341 {
342 if (pgpa_identifier_matches_target(&rids[i], target))
343 {
344 rids_used[i] = true;
345 result = true;
346 }
347 }
348 }
349
350 return result;
351}
#define Assert(condition)
Definition c.h:927
#define pg_unreachable()
Definition c.h:361
#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:469
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:229
void pgpa_format_advice_target(StringInfo str, pgpa_advice_target *target)
Definition pgpa_ast.c:164
static bool pgpa_identifiers_cover_target(int nrids, pgpa_identifier *rids, pgpa_advice_target *target, bool *rids_used)
Definition pgpa_ast.c:322
pgpa_advice_tag_type pgpa_parse_advice_tag(const char *tag, bool *fail)
Definition pgpa_ast.c:85
void pgpa_format_index_target(StringInfo str, pgpa_index_target *itarget)
Definition pgpa_ast.c:200
bool pgpa_index_targets_equal(pgpa_index_target *i1, pgpa_index_target *i2)
Definition pgpa_ast.c:212
pgpa_itm_type pgpa_identifiers_match_target(int nrids, pgpa_identifier *rids, pgpa_advice_target *target)
Definition pgpa_ast.c:277
pgpa_advice_tag_type
Definition pgpa_ast.h:81
@ PGPA_TAG_INDEX_SCAN
Definition pgpa_ast.h:88
@ PGPA_TAG_NESTED_LOOP_MATERIALIZE
Definition pgpa_ast.h:92
@ PGPA_TAG_MERGE_JOIN_PLAIN
Definition pgpa_ast.h:91
@ PGPA_TAG_GATHER_MERGE
Definition pgpa_ast.h:85
@ PGPA_TAG_GATHER
Definition pgpa_ast.h:84
@ PGPA_TAG_NESTED_LOOP_MEMOIZE
Definition pgpa_ast.h:93
@ PGPA_TAG_SEMIJOIN_NON_UNIQUE
Definition pgpa_ast.h:97
@ PGPA_TAG_BITMAP_HEAP_SCAN
Definition pgpa_ast.h:82
@ PGPA_TAG_PARTITIONWISE
Definition pgpa_ast.h:96
@ PGPA_TAG_NO_GATHER
Definition pgpa_ast.h:95
@ PGPA_TAG_INDEX_ONLY_SCAN
Definition pgpa_ast.h:87
@ PGPA_TAG_SEQ_SCAN
Definition pgpa_ast.h:99
@ PGPA_TAG_HASH_JOIN
Definition pgpa_ast.h:86
@ PGPA_TAG_SEMIJOIN_UNIQUE
Definition pgpa_ast.h:98
@ PGPA_TAG_JOIN_ORDER
Definition pgpa_ast.h:89
@ PGPA_TAG_TID_SCAN
Definition pgpa_ast.h:100
@ PGPA_TAG_FOREIGN_JOIN
Definition pgpa_ast.h:83
@ PGPA_TAG_NESTED_LOOP_PLAIN
Definition pgpa_ast.h:94
@ PGPA_TAG_MERGE_JOIN_MATERIALIZE
Definition pgpa_ast.h:90
@ PGPA_TARGET_UNORDERED_LIST
Definition pgpa_ast.h:29
@ PGPA_TARGET_IDENTIFIER
Definition pgpa_ast.h:27
pgpa_itm_type
Definition pgpa_ast.h:141
@ PGPA_ITM_EQUAL
Definition pgpa_ast.h:142
@ PGPA_ITM_DISJOINT
Definition pgpa_ast.h:146
@ PGPA_ITM_KEYS_ARE_SUBSET
Definition pgpa_ast.h:143
@ PGPA_ITM_TARGETS_ARE_SUBSET
Definition pgpa_ast.h:144
@ PGPA_ITM_INTERSECTING
Definition pgpa_ast.h:145
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