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 : 1182 : contain_aggs_of_level_walker(Node *node,
104 : : contain_aggs_of_level_context *context)
105 : : {
106 [ + + ]: 1182 : if (node == NULL)
107 : 316 : 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 : 8080 : 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 : 8080 : return query_or_expression_tree_walker(node,
222 : : contain_windowfuncs_walker,
223 : : NULL,
224 : : 0);
225 : : }
226 : :
227 : : static bool
228 : 8844 : contain_windowfuncs_walker(Node *node, void *context)
229 : : {
230 [ + + ]: 8844 : if (node == NULL)
231 : 120 : return false;
232 [ + + ]: 8724 : if (IsA(node, WindowFunc))
233 : 9 : return true; /* abort the tree traversal and return true */
234 : : /* Mustn't recurse into subselects */
235 : 8715 : 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 : 105341 : 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 : 105341 : return query_or_expression_tree_walker(node,
300 : : checkExprHasSubLink_walker,
301 : : NULL,
302 : : QTW_IGNORE_RC_SUBQUERIES);
303 : : }
304 : :
305 : : static bool
306 : 179470 : checkExprHasSubLink_walker(Node *node, void *context)
307 : : {
308 [ + + ]: 179470 : if (node == NULL)
309 : 3368 : return false;
310 [ + + ]: 176102 : if (IsA(node, SubLink))
311 : 1292 : return true; /* abort the tree traversal and return true */
312 : 174810 : 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 : 161365 : contains_multiexpr_param(Node *node, void *context)
323 : : {
324 [ + + ]: 161365 : if (node == NULL)
325 : 2578 : return false;
326 [ + + ]: 158787 : if (IsA(node, Param))
327 : : {
328 [ - + ]: 401 : if (((Param *) node)->paramkind == PARAM_MULTIEXPR)
329 : 0 : return true; /* abort the tree traversal and return true */
330 : 401 : return false;
331 : : }
332 : 158386 : 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 : 42568 : CombineRangeTables(List **dst_rtable, List **dst_perminfos,
349 : : List *src_rtable, List *src_perminfos)
350 : : {
351 : : ListCell *l;
352 : 42568 : int offset = list_length(*dst_perminfos);
353 : :
354 [ + + ]: 42568 : if (offset > 0)
355 : : {
356 [ + + + + : 102574 : foreach(l, src_rtable)
+ + ]
357 : : {
358 : 66913 : RangeTblEntry *rte = lfirst_node(RangeTblEntry, l);
359 : :
360 [ + + ]: 66913 : if (rte->perminfoindex > 0)
361 : 33477 : rte->perminfoindex += offset;
362 : : }
363 : : }
364 : :
365 : 42568 : *dst_perminfos = list_concat(*dst_perminfos, src_perminfos);
366 : 42568 : *dst_rtable = list_concat(*dst_rtable, src_rtable);
367 : 42568 : }
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 : 1958258 : OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context)
390 : : {
391 [ + + ]: 1958258 : if (node == NULL)
392 : 666146 : return false;
393 [ + + ]: 1292112 : if (IsA(node, Var))
394 : : {
395 : 653735 : Var *var = (Var *) node;
396 : :
397 [ + + ]: 653735 : if (var->varlevelsup == context->sublevels_up)
398 : : {
399 : 571132 : var->varno += context->offset;
400 : 571132 : var->varnullingrels = bms_offset_members(var->varnullingrels,
401 : : context->offset);
402 [ + - ]: 571132 : if (var->varnosyn > 0)
403 : 571132 : var->varnosyn += context->offset;
404 : : }
405 : 653735 : return false;
406 : : }
407 [ - + ]: 638377 : 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 [ + + ]: 638377 : if (IsA(node, RangeTblRef))
416 : : {
417 : 56439 : RangeTblRef *rtr = (RangeTblRef *) node;
418 : :
419 [ + + ]: 56439 : if (context->sublevels_up == 0)
420 : 51155 : rtr->rtindex += context->offset;
421 : : /* the subquery itself is visited separately */
422 : 56439 : return false;
423 : : }
424 [ + + ]: 581938 : if (IsA(node, JoinExpr))
425 : : {
426 : 11063 : JoinExpr *j = (JoinExpr *) node;
427 : :
428 [ + + + + ]: 11063 : if (j->rtindex && context->sublevels_up == 0)
429 : 9984 : j->rtindex += context->offset;
430 : : /* fall through to examine children */
431 : : }
432 [ + + ]: 581938 : if (IsA(node, PlaceHolderVar))
433 : : {
434 : 419 : PlaceHolderVar *phv = (PlaceHolderVar *) node;
435 : :
436 [ + + ]: 419 : if (phv->phlevelsup == context->sublevels_up)
437 : : {
438 : 329 : phv->phrels = bms_offset_members(phv->phrels,
439 : : context->offset);
440 : 329 : phv->phnullingrels = bms_offset_members(phv->phnullingrels,
441 : : context->offset);
442 : : }
443 : : /* fall through to examine children */
444 : : }
445 [ + + ]: 581938 : if (IsA(node, AppendRelInfo))
446 : : {
447 : 799 : AppendRelInfo *appinfo = (AppendRelInfo *) node;
448 : :
449 [ + - ]: 799 : if (context->sublevels_up == 0)
450 : : {
451 : 799 : appinfo->parent_relid += context->offset;
452 : 799 : 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 [ + + ]: 581938 : if (IsA(node, Query))
463 : : {
464 : : /* Recurse into subselects */
465 : : bool result;
466 : :
467 : 4327 : context->sublevels_up++;
468 : 4327 : result = query_tree_walker((Query *) node, OffsetVarNodes_walker,
469 : : context, 0);
470 : 4327 : context->sublevels_up--;
471 : 4327 : return result;
472 : : }
473 : 577611 : return expression_tree_walker(node, OffsetVarNodes_walker, context);
474 : : }
475 : :
476 : : void
477 : 77128 : OffsetVarNodes(Node *node, int offset, int sublevels_up)
478 : : {
479 : : OffsetVarNodes_context context;
480 : :
481 : 77128 : context.offset = offset;
482 : 77128 : 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 [ + + + + ]: 77128 : if (node && IsA(node, Query))
490 : 38564 : {
491 : 38564 : 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 [ + - ]: 38564 : if (sublevels_up == 0)
502 : : {
503 : : ListCell *l;
504 : :
505 [ + + ]: 38564 : if (qry->resultRelation)
506 : 928 : qry->resultRelation += offset;
507 : :
508 [ - + ]: 38564 : if (qry->mergeTargetRelation)
509 : 0 : qry->mergeTargetRelation += offset;
510 : :
511 [ + + + + ]: 38564 : if (qry->onConflict && qry->onConflict->exclRelIndex)
512 : 44 : qry->onConflict->exclRelIndex += offset;
513 : :
514 [ + + + + : 38672 : foreach(l, qry->rowMarks)
+ + ]
515 : : {
516 : 108 : RowMarkClause *rc = (RowMarkClause *) lfirst(l);
517 : :
518 : 108 : rc->rti += offset;
519 : : }
520 : : }
521 : 38564 : query_tree_walker(qry, OffsetVarNodes_walker, &context, 0);
522 : : }
523 : : else
524 : 38564 : OffsetVarNodes_walker(node, &context);
525 : 77128 : }
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.
540 : : *
541 : : * NOTE: although this has the form of a walker, we cheat and modify the
542 : : * nodes in-place. The given expression tree should have been copied
543 : : * earlier to ensure that no unwanted side-effects occur!
544 : : */
545 : :
546 : : typedef struct
547 : : {
548 : : int rt_index;
549 : : int new_index;
550 : : int sublevels_up;
551 : : } ChangeVarNodes_context;
552 : :
553 : : static bool
554 : 1753699 : ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
555 : : {
556 [ + + ]: 1753699 : if (node == NULL)
557 : 515109 : return false;
558 [ + + ]: 1238590 : if (IsA(node, Var))
559 : : {
560 : 362291 : Var *var = (Var *) node;
561 : :
562 [ + + ]: 362291 : if (var->varlevelsup == context->sublevels_up)
563 : : {
564 [ + + ]: 353855 : if (var->varno == context->rt_index)
565 : : {
566 : : Assert(context->new_index != INVALID_VAR);
567 : 45385 : var->varno = context->new_index;
568 : : }
569 : 353855 : var->varnullingrels = adjust_relid_set(var->varnullingrels,
570 : : context->rt_index,
571 : : context->new_index);
572 [ + + ]: 353855 : if (var->varnosyn == context->rt_index)
573 : : {
574 : : Assert(context->new_index != INVALID_VAR);
575 : 45385 : var->varnosyn = context->new_index;
576 : : }
577 : : }
578 : 362291 : return false;
579 : : }
580 [ - + ]: 876299 : if (IsA(node, CurrentOfExpr))
581 : : {
582 : 0 : CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
583 : :
584 [ # # ]: 0 : if (context->sublevels_up == 0 &&
585 [ # # ]: 0 : cexpr->cvarno == context->rt_index)
586 : : {
587 : : Assert(context->new_index != INVALID_VAR);
588 : 0 : cexpr->cvarno = context->new_index;
589 : : }
590 : 0 : return false;
591 : : }
592 [ + + ]: 876299 : if (IsA(node, RangeTblRef))
593 : : {
594 : 36544 : RangeTblRef *rtr = (RangeTblRef *) node;
595 : :
596 [ + + ]: 36544 : if (context->sublevels_up == 0 &&
597 [ + + ]: 34411 : rtr->rtindex == context->rt_index)
598 : : {
599 : : Assert(context->new_index != INVALID_VAR);
600 : 1214 : rtr->rtindex = context->new_index;
601 : : }
602 : : /* the subquery itself is visited separately */
603 : 36544 : return false;
604 : : }
605 [ + + ]: 839755 : if (IsA(node, JoinExpr))
606 : : {
607 : 13922 : JoinExpr *j = (JoinExpr *) node;
608 : :
609 [ + + ]: 13922 : if (context->sublevels_up == 0 &&
610 [ - + ]: 13864 : j->rtindex == context->rt_index)
611 : : {
612 : : Assert(context->new_index != INVALID_VAR);
613 : 0 : j->rtindex = context->new_index;
614 : : }
615 : : /* fall through to examine children */
616 : : }
617 [ + + ]: 839755 : if (IsA(node, PlaceHolderVar))
618 : : {
619 : 618 : PlaceHolderVar *phv = (PlaceHolderVar *) node;
620 : :
621 [ + - ]: 618 : if (phv->phlevelsup == context->sublevels_up)
622 : : {
623 : 618 : phv->phrels = adjust_relid_set(phv->phrels,
624 : : context->rt_index,
625 : : context->new_index);
626 : 618 : phv->phnullingrels = adjust_relid_set(phv->phnullingrels,
627 : : context->rt_index,
628 : : context->new_index);
629 : : }
630 : : /* fall through to examine children */
631 : : }
632 [ - + ]: 839755 : if (IsA(node, PlanRowMark))
633 : : {
634 : 0 : PlanRowMark *rowmark = (PlanRowMark *) node;
635 : :
636 [ # # ]: 0 : if (context->sublevels_up == 0)
637 : : {
638 [ # # ]: 0 : if (rowmark->rti == context->rt_index)
639 : : {
640 : : Assert(context->new_index != INVALID_VAR);
641 : 0 : rowmark->rti = context->new_index;
642 : : }
643 [ # # ]: 0 : if (rowmark->prti == context->rt_index)
644 : : {
645 : : Assert(context->new_index != INVALID_VAR);
646 : 0 : rowmark->prti = context->new_index;
647 : : }
648 : : }
649 : 0 : return false;
650 : : }
651 [ - + ]: 839755 : if (IsA(node, AppendRelInfo))
652 : : {
653 : 0 : AppendRelInfo *appinfo = (AppendRelInfo *) node;
654 : :
655 [ # # ]: 0 : if (context->sublevels_up == 0)
656 : : {
657 [ # # ]: 0 : if (appinfo->parent_relid == context->rt_index)
658 : : {
659 : : Assert(context->new_index != INVALID_VAR);
660 : 0 : appinfo->parent_relid = context->new_index;
661 : : }
662 [ # # ]: 0 : if (appinfo->child_relid == context->rt_index)
663 : : {
664 : : Assert(context->new_index != INVALID_VAR);
665 : 0 : appinfo->child_relid = context->new_index;
666 : : }
667 : : }
668 : : /* fall through to examine children */
669 : : }
670 : : /* Shouldn't need to handle other planner auxiliary nodes here */
671 : : Assert(!IsA(node, SpecialJoinInfo));
672 : : Assert(!IsA(node, PlaceHolderInfo));
673 : : Assert(!IsA(node, MinMaxAggInfo));
674 : :
675 [ + + ]: 839755 : if (IsA(node, Query))
676 : : {
677 : : /* Recurse into subselects */
678 : : bool result;
679 : :
680 : 2781 : context->sublevels_up++;
681 : 2781 : result = query_tree_walker((Query *) node, ChangeVarNodes_walker,
682 : : context, 0);
683 : 2781 : context->sublevels_up--;
684 : 2781 : return result;
685 : : }
686 : 836974 : return expression_tree_walker(node, ChangeVarNodes_walker, context);
687 : : }
688 : :
689 : : void
690 : 69776 : ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up)
691 : : {
692 : : ChangeVarNodes_context context;
693 : :
694 : 69776 : context.rt_index = rt_index;
695 : 69776 : context.new_index = new_index;
696 : 69776 : context.sublevels_up = sublevels_up;
697 : :
698 : : /*
699 : : * Must be prepared to start with a Query or a bare expression tree; if
700 : : * it's a Query, go straight to query_tree_walker to make sure that
701 : : * sublevels_up doesn't get incremented prematurely.
702 : : */
703 [ + + + + ]: 69776 : if (node && IsA(node, Query))
704 : 20907 : {
705 : 20907 : Query *qry = (Query *) node;
706 : :
707 : : /*
708 : : * If we are starting at a Query, and sublevels_up is zero, then we
709 : : * must also fix rangetable indexes in the Query itself --- namely
710 : : * resultRelation, mergeTargetRelation, exclRelIndex and rowMarks
711 : : * entries. sublevels_up cannot be zero when recursing into a
712 : : * subquery, so there's no need to have the same logic inside
713 : : * ChangeVarNodes_walker.
714 : : */
715 [ + - ]: 20907 : if (sublevels_up == 0)
716 : : {
717 : : ListCell *l;
718 : :
719 [ + + ]: 20907 : if (qry->resultRelation == rt_index)
720 : : {
721 : : Assert(new_index != INVALID_VAR);
722 : 2254 : qry->resultRelation = new_index;
723 : : }
724 : :
725 [ + + ]: 20907 : if (qry->mergeTargetRelation == rt_index)
726 : : {
727 : : Assert(new_index != INVALID_VAR);
728 : 568 : qry->mergeTargetRelation = new_index;
729 : : }
730 : :
731 : : /* this is unlikely to ever be used, but ... */
732 [ + + - + ]: 20907 : if (qry->onConflict && qry->onConflict->exclRelIndex == rt_index)
733 : : {
734 : : Assert(new_index != INVALID_VAR);
735 : 0 : qry->onConflict->exclRelIndex = new_index;
736 : : }
737 : :
738 [ + + + + : 21005 : foreach(l, qry->rowMarks)
+ + ]
739 : : {
740 : 98 : RowMarkClause *rc = (RowMarkClause *) lfirst(l);
741 : :
742 [ + + ]: 98 : if (rc->rti == rt_index)
743 : : {
744 : : Assert(new_index != INVALID_VAR);
745 : 36 : rc->rti = new_index;
746 : : }
747 : : }
748 : : }
749 : 20907 : query_tree_walker(qry, ChangeVarNodes_walker, &context, 0);
750 : : }
751 : : else
752 : 48869 : ChangeVarNodes_walker(node, &context);
753 : 69776 : }
754 : :
755 : : /*
756 : : * adjust_relid_set - substitute newrelid for oldrelid in a Relid set
757 : : *
758 : : * Attempt to remove oldrelid from a Relid set (as long as it's not a special
759 : : * varno). If oldrelid was found and removed, insert newrelid into a Relid
760 : : * set (as long as it's not a special varno). Therefore, when oldrelid is
761 : : * a special varno, this function does nothing. When newrelid is a special
762 : : * varno, this function behaves as delete.
763 : : */
764 : : static Relids
765 : 355091 : adjust_relid_set(Relids relids, int oldrelid, int newrelid)
766 : : {
767 [ + - + + ]: 355091 : if (!IS_SPECIAL_VARNO(oldrelid) && bms_is_member(oldrelid, relids))
768 : : {
769 : : /* Ensure we have a modifiable copy */
770 : 385 : relids = bms_copy(relids);
771 : : /* Remove old, add new */
772 : 385 : relids = bms_del_member(relids, oldrelid);
773 [ + + ]: 385 : if (!IS_SPECIAL_VARNO(newrelid))
774 : 25 : relids = bms_add_member(relids, newrelid);
775 : : }
776 : 355091 : return relids;
777 : : }
778 : :
779 : : /*
780 : : * IncrementVarSublevelsUp - adjust Var nodes when pushing them down in tree
781 : : *
782 : : * Find all Var nodes in the given tree having varlevelsup >= min_sublevels_up,
783 : : * and add delta_sublevels_up to their varlevelsup value. This is needed when
784 : : * an expression that's correct for some nesting level is inserted into a
785 : : * subquery. Ordinarily the initial call has min_sublevels_up == 0 so that
786 : : * all Vars are affected. The point of min_sublevels_up is that we can
787 : : * increment it when we recurse into a sublink, so that local variables in
788 : : * that sublink are not affected, only outer references to vars that belong
789 : : * to the expression's original query level or parents thereof.
790 : : *
791 : : * Likewise for other nodes containing levelsup fields, such as Aggref.
792 : : *
793 : : * NOTE: although this has the form of a walker, we cheat and modify the
794 : : * Var nodes in-place. The given expression tree should have been copied
795 : : * earlier to ensure that no unwanted side-effects occur!
796 : : */
797 : :
798 : : typedef struct
799 : : {
800 : : int delta_sublevels_up;
801 : : int min_sublevels_up;
802 : : } IncrementVarSublevelsUp_context;
803 : :
804 : : static bool
805 : 2664068 : IncrementVarSublevelsUp_walker(Node *node,
806 : : IncrementVarSublevelsUp_context *context)
807 : : {
808 [ + + ]: 2664068 : if (node == NULL)
809 : 876059 : return false;
810 [ + + ]: 1788009 : if (IsA(node, Var))
811 : : {
812 : 847761 : Var *var = (Var *) node;
813 : :
814 [ + + ]: 847761 : if (var->varlevelsup >= context->min_sublevels_up)
815 : 16602 : var->varlevelsup += context->delta_sublevels_up;
816 : 847761 : return false; /* done here */
817 : : }
818 [ - + ]: 940248 : if (IsA(node, CurrentOfExpr))
819 : : {
820 : : /* this should not happen */
821 [ # # ]: 0 : if (context->min_sublevels_up == 0)
822 [ # # ]: 0 : elog(ERROR, "cannot push down CurrentOfExpr");
823 : 0 : return false;
824 : : }
825 [ + + ]: 940248 : if (IsA(node, Aggref))
826 : : {
827 : 2280 : Aggref *agg = (Aggref *) node;
828 : :
829 [ + + ]: 2280 : if (agg->agglevelsup >= context->min_sublevels_up)
830 : 77 : agg->agglevelsup += context->delta_sublevels_up;
831 : : /* fall through to recurse into argument */
832 : : }
833 [ + + ]: 940248 : if (IsA(node, GroupingFunc))
834 : : {
835 : 57 : GroupingFunc *grp = (GroupingFunc *) node;
836 : :
837 [ + - ]: 57 : if (grp->agglevelsup >= context->min_sublevels_up)
838 : 57 : grp->agglevelsup += context->delta_sublevels_up;
839 : : /* fall through to recurse into argument */
840 : : }
841 [ + + ]: 940248 : if (IsA(node, PlaceHolderVar))
842 : : {
843 : 838 : PlaceHolderVar *phv = (PlaceHolderVar *) node;
844 : :
845 [ + + ]: 838 : if (phv->phlevelsup >= context->min_sublevels_up)
846 : 509 : phv->phlevelsup += context->delta_sublevels_up;
847 : : /* fall through to recurse into argument */
848 : : }
849 [ + + ]: 940248 : if (IsA(node, ReturningExpr))
850 : : {
851 : 108 : ReturningExpr *rexpr = (ReturningExpr *) node;
852 : :
853 [ + - ]: 108 : if (rexpr->retlevelsup >= context->min_sublevels_up)
854 : 108 : rexpr->retlevelsup += context->delta_sublevels_up;
855 : : /* fall through to recurse into argument */
856 : : }
857 [ + + ]: 940248 : if (IsA(node, RangeTblEntry))
858 : : {
859 : 103875 : RangeTblEntry *rte = (RangeTblEntry *) node;
860 : :
861 [ + + ]: 103875 : if (rte->rtekind == RTE_CTE)
862 : : {
863 [ + + ]: 4105 : if (rte->ctelevelsup >= context->min_sublevels_up)
864 : 4080 : rte->ctelevelsup += context->delta_sublevels_up;
865 : : }
866 : 103875 : return false; /* allow range_table_walker to continue */
867 : : }
868 [ + + ]: 836373 : if (IsA(node, Query))
869 : : {
870 : : /* Recurse into subselects */
871 : : bool result;
872 : :
873 : 17042 : context->min_sublevels_up++;
874 : 17042 : result = query_tree_walker((Query *) node,
875 : : IncrementVarSublevelsUp_walker,
876 : : context,
877 : : QTW_EXAMINE_RTES_BEFORE);
878 : 17042 : context->min_sublevels_up--;
879 : 17042 : return result;
880 : : }
881 : 819331 : return expression_tree_walker(node, IncrementVarSublevelsUp_walker, context);
882 : : }
883 : :
884 : : void
885 : 81172 : IncrementVarSublevelsUp(Node *node, int delta_sublevels_up,
886 : : int min_sublevels_up)
887 : : {
888 : : IncrementVarSublevelsUp_context context;
889 : :
890 : 81172 : context.delta_sublevels_up = delta_sublevels_up;
891 : 81172 : context.min_sublevels_up = min_sublevels_up;
892 : :
893 : : /*
894 : : * Must be prepared to start with a Query or a bare expression tree; if
895 : : * it's a Query, we don't want to increment sublevels_up.
896 : : */
897 : 81172 : query_or_expression_tree_walker(node,
898 : : IncrementVarSublevelsUp_walker,
899 : : &context,
900 : : QTW_EXAMINE_RTES_BEFORE);
901 : 81172 : }
902 : :
903 : : /*
904 : : * IncrementVarSublevelsUp_rtable -
905 : : * Same as IncrementVarSublevelsUp, but to be invoked on a range table.
906 : : */
907 : : void
908 : 4030 : IncrementVarSublevelsUp_rtable(List *rtable, int delta_sublevels_up,
909 : : int min_sublevels_up)
910 : : {
911 : : IncrementVarSublevelsUp_context context;
912 : :
913 : 4030 : context.delta_sublevels_up = delta_sublevels_up;
914 : 4030 : context.min_sublevels_up = min_sublevels_up;
915 : :
916 : 4030 : range_table_walker(rtable,
917 : : IncrementVarSublevelsUp_walker,
918 : : &context,
919 : : QTW_EXAMINE_RTES_BEFORE);
920 : 4030 : }
921 : :
922 : : /*
923 : : * SetVarReturningType - adjust Var nodes for a specified varreturningtype.
924 : : *
925 : : * Find all Var nodes referring to the specified result relation in the given
926 : : * expression and set their varreturningtype to the specified value.
927 : : *
928 : : * NOTE: although this has the form of a walker, we cheat and modify the
929 : : * Var nodes in-place. The given expression tree should have been copied
930 : : * earlier to ensure that no unwanted side-effects occur!
931 : : */
932 : :
933 : : typedef struct
934 : : {
935 : : int result_relation;
936 : : int sublevels_up;
937 : : VarReturningType returning_type;
938 : : } SetVarReturningType_context;
939 : :
940 : : static bool
941 : 1542 : SetVarReturningType_walker(Node *node, SetVarReturningType_context *context)
942 : : {
943 [ + + ]: 1542 : if (node == NULL)
944 : 416 : return false;
945 [ + + ]: 1126 : if (IsA(node, Var))
946 : : {
947 : 698 : Var *var = (Var *) node;
948 : :
949 [ + + ]: 698 : if (var->varno == context->result_relation &&
950 [ + - ]: 658 : var->varlevelsup == context->sublevels_up)
951 : 658 : var->varreturningtype = context->returning_type;
952 : :
953 : 698 : return false;
954 : : }
955 : :
956 [ + + ]: 428 : if (IsA(node, Query))
957 : : {
958 : : /* Recurse into subselects */
959 : : bool result;
960 : :
961 : 32 : context->sublevels_up++;
962 : 32 : result = query_tree_walker((Query *) node, SetVarReturningType_walker,
963 : : context, 0);
964 : 32 : context->sublevels_up--;
965 : 32 : return result;
966 : : }
967 : 396 : return expression_tree_walker(node, SetVarReturningType_walker, context);
968 : : }
969 : :
970 : : static void
971 : 826 : SetVarReturningType(Node *node, int result_relation, int sublevels_up,
972 : : VarReturningType returning_type)
973 : : {
974 : : SetVarReturningType_context context;
975 : :
976 : 826 : context.result_relation = result_relation;
977 : 826 : context.sublevels_up = sublevels_up;
978 : 826 : context.returning_type = returning_type;
979 : :
980 : : /* Expect to start with an expression */
981 : 826 : SetVarReturningType_walker(node, &context);
982 : 826 : }
983 : :
984 : : /*
985 : : * rangeTableEntry_used - detect whether an RTE is referenced somewhere
986 : : * in var nodes or join or setOp trees of a query or expression.
987 : : */
988 : :
989 : : typedef struct
990 : : {
991 : : int rt_index;
992 : : int sublevels_up;
993 : : } rangeTableEntry_used_context;
994 : :
995 : : static bool
996 : 2769459 : rangeTableEntry_used_walker(Node *node,
997 : : rangeTableEntry_used_context *context)
998 : : {
999 [ + + ]: 2769459 : if (node == NULL)
1000 : 571006 : return false;
1001 [ + + ]: 2198453 : if (IsA(node, Var))
1002 : : {
1003 : 630733 : Var *var = (Var *) node;
1004 : :
1005 [ + + ]: 630733 : if (var->varlevelsup == context->sublevels_up &&
1006 [ + + - + ]: 1001777 : (var->varno == context->rt_index ||
1007 : 397067 : bms_is_member(context->rt_index, var->varnullingrels)))
1008 : 207643 : return true;
1009 : 423090 : return false;
1010 : : }
1011 [ - + ]: 1567720 : if (IsA(node, CurrentOfExpr))
1012 : : {
1013 : 0 : CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
1014 : :
1015 [ # # ]: 0 : if (context->sublevels_up == 0 &&
1016 [ # # ]: 0 : cexpr->cvarno == context->rt_index)
1017 : 0 : return true;
1018 : 0 : return false;
1019 : : }
1020 [ + + ]: 1567720 : if (IsA(node, RangeTblRef))
1021 : : {
1022 : 96230 : RangeTblRef *rtr = (RangeTblRef *) node;
1023 : :
1024 [ + + ]: 96230 : if (rtr->rtindex == context->rt_index &&
1025 [ + + ]: 51056 : context->sublevels_up == 0)
1026 : 49367 : return true;
1027 : : /* the subquery itself is visited separately */
1028 : 46863 : return false;
1029 : : }
1030 [ + + ]: 1471490 : if (IsA(node, JoinExpr))
1031 : : {
1032 : 32288 : JoinExpr *j = (JoinExpr *) node;
1033 : :
1034 [ + + ]: 32288 : if (j->rtindex == context->rt_index &&
1035 [ - + ]: 52 : context->sublevels_up == 0)
1036 : 0 : return true;
1037 : : /* fall through to examine children */
1038 : : }
1039 : : /* Shouldn't need to handle planner auxiliary nodes here */
1040 : : Assert(!IsA(node, PlaceHolderVar));
1041 : : Assert(!IsA(node, PlanRowMark));
1042 : : Assert(!IsA(node, SpecialJoinInfo));
1043 : : Assert(!IsA(node, AppendRelInfo));
1044 : : Assert(!IsA(node, PlaceHolderInfo));
1045 : : Assert(!IsA(node, MinMaxAggInfo));
1046 : :
1047 [ + + ]: 1471490 : if (IsA(node, Query))
1048 : : {
1049 : : /* Recurse into subselects */
1050 : : bool result;
1051 : :
1052 : 9769 : context->sublevels_up++;
1053 : 9769 : result = query_tree_walker((Query *) node, rangeTableEntry_used_walker,
1054 : : context, 0);
1055 : 9769 : context->sublevels_up--;
1056 : 9769 : return result;
1057 : : }
1058 : 1461721 : return expression_tree_walker(node, rangeTableEntry_used_walker, context);
1059 : : }
1060 : :
1061 : : bool
1062 : 268113 : rangeTableEntry_used(Node *node, int rt_index, int sublevels_up)
1063 : : {
1064 : : rangeTableEntry_used_context context;
1065 : :
1066 : 268113 : context.rt_index = rt_index;
1067 : 268113 : context.sublevels_up = sublevels_up;
1068 : :
1069 : : /*
1070 : : * Must be prepared to start with a Query or a bare expression tree; if
1071 : : * it's a Query, we don't want to increment sublevels_up.
1072 : : */
1073 : 268113 : return query_or_expression_tree_walker(node,
1074 : : rangeTableEntry_used_walker,
1075 : : &context,
1076 : : 0);
1077 : : }
1078 : :
1079 : :
1080 : : /*
1081 : : * If the given Query is an INSERT ... SELECT construct, extract and
1082 : : * return the sub-Query node that represents the SELECT part. Otherwise
1083 : : * return the given Query.
1084 : : *
1085 : : * If subquery_ptr is not NULL, then *subquery_ptr is set to the location
1086 : : * of the link to the SELECT subquery inside parsetree, or NULL if not an
1087 : : * INSERT ... SELECT.
1088 : : *
1089 : : * This is a hack needed because transformations on INSERT ... SELECTs that
1090 : : * appear in rule actions should be applied to the source SELECT, not to the
1091 : : * INSERT part. Perhaps this can be cleaned up with redesigned querytrees.
1092 : : */
1093 : : Query *
1094 : 2477 : getInsertSelectQuery(Query *parsetree, Query ***subquery_ptr)
1095 : : {
1096 : : Query *selectquery;
1097 : : RangeTblEntry *selectrte;
1098 : : RangeTblRef *rtr;
1099 : :
1100 [ + + ]: 2477 : if (subquery_ptr)
1101 : 1004 : *subquery_ptr = NULL;
1102 : :
1103 [ - + ]: 2477 : if (parsetree == NULL)
1104 : 0 : return parsetree;
1105 [ + + ]: 2477 : if (parsetree->commandType != CMD_INSERT)
1106 : 1103 : return parsetree;
1107 : :
1108 : : /*
1109 : : * Currently, this is ONLY applied to rule-action queries, and so we
1110 : : * expect to find the OLD and NEW placeholder entries in the given query.
1111 : : * If they're not there, it must be an INSERT/SELECT in which they've been
1112 : : * pushed down to the SELECT.
1113 : : */
1114 [ + - ]: 1374 : if (list_length(parsetree->rtable) >= 2 &&
1115 [ + + ]: 1374 : strcmp(rt_fetch(PRS2_OLD_VARNO, parsetree->rtable)->eref->aliasname,
1116 : 1258 : "old") == 0 &&
1117 [ + - ]: 1258 : strcmp(rt_fetch(PRS2_NEW_VARNO, parsetree->rtable)->eref->aliasname,
1118 : : "new") == 0)
1119 : 1258 : return parsetree;
1120 : : Assert(parsetree->jointree && IsA(parsetree->jointree, FromExpr));
1121 [ - + ]: 116 : if (list_length(parsetree->jointree->fromlist) != 1)
1122 [ # # ]: 0 : elog(ERROR, "expected to find SELECT subquery");
1123 : 116 : rtr = (RangeTblRef *) linitial(parsetree->jointree->fromlist);
1124 [ - + ]: 116 : if (!IsA(rtr, RangeTblRef))
1125 [ # # ]: 0 : elog(ERROR, "expected to find SELECT subquery");
1126 : 116 : selectrte = rt_fetch(rtr->rtindex, parsetree->rtable);
1127 [ + - ]: 116 : if (!(selectrte->rtekind == RTE_SUBQUERY &&
1128 [ + - ]: 116 : selectrte->subquery &&
1129 [ + - ]: 116 : IsA(selectrte->subquery, Query) &&
1130 [ - + ]: 116 : selectrte->subquery->commandType == CMD_SELECT))
1131 [ # # ]: 0 : elog(ERROR, "expected to find SELECT subquery");
1132 : 116 : selectquery = selectrte->subquery;
1133 [ + - ]: 116 : if (list_length(selectquery->rtable) >= 2 &&
1134 [ + - ]: 116 : strcmp(rt_fetch(PRS2_OLD_VARNO, selectquery->rtable)->eref->aliasname,
1135 : 116 : "old") == 0 &&
1136 [ + - ]: 116 : strcmp(rt_fetch(PRS2_NEW_VARNO, selectquery->rtable)->eref->aliasname,
1137 : : "new") == 0)
1138 : : {
1139 [ + + ]: 116 : if (subquery_ptr)
1140 : 40 : *subquery_ptr = &(selectrte->subquery);
1141 : 116 : return selectquery;
1142 : : }
1143 [ # # ]: 0 : elog(ERROR, "could not find rule placeholders");
1144 : : return NULL; /* not reached */
1145 : : }
1146 : :
1147 : :
1148 : : /*
1149 : : * Add the given qualifier condition to the query's WHERE clause
1150 : : */
1151 : : void
1152 : 3715 : AddQual(Query *parsetree, Node *qual)
1153 : : {
1154 : : Node *copy;
1155 : :
1156 [ + + ]: 3715 : if (qual == NULL)
1157 : 1300 : return;
1158 : :
1159 [ - + ]: 2415 : if (parsetree->commandType == CMD_UTILITY)
1160 : : {
1161 : : /*
1162 : : * There's noplace to put the qual on a utility statement.
1163 : : *
1164 : : * If it's a NOTIFY, silently ignore the qual; this means that the
1165 : : * NOTIFY will execute, whether or not there are any qualifying rows.
1166 : : * While clearly wrong, this is much more useful than refusing to
1167 : : * execute the rule at all, and extra NOTIFY events are harmless for
1168 : : * typical uses of NOTIFY.
1169 : : *
1170 : : * If it isn't a NOTIFY, error out, since unconditional execution of
1171 : : * other utility stmts is unlikely to be wanted. (This case is not
1172 : : * currently allowed anyway, but keep the test for safety.)
1173 : : */
1174 [ # # # # ]: 0 : if (parsetree->utilityStmt && IsA(parsetree->utilityStmt, NotifyStmt))
1175 : 0 : return;
1176 : : else
1177 [ # # ]: 0 : ereport(ERROR,
1178 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1179 : : errmsg("conditional utility statements are not implemented")));
1180 : : }
1181 : :
1182 [ - + ]: 2415 : if (parsetree->setOperations != NULL)
1183 : : {
1184 : : /*
1185 : : * There's noplace to put the qual on a setop statement, either. (This
1186 : : * could be fixed, but right now the planner simply ignores any qual
1187 : : * condition on a setop query.)
1188 : : */
1189 [ # # ]: 0 : ereport(ERROR,
1190 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1191 : : errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
1192 : : }
1193 : :
1194 : : /* INTERSECT wants the original, but we need to copy - Jan */
1195 : 2415 : copy = copyObject(qual);
1196 : :
1197 : 2415 : parsetree->jointree->quals = make_and_qual(parsetree->jointree->quals,
1198 : : copy);
1199 : :
1200 : : /*
1201 : : * We had better not have stuck an aggregate into the WHERE clause.
1202 : : */
1203 : : Assert(!contain_aggs_of_level(copy, 0));
1204 : :
1205 : : /*
1206 : : * Make sure query is marked correctly if added qual has sublinks. Need
1207 : : * not search qual when query is already marked.
1208 : : */
1209 [ + + ]: 2415 : if (!parsetree->hasSubLinks)
1210 : 2387 : parsetree->hasSubLinks = checkExprHasSubLink(copy);
1211 : : }
1212 : :
1213 : :
1214 : : /*
1215 : : * Invert the given clause and add it to the WHERE qualifications of the
1216 : : * given querytree. Inversion means "x IS NOT TRUE", not just "NOT x",
1217 : : * else we will do the wrong thing when x evaluates to NULL.
1218 : : */
1219 : : void
1220 : 304 : AddInvertedQual(Query *parsetree, Node *qual)
1221 : : {
1222 : : BooleanTest *invqual;
1223 : :
1224 [ - + ]: 304 : if (qual == NULL)
1225 : 0 : return;
1226 : :
1227 : : /* Need not copy input qual, because AddQual will... */
1228 : 304 : invqual = makeNode(BooleanTest);
1229 : 304 : invqual->arg = (Expr *) qual;
1230 : 304 : invqual->booltesttype = IS_NOT_TRUE;
1231 : 304 : invqual->location = -1;
1232 : :
1233 : 304 : AddQual(parsetree, (Node *) invqual);
1234 : : }
1235 : :
1236 : :
1237 : : /*
1238 : : * add_nulling_relids() finds Vars and PlaceHolderVars that belong to any
1239 : : * of the target_relids, and adds added_relids to their varnullingrels
1240 : : * and phnullingrels fields. If target_relids is NULL, all level-zero
1241 : : * Vars and PHVs are modified.
1242 : : */
1243 : : Node *
1244 : 5680 : add_nulling_relids(Node *node,
1245 : : const Bitmapset *target_relids,
1246 : : const Bitmapset *added_relids)
1247 : : {
1248 : : add_nulling_relids_context context;
1249 : :
1250 : 5680 : context.target_relids = target_relids;
1251 : 5680 : context.added_relids = added_relids;
1252 : 5680 : context.sublevels_up = 0;
1253 : 5680 : return query_or_expression_tree_mutator(node,
1254 : : add_nulling_relids_mutator,
1255 : : &context,
1256 : : 0);
1257 : : }
1258 : :
1259 : : static Node *
1260 : 23329 : add_nulling_relids_mutator(Node *node,
1261 : : add_nulling_relids_context *context)
1262 : : {
1263 [ + + ]: 23329 : if (node == NULL)
1264 : 950 : return NULL;
1265 [ + + ]: 22379 : if (IsA(node, Var))
1266 : : {
1267 : 8337 : Var *var = (Var *) node;
1268 : :
1269 [ + + ]: 8337 : if (var->varlevelsup == context->sublevels_up &&
1270 [ + + + + ]: 16494 : (context->target_relids == NULL ||
1271 : 8162 : bms_is_member(var->varno, context->target_relids)))
1272 : : {
1273 : 4706 : Relids newnullingrels = bms_union(var->varnullingrels,
1274 : : context->added_relids);
1275 : :
1276 : : /* Copy the Var ... */
1277 : 4706 : var = copyObject(var);
1278 : : /* ... and replace the copy's varnullingrels field */
1279 : 4706 : var->varnullingrels = newnullingrels;
1280 : 4706 : return (Node *) var;
1281 : : }
1282 : : /* Otherwise fall through to copy the Var normally */
1283 : : }
1284 [ + + ]: 14042 : else if (IsA(node, PlaceHolderVar))
1285 : : {
1286 : 927 : PlaceHolderVar *phv = (PlaceHolderVar *) node;
1287 : :
1288 [ + - ]: 927 : if (phv->phlevelsup == context->sublevels_up &&
1289 [ + - + - ]: 1854 : (context->target_relids == NULL ||
1290 : 927 : bms_overlap(phv->phrels, context->target_relids)))
1291 : : {
1292 : 927 : Relids newnullingrels = bms_union(phv->phnullingrels,
1293 : : context->added_relids);
1294 : :
1295 : : /*
1296 : : * We don't modify the contents of the PHV's expression, only add
1297 : : * to phnullingrels. This corresponds to assuming that the PHV
1298 : : * will be evaluated at the same level as before, then perhaps be
1299 : : * nulled as it bubbles up. Hence, just flat-copy the node ...
1300 : : */
1301 : 927 : phv = makeNode(PlaceHolderVar);
1302 : 927 : memcpy(phv, node, sizeof(PlaceHolderVar));
1303 : : /* ... and replace the copy's phnullingrels field */
1304 : 927 : phv->phnullingrels = newnullingrels;
1305 : 927 : return (Node *) phv;
1306 : : }
1307 : : /* Otherwise fall through to copy the PlaceHolderVar normally */
1308 : : }
1309 [ + + ]: 13115 : else if (IsA(node, Query))
1310 : : {
1311 : : /* Recurse into RTE or sublink subquery */
1312 : : Query *newnode;
1313 : :
1314 : 40 : context->sublevels_up++;
1315 : 40 : newnode = query_tree_mutator((Query *) node,
1316 : : add_nulling_relids_mutator,
1317 : : context,
1318 : : 0);
1319 : 40 : context->sublevels_up--;
1320 : 40 : return (Node *) newnode;
1321 : : }
1322 : 16706 : return expression_tree_mutator(node, add_nulling_relids_mutator, context);
1323 : : }
1324 : :
1325 : : /*
1326 : : * remove_nulling_relids() removes mentions of the specified RT index(es)
1327 : : * in Var.varnullingrels and PlaceHolderVar.phnullingrels fields within
1328 : : * the given expression, except in nodes belonging to rels listed in
1329 : : * except_relids.
1330 : : */
1331 : : Node *
1332 : 303232 : remove_nulling_relids(Node *node,
1333 : : const Bitmapset *removable_relids,
1334 : : const Bitmapset *except_relids)
1335 : : {
1336 : : remove_nulling_relids_context context;
1337 : :
1338 : 303232 : context.removable_relids = removable_relids;
1339 : 303232 : context.except_relids = except_relids;
1340 : 303232 : context.sublevels_up = 0;
1341 : 303232 : return query_or_expression_tree_mutator(node,
1342 : : remove_nulling_relids_mutator,
1343 : : &context,
1344 : : 0);
1345 : : }
1346 : :
1347 : : static Node *
1348 : 734894 : remove_nulling_relids_mutator(Node *node,
1349 : : remove_nulling_relids_context *context)
1350 : : {
1351 [ + + ]: 734894 : if (node == NULL)
1352 : 91010 : return NULL;
1353 [ + + ]: 643884 : if (IsA(node, Var))
1354 : : {
1355 : 376000 : Var *var = (Var *) node;
1356 : :
1357 [ + + ]: 376000 : if (var->varlevelsup == context->sublevels_up &&
1358 [ + + + + ]: 736720 : !bms_is_member(var->varno, context->except_relids) &&
1359 : 368106 : bms_overlap(var->varnullingrels, context->removable_relids))
1360 : : {
1361 : : /* Copy the Var ... */
1362 : 13030 : var = copyObject(var);
1363 : : /* ... and replace the copy's varnullingrels field */
1364 : 13030 : var->varnullingrels = bms_difference(var->varnullingrels,
1365 : : context->removable_relids);
1366 : 13030 : return (Node *) var;
1367 : : }
1368 : : /* Otherwise fall through to copy the Var normally */
1369 : : }
1370 [ + + ]: 267884 : else if (IsA(node, PlaceHolderVar))
1371 : : {
1372 : 4176 : PlaceHolderVar *phv = (PlaceHolderVar *) node;
1373 : :
1374 [ + - ]: 4176 : if (phv->phlevelsup == context->sublevels_up &&
1375 [ + - ]: 4176 : !bms_overlap(phv->phrels, context->except_relids))
1376 : : {
1377 : : /*
1378 : : * Note: it might seem desirable to remove the PHV altogether if
1379 : : * phnullingrels goes to empty. Currently we dare not do that
1380 : : * because we use PHVs in some cases to enforce separate identity
1381 : : * of subexpressions; see wrap_option usages in prepjointree.c.
1382 : : */
1383 : : /* Copy the PlaceHolderVar and mutate what's below ... */
1384 : : phv = (PlaceHolderVar *)
1385 : 4176 : expression_tree_mutator(node,
1386 : : remove_nulling_relids_mutator,
1387 : : context);
1388 : : /* ... and replace the copy's phnullingrels field */
1389 : 4176 : phv->phnullingrels = bms_difference(phv->phnullingrels,
1390 : : context->removable_relids);
1391 : : /* We must also update phrels, if it contains a removable RTI */
1392 : 4176 : phv->phrels = bms_difference(phv->phrels,
1393 : : context->removable_relids);
1394 : : Assert(!bms_is_empty(phv->phrels));
1395 : 4176 : return (Node *) phv;
1396 : : }
1397 : : /* Otherwise fall through to copy the PlaceHolderVar normally */
1398 : : }
1399 [ + + ]: 263708 : else if (IsA(node, Query))
1400 : : {
1401 : : /* Recurse into RTE or sublink subquery */
1402 : : Query *newnode;
1403 : :
1404 : 811 : context->sublevels_up++;
1405 : 811 : newnode = query_tree_mutator((Query *) node,
1406 : : remove_nulling_relids_mutator,
1407 : : context,
1408 : : 0);
1409 : 811 : context->sublevels_up--;
1410 : 811 : return (Node *) newnode;
1411 : : }
1412 : 625867 : return expression_tree_mutator(node, remove_nulling_relids_mutator, context);
1413 : : }
1414 : :
1415 : :
1416 : : /*
1417 : : * replace_rte_variables() finds all Vars in an expression tree
1418 : : * that reference a particular RTE, and replaces them with substitute
1419 : : * expressions obtained from a caller-supplied callback function.
1420 : : *
1421 : : * When invoking replace_rte_variables on a portion of a Query, pass the
1422 : : * address of the containing Query's hasSubLinks field as outer_hasSubLinks.
1423 : : * Otherwise, pass NULL, but inserting a SubLink into a non-Query expression
1424 : : * will then cause an error.
1425 : : *
1426 : : * Note: the business with inserted_sublink is needed to update hasSubLinks
1427 : : * in subqueries when the replacement adds a subquery inside a subquery.
1428 : : * Messy, isn't it? We do not need to do similar pushups for hasAggs,
1429 : : * because it isn't possible for this transformation to insert a level-zero
1430 : : * aggregate reference into a subquery --- it could only insert outer aggs.
1431 : : * Likewise for hasWindowFuncs.
1432 : : *
1433 : : * Note: usually, we'd not expose the mutator function or context struct
1434 : : * for a function like this. We do so because callbacks often find it
1435 : : * convenient to recurse directly to the mutator on sub-expressions of
1436 : : * what they will return.
1437 : : */
1438 : : Node *
1439 : 174929 : replace_rte_variables(Node *node, int target_varno, int sublevels_up,
1440 : : replace_rte_variables_callback callback,
1441 : : void *callback_arg,
1442 : : bool *outer_hasSubLinks)
1443 : : {
1444 : : Node *result;
1445 : : replace_rte_variables_context context;
1446 : :
1447 : 174929 : context.callback = callback;
1448 : 174929 : context.callback_arg = callback_arg;
1449 : 174929 : context.target_varno = target_varno;
1450 : 174929 : context.sublevels_up = sublevels_up;
1451 : :
1452 : : /*
1453 : : * We try to initialize inserted_sublink to true if there is no need to
1454 : : * detect new sublinks because the query already has some.
1455 : : */
1456 [ + + + + ]: 174929 : if (node && IsA(node, Query))
1457 : 4644 : context.inserted_sublink = ((Query *) node)->hasSubLinks;
1458 [ + + ]: 170285 : else if (outer_hasSubLinks)
1459 : 169986 : context.inserted_sublink = *outer_hasSubLinks;
1460 : : else
1461 : 299 : context.inserted_sublink = false;
1462 : :
1463 : : /*
1464 : : * Must be prepared to start with a Query or a bare expression tree; if
1465 : : * it's a Query, we don't want to increment sublevels_up.
1466 : : */
1467 : 174929 : result = query_or_expression_tree_mutator(node,
1468 : : replace_rte_variables_mutator,
1469 : : &context,
1470 : : 0);
1471 : :
1472 [ + + ]: 174917 : if (context.inserted_sublink)
1473 : : {
1474 [ + + + + ]: 19276 : if (result && IsA(result, Query))
1475 : 173 : ((Query *) result)->hasSubLinks = true;
1476 [ + - ]: 19103 : else if (outer_hasSubLinks)
1477 : 19103 : *outer_hasSubLinks = true;
1478 : : else
1479 [ # # ]: 0 : elog(ERROR, "replace_rte_variables inserted a SubLink, but has noplace to record it");
1480 : : }
1481 : :
1482 : 174917 : return result;
1483 : : }
1484 : :
1485 : : Node *
1486 : 786176 : replace_rte_variables_mutator(Node *node,
1487 : : replace_rte_variables_context *context)
1488 : : {
1489 [ + + ]: 786176 : if (node == NULL)
1490 : 236478 : return NULL;
1491 [ + + ]: 549698 : if (IsA(node, Var))
1492 : : {
1493 : 219931 : Var *var = (Var *) node;
1494 : :
1495 [ + + ]: 219931 : if (var->varno == context->target_varno &&
1496 [ + + ]: 113938 : var->varlevelsup == context->sublevels_up)
1497 : : {
1498 : : /* Found a matching variable, make the substitution */
1499 : : Node *newnode;
1500 : :
1501 : 107523 : newnode = context->callback(var, context);
1502 : : /* Detect if we are adding a sublink to query */
1503 [ + + ]: 107511 : if (!context->inserted_sublink)
1504 : 97378 : context->inserted_sublink = checkExprHasSubLink(newnode);
1505 : 107511 : return newnode;
1506 : : }
1507 : : /* otherwise fall through to copy the var normally */
1508 : : }
1509 [ + + ]: 329767 : else if (IsA(node, Query))
1510 : : {
1511 : : /* Recurse into RTE subquery or not-yet-planned sublink subquery */
1512 : : Query *newnode;
1513 : : bool save_inserted_sublink;
1514 : :
1515 : 2389 : context->sublevels_up++;
1516 : 2389 : save_inserted_sublink = context->inserted_sublink;
1517 : 2389 : context->inserted_sublink = ((Query *) node)->hasSubLinks;
1518 : 2389 : newnode = query_tree_mutator((Query *) node,
1519 : : replace_rte_variables_mutator,
1520 : : context,
1521 : : 0);
1522 : 2389 : newnode->hasSubLinks |= context->inserted_sublink;
1523 : 2389 : context->inserted_sublink = save_inserted_sublink;
1524 : 2389 : context->sublevels_up--;
1525 : 2389 : return (Node *) newnode;
1526 : : }
1527 : 439786 : return expression_tree_mutator(node, replace_rte_variables_mutator, context);
1528 : : }
1529 : :
1530 : :
1531 : : /*
1532 : : * map_variable_attnos() finds all user-column Vars in an expression tree
1533 : : * that reference a particular RTE, and adjusts their varattnos according
1534 : : * to the given mapping array (varattno n is replaced by attno_map[n-1]).
1535 : : * Vars for system columns are not modified.
1536 : : *
1537 : : * A zero in the mapping array represents a dropped column, which should not
1538 : : * appear in the expression.
1539 : : *
1540 : : * If the expression tree contains a whole-row Var for the target RTE,
1541 : : * *found_whole_row is set to true. In addition, if to_rowtype is
1542 : : * not InvalidOid, we replace the Var with a Var of that vartype, inserting
1543 : : * a ConvertRowtypeExpr to map back to the rowtype expected by the expression.
1544 : : * (Therefore, to_rowtype had better be a child rowtype of the rowtype of the
1545 : : * RTE we're changing references to.) Callers that don't provide to_rowtype
1546 : : * should report an error if *found_whole_row is true; we don't do that here
1547 : : * because we don't know exactly what wording for the error message would
1548 : : * be most appropriate. The caller will be aware of the context.
1549 : : *
1550 : : * This could be built using replace_rte_variables and a callback function,
1551 : : * but since we don't ever need to insert sublinks, replace_rte_variables is
1552 : : * overly complicated.
1553 : : */
1554 : :
1555 : : typedef struct
1556 : : {
1557 : : int target_varno; /* RTE index to search for */
1558 : : int sublevels_up; /* (current) nesting depth */
1559 : : const AttrMap *attno_map; /* map array for user attnos */
1560 : : Oid to_rowtype; /* change whole-row Vars to this type */
1561 : : bool *found_whole_row; /* output flag */
1562 : : } map_variable_attnos_context;
1563 : :
1564 : : static Node *
1565 : 74153 : map_variable_attnos_mutator(Node *node,
1566 : : map_variable_attnos_context *context)
1567 : : {
1568 [ + + ]: 74153 : if (node == NULL)
1569 : 112 : return NULL;
1570 [ + + ]: 74041 : if (IsA(node, Var))
1571 : : {
1572 : 17506 : Var *var = (Var *) node;
1573 : :
1574 [ + + ]: 17506 : if (var->varno == context->target_varno &&
1575 [ + - ]: 17346 : var->varlevelsup == context->sublevels_up)
1576 : : {
1577 : : /* Found a matching variable, make the substitution */
1578 : 17346 : Var *newvar = palloc_object(Var);
1579 : 17346 : int attno = var->varattno;
1580 : :
1581 : 17346 : *newvar = *var; /* initially copy all fields of the Var */
1582 : :
1583 [ + + ]: 17346 : if (attno > 0)
1584 : : {
1585 : : /* user-defined column, replace attno */
1586 [ + - ]: 17118 : if (attno > context->attno_map->maplen ||
1587 [ - + ]: 17118 : context->attno_map->attnums[attno - 1] == 0)
1588 [ # # ]: 0 : elog(ERROR, "unexpected varattno %d in expression to be mapped",
1589 : : attno);
1590 : 17118 : newvar->varattno = context->attno_map->attnums[attno - 1];
1591 : : /* If the syntactic referent is same RTE, fix it too */
1592 [ + + ]: 17118 : if (newvar->varnosyn == context->target_varno)
1593 : 17058 : newvar->varattnosyn = newvar->varattno;
1594 : : }
1595 [ + + ]: 228 : else if (attno == 0)
1596 : : {
1597 : : /* whole-row variable, warn caller */
1598 : 40 : *(context->found_whole_row) = true;
1599 : :
1600 : : /* If the caller expects us to convert the Var, do so. */
1601 [ + + ]: 40 : if (OidIsValid(context->to_rowtype) &&
1602 [ + - ]: 36 : context->to_rowtype != var->vartype)
1603 : : {
1604 : : ConvertRowtypeExpr *r;
1605 : :
1606 : : /* This certainly won't work for a RECORD variable. */
1607 : : Assert(var->vartype != RECORDOID);
1608 : :
1609 : : /* Var itself is changed to the requested type. */
1610 : 36 : newvar->vartype = context->to_rowtype;
1611 : :
1612 : : /*
1613 : : * Add a conversion node on top to convert back to the
1614 : : * original type expected by the expression.
1615 : : */
1616 : 36 : r = makeNode(ConvertRowtypeExpr);
1617 : 36 : r->arg = (Expr *) newvar;
1618 : 36 : r->resulttype = var->vartype;
1619 : 36 : r->convertformat = COERCE_IMPLICIT_CAST;
1620 : 36 : r->location = -1;
1621 : :
1622 : 36 : return (Node *) r;
1623 : : }
1624 : : }
1625 : 17310 : return (Node *) newvar;
1626 : : }
1627 : : /* otherwise fall through to copy the var normally */
1628 : : }
1629 [ + + ]: 56535 : else if (IsA(node, ConvertRowtypeExpr))
1630 : : {
1631 : 36 : ConvertRowtypeExpr *r = (ConvertRowtypeExpr *) node;
1632 : 36 : Var *var = (Var *) r->arg;
1633 : :
1634 : : /*
1635 : : * If this is coercing a whole-row Var that we need to convert, then
1636 : : * just convert the Var without adding an extra ConvertRowtypeExpr.
1637 : : * Effectively we're simplifying var::parenttype::grandparenttype into
1638 : : * just var::grandparenttype. This avoids building stacks of CREs if
1639 : : * this function is applied repeatedly.
1640 : : */
1641 [ + + ]: 36 : if (IsA(var, Var) &&
1642 [ + + ]: 28 : var->varno == context->target_varno &&
1643 [ + - ]: 24 : var->varlevelsup == context->sublevels_up &&
1644 [ + - ]: 24 : var->varattno == 0 &&
1645 [ + - ]: 24 : OidIsValid(context->to_rowtype) &&
1646 [ + - ]: 24 : context->to_rowtype != var->vartype)
1647 : : {
1648 : : ConvertRowtypeExpr *newnode;
1649 : 24 : Var *newvar = palloc_object(Var);
1650 : :
1651 : : /* whole-row variable, warn caller */
1652 : 24 : *(context->found_whole_row) = true;
1653 : :
1654 : 24 : *newvar = *var; /* initially copy all fields of the Var */
1655 : :
1656 : : /* This certainly won't work for a RECORD variable. */
1657 : : Assert(var->vartype != RECORDOID);
1658 : :
1659 : : /* Var itself is changed to the requested type. */
1660 : 24 : newvar->vartype = context->to_rowtype;
1661 : :
1662 : 24 : newnode = palloc_object(ConvertRowtypeExpr);
1663 : 24 : *newnode = *r; /* initially copy all fields of the CRE */
1664 : 24 : newnode->arg = (Expr *) newvar;
1665 : :
1666 : 24 : return (Node *) newnode;
1667 : : }
1668 : : /* otherwise fall through to process the expression normally */
1669 : : }
1670 [ - + ]: 56499 : else if (IsA(node, Query))
1671 : : {
1672 : : /* Recurse into RTE subquery or not-yet-planned sublink subquery */
1673 : : Query *newnode;
1674 : :
1675 : 0 : context->sublevels_up++;
1676 : 0 : newnode = query_tree_mutator((Query *) node,
1677 : : map_variable_attnos_mutator,
1678 : : context,
1679 : : 0);
1680 : 0 : context->sublevels_up--;
1681 : 0 : return (Node *) newnode;
1682 : : }
1683 : 56671 : return expression_tree_mutator(node, map_variable_attnos_mutator, context);
1684 : : }
1685 : :
1686 : : Node *
1687 : 6314 : map_variable_attnos(Node *node,
1688 : : int target_varno, int sublevels_up,
1689 : : const AttrMap *attno_map,
1690 : : Oid to_rowtype, bool *found_whole_row)
1691 : : {
1692 : : map_variable_attnos_context context;
1693 : :
1694 : 6314 : context.target_varno = target_varno;
1695 : 6314 : context.sublevels_up = sublevels_up;
1696 : 6314 : context.attno_map = attno_map;
1697 : 6314 : context.to_rowtype = to_rowtype;
1698 : 6314 : context.found_whole_row = found_whole_row;
1699 : :
1700 : 6314 : *found_whole_row = false;
1701 : :
1702 : : /*
1703 : : * Must be prepared to start with a Query or a bare expression tree; if
1704 : : * it's a Query, we don't want to increment sublevels_up.
1705 : : */
1706 : 6314 : return query_or_expression_tree_mutator(node,
1707 : : map_variable_attnos_mutator,
1708 : : &context,
1709 : : 0);
1710 : : }
1711 : :
1712 : :
1713 : : /*
1714 : : * ReplaceVarsFromTargetList - replace Vars with items from a targetlist
1715 : : *
1716 : : * Vars matching target_varno and sublevels_up are replaced by the
1717 : : * entry with matching resno from targetlist, if there is one.
1718 : : *
1719 : : * If there is no matching resno for such a Var, the action depends on the
1720 : : * nomatch_option:
1721 : : * REPLACEVARS_REPORT_ERROR: throw an error
1722 : : * REPLACEVARS_CHANGE_VARNO: change Var's varno to nomatch_varno
1723 : : * REPLACEVARS_SUBSTITUTE_NULL: replace Var with a NULL Const of same type
1724 : : *
1725 : : * The caller must also provide target_rte, the RTE describing the target
1726 : : * relation. This is needed to handle whole-row Vars referencing the target.
1727 : : * We expand such Vars into RowExpr constructs.
1728 : : *
1729 : : * In addition, for INSERT/UPDATE/DELETE/MERGE queries, the caller must
1730 : : * provide result_relation, the index of the result relation in the rewritten
1731 : : * query. This is needed to handle OLD/NEW RETURNING list Vars referencing
1732 : : * target_varno. When such Vars are expanded, their varreturningtype is
1733 : : * copied onto any replacement Vars referencing result_relation. In addition,
1734 : : * if the replacement expression from the targetlist is not simply a Var
1735 : : * referencing result_relation, it is wrapped in a ReturningExpr node (causing
1736 : : * the executor to return NULL if the OLD/NEW row doesn't exist).
1737 : : *
1738 : : * Note that ReplaceVarFromTargetList always generates the replacement
1739 : : * expression with varlevelsup = 0. The caller is responsible for adjusting
1740 : : * the varlevelsup if needed. This simplifies the caller's life if it wants to
1741 : : * cache the replacement expressions.
1742 : : *
1743 : : * outer_hasSubLinks works the same as for replace_rte_variables().
1744 : : */
1745 : :
1746 : : typedef struct
1747 : : {
1748 : : RangeTblEntry *target_rte;
1749 : : List *targetlist;
1750 : : int result_relation;
1751 : : ReplaceVarsNoMatchOption nomatch_option;
1752 : : int nomatch_varno;
1753 : : } ReplaceVarsFromTargetList_context;
1754 : :
1755 : : static Node *
1756 : 9787 : ReplaceVarsFromTargetList_callback(const Var *var,
1757 : : replace_rte_variables_context *context)
1758 : : {
1759 : 9787 : ReplaceVarsFromTargetList_context *rcon = (ReplaceVarsFromTargetList_context *) context->callback_arg;
1760 : : Node *newnode;
1761 : :
1762 : 9787 : newnode = ReplaceVarFromTargetList(var,
1763 : : rcon->target_rte,
1764 : : rcon->targetlist,
1765 : : rcon->result_relation,
1766 : : rcon->nomatch_option,
1767 : : rcon->nomatch_varno);
1768 : :
1769 : : /* Must adjust varlevelsup if replaced Var is within a subquery */
1770 [ + + ]: 9775 : if (var->varlevelsup > 0)
1771 : 172 : IncrementVarSublevelsUp(newnode, var->varlevelsup, 0);
1772 : :
1773 : 9775 : return newnode;
1774 : : }
1775 : :
1776 : : Node *
1777 : 106590 : ReplaceVarFromTargetList(const Var *var,
1778 : : RangeTblEntry *target_rte,
1779 : : List *targetlist,
1780 : : int result_relation,
1781 : : ReplaceVarsNoMatchOption nomatch_option,
1782 : : int nomatch_varno)
1783 : : {
1784 : : TargetEntry *tle;
1785 : :
1786 [ + + ]: 106590 : if (var->varattno == InvalidAttrNumber)
1787 : : {
1788 : : /* Must expand whole-tuple reference into RowExpr */
1789 : : RowExpr *rowexpr;
1790 : : List *colnames;
1791 : : List *fields;
1792 : : ListCell *lc;
1793 : :
1794 : : /*
1795 : : * If generating an expansion for a var of a named rowtype (ie, this
1796 : : * is a plain relation RTE), then we must include dummy items for
1797 : : * dropped columns. If the var is RECORD (ie, this is a JOIN), then
1798 : : * omit dropped columns. In the latter case, attach column names to
1799 : : * the RowExpr for use of the executor and ruleutils.c.
1800 : : *
1801 : : * In order to be able to cache the results, we always generate the
1802 : : * expansion with varlevelsup = 0. The caller is responsible for
1803 : : * adjusting it if needed.
1804 : : *
1805 : : * The varreturningtype is copied onto each individual field Var, so
1806 : : * that it is handled correctly when we recurse.
1807 : : */
1808 : 614 : expandRTE(target_rte,
1809 : 614 : var->varno, 0 /* not varlevelsup */ ,
1810 : 614 : var->varreturningtype, var->location,
1811 : 614 : (var->vartype != RECORDOID),
1812 : : &colnames, &fields);
1813 : 614 : rowexpr = makeNode(RowExpr);
1814 : : /* the fields will be set below */
1815 : 614 : rowexpr->args = NIL;
1816 : 614 : rowexpr->row_typeid = var->vartype;
1817 : 614 : rowexpr->row_format = COERCE_IMPLICIT_CAST;
1818 [ + + ]: 614 : rowexpr->colnames = (var->vartype == RECORDOID) ? colnames : NIL;
1819 : 614 : rowexpr->location = var->location;
1820 : : /* Adjust the generated per-field Vars... */
1821 [ + - + + : 2256 : foreach(lc, fields)
+ + ]
1822 : : {
1823 : 1642 : Node *field = lfirst(lc);
1824 : :
1825 [ + - + - ]: 1642 : if (field && IsA(field, Var))
1826 : 1642 : field = ReplaceVarFromTargetList((Var *) field,
1827 : : target_rte,
1828 : : targetlist,
1829 : : result_relation,
1830 : : nomatch_option,
1831 : : nomatch_varno);
1832 : 1642 : rowexpr->args = lappend(rowexpr->args, field);
1833 : : }
1834 : :
1835 : : /* Wrap it in a ReturningExpr, if needed, per comments above */
1836 [ + + ]: 614 : if (var->varreturningtype != VAR_RETURNING_DEFAULT)
1837 : : {
1838 : 68 : ReturningExpr *rexpr = makeNode(ReturningExpr);
1839 : :
1840 : 68 : rexpr->retlevelsup = 0;
1841 : 68 : rexpr->retold = (var->varreturningtype == VAR_RETURNING_OLD);
1842 : 68 : rexpr->retexpr = (Expr *) rowexpr;
1843 : :
1844 : 68 : return (Node *) rexpr;
1845 : : }
1846 : :
1847 : 546 : return (Node *) rowexpr;
1848 : : }
1849 : :
1850 : : /* Normal case referencing one targetlist element */
1851 : 105976 : tle = get_tle_by_resno(targetlist, var->varattno);
1852 : :
1853 [ + + - + ]: 105976 : if (tle == NULL || tle->resjunk)
1854 : : {
1855 : : /* Failed to find column in targetlist */
1856 [ + + + - ]: 390 : switch (nomatch_option)
1857 : : {
1858 : 12 : case REPLACEVARS_REPORT_ERROR:
1859 : :
1860 : : /*
1861 : : * A system column can never match a targetlist entry, since
1862 : : * those all have positive resnos, so throw a suitable
1863 : : * user-facing error.
1864 : : */
1865 [ + - ]: 12 : if (var->varattno < 0)
1866 [ + - ]: 12 : ereport(ERROR,
1867 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1868 : : errmsg("cannot use system column \"%s\" in RETURNING list of a query that is rewritten by a rule",
1869 : : get_rte_attribute_name(target_rte,
1870 : : var->varattno))));
1871 : :
1872 : : /* fall through, throw error below */
1873 : 0 : break;
1874 : :
1875 : 278 : case REPLACEVARS_CHANGE_VARNO:
1876 : : {
1877 : 278 : Var *newvar = copyObject(var);
1878 : :
1879 : 278 : newvar->varno = nomatch_varno;
1880 : 278 : newvar->varlevelsup = 0;
1881 : : /* we leave the syntactic referent alone */
1882 : 278 : return (Node *) newvar;
1883 : : }
1884 : :
1885 : 100 : case REPLACEVARS_SUBSTITUTE_NULL:
1886 : : {
1887 : : /*
1888 : : * If Var is of domain type, we must add a CoerceToDomain
1889 : : * node, in case there is a NOT NULL domain constraint.
1890 : : */
1891 : : int16 vartyplen;
1892 : : bool vartypbyval;
1893 : :
1894 : 100 : get_typlenbyval(var->vartype, &vartyplen, &vartypbyval);
1895 : 100 : return coerce_null_to_domain(var->vartype,
1896 : 100 : var->vartypmod,
1897 : 100 : var->varcollid,
1898 : : vartyplen,
1899 : : vartypbyval);
1900 : : }
1901 : : }
1902 [ # # ]: 0 : elog(ERROR, "could not find replacement targetlist entry for attno %d",
1903 : : var->varattno);
1904 : : return NULL; /* keep compiler quiet */
1905 : : }
1906 : : else
1907 : : {
1908 : : /* Make a copy of the tlist item to return */
1909 : 105586 : Expr *newnode = copyObject(tle->expr);
1910 : :
1911 : : /*
1912 : : * Check to see if the tlist item contains a PARAM_MULTIEXPR Param,
1913 : : * and throw error if so. This case could only happen when expanding
1914 : : * an ON UPDATE rule's NEW variable and the referenced tlist item in
1915 : : * the original UPDATE command is part of a multiple assignment. There
1916 : : * seems no practical way to handle such cases without multiple
1917 : : * evaluation of the multiple assignment's sub-select, which would
1918 : : * create semantic oddities that users of rules would probably prefer
1919 : : * not to cope with. So treat it as an unimplemented feature.
1920 : : */
1921 [ - + ]: 105586 : if (contains_multiexpr_param((Node *) newnode, NULL))
1922 [ # # ]: 0 : ereport(ERROR,
1923 : : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1924 : : errmsg("NEW variables in ON UPDATE rules cannot reference columns that are part of a multiple assignment in the subject UPDATE command")));
1925 : :
1926 : : /* Handle any OLD/NEW RETURNING list Vars */
1927 [ + + ]: 105586 : if (var->varreturningtype != VAR_RETURNING_DEFAULT)
1928 : : {
1929 : : /*
1930 : : * Copy varreturningtype onto any Vars in the tlist item that
1931 : : * refer to result_relation (which had better be non-zero).
1932 : : */
1933 [ - + ]: 826 : if (result_relation == 0)
1934 [ # # ]: 0 : elog(ERROR, "variable returning old/new found outside RETURNING list");
1935 : :
1936 : 826 : SetVarReturningType((Node *) newnode, result_relation,
1937 : 826 : 0, var->varreturningtype);
1938 : :
1939 : : /* Wrap it in a ReturningExpr, if needed, per comments above */
1940 [ + + ]: 826 : if (!IsA(newnode, Var) ||
1941 [ + + ]: 620 : ((Var *) newnode)->varno != result_relation ||
1942 [ - + ]: 580 : ((Var *) newnode)->varlevelsup != 0)
1943 : : {
1944 : 246 : ReturningExpr *rexpr = makeNode(ReturningExpr);
1945 : :
1946 : 246 : rexpr->retlevelsup = 0;
1947 : 246 : rexpr->retold = (var->varreturningtype == VAR_RETURNING_OLD);
1948 : 246 : rexpr->retexpr = newnode;
1949 : :
1950 : 246 : newnode = (Expr *) rexpr;
1951 : : }
1952 : : }
1953 : :
1954 : 105586 : return (Node *) newnode;
1955 : : }
1956 : : }
1957 : :
1958 : : Node *
1959 : 8178 : ReplaceVarsFromTargetList(Node *node,
1960 : : int target_varno, int sublevels_up,
1961 : : RangeTblEntry *target_rte,
1962 : : List *targetlist,
1963 : : int result_relation,
1964 : : ReplaceVarsNoMatchOption nomatch_option,
1965 : : int nomatch_varno,
1966 : : bool *outer_hasSubLinks)
1967 : : {
1968 : : ReplaceVarsFromTargetList_context context;
1969 : :
1970 : 8178 : context.target_rte = target_rte;
1971 : 8178 : context.targetlist = targetlist;
1972 : 8178 : context.result_relation = result_relation;
1973 : 8178 : context.nomatch_option = nomatch_option;
1974 : 8178 : context.nomatch_varno = nomatch_varno;
1975 : :
1976 : 8178 : return replace_rte_variables(node, target_varno, sublevels_up,
1977 : : ReplaceVarsFromTargetList_callback,
1978 : : &context,
1979 : : outer_hasSubLinks);
1980 : : }
|