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_planner.h"
16#include "pgpa_scan.h"
17#include "pgpa_walker.h"
18
19#include "access/tsmapi.h"
20#include "nodes/plannodes.h"
21#include "parser/parsetree.h"
22#include "utils/lsyscache.h"
23
31
34 Plan *plan);
35
39 List *rtable);
40
44 pgpa_advice_target *target,
45 bool toplevel);
49 pgpa_advice_target *target);
51 pgpa_scan_strategy strategy,
52 Bitmapset *relids);
54 Plan *plan);
57 Bitmapset *relids);
59 pgpa_join_strategy strategy,
60 Bitmapset *relids);
62 Bitmapset *relids);
64 List *proots,
67
68/*
69 * Top-level entrypoint for the plan tree walk.
70 *
71 * Populates walker based on a traversal of the Plan trees in pstmt.
72 *
73 * proots is the list of pgpa_planner_info objects that were generated
74 * during planning.
75 */
76void
78 List *proots)
79{
80 ListCell *lc;
85
86 /* Initialization. */
88 walker->pstmt = pstmt;
89
90 /* Walk the main plan tree. */
91 pgpa_walk_recursively(walker, pstmt->planTree, false, NULL, NIL, false);
92
93 /* Main plan tree walk won't reach subplans, so walk those. */
94 foreach(lc, pstmt->subplans)
95 {
96 Plan *plan = lfirst(lc);
97
98 if (plan != NULL)
99 pgpa_walk_recursively(walker, plan, false, NULL, NIL, false);
100 }
101
102 /* Adjust RTIs from sj_unique_rels for the flattened range table. */
104 {
105 /* If there are no sj_unique_rels for this proot, we can skip it. */
106 if (proot->sj_unique_rels == NIL)
107 continue;
108
109 /* If this is a subplan, find the range table offset. */
110 if (!proot->has_rtoffset)
111 elog(ERROR, "no rtoffset for plan %s", proot->plan_name);
112
113 /* Offset each relid set by the proot's rtoffset. */
114 foreach_node(Bitmapset, relids, proot->sj_unique_rels)
115 {
116 int rtindex = -1;
118
119 while ((rtindex = bms_next_member(relids, rtindex)) >= 0)
121 rtindex + proot->rtoffset);
122
124 }
125 }
126
127 /*
128 * Remove any non-unique semijoin query features for which making the rel
129 * unique wasn't considered.
130 */
132 walker->query_features[PGPAQF_SEMIJOIN_NON_UNIQUE])
133 {
134 if (list_member(sj_unique_rtis, qf->relids))
136 }
138
139 /*
140 * If we find any cases where analysis of the Plan tree shows that the
141 * semijoin was made unique but this possibility was never observed to be
142 * considered during planning, then we have a bug somewhere.
143 */
145 walker->query_features[PGPAQF_SEMIJOIN_UNIQUE])
146 {
147 if (!list_member(sj_unique_rtis, qf->relids))
148 {
150
152 outBitmapset(&buf, qf->relids);
153 elog(ERROR,
154 "unique semijoin found for relids %s but not observed during planning",
155 buf.data);
156 }
157 }
158
159 /*
160 * It's possible for a Gather or Gather Merge query feature to find no
161 * RTIs when partitionwise aggregation is in use. We shouldn't emit
162 * something like GATHER_MERGE(()), so instead emit nothing. This means
163 * that we won't advise either GATHER or GATHER_MERGE or NO_GATHER in such
164 * cases, which might be something we want to improve in the future.
165 *
166 * (Should the Partial Aggregates in such a case be created in an
167 * UPPERREL_GROUP_AGG with a non-empty relid set? Right now that doesn't
168 * happen, but it seems like it would make life easier for us if it did.)
169 */
170 for (int t = 0; t < NUM_PGPA_QF_TYPES; ++t)
171 {
172 List *query_features = NIL;
173
174 foreach_ptr(pgpa_query_feature, qf, walker->query_features[t])
175 {
176 if (qf->relids != NULL)
177 query_features = lappend(query_features, qf);
178 else
180 }
181
182 walker->query_features[t] = query_features;
183 }
184
185 /* Classify alternative subplans. */
188
189 /*
190 * Figure out which of the discarded alternatives have a non-discarded
191 * alternative. Those are the ones for which we want to emit DO_NOT_SCAN
192 * advice. (If every alternative was discarded, then there's no point.)
193 */
195 {
196 bool some_alternative_chosen = false;
197
199 {
200 if (strings_equal_or_both_null(discarded_proot->alternative_plan_name,
201 chosen_proot->alternative_plan_name))
202 {
204 break;
205 }
206 }
207
209 {
210 for (int rti = 1; rti <= discarded_proot->rid_array_size; rti++)
211 {
212 pgpa_identifier *rid = &discarded_proot->rid_array[rti - 1];
213
214 if (rid->alias_name != NULL)
215 walker->do_not_scan_identifiers =
216 lappend(walker->do_not_scan_identifiers, rid);
217 }
218 }
219 }
220}
221
222/*
223 * Main workhorse for the plan tree walk.
224 *
225 * If within_join_problem is true, we encountered a join at some higher level
226 * of the tree walk and haven't yet descended out of the portion of the plan
227 * tree that is part of that same join problem. We're no longer in the same
228 * join problem if (1) we cross into a different subquery or (2) we descend
229 * through an Append or MergeAppend node, below which any further joins would
230 * be partitionwise joins planned separately from the outer join problem.
231 *
232 * If join_unroller != NULL, the join unroller code expects us to find a join
233 * that should be unrolled into that object. This implies that we're within a
234 * join problem, but the reverse is not true: when we've traversed all the
235 * joins but are still looking for the scan that is the leaf of the join tree,
236 * join_unroller will be NULL but within_join_problem will be true.
237 *
238 * Each element of active_query_features corresponds to some item of advice
239 * that needs to enumerate all the relations it affects. We add RTIs we find
240 * during tree traversal to each of these query features.
241 *
242 * If beneath_any_gather == true, some higher level of the tree traversal found
243 * a Gather or Gather Merge node.
244 */
245static void
251{
254 bool join_unroller_toplevel = false;
255 ListCell *lc;
258
260
261 /*
262 * Check the future_query_features list to see whether this was previously
263 * identified as a plan node that needs to be treated as a query feature.
264 * We must do this before handling elided nodes, because if there's an
265 * elided node associated with a future query feature, the RTIs associated
266 * with the elided node should be the only ones attributed to the query
267 * feature.
268 */
269 foreach_ptr(pgpa_query_feature, qf, walker->future_query_features)
270 {
271 if (qf->plan == plan)
272 {
275 walker->future_query_features =
276 list_delete_ptr(walker->future_query_features, qf);
277 break;
278 }
279 }
280
281 /*
282 * Find all elided nodes for this Plan node.
283 */
284 foreach_node(ElidedNode, n, walker->pstmt->elidedNodes)
285 {
286 if (n->plan_node_id == plan->plan_node_id)
288 }
289
290 /* If we found any elided_nodes, handle them. */
291 if (elided_nodes != NIL)
292 {
295
296 /*
297 * RTIs for the final -- and thus logically uppermost -- elided node
298 * should be collected for query features passed down by the caller.
299 * However, elided nodes act as barriers to query features, which
300 * means that (1) the remaining elided nodes, if any, should be
301 * ignored for purposes of query features and (2) the list of active
302 * query features should be reset to empty so that we do not add RTIs
303 * from the plan node that is logically beneath the elided node to the
304 * query features passed down from the caller.
305 */
309 walker->pstmt->rtable));
311
312 /*
313 * If we're within a join problem, the join_unroller is responsible
314 * for building the scan for the final elided node, so throw it out.
315 */
318
319 /* Build scans for all (or the remaining) elided nodes. */
321 {
322 (void) pgpa_build_scan(walker, plan, elided_node,
324 }
325
326 /*
327 * If there were any elided nodes, then everything beneath those nodes
328 * is not part of the same join problem.
329 *
330 * In more detail, if an Append or MergeAppend was elided, then a
331 * partitionwise join was chosen and only a single child survived; if
332 * a SubqueryScan was elided, the subquery was planned without
333 * flattening it into the parent.
334 */
335 within_join_problem = false;
337 }
338
339 /*
340 * If this is a Gather or Gather Merge node, directly add it to the list
341 * of currently-active query features. We must do this after handling
342 * elided nodes, since the Gather or Gather Merge node occurs logically
343 * beneath any associated elided nodes.
344 *
345 * Exception: We disregard any single_copy Gather nodes. These are created
346 * by debug_parallel_query, and having them affect the plan advice is
347 * counterproductive, as the result will be to advise the use of a real
348 * Gather node, rather than a single copy one.
349 */
350 if (IsA(plan, Gather) && !((Gather *) plan)->single_copy)
351 {
355 beneath_any_gather = true;
356 }
357 else if (IsA(plan, GatherMerge))
358 {
362 beneath_any_gather = true;
363 }
364
365 /*
366 * If we're within a join problem, the join unroller is responsible for
367 * building any required scan for this node. If not, we do it here.
368 */
371
372 /*
373 * If this join needs to be unrolled but there's no join unroller already
374 * available, create one.
375 */
377 {
380 within_join_problem = true;
381 }
382
383 /*
384 * If this join is to be unrolled, pgpa_unroll_join() will return the join
385 * unroller object that should be passed down when we recurse into the
386 * outer and inner sides of the plan.
387 */
388 if (join_unroller != NULL)
391
392 /* Add RTIs from the plan node to all active query features. */
394
395 /*
396 * Recurse into the outer and inner subtrees.
397 *
398 * As an exception, if this is a ForeignScan, don't recurse. postgres_fdw
399 * sometimes stores an EPQ recheck plan in plan->lefttree, but that's
400 * going to mention the same set of relations as the ForeignScan itself,
401 * and we have no way to emit advice targeting the EPQ case vs. the
402 * non-EPQ case. Moreover, it's not entirely clear what other FDWs might
403 * do with the left and right subtrees. Maybe some better handling is
404 * needed here, but for now, we just punt.
405 */
406 if (!IsA(plan, ForeignScan))
407 {
408 if (plan->lefttree != NULL)
412 if (plan->righttree != NULL)
416 }
417
418 /*
419 * If we created a join unroller up above, then it's also our join to use
420 * it to build the final pgpa_unrolled_join, and to destroy the object.
421 */
423 {
425
427 walker->toplevel_unrolled_joins =
428 lappend(walker->toplevel_unrolled_joins, ujoin);
431 }
432
433 /*
434 * Some plan types can have additional children. Nodes like Append that
435 * can have any number of children store them in a List; a SubqueryScan
436 * just has a field for a single additional Plan.
437 */
438 switch (nodeTag(plan))
439 {
440 case T_Append:
441 {
442 Append *aplan = (Append *) plan;
443
445 }
446 break;
447 case T_MergeAppend:
448 {
450
452 }
453 break;
454 case T_BitmapAnd:
455 extraplans = ((BitmapAnd *) plan)->bitmapplans;
456 break;
457 case T_BitmapOr:
458 extraplans = ((BitmapOr *) plan)->bitmapplans;
459 break;
460 case T_SubqueryScan:
461
462 /*
463 * We don't pass down active_query_features across here, because
464 * those are specific to a subquery level.
465 */
468 break;
469 case T_CustomScan:
470 extraplans = ((CustomScan *) plan)->custom_plans;
471 break;
472 default:
473 break;
474 }
475
476 /* If we found a list of extra children, iterate over it. */
477 foreach(lc, extraplans)
478 {
479 Plan *subplan = lfirst(lc);
480
481 pgpa_walk_recursively(walker, subplan, false, NULL, NIL,
483 }
484}
485
486/*
487 * Perform final processing of a newly-constructed pgpa_unrolled_join. This
488 * only needs to be called for toplevel pgpa_unrolled_join objects, since it
489 * recurses to sub-joins as needed.
490 *
491 * Our goal is to add the set of inner relids to the relevant join_strategies
492 * list, and to do the same for any sub-joins. To that end, the return value
493 * is the set of relids found beneath the join, but it is expected that
494 * the toplevel caller will ignore this.
495 */
496static Bitmapset *
499{
500 Bitmapset *all_relids = bms_copy(ujoin->outer.scan->relids);
501
502 /* If this fails, we didn't unroll properly. */
503 Assert(ujoin->outer.unrolled_join == NULL);
504
505 for (int k = 0; k < ujoin->ninner; ++k)
506 {
507 pgpa_join_member *member = &ujoin->inner[k];
508 Bitmapset *relids;
509
510 if (member->unrolled_join != NULL)
512 member->unrolled_join);
513 else
514 {
515 Assert(member->scan != NULL);
516 relids = member->scan->relids;
517 }
518 walker->join_strategies[ujoin->strategy[k]] =
519 lappend(walker->join_strategies[ujoin->strategy[k]], relids);
521 }
522
523 return all_relids;
524}
525
526/*
527 * Arrange for the given plan node to be treated as a query feature when the
528 * tree walk reaches it.
529 *
530 * Make sure to only use this for nodes that the tree walk can't have reached
531 * yet!
532 */
533void
536{
538
539 walker->future_query_features =
540 lappend(walker->future_query_features, qf);
541}
542
543/*
544 * Return the last of any elided nodes associated with this plan node ID.
545 *
546 * The last elided node is the one that would have been uppermost in the plan
547 * tree had it not been removed during setrefs processing.
548 */
551{
552 ElidedNode *elided_node = NULL;
553
555 {
556 if (n->plan_node_id == plan->plan_node_id)
557 elided_node = n;
558 }
559
560 return elided_node;
561}
562
563/*
564 * Certain plan nodes can refer to a set of RTIs. Extract and return the set.
565 */
566Bitmapset *
568{
569 if (IsA(plan, Result))
570 return ((Result *) plan)->relids;
571 else if (IsA(plan, ForeignScan))
572 return ((ForeignScan *) plan)->fs_relids;
573 else if (IsA(plan, Append))
574 return ((Append *) plan)->apprelids;
575 else if (IsA(plan, MergeAppend))
576 return ((MergeAppend *) plan)->apprelids;
577
578 return NULL;
579}
580
581/*
582 * Extract the scanned RTI from a plan node.
583 *
584 * Returns 0 if there isn't one.
585 */
586Index
588{
589 switch (nodeTag(plan))
590 {
591 case T_SeqScan:
592 case T_SampleScan:
593 case T_BitmapHeapScan:
594 case T_TidScan:
595 case T_TidRangeScan:
596 case T_SubqueryScan:
597 case T_FunctionScan:
598 case T_TableFuncScan:
599 case T_ValuesScan:
600 case T_CteScan:
602 case T_WorkTableScan:
603 case T_ForeignScan:
604 case T_CustomScan:
605 case T_IndexScan:
606 case T_IndexOnlyScan:
607 return ((Scan *) plan)->scanrelid;
608 default:
609 return 0;
610 }
611}
612
613/*
614 * Check whether a plan node is a Material node that should be treated as
615 * a scan. Currently, this only happens when set_tablesample_rel_pathlist
616 * inserts a Material node to protect a SampleScan that uses a non-repeatable
617 * tablesample method.
618 *
619 * (Most Material nodes we're likely to encounter are actually part of the
620 * join strategy: nested loops and merge joins can choose to materialize the
621 * inner sides of the join. The cases identified here are the rare
622 * exceptions.)
623 */
624bool
626{
627 Plan *child;
630
631 if (!IsA(plan, Material))
632 return false;
633 child = plan->lefttree;
634 if (child == NULL || !IsA(child, SampleScan))
635 return false;
636 sscan = (SampleScan *) child;
637 tsm = GetTsmRoutine(sscan->tablesample->tsmhandler);
638 return !tsm->repeatable_across_scans;
639}
640
641/*
642 * Construct a new Bitmapset containing non-RTE_JOIN members of 'relids'.
643 */
644Bitmapset *
646{
647 int rti = -1;
649
650 while ((rti = bms_next_member(relids, rti)) >= 0)
651 {
652 RangeTblEntry *rte = rt_fetch(rti, rtable);
653
654 if (rte->rtekind != RTE_JOIN)
656 }
657
658 return result;
659}
660
661/*
662 * Create a pgpa_query_feature and add it to the list of all query features
663 * for this plan.
664 */
665static pgpa_query_feature *
668{
670
671 qf->type = type;
672 qf->plan = plan;
673
674 walker->query_features[qf->type] =
675 lappend(walker->query_features[qf->type], qf);
676
677 return qf;
678}
679
680/*
681 * Add a single RTI to each active query feature.
682 */
683static void
691
692/*
693 * Add a set of RTIs to each active query feature.
694 */
695static void
703
704/*
705 * Add RTIs directly contained in a plan node to each active query feature,
706 * but filter out any join RTIs, since advice doesn't mention those.
707 */
708static void
710{
711 Bitmapset *relids;
712 Index rti;
713
714 if ((relids = pgpa_relids(plan)) != NULL)
715 {
716 relids = pgpa_filter_out_join_relids(relids, rtable);
718 }
719 else if ((rti = pgpa_scanrelid(plan)) != 0)
721}
722
723/*
724 * If we generated plan advice using the provided walker object and array
725 * of identifiers, would we generate the specified tag/target combination?
726 *
727 * If yes, the plan conforms to the advice; if no, it does not. Note that
728 * we have no way of knowing whether the planner was forced to emit a plan
729 * that conformed to the advice or just happened to do so.
730 */
731bool
735 pgpa_advice_target *target)
736{
737 Index rtable_length = list_length(walker->pstmt->rtable);
738 Bitmapset *relids = NULL;
739
740 if (tag == PGPA_TAG_JOIN_ORDER)
741 {
742 foreach_ptr(pgpa_unrolled_join, ujoin, walker->toplevel_unrolled_joins)
743 {
745 rt_identifiers, target, true))
746 return true;
747 }
748
749 return false;
750 }
751
752 /*
753 * DO_NOT_SCAN advice targets rels that may not be in the flat range table
754 * (e.g. MinMaxAgg losers), so pgpa_compute_rti_from_identifier won't work
755 * here. Instead, check directly against the do_not_scan_identifiers list.
756 */
757 if (tag == PGPA_TAG_DO_NOT_SCAN)
758 {
759 if (target->ttype != PGPA_TARGET_IDENTIFIER)
760 return false;
761 foreach_ptr(pgpa_identifier, rid, walker->do_not_scan_identifiers)
762 {
763 if (strcmp(rid->alias_name, target->rid.alias_name) == 0 &&
764 rid->occurrence == target->rid.occurrence &&
765 strings_equal_or_both_null(rid->partnsp,
766 target->rid.partnsp) &&
767 strings_equal_or_both_null(rid->partrel,
768 target->rid.partrel) &&
769 strings_equal_or_both_null(rid->plan_name,
770 target->rid.plan_name))
771 return true;
772 }
773 return false;
774 }
775
776 if (target->ttype == PGPA_TARGET_IDENTIFIER)
777 {
778 Index rti;
779
781 &target->rid);
782 if (rti == 0)
783 return false;
784 relids = bms_make_singleton(rti);
785 }
786 else
787 {
790 {
791 Index rti;
792
796 &child_target->rid);
797 if (rti == 0)
798 return false;
799 relids = bms_add_member(relids, rti);
800 }
801 }
802
803 switch (tag)
804 {
806 /* should have been handled above */
808 break;
810 /* should have been handled above */
812 break;
816 relids) != NULL;
820 relids) != NULL;
822 {
823 pgpa_scan *scan;
824
826 relids);
827 if (scan == NULL)
828 return false;
829
831 }
833 {
834 pgpa_scan *scan;
835
837 relids);
838 if (scan == NULL)
839 return false;
840
842 }
846 relids) != NULL;
850 relids) != NULL;
854 relids) != NULL;
855 case PGPA_TAG_GATHER:
858 relids);
862 relids);
866 relids);
870 relids);
874 relids);
878 relids);
882 relids);
886 relids);
890 relids);
894 relids);
897 }
898
899 /* should not get here */
900 return false;
901}
902
903/*
904 * Does the index target match the Plan?
905 *
906 * Should only be called when we know that itarget mandates an Index Scan or
907 * Index Only Scan and this corresponds to the type of Plan. Here, our job is
908 * just to check whether it's the same index.
909 */
910static bool
912{
913 Oid indexoid = InvalidOid;
914
915 /* Retrieve the index OID from the plan. */
916 if (IsA(plan, IndexScan))
917 indexoid = ((IndexScan *) plan)->indexid;
918 else if (IsA(plan, IndexOnlyScan))
919 indexoid = ((IndexOnlyScan *) plan)->indexid;
920 else
921 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(plan));
922
923 /* Check whether schema name matches, if specified in index target. */
924 if (itarget->indnamespace != NULL)
925 {
926 Oid nspoid = get_rel_namespace(indexoid);
927 char *relnamespace = get_namespace_name_or_temp(nspoid);
928
929 if (strcmp(itarget->indnamespace, relnamespace) != 0)
930 return false;
931 }
932
933 /* Check whether relation name matches. */
934 return (strcmp(itarget->indname, get_rel_name(indexoid)) == 0);
935}
936
937/*
938 * Does an unrolled join match the join order specified by an advice target?
939 */
940static bool
944 pgpa_advice_target *target,
945 bool toplevel)
946{
947 int nchildren = list_length(target->children);
948
950
951 /* At toplevel, we allow a prefix match. */
952 if (toplevel)
953 {
954 if (nchildren > ujoin->ninner + 1)
955 return false;
956 }
957 else
958 {
959 if (nchildren != ujoin->ninner + 1)
960 return false;
961 }
962
963 /* Outermost rel must match. */
967 linitial(target->children)))
968 return false;
969
970 /* Each inner rel must match. */
971 for (int n = 0; n < nchildren - 1; ++n)
972 {
974
979 return false;
980 }
981
982 return true;
983}
984
985/*
986 * Does one member of an unrolled join match an advice target?
987 */
988static bool
992 pgpa_advice_target *target)
993{
994 Bitmapset *relids = NULL;
995
996 if (member->unrolled_join != NULL)
997 {
998 if (target->ttype != PGPA_TARGET_ORDERED_LIST)
999 return false;
1003 target,
1004 false);
1005 }
1006
1007 Assert(member->scan != NULL);
1008 switch (target->ttype)
1009 {
1011 /* Could only match an unrolled join */
1012 return false;
1013
1015 {
1017 {
1018 Index rti;
1019
1022 &child_target->rid);
1023 if (rti == 0)
1024 return false;
1025 relids = bms_add_member(relids, rti);
1026 }
1027 break;
1028 }
1029
1031 {
1032 Index rti;
1033
1036 &target->rid);
1037 if (rti == 0)
1038 return false;
1039 relids = bms_make_singleton(rti);
1040 break;
1041 }
1042 }
1043
1044 return bms_equal(member->scan->relids, relids);
1045}
1046
1047/*
1048 * Find the scan where the walker says that the given scan strategy should be
1049 * used for the given relid set, if one exists.
1050 *
1051 * Returns the pgpa_scan object, or NULL if none was found.
1052 */
1053static pgpa_scan *
1055 pgpa_scan_strategy strategy,
1056 Bitmapset *relids)
1057{
1058 List *scans = walker->scans[strategy];
1059
1060 foreach_ptr(pgpa_scan, scan, scans)
1061 {
1062 if (bms_equal(scan->relids, relids))
1063 return scan;
1064 }
1065
1066 return NULL;
1067}
1068
1069/*
1070 * Does this walker say that the given query feature applies to the given
1071 * relid set?
1072 */
1073static bool
1076 Bitmapset *relids)
1077{
1078 List *query_features = walker->query_features[type];
1079
1080 foreach_ptr(pgpa_query_feature, qf, query_features)
1081 {
1082 if (bms_equal(qf->relids, relids))
1083 return true;
1084 }
1085
1086 return false;
1087}
1088
1089/*
1090 * Does the walker say that the given join strategy should be used for the
1091 * given relid set?
1092 */
1093static bool
1095 pgpa_join_strategy strategy,
1096 Bitmapset *relids)
1097{
1098 List *join_strategies = walker->join_strategies[strategy];
1099
1100 foreach_ptr(Bitmapset, jsrelids, join_strategies)
1101 {
1102 if (bms_equal(jsrelids, relids))
1103 return true;
1104 }
1105
1106 return false;
1107}
1108
1109/*
1110 * Does the walker say that the given relids should be marked as NO_GATHER?
1111 */
1112static bool
1114 Bitmapset *relids)
1115{
1116 return bms_is_subset(relids, walker->no_gather_scans);
1117}
1118
1119/*
1120 * Classify alternative subplans as chosen or discarded.
1121 */
1122static void
1124 List *proots,
1127{
1129
1130 /* Initialize both output lists to empty. */
1131 *chosen_proots = NIL;
1133
1134 /* Collect all scan RTIs. */
1135 for (int s = 0; s < NUM_PGPA_SCAN_STRATEGY; s++)
1136 foreach_ptr(pgpa_scan, scan, walker->scans[s])
1138
1139 /* Now classify each subplan. */
1141 {
1142 bool chosen = false;
1143
1144 /*
1145 * We're only interested in classifying subplans for which there are
1146 * alternatives.
1147 */
1148 if (!proot->is_alternative_plan)
1149 continue;
1150
1151 /*
1152 * A subplan has been chosen if any of its scan RTIs appear in the
1153 * final plan. This cannot be the case if it has no RT offset.
1154 */
1155 if (proot->has_rtoffset)
1156 {
1157 for (int rti = 1; rti <= proot->rid_array_size; rti++)
1158 {
1159 if (proot->rid_array[rti - 1].alias_name != NULL &&
1160 bms_is_member(proot->rtoffset + rti, all_scan_rtis))
1161 {
1162 chosen = true;
1163 break;
1164 }
1165 }
1166 }
1167
1168 /* Add it to the correct list. */
1169 if (chosen)
1171 else
1173 }
1174}
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
bool bms_is_member(int x, const Bitmapset *a)
Definition bitmapset.c:510
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:943
#define pg_unreachable()
Definition c.h:367
unsigned int Index
Definition c.h:698
uint32 result
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#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:2121
Oid get_rel_namespace(Oid relid)
Definition lsyscache.c:2145
char * get_namespace_name_or_temp(Oid nspid)
Definition lsyscache.c:3585
#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:501
static void * list_nth(const List *list, int n)
Definition pg_list.h:331
#define linitial(l)
Definition pg_list.h:178
#define foreach_node(type, var, lst)
Definition pg_list.h:528
#define plan(x)
Definition pg_regress.c:164
static char buf[DEFAULT_XLOG_SEG_SIZE]
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_TARGET_ORDERED_LIST
Definition pgpa_ast.h:28
Index pgpa_compute_rti_from_identifier(int rtable_length, pgpa_identifier *rt_identifiers, pgpa_identifier *rid)
static bool strings_equal_or_both_null(const char *a, const char *b)
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
#define NUM_PGPA_SCAN_STRATEGY
Definition pgpa_scan.h:68
static void pgpa_classify_alternative_subplans(pgpa_plan_walker_context *walker, List *proots, List **chosen_proots, List **discarded_proots)
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)
void pgpa_plan_walker(pgpa_plan_walker_context *walker, PlannedStmt *pstmt, List *proots)
Definition pgpa_walker.c:77
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)
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)
bool pgpa_is_scan_level_materialize(Plan *plan)
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:44
@ PGPAQF_GATHER
Definition pgpa_walker.h:45
@ PGPAQF_GATHER_MERGE
Definition pgpa_walker.h:46
@ PGPAQF_SEMIJOIN_UNIQUE
Definition pgpa_walker.h:48
@ PGPAQF_SEMIJOIN_NON_UNIQUE
Definition pgpa_walker.h:47
#define NUM_PGPA_QF_TYPES
Definition pgpa_walker.h:52
#define InvalidOid
unsigned int Oid
static int fb(int x)
void initStringInfo(StringInfo str)
Definition stringinfo.c:97
List * appendplans
Definition plannodes.h:409
Definition pg_list.h:54
List * mergeplans
Definition plannodes.h:444
struct Plan * lefttree
Definition plannodes.h:239
struct Plan * planTree
Definition plannodes.h:99
List * elidedNodes
Definition plannodes.h:156
List * subplans
Definition plannodes.h:129
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 * partnsp
const char * partrel
const char * plan_name
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
TsmRoutine * GetTsmRoutine(Oid tsmhandler)
Definition tablesample.c:27
const char * type