Branch data 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
86 : 486 : contain_aggs_of_level(Node *node, int levelsup)
87 : : {
88 : : contain_aggs_of_level_context context;
89 : :
90 : 486 : 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 : : */
96 : 486 : return query_or_expression_tree_walker(node,
97 : : contain_aggs_of_level_walker,
98 : : &context,
99 : : 0);
100 : : }
101 : :
102 : : static bool
103 : 1164 : contain_aggs_of_level_walker(Node *node,
104 : : contain_aggs_of_level_context *context)
105 : : {
106 [ + + ]: 1164 : if (node == NULL)
107 : 298 : return false;
108 [ - + ]: 866 : if (IsA(node, Aggref))
109 : : {
110 [ # # ]: 0 : if (((Aggref *) node)->agglevelsup == context->sublevels_up)
111 : 0 : return true; /* abort the tree traversal and return true */
112 : : /* else fall through to examine argument */
113 : : }
114 [ - + ]: 866 : if (IsA(node, GroupingFunc))
115 : : {
116 [ # # ]: 0 : if (((GroupingFunc *) node)->agglevelsup == context->sublevels_up)
117 : 0 : return true;
118 : : /* else fall through to examine argument */
119 : : }
120 [ + + ]: 866 : if (IsA(node, Query))
121 : : {
122 : : /* Recurse into subselects */
123 : : bool result;
124 : :
125 : 18 : context->sublevels_up++;
126 : 18 : result = query_tree_walker((Query *) node,
127 : : contain_aggs_of_level_walker,
128 : : context, 0);
129 : 18 : context->sublevels_up--;
130 : 18 : return result;
131 : : }
132 : 848 : 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
150 : 40 : locate_agg_of_level(Node *node, int levelsup)
151 : : {
152 : : locate_agg_of_level_context context;
153 : :
154 : 40 : context.agg_location = -1; /* in case we find nothing */
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 : : }
185 [ - + ]: 120 : if (IsA(node, GroupingFunc))
186 : : {
187 [ # # ]: 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 : : }
194 [ + + ]: 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 : : }
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
215 : 8048 : 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 : : */
221 : 8048 : return query_or_expression_tree_walker(node,
222 : : contain_windowfuncs_walker,
223 : : NULL,
224 : : 0);
225 : : }
226 : :
227 : : static bool
228 : 8812 : contain_windowfuncs_walker(Node *node, void *context)
229 : : {
230 [ + + ]: 8812 : if (node == NULL)
231 : 120 : return false;
232 [ + + ]: 8692 : if (IsA(node, WindowFunc))
233 : 9 : return true; /* abort the tree traversal and return true */
234 : : /* Mustn't recurse into subselects */
235 : 8683 : 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
252 : 4 : locate_windowfunc(Node *node)
253 : : {
254 : : locate_windowfunc_context context;
255 : :
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 : : */
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)
274 : 0 : return false;
275 [ + - ]: 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 */
285 : 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
293 : 100373 : 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 : : */
299 : 100373 : return query_or_expression_tree_walker(node,
300 : : checkExprHasSubLink_walker,
301 : : NULL,
302 : : QTW_IGNORE_RC_SUBQUERIES);
303 : : }
304 : :
305 : : static bool
306 : 169570 : checkExprHasSubLink_walker(Node *node, void *context)
307 : : {
308 [ + + ]: 169570 : if (node == NULL)
309 : 3062 : return false;
310 [ + + ]: 166508 : if (IsA(node, SubLink))
311 : 1289 : return true; /* abort the tree traversal and return true */
312 : 165219 : 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
322 : 158385 : contains_multiexpr_param(Node *node, void *context)
323 : : {
324 [ + + ]: 158385 : if (node == NULL)
325 : 2655 : return false;
326 [ + + ]: 155730 : if (IsA(node, Param))
327 : : {
328 [ - + ]: 421 : if (((Param *) node)->paramkind == PARAM_MULTIEXPR)
329 : 0 : return true; /* abort the tree traversal and return true */
330 : 421 : return false;
331 : : }
332 : 155309 : 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
348 : 41978 : CombineRangeTables(List **dst_rtable, List **dst_perminfos,
349 : : List *src_rtable, List *src_perminfos)
350 : : {
351 : : ListCell *l;
352 : 41978 : int offset = list_length(*dst_perminfos);
353 : :
354 [ + + ]: 41978 : if (offset > 0)
355 : : {
356 [ + + + + : 100112 : foreach(l, src_rtable)
+ + ]
357 : : {
358 : 65068 : RangeTblEntry *rte = lfirst_node(RangeTblEntry, l);
359 : :
360 [ + + ]: 65068 : if (rte->perminfoindex > 0)
361 : 32282 : rte->perminfoindex += offset;
362 : : }
363 : : }
364 : :
365 : 41978 : *dst_perminfos = list_concat(*dst_perminfos, src_perminfos);
366 : 41978 : *dst_rtable = list_concat(*dst_rtable, src_rtable);
367 : 41978 : }
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
389 : 1912497 : OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context)
390 : : {
391 [ + + ]: 1912497 : if (node == NULL)
392 : 617454 : return false;
393 [ + + ]: 1295043 : if (IsA(node, Var))
394 : : {
395 : 657761 : Var *var = (Var *) node;
396 : :
397 [ + + ]: 657761 : if (var->varlevelsup == context->sublevels_up)
398 : : {
399 : 566378 : var->varno += context->offset;
400 : 566378 : var->varnullingrels = bms_offset_members(var->varnullingrels,
401 : : context->offset);
402 [ + - ]: 566378 : if (var->varnosyn > 0)
403 : 566378 : var->varnosyn += context->offset;
404 : : }
405 : 657761 : return false;
406 : : }
407 [ - + ]: 637282 : if (IsA(node, CurrentOfExpr))
408 : : {
409 : 0 : CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
410 : :
411 [ # # ]: 0 : if (context->sublevels_up == 0)
412 : 0 : cexpr->cvarno += context->offset;
413 : 0 : return false;
414 : : }
415 [ + + ]: 637282 : if (IsA(node, RangeTblRef))
416 : : {
417 : 55498 : RangeTblRef *rtr = (RangeTblRef *) node;
418 : :
419 [ + + ]: 55498 : if (context->sublevels_up == 0)
420 : 50162 : rtr->rtindex += context->offset;
421 : : /* the subquery itself is visited separately */
422 : 55498 : return false;
423 : : }
424 [ + + ]: 581784 : if (IsA(node, JoinExpr))
425 : : {
426 : 11146 : JoinExpr *j = (JoinExpr *) node;
427 : :
428 [ + + + + ]: 11146 : if (j->rtindex && context->sublevels_up == 0)
429 : 10001 : j->rtindex += context->offset;
430 : : /* fall through to examine children */
431 : : }
432 [ + + ]: 581784 : if (IsA(node, PlaceHolderVar))
433 : : {
434 : 439 : PlaceHolderVar *phv = (PlaceHolderVar *) node;
435 : :
436 [ + + ]: 439 : if (phv->phlevelsup == context->sublevels_up)
437 : : {
438 : 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 : : }
445 [ + + ]: 581784 : 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 */
457 : : Assert(!IsA(node, PlanRowMark));
458 : : Assert(!IsA(node, SpecialJoinInfo));
459 : : Assert(!IsA(node, PlaceHolderInfo));
460 : : Assert(!IsA(node, MinMaxAggInfo));
461 : :
462 [ + + ]: 581784 : if (IsA(node, Query))
463 : : {
464 : : /* Recurse into subselects */
465 : : bool result;
466 : :
467 : 4369 : context->sublevels_up++;
468 : 4369 : result = query_tree_walker((Query *) node, OffsetVarNodes_walker,
469 : : context, 0);
470 : 4369 : context->sublevels_up--;
471 : 4369 : return result;
472 : : }
473 : 577415 : return expression_tree_walker(node, OffsetVarNodes_walker, context);
474 : : }
475 : :
476 : : void
477 : 76320 : OffsetVarNodes(Node *node, int offset, int sublevels_up)
478 : : {
479 : : OffsetVarNodes_context context;
480 : :
481 : 76320 : context.offset = offset;
482 : 76320 : 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 : : */
489 [ + + + + ]: 76320 : if (node && IsA(node, Query))
490 : 38160 : {
491 : 38160 : 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 : : */
501 [ + - ]: 38160 : if (sublevels_up == 0)
502 : : {
503 : : ListCell *l;
504 : :
505 [ + + ]: 38160 : if (qry->resultRelation)
506 : 900 : qry->resultRelation += offset;
507 : :
508 [ - + ]: 38160 : if (qry->mergeTargetRelation)
509 : 0 : qry->mergeTargetRelation += offset;
510 : :
511 [ + + + + ]: 38160 : if (qry->onConflict && qry->onConflict->exclRelIndex)
512 : 44 : qry->onConflict->exclRelIndex += offset;
513 : :
514 [ + + + + : 38268 : foreach(l, qry->rowMarks)
+ + ]
515 : : {
516 : 108 : RowMarkClause *rc = (RowMarkClause *) lfirst(l);
517 : :
518 : 108 : rc->rti += offset;
519 : : }
520 : : }
521 : 38160 : query_tree_walker(qry, OffsetVarNodes_walker, &context, 0);
522 : : }
523 : : else
524 : 38160 : OffsetVarNodes_walker(node, &context);
525 : 76320 : }
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 : 1724458 : ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
557 : : {
558 [ + + ]: 1724458 : if (node == NULL)
559 : 493881 : return false;
560 [ + + ]: 1230577 : if (IsA(node, Var))
561 : : {
562 : 351161 : Var *var = (Var *) node;
563 : :
564 [ + + ]: 351161 : if (var->varlevelsup == context->sublevels_up)
565 : : {
566 [ + + ]: 342781 : if (var->varno == context->rt_index)
567 : : {
568 : : Assert(context->new_index != INVALID_VAR);
569 : 33453 : var->varno = context->new_index;
570 : : }
571 : 342781 : 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 */
575 [ + + ]: 342781 : if (var->varnosyn == context->rt_index &&
576 [ + + ]: 33473 : context->new_index != INVALID_VAR)
577 : 33453 : var->varnosyn = context->new_index;
578 : : }
579 : 351161 : return false;
580 : : }
581 [ - + ]: 879416 : if (IsA(node, CurrentOfExpr))
582 : : {
583 : 0 : CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
584 : :
585 [ # # ]: 0 : if (context->sublevels_up == 0 &&
586 [ # # ]: 0 : cexpr->cvarno == context->rt_index)
587 : : {
588 : : Assert(context->new_index != INVALID_VAR);
589 : 0 : cexpr->cvarno = context->new_index;
590 : : }
591 : 0 : return false;
592 : : }
593 [ + + ]: 879416 : if (IsA(node, RangeTblRef))
594 : : {
595 : 36740 : RangeTblRef *rtr = (RangeTblRef *) node;
596 : :
597 [ + + ]: 36740 : if (context->sublevels_up == 0 &&
598 [ + + ]: 34553 : rtr->rtindex == context->rt_index)
599 : : {
600 : : Assert(context->new_index != INVALID_VAR);
601 : 1162 : rtr->rtindex = context->new_index;
602 : : }
603 : : /* the subquery itself is visited separately */
604 : 36740 : return false;
605 : : }
606 [ + + ]: 842676 : if (IsA(node, JoinExpr))
607 : : {
608 : 14062 : JoinExpr *j = (JoinExpr *) node;
609 : :
610 [ + + ]: 14062 : if (context->sublevels_up == 0 &&
611 [ - + ]: 13994 : j->rtindex == context->rt_index)
612 : : {
613 : : Assert(context->new_index != INVALID_VAR);
614 : 0 : j->rtindex = context->new_index;
615 : : }
616 : : /* fall through to examine children */
617 : : }
618 [ + + ]: 842676 : 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);
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 : : }
633 [ - + ]: 842676 : if (IsA(node, PlanRowMark))
634 : : {
635 : 0 : PlanRowMark *rowmark = (PlanRowMark *) node;
636 : :
637 [ # # ]: 0 : if (context->sublevels_up == 0)
638 : : {
639 [ # # ]: 0 : if (rowmark->rti == context->rt_index)
640 : : {
641 : : Assert(context->new_index != INVALID_VAR);
642 : 0 : rowmark->rti = context->new_index;
643 : : }
644 [ # # ]: 0 : if (rowmark->prti == context->rt_index)
645 : : {
646 : : Assert(context->new_index != INVALID_VAR);
647 : 0 : rowmark->prti = context->new_index;
648 : : }
649 : : }
650 : 0 : return false;
651 : : }
652 [ + + ]: 842676 : 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 : : {
660 : : Assert(context->new_index != INVALID_VAR);
661 : 0 : appinfo->parent_relid = context->new_index;
662 : : }
663 [ - + ]: 40 : if (appinfo->child_relid == context->rt_index)
664 : : {
665 : : Assert(context->new_index != INVALID_VAR);
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 */
672 : : Assert(!IsA(node, SpecialJoinInfo));
673 : : Assert(!IsA(node, PlaceHolderInfo));
674 : : Assert(!IsA(node, MinMaxAggInfo));
675 : :
676 [ + + ]: 842676 : if (IsA(node, Query))
677 : : {
678 : : /* Recurse into subselects */
679 : : bool result;
680 : :
681 : 2865 : context->sublevels_up++;
682 : 2865 : result = query_tree_walker((Query *) node, ChangeVarNodes_walker,
683 : : context, 0);
684 : 2865 : context->sublevels_up--;
685 : 2865 : return result;
686 : : }
687 : 839811 : return expression_tree_walker(node, ChangeVarNodes_walker, context);
688 : : }
689 : :
690 : : void
691 : 58122 : ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up)
692 : : {
693 : : ChangeVarNodes_context context;
694 : :
695 : 58122 : context.rt_index = rt_index;
696 : 58122 : context.new_index = new_index;
697 : 58122 : 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 : : */
704 [ + + + + ]: 58122 : if (node && IsA(node, Query))
705 : 20910 : {
706 : 20910 : 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 : : */
716 [ + - ]: 20910 : if (sublevels_up == 0)
717 : : {
718 : : ListCell *l;
719 : :
720 [ + + ]: 20910 : if (qry->resultRelation == rt_index)
721 : : {
722 : : Assert(new_index != INVALID_VAR);
723 : 2198 : qry->resultRelation = new_index;
724 : : }
725 : :
726 [ + + ]: 20910 : if (qry->mergeTargetRelation == rt_index)
727 : : {
728 : : Assert(new_index != INVALID_VAR);
729 : 568 : qry->mergeTargetRelation = new_index;
730 : : }
731 : :
732 : : /* this is unlikely to ever be used, but ... */
733 [ + + - + ]: 20910 : if (qry->onConflict && qry->onConflict->exclRelIndex == rt_index)
734 : : {
735 : : Assert(new_index != INVALID_VAR);
736 : 0 : qry->onConflict->exclRelIndex = new_index;
737 : : }
738 : :
739 [ + + + + : 21008 : foreach(l, qry->rowMarks)
+ + ]
740 : : {
741 : 98 : RowMarkClause *rc = (RowMarkClause *) lfirst(l);
742 : :
743 [ + + ]: 98 : if (rc->rti == rt_index)
744 : : {
745 : : Assert(new_index != INVALID_VAR);
746 : 36 : rc->rti = new_index;
747 : : }
748 : : }
749 : : }
750 : 20910 : query_tree_walker(qry, ChangeVarNodes_walker, &context, 0);
751 : : }
752 : : else
753 : 37212 : ChangeVarNodes_walker(node, &context);
754 : 58122 : }
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
766 : 344417 : adjust_relid_set(Relids relids, int oldrelid, int newrelid)
767 : : {
768 [ + - + + ]: 344417 : if (!IS_SPECIAL_VARNO(oldrelid) && bms_is_member(oldrelid, relids))
769 : : {
770 : : /* Ensure we have a modifiable copy */
771 : 535 : relids = bms_copy(relids);
772 : : /* Remove old, add new */
773 : 535 : relids = bms_del_member(relids, oldrelid);
774 [ + + ]: 535 : if (!IS_SPECIAL_VARNO(newrelid))
775 : 25 : relids = bms_add_member(relids, newrelid);
776 : : }
777 : 344417 : 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
806 : 2543276 : IncrementVarSublevelsUp_walker(Node *node,
807 : : IncrementVarSublevelsUp_context *context)
808 : : {
809 [ + + ]: 2543276 : if (node == NULL)
810 : 792324 : return false;
811 [ + + ]: 1750952 : if (IsA(node, Var))
812 : : {
813 : 838546 : Var *var = (Var *) node;
814 : :
815 [ + + ]: 838546 : if (var->varlevelsup >= context->min_sublevels_up)
816 : 16822 : var->varlevelsup += context->delta_sublevels_up;
817 : 838546 : return false; /* done here */
818 : : }
819 [ - + ]: 912406 : if (IsA(node, CurrentOfExpr))
820 : : {
821 : : /* this should not happen */
822 [ # # ]: 0 : if (context->min_sublevels_up == 0)
823 [ # # ]: 0 : elog(ERROR, "cannot push down CurrentOfExpr");
824 : 0 : return false;
825 : : }
826 [ + + ]: 912406 : if (IsA(node, Aggref))
827 : : {
828 : 2280 : Aggref *agg = (Aggref *) node;
829 : :
830 [ + + ]: 2280 : if (agg->agglevelsup >= context->min_sublevels_up)
831 : 77 : agg->agglevelsup += context->delta_sublevels_up;
832 : : /* fall through to recurse into argument */
833 : : }
834 [ + + ]: 912406 : if (IsA(node, GroupingFunc))
835 : : {
836 : 57 : GroupingFunc *grp = (GroupingFunc *) node;
837 : :
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 : : }
842 [ + + ]: 912406 : 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 : : }
850 [ + + ]: 912406 : 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 : : }
858 [ + + ]: 912406 : if (IsA(node, RangeTblEntry))
859 : : {
860 : 97735 : RangeTblEntry *rte = (RangeTblEntry *) node;
861 : :
862 [ + + ]: 97735 : if (rte->rtekind == RTE_CTE)
863 : : {
864 [ + + ]: 4197 : if (rte->ctelevelsup >= context->min_sublevels_up)
865 : 4172 : rte->ctelevelsup += context->delta_sublevels_up;
866 : : }
867 : 97735 : return false; /* allow range_table_walker to continue */
868 : : }
869 [ + + ]: 814671 : if (IsA(node, Query))
870 : : {
871 : : /* Recurse into subselects */
872 : : bool result;
873 : :
874 : 16387 : context->min_sublevels_up++;
875 : 16387 : result = query_tree_walker((Query *) node,
876 : : IncrementVarSublevelsUp_walker,
877 : : context,
878 : : QTW_EXAMINE_RTES_BEFORE);
879 : 16387 : context->min_sublevels_up--;
880 : 16387 : return result;
881 : : }
882 : 798284 : return expression_tree_walker(node, IncrementVarSublevelsUp_walker, context);
883 : : }
884 : :
885 : : void
886 : 80554 : IncrementVarSublevelsUp(Node *node, int delta_sublevels_up,
887 : : int min_sublevels_up)
888 : : {
889 : : IncrementVarSublevelsUp_context context;
890 : :
891 : 80554 : context.delta_sublevels_up = delta_sublevels_up;
892 : 80554 : 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 : : */
898 : 80554 : query_or_expression_tree_walker(node,
899 : : IncrementVarSublevelsUp_walker,
900 : : &context,
901 : : QTW_EXAMINE_RTES_BEFORE);
902 : 80554 : }
903 : :
904 : : /*
905 : : * IncrementVarSublevelsUp_rtable -
906 : : * Same as IncrementVarSublevelsUp, but to be invoked on a range table.
907 : : */
908 : : void
909 : 3844 : IncrementVarSublevelsUp_rtable(List *rtable, int delta_sublevels_up,
910 : : int min_sublevels_up)
911 : : {
912 : : IncrementVarSublevelsUp_context context;
913 : :
914 : 3844 : context.delta_sublevels_up = delta_sublevels_up;
915 : 3844 : context.min_sublevels_up = min_sublevels_up;
916 : :
917 : 3844 : range_table_walker(rtable,
918 : : IncrementVarSublevelsUp_walker,
919 : : &context,
920 : : QTW_EXAMINE_RTES_BEFORE);
921 : 3844 : }
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
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
997 : 2654823 : rangeTableEntry_used_walker(Node *node,
998 : : rangeTableEntry_used_context *context)
999 : : {
1000 [ + + ]: 2654823 : if (node == NULL)
1001 : 496052 : return false;
1002 [ + + ]: 2158771 : if (IsA(node, Var))
1003 : : {
1004 : 621962 : Var *var = (Var *) node;
1005 : :
1006 [ + + ]: 621962 : if (var->varlevelsup == context->sublevels_up &&
1007 [ + + - + ]: 987120 : (var->varno == context->rt_index ||
1008 : 391161 : bms_is_member(context->rt_index, var->varnullingrels)))
1009 : 204798 : return true;
1010 : 417164 : return false;
1011 : : }
1012 [ - + ]: 1536809 : if (IsA(node, CurrentOfExpr))
1013 : : {
1014 : 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 : : }
1021 [ + + ]: 1536809 : if (IsA(node, RangeTblRef))
1022 : : {
1023 : 92367 : RangeTblRef *rtr = (RangeTblRef *) node;
1024 : :
1025 [ + + ]: 92367 : if (rtr->rtindex == context->rt_index &&
1026 [ + + ]: 49645 : context->sublevels_up == 0)
1027 : 47956 : return true;
1028 : : /* the subquery itself is visited separately */
1029 : 44411 : return false;
1030 : : }
1031 [ + + ]: 1444442 : if (IsA(node, JoinExpr))
1032 : : {
1033 : 32593 : JoinExpr *j = (JoinExpr *) node;
1034 : :
1035 [ + + ]: 32593 : if (j->rtindex == context->rt_index &&
1036 [ - + ]: 52 : context->sublevels_up == 0)
1037 : 0 : return true;
1038 : : /* fall through to examine children */
1039 : : }
1040 : : /* Shouldn't need to handle planner auxiliary nodes here */
1041 : : Assert(!IsA(node, PlaceHolderVar));
1042 : : Assert(!IsA(node, PlanRowMark));
1043 : : Assert(!IsA(node, SpecialJoinInfo));
1044 : : Assert(!IsA(node, AppendRelInfo));
1045 : : Assert(!IsA(node, PlaceHolderInfo));
1046 : : Assert(!IsA(node, MinMaxAggInfo));
1047 : :
1048 [ + + ]: 1444442 : if (IsA(node, Query))
1049 : : {
1050 : : /* Recurse into subselects */
1051 : : bool result;
1052 : :
1053 : 9782 : context->sublevels_up++;
1054 : 9782 : result = query_tree_walker((Query *) node, rangeTableEntry_used_walker,
1055 : : context, 0);
1056 : 9782 : context->sublevels_up--;
1057 : 9782 : return result;
1058 : : }
1059 : 1434660 : return expression_tree_walker(node, rangeTableEntry_used_walker, context);
1060 : : }
1061 : :
1062 : : bool
1063 : 263429 : rangeTableEntry_used(Node *node, int rt_index, int sublevels_up)
1064 : : {
1065 : : rangeTableEntry_used_context context;
1066 : :
1067 : 263429 : context.rt_index = rt_index;
1068 : 263429 : 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 : : */
1074 : 263429 : 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 *
1095 : 2391 : getInsertSelectQuery(Query *parsetree, Query ***subquery_ptr)
1096 : : {
1097 : : Query *selectquery;
1098 : : RangeTblEntry *selectrte;
1099 : : RangeTblRef *rtr;
1100 : :
1101 [ + + ]: 2391 : if (subquery_ptr)
1102 : 976 : *subquery_ptr = NULL;
1103 : :
1104 [ - + ]: 2391 : if (parsetree == NULL)
1105 : 0 : return parsetree;
1106 [ + + ]: 2391 : if (parsetree->commandType != CMD_INSERT)
1107 : 1017 : 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 : : */
1115 [ + - ]: 1374 : if (list_length(parsetree->rtable) >= 2 &&
1116 [ + + ]: 1374 : strcmp(rt_fetch(PRS2_OLD_VARNO, parsetree->rtable)->eref->aliasname,
1117 : 1258 : "old") == 0 &&
1118 [ + - ]: 1258 : strcmp(rt_fetch(PRS2_NEW_VARNO, parsetree->rtable)->eref->aliasname,
1119 : : "new") == 0)
1120 : 1258 : return parsetree;
1121 : : Assert(parsetree->jointree && IsA(parsetree->jointree, FromExpr));
1122 [ - + ]: 116 : if (list_length(parsetree->jointree->fromlist) != 1)
1123 [ # # ]: 0 : elog(ERROR, "expected to find SELECT subquery");
1124 : 116 : rtr = (RangeTblRef *) linitial(parsetree->jointree->fromlist);
1125 [ - + ]: 116 : if (!IsA(rtr, RangeTblRef))
1126 [ # # ]: 0 : elog(ERROR, "expected to find SELECT subquery");
1127 : 116 : selectrte = rt_fetch(rtr->rtindex, parsetree->rtable);
1128 [ + - ]: 116 : if (!(selectrte->rtekind == RTE_SUBQUERY &&
1129 [ + - ]: 116 : selectrte->subquery &&
1130 [ + - ]: 116 : IsA(selectrte->subquery, Query) &&
1131 [ - + ]: 116 : selectrte->subquery->commandType == CMD_SELECT))
1132 [ # # ]: 0 : elog(ERROR, "expected to find SELECT subquery");
1133 : 116 : selectquery = selectrte->subquery;
1134 [ + - ]: 116 : if (list_length(selectquery->rtable) >= 2 &&
1135 [ + - ]: 116 : strcmp(rt_fetch(PRS2_OLD_VARNO, selectquery->rtable)->eref->aliasname,
1136 : 116 : "old") == 0 &&
1137 [ + - ]: 116 : strcmp(rt_fetch(PRS2_NEW_VARNO, selectquery->rtable)->eref->aliasname,
1138 : : "new") == 0)
1139 : : {
1140 [ + + ]: 116 : if (subquery_ptr)
1141 : 40 : *subquery_ptr = &(selectrte->subquery);
1142 : 116 : return selectquery;
1143 : : }
1144 [ # # ]: 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
1153 : 2594 : AddQual(Query *parsetree, Node *qual)
1154 : : {
1155 : : Node *copy;
1156 : :
1157 [ + + ]: 2594 : if (qual == NULL)
1158 : 1256 : return;
1159 : :
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 : : */
1175 [ # # # # ]: 0 : if (parsetree->utilityStmt && IsA(parsetree->utilityStmt, NotifyStmt))
1176 : 0 : return;
1177 : : else
1178 [ # # ]: 0 : ereport(ERROR,
1179 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1180 : : errmsg("conditional utility statements are not implemented")));
1181 : : }
1182 : :
1183 [ - + ]: 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 : : */
1190 [ # # ]: 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 */
1196 : 1338 : copy = copyObject(qual);
1197 : :
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 : : */
1204 : : 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 : : */
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
1221 : 304 : AddInvertedQual(Query *parsetree, Node *qual)
1222 : : {
1223 : : BooleanTest *invqual;
1224 : :
1225 [ - + ]: 304 : if (qual == NULL)
1226 : 0 : return;
1227 : :
1228 : : /* Need not copy input qual, because AddQual will... */
1229 : 304 : invqual = makeNode(BooleanTest);
1230 : 304 : invqual->arg = (Expr *) qual;
1231 : 304 : invqual->booltesttype = IS_NOT_TRUE;
1232 : 304 : invqual->location = -1;
1233 : :
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 *
1245 : 5768 : add_nulling_relids(Node *node,
1246 : : const Bitmapset *target_relids,
1247 : : const Bitmapset *added_relids)
1248 : : {
1249 : : add_nulling_relids_context context;
1250 : :
1251 : 5768 : context.target_relids = target_relids;
1252 : 5768 : context.added_relids = added_relids;
1253 : 5768 : context.sublevels_up = 0;
1254 : 5768 : return query_or_expression_tree_mutator(node,
1255 : : add_nulling_relids_mutator,
1256 : : &context,
1257 : : 0);
1258 : : }
1259 : :
1260 : : static Node *
1261 : 23938 : add_nulling_relids_mutator(Node *node,
1262 : : add_nulling_relids_context *context)
1263 : : {
1264 [ + + ]: 23938 : if (node == NULL)
1265 : 928 : return NULL;
1266 [ + + ]: 23010 : if (IsA(node, Var))
1267 : : {
1268 : 8548 : Var *var = (Var *) node;
1269 : :
1270 [ + + ]: 8548 : if (var->varlevelsup == context->sublevels_up &&
1271 [ + + + + ]: 16916 : (context->target_relids == NULL ||
1272 : 8373 : bms_is_member(var->varno, context->target_relids)))
1273 : : {
1274 : 4770 : Relids newnullingrels = bms_union(var->varnullingrels,
1275 : : context->added_relids);
1276 : :
1277 : : /* Copy the Var ... */
1278 : 4770 : var = copyObject(var);
1279 : : /* ... and replace the copy's varnullingrels field */
1280 : 4770 : var->varnullingrels = newnullingrels;
1281 : 4770 : return (Node *) var;
1282 : : }
1283 : : /* Otherwise fall through to copy the Var normally */
1284 : : }
1285 [ + + ]: 14462 : else if (IsA(node, PlaceHolderVar))
1286 : : {
1287 : 927 : PlaceHolderVar *phv = (PlaceHolderVar *) node;
1288 : :
1289 [ + - ]: 927 : if (phv->phlevelsup == context->sublevels_up &&
1290 [ + - + - ]: 1854 : (context->target_relids == NULL ||
1291 : 927 : bms_overlap(phv->phrels, context->target_relids)))
1292 : : {
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 [ + + ]: 13535 : 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 : : }
1323 : 17273 : 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 *
1333 : 301788 : remove_nulling_relids(Node *node,
1334 : : const Bitmapset *removable_relids,
1335 : : const Bitmapset *except_relids)
1336 : : {
1337 : : remove_nulling_relids_context context;
1338 : :
1339 : 301788 : context.removable_relids = removable_relids;
1340 : 301788 : context.except_relids = except_relids;
1341 : 301788 : context.sublevels_up = 0;
1342 : 301788 : return query_or_expression_tree_mutator(node,
1343 : : remove_nulling_relids_mutator,
1344 : : &context,
1345 : : 0);
1346 : : }
1347 : :
1348 : : static Node *
1349 : 746067 : remove_nulling_relids_mutator(Node *node,
1350 : : remove_nulling_relids_context *context)
1351 : : {
1352 [ + + ]: 746067 : if (node == NULL)
1353 : 89560 : return NULL;
1354 [ + + ]: 656507 : if (IsA(node, Var))
1355 : : {
1356 : 378402 : Var *var = (Var *) node;
1357 : :
1358 [ + + ]: 378402 : if (var->varlevelsup == context->sublevels_up &&
1359 [ + + + + ]: 741524 : !bms_is_member(var->varno, context->except_relids) &&
1360 : 370508 : bms_overlap(var->varnullingrels, context->removable_relids))
1361 : : {
1362 : : /* Copy the Var ... */
1363 : 13156 : var = copyObject(var);
1364 : : /* ... and replace the copy's varnullingrels field */
1365 : 13156 : var->varnullingrels = bms_difference(var->varnullingrels,
1366 : : context->removable_relids);
1367 : 13156 : return (Node *) var;
1368 : : }
1369 : : /* Otherwise fall through to copy the Var normally */
1370 : : }
1371 [ + + ]: 278105 : 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 */
1390 : 4216 : phv->phnullingrels = bms_difference(phv->phnullingrels,
1391 : : context->removable_relids);
1392 : : /* We must also update phrels, if it contains a removable RTI */
1393 : 4216 : phv->phrels = bms_difference(phv->phrels,
1394 : : context->removable_relids);
1395 : : Assert(!bms_is_empty(phv->phrels));
1396 : 4216 : return (Node *) phv;
1397 : : }
1398 : : /* Otherwise fall through to copy the PlaceHolderVar normally */
1399 : : }
1400 [ + + ]: 273889 : else if (IsA(node, Query))
1401 : : {
1402 : : /* Recurse into RTE or sublink subquery */
1403 : : Query *newnode;
1404 : :
1405 : 811 : context->sublevels_up++;
1406 : 811 : newnode = query_tree_mutator((Query *) node,
1407 : : remove_nulling_relids_mutator,
1408 : : context,
1409 : : 0);
1410 : 811 : context->sublevels_up--;
1411 : 811 : return (Node *) newnode;
1412 : : }
1413 : 638324 : 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 *
1440 : 173728 : 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 : 173728 : context.callback = callback;
1449 : 173728 : context.callback_arg = callback_arg;
1450 : 173728 : context.target_varno = target_varno;
1451 : 173728 : 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 [ + + + + ]: 173728 : if (node && IsA(node, Query))
1458 : 4559 : context.inserted_sublink = ((Query *) node)->hasSubLinks;
1459 [ + + ]: 169169 : else if (outer_hasSubLinks)
1460 : 168870 : 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 : 173728 : result = query_or_expression_tree_mutator(node,
1469 : : replace_rte_variables_mutator,
1470 : : &context,
1471 : : 0);
1472 : :
1473 [ + + ]: 173716 : if (context.inserted_sublink)
1474 : : {
1475 [ + + + + ]: 20177 : if (result && IsA(result, Query))
1476 : 183 : ((Query *) result)->hasSubLinks = true;
1477 [ + - ]: 19994 : else if (outer_hasSubLinks)
1478 : 19994 : *outer_hasSubLinks = true;
1479 : : else
1480 [ # # ]: 0 : elog(ERROR, "replace_rte_variables inserted a SubLink, but has noplace to record it");
1481 : : }
1482 : :
1483 : 173716 : return result;
1484 : : }
1485 : :
1486 : : Node *
1487 : 767552 : replace_rte_variables_mutator(Node *node,
1488 : : replace_rte_variables_context *context)
1489 : : {
1490 [ + + ]: 767552 : if (node == NULL)
1491 : 227913 : return NULL;
1492 [ + + ]: 539639 : if (IsA(node, Var))
1493 : : {
1494 : 211519 : Var *var = (Var *) node;
1495 : :
1496 [ + + ]: 211519 : if (var->varno == context->target_varno &&
1497 [ + + ]: 111108 : var->varlevelsup == context->sublevels_up)
1498 : : {
1499 : : /* Found a matching variable, make the substitution */
1500 : : Node *newnode;
1501 : :
1502 : 104698 : newnode = context->callback(var, context);
1503 : : /* Detect if we are adding a sublink to query */
1504 [ + + ]: 104686 : if (!context->inserted_sublink)
1505 : 94381 : context->inserted_sublink = checkExprHasSubLink(newnode);
1506 : 104686 : return newnode;
1507 : : }
1508 : : /* otherwise fall through to copy the var normally */
1509 : : }
1510 [ + + ]: 328120 : 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 : :
1516 : 2434 : context->sublevels_up++;
1517 : 2434 : save_inserted_sublink = context->inserted_sublink;
1518 : 2434 : context->inserted_sublink = ((Query *) node)->hasSubLinks;
1519 : 2434 : newnode = query_tree_mutator((Query *) node,
1520 : : replace_rte_variables_mutator,
1521 : : context,
1522 : : 0);
1523 : 2434 : newnode->hasSubLinks |= context->inserted_sublink;
1524 : 2434 : context->inserted_sublink = save_inserted_sublink;
1525 : 2434 : context->sublevels_up--;
1526 : 2434 : return (Node *) newnode;
1527 : : }
1528 : 432507 : 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 *
1566 : 73881 : map_variable_attnos_mutator(Node *node,
1567 : : map_variable_attnos_context *context)
1568 : : {
1569 [ + + ]: 73881 : if (node == NULL)
1570 : 112 : return NULL;
1571 [ + + ]: 73769 : if (IsA(node, Var))
1572 : : {
1573 : 17446 : Var *var = (Var *) node;
1574 : :
1575 [ + + ]: 17446 : if (var->varno == context->target_varno &&
1576 [ + - ]: 17286 : var->varlevelsup == context->sublevels_up)
1577 : : {
1578 : : /* Found a matching variable, make the substitution */
1579 : 17286 : Var *newvar = palloc_object(Var);
1580 : 17286 : int attno = var->varattno;
1581 : :
1582 : 17286 : *newvar = *var; /* initially copy all fields of the Var */
1583 : :
1584 [ + + ]: 17286 : if (attno > 0)
1585 : : {
1586 : : /* user-defined column, replace attno */
1587 [ + - ]: 17058 : if (attno > context->attno_map->maplen ||
1588 [ - + ]: 17058 : context->attno_map->attnums[attno - 1] == 0)
1589 [ # # ]: 0 : elog(ERROR, "unexpected varattno %d in expression to be mapped",
1590 : : attno);
1591 : 17058 : newvar->varattno = context->attno_map->attnums[attno - 1];
1592 : : /* If the syntactic referent is same RTE, fix it too */
1593 [ + + ]: 17058 : if (newvar->varnosyn == context->target_varno)
1594 : 16998 : newvar->varattnosyn = newvar->varattno;
1595 : : }
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. */
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. */
1608 : : Assert(var->vartype != RECORDOID);
1609 : :
1610 : : /* Var itself is changed to the requested type. */
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 : : }
1626 : 17250 : return (Node *) newvar;
1627 : : }
1628 : : /* otherwise fall through to copy the var normally */
1629 : : }
1630 [ + + ]: 56323 : else if (IsA(node, ConvertRowtypeExpr))
1631 : : {
1632 : 32 : ConvertRowtypeExpr *r = (ConvertRowtypeExpr *) node;
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;
1650 : 20 : Var *newvar = palloc_object(Var);
1651 : :
1652 : : /* whole-row variable, warn caller */
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 : : Assert(var->vartype != RECORDOID);
1659 : :
1660 : : /* Var itself is changed to the requested type. */
1661 : 20 : newvar->vartype = context->to_rowtype;
1662 : :
1663 : 20 : newnode = palloc_object(ConvertRowtypeExpr);
1664 : 20 : *newnode = *r; /* initially copy all fields of the CRE */
1665 : 20 : newnode->arg = (Expr *) newvar;
1666 : :
1667 : 20 : return (Node *) newnode;
1668 : : }
1669 : : /* otherwise fall through to process the expression normally */
1670 : : }
1671 [ - + ]: 56291 : else if (IsA(node, Query))
1672 : : {
1673 : : /* Recurse into RTE subquery or not-yet-planned sublink subquery */
1674 : : Query *newnode;
1675 : :
1676 : 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 : : }
1684 : 56463 : return expression_tree_mutator(node, map_variable_attnos_mutator, context);
1685 : : }
1686 : :
1687 : : Node *
1688 : 6254 : 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 : 6254 : context.target_varno = target_varno;
1696 : 6254 : context.sublevels_up = sublevels_up;
1697 : 6254 : context.attno_map = attno_map;
1698 : 6254 : context.to_rowtype = to_rowtype;
1699 : 6254 : context.found_whole_row = found_whole_row;
1700 : :
1701 : 6254 : *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 : 6254 : 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 *
1757 : 9764 : ReplaceVarsFromTargetList_callback(const Var *var,
1758 : : replace_rte_variables_context *context)
1759 : : {
1760 : 9764 : ReplaceVarsFromTargetList_context *rcon = (ReplaceVarsFromTargetList_context *) context->callback_arg;
1761 : : Node *newnode;
1762 : :
1763 : 9764 : 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 [ + + ]: 9752 : if (var->varlevelsup > 0)
1772 : 172 : IncrementVarSublevelsUp(newnode, var->varlevelsup, 0);
1773 : :
1774 : 9752 : return newnode;
1775 : : }
1776 : :
1777 : : Node *
1778 : 103624 : 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 : :
1787 [ + + ]: 103624 : 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 : : */
1809 : 620 : expandRTE(target_rte,
1810 : 620 : var->varno, 0 /* not varlevelsup */ ,
1811 : 620 : var->varreturningtype, var->location,
1812 : 620 : (var->vartype != RECORDOID),
1813 : : &colnames, &fields);
1814 : 620 : rowexpr = makeNode(RowExpr);
1815 : : /* the fields will be set below */
1816 : 620 : rowexpr->args = NIL;
1817 : 620 : rowexpr->row_typeid = var->vartype;
1818 : 620 : rowexpr->row_format = COERCE_IMPLICIT_CAST;
1819 [ + + ]: 620 : rowexpr->colnames = (var->vartype == RECORDOID) ? colnames : NIL;
1820 : 620 : rowexpr->location = var->location;
1821 : : /* Adjust the generated per-field Vars... */
1822 [ + - + + : 2274 : foreach(lc, fields)
+ + ]
1823 : : {
1824 : 1654 : Node *field = lfirst(lc);
1825 : :
1826 [ + - + - ]: 1654 : if (field && IsA(field, Var))
1827 : 1654 : field = ReplaceVarFromTargetList((Var *) field,
1828 : : target_rte,
1829 : : targetlist,
1830 : : result_relation,
1831 : : nomatch_option,
1832 : : nomatch_varno);
1833 : 1654 : rowexpr->args = lappend(rowexpr->args, field);
1834 : : }
1835 : :
1836 : : /* Wrap it in a ReturningExpr, if needed, per comments above */
1837 [ + + ]: 620 : if (var->varreturningtype != VAR_RETURNING_DEFAULT)
1838 : : {
1839 : 68 : ReturningExpr *rexpr = makeNode(ReturningExpr);
1840 : :
1841 : 68 : rexpr->retlevelsup = 0;
1842 : 68 : rexpr->retold = (var->varreturningtype == VAR_RETURNING_OLD);
1843 : 68 : rexpr->retexpr = (Expr *) rowexpr;
1844 : :
1845 : 68 : return (Node *) rexpr;
1846 : : }
1847 : :
1848 : 552 : return (Node *) rowexpr;
1849 : : }
1850 : :
1851 : : /* Normal case referencing one targetlist element */
1852 : 103004 : tle = get_tle_by_resno(targetlist, var->varattno);
1853 : :
1854 [ + + - + ]: 103004 : if (tle == NULL || tle->resjunk)
1855 : : {
1856 : : /* Failed to find column in targetlist */
1857 [ + + + - ]: 390 : switch (nomatch_option)
1858 : : {
1859 : 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 : : */
1866 [ + - ]: 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 */
1874 : 0 : break;
1875 : :
1876 : 278 : case REPLACEVARS_CHANGE_VARNO:
1877 : : {
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 : :
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 : :
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 : : }
1903 [ # # ]: 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 */
1910 : 102614 : 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 [ - + ]: 102614 : if (contains_multiexpr_param((Node *) newnode, NULL))
1923 [ # # ]: 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 */
1928 [ + + ]: 102614 : 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 : : */
1934 [ - + ]: 826 : if (result_relation == 0)
1935 [ # # ]: 0 : elog(ERROR, "variable returning old/new found outside RETURNING list");
1936 : :
1937 : 826 : SetVarReturningType((Node *) newnode, result_relation,
1938 : 826 : 0, var->varreturningtype);
1939 : :
1940 : : /* Wrap it in a ReturningExpr, if needed, per comments above */
1941 [ + + ]: 826 : if (!IsA(newnode, Var) ||
1942 [ + + ]: 620 : ((Var *) newnode)->varno != result_relation ||
1943 [ - + ]: 580 : ((Var *) newnode)->varlevelsup != 0)
1944 : : {
1945 : 246 : ReturningExpr *rexpr = makeNode(ReturningExpr);
1946 : :
1947 : 246 : rexpr->retlevelsup = 0;
1948 : 246 : rexpr->retold = (var->varreturningtype == VAR_RETURNING_OLD);
1949 : 246 : rexpr->retexpr = newnode;
1950 : :
1951 : 246 : newnode = (Expr *) rexpr;
1952 : : }
1953 : : }
1954 : :
1955 : 102614 : return (Node *) newnode;
1956 : : }
1957 : : }
1958 : :
1959 : : Node *
1960 : 8199 : 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 : :
1971 : 8199 : context.target_rte = target_rte;
1972 : 8199 : context.targetlist = targetlist;
1973 : 8199 : context.result_relation = result_relation;
1974 : 8199 : context.nomatch_option = nomatch_option;
1975 : 8199 : context.nomatch_varno = nomatch_varno;
1976 : :
1977 : 8199 : return replace_rte_variables(node, target_varno, sublevels_up,
1978 : : ReplaceVarsFromTargetList_callback,
1979 : : &context,
1980 : : outer_hasSubLinks);
1981 : : }
|