Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * rewriteManip.c
4 : *
5 : * Portions Copyright (c) 1996-2017, 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 "catalog/pg_type.h"
17 : #include "nodes/makefuncs.h"
18 : #include "nodes/nodeFuncs.h"
19 : #include "nodes/plannodes.h"
20 : #include "optimizer/clauses.h"
21 : #include "parser/parse_coerce.h"
22 : #include "parser/parse_relation.h"
23 : #include "parser/parsetree.h"
24 : #include "rewrite/rewriteManip.h"
25 :
26 :
27 : typedef struct
28 : {
29 : int sublevels_up;
30 : } contain_aggs_of_level_context;
31 :
32 : typedef struct
33 : {
34 : int agg_location;
35 : int sublevels_up;
36 : } locate_agg_of_level_context;
37 :
38 : typedef struct
39 : {
40 : int win_location;
41 : } locate_windowfunc_context;
42 :
43 : static bool contain_aggs_of_level_walker(Node *node,
44 : contain_aggs_of_level_context *context);
45 : static bool locate_agg_of_level_walker(Node *node,
46 : locate_agg_of_level_context *context);
47 : static bool contain_windowfuncs_walker(Node *node, void *context);
48 : static bool locate_windowfunc_walker(Node *node,
49 : locate_windowfunc_context *context);
50 : static bool checkExprHasSubLink_walker(Node *node, void *context);
51 : static Relids offset_relid_set(Relids relids, int offset);
52 : static Relids adjust_relid_set(Relids relids, int oldrelid, int newrelid);
53 :
54 :
55 : /*
56 : * contain_aggs_of_level -
57 : * Check if an expression contains an aggregate function call of a
58 : * specified query level.
59 : *
60 : * The objective of this routine is to detect whether there are aggregates
61 : * belonging to the given query level. Aggregates belonging to subqueries
62 : * or outer queries do NOT cause a true result. We must recurse into
63 : * subqueries to detect outer-reference aggregates that logically belong to
64 : * the specified query level.
65 : */
66 : bool
67 298 : contain_aggs_of_level(Node *node, int levelsup)
68 : {
69 : contain_aggs_of_level_context context;
70 :
71 298 : context.sublevels_up = levelsup;
72 :
73 : /*
74 : * Must be prepared to start with a Query or a bare expression tree; if
75 : * it's a Query, we don't want to increment sublevels_up.
76 : */
77 298 : return query_or_expression_tree_walker(node,
78 : contain_aggs_of_level_walker,
79 : (void *) &context,
80 : 0);
81 : }
82 :
83 : static bool
84 1629 : contain_aggs_of_level_walker(Node *node,
85 : contain_aggs_of_level_context *context)
86 : {
87 1629 : if (node == NULL)
88 136 : return false;
89 1493 : if (IsA(node, Aggref))
90 : {
91 0 : if (((Aggref *) node)->agglevelsup == context->sublevels_up)
92 0 : return true; /* abort the tree traversal and return true */
93 : /* else fall through to examine argument */
94 : }
95 1493 : if (IsA(node, GroupingFunc))
96 : {
97 0 : if (((GroupingFunc *) node)->agglevelsup == context->sublevels_up)
98 0 : return true;
99 : /* else fall through to examine argument */
100 : }
101 1493 : if (IsA(node, Query))
102 : {
103 : /* Recurse into subselects */
104 : bool result;
105 :
106 12 : context->sublevels_up++;
107 12 : result = query_tree_walker((Query *) node,
108 : contain_aggs_of_level_walker,
109 : (void *) context, 0);
110 12 : context->sublevels_up--;
111 12 : return result;
112 : }
113 1481 : return expression_tree_walker(node, contain_aggs_of_level_walker,
114 : (void *) context);
115 : }
116 :
117 : /*
118 : * locate_agg_of_level -
119 : * Find the parse location of any aggregate of the specified query level.
120 : *
121 : * Returns -1 if no such agg is in the querytree, or if they all have
122 : * unknown parse location. (The former case is probably caller error,
123 : * but we don't bother to distinguish it from the latter case.)
124 : *
125 : * Note: it might seem appropriate to merge this functionality into
126 : * contain_aggs_of_level, but that would complicate that function's API.
127 : * Currently, the only uses of this function are for error reporting,
128 : * and so shaving cycles probably isn't very important.
129 : */
130 : int
131 5 : locate_agg_of_level(Node *node, int levelsup)
132 : {
133 : locate_agg_of_level_context context;
134 :
135 5 : context.agg_location = -1; /* in case we find nothing */
136 5 : context.sublevels_up = levelsup;
137 :
138 : /*
139 : * Must be prepared to start with a Query or a bare expression tree; if
140 : * it's a Query, we don't want to increment sublevels_up.
141 : */
142 5 : (void) query_or_expression_tree_walker(node,
143 : locate_agg_of_level_walker,
144 : (void *) &context,
145 : 0);
146 :
147 5 : return context.agg_location;
148 : }
149 :
150 : static bool
151 14 : locate_agg_of_level_walker(Node *node,
152 : locate_agg_of_level_context *context)
153 : {
154 14 : if (node == NULL)
155 0 : return false;
156 14 : if (IsA(node, Aggref))
157 : {
158 10 : if (((Aggref *) node)->agglevelsup == context->sublevels_up &&
159 5 : ((Aggref *) node)->location >= 0)
160 : {
161 5 : context->agg_location = ((Aggref *) node)->location;
162 5 : return true; /* abort the tree traversal and return true */
163 : }
164 : /* else fall through to examine argument */
165 : }
166 9 : if (IsA(node, GroupingFunc))
167 : {
168 0 : if (((GroupingFunc *) node)->agglevelsup == context->sublevels_up &&
169 0 : ((GroupingFunc *) node)->location >= 0)
170 : {
171 0 : context->agg_location = ((GroupingFunc *) node)->location;
172 0 : return true; /* abort the tree traversal and return true */
173 : }
174 : }
175 9 : if (IsA(node, Query))
176 : {
177 : /* Recurse into subselects */
178 : bool result;
179 :
180 0 : context->sublevels_up++;
181 0 : result = query_tree_walker((Query *) node,
182 : locate_agg_of_level_walker,
183 : (void *) context, 0);
184 0 : context->sublevels_up--;
185 0 : return result;
186 : }
187 9 : return expression_tree_walker(node, locate_agg_of_level_walker,
188 : (void *) context);
189 : }
190 :
191 : /*
192 : * contain_windowfuncs -
193 : * Check if an expression contains a window function call of the
194 : * current query level.
195 : */
196 : bool
197 280 : contain_windowfuncs(Node *node)
198 : {
199 : /*
200 : * Must be prepared to start with a Query or a bare expression tree; if
201 : * it's a Query, we don't want to increment sublevels_up.
202 : */
203 280 : return query_or_expression_tree_walker(node,
204 : contain_windowfuncs_walker,
205 : NULL,
206 : 0);
207 : }
208 :
209 : static bool
210 805 : contain_windowfuncs_walker(Node *node, void *context)
211 : {
212 805 : if (node == NULL)
213 8 : return false;
214 797 : if (IsA(node, WindowFunc))
215 2 : return true; /* abort the tree traversal and return true */
216 : /* Mustn't recurse into subselects */
217 795 : return expression_tree_walker(node, contain_windowfuncs_walker,
218 : (void *) context);
219 : }
220 :
221 : /*
222 : * locate_windowfunc -
223 : * Find the parse location of any windowfunc of the current query level.
224 : *
225 : * Returns -1 if no such windowfunc is in the querytree, or if they all have
226 : * unknown parse location. (The former case is probably caller error,
227 : * but we don't bother to distinguish it from the latter case.)
228 : *
229 : * Note: it might seem appropriate to merge this functionality into
230 : * contain_windowfuncs, but that would complicate that function's API.
231 : * Currently, the only uses of this function are for error reporting,
232 : * and so shaving cycles probably isn't very important.
233 : */
234 : int
235 1 : locate_windowfunc(Node *node)
236 : {
237 : locate_windowfunc_context context;
238 :
239 1 : context.win_location = -1; /* in case we find nothing */
240 :
241 : /*
242 : * Must be prepared to start with a Query or a bare expression tree; if
243 : * it's a Query, we don't want to increment sublevels_up.
244 : */
245 1 : (void) query_or_expression_tree_walker(node,
246 : locate_windowfunc_walker,
247 : (void *) &context,
248 : 0);
249 :
250 1 : return context.win_location;
251 : }
252 :
253 : static bool
254 1 : locate_windowfunc_walker(Node *node, locate_windowfunc_context *context)
255 : {
256 1 : if (node == NULL)
257 0 : return false;
258 1 : if (IsA(node, WindowFunc))
259 : {
260 1 : if (((WindowFunc *) node)->location >= 0)
261 : {
262 1 : context->win_location = ((WindowFunc *) node)->location;
263 1 : return true; /* abort the tree traversal and return true */
264 : }
265 : /* else fall through to examine argument */
266 : }
267 : /* Mustn't recurse into subselects */
268 0 : return expression_tree_walker(node, locate_windowfunc_walker,
269 : (void *) context);
270 : }
271 :
272 : /*
273 : * checkExprHasSubLink -
274 : * Check if an expression contains a SubLink.
275 : */
276 : bool
277 7012 : checkExprHasSubLink(Node *node)
278 : {
279 : /*
280 : * If a Query is passed, examine it --- but we should not recurse into
281 : * sub-Queries that are in its rangetable or CTE list.
282 : */
283 7012 : return query_or_expression_tree_walker(node,
284 : checkExprHasSubLink_walker,
285 : NULL,
286 : QTW_IGNORE_RC_SUBQUERIES);
287 : }
288 :
289 : static bool
290 13524 : checkExprHasSubLink_walker(Node *node, void *context)
291 : {
292 13524 : if (node == NULL)
293 339 : return false;
294 13185 : if (IsA(node, SubLink))
295 101 : return true; /* abort the tree traversal and return true */
296 13084 : return expression_tree_walker(node, checkExprHasSubLink_walker, context);
297 : }
298 :
299 : /*
300 : * Check for MULTIEXPR Param within expression tree
301 : *
302 : * We intentionally don't descend into SubLinks: only Params at the current
303 : * query level are of interest.
304 : */
305 : static bool
306 1137 : contains_multiexpr_param(Node *node, void *context)
307 : {
308 1137 : if (node == NULL)
309 3 : return false;
310 1134 : if (IsA(node, Param))
311 : {
312 0 : if (((Param *) node)->paramkind == PARAM_MULTIEXPR)
313 0 : return true; /* abort the tree traversal and return true */
314 0 : return false;
315 : }
316 1134 : return expression_tree_walker(node, contains_multiexpr_param, context);
317 : }
318 :
319 :
320 : /*
321 : * OffsetVarNodes - adjust Vars when appending one query's RT to another
322 : *
323 : * Find all Var nodes in the given tree with varlevelsup == sublevels_up,
324 : * and increment their varno fields (rangetable indexes) by 'offset'.
325 : * The varnoold fields are adjusted similarly. Also, adjust other nodes
326 : * that contain rangetable indexes, such as RangeTblRef and JoinExpr.
327 : *
328 : * NOTE: although this has the form of a walker, we cheat and modify the
329 : * nodes in-place. The given expression tree should have been copied
330 : * earlier to ensure that no unwanted side-effects occur!
331 : */
332 :
333 : typedef struct
334 : {
335 : int offset;
336 : int sublevels_up;
337 : } OffsetVarNodes_context;
338 :
339 : static bool
340 152099 : OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context)
341 : {
342 152099 : if (node == NULL)
343 47096 : return false;
344 105003 : if (IsA(node, Var))
345 : {
346 47660 : Var *var = (Var *) node;
347 :
348 47660 : if (var->varlevelsup == context->sublevels_up)
349 : {
350 41374 : var->varno += context->offset;
351 41374 : var->varnoold += context->offset;
352 : }
353 47660 : return false;
354 : }
355 57343 : if (IsA(node, CurrentOfExpr))
356 : {
357 0 : CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
358 :
359 0 : if (context->sublevels_up == 0)
360 0 : cexpr->cvarno += context->offset;
361 0 : return false;
362 : }
363 57343 : if (IsA(node, RangeTblRef))
364 : {
365 4424 : RangeTblRef *rtr = (RangeTblRef *) node;
366 :
367 4424 : if (context->sublevels_up == 0)
368 3519 : rtr->rtindex += context->offset;
369 : /* the subquery itself is visited separately */
370 4424 : return false;
371 : }
372 52919 : if (IsA(node, JoinExpr))
373 : {
374 897 : JoinExpr *j = (JoinExpr *) node;
375 :
376 897 : if (j->rtindex && context->sublevels_up == 0)
377 778 : j->rtindex += context->offset;
378 : /* fall through to examine children */
379 : }
380 52919 : if (IsA(node, PlaceHolderVar))
381 : {
382 21 : PlaceHolderVar *phv = (PlaceHolderVar *) node;
383 :
384 21 : if (phv->phlevelsup == context->sublevels_up)
385 : {
386 12 : phv->phrels = offset_relid_set(phv->phrels,
387 : context->offset);
388 : }
389 : /* fall through to examine children */
390 : }
391 52919 : if (IsA(node, AppendRelInfo))
392 : {
393 30 : AppendRelInfo *appinfo = (AppendRelInfo *) node;
394 :
395 30 : if (context->sublevels_up == 0)
396 : {
397 30 : appinfo->parent_relid += context->offset;
398 30 : appinfo->child_relid += context->offset;
399 : }
400 : /* fall through to examine children */
401 : }
402 : /* Shouldn't need to handle other planner auxiliary nodes here */
403 52919 : Assert(!IsA(node, PlanRowMark));
404 52919 : Assert(!IsA(node, SpecialJoinInfo));
405 52919 : Assert(!IsA(node, PlaceHolderInfo));
406 52919 : Assert(!IsA(node, MinMaxAggInfo));
407 :
408 52919 : if (IsA(node, Query))
409 : {
410 : /* Recurse into subselects */
411 : bool result;
412 :
413 683 : context->sublevels_up++;
414 683 : result = query_tree_walker((Query *) node, OffsetVarNodes_walker,
415 : (void *) context, 0);
416 683 : context->sublevels_up--;
417 683 : return result;
418 : }
419 52236 : return expression_tree_walker(node, OffsetVarNodes_walker,
420 : (void *) context);
421 : }
422 :
423 : void
424 4737 : OffsetVarNodes(Node *node, int offset, int sublevels_up)
425 : {
426 : OffsetVarNodes_context context;
427 :
428 4737 : context.offset = offset;
429 4737 : context.sublevels_up = sublevels_up;
430 :
431 : /*
432 : * Must be prepared to start with a Query or a bare expression tree; if
433 : * it's a Query, go straight to query_tree_walker to make sure that
434 : * sublevels_up doesn't get incremented prematurely.
435 : */
436 4737 : if (node && IsA(node, Query))
437 2583 : {
438 2583 : Query *qry = (Query *) node;
439 :
440 : /*
441 : * If we are starting at a Query, and sublevels_up is zero, then we
442 : * must also fix rangetable indexes in the Query itself --- namely
443 : * resultRelation, exclRelIndex and rowMarks entries. sublevels_up
444 : * cannot be zero when recursing into a subquery, so there's no need
445 : * to have the same logic inside OffsetVarNodes_walker.
446 : */
447 2583 : if (sublevels_up == 0)
448 : {
449 : ListCell *l;
450 :
451 2583 : if (qry->resultRelation)
452 182 : qry->resultRelation += offset;
453 :
454 2583 : if (qry->onConflict && qry->onConflict->exclRelIndex)
455 6 : qry->onConflict->exclRelIndex += offset;
456 :
457 2583 : foreach(l, qry->rowMarks)
458 : {
459 0 : RowMarkClause *rc = (RowMarkClause *) lfirst(l);
460 :
461 0 : rc->rti += offset;
462 : }
463 : }
464 2583 : query_tree_walker(qry, OffsetVarNodes_walker,
465 : (void *) &context, 0);
466 : }
467 : else
468 2154 : OffsetVarNodes_walker(node, &context);
469 4737 : }
470 :
471 : static Relids
472 12 : offset_relid_set(Relids relids, int offset)
473 : {
474 12 : Relids result = NULL;
475 : int rtindex;
476 :
477 12 : rtindex = -1;
478 42 : while ((rtindex = bms_next_member(relids, rtindex)) >= 0)
479 18 : result = bms_add_member(result, rtindex + offset);
480 12 : return result;
481 : }
482 :
483 : /*
484 : * ChangeVarNodes - adjust Var nodes for a specific change of RT index
485 : *
486 : * Find all Var nodes in the given tree belonging to a specific relation
487 : * (identified by sublevels_up and rt_index), and change their varno fields
488 : * to 'new_index'. The varnoold fields are changed too. Also, adjust other
489 : * nodes that contain rangetable indexes, such as RangeTblRef and JoinExpr.
490 : *
491 : * NOTE: although this has the form of a walker, we cheat and modify the
492 : * nodes in-place. The given expression tree should have been copied
493 : * earlier to ensure that no unwanted side-effects occur!
494 : */
495 :
496 : typedef struct
497 : {
498 : int rt_index;
499 : int new_index;
500 : int sublevels_up;
501 : } ChangeVarNodes_context;
502 :
503 : static bool
504 26347 : ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
505 : {
506 26347 : if (node == NULL)
507 9697 : return false;
508 16650 : if (IsA(node, Var))
509 : {
510 4245 : Var *var = (Var *) node;
511 :
512 8186 : if (var->varlevelsup == context->sublevels_up &&
513 3941 : var->varno == context->rt_index)
514 : {
515 2889 : var->varno = context->new_index;
516 2889 : var->varnoold = context->new_index;
517 : }
518 4245 : return false;
519 : }
520 12405 : if (IsA(node, CurrentOfExpr))
521 : {
522 0 : CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
523 :
524 0 : if (context->sublevels_up == 0 &&
525 0 : cexpr->cvarno == context->rt_index)
526 0 : cexpr->cvarno = context->new_index;
527 0 : return false;
528 : }
529 12405 : if (IsA(node, RangeTblRef))
530 : {
531 558 : RangeTblRef *rtr = (RangeTblRef *) node;
532 :
533 847 : if (context->sublevels_up == 0 &&
534 289 : rtr->rtindex == context->rt_index)
535 186 : rtr->rtindex = context->new_index;
536 : /* the subquery itself is visited separately */
537 558 : return false;
538 : }
539 11847 : if (IsA(node, JoinExpr))
540 : {
541 1 : JoinExpr *j = (JoinExpr *) node;
542 :
543 2 : if (context->sublevels_up == 0 &&
544 1 : j->rtindex == context->rt_index)
545 0 : j->rtindex = context->new_index;
546 : /* fall through to examine children */
547 : }
548 11847 : if (IsA(node, PlaceHolderVar))
549 : {
550 0 : PlaceHolderVar *phv = (PlaceHolderVar *) node;
551 :
552 0 : if (phv->phlevelsup == context->sublevels_up)
553 : {
554 0 : phv->phrels = adjust_relid_set(phv->phrels,
555 : context->rt_index,
556 : context->new_index);
557 : }
558 : /* fall through to examine children */
559 : }
560 11847 : if (IsA(node, PlanRowMark))
561 : {
562 27 : PlanRowMark *rowmark = (PlanRowMark *) node;
563 :
564 27 : if (context->sublevels_up == 0)
565 : {
566 27 : if (rowmark->rti == context->rt_index)
567 14 : rowmark->rti = context->new_index;
568 27 : if (rowmark->prti == context->rt_index)
569 14 : rowmark->prti = context->new_index;
570 : }
571 27 : return false;
572 : }
573 11820 : if (IsA(node, AppendRelInfo))
574 : {
575 30 : AppendRelInfo *appinfo = (AppendRelInfo *) node;
576 :
577 30 : if (context->sublevels_up == 0)
578 : {
579 30 : if (appinfo->parent_relid == context->rt_index)
580 10 : appinfo->parent_relid = context->new_index;
581 30 : if (appinfo->child_relid == context->rt_index)
582 0 : appinfo->child_relid = context->new_index;
583 : }
584 : /* fall through to examine children */
585 : }
586 : /* Shouldn't need to handle other planner auxiliary nodes here */
587 11820 : Assert(!IsA(node, SpecialJoinInfo));
588 11820 : Assert(!IsA(node, PlaceHolderInfo));
589 11820 : Assert(!IsA(node, MinMaxAggInfo));
590 :
591 11820 : if (IsA(node, Query))
592 : {
593 : /* Recurse into subselects */
594 : bool result;
595 :
596 244 : context->sublevels_up++;
597 244 : result = query_tree_walker((Query *) node, ChangeVarNodes_walker,
598 : (void *) context, 0);
599 244 : context->sublevels_up--;
600 244 : return result;
601 : }
602 11576 : return expression_tree_walker(node, ChangeVarNodes_walker,
603 : (void *) context);
604 : }
605 :
606 : void
607 2166 : ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up)
608 : {
609 : ChangeVarNodes_context context;
610 :
611 2166 : context.rt_index = rt_index;
612 2166 : context.new_index = new_index;
613 2166 : context.sublevels_up = sublevels_up;
614 :
615 : /*
616 : * Must be prepared to start with a Query or a bare expression tree; if
617 : * it's a Query, go straight to query_tree_walker to make sure that
618 : * sublevels_up doesn't get incremented prematurely.
619 : */
620 2166 : if (node && IsA(node, Query))
621 440 : {
622 440 : Query *qry = (Query *) node;
623 :
624 : /*
625 : * If we are starting at a Query, and sublevels_up is zero, then we
626 : * must also fix rangetable indexes in the Query itself --- namely
627 : * resultRelation and rowMarks entries. sublevels_up cannot be zero
628 : * when recursing into a subquery, so there's no need to have the same
629 : * logic inside ChangeVarNodes_walker.
630 : */
631 440 : if (sublevels_up == 0)
632 : {
633 : ListCell *l;
634 :
635 440 : if (qry->resultRelation == rt_index)
636 225 : qry->resultRelation = new_index;
637 :
638 : /* this is unlikely to ever be used, but ... */
639 440 : if (qry->onConflict && qry->onConflict->exclRelIndex == rt_index)
640 0 : qry->onConflict->exclRelIndex = new_index;
641 :
642 440 : foreach(l, qry->rowMarks)
643 : {
644 0 : RowMarkClause *rc = (RowMarkClause *) lfirst(l);
645 :
646 0 : if (rc->rti == rt_index)
647 0 : rc->rti = new_index;
648 : }
649 : }
650 440 : query_tree_walker(qry, ChangeVarNodes_walker,
651 : (void *) &context, 0);
652 : }
653 : else
654 1726 : ChangeVarNodes_walker(node, &context);
655 2166 : }
656 :
657 : /*
658 : * Substitute newrelid for oldrelid in a Relid set
659 : */
660 : static Relids
661 0 : adjust_relid_set(Relids relids, int oldrelid, int newrelid)
662 : {
663 0 : if (bms_is_member(oldrelid, relids))
664 : {
665 : /* Ensure we have a modifiable copy */
666 0 : relids = bms_copy(relids);
667 : /* Remove old, add new */
668 0 : relids = bms_del_member(relids, oldrelid);
669 0 : relids = bms_add_member(relids, newrelid);
670 : }
671 0 : return relids;
672 : }
673 :
674 : /*
675 : * IncrementVarSublevelsUp - adjust Var nodes when pushing them down in tree
676 : *
677 : * Find all Var nodes in the given tree having varlevelsup >= min_sublevels_up,
678 : * and add delta_sublevels_up to their varlevelsup value. This is needed when
679 : * an expression that's correct for some nesting level is inserted into a
680 : * subquery. Ordinarily the initial call has min_sublevels_up == 0 so that
681 : * all Vars are affected. The point of min_sublevels_up is that we can
682 : * increment it when we recurse into a sublink, so that local variables in
683 : * that sublink are not affected, only outer references to vars that belong
684 : * to the expression's original query level or parents thereof.
685 : *
686 : * Likewise for other nodes containing levelsup fields, such as Aggref.
687 : *
688 : * NOTE: although this has the form of a walker, we cheat and modify the
689 : * Var nodes in-place. The given expression tree should have been copied
690 : * earlier to ensure that no unwanted side-effects occur!
691 : */
692 :
693 : typedef struct
694 : {
695 : int delta_sublevels_up;
696 : int min_sublevels_up;
697 : } IncrementVarSublevelsUp_context;
698 :
699 : static bool
700 135656 : IncrementVarSublevelsUp_walker(Node *node,
701 : IncrementVarSublevelsUp_context *context)
702 : {
703 135656 : if (node == NULL)
704 40183 : return false;
705 95473 : if (IsA(node, Var))
706 : {
707 38600 : Var *var = (Var *) node;
708 :
709 38600 : if (var->varlevelsup >= context->min_sublevels_up)
710 617 : var->varlevelsup += context->delta_sublevels_up;
711 38600 : return false; /* done here */
712 : }
713 56873 : if (IsA(node, CurrentOfExpr))
714 : {
715 : /* this should not happen */
716 0 : if (context->min_sublevels_up == 0)
717 0 : elog(ERROR, "cannot push down CurrentOfExpr");
718 0 : return false;
719 : }
720 56873 : if (IsA(node, Aggref))
721 : {
722 322 : Aggref *agg = (Aggref *) node;
723 :
724 322 : if (agg->agglevelsup >= context->min_sublevels_up)
725 9 : agg->agglevelsup += context->delta_sublevels_up;
726 : /* fall through to recurse into argument */
727 : }
728 56873 : if (IsA(node, GroupingFunc))
729 : {
730 4 : GroupingFunc *grp = (GroupingFunc *) node;
731 :
732 4 : if (grp->agglevelsup >= context->min_sublevels_up)
733 4 : grp->agglevelsup += context->delta_sublevels_up;
734 : /* fall through to recurse into argument */
735 : }
736 56873 : if (IsA(node, PlaceHolderVar))
737 : {
738 55 : PlaceHolderVar *phv = (PlaceHolderVar *) node;
739 :
740 55 : if (phv->phlevelsup >= context->min_sublevels_up)
741 43 : phv->phlevelsup += context->delta_sublevels_up;
742 : /* fall through to recurse into argument */
743 : }
744 56873 : if (IsA(node, RangeTblEntry))
745 : {
746 6771 : RangeTblEntry *rte = (RangeTblEntry *) node;
747 :
748 6771 : if (rte->rtekind == RTE_CTE)
749 : {
750 23 : if (rte->ctelevelsup >= context->min_sublevels_up)
751 23 : rte->ctelevelsup += context->delta_sublevels_up;
752 : }
753 6771 : return false; /* allow range_table_walker to continue */
754 : }
755 50102 : if (IsA(node, Query))
756 : {
757 : /* Recurse into subselects */
758 : bool result;
759 :
760 751 : context->min_sublevels_up++;
761 751 : result = query_tree_walker((Query *) node,
762 : IncrementVarSublevelsUp_walker,
763 : (void *) context,
764 : QTW_EXAMINE_RTES);
765 751 : context->min_sublevels_up--;
766 751 : return result;
767 : }
768 49351 : return expression_tree_walker(node, IncrementVarSublevelsUp_walker,
769 : (void *) context);
770 : }
771 :
772 : void
773 4495 : IncrementVarSublevelsUp(Node *node, int delta_sublevels_up,
774 : int min_sublevels_up)
775 : {
776 : IncrementVarSublevelsUp_context context;
777 :
778 4495 : context.delta_sublevels_up = delta_sublevels_up;
779 4495 : context.min_sublevels_up = min_sublevels_up;
780 :
781 : /*
782 : * Must be prepared to start with a Query or a bare expression tree; if
783 : * it's a Query, we don't want to increment sublevels_up.
784 : */
785 4495 : query_or_expression_tree_walker(node,
786 : IncrementVarSublevelsUp_walker,
787 : (void *) &context,
788 : QTW_EXAMINE_RTES);
789 4495 : }
790 :
791 : /*
792 : * IncrementVarSublevelsUp_rtable -
793 : * Same as IncrementVarSublevelsUp, but to be invoked on a range table.
794 : */
795 : void
796 69 : IncrementVarSublevelsUp_rtable(List *rtable, int delta_sublevels_up,
797 : int min_sublevels_up)
798 : {
799 : IncrementVarSublevelsUp_context context;
800 :
801 69 : context.delta_sublevels_up = delta_sublevels_up;
802 69 : context.min_sublevels_up = min_sublevels_up;
803 :
804 69 : range_table_walker(rtable,
805 : IncrementVarSublevelsUp_walker,
806 : (void *) &context,
807 : QTW_EXAMINE_RTES);
808 69 : }
809 :
810 :
811 : /*
812 : * rangeTableEntry_used - detect whether an RTE is referenced somewhere
813 : * in var nodes or join or setOp trees of a query or expression.
814 : */
815 :
816 : typedef struct
817 : {
818 : int rt_index;
819 : int sublevels_up;
820 : } rangeTableEntry_used_context;
821 :
822 : static bool
823 359862 : rangeTableEntry_used_walker(Node *node,
824 : rangeTableEntry_used_context *context)
825 : {
826 359862 : if (node == NULL)
827 72594 : return false;
828 287268 : if (IsA(node, Var))
829 : {
830 112084 : Var *var = (Var *) node;
831 :
832 220797 : if (var->varlevelsup == context->sublevels_up &&
833 108713 : var->varno == context->rt_index)
834 11660 : return true;
835 100424 : return false;
836 : }
837 175184 : if (IsA(node, CurrentOfExpr))
838 : {
839 2 : CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
840 :
841 4 : if (context->sublevels_up == 0 &&
842 2 : cexpr->cvarno == context->rt_index)
843 0 : return true;
844 2 : return false;
845 : }
846 175182 : if (IsA(node, RangeTblRef))
847 : {
848 10665 : RangeTblRef *rtr = (RangeTblRef *) node;
849 :
850 14455 : if (rtr->rtindex == context->rt_index &&
851 3790 : context->sublevels_up == 0)
852 3355 : return true;
853 : /* the subquery itself is visited separately */
854 7310 : return false;
855 : }
856 164517 : if (IsA(node, JoinExpr))
857 : {
858 3507 : JoinExpr *j = (JoinExpr *) node;
859 :
860 3507 : if (j->rtindex == context->rt_index &&
861 0 : context->sublevels_up == 0)
862 0 : return true;
863 : /* fall through to examine children */
864 : }
865 : /* Shouldn't need to handle planner auxiliary nodes here */
866 164517 : Assert(!IsA(node, PlaceHolderVar));
867 164517 : Assert(!IsA(node, PlanRowMark));
868 164517 : Assert(!IsA(node, SpecialJoinInfo));
869 164517 : Assert(!IsA(node, AppendRelInfo));
870 164517 : Assert(!IsA(node, PlaceHolderInfo));
871 164517 : Assert(!IsA(node, MinMaxAggInfo));
872 :
873 164517 : if (IsA(node, Query))
874 : {
875 : /* Recurse into subselects */
876 : bool result;
877 :
878 1136 : context->sublevels_up++;
879 1136 : result = query_tree_walker((Query *) node, rangeTableEntry_used_walker,
880 : (void *) context, 0);
881 1136 : context->sublevels_up--;
882 1136 : return result;
883 : }
884 163381 : return expression_tree_walker(node, rangeTableEntry_used_walker,
885 : (void *) context);
886 : }
887 :
888 : bool
889 18550 : rangeTableEntry_used(Node *node, int rt_index, int sublevels_up)
890 : {
891 : rangeTableEntry_used_context context;
892 :
893 18550 : context.rt_index = rt_index;
894 18550 : context.sublevels_up = sublevels_up;
895 :
896 : /*
897 : * Must be prepared to start with a Query or a bare expression tree; if
898 : * it's a Query, we don't want to increment sublevels_up.
899 : */
900 18550 : return query_or_expression_tree_walker(node,
901 : rangeTableEntry_used_walker,
902 : (void *) &context,
903 : 0);
904 : }
905 :
906 :
907 : /*
908 : * If the given Query is an INSERT ... SELECT construct, extract and
909 : * return the sub-Query node that represents the SELECT part. Otherwise
910 : * return the given Query.
911 : *
912 : * If subquery_ptr is not NULL, then *subquery_ptr is set to the location
913 : * of the link to the SELECT subquery inside parsetree, or NULL if not an
914 : * INSERT ... SELECT.
915 : *
916 : * This is a hack needed because transformations on INSERT ... SELECTs that
917 : * appear in rule actions should be applied to the source SELECT, not to the
918 : * INSERT part. Perhaps this can be cleaned up with redesigned querytrees.
919 : */
920 : Query *
921 410 : getInsertSelectQuery(Query *parsetree, Query ***subquery_ptr)
922 : {
923 : Query *selectquery;
924 : RangeTblEntry *selectrte;
925 : RangeTblRef *rtr;
926 :
927 410 : if (subquery_ptr)
928 190 : *subquery_ptr = NULL;
929 :
930 410 : if (parsetree == NULL)
931 0 : return parsetree;
932 410 : if (parsetree->commandType != CMD_INSERT)
933 165 : return parsetree;
934 :
935 : /*
936 : * Currently, this is ONLY applied to rule-action queries, and so we
937 : * expect to find the OLD and NEW placeholder entries in the given query.
938 : * If they're not there, it must be an INSERT/SELECT in which they've been
939 : * pushed down to the SELECT.
940 : */
941 490 : if (list_length(parsetree->rtable) >= 2 &&
942 245 : strcmp(rt_fetch(PRS2_OLD_VARNO, parsetree->rtable)->eref->aliasname,
943 228 : "old") == 0 &&
944 228 : strcmp(rt_fetch(PRS2_NEW_VARNO, parsetree->rtable)->eref->aliasname,
945 : "new") == 0)
946 228 : return parsetree;
947 17 : Assert(parsetree->jointree && IsA(parsetree->jointree, FromExpr));
948 17 : if (list_length(parsetree->jointree->fromlist) != 1)
949 0 : elog(ERROR, "expected to find SELECT subquery");
950 17 : rtr = (RangeTblRef *) linitial(parsetree->jointree->fromlist);
951 17 : Assert(IsA(rtr, RangeTblRef));
952 17 : selectrte = rt_fetch(rtr->rtindex, parsetree->rtable);
953 17 : selectquery = selectrte->subquery;
954 34 : if (!(selectquery && IsA(selectquery, Query) &&
955 17 : selectquery->commandType == CMD_SELECT))
956 0 : elog(ERROR, "expected to find SELECT subquery");
957 34 : if (list_length(selectquery->rtable) >= 2 &&
958 17 : strcmp(rt_fetch(PRS2_OLD_VARNO, selectquery->rtable)->eref->aliasname,
959 17 : "old") == 0 &&
960 17 : strcmp(rt_fetch(PRS2_NEW_VARNO, selectquery->rtable)->eref->aliasname,
961 : "new") == 0)
962 : {
963 17 : if (subquery_ptr)
964 7 : *subquery_ptr = &(selectrte->subquery);
965 17 : return selectquery;
966 : }
967 0 : elog(ERROR, "could not find rule placeholders");
968 : return NULL; /* not reached */
969 : }
970 :
971 :
972 : /*
973 : * Add the given qualifier condition to the query's WHERE clause
974 : */
975 : void
976 494 : AddQual(Query *parsetree, Node *qual)
977 : {
978 : Node *copy;
979 :
980 494 : if (qual == NULL)
981 222 : return;
982 :
983 272 : if (parsetree->commandType == CMD_UTILITY)
984 : {
985 : /*
986 : * There's noplace to put the qual on a utility statement.
987 : *
988 : * If it's a NOTIFY, silently ignore the qual; this means that the
989 : * NOTIFY will execute, whether or not there are any qualifying rows.
990 : * While clearly wrong, this is much more useful than refusing to
991 : * execute the rule at all, and extra NOTIFY events are harmless for
992 : * typical uses of NOTIFY.
993 : *
994 : * If it isn't a NOTIFY, error out, since unconditional execution of
995 : * other utility stmts is unlikely to be wanted. (This case is not
996 : * currently allowed anyway, but keep the test for safety.)
997 : */
998 0 : if (parsetree->utilityStmt && IsA(parsetree->utilityStmt, NotifyStmt))
999 0 : return;
1000 : else
1001 0 : ereport(ERROR,
1002 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1003 : errmsg("conditional utility statements are not implemented")));
1004 : }
1005 :
1006 272 : if (parsetree->setOperations != NULL)
1007 : {
1008 : /*
1009 : * There's noplace to put the qual on a setop statement, either. (This
1010 : * could be fixed, but right now the planner simply ignores any qual
1011 : * condition on a setop query.)
1012 : */
1013 0 : ereport(ERROR,
1014 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1015 : errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
1016 : }
1017 :
1018 : /* INTERSECT want's the original, but we need to copy - Jan */
1019 272 : copy = copyObject(qual);
1020 :
1021 272 : parsetree->jointree->quals = make_and_qual(parsetree->jointree->quals,
1022 : copy);
1023 :
1024 : /*
1025 : * We had better not have stuck an aggregate into the WHERE clause.
1026 : */
1027 272 : Assert(!contain_aggs_of_level(copy, 0));
1028 :
1029 : /*
1030 : * Make sure query is marked correctly if added qual has sublinks. Need
1031 : * not search qual when query is already marked.
1032 : */
1033 272 : if (!parsetree->hasSubLinks)
1034 271 : parsetree->hasSubLinks = checkExprHasSubLink(copy);
1035 : }
1036 :
1037 :
1038 : /*
1039 : * Invert the given clause and add it to the WHERE qualifications of the
1040 : * given querytree. Inversion means "x IS NOT TRUE", not just "NOT x",
1041 : * else we will do the wrong thing when x evaluates to NULL.
1042 : */
1043 : void
1044 71 : AddInvertedQual(Query *parsetree, Node *qual)
1045 : {
1046 : BooleanTest *invqual;
1047 :
1048 71 : if (qual == NULL)
1049 71 : return;
1050 :
1051 : /* Need not copy input qual, because AddQual will... */
1052 71 : invqual = makeNode(BooleanTest);
1053 71 : invqual->arg = (Expr *) qual;
1054 71 : invqual->booltesttype = IS_NOT_TRUE;
1055 71 : invqual->location = -1;
1056 :
1057 71 : AddQual(parsetree, (Node *) invqual);
1058 : }
1059 :
1060 :
1061 : /*
1062 : * replace_rte_variables() finds all Vars in an expression tree
1063 : * that reference a particular RTE, and replaces them with substitute
1064 : * expressions obtained from a caller-supplied callback function.
1065 : *
1066 : * When invoking replace_rte_variables on a portion of a Query, pass the
1067 : * address of the containing Query's hasSubLinks field as outer_hasSubLinks.
1068 : * Otherwise, pass NULL, but inserting a SubLink into a non-Query expression
1069 : * will then cause an error.
1070 : *
1071 : * Note: the business with inserted_sublink is needed to update hasSubLinks
1072 : * in subqueries when the replacement adds a subquery inside a subquery.
1073 : * Messy, isn't it? We do not need to do similar pushups for hasAggs,
1074 : * because it isn't possible for this transformation to insert a level-zero
1075 : * aggregate reference into a subquery --- it could only insert outer aggs.
1076 : * Likewise for hasWindowFuncs.
1077 : *
1078 : * Note: usually, we'd not expose the mutator function or context struct
1079 : * for a function like this. We do so because callbacks often find it
1080 : * convenient to recurse directly to the mutator on sub-expressions of
1081 : * what they will return.
1082 : */
1083 : Node *
1084 9189 : replace_rte_variables(Node *node, int target_varno, int sublevels_up,
1085 : replace_rte_variables_callback callback,
1086 : void *callback_arg,
1087 : bool *outer_hasSubLinks)
1088 : {
1089 : Node *result;
1090 : replace_rte_variables_context context;
1091 :
1092 9189 : context.callback = callback;
1093 9189 : context.callback_arg = callback_arg;
1094 9189 : context.target_varno = target_varno;
1095 9189 : context.sublevels_up = sublevels_up;
1096 :
1097 : /*
1098 : * We try to initialize inserted_sublink to true if there is no need to
1099 : * detect new sublinks because the query already has some.
1100 : */
1101 9189 : if (node && IsA(node, Query))
1102 429 : context.inserted_sublink = ((Query *) node)->hasSubLinks;
1103 8760 : else if (outer_hasSubLinks)
1104 8760 : context.inserted_sublink = *outer_hasSubLinks;
1105 : else
1106 0 : context.inserted_sublink = false;
1107 :
1108 : /*
1109 : * Must be prepared to start with a Query or a bare expression tree; if
1110 : * it's a Query, we don't want to increment sublevels_up.
1111 : */
1112 9189 : result = query_or_expression_tree_mutator(node,
1113 : replace_rte_variables_mutator,
1114 : (void *) &context,
1115 : 0);
1116 :
1117 9188 : if (context.inserted_sublink)
1118 : {
1119 787 : if (result && IsA(result, Query))
1120 15 : ((Query *) result)->hasSubLinks = true;
1121 772 : else if (outer_hasSubLinks)
1122 772 : *outer_hasSubLinks = true;
1123 : else
1124 0 : elog(ERROR, "replace_rte_variables inserted a SubLink, but has noplace to record it");
1125 : }
1126 :
1127 9188 : return result;
1128 : }
1129 :
1130 : Node *
1131 54391 : replace_rte_variables_mutator(Node *node,
1132 : replace_rte_variables_context *context)
1133 : {
1134 54391 : if (node == NULL)
1135 14810 : return NULL;
1136 39581 : if (IsA(node, Var))
1137 : {
1138 14686 : Var *var = (Var *) node;
1139 :
1140 22046 : if (var->varno == context->target_varno &&
1141 7360 : var->varlevelsup == context->sublevels_up)
1142 : {
1143 : /* Found a matching variable, make the substitution */
1144 : Node *newnode;
1145 :
1146 6733 : newnode = (*context->callback) (var, context);
1147 : /* Detect if we are adding a sublink to query */
1148 6733 : if (!context->inserted_sublink)
1149 6187 : context->inserted_sublink = checkExprHasSubLink(newnode);
1150 6733 : return newnode;
1151 : }
1152 : /* otherwise fall through to copy the var normally */
1153 : }
1154 24895 : else if (IsA(node, CurrentOfExpr))
1155 : {
1156 1 : CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
1157 :
1158 2 : if (cexpr->cvarno == context->target_varno &&
1159 1 : context->sublevels_up == 0)
1160 : {
1161 : /*
1162 : * We get here if a WHERE CURRENT OF expression turns out to apply
1163 : * to a view. Someday we might be able to translate the
1164 : * expression to apply to an underlying table of the view, but
1165 : * right now it's not implemented.
1166 : */
1167 1 : ereport(ERROR,
1168 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1169 : errmsg("WHERE CURRENT OF on a view is not implemented")));
1170 : }
1171 : /* otherwise fall through to copy the expr normally */
1172 : }
1173 24894 : else if (IsA(node, Query))
1174 : {
1175 : /* Recurse into RTE subquery or not-yet-planned sublink subquery */
1176 : Query *newnode;
1177 : bool save_inserted_sublink;
1178 :
1179 204 : context->sublevels_up++;
1180 204 : save_inserted_sublink = context->inserted_sublink;
1181 204 : context->inserted_sublink = ((Query *) node)->hasSubLinks;
1182 204 : newnode = query_tree_mutator((Query *) node,
1183 : replace_rte_variables_mutator,
1184 : (void *) context,
1185 : 0);
1186 204 : newnode->hasSubLinks |= context->inserted_sublink;
1187 204 : context->inserted_sublink = save_inserted_sublink;
1188 204 : context->sublevels_up--;
1189 204 : return (Node *) newnode;
1190 : }
1191 32643 : return expression_tree_mutator(node, replace_rte_variables_mutator,
1192 : (void *) context);
1193 : }
1194 :
1195 :
1196 : /*
1197 : * map_variable_attnos() finds all user-column Vars in an expression tree
1198 : * that reference a particular RTE, and adjusts their varattnos according
1199 : * to the given mapping array (varattno n is replaced by attno_map[n-1]).
1200 : * Vars for system columns are not modified.
1201 : *
1202 : * A zero in the mapping array represents a dropped column, which should not
1203 : * appear in the expression.
1204 : *
1205 : * If the expression tree contains a whole-row Var for the target RTE,
1206 : * *found_whole_row is returned as TRUE. In addition, if to_rowtype is
1207 : * not InvalidOid, we modify the Var's vartype and insert a ConvertRowTypeExpr
1208 : * to map back to the orignal rowtype. Callers that don't provide to_rowtype
1209 : * should report an error if *found_row_type is true; we don't do that here
1210 : * because we don't know exactly what wording for the error message would
1211 : * be most appropriate. The caller will be aware of the context.
1212 : *
1213 : * This could be built using replace_rte_variables and a callback function,
1214 : * but since we don't ever need to insert sublinks, replace_rte_variables is
1215 : * overly complicated.
1216 : */
1217 :
1218 : typedef struct
1219 : {
1220 : int target_varno; /* RTE index to search for */
1221 : int sublevels_up; /* (current) nesting depth */
1222 : const AttrNumber *attno_map; /* map array for user attnos */
1223 : int map_length; /* number of entries in attno_map[] */
1224 : /* Target type when converting whole-row vars */
1225 : Oid to_rowtype;
1226 : bool *found_whole_row; /* output flag */
1227 : } map_variable_attnos_context;
1228 :
1229 : static Node *
1230 3741 : map_variable_attnos_mutator(Node *node,
1231 : map_variable_attnos_context *context)
1232 : {
1233 3741 : if (node == NULL)
1234 3 : return NULL;
1235 3738 : if (IsA(node, Var))
1236 : {
1237 858 : Var *var = (Var *) node;
1238 :
1239 1716 : if (var->varno == context->target_varno &&
1240 858 : var->varlevelsup == context->sublevels_up)
1241 : {
1242 : /* Found a matching variable, make the substitution */
1243 858 : Var *newvar = (Var *) palloc(sizeof(Var));
1244 858 : int attno = var->varattno;
1245 :
1246 858 : *newvar = *var;
1247 858 : if (attno > 0)
1248 : {
1249 : /* user-defined column, replace attno */
1250 1688 : if (attno > context->map_length ||
1251 844 : context->attno_map[attno - 1] == 0)
1252 0 : elog(ERROR, "unexpected varattno %d in expression to be mapped",
1253 : attno);
1254 844 : newvar->varattno = newvar->varoattno = context->attno_map[attno - 1];
1255 : }
1256 14 : else if (attno == 0)
1257 : {
1258 : /* whole-row variable, warn caller */
1259 7 : *(context->found_whole_row) = true;
1260 :
1261 : /* If the callers expects us to convert the same, do so. */
1262 7 : if (OidIsValid(context->to_rowtype))
1263 : {
1264 : /* No support for RECORDOID. */
1265 6 : Assert(var->vartype != RECORDOID);
1266 :
1267 : /* Don't convert unless necessary. */
1268 6 : if (context->to_rowtype != var->vartype)
1269 : {
1270 : ConvertRowtypeExpr *r;
1271 :
1272 : /* Var itself is converted to the requested type. */
1273 6 : newvar->vartype = context->to_rowtype;
1274 :
1275 : /*
1276 : * And a conversion node on top to convert back to the
1277 : * original type.
1278 : */
1279 6 : r = makeNode(ConvertRowtypeExpr);
1280 6 : r->arg = (Expr *) newvar;
1281 6 : r->resulttype = var->vartype;
1282 6 : r->convertformat = COERCE_IMPLICIT_CAST;
1283 6 : r->location = -1;
1284 :
1285 6 : return (Node *) r;
1286 : }
1287 : }
1288 : }
1289 852 : return (Node *) newvar;
1290 : }
1291 : /* otherwise fall through to copy the var normally */
1292 : }
1293 2880 : else if (IsA(node, Query))
1294 : {
1295 : /* Recurse into RTE subquery or not-yet-planned sublink subquery */
1296 : Query *newnode;
1297 :
1298 0 : context->sublevels_up++;
1299 0 : newnode = query_tree_mutator((Query *) node,
1300 : map_variable_attnos_mutator,
1301 : (void *) context,
1302 : 0);
1303 0 : context->sublevels_up--;
1304 0 : return (Node *) newnode;
1305 : }
1306 2880 : return expression_tree_mutator(node, map_variable_attnos_mutator,
1307 : (void *) context);
1308 : }
1309 :
1310 : Node *
1311 257 : map_variable_attnos(Node *node,
1312 : int target_varno, int sublevels_up,
1313 : const AttrNumber *attno_map, int map_length,
1314 : Oid to_rowtype, bool *found_whole_row)
1315 : {
1316 : map_variable_attnos_context context;
1317 :
1318 257 : context.target_varno = target_varno;
1319 257 : context.sublevels_up = sublevels_up;
1320 257 : context.attno_map = attno_map;
1321 257 : context.map_length = map_length;
1322 257 : context.to_rowtype = to_rowtype;
1323 257 : context.found_whole_row = found_whole_row;
1324 :
1325 257 : *found_whole_row = false;
1326 :
1327 : /*
1328 : * Must be prepared to start with a Query or a bare expression tree; if
1329 : * it's a Query, we don't want to increment sublevels_up.
1330 : */
1331 257 : return query_or_expression_tree_mutator(node,
1332 : map_variable_attnos_mutator,
1333 : (void *) &context,
1334 : 0);
1335 : }
1336 :
1337 :
1338 : /*
1339 : * ReplaceVarsFromTargetList - replace Vars with items from a targetlist
1340 : *
1341 : * Vars matching target_varno and sublevels_up are replaced by the
1342 : * entry with matching resno from targetlist, if there is one.
1343 : *
1344 : * If there is no matching resno for such a Var, the action depends on the
1345 : * nomatch_option:
1346 : * REPLACEVARS_REPORT_ERROR: throw an error
1347 : * REPLACEVARS_CHANGE_VARNO: change Var's varno to nomatch_varno
1348 : * REPLACEVARS_SUBSTITUTE_NULL: replace Var with a NULL Const of same type
1349 : *
1350 : * The caller must also provide target_rte, the RTE describing the target
1351 : * relation. This is needed to handle whole-row Vars referencing the target.
1352 : * We expand such Vars into RowExpr constructs.
1353 : *
1354 : * outer_hasSubLinks works the same as for replace_rte_variables().
1355 : */
1356 :
1357 : typedef struct
1358 : {
1359 : RangeTblEntry *target_rte;
1360 : List *targetlist;
1361 : ReplaceVarsNoMatchOption nomatch_option;
1362 : int nomatch_varno;
1363 : } ReplaceVarsFromTargetList_context;
1364 :
1365 : static Node *
1366 941 : ReplaceVarsFromTargetList_callback(Var *var,
1367 : replace_rte_variables_context *context)
1368 : {
1369 941 : ReplaceVarsFromTargetList_context *rcon = (ReplaceVarsFromTargetList_context *) context->callback_arg;
1370 : TargetEntry *tle;
1371 :
1372 941 : if (var->varattno == InvalidAttrNumber)
1373 : {
1374 : /* Must expand whole-tuple reference into RowExpr */
1375 : RowExpr *rowexpr;
1376 : List *colnames;
1377 : List *fields;
1378 :
1379 : /*
1380 : * If generating an expansion for a var of a named rowtype (ie, this
1381 : * is a plain relation RTE), then we must include dummy items for
1382 : * dropped columns. If the var is RECORD (ie, this is a JOIN), then
1383 : * omit dropped columns. Either way, attach column names to the
1384 : * RowExpr for use of ruleutils.c.
1385 : */
1386 12 : expandRTE(rcon->target_rte,
1387 8 : var->varno, var->varlevelsup, var->location,
1388 4 : (var->vartype != RECORDOID),
1389 : &colnames, &fields);
1390 : /* Adjust the generated per-field Vars... */
1391 4 : fields = (List *) replace_rte_variables_mutator((Node *) fields,
1392 : context);
1393 4 : rowexpr = makeNode(RowExpr);
1394 4 : rowexpr->args = fields;
1395 4 : rowexpr->row_typeid = var->vartype;
1396 4 : rowexpr->row_format = COERCE_IMPLICIT_CAST;
1397 4 : rowexpr->colnames = colnames;
1398 4 : rowexpr->location = var->location;
1399 :
1400 4 : return (Node *) rowexpr;
1401 : }
1402 :
1403 : /* Normal case referencing one targetlist element */
1404 937 : tle = get_tle_by_resno(rcon->targetlist, var->varattno);
1405 :
1406 937 : if (tle == NULL || tle->resjunk)
1407 : {
1408 : /* Failed to find column in targetlist */
1409 36 : switch (rcon->nomatch_option)
1410 : {
1411 : case REPLACEVARS_REPORT_ERROR:
1412 : /* fall through, throw error below */
1413 0 : break;
1414 :
1415 : case REPLACEVARS_CHANGE_VARNO:
1416 27 : var = (Var *) copyObject(var);
1417 27 : var->varno = rcon->nomatch_varno;
1418 27 : var->varnoold = rcon->nomatch_varno;
1419 27 : return (Node *) var;
1420 :
1421 : case REPLACEVARS_SUBSTITUTE_NULL:
1422 :
1423 : /*
1424 : * If Var is of domain type, we should add a CoerceToDomain
1425 : * node, in case there is a NOT NULL domain constraint.
1426 : */
1427 9 : return coerce_to_domain((Node *) makeNullConst(var->vartype,
1428 : var->vartypmod,
1429 : var->varcollid),
1430 : InvalidOid, -1,
1431 : var->vartype,
1432 : COERCE_IMPLICIT_CAST,
1433 : -1,
1434 : false,
1435 : false);
1436 : }
1437 0 : elog(ERROR, "could not find replacement targetlist entry for attno %d",
1438 : var->varattno);
1439 : return NULL; /* keep compiler quiet */
1440 : }
1441 : else
1442 : {
1443 : /* Make a copy of the tlist item to return */
1444 901 : Expr *newnode = copyObject(tle->expr);
1445 :
1446 : /* Must adjust varlevelsup if tlist item is from higher query */
1447 901 : if (var->varlevelsup > 0)
1448 26 : IncrementVarSublevelsUp((Node *) newnode, var->varlevelsup, 0);
1449 :
1450 : /*
1451 : * Check to see if the tlist item contains a PARAM_MULTIEXPR Param,
1452 : * and throw error if so. This case could only happen when expanding
1453 : * an ON UPDATE rule's NEW variable and the referenced tlist item in
1454 : * the original UPDATE command is part of a multiple assignment. There
1455 : * seems no practical way to handle such cases without multiple
1456 : * evaluation of the multiple assignment's sub-select, which would
1457 : * create semantic oddities that users of rules would probably prefer
1458 : * not to cope with. So treat it as an unimplemented feature.
1459 : */
1460 901 : if (contains_multiexpr_param((Node *) newnode, NULL))
1461 0 : ereport(ERROR,
1462 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1463 : errmsg("NEW variables in ON UPDATE rules cannot reference columns that are part of a multiple assignment in the subject UPDATE command")));
1464 :
1465 901 : return (Node *) newnode;
1466 : }
1467 : }
1468 :
1469 : Node *
1470 616 : ReplaceVarsFromTargetList(Node *node,
1471 : int target_varno, int sublevels_up,
1472 : RangeTblEntry *target_rte,
1473 : List *targetlist,
1474 : ReplaceVarsNoMatchOption nomatch_option,
1475 : int nomatch_varno,
1476 : bool *outer_hasSubLinks)
1477 : {
1478 : ReplaceVarsFromTargetList_context context;
1479 :
1480 616 : context.target_rte = target_rte;
1481 616 : context.targetlist = targetlist;
1482 616 : context.nomatch_option = nomatch_option;
1483 616 : context.nomatch_varno = nomatch_varno;
1484 :
1485 616 : return replace_rte_variables(node, target_varno, sublevels_up,
1486 : ReplaceVarsFromTargetList_callback,
1487 : (void *) &context,
1488 : outer_hasSubLinks);
1489 : }
|