PostgreSQL Source Code git master
Loading...
Searching...
No Matches
pgpa_scan.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * pgpa_scan.c
4 * analysis of scans in Plan trees
5 *
6 * Copyright (c) 2016-2026, PostgreSQL Global Development Group
7 *
8 * contrib/pg_plan_advice/pgpa_scan.c
9 *
10 *-------------------------------------------------------------------------
11 */
12#include "postgres.h"
13
14#include "pgpa_scan.h"
15#include "pgpa_walker.h"
16
17#include "nodes/parsenodes.h"
18#include "parser/parsetree.h"
19
21 pgpa_scan_strategy strategy,
22 Bitmapset *relids);
23
24
25static RTEKind unique_nonjoin_rtekind(Bitmapset *relids, List *rtable);
26
27/*
28 * Build a pgpa_scan object for a Plan node and update the plan walker
29 * context as appropriate. If this is an Append or MergeAppend scan, also
30 * build pgpa_scan for any scans that were consolidated into this one by
31 * Append/MergeAppend pull-up.
32 *
33 * If there is at least one ElidedNode for this plan node, pass the uppermost
34 * one as elided_node, else pass NULL.
35 *
36 * Set the 'beneath_any_gather' node if we are underneath a Gather or
37 * Gather Merge node (except for a single-copy Gather node, for which
38 * GATHER or GATHER_MERGE advice should not be emitted).
39 *
40 * Set the 'within_join_problem' flag if we're inside of a join problem and
41 * not otherwise.
42 */
45 ElidedNode *elided_node,
47{
49 Bitmapset *relids = NULL;
50 int rti = -1;
51 List *child_append_relid_sets = NIL;
53
54 if (elided_node != NULL)
55 {
56 nodetype = elided_node->elided_type;
57 relids = elided_node->relids;
58
59 /*
60 * If setrefs processing elided an Append or MergeAppend node that had
61 * only one surviving child, it could be either a partitionwise
62 * operation or a setop over subqueries, depending on the rtekind.
63 *
64 * A setop over subqueries, or a trivial SubqueryScan that was elided,
65 * is an "ordinary" scan i.e. one for which we do not need to generate
66 * advice because the planner has not made any meaningful choice.
67 *
68 * Note that the PGPA_SCAN_PARTITIONWISE case also includes
69 * partitionwise joins; this module considers those to be a form of
70 * scan, since they lack internal structure that we can decompose.
71 *
72 * Note also that it's possible for relids to be NULL here, if the
73 * elided Append node is part of a partitionwise aggregate. In that
74 * case, it doesn't matter what strategy we choose, but we do need to
75 * avoid calling unique_nonjoin_rtekind(), which would fail an
76 * assertion.
77 */
78 if ((nodetype == T_Append || nodetype == T_MergeAppend) &&
79 relids != NULL &&
81 walker->pstmt->rtable) == RTE_RELATION)
82 strategy = PGPA_SCAN_PARTITIONWISE;
83 else
84 strategy = PGPA_SCAN_ORDINARY;
85
86 /* Join RTIs can be present, but advice never refers to them. */
87 relids = pgpa_filter_out_join_relids(relids, walker->pstmt->rtable);
88 }
89 else if ((rti = pgpa_scanrelid(plan)) != 0)
90 {
91 relids = bms_make_singleton(rti);
92
93 switch (nodeTag(plan))
94 {
95 case T_SeqScan:
96 strategy = PGPA_SCAN_SEQ;
97 break;
99 strategy = PGPA_SCAN_BITMAP_HEAP;
100 break;
101 case T_IndexScan:
102 strategy = PGPA_SCAN_INDEX;
103 break;
104 case T_IndexOnlyScan:
105 strategy = PGPA_SCAN_INDEX_ONLY;
106 break;
107 case T_TidScan:
108 case T_TidRangeScan:
109 strategy = PGPA_SCAN_TID;
110 break;
111 default:
112
113 /*
114 * This case includes a ForeignScan targeting a single
115 * relation; no other strategy is possible in that case, but
116 * see below, where things are different in multi-relation
117 * cases.
118 */
119 strategy = PGPA_SCAN_ORDINARY;
120 break;
121 }
122 }
124 {
125 /*
126 * Non-repeatable tablesample methods can be wrapped in a Materialize
127 * node that must be treated as part of the scan itself. See
128 * set_tablesample_rel_pathlist().
129 */
130 rti = pgpa_scanrelid(plan->lefttree);
131 relids = bms_make_singleton(rti);
132 strategy = PGPA_SCAN_ORDINARY;
133 }
134 else if ((relids = pgpa_relids(plan)) != NULL)
135 {
136 switch (nodeTag(plan))
137 {
138 case T_ForeignScan:
139
140 /*
141 * If multiple relations are being targeted by a single
142 * foreign scan, then the foreign join has been pushed to the
143 * remote side, and we want that to be reflected in the
144 * generated advice. We can't emit FOREIGN_JOIN() advice for a
145 * single relation, so treat that case as an ordinary scan.
146 */
147 if (bms_membership(relids) == BMS_MULTIPLE)
148 strategy = PGPA_SCAN_FOREIGN;
149 else
150 strategy = PGPA_SCAN_ORDINARY;
151 break;
152 case T_Append:
153
154 /*
155 * Append nodes can represent partitionwise scans of a
156 * relation, but when they implement a set operation, they are
157 * just ordinary scans.
158 */
159 if (unique_nonjoin_rtekind(relids, walker->pstmt->rtable)
160 == RTE_RELATION)
161 strategy = PGPA_SCAN_PARTITIONWISE;
162 else
163 strategy = PGPA_SCAN_ORDINARY;
164
165 /* Be sure to account for pulled-up scans. */
166 child_append_relid_sets =
167 ((Append *) plan)->child_append_relid_sets;
168 break;
169 case T_MergeAppend:
170 /* Same logic here as for Append, above. */
171 if (unique_nonjoin_rtekind(relids, walker->pstmt->rtable)
172 == RTE_RELATION)
173 strategy = PGPA_SCAN_PARTITIONWISE;
174 else
175 strategy = PGPA_SCAN_ORDINARY;
176
177 /* Be sure to account for pulled-up scans. */
178 child_append_relid_sets =
179 ((MergeAppend *) plan)->child_append_relid_sets;
180 break;
181 default:
182 strategy = PGPA_SCAN_ORDINARY;
183 break;
184 }
185
186
187 /* Join RTIs can be present, but advice never refers to them. */
188 relids = pgpa_filter_out_join_relids(relids, walker->pstmt->rtable);
189 }
190
191 /*
192 * If this is an Append or MergeAppend node into which subordinate Append
193 * or MergeAppend paths were merged, each of those merged paths is
194 * effectively another scan for which we need to account.
195 */
196 foreach_node(Bitmapset, child_relids, child_append_relid_sets)
197 {
199
201 pgpa_filter_out_join_relids(child_relids,
202 walker->pstmt->rtable);
203 (void) pgpa_make_scan(walker, plan, strategy,
205 }
206
207 /*
208 * If this plan node has no associated RTIs, it's not a scan. When the
209 * 'within_join_problem' flag is set, that's unexpected, so throw an
210 * error, else return quietly.
211 */
212 if (relids == NULL)
213 {
215 elog(ERROR, "plan node has no RTIs: %d", (int) nodeTag(plan));
216 return NULL;
217 }
218
219 /*
220 * Add the appropriate set of RTIs to walker->no_gather_scans.
221 *
222 * Add nothing if we're beneath a Gather or Gather Merge node, since
223 * NO_GATHER advice is clearly inappropriate in that situation.
224 *
225 * Add nothing if this is an Append or MergeAppend node, whether or not
226 * elided. We'll emit NO_GATHER() for the underlying scan, which is good
227 * enough.
228 */
231 walker->no_gather_scans =
232 bms_add_members(walker->no_gather_scans, relids);
233
234 /* Caller tells us whether NO_GATHER() advice for this scan is needed. */
235 return pgpa_make_scan(walker, plan, strategy, relids);
236}
237
238/*
239 * Create a single pgpa_scan object and update the pgpa_plan_walker_context.
240 */
241static pgpa_scan *
243 pgpa_scan_strategy strategy, Bitmapset *relids)
244{
245 pgpa_scan *scan;
246
247 /* Create the scan object. */
248 scan = palloc(sizeof(pgpa_scan));
249 scan->plan = plan;
250 scan->strategy = strategy;
251 scan->relids = relids;
252
253 /* Add it to the appropriate list. */
254 walker->scans[scan->strategy] = lappend(walker->scans[scan->strategy],
255 scan);
256
257 return scan;
258}
259
260/*
261 * Determine the unique rtekind of a set of relids.
262 */
263static RTEKind
265{
266 int rti = -1;
267 bool first = true;
268 RTEKind rtekind = RTE_RELATION; /* silence compiler warning */
269
270 Assert(relids != NULL);
271
272 while ((rti = bms_next_member(relids, rti)) >= 0)
273 {
274 RangeTblEntry *rte = rt_fetch(rti, rtable);
275
276 if (rte->rtekind == RTE_JOIN)
277 continue;
278
279 if (first)
280 {
281 rtekind = rte->rtekind;
282 first = false;
283 }
284 else if (rtekind != rte->rtekind)
285 elog(ERROR, "rtekind mismatch: %d vs. %d",
286 rtekind, rte->rtekind);
287 }
288
289 if (first)
290 elog(ERROR, "no non-RTE_JOIN RTEs found");
291
292 return rtekind;
293}
Bitmapset * bms_make_singleton(int x)
Definition bitmapset.c:217
int bms_next_member(const Bitmapset *a, int prevbit)
Definition bitmapset.c:1425
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:1036
BMS_Membership bms_membership(const Bitmapset *a)
Definition bitmapset.c:900
@ BMS_MULTIPLE
Definition bitmapset.h:73
#define Assert(condition)
Definition c.h:1002
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
List * lappend(List *list, void *datum)
Definition list.c:339
void * palloc(Size size)
Definition mcxt.c:1390
#define nodeTag(nodeptr)
Definition nodes.h:137
NodeTag
Definition nodes.h:27
RTEKind
@ RTE_JOIN
@ RTE_RELATION
#define rt_fetch(rangetable_index, rangetable)
Definition parsetree.h:31
#define NIL
Definition pg_list.h:68
#define foreach_node(type, var, lst)
Definition pg_list.h:528
#define plan(x)
Definition pg_regress.c:164
static RTEKind unique_nonjoin_rtekind(Bitmapset *relids, List *rtable)
Definition pgpa_scan.c:264
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
static pgpa_scan * pgpa_make_scan(pgpa_plan_walker_context *walker, Plan *plan, pgpa_scan_strategy strategy, Bitmapset *relids)
Definition pgpa_scan.c:242
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
@ PGPA_SCAN_ORDINARY
Definition pgpa_scan.h:57
Bitmapset * pgpa_filter_out_join_relids(Bitmapset *relids, List *rtable)
Bitmapset * pgpa_relids(Plan *plan)
bool pgpa_is_scan_level_materialize(Plan *plan)
Index pgpa_scanrelid(Plan *plan)
static int fb(int x)
NodeTag elided_type
Definition plannodes.h:1874
Bitmapset * relids
Definition plannodes.h:1875
Definition pg_list.h:54
Plan * plan
Definition pgpa_scan.h:75
pgpa_scan_strategy strategy
Definition pgpa_scan.h:76
Bitmapset * relids
Definition pgpa_scan.h:77