Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * rewriteManip.c
4 : : *
5 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
6 : : * Portions Copyright (c) 1994, Regents of the University of California
7 : : *
8 : : *
9 : : * IDENTIFICATION
10 : : * src/backend/rewrite/rewriteManip.c
11 : : *
12 : : *-------------------------------------------------------------------------
13 : : */
14 : : #include "postgres.h"
15 : :
16 : : #include "access/attmap.h"
17 : : #include "catalog/pg_type.h"
18 : : #include "nodes/makefuncs.h"
19 : : #include "nodes/nodeFuncs.h"
20 : : #include "nodes/pathnodes.h"
21 : : #include "nodes/plannodes.h"
22 : : #include "parser/parse_coerce.h"
23 : : #include "parser/parse_relation.h"
24 : : #include "parser/parsetree.h"
25 : : #include "rewrite/rewriteManip.h"
26 : : #include "utils/lsyscache.h"
27 : :
28 : :
29 : : typedef struct
30 : : {
31 : : int sublevels_up;
32 : : } contain_aggs_of_level_context;
33 : :
34 : : typedef struct
35 : : {
36 : : int agg_location;
37 : : int sublevels_up;
38 : : } locate_agg_of_level_context;
39 : :
40 : : typedef struct
41 : : {
42 : : int win_location;
43 : : } locate_windowfunc_context;
44 : :
45 : : typedef struct
46 : : {
47 : : const Bitmapset *target_relids;
48 : : const Bitmapset *added_relids;
49 : : int sublevels_up;
50 : : } add_nulling_relids_context;
51 : :
52 : : typedef struct
53 : : {
54 : : const Bitmapset *removable_relids;
55 : : const Bitmapset *except_relids;
56 : : int sublevels_up;
57 : : } remove_nulling_relids_context;
58 : :
59 : : static bool contain_aggs_of_level_walker(Node *node,
60 : : contain_aggs_of_level_context *context);
61 : : static bool locate_agg_of_level_walker(Node *node,
62 : : locate_agg_of_level_context *context);
63 : : static bool contain_windowfuncs_walker(Node *node, void *context);
64 : : static bool locate_windowfunc_walker(Node *node,
65 : : locate_windowfunc_context *context);
66 : : static bool checkExprHasSubLink_walker(Node *node, void *context);
67 : : static Relids adjust_relid_set(Relids relids, int oldrelid, int newrelid);
68 : : static Node *add_nulling_relids_mutator(Node *node,
69 : : add_nulling_relids_context *context);
70 : : static Node *remove_nulling_relids_mutator(Node *node,
71 : : remove_nulling_relids_context *context);
72 : :
73 : :
74 : : /*
75 : : * contain_aggs_of_level -
76 : : * Check if an expression contains an aggregate function call of a
77 : : * specified query level.
78 : : *
79 : : * The objective of this routine is to detect whether there are aggregates
80 : : * belonging to the given query level. Aggregates belonging to subqueries
81 : : * or outer queries do NOT cause a true result. We must recurse into
82 : : * subqueries to detect outer-reference aggregates that logically belong to
83 : : * the specified query level.
84 : : */
85 : : bool
6603 tgl@sss.pgh.pa.us 86 :CBC 1824 : contain_aggs_of_level(Node *node, int levelsup)
87 : : {
88 : : contain_aggs_of_level_context context;
89 : :
90 : 1824 : context.sublevels_up = levelsup;
91 : :
92 : : /*
93 : : * Must be prepared to start with a Query or a bare expression tree; if
94 : : * it's a Query, we don't want to increment sublevels_up.
95 : : */
8647 96 : 1824 : return query_or_expression_tree_walker(node,
97 : : contain_aggs_of_level_walker,
98 : : &context,
99 : : 0);
100 : : }
101 : :
102 : : static bool
6603 103 : 8738 : contain_aggs_of_level_walker(Node *node,
104 : : contain_aggs_of_level_context *context)
105 : : {
9684 106 [ + + ]: 8738 : if (node == NULL)
107 : 1202 : return false;
108 [ - + ]: 7536 : if (IsA(node, Aggref))
109 : : {
8507 tgl@sss.pgh.pa.us 110 [ # # ]:UBC 0 : if (((Aggref *) node)->agglevelsup == context->sublevels_up)
7645 bruce@momjian.us 111 : 0 : return true; /* abort the tree traversal and return true */
112 : : /* else fall through to examine argument */
113 : : }
4145 andres@anarazel.de 114 [ - + ]:CBC 7536 : if (IsA(node, GroupingFunc))
115 : : {
4145 andres@anarazel.de 116 [ # # ]:UBC 0 : if (((GroupingFunc *) node)->agglevelsup == context->sublevels_up)
117 : 0 : return true;
118 : : /* else fall through to examine argument */
119 : : }
8507 tgl@sss.pgh.pa.us 120 [ + + ]:CBC 7536 : if (IsA(node, Query))
121 : : {
122 : : /* Recurse into subselects */
123 : : bool result;
124 : :
125 : 86 : context->sublevels_up++;
126 : 86 : result = query_tree_walker((Query *) node,
127 : : contain_aggs_of_level_walker,
128 : : context, 0);
129 : 86 : context->sublevels_up--;
130 : 86 : return result;
131 : : }
6603 132 : 7450 : return expression_tree_walker(node, contain_aggs_of_level_walker,
133 : : context);
134 : : }
135 : :
136 : : /*
137 : : * locate_agg_of_level -
138 : : * Find the parse location of any aggregate of the specified query level.
139 : : *
140 : : * Returns -1 if no such agg is in the querytree, or if they all have
141 : : * unknown parse location. (The former case is probably caller error,
142 : : * but we don't bother to distinguish it from the latter case.)
143 : : *
144 : : * Note: it might seem appropriate to merge this functionality into
145 : : * contain_aggs_of_level, but that would complicate that function's API.
146 : : * Currently, the only uses of this function are for error reporting,
147 : : * and so shaving cycles probably isn't very important.
148 : : */
149 : : int
6593 150 : 40 : locate_agg_of_level(Node *node, int levelsup)
151 : : {
152 : : locate_agg_of_level_context context;
153 : :
6310 bruce@momjian.us 154 : 40 : context.agg_location = -1; /* in case we find nothing */
6593 tgl@sss.pgh.pa.us 155 : 40 : context.sublevels_up = levelsup;
156 : :
157 : : /*
158 : : * Must be prepared to start with a Query or a bare expression tree; if
159 : : * it's a Query, we don't want to increment sublevels_up.
160 : : */
161 : 40 : (void) query_or_expression_tree_walker(node,
162 : : locate_agg_of_level_walker,
163 : : &context,
164 : : 0);
165 : :
166 : 40 : return context.agg_location;
167 : : }
168 : :
169 : : static bool
170 : 160 : locate_agg_of_level_walker(Node *node,
171 : : locate_agg_of_level_context *context)
172 : : {
173 [ + + ]: 160 : if (node == NULL)
174 : 8 : return false;
175 [ + + ]: 152 : if (IsA(node, Aggref))
176 : : {
177 [ + + ]: 36 : if (((Aggref *) node)->agglevelsup == context->sublevels_up &&
178 [ + - ]: 32 : ((Aggref *) node)->location >= 0)
179 : : {
180 : 32 : context->agg_location = ((Aggref *) node)->location;
181 : 32 : return true; /* abort the tree traversal and return true */
182 : : }
183 : : /* else fall through to examine argument */
184 : : }
4145 andres@anarazel.de 185 [ - + ]: 120 : if (IsA(node, GroupingFunc))
186 : : {
4145 andres@anarazel.de 187 [ # # ]:UBC 0 : if (((GroupingFunc *) node)->agglevelsup == context->sublevels_up &&
188 [ # # ]: 0 : ((GroupingFunc *) node)->location >= 0)
189 : : {
190 : 0 : context->agg_location = ((GroupingFunc *) node)->location;
191 : 0 : return true; /* abort the tree traversal and return true */
192 : : }
193 : : }
6593 tgl@sss.pgh.pa.us 194 [ + + ]:CBC 120 : if (IsA(node, Query))
195 : : {
196 : : /* Recurse into subselects */
197 : : bool result;
198 : :
199 : 8 : context->sublevels_up++;
200 : 8 : result = query_tree_walker((Query *) node,
201 : : locate_agg_of_level_walker,
202 : : context, 0);
203 : 8 : context->sublevels_up--;
204 : 8 : return result;
205 : : }
661 peter@eisentraut.org 206 : 112 : return expression_tree_walker(node, locate_agg_of_level_walker, context);
207 : : }
208 : :
209 : : /*
210 : : * contain_windowfuncs -
211 : : * Check if an expression contains a window function call of the
212 : : * current query level.
213 : : */
214 : : bool
5154 tgl@sss.pgh.pa.us 215 : 8087 : contain_windowfuncs(Node *node)
216 : : {
217 : : /*
218 : : * Must be prepared to start with a Query or a bare expression tree; if
219 : : * it's a Query, we don't want to increment sublevels_up.
220 : : */
6475 221 : 8087 : return query_or_expression_tree_walker(node,
222 : : contain_windowfuncs_walker,
223 : : NULL,
224 : : 0);
225 : : }
226 : :
227 : : static bool
228 : 8881 : contain_windowfuncs_walker(Node *node, void *context)
229 : : {
230 [ + + ]: 8881 : if (node == NULL)
231 : 120 : return false;
232 [ + + ]: 8761 : if (IsA(node, WindowFunc))
6310 bruce@momjian.us 233 : 9 : return true; /* abort the tree traversal and return true */
234 : : /* Mustn't recurse into subselects */
661 peter@eisentraut.org 235 : 8752 : return expression_tree_walker(node, contain_windowfuncs_walker, context);
236 : : }
237 : :
238 : : /*
239 : : * locate_windowfunc -
240 : : * Find the parse location of any windowfunc of the current query level.
241 : : *
242 : : * Returns -1 if no such windowfunc is in the querytree, or if they all have
243 : : * unknown parse location. (The former case is probably caller error,
244 : : * but we don't bother to distinguish it from the latter case.)
245 : : *
246 : : * Note: it might seem appropriate to merge this functionality into
247 : : * contain_windowfuncs, but that would complicate that function's API.
248 : : * Currently, the only uses of this function are for error reporting,
249 : : * and so shaving cycles probably isn't very important.
250 : : */
251 : : int
6475 tgl@sss.pgh.pa.us 252 : 4 : locate_windowfunc(Node *node)
253 : : {
254 : : locate_windowfunc_context context;
255 : :
6310 bruce@momjian.us 256 : 4 : context.win_location = -1; /* in case we find nothing */
257 : :
258 : : /*
259 : : * Must be prepared to start with a Query or a bare expression tree; if
260 : : * it's a Query, we don't want to increment sublevels_up.
261 : : */
6475 tgl@sss.pgh.pa.us 262 : 4 : (void) query_or_expression_tree_walker(node,
263 : : locate_windowfunc_walker,
264 : : &context,
265 : : 0);
266 : :
267 : 4 : return context.win_location;
268 : : }
269 : :
270 : : static bool
271 : 4 : locate_windowfunc_walker(Node *node, locate_windowfunc_context *context)
272 : : {
273 [ - + ]: 4 : if (node == NULL)
6475 tgl@sss.pgh.pa.us 274 :UBC 0 : return false;
6475 tgl@sss.pgh.pa.us 275 [ + - ]:CBC 4 : if (IsA(node, WindowFunc))
276 : : {
277 [ + - ]: 4 : if (((WindowFunc *) node)->location >= 0)
278 : : {
279 : 4 : context->win_location = ((WindowFunc *) node)->location;
280 : 4 : return true; /* abort the tree traversal and return true */
281 : : }
282 : : /* else fall through to examine argument */
283 : : }
284 : : /* Mustn't recurse into subselects */
661 peter@eisentraut.org 285 :UBC 0 : return expression_tree_walker(node, locate_windowfunc_walker, context);
286 : : }
287 : :
288 : : /*
289 : : * checkExprHasSubLink -
290 : : * Check if an expression contains a SubLink.
291 : : */
292 : : bool
9684 tgl@sss.pgh.pa.us 293 :CBC 100239 : checkExprHasSubLink(Node *node)
294 : : {
295 : : /*
296 : : * If a Query is passed, examine it --- but we should not recurse into
297 : : * sub-Queries that are in its rangetable or CTE list.
298 : : */
8647 299 : 100239 : return query_or_expression_tree_walker(node,
300 : : checkExprHasSubLink_walker,
301 : : NULL,
302 : : QTW_IGNORE_RC_SUBQUERIES);
303 : : }
304 : :
305 : : static bool
9684 306 : 169348 : checkExprHasSubLink_walker(Node *node, void *context)
307 : : {
308 [ + + ]: 169348 : if (node == NULL)
309 : 3088 : return false;
310 [ + + ]: 166260 : if (IsA(node, SubLink))
7645 bruce@momjian.us 311 : 1280 : return true; /* abort the tree traversal and return true */
9684 tgl@sss.pgh.pa.us 312 : 164980 : return expression_tree_walker(node, checkExprHasSubLink_walker, context);
313 : : }
314 : :
315 : : /*
316 : : * Check for MULTIEXPR Param within expression tree
317 : : *
318 : : * We intentionally don't descend into SubLinks: only Params at the current
319 : : * query level are of interest.
320 : : */
321 : : static bool
4477 322 : 157009 : contains_multiexpr_param(Node *node, void *context)
323 : : {
324 [ + + ]: 157009 : if (node == NULL)
325 : 2646 : return false;
326 [ + + ]: 154363 : if (IsA(node, Param))
327 : : {
328 [ - + ]: 399 : if (((Param *) node)->paramkind == PARAM_MULTIEXPR)
4477 tgl@sss.pgh.pa.us 329 :UBC 0 : return true; /* abort the tree traversal and return true */
4477 tgl@sss.pgh.pa.us 330 :CBC 399 : return false;
331 : : }
332 : 153964 : return expression_tree_walker(node, contains_multiexpr_param, context);
333 : : }
334 : :
335 : : /*
336 : : * CombineRangeTables
337 : : * Adds the RTEs of 'src_rtable' into 'dst_rtable'
338 : : *
339 : : * This also adds the RTEPermissionInfos of 'src_perminfos' (belonging to the
340 : : * RTEs in 'src_rtable') into *dst_perminfos and also updates perminfoindex of
341 : : * the RTEs in 'src_rtable' to now point to the perminfos' indexes in
342 : : * *dst_perminfos.
343 : : *
344 : : * Note that this changes both 'dst_rtable' and 'dst_perminfos' destructively,
345 : : * so the caller should have better passed safe-to-modify copies.
346 : : */
347 : : void
1384 alvherre@alvh.no-ip. 348 : 41939 : CombineRangeTables(List **dst_rtable, List **dst_perminfos,
349 : : List *src_rtable, List *src_perminfos)
350 : : {
351 : : ListCell *l;
352 : 41939 : int offset = list_length(*dst_perminfos);
353 : :
354 [ + + ]: 41939 : if (offset > 0)
355 : : {
356 [ + + + + : 100538 : foreach(l, src_rtable)
+ + ]
357 : : {
358 : 65510 : RangeTblEntry *rte = lfirst_node(RangeTblEntry, l);
359 : :
360 [ + + ]: 65510 : if (rte->perminfoindex > 0)
361 : 32349 : rte->perminfoindex += offset;
362 : : }
363 : : }
364 : :
365 : 41939 : *dst_perminfos = list_concat(*dst_perminfos, src_perminfos);
366 : 41939 : *dst_rtable = list_concat(*dst_rtable, src_rtable);
367 : 41939 : }
368 : :
369 : : /*
370 : : * OffsetVarNodes - adjust Vars when appending one query's RT to another
371 : : *
372 : : * Find all Var nodes in the given tree with varlevelsup == sublevels_up,
373 : : * and increment their varno fields (rangetable indexes) by 'offset'.
374 : : * The varnosyn fields are adjusted similarly. Also, adjust other nodes
375 : : * that contain rangetable indexes, such as RangeTblRef and JoinExpr.
376 : : *
377 : : * NOTE: although this has the form of a walker, we cheat and modify the
378 : : * nodes in-place. The given expression tree should have been copied
379 : : * earlier to ensure that no unwanted side-effects occur!
380 : : */
381 : :
382 : : typedef struct
383 : : {
384 : : int offset;
385 : : int sublevels_up;
386 : : } OffsetVarNodes_context;
387 : :
388 : : static bool
9851 tgl@sss.pgh.pa.us 389 : 1912396 : OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context)
390 : : {
391 [ + + ]: 1912396 : if (node == NULL)
392 : 615907 : return false;
393 [ + + ]: 1296489 : if (IsA(node, Var))
394 : : {
395 : 662003 : Var *var = (Var *) node;
396 : :
397 [ + + ]: 662003 : if (var->varlevelsup == context->sublevels_up)
398 : : {
399 : 579867 : var->varno += context->offset;
73 drowley@postgresql.o 400 :GNC 579867 : var->varnullingrels = bms_offset_members(var->varnullingrels,
401 : : context->offset);
2446 tgl@sss.pgh.pa.us 402 [ + - ]:CBC 579867 : if (var->varnosyn > 0)
403 : 579867 : var->varnosyn += context->offset;
404 : : }
9851 405 : 662003 : return false;
406 : : }
7041 407 [ - + ]: 634486 : if (IsA(node, CurrentOfExpr))
408 : : {
7041 tgl@sss.pgh.pa.us 409 :UBC 0 : CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
410 : :
411 [ # # ]: 0 : if (context->sublevels_up == 0)
412 : 0 : cexpr->cvarno += context->offset;
413 : 0 : return false;
414 : : }
9504 tgl@sss.pgh.pa.us 415 [ + + ]:CBC 634486 : if (IsA(node, RangeTblRef))
416 : : {
9313 bruce@momjian.us 417 : 55527 : RangeTblRef *rtr = (RangeTblRef *) node;
418 : :
9504 tgl@sss.pgh.pa.us 419 [ + + ]: 55527 : if (context->sublevels_up == 0)
420 : 50360 : rtr->rtindex += context->offset;
421 : : /* the subquery itself is visited separately */
9851 422 : 55527 : return false;
423 : : }
8958 424 [ + + ]: 578959 : if (IsA(node, JoinExpr))
425 : : {
8782 bruce@momjian.us 426 : 11319 : JoinExpr *j = (JoinExpr *) node;
427 : :
6416 tgl@sss.pgh.pa.us 428 [ + + + + ]: 11319 : if (j->rtindex && context->sublevels_up == 0)
8958 429 : 10236 : j->rtindex += context->offset;
430 : : /* fall through to examine children */
431 : : }
6543 432 [ + + ]: 578959 : if (IsA(node, PlaceHolderVar))
433 : : {
434 : 439 : PlaceHolderVar *phv = (PlaceHolderVar *) node;
435 : :
436 [ + + ]: 439 : if (phv->phlevelsup == context->sublevels_up)
437 : : {
73 drowley@postgresql.o 438 :GNC 339 : phv->phrels = bms_offset_members(phv->phrels,
439 : : context->offset);
440 : 339 : phv->phnullingrels = bms_offset_members(phv->phnullingrels,
441 : : context->offset);
442 : : }
443 : : /* fall through to examine children */
444 : : }
7537 tgl@sss.pgh.pa.us 445 [ + + ]:CBC 578959 : if (IsA(node, AppendRelInfo))
446 : : {
447 : 764 : AppendRelInfo *appinfo = (AppendRelInfo *) node;
448 : :
449 [ + - ]: 764 : if (context->sublevels_up == 0)
450 : : {
451 : 764 : appinfo->parent_relid += context->offset;
452 : 764 : appinfo->child_relid += context->offset;
453 : : }
454 : : /* fall through to examine children */
455 : : }
456 : : /* Shouldn't need to handle other planner auxiliary nodes here */
5496 457 [ - + ]: 578959 : Assert(!IsA(node, PlanRowMark));
6542 458 [ - + ]: 578959 : Assert(!IsA(node, SpecialJoinInfo));
459 [ - + ]: 578959 : Assert(!IsA(node, PlaceHolderInfo));
5799 460 [ - + ]: 578959 : Assert(!IsA(node, MinMaxAggInfo));
461 : :
9851 462 [ + + ]: 578959 : if (IsA(node, Query))
463 : : {
464 : : /* Recurse into subselects */
465 : : bool result;
466 : :
9504 467 : 4280 : context->sublevels_up++;
468 : 4280 : result = query_tree_walker((Query *) node, OffsetVarNodes_walker,
469 : : context, 0);
470 : 4280 : context->sublevels_up--;
471 : 4280 : return result;
472 : : }
661 peter@eisentraut.org 473 : 574679 : return expression_tree_walker(node, OffsetVarNodes_walker, context);
474 : : }
475 : :
476 : : void
9851 tgl@sss.pgh.pa.us 477 : 76244 : OffsetVarNodes(Node *node, int offset, int sublevels_up)
478 : : {
479 : : OffsetVarNodes_context context;
480 : :
481 : 76244 : context.offset = offset;
482 : 76244 : context.sublevels_up = sublevels_up;
483 : :
484 : : /*
485 : : * Must be prepared to start with a Query or a bare expression tree; if
486 : : * it's a Query, go straight to query_tree_walker to make sure that
487 : : * sublevels_up doesn't get incremented prematurely.
488 : : */
9504 489 [ + + + + ]: 76244 : if (node && IsA(node, Query))
9419 490 : 38122 : {
9313 bruce@momjian.us 491 : 38122 : Query *qry = (Query *) node;
492 : :
493 : : /*
494 : : * If we are starting at a Query, and sublevels_up is zero, then we
495 : : * must also fix rangetable indexes in the Query itself --- namely
496 : : * resultRelation, mergeTargetRelation, exclRelIndex and rowMarks
497 : : * entries. sublevels_up cannot be zero when recursing into a
498 : : * subquery, so there's no need to have the same logic inside
499 : : * OffsetVarNodes_walker.
500 : : */
9419 tgl@sss.pgh.pa.us 501 [ + - ]: 38122 : if (sublevels_up == 0)
502 : : {
503 : : ListCell *l;
504 : :
505 [ + + ]: 38122 : if (qry->resultRelation)
506 : 900 : qry->resultRelation += offset;
507 : :
934 dean.a.rasheed@gmail 508 [ - + ]: 38122 : if (qry->mergeTargetRelation)
934 dean.a.rasheed@gmail 509 :UBC 0 : qry->mergeTargetRelation += offset;
510 : :
4148 andres@anarazel.de 511 [ + + + + ]:CBC 38122 : if (qry->onConflict && qry->onConflict->exclRelIndex)
512 : 44 : qry->onConflict->exclRelIndex += offset;
513 : :
9419 tgl@sss.pgh.pa.us 514 [ + + + + : 38230 : foreach(l, qry->rowMarks)
+ + ]
515 : : {
7448 516 : 108 : RowMarkClause *rc = (RowMarkClause *) lfirst(l);
517 : :
518 : 108 : rc->rti += offset;
519 : : }
520 : : }
661 peter@eisentraut.org 521 : 38122 : query_tree_walker(qry, OffsetVarNodes_walker, &context, 0);
522 : : }
523 : : else
9504 tgl@sss.pgh.pa.us 524 : 38122 : OffsetVarNodes_walker(node, &context);
9851 525 : 76244 : }
526 : :
527 : : /*
528 : : * ChangeVarNodes - adjust Var nodes for a specific change of RT index
529 : : *
530 : : * Find all Var nodes in the given tree belonging to a specific relation
531 : : * (identified by sublevels_up and rt_index), and change their varno fields
532 : : * to 'new_index', and update varnosyn and varnullingrels fields similarly.
533 : : * Also adjust other nodes that contain rangetable indexes, such as
534 : : * RangeTblRef and JoinExpr.
535 : : *
536 : : * Also, new_index can be INVALID_VAR to indicate that we are deleting the
537 : : * given relid from the tree. In this case we expect to find rt_index only
538 : : * in Relids fields (varnullingrels, phnullingrels, phrels), never in any
539 : : * field that identifies a single relation. The exception is varnosyn,
540 : : * which may name an aliased join being removed; the join's RTE remains in
541 : : * the rangetable, so we leave such syntactic references unchanged.
542 : : *
543 : : * NOTE: although this has the form of a walker, we cheat and modify the
544 : : * nodes in-place. The given expression tree should have been copied
545 : : * earlier to ensure that no unwanted side-effects occur!
546 : : */
547 : :
548 : : typedef struct
549 : : {
550 : : int rt_index;
551 : : int new_index;
552 : : int sublevels_up;
553 : : } ChangeVarNodes_context;
554 : :
555 : : static bool
556 : 1729388 : ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
557 : : {
558 [ + + ]: 1729388 : if (node == NULL)
559 : 497213 : return false;
560 [ + + ]: 1232175 : if (IsA(node, Var))
561 : : {
562 : 351015 : Var *var = (Var *) node;
563 : :
1329 564 [ + + ]: 351015 : if (var->varlevelsup == context->sublevels_up)
565 : : {
566 [ + + ]: 342635 : if (var->varno == context->rt_index)
567 : : {
23 568 [ - + ]: 33349 : Assert(context->new_index != INVALID_VAR);
1329 569 : 33349 : var->varno = context->new_index;
570 : : }
571 : 342635 : var->varnullingrels = adjust_relid_set(var->varnullingrels,
572 : : context->rt_index,
573 : : context->new_index);
574 : : /* when deleting, leave syntactic refs to a removed join alone */
1 rguo@postgresql.org 575 [ + + ]: 342635 : if (var->varnosyn == context->rt_index &&
576 [ + + ]: 33369 : context->new_index != INVALID_VAR)
2446 tgl@sss.pgh.pa.us 577 : 33349 : var->varnosyn = context->new_index;
578 : : }
9851 579 : 351015 : return false;
580 : : }
7041 581 [ - + ]: 881160 : if (IsA(node, CurrentOfExpr))
582 : : {
7041 tgl@sss.pgh.pa.us 583 :UBC 0 : CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
584 : :
585 [ # # ]: 0 : if (context->sublevels_up == 0 &&
586 [ # # ]: 0 : cexpr->cvarno == context->rt_index)
587 : : {
23 588 [ # # ]: 0 : Assert(context->new_index != INVALID_VAR);
7041 589 : 0 : cexpr->cvarno = context->new_index;
590 : : }
591 : 0 : return false;
592 : : }
501 akorotkov@postgresql 593 [ + + ]:CBC 881160 : if (IsA(node, RangeTblRef))
594 : : {
9313 bruce@momjian.us 595 : 37092 : RangeTblRef *rtr = (RangeTblRef *) node;
596 : :
9504 tgl@sss.pgh.pa.us 597 [ + + ]: 37092 : if (context->sublevels_up == 0 &&
598 [ + + ]: 34905 : rtr->rtindex == context->rt_index)
599 : : {
23 600 [ - + ]: 1162 : Assert(context->new_index != INVALID_VAR);
9504 601 : 1162 : rtr->rtindex = context->new_index;
602 : : }
603 : : /* the subquery itself is visited separately */
9851 604 : 37092 : return false;
605 : : }
8958 606 [ + + ]: 844068 : if (IsA(node, JoinExpr))
607 : : {
8782 bruce@momjian.us 608 : 14218 : JoinExpr *j = (JoinExpr *) node;
609 : :
8958 tgl@sss.pgh.pa.us 610 [ + + ]: 14218 : if (context->sublevels_up == 0 &&
611 [ - + ]: 14150 : j->rtindex == context->rt_index)
612 : : {
23 tgl@sss.pgh.pa.us 613 [ # # ]:UBC 0 : Assert(context->new_index != INVALID_VAR);
8958 614 : 0 : j->rtindex = context->new_index;
615 : : }
616 : : /* fall through to examine children */
617 : : }
6543 tgl@sss.pgh.pa.us 618 [ + + ]:CBC 844068 : if (IsA(node, PlaceHolderVar))
619 : : {
620 : 818 : PlaceHolderVar *phv = (PlaceHolderVar *) node;
621 : :
622 [ + - ]: 818 : if (phv->phlevelsup == context->sublevels_up)
623 : : {
624 : 818 : phv->phrels = adjust_relid_set(phv->phrels,
625 : : context->rt_index,
626 : : context->new_index);
1329 627 : 818 : phv->phnullingrels = adjust_relid_set(phv->phnullingrels,
628 : : context->rt_index,
629 : : context->new_index);
630 : : }
631 : : /* fall through to examine children */
632 : : }
5496 633 [ - + ]: 844068 : if (IsA(node, PlanRowMark))
634 : : {
5496 tgl@sss.pgh.pa.us 635 :UBC 0 : PlanRowMark *rowmark = (PlanRowMark *) node;
636 : :
637 [ # # ]: 0 : if (context->sublevels_up == 0)
638 : : {
639 [ # # ]: 0 : if (rowmark->rti == context->rt_index)
640 : : {
23 641 [ # # ]: 0 : Assert(context->new_index != INVALID_VAR);
5496 642 : 0 : rowmark->rti = context->new_index;
643 : : }
644 [ # # ]: 0 : if (rowmark->prti == context->rt_index)
645 : : {
23 646 [ # # ]: 0 : Assert(context->new_index != INVALID_VAR);
5496 647 : 0 : rowmark->prti = context->new_index;
648 : : }
649 : : }
650 : 0 : return false;
651 : : }
7537 tgl@sss.pgh.pa.us 652 [ + + ]:CBC 844068 : if (IsA(node, AppendRelInfo))
653 : : {
654 : 40 : AppendRelInfo *appinfo = (AppendRelInfo *) node;
655 : :
656 [ + - ]: 40 : if (context->sublevels_up == 0)
657 : : {
658 [ - + ]: 40 : if (appinfo->parent_relid == context->rt_index)
659 : : {
23 tgl@sss.pgh.pa.us 660 [ # # ]:UBC 0 : Assert(context->new_index != INVALID_VAR);
7537 661 : 0 : appinfo->parent_relid = context->new_index;
662 : : }
7537 tgl@sss.pgh.pa.us 663 [ - + ]:CBC 40 : if (appinfo->child_relid == context->rt_index)
664 : : {
23 tgl@sss.pgh.pa.us 665 [ # # ]:UBC 0 : Assert(context->new_index != INVALID_VAR);
7537 666 : 0 : appinfo->child_relid = context->new_index;
667 : : }
668 : : }
669 : : /* fall through to examine children */
670 : : }
671 : : /* Shouldn't need to handle other planner auxiliary nodes here */
6542 tgl@sss.pgh.pa.us 672 [ - + ]:CBC 844068 : Assert(!IsA(node, SpecialJoinInfo));
673 [ - + ]: 844068 : Assert(!IsA(node, PlaceHolderInfo));
5799 674 [ - + ]: 844068 : Assert(!IsA(node, MinMaxAggInfo));
675 : :
9851 676 [ + + ]: 844068 : if (IsA(node, Query))
677 : : {
678 : : /* Recurse into subselects */
679 : : bool result;
680 : :
9504 681 : 2845 : context->sublevels_up++;
682 : 2845 : result = query_tree_walker((Query *) node, ChangeVarNodes_walker,
683 : : context, 0);
684 : 2845 : context->sublevels_up--;
685 : 2845 : return result;
686 : : }
661 peter@eisentraut.org 687 : 841223 : return expression_tree_walker(node, ChangeVarNodes_walker, context);
688 : : }
689 : :
690 : : void
23 tgl@sss.pgh.pa.us 691 : 58415 : ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up)
692 : : {
693 : : ChangeVarNodes_context context;
694 : :
9851 695 : 58415 : context.rt_index = rt_index;
696 : 58415 : context.new_index = new_index;
697 : 58415 : context.sublevels_up = sublevels_up;
698 : :
699 : : /*
700 : : * Must be prepared to start with a Query or a bare expression tree; if
701 : : * it's a Query, go straight to query_tree_walker to make sure that
702 : : * sublevels_up doesn't get incremented prematurely.
703 : : */
9504 704 [ + + + + ]: 58415 : if (node && IsA(node, Query))
9419 705 : 21110 : {
9313 bruce@momjian.us 706 : 21110 : Query *qry = (Query *) node;
707 : :
708 : : /*
709 : : * If we are starting at a Query, and sublevels_up is zero, then we
710 : : * must also fix rangetable indexes in the Query itself --- namely
711 : : * resultRelation, mergeTargetRelation, exclRelIndex and rowMarks
712 : : * entries. sublevels_up cannot be zero when recursing into a
713 : : * subquery, so there's no need to have the same logic inside
714 : : * ChangeVarNodes_walker.
715 : : */
9419 tgl@sss.pgh.pa.us 716 [ + - ]: 21110 : if (sublevels_up == 0)
717 : : {
718 : : ListCell *l;
719 : :
720 [ + + ]: 21110 : if (qry->resultRelation == rt_index)
721 : : {
23 722 [ - + ]: 2198 : Assert(new_index != INVALID_VAR);
9419 723 : 2198 : qry->resultRelation = new_index;
724 : : }
725 : :
934 dean.a.rasheed@gmail 726 [ + + ]: 21110 : if (qry->mergeTargetRelation == rt_index)
727 : : {
23 tgl@sss.pgh.pa.us 728 [ - + ]: 568 : Assert(new_index != INVALID_VAR);
934 dean.a.rasheed@gmail 729 : 568 : qry->mergeTargetRelation = new_index;
730 : : }
731 : :
732 : : /* this is unlikely to ever be used, but ... */
4148 andres@anarazel.de 733 [ + + - + ]: 21110 : if (qry->onConflict && qry->onConflict->exclRelIndex == rt_index)
734 : : {
23 tgl@sss.pgh.pa.us 735 [ # # ]:UBC 0 : Assert(new_index != INVALID_VAR);
4148 andres@anarazel.de 736 : 0 : qry->onConflict->exclRelIndex = new_index;
737 : : }
738 : :
9419 tgl@sss.pgh.pa.us 739 [ + + + + :CBC 21208 : foreach(l, qry->rowMarks)
+ + ]
740 : : {
7448 741 : 98 : RowMarkClause *rc = (RowMarkClause *) lfirst(l);
742 : :
743 [ + + ]: 98 : if (rc->rti == rt_index)
744 : : {
23 745 [ - + ]: 36 : Assert(new_index != INVALID_VAR);
7448 746 : 36 : rc->rti = new_index;
747 : : }
748 : : }
749 : : }
661 peter@eisentraut.org 750 : 21110 : query_tree_walker(qry, ChangeVarNodes_walker, &context, 0);
751 : : }
752 : : else
9504 tgl@sss.pgh.pa.us 753 : 37305 : ChangeVarNodes_walker(node, &context);
9851 754 : 58415 : }
755 : :
756 : : /*
757 : : * adjust_relid_set - substitute newrelid for oldrelid in a Relid set
758 : : *
759 : : * Attempt to remove oldrelid from a Relid set (as long as it's not a special
760 : : * varno). If oldrelid was found and removed, insert newrelid into a Relid
761 : : * set (as long as it's not a special varno). Therefore, when oldrelid is
762 : : * a special varno, this function does nothing. When newrelid is a special
763 : : * varno, this function behaves as delete.
764 : : */
765 : : static Relids
8625 766 : 344271 : adjust_relid_set(Relids relids, int oldrelid, int newrelid)
767 : : {
1200 768 [ + - + + ]: 344271 : if (!IS_SPECIAL_VARNO(oldrelid) && bms_is_member(oldrelid, relids))
769 : : {
770 : : /* Ensure we have a modifiable copy */
8625 771 : 535 : relids = bms_copy(relids);
772 : : /* Remove old, add new */
773 : 535 : relids = bms_del_member(relids, oldrelid);
584 akorotkov@postgresql 774 [ + + ]: 535 : if (!IS_SPECIAL_VARNO(newrelid))
775 : 25 : relids = bms_add_member(relids, newrelid);
776 : : }
8625 tgl@sss.pgh.pa.us 777 : 344271 : return relids;
778 : : }
779 : :
780 : : /*
781 : : * IncrementVarSublevelsUp - adjust Var nodes when pushing them down in tree
782 : : *
783 : : * Find all Var nodes in the given tree having varlevelsup >= min_sublevels_up,
784 : : * and add delta_sublevels_up to their varlevelsup value. This is needed when
785 : : * an expression that's correct for some nesting level is inserted into a
786 : : * subquery. Ordinarily the initial call has min_sublevels_up == 0 so that
787 : : * all Vars are affected. The point of min_sublevels_up is that we can
788 : : * increment it when we recurse into a sublink, so that local variables in
789 : : * that sublink are not affected, only outer references to vars that belong
790 : : * to the expression's original query level or parents thereof.
791 : : *
792 : : * Likewise for other nodes containing levelsup fields, such as Aggref.
793 : : *
794 : : * NOTE: although this has the form of a walker, we cheat and modify the
795 : : * Var nodes in-place. The given expression tree should have been copied
796 : : * earlier to ensure that no unwanted side-effects occur!
797 : : */
798 : :
799 : : typedef struct
800 : : {
801 : : int delta_sublevels_up;
802 : : int min_sublevels_up;
803 : : } IncrementVarSublevelsUp_context;
804 : :
805 : : static bool
9684 806 : 2541781 : IncrementVarSublevelsUp_walker(Node *node,
807 : : IncrementVarSublevelsUp_context *context)
808 : : {
809 [ + + ]: 2541781 : if (node == NULL)
810 : 790468 : return false;
811 [ + + ]: 1751313 : if (IsA(node, Var))
812 : : {
813 : 842021 : Var *var = (Var *) node;
814 : :
815 [ + + ]: 842021 : if (var->varlevelsup >= context->min_sublevels_up)
816 : 16812 : var->varlevelsup += context->delta_sublevels_up;
8507 817 : 842021 : return false; /* done here */
818 : : }
7041 819 [ - + ]: 909292 : if (IsA(node, CurrentOfExpr))
820 : : {
821 : : /* this should not happen */
7041 tgl@sss.pgh.pa.us 822 [ # # ]:UBC 0 : if (context->min_sublevels_up == 0)
823 [ # # ]: 0 : elog(ERROR, "cannot push down CurrentOfExpr");
824 : 0 : return false;
825 : : }
8507 tgl@sss.pgh.pa.us 826 [ + + ]:CBC 909292 : if (IsA(node, Aggref))
827 : : {
828 : 2276 : Aggref *agg = (Aggref *) node;
829 : :
830 [ + + ]: 2276 : if (agg->agglevelsup >= context->min_sublevels_up)
831 : 77 : agg->agglevelsup += context->delta_sublevels_up;
832 : : /* fall through to recurse into argument */
833 : : }
4145 andres@anarazel.de 834 [ + + ]: 909292 : if (IsA(node, GroupingFunc))
835 : : {
4138 bruce@momjian.us 836 : 57 : GroupingFunc *grp = (GroupingFunc *) node;
837 : :
4145 andres@anarazel.de 838 [ + - ]: 57 : if (grp->agglevelsup >= context->min_sublevels_up)
839 : 57 : grp->agglevelsup += context->delta_sublevels_up;
840 : : /* fall through to recurse into argument */
841 : : }
6543 tgl@sss.pgh.pa.us 842 [ + + ]: 909292 : if (IsA(node, PlaceHolderVar))
843 : : {
844 : 1123 : PlaceHolderVar *phv = (PlaceHolderVar *) node;
845 : :
846 [ + + ]: 1123 : if (phv->phlevelsup >= context->min_sublevels_up)
847 : 784 : phv->phlevelsup += context->delta_sublevels_up;
848 : : /* fall through to recurse into argument */
849 : : }
612 dean.a.rasheed@gmail 850 [ + + ]: 909292 : if (IsA(node, ReturningExpr))
851 : : {
852 : 108 : ReturningExpr *rexpr = (ReturningExpr *) node;
853 : :
854 [ + - ]: 108 : if (rexpr->retlevelsup >= context->min_sublevels_up)
855 : 108 : rexpr->retlevelsup += context->delta_sublevels_up;
856 : : /* fall through to recurse into argument */
857 : : }
6560 tgl@sss.pgh.pa.us 858 [ + + ]: 909292 : if (IsA(node, RangeTblEntry))
859 : : {
860 : 97869 : RangeTblEntry *rte = (RangeTblEntry *) node;
861 : :
862 [ + + ]: 97869 : if (rte->rtekind == RTE_CTE)
863 : : {
864 [ + + ]: 4101 : if (rte->ctelevelsup >= context->min_sublevels_up)
865 : 4076 : rte->ctelevelsup += context->delta_sublevels_up;
866 : : }
867 : 97869 : return false; /* allow range_table_walker to continue */
868 : : }
9504 869 [ + + ]: 811423 : if (IsA(node, Query))
870 : : {
871 : : /* Recurse into subselects */
872 : : bool result;
873 : :
874 : 16285 : context->min_sublevels_up++;
875 : 16285 : result = query_tree_walker((Query *) node,
876 : : IncrementVarSublevelsUp_walker,
877 : : context,
878 : : QTW_EXAMINE_RTES_BEFORE);
879 : 16285 : context->min_sublevels_up--;
880 : 16285 : return result;
881 : : }
661 peter@eisentraut.org 882 : 795138 : return expression_tree_walker(node, IncrementVarSublevelsUp_walker, context);
883 : : }
884 : :
885 : : void
9504 tgl@sss.pgh.pa.us 886 : 80468 : IncrementVarSublevelsUp(Node *node, int delta_sublevels_up,
887 : : int min_sublevels_up)
888 : : {
889 : : IncrementVarSublevelsUp_context context;
890 : :
891 : 80468 : context.delta_sublevels_up = delta_sublevels_up;
892 : 80468 : context.min_sublevels_up = min_sublevels_up;
893 : :
894 : : /*
895 : : * Must be prepared to start with a Query or a bare expression tree; if
896 : : * it's a Query, we don't want to increment sublevels_up.
897 : : */
8647 898 : 80468 : query_or_expression_tree_walker(node,
899 : : IncrementVarSublevelsUp_walker,
900 : : &context,
901 : : QTW_EXAMINE_RTES_BEFORE);
9504 902 : 80468 : }
903 : :
904 : : /*
905 : : * IncrementVarSublevelsUp_rtable -
906 : : * Same as IncrementVarSublevelsUp, but to be invoked on a range table.
907 : : */
908 : : void
6611 heikki.linnakangas@i 909 : 3843 : IncrementVarSublevelsUp_rtable(List *rtable, int delta_sublevels_up,
910 : : int min_sublevels_up)
911 : : {
912 : : IncrementVarSublevelsUp_context context;
913 : :
914 : 3843 : context.delta_sublevels_up = delta_sublevels_up;
915 : 3843 : context.min_sublevels_up = min_sublevels_up;
916 : :
917 : 3843 : range_table_walker(rtable,
918 : : IncrementVarSublevelsUp_walker,
919 : : &context,
920 : : QTW_EXAMINE_RTES_BEFORE);
921 : 3843 : }
922 : :
923 : : /*
924 : : * SetVarReturningType - adjust Var nodes for a specified varreturningtype.
925 : : *
926 : : * Find all Var nodes referring to the specified result relation in the given
927 : : * expression and set their varreturningtype to the specified value.
928 : : *
929 : : * NOTE: although this has the form of a walker, we cheat and modify the
930 : : * Var nodes in-place. The given expression tree should have been copied
931 : : * earlier to ensure that no unwanted side-effects occur!
932 : : */
933 : :
934 : : typedef struct
935 : : {
936 : : int result_relation;
937 : : int sublevels_up;
938 : : VarReturningType returning_type;
939 : : } SetVarReturningType_context;
940 : :
941 : : static bool
612 dean.a.rasheed@gmail 942 : 1510 : SetVarReturningType_walker(Node *node, SetVarReturningType_context *context)
943 : : {
944 [ + + ]: 1510 : if (node == NULL)
945 : 384 : return false;
946 [ + + ]: 1126 : if (IsA(node, Var))
947 : : {
948 : 698 : Var *var = (Var *) node;
949 : :
950 [ + + ]: 698 : if (var->varno == context->result_relation &&
951 [ + - ]: 658 : var->varlevelsup == context->sublevels_up)
952 : 658 : var->varreturningtype = context->returning_type;
953 : :
954 : 698 : return false;
955 : : }
956 : :
957 [ + + ]: 428 : if (IsA(node, Query))
958 : : {
959 : : /* Recurse into subselects */
960 : : bool result;
961 : :
962 : 32 : context->sublevels_up++;
963 : 32 : result = query_tree_walker((Query *) node, SetVarReturningType_walker,
964 : : context, 0);
965 : 32 : context->sublevels_up--;
966 : 32 : return result;
967 : : }
968 : 396 : return expression_tree_walker(node, SetVarReturningType_walker, context);
969 : : }
970 : :
971 : : static void
972 : 826 : SetVarReturningType(Node *node, int result_relation, int sublevels_up,
973 : : VarReturningType returning_type)
974 : : {
975 : : SetVarReturningType_context context;
976 : :
977 : 826 : context.result_relation = result_relation;
978 : 826 : context.sublevels_up = sublevels_up;
979 : 826 : context.returning_type = returning_type;
980 : :
981 : : /* Expect to start with an expression */
982 : 826 : SetVarReturningType_walker(node, &context);
983 : 826 : }
984 : :
985 : : /*
986 : : * rangeTableEntry_used - detect whether an RTE is referenced somewhere
987 : : * in var nodes or join or setOp trees of a query or expression.
988 : : */
989 : :
990 : : typedef struct
991 : : {
992 : : int rt_index;
993 : : int sublevels_up;
994 : : } rangeTableEntry_used_context;
995 : :
996 : : static bool
9504 tgl@sss.pgh.pa.us 997 : 2645504 : rangeTableEntry_used_walker(Node *node,
998 : : rangeTableEntry_used_context *context)
999 : : {
1000 [ + + ]: 2645504 : if (node == NULL)
1001 : 494810 : return false;
1002 [ + + ]: 2150694 : if (IsA(node, Var))
1003 : : {
1004 : 619169 : Var *var = (Var *) node;
1005 : :
1006 [ + + ]: 619169 : if (var->varlevelsup == context->sublevels_up &&
1329 1007 [ + + - + ]: 983142 : (var->varno == context->rt_index ||
1008 : 389974 : bms_is_member(context->rt_index, var->varnullingrels)))
9684 1009 : 203194 : return true;
1010 : 415975 : return false;
1011 : : }
7041 1012 [ - + ]: 1531525 : if (IsA(node, CurrentOfExpr))
1013 : : {
7041 tgl@sss.pgh.pa.us 1014 :UBC 0 : CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
1015 : :
1016 [ # # ]: 0 : if (context->sublevels_up == 0 &&
1017 [ # # ]: 0 : cexpr->cvarno == context->rt_index)
1018 : 0 : return true;
1019 : 0 : return false;
1020 : : }
9504 tgl@sss.pgh.pa.us 1021 [ + + ]:CBC 1531525 : if (IsA(node, RangeTblRef))
1022 : : {
1023 : 91701 : RangeTblRef *rtr = (RangeTblRef *) node;
1024 : :
1025 [ + + ]: 91701 : if (rtr->rtindex == context->rt_index &&
1026 [ + + ]: 49417 : context->sublevels_up == 0)
9684 1027 : 47728 : return true;
1028 : : /* the subquery itself is visited separately */
9504 1029 : 43973 : return false;
1030 : : }
8958 1031 [ + + ]: 1439824 : if (IsA(node, JoinExpr))
1032 : : {
8782 bruce@momjian.us 1033 : 32318 : JoinExpr *j = (JoinExpr *) node;
1034 : :
8958 tgl@sss.pgh.pa.us 1035 [ + + ]: 32318 : if (j->rtindex == context->rt_index &&
1036 [ - + ]: 52 : context->sublevels_up == 0)
8958 tgl@sss.pgh.pa.us 1037 :UBC 0 : return true;
1038 : : /* fall through to examine children */
1039 : : }
1040 : : /* Shouldn't need to handle planner auxiliary nodes here */
6543 tgl@sss.pgh.pa.us 1041 [ - + ]:CBC 1439824 : Assert(!IsA(node, PlaceHolderVar));
5496 1042 [ - + ]: 1439824 : Assert(!IsA(node, PlanRowMark));
6611 1043 [ - + ]: 1439824 : Assert(!IsA(node, SpecialJoinInfo));
7537 1044 [ - + ]: 1439824 : Assert(!IsA(node, AppendRelInfo));
6543 1045 [ - + ]: 1439824 : Assert(!IsA(node, PlaceHolderInfo));
5799 1046 [ - + ]: 1439824 : Assert(!IsA(node, MinMaxAggInfo));
1047 : :
9504 1048 [ + + ]: 1439824 : if (IsA(node, Query))
1049 : : {
1050 : : /* Recurse into subselects */
1051 : : bool result;
1052 : :
1053 : 9770 : context->sublevels_up++;
1054 : 9770 : result = query_tree_walker((Query *) node, rangeTableEntry_used_walker,
1055 : : context, 0);
1056 : 9770 : context->sublevels_up--;
1057 : 9770 : return result;
1058 : : }
661 peter@eisentraut.org 1059 : 1430054 : return expression_tree_walker(node, rangeTableEntry_used_walker, context);
1060 : : }
1061 : :
1062 : : bool
9504 tgl@sss.pgh.pa.us 1063 : 261593 : rangeTableEntry_used(Node *node, int rt_index, int sublevels_up)
1064 : : {
1065 : : rangeTableEntry_used_context context;
1066 : :
1067 : 261593 : context.rt_index = rt_index;
1068 : 261593 : context.sublevels_up = sublevels_up;
1069 : :
1070 : : /*
1071 : : * Must be prepared to start with a Query or a bare expression tree; if
1072 : : * it's a Query, we don't want to increment sublevels_up.
1073 : : */
8647 1074 : 261593 : return query_or_expression_tree_walker(node,
1075 : : rangeTableEntry_used_walker,
1076 : : &context,
1077 : : 0);
1078 : : }
1079 : :
1080 : :
1081 : : /*
1082 : : * If the given Query is an INSERT ... SELECT construct, extract and
1083 : : * return the sub-Query node that represents the SELECT part. Otherwise
1084 : : * return the given Query.
1085 : : *
1086 : : * If subquery_ptr is not NULL, then *subquery_ptr is set to the location
1087 : : * of the link to the SELECT subquery inside parsetree, or NULL if not an
1088 : : * INSERT ... SELECT.
1089 : : *
1090 : : * This is a hack needed because transformations on INSERT ... SELECTs that
1091 : : * appear in rule actions should be applied to the source SELECT, not to the
1092 : : * INSERT part. Perhaps this can be cleaned up with redesigned querytrees.
1093 : : */
1094 : : Query *
9420 1095 : 2389 : getInsertSelectQuery(Query *parsetree, Query ***subquery_ptr)
1096 : : {
1097 : : Query *selectquery;
1098 : : RangeTblEntry *selectrte;
1099 : : RangeTblRef *rtr;
1100 : :
1101 [ + + ]: 2389 : if (subquery_ptr)
1102 : 976 : *subquery_ptr = NULL;
1103 : :
1104 [ - + ]: 2389 : if (parsetree == NULL)
9420 tgl@sss.pgh.pa.us 1105 :UBC 0 : return parsetree;
9420 tgl@sss.pgh.pa.us 1106 [ + + ]:CBC 2389 : if (parsetree->commandType != CMD_INSERT)
1107 : 1015 : return parsetree;
1108 : :
1109 : : /*
1110 : : * Currently, this is ONLY applied to rule-action queries, and so we
1111 : : * expect to find the OLD and NEW placeholder entries in the given query.
1112 : : * If they're not there, it must be an INSERT/SELECT in which they've been
1113 : : * pushed down to the SELECT.
1114 : : */
8148 neilc@samurai.com 1115 [ + - ]: 1374 : if (list_length(parsetree->rtable) >= 2 &&
7645 bruce@momjian.us 1116 [ + + ]: 1374 : strcmp(rt_fetch(PRS2_OLD_VARNO, parsetree->rtable)->eref->aliasname,
6163 tgl@sss.pgh.pa.us 1117 : 1258 : "old") == 0 &&
7645 bruce@momjian.us 1118 [ + - ]: 1258 : strcmp(rt_fetch(PRS2_NEW_VARNO, parsetree->rtable)->eref->aliasname,
1119 : : "new") == 0)
9420 tgl@sss.pgh.pa.us 1120 : 1258 : return parsetree;
1121 [ + - - + ]: 116 : Assert(parsetree->jointree && IsA(parsetree->jointree, FromExpr));
8148 neilc@samurai.com 1122 [ - + ]: 116 : if (list_length(parsetree->jointree->fromlist) != 1)
8458 tgl@sss.pgh.pa.us 1123 [ # # ]:UBC 0 : elog(ERROR, "expected to find SELECT subquery");
8152 neilc@samurai.com 1124 :CBC 116 : rtr = (RangeTblRef *) linitial(parsetree->jointree->fromlist);
1305 dean.a.rasheed@gmail 1125 [ - + ]: 116 : if (!IsA(rtr, RangeTblRef))
1305 dean.a.rasheed@gmail 1126 [ # # ]:UBC 0 : elog(ERROR, "expected to find SELECT subquery");
9420 tgl@sss.pgh.pa.us 1127 :CBC 116 : selectrte = rt_fetch(rtr->rtindex, parsetree->rtable);
1305 dean.a.rasheed@gmail 1128 [ + - ]: 116 : if (!(selectrte->rtekind == RTE_SUBQUERY &&
1129 [ + - ]: 116 : selectrte->subquery &&
1130 [ + - ]: 116 : IsA(selectrte->subquery, Query) &&
1131 [ - + ]: 116 : selectrte->subquery->commandType == CMD_SELECT))
8458 tgl@sss.pgh.pa.us 1132 [ # # ]:UBC 0 : elog(ERROR, "expected to find SELECT subquery");
1305 dean.a.rasheed@gmail 1133 :CBC 116 : selectquery = selectrte->subquery;
8148 neilc@samurai.com 1134 [ + - ]: 116 : if (list_length(selectquery->rtable) >= 2 &&
7645 bruce@momjian.us 1135 [ + - ]: 116 : strcmp(rt_fetch(PRS2_OLD_VARNO, selectquery->rtable)->eref->aliasname,
6163 tgl@sss.pgh.pa.us 1136 : 116 : "old") == 0 &&
7645 bruce@momjian.us 1137 [ + - ]: 116 : strcmp(rt_fetch(PRS2_NEW_VARNO, selectquery->rtable)->eref->aliasname,
1138 : : "new") == 0)
1139 : : {
9420 tgl@sss.pgh.pa.us 1140 [ + + ]: 116 : if (subquery_ptr)
9313 bruce@momjian.us 1141 : 40 : *subquery_ptr = &(selectrte->subquery);
9420 tgl@sss.pgh.pa.us 1142 : 116 : return selectquery;
1143 : : }
8458 tgl@sss.pgh.pa.us 1144 [ # # ]:UBC 0 : elog(ERROR, "could not find rule placeholders");
1145 : : return NULL; /* not reached */
1146 : : }
1147 : :
1148 : :
1149 : : /*
1150 : : * Add the given qualifier condition to the query's WHERE clause
1151 : : */
1152 : : void
10604 bruce@momjian.us 1153 :CBC 2594 : AddQual(Query *parsetree, Node *qual)
1154 : : {
1155 : : Node *copy;
1156 : :
10605 1157 [ + + ]: 2594 : if (qual == NULL)
1158 : 1256 : return;
1159 : :
9367 tgl@sss.pgh.pa.us 1160 [ - + ]: 1338 : if (parsetree->commandType == CMD_UTILITY)
1161 : : {
1162 : : /*
1163 : : * There's noplace to put the qual on a utility statement.
1164 : : *
1165 : : * If it's a NOTIFY, silently ignore the qual; this means that the
1166 : : * NOTIFY will execute, whether or not there are any qualifying rows.
1167 : : * While clearly wrong, this is much more useful than refusing to
1168 : : * execute the rule at all, and extra NOTIFY events are harmless for
1169 : : * typical uses of NOTIFY.
1170 : : *
1171 : : * If it isn't a NOTIFY, error out, since unconditional execution of
1172 : : * other utility stmts is unlikely to be wanted. (This case is not
1173 : : * currently allowed anyway, but keep the test for safety.)
1174 : : */
9367 tgl@sss.pgh.pa.us 1175 [ # # # # ]:UBC 0 : if (parsetree->utilityStmt && IsA(parsetree->utilityStmt, NotifyStmt))
9144 1176 : 0 : return;
1177 : : else
8458 1178 [ # # ]: 0 : ereport(ERROR,
1179 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1180 : : errmsg("conditional utility statements are not implemented")));
1181 : : }
1182 : :
8467 tgl@sss.pgh.pa.us 1183 [ - + ]:CBC 1338 : if (parsetree->setOperations != NULL)
1184 : : {
1185 : : /*
1186 : : * There's noplace to put the qual on a setop statement, either. (This
1187 : : * could be fixed, but right now the planner simply ignores any qual
1188 : : * condition on a setop query.)
1189 : : */
8458 tgl@sss.pgh.pa.us 1190 [ # # ]:UBC 0 : ereport(ERROR,
1191 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1192 : : errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
1193 : : }
1194 : :
1195 : : /* INTERSECT wants the original, but we need to copy - Jan */
10086 JanWieck@Yahoo.com 1196 :CBC 1338 : copy = copyObject(qual);
1197 : :
9487 tgl@sss.pgh.pa.us 1198 : 1338 : parsetree->jointree->quals = make_and_qual(parsetree->jointree->quals,
1199 : : copy);
1200 : :
1201 : : /*
1202 : : * We had better not have stuck an aggregate into the WHERE clause.
1203 : : */
5154 1204 [ - + ]: 1338 : Assert(!contain_aggs_of_level(copy, 0));
1205 : :
1206 : : /*
1207 : : * Make sure query is marked correctly if added qual has sublinks. Need
1208 : : * not search qual when query is already marked.
1209 : : */
8371 1210 [ + + ]: 1338 : if (!parsetree->hasSubLinks)
1211 : 1310 : parsetree->hasSubLinks = checkExprHasSubLink(copy);
1212 : : }
1213 : :
1214 : :
1215 : : /*
1216 : : * Invert the given clause and add it to the WHERE qualifications of the
1217 : : * given querytree. Inversion means "x IS NOT TRUE", not just "NOT x",
1218 : : * else we will do the wrong thing when x evaluates to NULL.
1219 : : */
1220 : : void
8736 1221 : 304 : AddInvertedQual(Query *parsetree, Node *qual)
1222 : : {
1223 : : BooleanTest *invqual;
1224 : :
10605 bruce@momjian.us 1225 [ - + ]: 304 : if (qual == NULL)
10605 bruce@momjian.us 1226 :UBC 0 : return;
1227 : :
1228 : : /* Need not copy input qual, because AddQual will... */
8736 tgl@sss.pgh.pa.us 1229 :CBC 304 : invqual = makeNode(BooleanTest);
8683 1230 : 304 : invqual->arg = (Expr *) qual;
8736 1231 : 304 : invqual->booltesttype = IS_NOT_TRUE;
4228 1232 : 304 : invqual->location = -1;
1233 : :
8736 1234 : 304 : AddQual(parsetree, (Node *) invqual);
1235 : : }
1236 : :
1237 : :
1238 : : /*
1239 : : * add_nulling_relids() finds Vars and PlaceHolderVars that belong to any
1240 : : * of the target_relids, and adds added_relids to their varnullingrels
1241 : : * and phnullingrels fields. If target_relids is NULL, all level-zero
1242 : : * Vars and PHVs are modified.
1243 : : */
1244 : : Node *
1329 1245 : 5719 : add_nulling_relids(Node *node,
1246 : : const Bitmapset *target_relids,
1247 : : const Bitmapset *added_relids)
1248 : : {
1249 : : add_nulling_relids_context context;
1250 : :
1251 : 5719 : context.target_relids = target_relids;
1252 : 5719 : context.added_relids = added_relids;
1253 : 5719 : context.sublevels_up = 0;
1254 : 5719 : return query_or_expression_tree_mutator(node,
1255 : : add_nulling_relids_mutator,
1256 : : &context,
1257 : : 0);
1258 : : }
1259 : :
1260 : : static Node *
1261 : 23665 : add_nulling_relids_mutator(Node *node,
1262 : : add_nulling_relids_context *context)
1263 : : {
1264 [ + + ]: 23665 : if (node == NULL)
1265 : 910 : return NULL;
1266 [ + + ]: 22755 : if (IsA(node, Var))
1267 : : {
1268 : 8478 : Var *var = (Var *) node;
1269 : :
1270 [ + + ]: 8478 : if (var->varlevelsup == context->sublevels_up &&
751 1271 [ + + + + ]: 16776 : (context->target_relids == NULL ||
1272 : 8303 : bms_is_member(var->varno, context->target_relids)))
1273 : : {
1329 1274 : 4744 : Relids newnullingrels = bms_union(var->varnullingrels,
1275 : : context->added_relids);
1276 : :
1277 : : /* Copy the Var ... */
1278 : 4744 : var = copyObject(var);
1279 : : /* ... and replace the copy's varnullingrels field */
1280 : 4744 : var->varnullingrels = newnullingrels;
1281 : 4744 : return (Node *) var;
1282 : : }
1283 : : /* Otherwise fall through to copy the Var normally */
1284 : : }
1285 [ + + ]: 14277 : else if (IsA(node, PlaceHolderVar))
1286 : : {
1287 : 927 : PlaceHolderVar *phv = (PlaceHolderVar *) node;
1288 : :
1289 [ + - ]: 927 : if (phv->phlevelsup == context->sublevels_up &&
751 1290 [ + - + - ]: 1854 : (context->target_relids == NULL ||
1291 : 927 : bms_overlap(phv->phrels, context->target_relids)))
1292 : : {
1329 1293 : 927 : Relids newnullingrels = bms_union(phv->phnullingrels,
1294 : : context->added_relids);
1295 : :
1296 : : /*
1297 : : * We don't modify the contents of the PHV's expression, only add
1298 : : * to phnullingrels. This corresponds to assuming that the PHV
1299 : : * will be evaluated at the same level as before, then perhaps be
1300 : : * nulled as it bubbles up. Hence, just flat-copy the node ...
1301 : : */
1302 : 927 : phv = makeNode(PlaceHolderVar);
1303 : 927 : memcpy(phv, node, sizeof(PlaceHolderVar));
1304 : : /* ... and replace the copy's phnullingrels field */
1305 : 927 : phv->phnullingrels = newnullingrels;
1306 : 927 : return (Node *) phv;
1307 : : }
1308 : : /* Otherwise fall through to copy the PlaceHolderVar normally */
1309 : : }
1310 [ + + ]: 13350 : else if (IsA(node, Query))
1311 : : {
1312 : : /* Recurse into RTE or sublink subquery */
1313 : : Query *newnode;
1314 : :
1315 : 40 : context->sublevels_up++;
1316 : 40 : newnode = query_tree_mutator((Query *) node,
1317 : : add_nulling_relids_mutator,
1318 : : context,
1319 : : 0);
1320 : 40 : context->sublevels_up--;
1321 : 40 : return (Node *) newnode;
1322 : : }
661 peter@eisentraut.org 1323 : 17044 : return expression_tree_mutator(node, add_nulling_relids_mutator, context);
1324 : : }
1325 : :
1326 : : /*
1327 : : * remove_nulling_relids() removes mentions of the specified RT index(es)
1328 : : * in Var.varnullingrels and PlaceHolderVar.phnullingrels fields within
1329 : : * the given expression, except in nodes belonging to rels listed in
1330 : : * except_relids.
1331 : : */
1332 : : Node *
1329 tgl@sss.pgh.pa.us 1333 : 295870 : remove_nulling_relids(Node *node,
1334 : : const Bitmapset *removable_relids,
1335 : : const Bitmapset *except_relids)
1336 : : {
1337 : : remove_nulling_relids_context context;
1338 : :
1339 : 295870 : context.removable_relids = removable_relids;
1340 : 295870 : context.except_relids = except_relids;
1341 : 295870 : context.sublevels_up = 0;
1342 : 295870 : return query_or_expression_tree_mutator(node,
1343 : : remove_nulling_relids_mutator,
1344 : : &context,
1345 : : 0);
1346 : : }
1347 : :
1348 : : static Node *
1349 : 723266 : remove_nulling_relids_mutator(Node *node,
1350 : : remove_nulling_relids_context *context)
1351 : : {
1352 [ + + ]: 723266 : if (node == NULL)
1353 : 87524 : return NULL;
1354 [ + + ]: 635742 : if (IsA(node, Var))
1355 : : {
1356 : 368711 : Var *var = (Var *) node;
1357 : :
1358 [ + + ]: 368711 : if (var->varlevelsup == context->sublevels_up &&
1359 [ + + + + ]: 722142 : !bms_is_member(var->varno, context->except_relids) &&
1360 : 360817 : bms_overlap(var->varnullingrels, context->removable_relids))
1361 : : {
1362 : : /* Copy the Var ... */
1363 : 13059 : var = copyObject(var);
1364 : : /* ... and replace the copy's varnullingrels field */
1298 1365 : 13059 : var->varnullingrels = bms_difference(var->varnullingrels,
1366 : : context->removable_relids);
1329 1367 : 13059 : return (Node *) var;
1368 : : }
1369 : : /* Otherwise fall through to copy the Var normally */
1370 : : }
1371 [ + + ]: 267031 : else if (IsA(node, PlaceHolderVar))
1372 : : {
1373 : 4216 : PlaceHolderVar *phv = (PlaceHolderVar *) node;
1374 : :
1375 [ + - ]: 4216 : if (phv->phlevelsup == context->sublevels_up &&
1376 [ + - ]: 4216 : !bms_overlap(phv->phrels, context->except_relids))
1377 : : {
1378 : : /*
1379 : : * Note: it might seem desirable to remove the PHV altogether if
1380 : : * phnullingrels goes to empty. Currently we dare not do that
1381 : : * because we use PHVs in some cases to enforce separate identity
1382 : : * of subexpressions; see wrap_option usages in prepjointree.c.
1383 : : */
1384 : : /* Copy the PlaceHolderVar and mutate what's below ... */
1385 : : phv = (PlaceHolderVar *)
1386 : 4216 : expression_tree_mutator(node,
1387 : : remove_nulling_relids_mutator,
1388 : : context);
1389 : : /* ... and replace the copy's phnullingrels field */
1298 1390 : 4216 : phv->phnullingrels = bms_difference(phv->phnullingrels,
1391 : : context->removable_relids);
1392 : : /* We must also update phrels, if it contains a removable RTI */
1329 1393 : 4216 : phv->phrels = bms_difference(phv->phrels,
1394 : : context->removable_relids);
1395 [ - + ]: 4216 : Assert(!bms_is_empty(phv->phrels));
1396 : 4216 : return (Node *) phv;
1397 : : }
1398 : : /* Otherwise fall through to copy the PlaceHolderVar normally */
1399 : : }
1400 [ + + ]: 262815 : else if (IsA(node, Query))
1401 : : {
1402 : : /* Recurse into RTE or sublink subquery */
1403 : : Query *newnode;
1404 : :
1405 : 801 : context->sublevels_up++;
1406 : 801 : newnode = query_tree_mutator((Query *) node,
1407 : : remove_nulling_relids_mutator,
1408 : : context,
1409 : : 0);
1410 : 801 : context->sublevels_up--;
1411 : 801 : return (Node *) newnode;
1412 : : }
661 peter@eisentraut.org 1413 : 617666 : return expression_tree_mutator(node, remove_nulling_relids_mutator, context);
1414 : : }
1415 : :
1416 : :
1417 : : /*
1418 : : * replace_rte_variables() finds all Vars in an expression tree
1419 : : * that reference a particular RTE, and replaces them with substitute
1420 : : * expressions obtained from a caller-supplied callback function.
1421 : : *
1422 : : * When invoking replace_rte_variables on a portion of a Query, pass the
1423 : : * address of the containing Query's hasSubLinks field as outer_hasSubLinks.
1424 : : * Otherwise, pass NULL, but inserting a SubLink into a non-Query expression
1425 : : * will then cause an error.
1426 : : *
1427 : : * Note: the business with inserted_sublink is needed to update hasSubLinks
1428 : : * in subqueries when the replacement adds a subquery inside a subquery.
1429 : : * Messy, isn't it? We do not need to do similar pushups for hasAggs,
1430 : : * because it isn't possible for this transformation to insert a level-zero
1431 : : * aggregate reference into a subquery --- it could only insert outer aggs.
1432 : : * Likewise for hasWindowFuncs.
1433 : : *
1434 : : * Note: usually, we'd not expose the mutator function or context struct
1435 : : * for a function like this. We do so because callbacks often find it
1436 : : * convenient to recurse directly to the mutator on sub-expressions of
1437 : : * what they will return.
1438 : : */
1439 : : Node *
6227 tgl@sss.pgh.pa.us 1440 : 173287 : replace_rte_variables(Node *node, int target_varno, int sublevels_up,
1441 : : replace_rte_variables_callback callback,
1442 : : void *callback_arg,
1443 : : bool *outer_hasSubLinks)
1444 : : {
1445 : : Node *result;
1446 : : replace_rte_variables_context context;
1447 : :
1448 : 173287 : context.callback = callback;
1449 : 173287 : context.callback_arg = callback_arg;
1450 : 173287 : context.target_varno = target_varno;
1451 : 173287 : context.sublevels_up = sublevels_up;
1452 : :
1453 : : /*
1454 : : * We try to initialize inserted_sublink to true if there is no need to
1455 : : * detect new sublinks because the query already has some.
1456 : : */
1457 [ + + + + ]: 173287 : if (node && IsA(node, Query))
1458 : 4559 : context.inserted_sublink = ((Query *) node)->hasSubLinks;
1459 [ + + ]: 168728 : else if (outer_hasSubLinks)
1460 : 168429 : context.inserted_sublink = *outer_hasSubLinks;
1461 : : else
1462 : 299 : context.inserted_sublink = false;
1463 : :
1464 : : /*
1465 : : * Must be prepared to start with a Query or a bare expression tree; if
1466 : : * it's a Query, we don't want to increment sublevels_up.
1467 : : */
1468 : 173287 : result = query_or_expression_tree_mutator(node,
1469 : : replace_rte_variables_mutator,
1470 : : &context,
1471 : : 0);
1472 : :
1473 [ + + ]: 173275 : if (context.inserted_sublink)
1474 : : {
1475 [ + + + + ]: 19732 : if (result && IsA(result, Query))
1476 : 183 : ((Query *) result)->hasSubLinks = true;
1477 [ + - ]: 19549 : else if (outer_hasSubLinks)
1478 : 19549 : *outer_hasSubLinks = true;
1479 : : else
6227 tgl@sss.pgh.pa.us 1480 [ # # ]:UBC 0 : elog(ERROR, "replace_rte_variables inserted a SubLink, but has noplace to record it");
1481 : : }
1482 : :
6227 tgl@sss.pgh.pa.us 1483 :CBC 173275 : return result;
1484 : : }
1485 : :
1486 : : Node *
1487 : 763325 : replace_rte_variables_mutator(Node *node,
1488 : : replace_rte_variables_context *context)
1489 : : {
10605 bruce@momjian.us 1490 [ + + ]: 763325 : if (node == NULL)
9851 tgl@sss.pgh.pa.us 1491 : 227719 : return NULL;
1492 [ + + ]: 535606 : if (IsA(node, Var))
1493 : : {
1494 : 209131 : Var *var = (Var *) node;
1495 : :
6227 1496 [ + + ]: 209131 : if (var->varno == context->target_varno &&
1497 [ + + ]: 110569 : var->varlevelsup == context->sublevels_up)
1498 : : {
1499 : : /* Found a matching variable, make the substitution */
1500 : : Node *newnode;
1501 : :
3300 peter_e@gmx.net 1502 : 104159 : newnode = context->callback(var, context);
1503 : : /* Detect if we are adding a sublink to query */
6227 tgl@sss.pgh.pa.us 1504 [ + + ]: 104147 : if (!context->inserted_sublink)
1505 : 93920 : context->inserted_sublink = checkExprHasSubLink(newnode);
1506 : 104147 : return newnode;
1507 : : }
1508 : : /* otherwise fall through to copy the var normally */
1509 : : }
7041 1510 [ + + ]: 326475 : else if (IsA(node, Query))
1511 : : {
1512 : : /* Recurse into RTE subquery or not-yet-planned sublink subquery */
1513 : : Query *newnode;
1514 : : bool save_inserted_sublink;
1515 : :
9481 1516 : 2434 : context->sublevels_up++;
8371 1517 : 2434 : save_inserted_sublink = context->inserted_sublink;
6227 1518 : 2434 : context->inserted_sublink = ((Query *) node)->hasSubLinks;
8647 1519 : 2434 : newnode = query_tree_mutator((Query *) node,
1520 : : replace_rte_variables_mutator,
1521 : : context,
1522 : : 0);
8371 1523 : 2434 : newnode->hasSubLinks |= context->inserted_sublink;
1524 : 2434 : context->inserted_sublink = save_inserted_sublink;
9481 1525 : 2434 : context->sublevels_up--;
9851 1526 : 2434 : return (Node *) newnode;
1527 : : }
661 peter@eisentraut.org 1528 : 429013 : return expression_tree_mutator(node, replace_rte_variables_mutator, context);
1529 : : }
1530 : :
1531 : :
1532 : : /*
1533 : : * map_variable_attnos() finds all user-column Vars in an expression tree
1534 : : * that reference a particular RTE, and adjusts their varattnos according
1535 : : * to the given mapping array (varattno n is replaced by attno_map[n-1]).
1536 : : * Vars for system columns are not modified.
1537 : : *
1538 : : * A zero in the mapping array represents a dropped column, which should not
1539 : : * appear in the expression.
1540 : : *
1541 : : * If the expression tree contains a whole-row Var for the target RTE,
1542 : : * *found_whole_row is set to true. In addition, if to_rowtype is
1543 : : * not InvalidOid, we replace the Var with a Var of that vartype, inserting
1544 : : * a ConvertRowtypeExpr to map back to the rowtype expected by the expression.
1545 : : * (Therefore, to_rowtype had better be a child rowtype of the rowtype of the
1546 : : * RTE we're changing references to.) Callers that don't provide to_rowtype
1547 : : * should report an error if *found_whole_row is true; we don't do that here
1548 : : * because we don't know exactly what wording for the error message would
1549 : : * be most appropriate. The caller will be aware of the context.
1550 : : *
1551 : : * This could be built using replace_rte_variables and a callback function,
1552 : : * but since we don't ever need to insert sublinks, replace_rte_variables is
1553 : : * overly complicated.
1554 : : */
1555 : :
1556 : : typedef struct
1557 : : {
1558 : : int target_varno; /* RTE index to search for */
1559 : : int sublevels_up; /* (current) nesting depth */
1560 : : const AttrMap *attno_map; /* map array for user attnos */
1561 : : Oid to_rowtype; /* change whole-row Vars to this type */
1562 : : bool *found_whole_row; /* output flag */
1563 : : } map_variable_attnos_context;
1564 : :
1565 : : static Node *
5195 tgl@sss.pgh.pa.us 1566 : 73794 : map_variable_attnos_mutator(Node *node,
1567 : : map_variable_attnos_context *context)
1568 : : {
1569 [ + + ]: 73794 : if (node == NULL)
1570 : 112 : return NULL;
1571 [ + + ]: 73682 : if (IsA(node, Var))
1572 : : {
1573 : 17420 : Var *var = (Var *) node;
1574 : :
1575 [ + + ]: 17420 : if (var->varno == context->target_varno &&
1576 [ + - ]: 17260 : var->varlevelsup == context->sublevels_up)
1577 : : {
1578 : : /* Found a matching variable, make the substitution */
284 michael@paquier.xyz 1579 : 17260 : Var *newvar = palloc_object(Var);
4862 bruce@momjian.us 1580 : 17260 : int attno = var->varattno;
1581 : :
3264 tgl@sss.pgh.pa.us 1582 : 17260 : *newvar = *var; /* initially copy all fields of the Var */
1583 : :
5195 1584 [ + + ]: 17260 : if (attno > 0)
1585 : : {
1586 : : /* user-defined column, replace attno */
2468 michael@paquier.xyz 1587 [ + - ]: 17032 : if (attno > context->attno_map->maplen ||
1588 [ - + ]: 17032 : context->attno_map->attnums[attno - 1] == 0)
5195 tgl@sss.pgh.pa.us 1589 [ # # ]:UBC 0 : elog(ERROR, "unexpected varattno %d in expression to be mapped",
1590 : : attno);
2446 tgl@sss.pgh.pa.us 1591 :CBC 17032 : newvar->varattno = context->attno_map->attnums[attno - 1];
1592 : : /* If the syntactic referent is same RTE, fix it too */
1593 [ + + ]: 17032 : if (newvar->varnosyn == context->target_varno)
1594 : 16972 : newvar->varattnosyn = newvar->varattno;
1595 : : }
5195 1596 [ + + ]: 228 : else if (attno == 0)
1597 : : {
1598 : : /* whole-row variable, warn caller */
1599 : 40 : *(context->found_whole_row) = true;
1600 : :
1601 : : /* If the caller expects us to convert the Var, do so. */
3264 1602 [ + + ]: 40 : if (OidIsValid(context->to_rowtype) &&
1603 [ + - ]: 36 : context->to_rowtype != var->vartype)
1604 : : {
1605 : : ConvertRowtypeExpr *r;
1606 : :
1607 : : /* This certainly won't work for a RECORD variable. */
3335 rhaas@postgresql.org 1608 [ - + ]: 36 : Assert(var->vartype != RECORDOID);
1609 : :
1610 : : /* Var itself is changed to the requested type. */
3264 tgl@sss.pgh.pa.us 1611 : 36 : newvar->vartype = context->to_rowtype;
1612 : :
1613 : : /*
1614 : : * Add a conversion node on top to convert back to the
1615 : : * original type expected by the expression.
1616 : : */
1617 : 36 : r = makeNode(ConvertRowtypeExpr);
1618 : 36 : r->arg = (Expr *) newvar;
1619 : 36 : r->resulttype = var->vartype;
1620 : 36 : r->convertformat = COERCE_IMPLICIT_CAST;
1621 : 36 : r->location = -1;
1622 : :
1623 : 36 : return (Node *) r;
1624 : : }
1625 : : }
5195 1626 : 17224 : return (Node *) newvar;
1627 : : }
1628 : : /* otherwise fall through to copy the var normally */
1629 : : }
3265 rhaas@postgresql.org 1630 [ + + ]: 56262 : else if (IsA(node, ConvertRowtypeExpr))
1631 : : {
1632 : 32 : ConvertRowtypeExpr *r = (ConvertRowtypeExpr *) node;
3264 tgl@sss.pgh.pa.us 1633 : 32 : Var *var = (Var *) r->arg;
1634 : :
1635 : : /*
1636 : : * If this is coercing a whole-row Var that we need to convert, then
1637 : : * just convert the Var without adding an extra ConvertRowtypeExpr.
1638 : : * Effectively we're simplifying var::parenttype::grandparenttype into
1639 : : * just var::grandparenttype. This avoids building stacks of CREs if
1640 : : * this function is applied repeatedly.
1641 : : */
1642 [ + + ]: 32 : if (IsA(var, Var) &&
1643 [ + + ]: 24 : var->varno == context->target_varno &&
1644 [ + - ]: 20 : var->varlevelsup == context->sublevels_up &&
1645 [ + - ]: 20 : var->varattno == 0 &&
1646 [ + - ]: 20 : OidIsValid(context->to_rowtype) &&
1647 [ + - ]: 20 : context->to_rowtype != var->vartype)
1648 : : {
1649 : : ConvertRowtypeExpr *newnode;
284 michael@paquier.xyz 1650 : 20 : Var *newvar = palloc_object(Var);
1651 : :
1652 : : /* whole-row variable, warn caller */
3264 tgl@sss.pgh.pa.us 1653 : 20 : *(context->found_whole_row) = true;
1654 : :
1655 : 20 : *newvar = *var; /* initially copy all fields of the Var */
1656 : :
1657 : : /* This certainly won't work for a RECORD variable. */
1658 [ - + ]: 20 : Assert(var->vartype != RECORDOID);
1659 : :
1660 : : /* Var itself is changed to the requested type. */
1661 : 20 : newvar->vartype = context->to_rowtype;
1662 : :
284 michael@paquier.xyz 1663 : 20 : newnode = palloc_object(ConvertRowtypeExpr);
3264 tgl@sss.pgh.pa.us 1664 : 20 : *newnode = *r; /* initially copy all fields of the CRE */
1665 : 20 : newnode->arg = (Expr *) newvar;
1666 : :
3265 rhaas@postgresql.org 1667 : 20 : return (Node *) newnode;
1668 : : }
1669 : : /* otherwise fall through to process the expression normally */
1670 : : }
5195 tgl@sss.pgh.pa.us 1671 [ - + ]: 56230 : else if (IsA(node, Query))
1672 : : {
1673 : : /* Recurse into RTE subquery or not-yet-planned sublink subquery */
1674 : : Query *newnode;
1675 : :
5195 tgl@sss.pgh.pa.us 1676 :UBC 0 : context->sublevels_up++;
1677 : 0 : newnode = query_tree_mutator((Query *) node,
1678 : : map_variable_attnos_mutator,
1679 : : context,
1680 : : 0);
1681 : 0 : context->sublevels_up--;
1682 : 0 : return (Node *) newnode;
1683 : : }
661 peter@eisentraut.org 1684 :CBC 56402 : return expression_tree_mutator(node, map_variable_attnos_mutator, context);
1685 : : }
1686 : :
1687 : : Node *
5195 tgl@sss.pgh.pa.us 1688 : 6237 : map_variable_attnos(Node *node,
1689 : : int target_varno, int sublevels_up,
1690 : : const AttrMap *attno_map,
1691 : : Oid to_rowtype, bool *found_whole_row)
1692 : : {
1693 : : map_variable_attnos_context context;
1694 : :
1695 : 6237 : context.target_varno = target_varno;
1696 : 6237 : context.sublevels_up = sublevels_up;
1697 : 6237 : context.attno_map = attno_map;
3335 rhaas@postgresql.org 1698 : 6237 : context.to_rowtype = to_rowtype;
5195 tgl@sss.pgh.pa.us 1699 : 6237 : context.found_whole_row = found_whole_row;
1700 : :
1701 : 6237 : *found_whole_row = false;
1702 : :
1703 : : /*
1704 : : * Must be prepared to start with a Query or a bare expression tree; if
1705 : : * it's a Query, we don't want to increment sublevels_up.
1706 : : */
1707 : 6237 : return query_or_expression_tree_mutator(node,
1708 : : map_variable_attnos_mutator,
1709 : : &context,
1710 : : 0);
1711 : : }
1712 : :
1713 : :
1714 : : /*
1715 : : * ReplaceVarsFromTargetList - replace Vars with items from a targetlist
1716 : : *
1717 : : * Vars matching target_varno and sublevels_up are replaced by the
1718 : : * entry with matching resno from targetlist, if there is one.
1719 : : *
1720 : : * If there is no matching resno for such a Var, the action depends on the
1721 : : * nomatch_option:
1722 : : * REPLACEVARS_REPORT_ERROR: throw an error
1723 : : * REPLACEVARS_CHANGE_VARNO: change Var's varno to nomatch_varno
1724 : : * REPLACEVARS_SUBSTITUTE_NULL: replace Var with a NULL Const of same type
1725 : : *
1726 : : * The caller must also provide target_rte, the RTE describing the target
1727 : : * relation. This is needed to handle whole-row Vars referencing the target.
1728 : : * We expand such Vars into RowExpr constructs.
1729 : : *
1730 : : * In addition, for INSERT/UPDATE/DELETE/MERGE queries, the caller must
1731 : : * provide result_relation, the index of the result relation in the rewritten
1732 : : * query. This is needed to handle OLD/NEW RETURNING list Vars referencing
1733 : : * target_varno. When such Vars are expanded, their varreturningtype is
1734 : : * copied onto any replacement Vars referencing result_relation. In addition,
1735 : : * if the replacement expression from the targetlist is not simply a Var
1736 : : * referencing result_relation, it is wrapped in a ReturningExpr node (causing
1737 : : * the executor to return NULL if the OLD/NEW row doesn't exist).
1738 : : *
1739 : : * Note that ReplaceVarFromTargetList always generates the replacement
1740 : : * expression with varlevelsup = 0. The caller is responsible for adjusting
1741 : : * the varlevelsup if needed. This simplifies the caller's life if it wants to
1742 : : * cache the replacement expressions.
1743 : : *
1744 : : * outer_hasSubLinks works the same as for replace_rte_variables().
1745 : : */
1746 : :
1747 : : typedef struct
1748 : : {
1749 : : RangeTblEntry *target_rte;
1750 : : List *targetlist;
1751 : : int result_relation;
1752 : : ReplaceVarsNoMatchOption nomatch_option;
1753 : : int nomatch_varno;
1754 : : } ReplaceVarsFromTargetList_context;
1755 : :
1756 : : static Node *
185 peter@eisentraut.org 1757 : 9633 : ReplaceVarsFromTargetList_callback(const Var *var,
1758 : : replace_rte_variables_context *context)
1759 : : {
5064 tgl@sss.pgh.pa.us 1760 : 9633 : ReplaceVarsFromTargetList_context *rcon = (ReplaceVarsFromTargetList_context *) context->callback_arg;
1761 : : Node *newnode;
1762 : :
572 rguo@postgresql.org 1763 : 9633 : newnode = ReplaceVarFromTargetList(var,
1764 : : rcon->target_rte,
1765 : : rcon->targetlist,
1766 : : rcon->result_relation,
1767 : : rcon->nomatch_option,
1768 : : rcon->nomatch_varno);
1769 : :
1770 : : /* Must adjust varlevelsup if replaced Var is within a subquery */
1771 [ + + ]: 9621 : if (var->varlevelsup > 0)
1772 : 172 : IncrementVarSublevelsUp(newnode, var->varlevelsup, 0);
1773 : :
1774 : 9621 : return newnode;
1775 : : }
1776 : :
1777 : : Node *
185 peter@eisentraut.org 1778 : 103091 : ReplaceVarFromTargetList(const Var *var,
1779 : : RangeTblEntry *target_rte,
1780 : : List *targetlist,
1781 : : int result_relation,
1782 : : ReplaceVarsNoMatchOption nomatch_option,
1783 : : int nomatch_varno)
1784 : : {
1785 : : TargetEntry *tle;
1786 : :
6227 tgl@sss.pgh.pa.us 1787 [ + + ]: 103091 : if (var->varattno == InvalidAttrNumber)
1788 : : {
1789 : : /* Must expand whole-tuple reference into RowExpr */
1790 : : RowExpr *rowexpr;
1791 : : List *colnames;
1792 : : List *fields;
1793 : : ListCell *lc;
1794 : :
1795 : : /*
1796 : : * If generating an expansion for a var of a named rowtype (ie, this
1797 : : * is a plain relation RTE), then we must include dummy items for
1798 : : * dropped columns. If the var is RECORD (ie, this is a JOIN), then
1799 : : * omit dropped columns. In the latter case, attach column names to
1800 : : * the RowExpr for use of the executor and ruleutils.c.
1801 : : *
1802 : : * In order to be able to cache the results, we always generate the
1803 : : * expansion with varlevelsup = 0. The caller is responsible for
1804 : : * adjusting it if needed.
1805 : : *
1806 : : * The varreturningtype is copied onto each individual field Var, so
1807 : : * that it is handled correctly when we recurse.
1808 : : */
572 rguo@postgresql.org 1809 : 614 : expandRTE(target_rte,
1810 : 614 : var->varno, 0 /* not varlevelsup */ ,
1811 : 614 : var->varreturningtype, var->location,
1812 : 614 : (var->vartype != RECORDOID),
1813 : : &colnames, &fields);
6227 tgl@sss.pgh.pa.us 1814 : 614 : rowexpr = makeNode(RowExpr);
1815 : : /* the fields will be set below */
572 rguo@postgresql.org 1816 : 614 : rowexpr->args = NIL;
6227 tgl@sss.pgh.pa.us 1817 : 614 : rowexpr->row_typeid = var->vartype;
1818 : 614 : rowexpr->row_format = COERCE_IMPLICIT_CAST;
1648 1819 [ + + ]: 614 : rowexpr->colnames = (var->vartype == RECORDOID) ? colnames : NIL;
6227 1820 : 614 : rowexpr->location = var->location;
1821 : : /* Adjust the generated per-field Vars... */
572 rguo@postgresql.org 1822 [ + - + + : 2256 : foreach(lc, fields)
+ + ]
1823 : : {
1824 : 1642 : Node *field = lfirst(lc);
1825 : :
1826 [ + - + - ]: 1642 : if (field && IsA(field, Var))
1827 : 1642 : field = ReplaceVarFromTargetList((Var *) field,
1828 : : target_rte,
1829 : : targetlist,
1830 : : result_relation,
1831 : : nomatch_option,
1832 : : nomatch_varno);
1833 : 1642 : rowexpr->args = lappend(rowexpr->args, field);
1834 : : }
1835 : :
1836 : : /* Wrap it in a ReturningExpr, if needed, per comments above */
612 dean.a.rasheed@gmail 1837 [ + + ]: 614 : if (var->varreturningtype != VAR_RETURNING_DEFAULT)
1838 : : {
1839 : 68 : ReturningExpr *rexpr = makeNode(ReturningExpr);
1840 : :
572 rguo@postgresql.org 1841 : 68 : rexpr->retlevelsup = 0;
612 dean.a.rasheed@gmail 1842 : 68 : rexpr->retold = (var->varreturningtype == VAR_RETURNING_OLD);
1843 : 68 : rexpr->retexpr = (Expr *) rowexpr;
1844 : :
1845 : 68 : return (Node *) rexpr;
1846 : : }
1847 : :
6227 tgl@sss.pgh.pa.us 1848 : 546 : return (Node *) rowexpr;
1849 : : }
1850 : :
1851 : : /* Normal case referencing one targetlist element */
572 rguo@postgresql.org 1852 : 102477 : tle = get_tle_by_resno(targetlist, var->varattno);
1853 : :
5824 tgl@sss.pgh.pa.us 1854 [ + + - + ]: 102477 : if (tle == NULL || tle->resjunk)
1855 : : {
1856 : : /* Failed to find column in targetlist */
572 rguo@postgresql.org 1857 [ + + + - ]: 390 : switch (nomatch_option)
1858 : : {
5064 tgl@sss.pgh.pa.us 1859 :GBC 12 : case REPLACEVARS_REPORT_ERROR:
1860 : :
1861 : : /*
1862 : : * A system column can never match a targetlist entry, since
1863 : : * those all have positive resnos, so throw a suitable
1864 : : * user-facing error.
1865 : : */
30 michael@paquier.xyz 1866 [ + - ]:GNC 12 : if (var->varattno < 0)
1867 [ + - ]: 12 : ereport(ERROR,
1868 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1869 : : errmsg("cannot use system column \"%s\" in RETURNING list of a query that is rewritten by a rule",
1870 : : get_rte_attribute_name(target_rte,
1871 : : var->varattno))));
1872 : :
1873 : : /* fall through, throw error below */
5064 tgl@sss.pgh.pa.us 1874 :UBC 0 : break;
1875 : :
5064 tgl@sss.pgh.pa.us 1876 :CBC 278 : case REPLACEVARS_CHANGE_VARNO:
1877 : : {
185 peter@eisentraut.org 1878 : 278 : Var *newvar = copyObject(var);
1879 : :
1880 : 278 : newvar->varno = nomatch_varno;
1881 : 278 : newvar->varlevelsup = 0;
1882 : : /* we leave the syntactic referent alone */
1883 : 278 : return (Node *) newvar;
1884 : : }
1885 : :
5064 tgl@sss.pgh.pa.us 1886 : 100 : case REPLACEVARS_SUBSTITUTE_NULL:
1887 : : {
1888 : : /*
1889 : : * If Var is of domain type, we must add a CoerceToDomain
1890 : : * node, in case there is a NOT NULL domain constraint.
1891 : : */
1892 : : int16 vartyplen;
1893 : : bool vartypbyval;
1894 : :
599 1895 : 100 : get_typlenbyval(var->vartype, &vartyplen, &vartypbyval);
1896 : 100 : return coerce_null_to_domain(var->vartype,
1897 : 100 : var->vartypmod,
1898 : 100 : var->varcollid,
1899 : : vartyplen,
1900 : : vartypbyval);
1901 : : }
1902 : : }
5064 tgl@sss.pgh.pa.us 1903 [ # # ]:UBC 0 : elog(ERROR, "could not find replacement targetlist entry for attno %d",
1904 : : var->varattno);
1905 : : return NULL; /* keep compiler quiet */
1906 : : }
1907 : : else
1908 : : {
1909 : : /* Make a copy of the tlist item to return */
3482 peter_e@gmx.net 1910 :CBC 102087 : Expr *newnode = copyObject(tle->expr);
1911 : :
1912 : : /*
1913 : : * Check to see if the tlist item contains a PARAM_MULTIEXPR Param,
1914 : : * and throw error if so. This case could only happen when expanding
1915 : : * an ON UPDATE rule's NEW variable and the referenced tlist item in
1916 : : * the original UPDATE command is part of a multiple assignment. There
1917 : : * seems no practical way to handle such cases without multiple
1918 : : * evaluation of the multiple assignment's sub-select, which would
1919 : : * create semantic oddities that users of rules would probably prefer
1920 : : * not to cope with. So treat it as an unimplemented feature.
1921 : : */
1922 [ - + ]: 102087 : if (contains_multiexpr_param((Node *) newnode, NULL))
4477 tgl@sss.pgh.pa.us 1923 [ # # ]:UBC 0 : ereport(ERROR,
1924 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1925 : : errmsg("NEW variables in ON UPDATE rules cannot reference columns that are part of a multiple assignment in the subject UPDATE command")));
1926 : :
1927 : : /* Handle any OLD/NEW RETURNING list Vars */
612 dean.a.rasheed@gmail 1928 [ + + ]:CBC 102087 : if (var->varreturningtype != VAR_RETURNING_DEFAULT)
1929 : : {
1930 : : /*
1931 : : * Copy varreturningtype onto any Vars in the tlist item that
1932 : : * refer to result_relation (which had better be non-zero).
1933 : : */
572 rguo@postgresql.org 1934 [ - + ]: 826 : if (result_relation == 0)
612 dean.a.rasheed@gmail 1935 [ # # ]:UBC 0 : elog(ERROR, "variable returning old/new found outside RETURNING list");
1936 : :
572 rguo@postgresql.org 1937 :CBC 826 : SetVarReturningType((Node *) newnode, result_relation,
1938 : 826 : 0, var->varreturningtype);
1939 : :
1940 : : /* Wrap it in a ReturningExpr, if needed, per comments above */
612 dean.a.rasheed@gmail 1941 [ + + ]: 826 : if (!IsA(newnode, Var) ||
572 rguo@postgresql.org 1942 [ + + ]: 620 : ((Var *) newnode)->varno != result_relation ||
1943 [ - + ]: 580 : ((Var *) newnode)->varlevelsup != 0)
1944 : : {
612 dean.a.rasheed@gmail 1945 : 246 : ReturningExpr *rexpr = makeNode(ReturningExpr);
1946 : :
572 rguo@postgresql.org 1947 : 246 : rexpr->retlevelsup = 0;
612 dean.a.rasheed@gmail 1948 : 246 : rexpr->retold = (var->varreturningtype == VAR_RETURNING_OLD);
1949 : 246 : rexpr->retexpr = newnode;
1950 : :
1951 : 246 : newnode = (Expr *) rexpr;
1952 : : }
1953 : : }
1954 : :
3482 peter_e@gmx.net 1955 : 102087 : return (Node *) newnode;
1956 : : }
1957 : : }
1958 : :
1959 : : Node *
5064 tgl@sss.pgh.pa.us 1960 : 8068 : ReplaceVarsFromTargetList(Node *node,
1961 : : int target_varno, int sublevels_up,
1962 : : RangeTblEntry *target_rte,
1963 : : List *targetlist,
1964 : : int result_relation,
1965 : : ReplaceVarsNoMatchOption nomatch_option,
1966 : : int nomatch_varno,
1967 : : bool *outer_hasSubLinks)
1968 : : {
1969 : : ReplaceVarsFromTargetList_context context;
1970 : :
7778 1971 : 8068 : context.target_rte = target_rte;
9487 1972 : 8068 : context.targetlist = targetlist;
612 dean.a.rasheed@gmail 1973 : 8068 : context.result_relation = result_relation;
5064 tgl@sss.pgh.pa.us 1974 : 8068 : context.nomatch_option = nomatch_option;
1975 : 8068 : context.nomatch_varno = nomatch_varno;
1976 : :
6227 1977 : 8068 : return replace_rte_variables(node, target_varno, sublevels_up,
1978 : : ReplaceVarsFromTargetList_callback,
1979 : : &context,
1980 : : outer_hasSubLinks);
1981 : : }
|