PostgreSQL Source Code git master
Loading...
Searching...
No Matches
pgpa_trove.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * pgpa_trove.c
4 * All of the advice given for a particular query, appropriately
5 * organized for convenient access.
6 *
7 * This name comes from the English expression "trove of advice", which
8 * means a collection of wisdom. This slightly unusual term is chosen
9 * partly because it seems to fit and partly because it's not presently
10 * used for anything else, making it easy to grep. Note that, while we
11 * don't know whether the provided advice is actually wise, it's not our
12 * job to question the user's choices.
13 *
14 * The goal of this module is to make it easy to locate the specific
15 * bits of advice that pertain to any given part of a query, or to
16 * determine that there are none.
17 *
18 * Copyright (c) 2016-2026, PostgreSQL Global Development Group
19 *
20 * contrib/pg_plan_advice/pgpa_trove.c
21 *
22 *-------------------------------------------------------------------------
23 */
24#include "postgres.h"
25
26#include "pgpa_trove.h"
27
29
30/*
31 * An advice trove is organized into a series of "slices", each of which
32 * contains information about one topic e.g. scan methods. Each slice consists
33 * of an array of trove entries plus a hash table that we can use to determine
34 * which ones are relevant to a particular part of the query.
35 */
43
44/*
45 * Scan advice is stored into 'scan'; join advice is stored into 'join'; and
46 * advice that can apply to both cases is stored into 'rel'. This lets callers
47 * ask just for what's relevant. These slices correspond to the possible values
48 * of pgpa_trove_lookup_type.
49 */
56
57/*
58 * We're going to build a hash table to allow clients of this module to find
59 * relevant advice for a given part of the query quickly. However, we're going
60 * to use only three of the five key fields as hash keys. There are two reasons
61 * for this.
62 *
63 * First, it's allowable to set partition_schema to NULL to match a partition
64 * with the correct name in any schema.
65 *
66 * Second, we expect the "occurrence" and "partition_schema" portions of the
67 * relation identifiers to be mostly uninteresting. Most of the time, the
68 * occurrence field will be 1 and the partition_schema values will all be the
69 * same. Even when there is some variation, the absolute number of entries
70 * that have the same values for all three of these key fields should be
71 * quite small.
72 */
73typedef struct
74{
75 const char *alias_name;
76 const char *partition_name;
77 const char *plan_name;
79
86
88
89static inline bool
91{
92 if (strcmp(a.alias_name, b.alias_name) != 0)
93 return false;
94
95 if (!strings_equal_or_both_null(a.partition_name, b.partition_name))
96 return false;
97
98 if (!strings_equal_or_both_null(a.plan_name, b.plan_name))
99 return false;
100
101 return true;
102}
103
104#define SH_PREFIX pgpa_trove_entry
105#define SH_ELEMENT_TYPE pgpa_trove_entry_element
106#define SH_KEY_TYPE pgpa_trove_entry_key
107#define SH_KEY key
108#define SH_HASH_KEY(tb, key) pgpa_trove_entry_hash_key(key)
109#define SH_EQUAL(tb, a, b) pgpa_trove_entry_compare_key(a, b)
110#define SH_SCOPE static inline
111#define SH_DECLARE
112#define SH_DEFINE
113#include "lib/simplehash.h"
114
118 pgpa_advice_target *target);
120 pgpa_advice_target *target,
121 int index);
123 pgpa_identifier *rid);
124
125/*
126 * Build a trove of advice from a list of advice items.
127 *
128 * Caller can obtain a list of advice items to pass to this function by
129 * calling pgpa_parse().
130 */
133{
135
137 pgpa_init_trove_slice(&trove->rel);
139
141 {
142 switch (item->tag)
143 {
145 {
146 pgpa_advice_target *target;
147
148 /*
149 * For most advice types, each element in the top-level
150 * list is a separate target, but it's most convenient to
151 * regard the entirety of a JOIN_ORDER specification as a
152 * single target. Since it wasn't represented that way
153 * during parsing, build a surrogate object now.
154 */
157 target->children = item->targets;
158
160 item->tag, target);
161 }
162 break;
163
170
171 /*
172 * Scan advice.
173 */
174 foreach_ptr(pgpa_advice_target, target, item->targets)
175 {
176 /*
177 * For now, all of our scan types target single relations,
178 * but in the future this might not be true, e.g. a custom
179 * scan could replace a join.
180 */
181 Assert(target->ttype == PGPA_TARGET_IDENTIFIER);
183 item->tag, target);
184 }
185 break;
186
196
197 /*
198 * Join strategy advice.
199 */
200 foreach_ptr(pgpa_advice_target, target, item->targets)
201 {
203 item->tag, target);
204 }
205 break;
206
208 case PGPA_TAG_GATHER:
211
212 /*
213 * Advice about a RelOptInfo relevant to both scans and joins.
214 */
215 foreach_ptr(pgpa_advice_target, target, item->targets)
216 {
218 item->tag, target);
219 }
220 break;
221 }
222 }
223
224 return trove;
225}
226
227/*
228 * Search a trove of advice for relevant entries.
229 *
230 * All parameters are input parameters except for *result, which is an output
231 * parameter used to return results to the caller.
232 */
233void
235 int nrids, pgpa_identifier *rids, pgpa_trove_result *result)
236{
238 Bitmapset *indexes;
239
240 Assert(nrids > 0);
241
243 tslice = &trove->scan;
244 else if (type == PGPA_TROVE_LOOKUP_JOIN)
245 tslice = &trove->join;
246 else
247 tslice = &trove->rel;
248
249 indexes = pgpa_trove_slice_lookup(tslice, &rids[0]);
250 for (int i = 1; i < nrids; ++i)
251 {
253
254 /*
255 * If the caller is asking about two relations that aren't part of the
256 * same subquery, they've messed up.
257 */
258 Assert(strings_equal_or_both_null(rids[0].plan_name,
259 rids[i].plan_name));
260
262 indexes = bms_union(indexes, other_indexes);
263 }
264
265 result->entries = tslice->entries;
266 result->indexes = indexes;
267}
268
269/*
270 * Return all entries in a trove slice to the caller.
271 *
272 * The first two arguments are input arguments, and the remainder are output
273 * arguments.
274 */
275void
277 pgpa_trove_entry **entries, int *nentries)
278{
280
282 tslice = &trove->scan;
283 else if (type == PGPA_TROVE_LOOKUP_JOIN)
284 tslice = &trove->join;
285 else
286 tslice = &trove->rel;
287
288 *entries = tslice->entries;
289 *nentries = tslice->nused;
290}
291
292/*
293 * Convert a trove entry to an item of plan advice that would produce it.
294 */
295char *
297{
299
302
303 /* JOIN_ORDER tags are transformed by pgpa_build_trove; undo that here */
304 if (entry->tag != PGPA_TAG_JOIN_ORDER)
306 else
308
310
311 if (entry->target->itarget != NULL)
312 {
315 }
316
317 if (entry->tag != PGPA_TAG_JOIN_ORDER)
319
320 return buf.data;
321}
322
323/*
324 * Set PGPA_TE_* flags on a set of trove entries.
325 */
326void
327pgpa_trove_set_flags(pgpa_trove_entry *entries, Bitmapset *indexes, int flags)
328{
329 int i = -1;
330
331 while ((i = bms_next_member(indexes, i)) >= 0)
332 {
333 pgpa_trove_entry *entry = &entries[i];
334
335 entry->flags |= flags;
336 }
337}
338
339/*
340 * Append a string representation of the specified PGPA_TE_* flags to the
341 * given StringInfo.
342 */
343void
345{
346 if ((flags & PGPA_TE_MATCH_FULL) != 0)
347 {
348 Assert((flags & PGPA_TE_MATCH_PARTIAL) != 0);
349 appendStringInfo(buf, "matched");
350 }
351 else if ((flags & PGPA_TE_MATCH_PARTIAL) != 0)
352 appendStringInfo(buf, "partially matched");
353 else
354 appendStringInfo(buf, "not matched");
355 if ((flags & PGPA_TE_INAPPLICABLE) != 0)
356 appendStringInfo(buf, ", inapplicable");
357 if ((flags & PGPA_TE_CONFLICTING) != 0)
358 appendStringInfo(buf, ", conflicting");
359 if ((flags & PGPA_TE_FAILED) != 0)
360 appendStringInfo(buf, ", failed");
361}
362
363/*
364 * Add a new advice target to an existing pgpa_trove_slice object.
365 */
366static void
369 pgpa_advice_target *target)
370{
371 pgpa_trove_entry *entry;
372
373 if (tslice->nused >= tslice->nallocated)
374 {
375 int new_allocated;
376
377 new_allocated = tslice->nallocated * 2;
378 tslice->entries = repalloc_array(tslice->entries, pgpa_trove_entry,
380 tslice->nallocated = new_allocated;
381 }
382
383 entry = &tslice->entries[tslice->nused];
384 entry->tag = tag;
385 entry->target = target;
386 entry->flags = 0;
387
388 pgpa_trove_add_to_hash(tslice->hash, target, tslice->nused);
389
390 tslice->nused++;
391}
392
393/*
394 * Update the hash table for a newly-added advice target.
395 */
396static void
398 int index)
399{
402 bool found;
403
404 /* For non-identifiers, add entries for all descendants. */
405 if (target->ttype != PGPA_TARGET_IDENTIFIER)
406 {
408 {
410 }
411 return;
412 }
413
414 /* Sanity checks. */
415 Assert(target->rid.occurrence > 0);
416 Assert(target->rid.alias_name != NULL);
417
418 /* Add an entry for this relation identifier. */
419 key.alias_name = target->rid.alias_name;
420 key.partition_name = target->rid.partrel;
421 key.plan_name = target->rid.plan_name;
422 element = pgpa_trove_entry_insert(hash, key, &found);
423 if (!found)
424 element->indexes = NULL;
425 element->indexes = bms_add_member(element->indexes, index);
426}
427
428/*
429 * Create and initialize a new pgpa_trove_slice object.
430 */
431static void
433{
434 /*
435 * In an ideal world, we'll make tslice->nallocated big enough that the
436 * array and hash table will be large enough to contain the number of
437 * advice items in this trove slice, but a generous default value is not
438 * good for performance, because pgpa_init_trove_slice() has to zero an
439 * amount of memory proportional to tslice->nallocated. Hence, we keep the
440 * starting value quite small, on the theory that advice strings will
441 * often be relatively short.
442 */
443 tslice->nallocated = 16;
444 tslice->nused = 0;
445 tslice->entries = palloc_array(pgpa_trove_entry, tslice->nallocated);
447 tslice->nallocated, NULL);
448}
449
450/*
451 * Fast hash function for a key consisting of alias_name, partition_name,
452 * and plan_name.
453 */
454static uint32
456{
458 int sp_len;
459
460 fasthash_init(&hs, 0);
461
462 /* alias_name may not be NULL */
463 sp_len = fasthash_accum_cstring(&hs, key.alias_name);
464
465 /* partition_name and plan_name, however, can be NULL */
466 if (key.partition_name != NULL)
467 sp_len += fasthash_accum_cstring(&hs, key.partition_name);
468 if (key.plan_name != NULL)
469 sp_len += fasthash_accum_cstring(&hs, key.plan_name);
470
471 /*
472 * hashfn_unstable.h recommends using string length as tweak. It's not
473 * clear to me what to do if there are multiple strings, so for now I'm
474 * just using the total of all of the lengths.
475 */
476 return fasthash_final32(&hs, sp_len);
477}
478
479/*
480 * Look for matching entries.
481 */
482static Bitmapset *
484{
487 Bitmapset *result = NULL;
488
489 Assert(rid->occurrence >= 1);
490
491 key.alias_name = rid->alias_name;
492 key.partition_name = rid->partrel;
493 key.plan_name = rid->plan_name;
494
496
497 if (element != NULL)
498 {
499 int i = -1;
500
501 while ((i = bms_next_member(element->indexes, i)) >= 0)
502 {
503 pgpa_trove_entry *entry = &tslice->entries[i];
504
505 /*
506 * We know that this target or one of its descendants matches the
507 * identifier on the three key fields above, but we don't know
508 * which descendant or whether the occurrence and schema also
509 * match.
510 */
511 if (pgpa_identifier_matches_target(rid, entry->target))
512 result = bms_add_member(result, i);
513 }
514 }
515
516 return result;
517}
int bms_next_member(const Bitmapset *a, int prevbit)
Definition bitmapset.c:1290
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition bitmapset.c:799
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:251
#define Assert(condition)
Definition c.h:943
uint32_t uint32
Definition c.h:624
#define palloc_object(type)
Definition fe_memutils.h:74
#define repalloc_array(pointer, type, count)
Definition fe_memutils.h:78
#define palloc_array(type, count)
Definition fe_memutils.h:76
#define palloc0_object(type)
Definition fe_memutils.h:75
static size_t fasthash_accum_cstring(fasthash_state *hs, const char *str)
static uint32 fasthash_final32(fasthash_state *hs, uint64 tweak)
static void fasthash_init(fasthash_state *hs, uint64 seed)
int b
Definition isn.c:74
int a
Definition isn.c:73
int i
Definition isn.c:77
MemoryContext CurrentMemoryContext
Definition mcxt.c:160
#define foreach_ptr(type, var, lst)
Definition pg_list.h:501
static char buf[DEFAULT_XLOG_SEG_SIZE]
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
void pgpa_format_index_target(StringInfo str, pgpa_index_target *itarget)
Definition pgpa_ast.c:206
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_IDENTIFIER
Definition pgpa_ast.h:27
@ PGPA_TARGET_ORDERED_LIST
Definition pgpa_ast.h:28
static bool strings_equal_or_both_null(const char *a, const char *b)
static bool pgpa_trove_entry_compare_key(pgpa_trove_entry_key a, pgpa_trove_entry_key b)
Definition pgpa_trove.c:90
static Bitmapset * pgpa_trove_slice_lookup(pgpa_trove_slice *tslice, pgpa_identifier *rid)
Definition pgpa_trove.c:483
void pgpa_trove_set_flags(pgpa_trove_entry *entries, Bitmapset *indexes, int flags)
Definition pgpa_trove.c:327
static void pgpa_init_trove_slice(pgpa_trove_slice *tslice)
Definition pgpa_trove.c:432
pgpa_trove * pgpa_build_trove(List *advice_items)
Definition pgpa_trove.c:132
void pgpa_trove_append_flags(StringInfo buf, int flags)
Definition pgpa_trove.c:344
static void pgpa_trove_add_to_slice(pgpa_trove_slice *tslice, pgpa_advice_tag_type tag, pgpa_advice_target *target)
Definition pgpa_trove.c:367
void pgpa_trove_lookup_all(pgpa_trove *trove, pgpa_trove_lookup_type type, pgpa_trove_entry **entries, int *nentries)
Definition pgpa_trove.c:276
char * pgpa_cstring_trove_entry(pgpa_trove_entry *entry)
Definition pgpa_trove.c:296
static void pgpa_trove_add_to_hash(pgpa_trove_entry_hash *hash, pgpa_advice_target *target, int index)
Definition pgpa_trove.c:397
void pgpa_trove_lookup(pgpa_trove *trove, pgpa_trove_lookup_type type, int nrids, pgpa_identifier *rids, pgpa_trove_result *result)
Definition pgpa_trove.c:234
static uint32 pgpa_trove_entry_hash_key(pgpa_trove_entry_key key)
Definition pgpa_trove.c:455
pgpa_trove_lookup_type
Definition pgpa_trove.h:82
@ PGPA_TROVE_LOOKUP_SCAN
Definition pgpa_trove.h:85
@ PGPA_TROVE_LOOKUP_JOIN
Definition pgpa_trove.h:83
#define PGPA_TE_INAPPLICABLE
Definition pgpa_trove.h:48
#define PGPA_TE_MATCH_FULL
Definition pgpa_trove.h:47
#define PGPA_TE_MATCH_PARTIAL
Definition pgpa_trove.h:46
#define PGPA_TE_CONFLICTING
Definition pgpa_trove.h:49
#define PGPA_TE_FAILED
Definition pgpa_trove.h:50
static int fb(int x)
static chr element(struct vars *v, const chr *startp, const chr *endp)
static unsigned hash(unsigned *uv, int n)
Definition rege_dfa.c:715
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition stringinfo.c:145
void appendStringInfoChar(StringInfo str, char ch)
Definition stringinfo.c:242
void initStringInfo(StringInfo str)
Definition stringinfo.c:97
Definition pg_list.h:54
Definition type.h:96
pgpa_identifier rid
Definition pgpa_ast.h:58
pgpa_target_type ttype
Definition pgpa_ast.h:49
pgpa_index_target * itarget
Definition pgpa_ast.h:64
const char * alias_name
const char * partrel
const char * plan_name
Definition pgpa_trove.c:81
int status
Definition pgpa_trove.c:83
Bitmapset * indexes
Definition pgpa_trove.c:84
pgpa_trove_entry_key key
Definition pgpa_trove.c:82
Definition pgpa_trove.c:74
const char * partition_name
Definition pgpa_trove.c:76
const char * alias_name
Definition pgpa_trove.c:75
const char * plan_name
Definition pgpa_trove.c:77
Definition pgpa_trove.h:57
pgpa_advice_target * target
Definition pgpa_trove.h:59
pgpa_advice_tag_type tag
Definition pgpa_trove.h:58
int flags
Definition pgpa_trove.h:60
pgpa_trove_entry * entries
Definition pgpa_trove.h:95
Bitmapset * indexes
Definition pgpa_trove.h:96
struct pgpa_trove_entry_hash * hash
Definition pgpa_trove.c:41
pgpa_trove_entry * entries
Definition pgpa_trove.c:40
unsigned nallocated
Definition pgpa_trove.c:38
pgpa_trove_slice rel
Definition pgpa_trove.c:53
pgpa_trove_slice join
Definition pgpa_trove.c:52
pgpa_trove_slice scan
Definition pgpa_trove.c:54
const char * type