PostgreSQL Source Code git master
Loading...
Searching...
No Matches
pgpa_walker.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * pgpa_walker.c
4 * Main entrypoints for analyzing a plan to generate an advice string
5 *
6 * Copyright (c) 2016-2026, PostgreSQL Global Development Group
7 *
8 * contrib/pg_plan_advice/pgpa_walker.c
9 *
10 *-------------------------------------------------------------------------
11 */
12#include "postgres.h"
13
14#include "pgpa_join.h"
15#include "pgpa_scan.h"
16#include "pgpa_walker.h"
17
18#include "nodes/plannodes.h"
19#include "parser/parsetree.h"
20#include "utils/lsyscache.h"
21
29
32 Plan *plan);
33
37 List *rtable);
38
42 pgpa_advice_target *target,
43 bool toplevel);
47 pgpa_advice_target *target);
49 pgpa_scan_strategy strategy,
50 Bitmapset *relids);
52 Plan *plan);
55 Bitmapset *relids);
57 pgpa_join_strategy strategy,
58 Bitmapset *relids);
60 Bitmapset *relids);
61
62/*
63 * Top-level entrypoint for the plan tree walk.
64 *
65 * Populates walker based on a traversal of the Plan trees in pstmt.
66 *
67 * sj_unique_rels is a list of pgpa_sj_unique_rel objects, one for each
68 * relation we considered making unique as part of semijoin planning.
69 */
70void
72 List *sj_unique_rels)
73{
74 ListCell *lc;
77
78 /* Initialization. */
80 walker->pstmt = pstmt;
81
82 /* Walk the main plan tree. */
83 pgpa_walk_recursively(walker, pstmt->planTree, false, NULL, NIL, false);
84
85 /* Main plan tree walk won't reach subplans, so walk those. */
86 foreach(lc, pstmt->subplans)
87 {
88 Plan *plan = lfirst(lc);
89
90 if (plan != NULL)
91 pgpa_walk_recursively(walker, plan, false, NULL, NIL, false);
92 }
93
94 /* Adjust RTIs from sj_unique_rels for the flattened range table. */
95 foreach_ptr(pgpa_sj_unique_rel, ur, sj_unique_rels)
96 {
97 int rtindex = -1;
98 int rtoffset = 0;
99 bool dummy = false;
100 Bitmapset *relids = NULL;
101
102 /* If this is a subplan, find the range table offset. */
103 if (ur->plan_name != NULL)
104 {
106 {
107 if (strcmp(ur->plan_name, rtinfo->plan_name) == 0)
108 {
109 rtoffset = rtinfo->rtoffset;
110 dummy = rtinfo->dummy;
111 break;
112 }
113 }
114
115 if (rtoffset == 0)
116 elog(ERROR, "no rtoffset for plan %s", ur->plan_name);
117 }
118
119 /* If this entry pertains to a dummy subquery, ignore it. */
120 if (dummy)
121 continue;
122
123 /* Offset each entry from the original set. */
124 while ((rtindex = bms_next_member(ur->relids, rtindex)) >= 0)
125 relids = bms_add_member(relids, rtindex + rtoffset);
126
127 /* Store the resulting set. */
129 }
130
131 /*
132 * Remove any non-unique semijoin query features for which making the rel
133 * unique wasn't considered.
134 */
136 walker->query_features[PGPAQF_SEMIJOIN_NON_UNIQUE])
137 {
138 if (list_member(sj_unique_rtis, qf->relids))
140 }
142
143 /*
144 * If we find any cases where analysis of the Plan tree shows that the
145 * semijoin was made unique but this possibility was never observed to be
146 * considered during planning, then we have a bug somewhere.
147 */
149 walker->query_features[PGPAQF_SEMIJOIN_UNIQUE])
150 {
151 if (!list_member(sj_unique_rtis, qf->relids))
152 {
154
156 outBitmapset(&buf, qf->relids);
157 elog(ERROR,
158 "unique semijoin found for relids %s but not observed during planning",
159 buf.data);
160 }
161 }
162
163 /*
164 * It's possible for a Gather or Gather Merge query feature to find no
165 * RTIs when partitionwise aggregation is in use. We shouldn't emit
166 * something like GATHER_MERGE(()), so instead emit nothing. This means
167 * that we won't advise either GATHER or GATHER_MERGE or NO_GATHER in such
168 * cases, which might be something we want to improve in the future.
169 *
170 * (Should the Partial Aggregates in such a case be created in an
171 * UPPERREL_GROUP_AGG with a non-empty relid set? Right now that doesn't
172 * happen, but it seems like it would make life easier for us if it did.)
173 */
174 for (int t = 0; t < NUM_PGPA_QF_TYPES; ++t)
175 {
176 List *query_features = NIL;
177
178 foreach_ptr(pgpa_query_feature, qf, walker->query_features[t])
179 {
180 if (qf->relids != NULL)
181 query_features = lappend(query_features, qf);
182 else
184 }
185
186 walker->query_features[t] = query_features;
187 }
188}
189
190/*
191 * Main workhorse for the plan tree walk.
192 *
193 * If within_join_problem is true, we encountered a join at some higher level
194 * of the tree walk and haven't yet descended out of the portion of the plan
195 * tree that is part of that same join problem. We're no longer in the same
196 * join problem if (1) we cross into a different subquery or (2) we descend
197 * through an Append or MergeAppend node, below which any further joins would
198 * be partitionwise joins planned separately from the outer join problem.
199 *
200 * If join_unroller != NULL, the join unroller code expects us to find a join
201 * that should be unrolled into that object. This implies that we're within a
202 * join problem, but the reverse is not true: when we've traversed all the
203 * joins but are still looking for the scan that is the leaf of the join tree,
204 * join_unroller will be NULL but within_join_problem will be true.
205 *
206 * Each element of active_query_features corresponds to some item of advice
207 * that needs to enumerate all the relations it affects. We add RTIs we find
208 * during tree traversal to each of these query features.
209 *
210 * If beneath_any_gather == true, some higher level of the tree traversal found
211 * a Gather or Gather Merge node.
212 */
213static void
219{
222 bool join_unroller_toplevel = false;
223 ListCell *lc;
226
228
229 /*
230 * Check the future_query_features list to see whether this was previously
231 * identified as a plan node that needs to be treated as a query feature.
232 * We must do this before handling elided nodes, because if there's an
233 * elided node associated with a future query feature, the RTIs associated
234 * with the elided node should be the only ones attributed to the query
235 * feature.
236 */
237 foreach_ptr(pgpa_query_feature, qf, walker->future_query_features)
238 {
239 if (qf->plan == plan)
240 {
243 walker->future_query_features =
244 list_delete_ptr(walker->future_query_features, qf);
245 break;
246 }
247 }
248
249 /*
250 * Find all elided nodes for this Plan node.
251 */
252 foreach_node(ElidedNode, n, walker->pstmt->elidedNodes)
253 {
254 if (n->plan_node_id == plan->plan_node_id)
256 }
257
258 /* If we found any elided_nodes, handle them. */
259 if (elided_nodes != NIL)
260 {
263
264 /*
265 * RTIs for the final -- and thus logically uppermost -- elided node
266 * should be collected for query features passed down by the caller.
267 * However, elided nodes act as barriers to query features, which
268 * means that (1) the remaining elided nodes, if any, should be
269 * ignored for purposes of query features and (2) the list of active
270 * query features should be reset to empty so that we do not add RTIs
271 * from the plan node that is logically beneath the elided node to the
272 * query features passed down from the caller.
273 */
277 walker->pstmt->rtable));
279
280 /*
281 * If we're within a join problem, the join_unroller is responsible
282 * for building the scan for the final elided node, so throw it out.
283 */
286
287 /* Build scans for all (or the remaining) elided nodes. */
289 {
290 (void) pgpa_build_scan(walker, plan, elided_node,
292 }
293
294 /*
295 * If there were any elided nodes, then everything beneath those nodes
296 * is not part of the same join problem.
297 *
298 * In more detail, if an Append or MergeAppend was elided, then a
299 * partitionwise join was chosen and only a single child survived; if
300 * a SubqueryScan was elided, the subquery was planned without
301 * flattening it into the parent.
302 */
303 within_join_problem = false;
305 }
306
307 /*
308 * If this is a Gather or Gather Merge node, directly add it to the list
309 * of currently-active query features. We must do this after handling
310 * elided nodes, since the Gather or Gather Merge node occurs logically
311 * beneath any associated elided nodes.
312 *
313 * Exception: We disregard any single_copy Gather nodes. These are created
314 * by debug_parallel_query, and having them affect the plan advice is
315 * counterproductive, as the result will be to advise the use of a real
316 * Gather node, rather than a single copy one.
317 */
318 if (IsA(plan, Gather) && !((Gather *) plan)->single_copy)
319 {
323 beneath_any_gather = true;
324 }
325 else if (IsA(plan, GatherMerge))
326 {
330 beneath_any_gather = true;
331 }
332
333 /*
334 * If we're within a join problem, the join unroller is responsible for
335 * building any required scan for this node. If not, we do it here.
336 */
339
340 /*
341 * If this join needs to be unrolled but there's no join unroller already
342 * available, create one.
343 */
345 {
348 within_join_problem = true;
349 }
350
351 /*
352 * If this join is to be unrolled, pgpa_unroll_join() will return the join
353 * unroller object that should be passed down when we recurse into the
354 * outer and inner sides of the plan.
355 */
356 if (join_unroller != NULL)
359
360 /* Add RTIs from the plan node to all active query features. */
362
363 /*
364 * Recurse into the outer and inner subtrees.
365 *
366 * As an exception, if this is a ForeignScan, don't recurse. postgres_fdw
367 * sometimes stores an EPQ recheck plan in plan->lefttree, but that's
368 * going to mention the same set of relations as the ForeignScan itself,
369 * and we have no way to emit advice targeting the EPQ case vs. the
370 * non-EPQ case. Moreover, it's not entirely clear what other FDWs might
371 * do with the left and right subtrees. Maybe some better handling is
372 * needed here, but for now, we just punt.
373 */
374 if (!IsA(plan, ForeignScan))
375 {
376 if (plan->lefttree != NULL)
380 if (plan->righttree != NULL)
384 }
385
386 /*
387 * If we created a join unroller up above, then it's also our join to use
388 * it to build the final pgpa_unrolled_join, and to destroy the object.
389 */
391 {
393
395 walker->toplevel_unrolled_joins =
396 lappend(walker->toplevel_unrolled_joins, ujoin);
399 }
400
401 /*
402 * Some plan types can have additional children. Nodes like Append that
403 * can have any number of children store them in a List; a SubqueryScan
404 * just has a field for a single additional Plan.
405 */
406 switch (nodeTag(plan))
407 {
408 case T_Append:
409 {
410 Append *aplan = (Append *) plan;
411
413 }
414 break;
415 case T_MergeAppend:
416 {
418
420 }
421 break;
422 case T_BitmapAnd:
423 extraplans = ((BitmapAnd *) plan)->bitmapplans;
424 break;
425 case T_BitmapOr:
426 extraplans = ((BitmapOr *) plan)->bitmapplans;
427 break;
428 case T_SubqueryScan:
429
430 /*
431 * We don't pass down active_query_features across here, because
432 * those are specific to a subquery level.
433 */
436 break;
437 case T_CustomScan:
438 extraplans = ((CustomScan *) plan)->custom_plans;
439 break;
440 default:
441 break;
442 }
443
444 /* If we found a list of extra children, iterate over it. */
445 foreach(lc, extraplans)
446 {
447 Plan *subplan = lfirst(lc);
448
449 pgpa_walk_recursively(walker, subplan, false, NULL, NIL,
451 }
452}
453
454/*
455 * Perform final processing of a newly-constructed pgpa_unrolled_join. This
456 * only needs to be called for toplevel pgpa_unrolled_join objects, since it
457 * recurses to sub-joins as needed.
458 *
459 * Our goal is to add the set of inner relids to the relevant join_strategies
460 * list, and to do the same for any sub-joins. To that end, the return value
461 * is the set of relids found beneath the join, but it is expected that
462 * the toplevel caller will ignore this.
463 */
464static Bitmapset *
467{
468 Bitmapset *all_relids = bms_copy(ujoin->outer.scan->relids);
469
470 /* If this fails, we didn't unroll properly. */
471 Assert(ujoin->outer.unrolled_join == NULL);
472
473 for (int k = 0; k < ujoin->ninner; ++k)
474 {
475 pgpa_join_member *member = &ujoin->inner[k];
476 Bitmapset *relids;
477
478 if (member->unrolled_join != NULL)
480 member->unrolled_join);
481 else
482 {
483 Assert(member->scan != NULL);
484 relids = member->scan->relids;
485 }
486 walker->join_strategies[ujoin->strategy[k]] =
487 lappend(walker->join_strategies[ujoin->strategy[k]], relids);
489 }
490
491 return all_relids;
492}
493
494/*
495 * Arrange for the given plan node to be treated as a query feature when the
496 * tree walk reaches it.
497 *
498 * Make sure to only use this for nodes that the tree walk can't have reached
499 * yet!
500 */
501void
504{
506
507 walker->future_query_features =
508 lappend(walker->future_query_features, qf);
509}
510
511/*
512 * Return the last of any elided nodes associated with this plan node ID.
513 *
514 * The last elided node is the one that would have been uppermost in the plan
515 * tree had it not been removed during setrefs processing.
516 */
519{
520 ElidedNode *elided_node = NULL;
521
523 {
524 if (n->plan_node_id == plan->plan_node_id)
525 elided_node = n;
526 }
527
528 return elided_node;
529}
530
531/*
532 * Certain plan nodes can refer to a set of RTIs. Extract and return the set.
533 */
534Bitmapset *
536{
537 if (IsA(plan, Result))
538 return ((Result *) plan)->relids;
539 else if (IsA(plan, ForeignScan))
540 return ((ForeignScan *) plan)->fs_relids;
541 else if (IsA(plan, Append))
542 return ((Append *) plan)->apprelids;
543 else if (IsA(plan, MergeAppend))
544 return ((MergeAppend *) plan)->apprelids;
545
546 return NULL;
547}
548
549/*
550 * Extract the scanned RTI from a plan node.
551 *
552 * Returns 0 if there isn't one.
553 */
554Index
556{
557 switch (nodeTag(plan))
558 {
559 case T_SeqScan:
560 case T_SampleScan:
561 case T_BitmapHeapScan:
562 case T_TidScan:
563 case T_TidRangeScan:
564 case T_SubqueryScan:
565 case T_FunctionScan:
566 case T_TableFuncScan:
567 case T_ValuesScan:
568 case T_CteScan:
570 case T_WorkTableScan:
571 case T_ForeignScan:
572 case T_CustomScan:
573 case T_IndexScan:
574 case T_IndexOnlyScan:
575 return ((Scan *) plan)->scanrelid;
576 default:
577 return 0;
578 }
579}
580
581/*
582 * Construct a new Bitmapset containing non-RTE_JOIN members of 'relids'.
583 */
584Bitmapset *
586{
587 int rti = -1;
588 Bitmapset *result = NULL;
589
590 while ((rti = bms_next_member(relids, rti)) >= 0)
591 {
592 RangeTblEntry *rte = rt_fetch(rti, rtable);
593
594 if (rte->rtekind != RTE_JOIN)
595 result = bms_add_member(result, rti);
596 }
597
598 return result;
599}
600
601/*
602 * Create a pgpa_query_feature and add it to the list of all query features
603 * for this plan.
604 */
605static pgpa_query_feature *
608{
610
611 qf->type = type;
612 qf->plan = plan;
613
614 walker->query_features[qf->type] =
615 lappend(walker->query_features[qf->type], qf);
616
617 return qf;
618}
619
620/*
621 * Add a single RTI to each active query feature.
622 */
623static void
631
632/*
633 * Add a set of RTIs to each active query feature.
634 */
635static void
643
644/*
645 * Add RTIs directly contained in a plan node to each active query feature,
646 * but filter out any join RTIs, since advice doesn't mention those.
647 */
648static void
650{
651 Bitmapset *relids;
652 Index rti;
653
654 if ((relids = pgpa_relids(plan)) != NULL)
655 {
656 relids = pgpa_filter_out_join_relids(relids, rtable);
658 }
659 else if ((rti = pgpa_scanrelid(plan)) != 0)
661}
662
663/*
664 * If we generated plan advice using the provided walker object and array
665 * of identifiers, would we generate the specified tag/target combination?
666 *
667 * If yes, the plan conforms to the advice; if no, it does not. Note that
668 * we have no way of knowing whether the planner was forced to emit a plan
669 * that conformed to the advice or just happened to do so.
670 */
671bool
675 pgpa_advice_target *target)
676{
677 Index rtable_length = list_length(walker->pstmt->rtable);
678 Bitmapset *relids = NULL;
679
680 if (tag == PGPA_TAG_JOIN_ORDER)
681 {
682 foreach_ptr(pgpa_unrolled_join, ujoin, walker->toplevel_unrolled_joins)
683 {
685 rt_identifiers, target, true))
686 return true;
687 }
688
689 return false;
690 }
691
692 if (target->ttype == PGPA_TARGET_IDENTIFIER)
693 {
694 Index rti;
695
697 &target->rid);
698 if (rti == 0)
699 return false;
700 relids = bms_make_singleton(rti);
701 }
702 else
703 {
706 {
707 Index rti;
708
712 &child_target->rid);
713 if (rti == 0)
714 return false;
715 relids = bms_add_member(relids, rti);
716 }
717 }
718
719 switch (tag)
720 {
722 /* should have been handled above */
724 break;
728 relids) != NULL;
732 relids) != NULL;
734 {
735 pgpa_scan *scan;
736
738 relids);
739 if (scan == NULL)
740 return false;
741
743 }
745 {
746 pgpa_scan *scan;
747
749 relids);
750 if (scan == NULL)
751 return false;
752
754 }
758 relids) != NULL;
762 relids) != NULL;
766 relids) != NULL;
767 case PGPA_TAG_GATHER:
770 relids);
774 relids);
778 relids);
782 relids);
786 relids);
790 relids);
794 relids);
798 relids);
802 relids);
806 relids);
809 }
810
811 /* should not get here */
812 return false;
813}
814
815/*
816 * Does the index target match the Plan?
817 *
818 * Should only be called when we know that itarget mandates an Index Scan or
819 * Index Only Scan and this corresponds to the type of Plan. Here, our job is
820 * just to check whether it's the same index.
821 */
822static bool
824{
825 Oid indexoid = InvalidOid;
826
827 /* Retrieve the index OID from the plan. */
828 if (IsA(plan, IndexScan))
829 indexoid = ((IndexScan *) plan)->indexid;
830 else if (IsA(plan, IndexOnlyScan))
831 indexoid = ((IndexOnlyScan *) plan)->indexid;
832 else
833 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(plan));
834
835 /* Check whether schema name matches, if specified in index target. */
836 if (itarget->indnamespace != NULL)
837 {
838 Oid nspoid = get_rel_namespace(indexoid);
839 char *relnamespace = get_namespace_name_or_temp(nspoid);
840
841 if (strcmp(itarget->indnamespace, relnamespace) != 0)
842 return false;
843 }
844
845 /* Check whether relation name matches. */
846 return (strcmp(itarget->indname, get_rel_name(indexoid)) == 0);
847}
848
849/*
850 * Does an unrolled join match the join order specified by an advice target?
851 */
852static bool
856 pgpa_advice_target *target,
857 bool toplevel)
858{
859 int nchildren = list_length(target->children);
860
862
863 /* At toplevel, we allow a prefix match. */
864 if (toplevel)
865 {
866 if (nchildren > ujoin->ninner + 1)
867 return false;
868 }
869 else
870 {
871 if (nchildren != ujoin->ninner + 1)
872 return false;
873 }
874
875 /* Outermost rel must match. */
879 linitial(target->children)))
880 return false;
881
882 /* Each inner rel must match. */
883 for (int n = 0; n < nchildren - 1; ++n)
884 {
886
891 return false;
892 }
893
894 return true;
895}
896
897/*
898 * Does one member of an unrolled join match an advice target?
899 */
900static bool
904 pgpa_advice_target *target)
905{
906 Bitmapset *relids = NULL;
907
908 if (member->unrolled_join != NULL)
909 {
910 if (target->ttype != PGPA_TARGET_ORDERED_LIST)
911 return false;
915 target,
916 false);
917 }
918
919 Assert(member->scan != NULL);
920 switch (target->ttype)
921 {
923 /* Could only match an unrolled join */
924 return false;
925
927 {
929 {
930 Index rti;
931
934 &child_target->rid);
935 if (rti == 0)
936 return false;
937 relids = bms_add_member(relids, rti);
938 }
939 break;
940 }
941
943 {
944 Index rti;
945
948 &target->rid);
949 if (rti == 0)
950 return false;
951 relids = bms_make_singleton(rti);
952 break;
953 }
954 }
955
956 return bms_equal(member->scan->relids, relids);
957}
958
959/*
960 * Find the scan where the walker says that the given scan strategy should be
961 * used for the given relid set, if one exists.
962 *
963 * Returns the pgpa_scan object, or NULL if none was found.
964 */
965static pgpa_scan *
967 pgpa_scan_strategy strategy,
968 Bitmapset *relids)
969{
970 List *scans = walker->scans[strategy];
971
972 foreach_ptr(pgpa_scan, scan, scans)
973 {
974 if (bms_equal(scan->relids, relids))
975 return scan;
976 }
977
978 return NULL;
979}
980
981/*
982 * Does this walker say that the given query feature applies to the given
983 * relid set?
984 */
985static bool
988 Bitmapset *relids)
989{
990 List *query_features = walker->query_features[type];
991
992 foreach_ptr(pgpa_query_feature, qf, query_features)
993 {
994 if (bms_equal(qf->relids, relids))
995 return true;
996 }
997
998 return false;
999}
1000
1001/*
1002 * Does the walker say that the given join strategy should be used for the
1003 * given relid set?
1004 */
1005static bool
1007 pgpa_join_strategy strategy,
1008 Bitmapset *relids)
1009{
1010 List *join_strategies = walker->join_strategies[strategy];
1011
1012 foreach_ptr(Bitmapset, jsrelids, join_strategies)
1013 {
1014 if (bms_equal(jsrelids, relids))
1015 return true;
1016 }
1017
1018 return false;
1019}
1020
1021/*
1022 * Does the walker say that the given relids should be marked as NO_GATHER?
1023 */
1024static bool
1026 Bitmapset *relids)
1027{
1028 return bms_is_subset(relids, walker->no_gather_scans);
1029}
Bitmapset * bms_make_singleton(int x)
Definition bitmapset.c:216
bool bms_equal(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:142
int bms_next_member(const Bitmapset *a, int prevbit)
Definition bitmapset.c:1290
bool bms_is_subset(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:412
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition bitmapset.c:799
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:901
Bitmapset * bms_copy(const Bitmapset *a)
Definition bitmapset.c:122
#define Assert(condition)
Definition c.h:927
#define pg_unreachable()
Definition c.h:361
unsigned int Index
Definition c.h:682
#define ERROR
Definition elog.h:39
#define elog(elevel,...)
Definition elog.h:226
#define palloc0_object(type)
Definition fe_memutils.h:75
List * list_delete_ptr(List *list, void *datum)
Definition list.c:872
List * lappend(List *list, void *datum)
Definition list.c:339
List * list_copy(const List *oldlist)
Definition list.c:1573
List * list_truncate(List *list, int new_size)
Definition list.c:631
bool list_member(const List *list, const void *datum)
Definition list.c:661
char * get_rel_name(Oid relid)
Definition lsyscache.c:2146
Oid get_rel_namespace(Oid relid)
Definition lsyscache.c:2170
char * get_namespace_name_or_temp(Oid nspid)
Definition lsyscache.c:3610
#define IsA(nodeptr, _type_)
Definition nodes.h:164
#define nodeTag(nodeptr)
Definition nodes.h:139
void outBitmapset(StringInfo str, const Bitmapset *bms)
Definition outfuncs.c:331
@ RTE_JOIN
#define rt_fetch(rangetable_index, rangetable)
Definition parsetree.h:31
#define lfirst(lc)
Definition pg_list.h:172
static int list_length(const List *l)
Definition pg_list.h:152
#define NIL
Definition pg_list.h:68
#define foreach_ptr(type, var, lst)
Definition pg_list.h:469
static void * list_nth(const List *list, int n)
Definition pg_list.h:299
#define linitial(l)
Definition pg_list.h:178
#define foreach_node(type, var, lst)
Definition pg_list.h:496
#define plan(x)
Definition pg_regress.c:161
static char buf[DEFAULT_XLOG_SEG_SIZE]
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_TARGET_ORDERED_LIST
Definition pgpa_ast.h:28
Index pgpa_compute_rti_from_identifier(int rtable_length, pgpa_identifier *rt_identifiers, pgpa_identifier *rid)
pgpa_unrolled_join * pgpa_build_unrolled_join(pgpa_plan_walker_context *walker, pgpa_join_unroller *join_unroller)
Definition pgpa_join.c:230
void pgpa_unroll_join(pgpa_plan_walker_context *walker, Plan *plan, bool beneath_any_gather, pgpa_join_unroller *join_unroller, pgpa_join_unroller **outer_join_unroller, pgpa_join_unroller **inner_join_unroller)
Definition pgpa_join.c:105
void pgpa_destroy_join_unroller(pgpa_join_unroller *join_unroller)
Definition pgpa_join.c:295
pgpa_join_unroller * pgpa_create_join_unroller(void)
Definition pgpa_join.c:64
pgpa_join_strategy
Definition pgpa_join.h:28
@ JSTRAT_MERGE_JOIN_PLAIN
Definition pgpa_join.h:29
@ JSTRAT_NESTED_LOOP_MATERIALIZE
Definition pgpa_join.h:32
@ JSTRAT_NESTED_LOOP_MEMOIZE
Definition pgpa_join.h:33
@ JSTRAT_HASH_JOIN
Definition pgpa_join.h:34
@ JSTRAT_NESTED_LOOP_PLAIN
Definition pgpa_join.h:31
@ JSTRAT_MERGE_JOIN_MATERIALIZE
Definition pgpa_join.h:30
static bool pgpa_is_join(Plan *plan)
Definition pgpa_join.h:90
pgpa_scan * pgpa_build_scan(pgpa_plan_walker_context *walker, Plan *plan, ElidedNode *elided_node, bool beneath_any_gather, bool within_join_problem)
Definition pgpa_scan.c:44
pgpa_scan_strategy
Definition pgpa_scan.h:56
@ PGPA_SCAN_SEQ
Definition pgpa_scan.h:58
@ PGPA_SCAN_INDEX
Definition pgpa_scan.h:61
@ PGPA_SCAN_INDEX_ONLY
Definition pgpa_scan.h:62
@ PGPA_SCAN_BITMAP_HEAP
Definition pgpa_scan.h:59
@ PGPA_SCAN_FOREIGN
Definition pgpa_scan.h:60
@ PGPA_SCAN_TID
Definition pgpa_scan.h:64
@ PGPA_SCAN_PARTITIONWISE
Definition pgpa_scan.h:63
static bool pgpa_walker_contains_feature(pgpa_plan_walker_context *walker, pgpa_qf_type type, Bitmapset *relids)
Bitmapset * pgpa_filter_out_join_relids(Bitmapset *relids, List *rtable)
static bool pgpa_walker_join_order_matches_member(pgpa_join_member *member, Index rtable_length, pgpa_identifier *rt_identifiers, pgpa_advice_target *target)
Bitmapset * pgpa_relids(Plan *plan)
ElidedNode * pgpa_last_elided_node(PlannedStmt *pstmt, Plan *plan)
static pgpa_scan * pgpa_walker_find_scan(pgpa_plan_walker_context *walker, pgpa_scan_strategy strategy, Bitmapset *relids)
bool pgpa_walker_would_advise(pgpa_plan_walker_context *walker, pgpa_identifier *rt_identifiers, pgpa_advice_tag_type tag, pgpa_advice_target *target)
static bool pgpa_walker_contains_no_gather(pgpa_plan_walker_context *walker, Bitmapset *relids)
void pgpa_plan_walker(pgpa_plan_walker_context *walker, PlannedStmt *pstmt, List *sj_unique_rels)
Definition pgpa_walker.c:71
static bool pgpa_walker_contains_join(pgpa_plan_walker_context *walker, pgpa_join_strategy strategy, Bitmapset *relids)
static pgpa_query_feature * pgpa_add_feature(pgpa_plan_walker_context *walker, pgpa_qf_type type, Plan *plan)
static Bitmapset * pgpa_process_unrolled_join(pgpa_plan_walker_context *walker, pgpa_unrolled_join *ujoin)
static bool pgpa_walker_index_target_matches_plan(pgpa_index_target *itarget, Plan *plan)
static void pgpa_walk_recursively(pgpa_plan_walker_context *walker, Plan *plan, bool within_join_problem, pgpa_join_unroller *join_unroller, List *active_query_features, bool beneath_any_gather)
void pgpa_add_future_feature(pgpa_plan_walker_context *walker, pgpa_qf_type type, Plan *plan)
static bool pgpa_walker_join_order_matches(pgpa_unrolled_join *ujoin, Index rtable_length, pgpa_identifier *rt_identifiers, pgpa_advice_target *target, bool toplevel)
static void pgpa_qf_add_rtis(List *active_query_features, Bitmapset *relids)
static void pgpa_qf_add_plan_rtis(List *active_query_features, Plan *plan, List *rtable)
Index pgpa_scanrelid(Plan *plan)
static void pgpa_qf_add_rti(List *active_query_features, Index rti)
pgpa_qf_type
Definition pgpa_walker.h:62
@ PGPAQF_GATHER
Definition pgpa_walker.h:63
@ PGPAQF_GATHER_MERGE
Definition pgpa_walker.h:64
@ PGPAQF_SEMIJOIN_UNIQUE
Definition pgpa_walker.h:66
@ PGPAQF_SEMIJOIN_NON_UNIQUE
Definition pgpa_walker.h:65
#define NUM_PGPA_QF_TYPES
Definition pgpa_walker.h:70
#define InvalidOid
unsigned int Oid
static int fb(int x)
void initStringInfo(StringInfo str)
Definition stringinfo.c:97
List * appendplans
Definition plannodes.h:407
Definition pg_list.h:54
List * mergeplans
Definition plannodes.h:442
struct Plan * planTree
Definition plannodes.h:101
List * elidedNodes
Definition plannodes.h:156
List * subplans
Definition plannodes.h:132
List * subrtinfos
Definition plannodes.h:135
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
char * indnamespace
Definition pgpa_ast.h:38
struct pgpa_scan * scan
Definition pgpa_join.h:60
pgpa_unrolled_join * unrolled_join
Definition pgpa_join.h:61
Plan * plan
Definition pgpa_scan.h:75
Bitmapset * relids
Definition pgpa_scan.h:77
const char * type