21
21
#include "nodes/nodeFuncs.h"
22
22
#include "optimizer/pathnode.h"
23
23
#include "optimizer/planmain.h"
24
+ #include "optimizer/planner.h"
24
25
#include "optimizer/tlist.h"
25
26
#include "tcop/utility.h"
26
27
#include "utils/lsyscache.h"
@@ -81,6 +82,10 @@ typedef struct
81
82
#define fix_scan_list (root , lst , rtoffset ) \
82
83
((List *) fix_scan_expr(root, (Node *) (lst), rtoffset))
83
84
85
+ static void add_rtes_to_flat_rtable (PlannerInfo * root , bool recursing );
86
+ static void flatten_unplanned_rtes (PlannerGlobal * glob , RangeTblEntry * rte );
87
+ static bool flatten_rtes_walker (Node * node , PlannerGlobal * glob );
88
+ static void add_rte_to_flat_rtable (PlannerGlobal * glob , RangeTblEntry * rte );
84
89
static Plan * set_plan_refs (PlannerInfo * root , Plan * plan , int rtoffset );
85
90
static Plan * set_indexonlyscan_references (PlannerInfo * root ,
86
91
IndexOnlyScan * plan ,
@@ -196,63 +201,11 @@ set_plan_references(PlannerInfo *root, Plan *plan)
196
201
ListCell * lc ;
197
202
198
203
/*
199
- * In the flat rangetable, we zero out substructure pointers that are not
200
- * needed by the executor; this reduces the storage space and copying cost
201
- * for cached plans. We keep only the alias and eref Alias fields, which
202
- * are needed by EXPLAIN, and the selectedCols and modifiedCols bitmaps,
203
- * which are needed for executor-startup permissions checking and for
204
- * trigger event checking.
204
+ * Add all the query's RTEs to the flattened rangetable. The live ones
205
+ * will have their rangetable indexes increased by rtoffset. (Additional
206
+ * RTEs, not referenced by the Plan tree, might get added after those.)
205
207
*/
206
- foreach (lc , root -> parse -> rtable )
207
- {
208
- RangeTblEntry * rte = (RangeTblEntry * ) lfirst (lc );
209
- RangeTblEntry * newrte ;
210
-
211
- /* flat copy to duplicate all the scalar fields */
212
- newrte = (RangeTblEntry * ) palloc (sizeof (RangeTblEntry ));
213
- memcpy (newrte , rte , sizeof (RangeTblEntry ));
214
-
215
- /* zap unneeded sub-structure */
216
- newrte -> subquery = NULL ;
217
- newrte -> joinaliasvars = NIL ;
218
- newrte -> funcexpr = NULL ;
219
- newrte -> funccoltypes = NIL ;
220
- newrte -> funccoltypmods = NIL ;
221
- newrte -> funccolcollations = NIL ;
222
- newrte -> values_lists = NIL ;
223
- newrte -> values_collations = NIL ;
224
- newrte -> ctecoltypes = NIL ;
225
- newrte -> ctecoltypmods = NIL ;
226
- newrte -> ctecolcollations = NIL ;
227
-
228
- glob -> finalrtable = lappend (glob -> finalrtable , newrte );
229
-
230
- /*
231
- * If it's a plain relation RTE, add the table to relationOids.
232
- *
233
- * We do this even though the RTE might be unreferenced in the plan
234
- * tree; this would correspond to cases such as views that were
235
- * expanded, child tables that were eliminated by constraint
236
- * exclusion, etc. Schema invalidation on such a rel must still force
237
- * rebuilding of the plan.
238
- *
239
- * Note we don't bother to avoid duplicate list entries. We could,
240
- * but it would probably cost more cycles than it would save.
241
- */
242
- if (newrte -> rtekind == RTE_RELATION )
243
- glob -> relationOids = lappend_oid (glob -> relationOids ,
244
- newrte -> relid );
245
- }
246
-
247
- /*
248
- * Check for RT index overflow; it's very unlikely, but if it did happen,
249
- * the executor would get confused by varnos that match the special varno
250
- * values.
251
- */
252
- if (IS_SPECIAL_VARNO (list_length (glob -> finalrtable )))
253
- ereport (ERROR ,
254
- (errcode (ERRCODE_PROGRAM_LIMIT_EXCEEDED ),
255
- errmsg ("too many range table entries" )));
208
+ add_rtes_to_flat_rtable (root , false);
256
209
257
210
/*
258
211
* Adjust RT indexes of PlanRowMarks and add to final rowmarks list
@@ -279,6 +232,192 @@ set_plan_references(PlannerInfo *root, Plan *plan)
279
232
return set_plan_refs (root , plan , rtoffset );
280
233
}
281
234
235
+ /*
236
+ * Extract RangeTblEntries from the plan's rangetable, and add to flat rtable
237
+ *
238
+ * This can recurse into subquery plans; "recursing" is true if so.
239
+ */
240
+ static void
241
+ add_rtes_to_flat_rtable (PlannerInfo * root , bool recursing )
242
+ {
243
+ PlannerGlobal * glob = root -> glob ;
244
+ Index rti ;
245
+ ListCell * lc ;
246
+
247
+ /*
248
+ * Add the query's own RTEs to the flattened rangetable.
249
+ *
250
+ * At top level, we must add all RTEs so that their indexes in the
251
+ * flattened rangetable match up with their original indexes. When
252
+ * recursing, we only care about extracting relation RTEs.
253
+ */
254
+ foreach (lc , root -> parse -> rtable )
255
+ {
256
+ RangeTblEntry * rte = (RangeTblEntry * ) lfirst (lc );
257
+
258
+ if (!recursing || rte -> rtekind == RTE_RELATION )
259
+ add_rte_to_flat_rtable (glob , rte );
260
+ }
261
+
262
+ /*
263
+ * If there are any dead subqueries, they are not referenced in the Plan
264
+ * tree, so we must add RTEs contained in them to the flattened rtable
265
+ * separately. (If we failed to do this, the executor would not perform
266
+ * expected permission checks for tables mentioned in such subqueries.)
267
+ *
268
+ * Note: this pass over the rangetable can't be combined with the previous
269
+ * one, because that would mess up the numbering of the live RTEs in the
270
+ * flattened rangetable.
271
+ */
272
+ rti = 1 ;
273
+ foreach (lc , root -> parse -> rtable )
274
+ {
275
+ RangeTblEntry * rte = (RangeTblEntry * ) lfirst (lc );
276
+
277
+ /*
278
+ * We should ignore inheritance-parent RTEs: their contents have been
279
+ * pulled up into our rangetable already. Also ignore any subquery
280
+ * RTEs without matching RelOptInfos, as they likewise have been
281
+ * pulled up.
282
+ */
283
+ if (rte -> rtekind == RTE_SUBQUERY && !rte -> inh )
284
+ {
285
+ RelOptInfo * rel = root -> simple_rel_array [rti ];
286
+
287
+ if (rel != NULL )
288
+ {
289
+ Assert (rel -> relid == rti ); /* sanity check on array */
290
+
291
+ /*
292
+ * The subquery might never have been planned at all, if it
293
+ * was excluded on the basis of self-contradictory constraints
294
+ * in our query level. In this case apply
295
+ * flatten_unplanned_rtes.
296
+ *
297
+ * If it was planned but the plan is dummy, we assume that it
298
+ * has been omitted from our plan tree (see
299
+ * set_subquery_pathlist), and recurse to pull up its RTEs.
300
+ *
301
+ * Otherwise, it should be represented by a SubqueryScan node
302
+ * somewhere in our plan tree, and we'll pull up its RTEs when
303
+ * we process that plan node.
304
+ *
305
+ * However, if we're recursing, then we should pull up RTEs
306
+ * whether the subplan is dummy or not, because we've found
307
+ * that some upper query level is treating this one as dummy,
308
+ * and so we won't scan this level's plan tree at all.
309
+ */
310
+ if (rel -> subplan == NULL )
311
+ flatten_unplanned_rtes (glob , rte );
312
+ else if (recursing || is_dummy_plan (rel -> subplan ))
313
+ {
314
+ Assert (rel -> subroot != NULL );
315
+ add_rtes_to_flat_rtable (rel -> subroot , true);
316
+ }
317
+ }
318
+ }
319
+ rti ++ ;
320
+ }
321
+ }
322
+
323
+ /*
324
+ * Extract RangeTblEntries from a subquery that was never planned at all
325
+ */
326
+ static void
327
+ flatten_unplanned_rtes (PlannerGlobal * glob , RangeTblEntry * rte )
328
+ {
329
+ /* Use query_tree_walker to find all RTEs in the parse tree */
330
+ (void ) query_tree_walker (rte -> subquery ,
331
+ flatten_rtes_walker ,
332
+ (void * ) glob ,
333
+ QTW_EXAMINE_RTES );
334
+ }
335
+
336
+ static bool
337
+ flatten_rtes_walker (Node * node , PlannerGlobal * glob )
338
+ {
339
+ if (node == NULL )
340
+ return false;
341
+ if (IsA (node , RangeTblEntry ))
342
+ {
343
+ RangeTblEntry * rte = (RangeTblEntry * ) node ;
344
+
345
+ /* As above, we need only save relation RTEs */
346
+ if (rte -> rtekind == RTE_RELATION )
347
+ add_rte_to_flat_rtable (glob , rte );
348
+ return false;
349
+ }
350
+ if (IsA (node , Query ))
351
+ {
352
+ /* Recurse into subselects */
353
+ return query_tree_walker ((Query * ) node ,
354
+ flatten_rtes_walker ,
355
+ (void * ) glob ,
356
+ QTW_EXAMINE_RTES );
357
+ }
358
+ return expression_tree_walker (node , flatten_rtes_walker ,
359
+ (void * ) glob );
360
+ }
361
+
362
+ /*
363
+ * Add (a copy of) the given RTE to the final rangetable
364
+ *
365
+ * In the flat rangetable, we zero out substructure pointers that are not
366
+ * needed by the executor; this reduces the storage space and copying cost
367
+ * for cached plans. We keep only the alias and eref Alias fields, which
368
+ * are needed by EXPLAIN, and the selectedCols and modifiedCols bitmaps,
369
+ * which are needed for executor-startup permissions checking and for
370
+ * trigger event checking.
371
+ */
372
+ static void
373
+ add_rte_to_flat_rtable (PlannerGlobal * glob , RangeTblEntry * rte )
374
+ {
375
+ RangeTblEntry * newrte ;
376
+
377
+ /* flat copy to duplicate all the scalar fields */
378
+ newrte = (RangeTblEntry * ) palloc (sizeof (RangeTblEntry ));
379
+ memcpy (newrte , rte , sizeof (RangeTblEntry ));
380
+
381
+ /* zap unneeded sub-structure */
382
+ newrte -> subquery = NULL ;
383
+ newrte -> joinaliasvars = NIL ;
384
+ newrte -> funcexpr = NULL ;
385
+ newrte -> funccoltypes = NIL ;
386
+ newrte -> funccoltypmods = NIL ;
387
+ newrte -> funccolcollations = NIL ;
388
+ newrte -> values_lists = NIL ;
389
+ newrte -> values_collations = NIL ;
390
+ newrte -> ctecoltypes = NIL ;
391
+ newrte -> ctecoltypmods = NIL ;
392
+ newrte -> ctecolcollations = NIL ;
393
+
394
+ glob -> finalrtable = lappend (glob -> finalrtable , newrte );
395
+
396
+ /*
397
+ * Check for RT index overflow; it's very unlikely, but if it did happen,
398
+ * the executor would get confused by varnos that match the special varno
399
+ * values.
400
+ */
401
+ if (IS_SPECIAL_VARNO (list_length (glob -> finalrtable )))
402
+ ereport (ERROR ,
403
+ (errcode (ERRCODE_PROGRAM_LIMIT_EXCEEDED ),
404
+ errmsg ("too many range table entries" )));
405
+
406
+ /*
407
+ * If it's a plain relation RTE, add the table to relationOids.
408
+ *
409
+ * We do this even though the RTE might be unreferenced in the plan tree;
410
+ * this would correspond to cases such as views that were expanded, child
411
+ * tables that were eliminated by constraint exclusion, etc. Schema
412
+ * invalidation on such a rel must still force rebuilding of the plan.
413
+ *
414
+ * Note we don't bother to avoid making duplicate list entries. We could,
415
+ * but it would probably cost more cycles than it would save.
416
+ */
417
+ if (newrte -> rtekind == RTE_RELATION )
418
+ glob -> relationOids = lappend_oid (glob -> relationOids , newrte -> relid );
419
+ }
420
+
282
421
/*
283
422
* set_plan_refs: recurse through the Plan nodes of a single subquery level
284
423
*/
0 commit comments