LCOV - code coverage report
Current view: top level - contrib/pg_plan_advice - pgpa_walker.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 93.0 % 415 386
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 20 20
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 82.1 % 368 302

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * pgpa_walker.c
       4                 :             :  *        Plan tree iteration
       5                 :             :  *
       6                 :             :  * Copyright (c) 2016-2025, 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                 :             : 
      22                 :             : static void pgpa_walk_recursively(pgpa_plan_walker_context *walker, Plan *plan,
      23                 :             :                                                                   bool within_join_problem,
      24                 :             :                                                                   pgpa_join_unroller *join_unroller,
      25                 :             :                                                                   List *active_query_features,
      26                 :             :                                                                   bool beneath_any_gather);
      27                 :             : static Bitmapset *pgpa_process_unrolled_join(pgpa_plan_walker_context *walker,
      28                 :             :                                                                                          pgpa_unrolled_join *ujoin);
      29                 :             : 
      30                 :             : static pgpa_query_feature *pgpa_add_feature(pgpa_plan_walker_context *walker,
      31                 :             :                                                                                         pgpa_qf_type type,
      32                 :             :                                                                                         Plan *plan);
      33                 :             : 
      34                 :             : static void pgpa_qf_add_rti(List *active_query_features, Index rti);
      35                 :             : static void pgpa_qf_add_rtis(List *active_query_features, Bitmapset *relids);
      36                 :             : static void pgpa_qf_add_plan_rtis(List *active_query_features, Plan *plan,
      37                 :             :                                                                   List *rtable);
      38                 :             : 
      39                 :             : static bool pgpa_walker_join_order_matches(pgpa_unrolled_join *ujoin,
      40                 :             :                                                                                    Index rtable_length,
      41                 :             :                                                                                    pgpa_identifier *rt_identifiers,
      42                 :             :                                                                                    pgpa_advice_target *target,
      43                 :             :                                                                                    bool toplevel);
      44                 :             : static bool pgpa_walker_join_order_matches_member(pgpa_join_member *member,
      45                 :             :                                                                                                   Index rtable_length,
      46                 :             :                                                                                                   pgpa_identifier *rt_identifiers,
      47                 :             :                                                                                                   pgpa_advice_target *target);
      48                 :             : static pgpa_scan *pgpa_walker_find_scan(pgpa_plan_walker_context *walker,
      49                 :             :                                                                                 pgpa_scan_strategy strategy,
      50                 :             :                                                                                 Bitmapset *relids);
      51                 :             : static bool pgpa_walker_index_target_matches_plan(pgpa_index_target *itarget,
      52                 :             :                                                                                                   Plan *plan);
      53                 :             : static bool pgpa_walker_contains_feature(pgpa_plan_walker_context *walker,
      54                 :             :                                                                                  pgpa_qf_type type,
      55                 :             :                                                                                  Bitmapset *relids);
      56                 :             : static bool pgpa_walker_contains_join(pgpa_plan_walker_context *walker,
      57                 :             :                                                                           pgpa_join_strategy strategy,
      58                 :             :                                                                           Bitmapset *relids);
      59                 :             : static bool pgpa_walker_contains_no_gather(pgpa_plan_walker_context *walker,
      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                 :             :  */
      70                 :             : void
      71                 :       43527 : pgpa_plan_walker(pgpa_plan_walker_context *walker, PlannedStmt *pstmt,
      72                 :             :                                  List *sj_unique_rels)
      73                 :             : {
      74                 :       43527 :         ListCell   *lc;
      75                 :       43527 :         List       *sj_unique_rtis = NULL;
      76                 :       43527 :         List       *sj_nonunique_qfs = NULL;
      77                 :             : 
      78                 :             :         /* Initialization. */
      79                 :       43527 :         memset(walker, 0, sizeof(pgpa_plan_walker_context));
      80                 :       43527 :         walker->pstmt = pstmt;
      81                 :             : 
      82                 :             :         /* Walk the main plan tree. */
      83                 :       43527 :         pgpa_walk_recursively(walker, pstmt->planTree, 0, NULL, NIL, false);
      84                 :             : 
      85                 :             :         /* Main plan tree walk won't reach subplans, so walk those. */
      86   [ +  +  +  +  :       47897 :         foreach(lc, pstmt->subplans)
                   +  + ]
      87                 :             :         {
      88                 :        4370 :                 Plan       *plan = lfirst(lc);
      89                 :             : 
      90         [ +  + ]:        4370 :                 if (plan != NULL)
      91                 :        4177 :                         pgpa_walk_recursively(walker, plan, 0, NULL, NIL, false);
      92                 :        4370 :         }
      93                 :             : 
      94                 :             :         /* Adjust RTIs from sj_unique_rels for the flattened range table. */
      95   [ +  +  +  +  :       87776 :         foreach_ptr(pgpa_sj_unique_rel, ur, sj_unique_rels)
             +  +  +  + ]
      96                 :             :         {
      97                 :         722 :                 int                     rtindex = -1;
      98                 :         722 :                 int                     rtoffset = 0;
      99                 :         722 :                 bool            dummy = false;
     100                 :         722 :                 Bitmapset  *relids = NULL;
     101                 :             : 
     102                 :             :                 /* If this is a subplan, find the range table offset. */
     103         [ +  + ]:         722 :                 if (ur->plan_name != NULL)
     104                 :             :                 {
     105   [ +  +  +  -  :          18 :                         foreach_node(SubPlanRTInfo, rtinfo, pstmt->subrtinfos)
             -  +  +  - ]
     106                 :             :                         {
     107         [ +  - ]:           6 :                                 if (strcmp(ur->plan_name, rtinfo->plan_name) == 0)
     108                 :             :                                 {
     109                 :           6 :                                         rtoffset = rtinfo->rtoffset;
     110                 :           6 :                                         dummy = rtinfo->dummy;
     111                 :           6 :                                         break;
     112                 :             :                                 }
     113                 :           6 :                         }
     114                 :             : 
     115         [ +  - ]:           6 :                         if (rtoffset == 0)
     116   [ #  #  #  # ]:           0 :                                 elog(ERROR, "no rtoffset for plan %s", ur->plan_name);
     117                 :           6 :                 }
     118                 :             : 
     119                 :             :                 /* If this entry pertains to a dummy subquery, ignore it. */
     120         [ -  + ]:         722 :                 if (dummy)
     121                 :           0 :                         continue;
     122                 :             : 
     123                 :             :                 /* Offset each entry from the original set. */
     124         [ +  + ]:        1486 :                 while ((rtindex = bms_next_member(ur->relids, rtindex)) >= 0)
     125                 :         764 :                         relids = bms_add_member(relids, rtindex + rtoffset);
     126                 :             : 
     127                 :             :                 /* Store the resulting set. */
     128                 :         722 :                 sj_unique_rtis = lappend(sj_unique_rtis, relids);
     129      [ -  -  + ]:       44249 :         }
     130                 :             : 
     131                 :             :         /*
     132                 :             :          * Remove any non-unique semjoin query features for which making the rel
     133                 :             :          * unique wasn't considered.
     134                 :             :          */
     135   [ +  +  +  +  :       87698 :         foreach_ptr(pgpa_query_feature, qf,
             +  +  +  + ]
     136                 :             :                                 walker->query_features[PGPAQF_SEMIJOIN_NON_UNIQUE])
     137                 :             :         {
     138         [ +  + ]:         644 :                 if (list_member(sj_unique_rtis, qf->relids))
     139                 :         617 :                         sj_nonunique_qfs = lappend(sj_nonunique_qfs, qf);
     140                 :       44171 :         }
     141                 :       43527 :         walker->query_features[PGPAQF_SEMIJOIN_NON_UNIQUE] = sj_nonunique_qfs;
     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                 :             :          */
     148   [ +  +  +  +  :       87135 :         foreach_ptr(pgpa_query_feature, qf,
             +  +  +  + ]
     149                 :             :                                 walker->query_features[PGPAQF_SEMIJOIN_UNIQUE])
     150                 :             :         {
     151         [ +  - ]:          81 :                 if (!list_member(sj_unique_rtis, qf->relids))
     152                 :             :                 {
     153                 :           0 :                         StringInfoData buf;
     154                 :             : 
     155                 :           0 :                         initStringInfo(&buf);
     156                 :           0 :                         outBitmapset(&buf, qf->relids);
     157   [ #  #  #  # ]:           0 :                         elog(ERROR,
     158                 :             :                                  "unique semijoin found for relids %s but not observed during planning",
     159                 :             :                                  buf.data);
     160                 :           0 :                 }
     161                 :       43608 :         }
     162                 :       43527 : }
     163                 :             : 
     164                 :             : /*
     165                 :             :  * Main workhorse for the plan tree walk.
     166                 :             :  *
     167                 :             :  * If within_join_problem is true, we encountered a join at some higher level
     168                 :             :  * of the tree walk and haven't yet descended out of the portion of the plan
     169                 :             :  * tree that is part of that same join problem. We're no longer in the same
     170                 :             :  * join problem if (1) we cross into a different subquery or (2) we descend
     171                 :             :  * through an Append or MergeAppend node, below which any further joins would
     172                 :             :  * be partitionwise joins planned separately from the outer join problem.
     173                 :             :  *
     174                 :             :  * If join_unroller != NULL, the join unroller code expects us to find a join
     175                 :             :  * that should be unrolled into that object. This implies that we're within a
     176                 :             :  * join problem, but the reverse is not true: when we've traversed all the
     177                 :             :  * joins but are still looking for the scan that is the leaf of the join tree,
     178                 :             :  * join_unroller will be NULL but within_join_problem will be true.
     179                 :             :  *
     180                 :             :  * Each element of active_query_features corresponds to some item of advice
     181                 :             :  * that needs to enumerate all the relations it affects. We add RTIs we find
     182                 :             :  * during tree traversal to each of these query features.
     183                 :             :  *
     184                 :             :  * If beneath_any_gather == true, some higher level of the tree traversal found
     185                 :             :  * a Gather or Gather Merge node.
     186                 :             :  */
     187                 :             : static void
     188                 :      118039 : pgpa_walk_recursively(pgpa_plan_walker_context *walker, Plan *plan,
     189                 :             :                                           bool within_join_problem,
     190                 :             :                                           pgpa_join_unroller *join_unroller,
     191                 :             :                                           List *active_query_features,
     192                 :             :                                           bool beneath_any_gather)
     193                 :             : {
     194                 :      118039 :         pgpa_join_unroller *outer_join_unroller = NULL;
     195                 :      118039 :         pgpa_join_unroller *inner_join_unroller = NULL;
     196                 :      118039 :         bool            join_unroller_toplevel = false;
     197                 :      118039 :         List       *pushdown_query_features = NIL;
     198                 :      118039 :         ListCell   *lc;
     199                 :      118039 :         List       *extraplans = NIL;
     200                 :      118039 :         List       *elided_nodes = NIL;
     201                 :             : 
     202   [ +  +  +  - ]:      118039 :         Assert(within_join_problem || join_unroller == NULL);
     203                 :             : 
     204                 :             :         /*
     205                 :             :          * If this is a Gather or Gather Merge node, directly add it to the list
     206                 :             :          * of currently-active query features.
     207                 :             :          *
     208                 :             :          * Otherwise, check the future_query_features list to see whether this was
     209                 :             :          * previously identified as a plan node that needs to be treated as a
     210                 :             :          * query feature.
     211                 :             :          *
     212                 :             :          * Note that the caller also has a copy to active_query_features, so we
     213                 :             :          * can't destructively modify it without making a copy.
     214                 :             :          */
     215         [ +  + ]:      118039 :         if (IsA(plan, Gather))
     216                 :             :         {
     217                 :         193 :                 active_query_features =
     218                 :         386 :                         lappend(list_copy(active_query_features),
     219                 :         193 :                                         pgpa_add_feature(walker, PGPAQF_GATHER, plan));
     220                 :         193 :                 beneath_any_gather = true;
     221                 :         193 :         }
     222         [ +  + ]:      117846 :         else if (IsA(plan, GatherMerge))
     223                 :             :         {
     224                 :          67 :                 active_query_features =
     225                 :         134 :                         lappend(list_copy(active_query_features),
     226                 :          67 :                                         pgpa_add_feature(walker, PGPAQF_GATHER_MERGE, plan));
     227                 :          67 :                 beneath_any_gather = true;
     228                 :          67 :         }
     229                 :             :         else
     230                 :             :         {
     231   [ +  +  +  +  :      239555 :                 foreach_ptr(pgpa_query_feature, qf, walker->future_query_features)
             +  +  +  + ]
     232                 :             :                 {
     233         [ +  + ]:        3997 :                         if (qf->plan == plan)
     234                 :             :                         {
     235                 :         725 :                                 active_query_features = list_copy(active_query_features);
     236                 :         725 :                                 active_query_features = lappend(active_query_features, qf);
     237                 :         725 :                                 walker->future_query_features =
     238                 :         725 :                                         list_delete_ptr(walker->future_query_features, plan);
     239                 :         725 :                                 break;
     240                 :             :                         }
     241                 :      121051 :                 }
     242                 :             :         }
     243                 :             : 
     244                 :             :         /*
     245                 :             :          * Find all elided nodes for this Plan node.
     246                 :             :          */
     247   [ +  +  +  +  :      260124 :         foreach_node(ElidedNode, n, walker->pstmt->elidedNodes)
             +  +  +  + ]
     248                 :             :         {
     249         [ +  + ]:       24046 :                 if (n->plan_node_id == plan->plan_node_id)
     250                 :        2645 :                         elided_nodes = lappend(elided_nodes, n);
     251                 :      142085 :         }
     252                 :             : 
     253                 :             :         /* If we found any elided_nodes, handle them. */
     254         [ +  + ]:      118039 :         if (elided_nodes != NIL)
     255                 :             :         {
     256                 :        2628 :                 int                     num_elided_nodes = list_length(elided_nodes);
     257                 :        2628 :                 ElidedNode *last_elided_node;
     258                 :             : 
     259                 :             :                 /*
     260                 :             :                  * RTIs for the final -- and thus logically uppermost -- elided node
     261                 :             :                  * should be collected for query features passed down by the caller.
     262                 :             :                  * However, elided nodes act as barriers to query features, which
     263                 :             :                  * means that (1) the remaining elided nodes, if any, should be
     264                 :             :                  * ignored for purposes of query features and (2) the list of active
     265                 :             :                  * query features should be reset to empty so that we do not add RTIs
     266                 :             :                  * from the plan node that is logically beneath the elided node to the
     267                 :             :                  * query features passed down from the caller.
     268                 :             :                  */
     269                 :        2628 :                 last_elided_node = list_nth(elided_nodes, num_elided_nodes - 1);
     270                 :        5256 :                 pgpa_qf_add_rtis(active_query_features,
     271                 :        5256 :                                                  pgpa_filter_out_join_relids(last_elided_node->relids,
     272                 :        2628 :                                                                                                          walker->pstmt->rtable));
     273                 :        2628 :                 active_query_features = NIL;
     274                 :             : 
     275                 :             :                 /*
     276                 :             :                  * If we're within a join problem, the join_unroller is responsible
     277                 :             :                  * for building the scan for the final elided node, so throw it out.
     278                 :             :                  */
     279         [ +  + ]:        2628 :                 if (within_join_problem)
     280                 :         235 :                         elided_nodes = list_truncate(elided_nodes, num_elided_nodes - 1);
     281                 :             : 
     282                 :             :                 /* Build scans for all (or the remaining) elided nodes. */
     283   [ +  +  +  +  :        7666 :                 foreach_node(ElidedNode, elided_node, elided_nodes)
             +  +  +  + ]
     284                 :             :                 {
     285                 :        4820 :                         (void) pgpa_build_scan(walker, plan, elided_node,
     286                 :        2410 :                                                                    beneath_any_gather, within_join_problem);
     287                 :        5038 :                 }
     288                 :             : 
     289                 :             :                 /*
     290                 :             :                  * If there were any elided nodes, then everything beneath those nodes
     291                 :             :                  * is not part of the same join problem.
     292                 :             :                  *
     293                 :             :                  * In more detail, if an Append or MergeAppend was elided, then a
     294                 :             :                  * partitionwise join was chosen and only a single child survived; if
     295                 :             :                  * a SubqueryScan was elided, the subquery was planned without
     296                 :             :                  * flattening it into the parent.
     297                 :             :                  */
     298                 :        2628 :                 within_join_problem = false;
     299                 :        2628 :                 join_unroller = NULL;
     300                 :        2628 :         }
     301                 :             : 
     302                 :             :         /*
     303                 :             :          * If we're within a join problem, the join unroller is responsible for
     304                 :             :          * building any required scan for this node. If not, we do it here.
     305                 :             :          */
     306         [ +  + ]:      118039 :         if (!within_join_problem)
     307                 :       86795 :                 (void) pgpa_build_scan(walker, plan, NULL, beneath_any_gather, false);
     308                 :             : 
     309                 :             :         /*
     310                 :             :          * If this join needs to unrolled but there's no join unroller already
     311                 :             :          * available, create one.
     312                 :             :          */
     313   [ +  +  +  + ]:      118039 :         if (join_unroller == NULL && pgpa_is_join(plan))
     314                 :             :         {
     315                 :        9756 :                 join_unroller = pgpa_create_join_unroller();
     316                 :        9756 :                 join_unroller_toplevel = true;
     317                 :        9756 :                 within_join_problem = true;
     318                 :        9756 :         }
     319                 :             : 
     320                 :             :         /*
     321                 :             :          * If this join is to be unrolled, pgpa_unroll_join() will return the join
     322                 :             :          * unroller object that should be passed down when we recurse into the
     323                 :             :          * outer and inner sides of the plan.
     324                 :             :          */
     325         [ +  + ]:      118039 :         if (join_unroller != NULL)
     326                 :       13297 :                 pgpa_unroll_join(walker, plan, beneath_any_gather, join_unroller,
     327                 :             :                                                  &outer_join_unroller, &inner_join_unroller);
     328                 :             : 
     329                 :             :         /* Add RTIs from the plan node to all active query features. */
     330                 :      118039 :         pgpa_qf_add_plan_rtis(active_query_features, plan, walker->pstmt->rtable);
     331                 :             : 
     332                 :             :         /*
     333                 :             :          * Recurse into the outer and inner subtrees.
     334                 :             :          *
     335                 :             :          * As an exception, if this is a ForeignScan, don't recurse. postgres_fdw
     336                 :             :          * sometimes stores an EPQ recheck plan in plan->leftree, but that's going
     337                 :             :          * to mention the same set of relations as the ForeignScan itself, and we
     338                 :             :          * have no way to emit advice targeting the EPQ case vs. the non-EPQ case.
     339                 :             :          * Moreover, it's not entirely clear what other FDWs might do with the
     340                 :             :          * left and right subtrees. Maybe some better handling is needed here, but
     341                 :             :          * for now, we just punt.
     342                 :             :          */
     343         [ -  + ]:      118039 :         if (!IsA(plan, ForeignScan))
     344                 :             :         {
     345         [ +  + ]:      118039 :                 if (plan->lefttree != NULL)
     346                 :       96340 :                         pgpa_walk_recursively(walker, plan->lefttree, within_join_problem,
     347                 :       48170 :                                                                   outer_join_unroller, active_query_features,
     348                 :       48170 :                                                                   beneath_any_gather);
     349         [ +  + ]:      118039 :                 if (plan->righttree != NULL)
     350                 :       26326 :                         pgpa_walk_recursively(walker, plan->righttree, within_join_problem,
     351                 :       13163 :                                                                   inner_join_unroller, active_query_features,
     352                 :       13163 :                                                                   beneath_any_gather);
     353                 :      118039 :         }
     354                 :             : 
     355                 :             :         /*
     356                 :             :          * If we created a join unroller up above, then it's also our join to use
     357                 :             :          * it to build the final pgpa_unrolled_join, and to destroy the object.
     358                 :             :          */
     359         [ +  + ]:      118039 :         if (join_unroller_toplevel)
     360                 :             :         {
     361                 :        9756 :                 pgpa_unrolled_join *ujoin;
     362                 :             : 
     363                 :        9756 :                 ujoin = pgpa_build_unrolled_join(walker, join_unroller);
     364                 :        9756 :                 walker->toplevel_unrolled_joins =
     365                 :        9756 :                         lappend(walker->toplevel_unrolled_joins, ujoin);
     366                 :        9756 :                 pgpa_destroy_join_unroller(join_unroller);
     367                 :        9756 :                 (void) pgpa_process_unrolled_join(walker, ujoin);
     368                 :        9756 :         }
     369                 :             : 
     370                 :             :         /*
     371                 :             :          * Some plan types can have additional children. Nodes like Append that
     372                 :             :          * can have any number of children store them in a List; a SubqueryScan
     373                 :             :          * just has a field for a single additional Plan.
     374                 :             :          */
     375   [ +  +  +  +  :      118039 :         switch (nodeTag(plan))
                +  -  + ]
     376                 :             :         {
     377                 :             :                 case T_Append:
     378                 :             :                         {
     379                 :        2512 :                                 Append     *aplan = (Append *) plan;
     380                 :             : 
     381                 :        2512 :                                 extraplans = aplan->appendplans;
     382         [ +  + ]:        2512 :                                 if (bms_is_empty(aplan->apprelids))
     383                 :          78 :                                         pushdown_query_features = active_query_features;
     384                 :        2512 :                         }
     385                 :        2512 :                         break;
     386                 :             :                 case T_MergeAppend:
     387                 :             :                         {
     388                 :          91 :                                 MergeAppend *maplan = (MergeAppend *) plan;
     389                 :             : 
     390                 :          91 :                                 extraplans = maplan->mergeplans;
     391         [ +  + ]:          91 :                                 if (bms_is_empty(maplan->apprelids))
     392                 :          12 :                                         pushdown_query_features = active_query_features;
     393                 :          91 :                         }
     394                 :          91 :                         break;
     395                 :             :                 case T_BitmapAnd:
     396                 :          17 :                         extraplans = ((BitmapAnd *) plan)->bitmapplans;
     397                 :          17 :                         break;
     398                 :             :                 case T_BitmapOr:
     399                 :          33 :                         extraplans = ((BitmapOr *) plan)->bitmapplans;
     400                 :          33 :                         break;
     401                 :             :                 case T_SubqueryScan:
     402                 :             : 
     403                 :             :                         /*
     404                 :             :                          * We don't pass down active_query_features across here, because
     405                 :             :                          * those are specific to a subquery level.
     406                 :             :                          */
     407                 :        2986 :                         pgpa_walk_recursively(walker, ((SubqueryScan *) plan)->subplan,
     408                 :        1493 :                                                                   0, NULL, NIL, beneath_any_gather);
     409                 :        1493 :                         break;
     410                 :             :                 case T_CustomScan:
     411                 :           0 :                         extraplans = ((CustomScan *) plan)->custom_plans;
     412                 :           0 :                         break;
     413                 :             :                 default:
     414                 :      113893 :                         break;
     415                 :             :         }
     416                 :             : 
     417                 :             :         /* If we found a list of extra children, iterate over it. */
     418   [ +  +  +  +  :      125548 :         foreach(lc, extraplans)
                   +  + ]
     419                 :             :         {
     420                 :        7509 :                 Plan       *subplan = lfirst(lc);
     421                 :             : 
     422                 :       15018 :                 pgpa_walk_recursively(walker, subplan, 0, NULL, pushdown_query_features,
     423                 :        7509 :                                                           beneath_any_gather);
     424                 :        7509 :         }
     425                 :      118039 : }
     426                 :             : 
     427                 :             : /*
     428                 :             :  * Perform final processing of a newly-constructed pgpa_unrolled_join. This
     429                 :             :  * only needs to be called for toplevel pgpa_unrolled_join objects, since it
     430                 :             :  * recurses to sub-joins as needed.
     431                 :             :  *
     432                 :             :  * Our goal is to add the set of inner relids to the relevant join_strategies
     433                 :             :  * list, and to do the same for any sub-joins. To that end, the return value
     434                 :             :  * is the set of relids found beneath the the join, but it is expected that
     435                 :             :  * the toplevel caller will ignore this.
     436                 :             :  */
     437                 :             : static Bitmapset *
     438                 :       10166 : pgpa_process_unrolled_join(pgpa_plan_walker_context *walker,
     439                 :             :                                                    pgpa_unrolled_join *ujoin)
     440                 :             : {
     441                 :       10166 :         Bitmapset  *all_relids = bms_copy(ujoin->outer.scan->relids);
     442                 :             : 
     443                 :             :         /* If this fails, we didn't unroll properly. */
     444         [ +  - ]:       10166 :         Assert(ujoin->outer.unrolled_join == NULL);
     445                 :             : 
     446         [ +  + ]:       23146 :         for (int k = 0; k < ujoin->ninner; ++k)
     447                 :             :         {
     448                 :       12980 :                 pgpa_join_member *member = &ujoin->inner[k];
     449                 :       12980 :                 Bitmapset  *relids;
     450                 :             : 
     451         [ +  + ]:       12980 :                 if (member->unrolled_join != NULL)
     452                 :         820 :                         relids = pgpa_process_unrolled_join(walker,
     453                 :         410 :                                                                                                 member->unrolled_join);
     454                 :             :                 else
     455                 :             :                 {
     456         [ -  + ]:       12570 :                         Assert(member->scan != NULL);
     457                 :       12570 :                         relids = member->scan->relids;
     458                 :             :                 }
     459                 :       12980 :                 walker->join_strategies[ujoin->strategy[k]] =
     460                 :       12980 :                         lappend(walker->join_strategies[ujoin->strategy[k]], relids);
     461                 :       12980 :                 all_relids = bms_add_members(all_relids, relids);
     462                 :       12980 :         }
     463                 :             : 
     464                 :       20332 :         return all_relids;
     465                 :       10166 : }
     466                 :             : 
     467                 :             : /*
     468                 :             :  * Arrange for the given plan node to be treated as a query feature when the
     469                 :             :  * tree walk reaches it.
     470                 :             :  *
     471                 :             :  * Make sure to only use this for nodes that the tree walk can't have reached
     472                 :             :  * yet!
     473                 :             :  */
     474                 :             : void
     475                 :         725 : pgpa_add_future_feature(pgpa_plan_walker_context *walker,
     476                 :             :                                                 pgpa_qf_type type, Plan *plan)
     477                 :             : {
     478                 :         725 :         pgpa_query_feature *qf = pgpa_add_feature(walker, type, plan);
     479                 :             : 
     480                 :         725 :         walker->future_query_features =
     481                 :         725 :                 lappend(walker->future_query_features, qf);
     482                 :         725 : }
     483                 :             : 
     484                 :             : /*
     485                 :             :  * Return the last of any elided nodes associated with this plan node ID.
     486                 :             :  *
     487                 :             :  * The last elided node is the one that would have been uppermost in the plan
     488                 :             :  * tree had it not been removed during setrefs processig.
     489                 :             :  */
     490                 :             : ElidedNode *
     491                 :       31080 : pgpa_last_elided_node(PlannedStmt *pstmt, Plan *plan)
     492                 :             : {
     493                 :       31080 :         ElidedNode *elided_node = NULL;
     494                 :             : 
     495   [ +  +  +  +  :       71317 :         foreach_node(ElidedNode, n, pstmt->elidedNodes)
             +  +  +  + ]
     496                 :             :         {
     497         [ +  + ]:        9157 :                 if (n->plan_node_id == plan->plan_node_id)
     498                 :         236 :                         elided_node = n;
     499                 :       40237 :         }
     500                 :             : 
     501                 :       62160 :         return elided_node;
     502                 :       31080 : }
     503                 :             : 
     504                 :             : /*
     505                 :             :  * Certain plan nodes can refer to a set of RTIs. Extract and return the set.
     506                 :             :  */
     507                 :             : Bitmapset *
     508                 :      178053 : pgpa_relids(Plan *plan)
     509                 :             : {
     510         [ +  + ]:      178053 :         if (IsA(plan, Result))
     511                 :       39392 :                 return ((Result *) plan)->relids;
     512         [ -  + ]:      138661 :         else if (IsA(plan, ForeignScan))
     513                 :           0 :                 return ((ForeignScan *) plan)->fs_relids;
     514         [ +  + ]:      138661 :         else if (IsA(plan, Append))
     515                 :        5024 :                 return ((Append *) plan)->apprelids;
     516         [ +  + ]:      133637 :         else if (IsA(plan, MergeAppend))
     517                 :         182 :                 return ((MergeAppend *) plan)->apprelids;
     518                 :             : 
     519                 :      133455 :         return NULL;
     520                 :      178053 : }
     521                 :             : 
     522                 :             : /*
     523                 :             :  * Extract the scanned RTI from a plan node.
     524                 :             :  *
     525                 :             :  * Returns 0 if there isn't one.
     526                 :             :  */
     527                 :             : Index
     528                 :      207002 : pgpa_scanrelid(Plan *plan)
     529                 :             : {
     530         [ +  + ]:      207002 :         switch (nodeTag(plan))
     531                 :             :         {
     532                 :             :                 case T_SeqScan:
     533                 :             :                 case T_SampleScan:
     534                 :             :                 case T_BitmapHeapScan:
     535                 :             :                 case T_TidScan:
     536                 :             :                 case T_TidRangeScan:
     537                 :             :                 case T_SubqueryScan:
     538                 :             :                 case T_FunctionScan:
     539                 :             :                 case T_TableFuncScan:
     540                 :             :                 case T_ValuesScan:
     541                 :             :                 case T_CteScan:
     542                 :             :                 case T_NamedTuplestoreScan:
     543                 :             :                 case T_WorkTableScan:
     544                 :             :                 case T_ForeignScan:
     545                 :             :                 case T_CustomScan:
     546                 :             :                 case T_IndexScan:
     547                 :             :                 case T_IndexOnlyScan:
     548                 :       98564 :                         return ((Scan *) plan)->scanrelid;
     549                 :             :                 default:
     550                 :      108438 :                         return 0;
     551                 :             :         }
     552                 :      207002 : }
     553                 :             : 
     554                 :             : /*
     555                 :             :  * Construct a new Bitmapset containing non-RTE_JOIN members of 'relids'.
     556                 :             :  */
     557                 :             : Bitmapset *
     558                 :       46305 : pgpa_filter_out_join_relids(Bitmapset *relids, List *rtable)
     559                 :             : {
     560                 :       46305 :         int                     rti = -1;
     561                 :       46305 :         Bitmapset  *result = NULL;
     562                 :             : 
     563         [ +  + ]:       95798 :         while ((rti = bms_next_member(relids, rti)) >= 0)
     564                 :             :         {
     565                 :       49493 :                 RangeTblEntry *rte = rt_fetch(rti, rtable);
     566                 :             : 
     567         [ +  + ]:       49493 :                 if (rte->rtekind != RTE_JOIN)
     568                 :       49257 :                         result = bms_add_member(result, rti);
     569                 :       49493 :         }
     570                 :             : 
     571                 :       92610 :         return result;
     572                 :       46305 : }
     573                 :             : 
     574                 :             : /*
     575                 :             :  * Create a pgpa_query_feature and add it to the list of all query features
     576                 :             :  * for this plan.
     577                 :             :  */
     578                 :             : static pgpa_query_feature *
     579                 :         985 : pgpa_add_feature(pgpa_plan_walker_context *walker,
     580                 :             :                                  pgpa_qf_type type, Plan *plan)
     581                 :             : {
     582                 :         985 :         pgpa_query_feature *qf = palloc0_object(pgpa_query_feature);
     583                 :             : 
     584                 :         985 :         qf->type = type;
     585                 :         985 :         qf->plan = plan;
     586                 :             : 
     587                 :         985 :         walker->query_features[qf->type] =
     588                 :         985 :                 lappend(walker->query_features[qf->type], qf);
     589                 :             : 
     590                 :        1970 :         return qf;
     591                 :         985 : }
     592                 :             : 
     593                 :             : /*
     594                 :             :  * Add a single RTI to each active query feature.
     595                 :             :  */
     596                 :             : static void
     597                 :       49282 : pgpa_qf_add_rti(List *active_query_features, Index rti)
     598                 :             : {
     599   [ +  +  +  +  :       99095 :         foreach_ptr(pgpa_query_feature, qf, active_query_features)
             +  +  +  + ]
     600                 :             :         {
     601                 :         531 :                 qf->relids = bms_add_member(qf->relids, rti);
     602                 :       49813 :         }
     603                 :       49282 : }
     604                 :             : 
     605                 :             : /*
     606                 :             :  * Add a set of RTIs to each active query feature.
     607                 :             :  */
     608                 :             : static void
     609                 :       22961 : pgpa_qf_add_rtis(List *active_query_features, Bitmapset *relids)
     610                 :             : {
     611   [ +  +  +  +  :       46518 :         foreach_ptr(pgpa_query_feature, qf, active_query_features)
             +  +  +  + ]
     612                 :             :         {
     613                 :         596 :                 qf->relids = bms_add_members(qf->relids, relids);
     614                 :       23557 :         }
     615                 :       22961 : }
     616                 :             : 
     617                 :             : /*
     618                 :             :  * Add RTIs directly contained in a plan node to each active query feature,
     619                 :             :  * but filter out any join RTIs, since advice doesn't mention those.
     620                 :             :  */
     621                 :             : static void
     622                 :      118039 : pgpa_qf_add_plan_rtis(List *active_query_features, Plan *plan, List *rtable)
     623                 :             : {
     624                 :      118039 :         Bitmapset  *relids;
     625                 :      118039 :         Index           rti;
     626                 :             : 
     627         [ +  + ]:      118039 :         if ((relids = pgpa_relids(plan)) != NULL)
     628                 :             :         {
     629                 :       20333 :                 relids = pgpa_filter_out_join_relids(relids, rtable);
     630                 :       20333 :                 pgpa_qf_add_rtis(active_query_features, relids);
     631                 :       20333 :         }
     632         [ +  + ]:       97706 :         else if ((rti = pgpa_scanrelid(plan)) != 0)
     633                 :       49282 :                 pgpa_qf_add_rti(active_query_features, rti);
     634                 :      118039 : }
     635                 :             : 
     636                 :             : /*
     637                 :             :  * If we generated plan advice using the provided walker object and array
     638                 :             :  * of identifiers, would we generate the specified tag/target combination?
     639                 :             :  *
     640                 :             :  * If yes, the plan conforms to the advice; if no, it does not. Note that
     641                 :             :  * we have know way of knowing whether the planner was forced to emit a plan
     642                 :             :  * that conformed to the advice or just happened to do so.
     643                 :             :  */
     644                 :             : bool
     645                 :         113 : pgpa_walker_would_advise(pgpa_plan_walker_context *walker,
     646                 :             :                                                  pgpa_identifier *rt_identifiers,
     647                 :             :                                                  pgpa_advice_tag_type tag,
     648                 :             :                                                  pgpa_advice_target *target)
     649                 :             : {
     650                 :         113 :         Index           rtable_length = list_length(walker->pstmt->rtable);
     651                 :         113 :         Bitmapset  *relids = NULL;
     652                 :             : 
     653         [ +  + ]:         113 :         if (tag == PGPA_TAG_JOIN_ORDER)
     654                 :             :         {
     655   [ +  +  +  -  :          34 :                 foreach_ptr(pgpa_unrolled_join, ujoin, walker->toplevel_unrolled_joins)
          +  +  +  +  +  
                +  +  + ]
     656                 :             :                 {
     657   [ +  +  +  + ]:          32 :                         if (pgpa_walker_join_order_matches(ujoin, rtable_length,
     658                 :          16 :                                                                                            rt_identifiers, target, true))
     659                 :          14 :                                 return true;
     660                 :           4 :                 }
     661                 :             : 
     662                 :           2 :                 return false;
     663                 :             :         }
     664                 :             : 
     665         [ +  + ]:          97 :         if (target->ttype == PGPA_TARGET_IDENTIFIER)
     666                 :             :         {
     667                 :          87 :                 Index           rti;
     668                 :             : 
     669                 :         174 :                 rti = pgpa_compute_rti_from_identifier(rtable_length, rt_identifiers,
     670                 :          87 :                                                                                            &target->rid);
     671         [ +  - ]:          87 :                 if (rti == 0)
     672                 :           0 :                         return false;
     673                 :          87 :                 relids = bms_make_singleton(rti);
     674         [ -  + ]:          87 :         }
     675                 :             :         else
     676                 :             :         {
     677         [ +  - ]:          10 :                 Assert(target->ttype == PGPA_TARGET_ORDERED_LIST);
     678   [ +  +  +  -  :          40 :                 foreach_ptr(pgpa_advice_target, child_target, target->children)
          +  +  +  +  -  
                +  -  + ]
     679                 :             :                 {
     680                 :          20 :                         Index           rti;
     681                 :             : 
     682         [ +  - ]:          20 :                         Assert(child_target->ttype == PGPA_TARGET_IDENTIFIER);
     683                 :          40 :                         rti = pgpa_compute_rti_from_identifier(rtable_length,
     684                 :          20 :                                                                                                    rt_identifiers,
     685                 :          20 :                                                                                                    &child_target->rid);
     686         [ +  - ]:          20 :                         if (rti == 0)
     687                 :           0 :                                 return false;
     688                 :          20 :                         relids = bms_add_member(relids, rti);
     689         [ -  + ]:          30 :                 }
     690                 :             :         }
     691                 :             : 
     692   [ -  +  +  +  :          97 :         switch (tag)
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  -  +  +  
                      + ]
     693                 :             :         {
     694                 :             :                 case PGPA_TAG_JOIN_ORDER:
     695                 :             :                         /* should have been handled above */
     696                 :           0 :                         pg_unreachable();
     697                 :             :                         break;
     698                 :             :                 case PGPA_TAG_BITMAP_HEAP_SCAN:
     699                 :             :                         /* XXX currenly doesn't try to match the index specification */
     700                 :           9 :                         return pgpa_walker_find_scan(walker,
     701                 :             :                                                                                  PGPA_SCAN_BITMAP_HEAP,
     702                 :           6 :                                                                                  relids) != NULL;
     703                 :             :                 case PGPA_TAG_FOREIGN_JOIN:
     704                 :           3 :                         return pgpa_walker_find_scan(walker,
     705                 :             :                                                                                  PGPA_SCAN_FOREIGN,
     706                 :           2 :                                                                                  relids) != NULL;
     707                 :             :                 case PGPA_TAG_INDEX_ONLY_SCAN:
     708                 :             :                         {
     709                 :           5 :                                 pgpa_scan  *scan;
     710                 :             : 
     711                 :          10 :                                 scan = pgpa_walker_find_scan(walker, PGPA_SCAN_INDEX_ONLY,
     712                 :           5 :                                                                                          relids);
     713         [ +  + ]:           5 :                                 if (scan == NULL)
     714                 :           3 :                                         return false;
     715                 :             : 
     716                 :           2 :                                 return pgpa_walker_index_target_matches_plan(target->itarget, scan->plan);
     717                 :           5 :                         }
     718                 :             :                 case PGPA_TAG_INDEX_SCAN:
     719                 :             :                         {
     720                 :          14 :                                 pgpa_scan  *scan;
     721                 :             : 
     722                 :          28 :                                 scan = pgpa_walker_find_scan(walker, PGPA_SCAN_INDEX,
     723                 :          14 :                                                                                          relids);
     724         [ +  + ]:          14 :                                 if (scan == NULL)
     725                 :           4 :                                         return false;
     726                 :             : 
     727                 :          10 :                                 return pgpa_walker_index_target_matches_plan(target->itarget, scan->plan);
     728                 :          14 :                         }
     729                 :             :                 case PGPA_TAG_PARTITIONWISE:
     730                 :          24 :                         return pgpa_walker_find_scan(walker,
     731                 :             :                                                                                  PGPA_SCAN_PARTITIONWISE,
     732                 :          16 :                                                                                  relids) != NULL;
     733                 :             :                 case PGPA_TAG_SEQ_SCAN:
     734                 :          36 :                         return pgpa_walker_find_scan(walker,
     735                 :             :                                                                                  PGPA_SCAN_SEQ,
     736                 :          24 :                                                                                  relids) != NULL;
     737                 :             :                 case PGPA_TAG_TID_SCAN:
     738                 :          12 :                         return pgpa_walker_find_scan(walker,
     739                 :             :                                                                                  PGPA_SCAN_TID,
     740                 :           8 :                                                                                  relids) != NULL;
     741                 :             :                 case PGPA_TAG_GATHER:
     742                 :          14 :                         return pgpa_walker_contains_feature(walker,
     743                 :             :                                                                                                 PGPAQF_GATHER,
     744                 :           7 :                                                                                                 relids);
     745                 :             :                 case PGPA_TAG_GATHER_MERGE:
     746                 :          12 :                         return pgpa_walker_contains_feature(walker,
     747                 :             :                                                                                                 PGPAQF_GATHER_MERGE,
     748                 :           6 :                                                                                                 relids);
     749                 :             :                 case PGPA_TAG_SEMIJOIN_NON_UNIQUE:
     750                 :          12 :                         return pgpa_walker_contains_feature(walker,
     751                 :             :                                                                                                 PGPAQF_SEMIJOIN_NON_UNIQUE,
     752                 :           6 :                                                                                                 relids);
     753                 :             :                 case PGPA_TAG_SEMIJOIN_UNIQUE:
     754                 :          14 :                         return pgpa_walker_contains_feature(walker,
     755                 :             :                                                                                                 PGPAQF_SEMIJOIN_UNIQUE,
     756                 :           7 :                                                                                                 relids);
     757                 :             :                 case PGPA_TAG_HASH_JOIN:
     758                 :           6 :                         return pgpa_walker_contains_join(walker,
     759                 :             :                                                                                          JSTRAT_HASH_JOIN,
     760                 :           3 :                                                                                          relids);
     761                 :             :                 case PGPA_TAG_MERGE_JOIN_MATERIALIZE:
     762                 :           4 :                         return pgpa_walker_contains_join(walker,
     763                 :             :                                                                                          JSTRAT_MERGE_JOIN_MATERIALIZE,
     764                 :           2 :                                                                                          relids);
     765                 :             :                 case PGPA_TAG_MERGE_JOIN_PLAIN:
     766                 :           4 :                         return pgpa_walker_contains_join(walker,
     767                 :             :                                                                                          JSTRAT_MERGE_JOIN_PLAIN,
     768                 :           2 :                                                                                          relids);
     769                 :             :                 case PGPA_TAG_NESTED_LOOP_MATERIALIZE:
     770                 :           6 :                         return pgpa_walker_contains_join(walker,
     771                 :             :                                                                                          JSTRAT_NESTED_LOOP_MATERIALIZE,
     772                 :           3 :                                                                                          relids);
     773                 :             :                 case PGPA_TAG_NESTED_LOOP_MEMOIZE:
     774                 :           4 :                         return pgpa_walker_contains_join(walker,
     775                 :             :                                                                                          JSTRAT_NESTED_LOOP_MEMOIZE,
     776                 :           2 :                                                                                          relids);
     777                 :             :                 case PGPA_TAG_NESTED_LOOP_PLAIN:
     778                 :          10 :                         return pgpa_walker_contains_join(walker,
     779                 :             :                                                                                          JSTRAT_NESTED_LOOP_PLAIN,
     780                 :           5 :                                                                                          relids);
     781                 :             :                 case PGPA_TAG_NO_GATHER:
     782                 :           7 :                         return pgpa_walker_contains_no_gather(walker, relids);
     783                 :             :         }
     784                 :             : 
     785                 :             :         /* should not get here */
     786                 :           0 :         return false;
     787                 :         113 : }
     788                 :             : 
     789                 :             : /*
     790                 :             :  * Does the index target match the Plan?
     791                 :             :  *
     792                 :             :  * Should only be called when we know that itarget mandates an Index Scan or
     793                 :             :  * Index Only Scan and this corresponds to the type of Plan. Here, our job is
     794                 :             :  * just to check whether it's the same index.
     795                 :             :  */
     796                 :             : static bool
     797                 :          12 : pgpa_walker_index_target_matches_plan(pgpa_index_target *itarget, Plan *plan)
     798                 :             : {
     799                 :          12 :         Oid                     indexoid = InvalidOid;
     800                 :             : 
     801                 :             :         /* Retrieve the index OID from the plan. */
     802         [ +  + ]:          12 :         if (IsA(plan, IndexScan))
     803                 :          10 :                 indexoid = ((IndexScan *) plan)->indexid;
     804         [ +  - ]:           2 :         else if (IsA(plan, IndexOnlyScan))
     805                 :           2 :                 indexoid = ((IndexOnlyScan *) plan)->indexid;
     806                 :             :         else
     807   [ #  #  #  # ]:           0 :                 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(plan));
     808                 :             : 
     809                 :             :         /* Shouldn't be called in any other case. */
     810         [ +  - ]:          12 :         Assert(itarget->itype == PGPA_INDEX_NAME);
     811                 :             : 
     812                 :             :         /* Check whether schema name matches, if specified in index target. */
     813         [ +  + ]:          12 :         if (itarget->indnamespace != NULL)
     814                 :             :         {
     815                 :           4 :                 Oid                     nspoid = get_rel_namespace(indexoid);
     816                 :           4 :                 char       *relnamespace = get_namespace_name_or_temp(nspoid);
     817                 :             : 
     818         [ +  + ]:           4 :                 if (strcmp(itarget->indnamespace, relnamespace) != 0)
     819                 :           1 :                         return false;
     820         [ +  + ]:           4 :         }
     821                 :             : 
     822                 :             :         /* Check whether relation name matches. */
     823                 :          11 :         return (strcmp(itarget->indname, get_rel_name(indexoid)) == 0);
     824                 :          12 : }
     825                 :             : 
     826                 :             : /*
     827                 :             :  * Does an unrolled join match the join order specified by an advice target?
     828                 :             :  */
     829                 :             : static bool
     830                 :          17 : pgpa_walker_join_order_matches(pgpa_unrolled_join *ujoin,
     831                 :             :                                                            Index rtable_length,
     832                 :             :                                                            pgpa_identifier *rt_identifiers,
     833                 :             :                                                            pgpa_advice_target *target,
     834                 :             :                                                            bool toplevel)
     835                 :             : {
     836                 :          17 :         int                     nchildren = list_length(target->children);
     837                 :             : 
     838         [ +  - ]:          17 :         Assert(target->ttype == PGPA_TARGET_ORDERED_LIST);
     839                 :             : 
     840                 :             :         /* At toplevel, we allow a prefix match. */
     841         [ +  + ]:          17 :         if (toplevel)
     842                 :             :         {
     843         [ -  + ]:          16 :                 if (nchildren > ujoin->ninner + 1)
     844                 :           0 :                         return false;
     845                 :          16 :         }
     846                 :             :         else
     847                 :             :         {
     848         [ -  + ]:           1 :                 if (nchildren != ujoin->ninner + 1)
     849                 :           0 :                         return false;
     850                 :             :         }
     851                 :             : 
     852                 :             :         /* Outermost rel must match. */
     853   [ +  +  +  + ]:          34 :         if (!pgpa_walker_join_order_matches_member(&ujoin->outer,
     854                 :          17 :                                                                                            rtable_length,
     855                 :          17 :                                                                                            rt_identifiers,
     856                 :          17 :                                                                                            linitial(target->children)))
     857                 :           1 :                 return false;
     858                 :             : 
     859                 :             :         /* Each inner rel must match. */
     860   [ +  +  +  + ]:          34 :         for (int n = 0; n < nchildren - 1; ++n)
     861                 :             :         {
     862                 :          18 :                 pgpa_advice_target *child_target = list_nth(target->children, n + 1);
     863                 :             : 
     864   [ +  +  +  + ]:          36 :                 if (!pgpa_walker_join_order_matches_member(&ujoin->inner[n],
     865                 :          18 :                                                                                                    rtable_length,
     866                 :          18 :                                                                                                    rt_identifiers,
     867                 :          18 :                                                                                                    child_target))
     868                 :           1 :                         return false;
     869         [ +  + ]:          18 :         }
     870                 :             : 
     871                 :          15 :         return true;
     872                 :          17 : }
     873                 :             : 
     874                 :             : /*
     875                 :             :  * Does one member of an unrolled join match an advice target?
     876                 :             :  */
     877                 :             : static bool
     878                 :          35 : pgpa_walker_join_order_matches_member(pgpa_join_member *member,
     879                 :             :                                                                           Index rtable_length,
     880                 :             :                                                                           pgpa_identifier *rt_identifiers,
     881                 :             :                                                                           pgpa_advice_target *target)
     882                 :             : {
     883                 :          35 :         Bitmapset  *relids = NULL;
     884                 :             : 
     885         [ +  + ]:          35 :         if (member->unrolled_join != NULL)
     886                 :             :         {
     887         [ +  + ]:           2 :                 if (target->ttype != PGPA_TARGET_ORDERED_LIST)
     888                 :           1 :                         return false;
     889                 :           2 :                 return pgpa_walker_join_order_matches(member->unrolled_join,
     890                 :           1 :                                                                                           rtable_length,
     891                 :           1 :                                                                                           rt_identifiers,
     892                 :           1 :                                                                                           target,
     893                 :             :                                                                                           false);
     894                 :             :         }
     895                 :             : 
     896         [ +  - ]:          33 :         Assert(member->scan != NULL);
     897   [ -  -  -  + ]:          33 :         switch (target->ttype)
     898                 :             :         {
     899                 :             :                 case PGPA_TARGET_ORDERED_LIST:
     900                 :             :                         /* Could only match an unrolled join */
     901                 :           0 :                         return false;
     902                 :             : 
     903                 :             :                 case PGPA_TARGET_UNORDERED_LIST:
     904                 :             :                         {
     905   [ #  #  #  #  :           0 :                                 foreach_ptr(pgpa_advice_target, child_target, target->children)
          #  #  #  #  #  
                #  #  # ]
     906                 :             :                                 {
     907                 :           0 :                                         Index           rti;
     908                 :             : 
     909                 :           0 :                                         rti = pgpa_compute_rti_from_identifier(rtable_length,
     910                 :           0 :                                                                                                                    rt_identifiers,
     911                 :           0 :                                                                                                                    &child_target->rid);
     912         [ #  # ]:           0 :                                         if (rti == 0)
     913                 :           0 :                                                 return false;
     914                 :           0 :                                         relids = bms_add_member(relids, rti);
     915         [ #  # ]:           0 :                                 }
     916                 :           0 :                                 break;
     917                 :             :                         }
     918                 :             : 
     919                 :             :                 case PGPA_TARGET_IDENTIFIER:
     920                 :             :                         {
     921                 :          33 :                                 Index           rti;
     922                 :             : 
     923                 :          66 :                                 rti = pgpa_compute_rti_from_identifier(rtable_length,
     924                 :          33 :                                                                                                            rt_identifiers,
     925                 :          33 :                                                                                                            &target->rid);
     926         [ +  - ]:          33 :                                 if (rti == 0)
     927                 :           0 :                                         return false;
     928                 :          33 :                                 relids = bms_make_singleton(rti);
     929                 :          33 :                                 break;
     930         [ -  + ]:          33 :                         }
     931                 :             :         }
     932                 :             : 
     933                 :          33 :         return bms_equal(member->scan->relids, relids);
     934                 :          35 : }
     935                 :             : 
     936                 :             : /*
     937                 :             :  * Find the scan where the walker says that the given scan strategy should be
     938                 :             :  * used for the given relid set, if one exists.
     939                 :             :  *
     940                 :             :  * Returns the pgpa_scan object, or NULL if none was found.
     941                 :             :  */
     942                 :             : static pgpa_scan *
     943                 :          47 : pgpa_walker_find_scan(pgpa_plan_walker_context *walker,
     944                 :             :                                           pgpa_scan_strategy strategy,
     945                 :             :                                           Bitmapset *relids)
     946                 :             : {
     947                 :          47 :         List       *scans = walker->scans[strategy];
     948                 :             : 
     949   [ +  +  +  +  :         103 :         foreach_ptr(pgpa_scan, scan, scans)
          +  +  +  +  +  
                +  +  + ]
     950                 :             :         {
     951         [ +  + ]:          42 :                 if (bms_equal(scan->relids, relids))
     952                 :          33 :                         return scan;
     953                 :          23 :         }
     954                 :             : 
     955                 :          14 :         return NULL;
     956                 :          47 : }
     957                 :             : 
     958                 :             : /*
     959                 :             :  * Does this walker say that the given query feature applies to the given
     960                 :             :  * relid set?
     961                 :             :  */
     962                 :             : static bool
     963                 :          26 : pgpa_walker_contains_feature(pgpa_plan_walker_context *walker,
     964                 :             :                                                          pgpa_qf_type type,
     965                 :             :                                                          Bitmapset *relids)
     966                 :             : {
     967                 :          26 :         List       *query_features = walker->query_features[type];
     968                 :             : 
     969   [ +  +  +  +  :          55 :         foreach_ptr(pgpa_query_feature, qf, query_features)
          +  +  +  +  +  
                +  +  + ]
     970                 :             :         {
     971         [ +  + ]:          23 :                 if (bms_equal(qf->relids, relids))
     972                 :          20 :                         return true;
     973                 :           9 :         }
     974                 :             : 
     975                 :           6 :         return false;
     976                 :          26 : }
     977                 :             : 
     978                 :             : /*
     979                 :             :  * Does the walker say that the given join strategy should be used for the
     980                 :             :  * given relid set?
     981                 :             :  */
     982                 :             : static bool
     983                 :          17 : pgpa_walker_contains_join(pgpa_plan_walker_context *walker,
     984                 :             :                                                   pgpa_join_strategy strategy,
     985                 :             :                                                   Bitmapset *relids)
     986                 :             : {
     987                 :          17 :         List       *join_strategies = walker->join_strategies[strategy];
     988                 :             : 
     989   [ +  +  +  +  :          35 :         foreach_ptr(Bitmapset, jsrelids, join_strategies)
          +  +  +  +  +  
                +  +  + ]
     990                 :             :         {
     991         [ +  + ]:          13 :                 if (bms_equal(jsrelids, relids))
     992                 :          12 :                         return true;
     993                 :           6 :         }
     994                 :             : 
     995                 :           5 :         return false;
     996                 :          17 : }
     997                 :             : 
     998                 :             : /*
     999                 :             :  * Does the walker say that the given relids should be marked as NO_GATHER?
    1000                 :             :  */
    1001                 :             : static bool
    1002                 :           7 : pgpa_walker_contains_no_gather(pgpa_plan_walker_context *walker,
    1003                 :             :                                                            Bitmapset *relids)
    1004                 :             : {
    1005                 :           7 :         return bms_is_subset(relids, walker->no_gather_scans);
    1006                 :             : }
        

Generated by: LCOV version 2.3.2-1