]> granicus.if.org Git - postgresql/blob - src/backend/rewrite/rewriteManip.c
IN clauses appearing at top level of WHERE can now be handled as joins.
[postgresql] / src / backend / rewrite / rewriteManip.c
1 /*-------------------------------------------------------------------------
2  *
3  * rewriteManip.c
4  *
5  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
6  * Portions Copyright (c) 1994, Regents of the University of California
7  *
8  *
9  * IDENTIFICATION
10  *        $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.70 2003/01/20 18:54:59 tgl Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include "postgres.h"
15
16 #include "nodes/makefuncs.h"
17 #include "optimizer/clauses.h"
18 #include "optimizer/tlist.h"
19 #include "parser/parsetree.h"
20 #include "parser/parse_clause.h"
21 #include "rewrite/rewriteManip.h"
22 #include "utils/lsyscache.h"
23
24
25 static bool checkExprHasAggs_walker(Node *node, void *context);
26 static bool checkExprHasSubLink_walker(Node *node, void *context);
27
28
29 /*
30  * checkExprHasAggs -
31  *      Queries marked hasAggs might not have them any longer after
32  *      rewriting. Check it.
33  */
34 bool
35 checkExprHasAggs(Node *node)
36 {
37         /*
38          * If a Query is passed, examine it --- but we will not recurse into
39          * sub-Queries.
40          */
41         return query_or_expression_tree_walker(node,
42                                                                                    checkExprHasAggs_walker,
43                                                                                    NULL,
44                                                                                    QTW_IGNORE_RT_SUBQUERIES);
45 }
46
47 static bool
48 checkExprHasAggs_walker(Node *node, void *context)
49 {
50         if (node == NULL)
51                 return false;
52         if (IsA(node, Aggref))
53                 return true;                    /* abort the tree traversal and return
54                                                                  * true */
55         return expression_tree_walker(node, checkExprHasAggs_walker, context);
56 }
57
58 /*
59  * checkExprHasSubLink -
60  *      Queries marked hasSubLinks might not have them any longer after
61  *      rewriting. Check it.
62  */
63 bool
64 checkExprHasSubLink(Node *node)
65 {
66         /*
67          * If a Query is passed, examine it --- but we will not recurse into
68          * sub-Queries.
69          */
70         return query_or_expression_tree_walker(node,
71                                                                                    checkExprHasSubLink_walker,
72                                                                                    NULL,
73                                                                                    QTW_IGNORE_RT_SUBQUERIES);
74 }
75
76 static bool
77 checkExprHasSubLink_walker(Node *node, void *context)
78 {
79         if (node == NULL)
80                 return false;
81         if (IsA(node, SubLink))
82                 return true;                    /* abort the tree traversal and return
83                                                                  * true */
84         return expression_tree_walker(node, checkExprHasSubLink_walker, context);
85 }
86
87
88 /*
89  * OffsetVarNodes - adjust Vars when appending one query's RT to another
90  *
91  * Find all Var nodes in the given tree with varlevelsup == sublevels_up,
92  * and increment their varno fields (rangetable indexes) by 'offset'.
93  * The varnoold fields are adjusted similarly.  Also, adjust other nodes
94  * that contain rangetable indexes, such as RangeTblRef and JoinExpr.
95  *
96  * NOTE: although this has the form of a walker, we cheat and modify the
97  * nodes in-place.      The given expression tree should have been copied
98  * earlier to ensure that no unwanted side-effects occur!
99  */
100
101 typedef struct
102 {
103         int                     offset;
104         int                     sublevels_up;
105 } OffsetVarNodes_context;
106
107 static bool
108 OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context)
109 {
110         if (node == NULL)
111                 return false;
112         if (IsA(node, Var))
113         {
114                 Var                *var = (Var *) node;
115
116                 if (var->varlevelsup == context->sublevels_up)
117                 {
118                         var->varno += context->offset;
119                         var->varnoold += context->offset;
120                 }
121                 return false;
122         }
123         if (IsA(node, RangeTblRef))
124         {
125                 RangeTblRef *rtr = (RangeTblRef *) node;
126
127                 if (context->sublevels_up == 0)
128                         rtr->rtindex += context->offset;
129                 /* the subquery itself is visited separately */
130                 return false;
131         }
132         if (IsA(node, JoinExpr))
133         {
134                 JoinExpr   *j = (JoinExpr *) node;
135
136                 if (context->sublevels_up == 0)
137                         j->rtindex += context->offset;
138                 /* fall through to examine children */
139         }
140         if (IsA(node, InClauseInfo))
141         {
142                 InClauseInfo   *ininfo = (InClauseInfo *) node;
143
144                 if (context->sublevels_up == 0)
145                 {
146                         List    *rt;
147
148                         foreach(rt, ininfo->lefthand)
149                         {
150                                 lfirsti(rt) += context->offset;
151                         }
152                         foreach(rt, ininfo->righthand)
153                         {
154                                 lfirsti(rt) += context->offset;
155                         }
156                 }
157                 /* fall through to examine children */
158         }
159         if (IsA(node, Query))
160         {
161                 /* Recurse into subselects */
162                 bool            result;
163
164                 context->sublevels_up++;
165                 result = query_tree_walker((Query *) node, OffsetVarNodes_walker,
166                                                                    (void *) context, 0);
167                 context->sublevels_up--;
168                 return result;
169         }
170         return expression_tree_walker(node, OffsetVarNodes_walker,
171                                                                   (void *) context);
172 }
173
174 void
175 OffsetVarNodes(Node *node, int offset, int sublevels_up)
176 {
177         OffsetVarNodes_context context;
178
179         context.offset = offset;
180         context.sublevels_up = sublevels_up;
181
182         /*
183          * Must be prepared to start with a Query or a bare expression tree;
184          * if it's a Query, go straight to query_tree_walker to make sure that
185          * sublevels_up doesn't get incremented prematurely.
186          */
187         if (node && IsA(node, Query))
188         {
189                 Query      *qry = (Query *) node;
190                 List       *l;
191
192                 /*
193                  * If we are starting at a Query, and sublevels_up is zero, then
194                  * we must also fix rangetable indexes in the Query itself ---
195                  * namely resultRelation and rowMarks entries.  sublevels_up
196                  * cannot be zero when recursing into a subquery, so there's no
197                  * need to have the same logic inside OffsetVarNodes_walker.
198                  */
199                 if (sublevels_up == 0)
200                 {
201                         if (qry->resultRelation)
202                                 qry->resultRelation += offset;
203                         foreach(l, qry->rowMarks)
204                                 lfirsti(l) += offset;
205                 }
206                 query_tree_walker(qry, OffsetVarNodes_walker,
207                                                   (void *) &context, 0);
208         }
209         else
210                 OffsetVarNodes_walker(node, &context);
211 }
212
213 /*
214  * ChangeVarNodes - adjust Var nodes for a specific change of RT index
215  *
216  * Find all Var nodes in the given tree belonging to a specific relation
217  * (identified by sublevels_up and rt_index), and change their varno fields
218  * to 'new_index'.      The varnoold fields are changed too.  Also, adjust other
219  * nodes that contain rangetable indexes, such as RangeTblRef and JoinExpr.
220  *
221  * NOTE: although this has the form of a walker, we cheat and modify the
222  * nodes in-place.      The given expression tree should have been copied
223  * earlier to ensure that no unwanted side-effects occur!
224  */
225
226 typedef struct
227 {
228         int                     rt_index;
229         int                     new_index;
230         int                     sublevels_up;
231 } ChangeVarNodes_context;
232
233 static bool
234 ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
235 {
236         if (node == NULL)
237                 return false;
238         if (IsA(node, Var))
239         {
240                 Var                *var = (Var *) node;
241
242                 if (var->varlevelsup == context->sublevels_up &&
243                         var->varno == context->rt_index)
244                 {
245                         var->varno = context->new_index;
246                         var->varnoold = context->new_index;
247                 }
248                 return false;
249         }
250         if (IsA(node, RangeTblRef))
251         {
252                 RangeTblRef *rtr = (RangeTblRef *) node;
253
254                 if (context->sublevels_up == 0 &&
255                         rtr->rtindex == context->rt_index)
256                         rtr->rtindex = context->new_index;
257                 /* the subquery itself is visited separately */
258                 return false;
259         }
260         if (IsA(node, JoinExpr))
261         {
262                 JoinExpr   *j = (JoinExpr *) node;
263
264                 if (context->sublevels_up == 0 &&
265                         j->rtindex == context->rt_index)
266                         j->rtindex = context->new_index;
267                 /* fall through to examine children */
268         }
269         if (IsA(node, InClauseInfo))
270         {
271                 InClauseInfo   *ininfo = (InClauseInfo *) node;
272
273                 if (context->sublevels_up == 0)
274                 {
275                         List    *rt;
276
277                         foreach(rt, ininfo->lefthand)
278                         {
279                                 if (lfirsti(rt) == context->rt_index)
280                                         lfirsti(rt) = context->new_index;
281                         }
282                         foreach(rt, ininfo->righthand)
283                         {
284                                 if (lfirsti(rt) == context->rt_index)
285                                         lfirsti(rt) = context->new_index;
286                         }
287                 }
288                 /* fall through to examine children */
289         }
290         if (IsA(node, Query))
291         {
292                 /* Recurse into subselects */
293                 bool            result;
294
295                 context->sublevels_up++;
296                 result = query_tree_walker((Query *) node, ChangeVarNodes_walker,
297                                                                    (void *) context, 0);
298                 context->sublevels_up--;
299                 return result;
300         }
301         return expression_tree_walker(node, ChangeVarNodes_walker,
302                                                                   (void *) context);
303 }
304
305 void
306 ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up)
307 {
308         ChangeVarNodes_context context;
309
310         context.rt_index = rt_index;
311         context.new_index = new_index;
312         context.sublevels_up = sublevels_up;
313
314         /*
315          * Must be prepared to start with a Query or a bare expression tree;
316          * if it's a Query, go straight to query_tree_walker to make sure that
317          * sublevels_up doesn't get incremented prematurely.
318          */
319         if (node && IsA(node, Query))
320         {
321                 Query      *qry = (Query *) node;
322                 List       *l;
323
324                 /*
325                  * If we are starting at a Query, and sublevels_up is zero, then
326                  * we must also fix rangetable indexes in the Query itself ---
327                  * namely resultRelation and rowMarks entries.  sublevels_up
328                  * cannot be zero when recursing into a subquery, so there's no
329                  * need to have the same logic inside ChangeVarNodes_walker.
330                  */
331                 if (sublevels_up == 0)
332                 {
333                         if (qry->resultRelation == rt_index)
334                                 qry->resultRelation = new_index;
335                         foreach(l, qry->rowMarks)
336                         {
337                                 if (lfirsti(l) == rt_index)
338                                         lfirsti(l) = new_index;
339                         }
340                 }
341                 query_tree_walker(qry, ChangeVarNodes_walker,
342                                                   (void *) &context, 0);
343         }
344         else
345                 ChangeVarNodes_walker(node, &context);
346 }
347
348 /*
349  * IncrementVarSublevelsUp - adjust Var nodes when pushing them down in tree
350  *
351  * Find all Var nodes in the given tree having varlevelsup >= min_sublevels_up,
352  * and add delta_sublevels_up to their varlevelsup value.  This is needed when
353  * an expression that's correct for some nesting level is inserted into a
354  * subquery.  Ordinarily the initial call has min_sublevels_up == 0 so that
355  * all Vars are affected.  The point of min_sublevels_up is that we can
356  * increment it when we recurse into a sublink, so that local variables in
357  * that sublink are not affected, only outer references to vars that belong
358  * to the expression's original query level or parents thereof.
359  *
360  * NOTE: although this has the form of a walker, we cheat and modify the
361  * Var nodes in-place.  The given expression tree should have been copied
362  * earlier to ensure that no unwanted side-effects occur!
363  */
364
365 typedef struct
366 {
367         int                     delta_sublevels_up;
368         int                     min_sublevels_up;
369 } IncrementVarSublevelsUp_context;
370
371 static bool
372 IncrementVarSublevelsUp_walker(Node *node,
373                                                            IncrementVarSublevelsUp_context *context)
374 {
375         if (node == NULL)
376                 return false;
377         if (IsA(node, Var))
378         {
379                 Var                *var = (Var *) node;
380
381                 if (var->varlevelsup >= context->min_sublevels_up)
382                         var->varlevelsup += context->delta_sublevels_up;
383                 return false;
384         }
385         if (IsA(node, Query))
386         {
387                 /* Recurse into subselects */
388                 bool            result;
389
390                 context->min_sublevels_up++;
391                 result = query_tree_walker((Query *) node,
392                                                                    IncrementVarSublevelsUp_walker,
393                                                                    (void *) context, 0);
394                 context->min_sublevels_up--;
395                 return result;
396         }
397         return expression_tree_walker(node, IncrementVarSublevelsUp_walker,
398                                                                   (void *) context);
399 }
400
401 void
402 IncrementVarSublevelsUp(Node *node, int delta_sublevels_up,
403                                                 int min_sublevels_up)
404 {
405         IncrementVarSublevelsUp_context context;
406
407         context.delta_sublevels_up = delta_sublevels_up;
408         context.min_sublevels_up = min_sublevels_up;
409
410         /*
411          * Must be prepared to start with a Query or a bare expression tree;
412          * if it's a Query, we don't want to increment sublevels_up.
413          */
414         query_or_expression_tree_walker(node,
415                                                                         IncrementVarSublevelsUp_walker,
416                                                                         (void *) &context,
417                                                                         0);
418 }
419
420
421 /*
422  * rangeTableEntry_used - detect whether an RTE is referenced somewhere
423  *      in var nodes or join or setOp trees of a query or expression.
424  */
425
426 typedef struct
427 {
428         int                     rt_index;
429         int                     sublevels_up;
430 } rangeTableEntry_used_context;
431
432 static bool
433 rangeTableEntry_used_walker(Node *node,
434                                                         rangeTableEntry_used_context *context)
435 {
436         if (node == NULL)
437                 return false;
438         if (IsA(node, Var))
439         {
440                 Var                *var = (Var *) node;
441
442                 if (var->varlevelsup == context->sublevels_up &&
443                         var->varno == context->rt_index)
444                         return true;
445                 return false;
446         }
447         if (IsA(node, RangeTblRef))
448         {
449                 RangeTblRef *rtr = (RangeTblRef *) node;
450
451                 if (rtr->rtindex == context->rt_index &&
452                         context->sublevels_up == 0)
453                         return true;
454                 /* the subquery itself is visited separately */
455                 return false;
456         }
457         if (IsA(node, JoinExpr))
458         {
459                 JoinExpr   *j = (JoinExpr *) node;
460
461                 if (j->rtindex == context->rt_index &&
462                         context->sublevels_up == 0)
463                         return true;
464                 /* fall through to examine children */
465         }
466         if (IsA(node, InClauseInfo))
467         {
468                 InClauseInfo   *ininfo = (InClauseInfo *) node;
469
470                 if (context->sublevels_up == 0 &&
471                         (intMember(context->rt_index, ininfo->lefthand) ||
472                          intMember(context->rt_index, ininfo->righthand)))
473                         return true;
474                 /* fall through to examine children */
475         }
476         if (IsA(node, Query))
477         {
478                 /* Recurse into subselects */
479                 bool            result;
480
481                 context->sublevels_up++;
482                 result = query_tree_walker((Query *) node, rangeTableEntry_used_walker,
483                                                                    (void *) context, 0);
484                 context->sublevels_up--;
485                 return result;
486         }
487         return expression_tree_walker(node, rangeTableEntry_used_walker,
488                                                                   (void *) context);
489 }
490
491 bool
492 rangeTableEntry_used(Node *node, int rt_index, int sublevels_up)
493 {
494         rangeTableEntry_used_context context;
495
496         context.rt_index = rt_index;
497         context.sublevels_up = sublevels_up;
498
499         /*
500          * Must be prepared to start with a Query or a bare expression tree;
501          * if it's a Query, we don't want to increment sublevels_up.
502          */
503         return query_or_expression_tree_walker(node,
504                                                                                    rangeTableEntry_used_walker,
505                                                                                    (void *) &context,
506                                                                                    0);
507 }
508
509
510 /*
511  * attribute_used -
512  *      Check if a specific attribute number of a RTE is used
513  *      somewhere in the query or expression.
514  */
515
516 typedef struct
517 {
518         int                     rt_index;
519         int                     attno;
520         int                     sublevels_up;
521 } attribute_used_context;
522
523 static bool
524 attribute_used_walker(Node *node,
525                                           attribute_used_context *context)
526 {
527         if (node == NULL)
528                 return false;
529         if (IsA(node, Var))
530         {
531                 Var                *var = (Var *) node;
532
533                 if (var->varlevelsup == context->sublevels_up &&
534                         var->varno == context->rt_index &&
535                         var->varattno == context->attno)
536                         return true;
537                 return false;
538         }
539         if (IsA(node, Query))
540         {
541                 /* Recurse into subselects */
542                 bool            result;
543
544                 context->sublevels_up++;
545                 result = query_tree_walker((Query *) node, attribute_used_walker,
546                                                                    (void *) context, 0);
547                 context->sublevels_up--;
548                 return result;
549         }
550         return expression_tree_walker(node, attribute_used_walker,
551                                                                   (void *) context);
552 }
553
554 bool
555 attribute_used(Node *node, int rt_index, int attno, int sublevels_up)
556 {
557         attribute_used_context context;
558
559         context.rt_index = rt_index;
560         context.attno = attno;
561         context.sublevels_up = sublevels_up;
562
563         /*
564          * Must be prepared to start with a Query or a bare expression tree;
565          * if it's a Query, we don't want to increment sublevels_up.
566          */
567         return query_or_expression_tree_walker(node,
568                                                                                    attribute_used_walker,
569                                                                                    (void *) &context,
570                                                                                    0);
571 }
572
573
574 /*
575  * If the given Query is an INSERT ... SELECT construct, extract and
576  * return the sub-Query node that represents the SELECT part.  Otherwise
577  * return the given Query.
578  *
579  * If subquery_ptr is not NULL, then *subquery_ptr is set to the location
580  * of the link to the SELECT subquery inside parsetree, or NULL if not an
581  * INSERT ... SELECT.
582  *
583  * This is a hack needed because transformations on INSERT ... SELECTs that
584  * appear in rule actions should be applied to the source SELECT, not to the
585  * INSERT part.  Perhaps this can be cleaned up with redesigned querytrees.
586  */
587 Query *
588 getInsertSelectQuery(Query *parsetree, Query ***subquery_ptr)
589 {
590         Query      *selectquery;
591         RangeTblEntry *selectrte;
592         RangeTblRef *rtr;
593
594         if (subquery_ptr)
595                 *subquery_ptr = NULL;
596
597         if (parsetree == NULL)
598                 return parsetree;
599         if (parsetree->commandType != CMD_INSERT)
600                 return parsetree;
601
602         /*
603          * Currently, this is ONLY applied to rule-action queries, and so we
604          * expect to find the *OLD* and *NEW* placeholder entries in the given
605          * query.  If they're not there, it must be an INSERT/SELECT in which
606          * they've been pushed down to the SELECT.
607          */
608         if (length(parsetree->rtable) >= 2 &&
609          strcmp(rt_fetch(PRS2_OLD_VARNO, parsetree->rtable)->eref->aliasname,
610                         "*OLD*") == 0 &&
611          strcmp(rt_fetch(PRS2_NEW_VARNO, parsetree->rtable)->eref->aliasname,
612                         "*NEW*") == 0)
613                 return parsetree;
614         Assert(parsetree->jointree && IsA(parsetree->jointree, FromExpr));
615         if (length(parsetree->jointree->fromlist) != 1)
616                 elog(ERROR, "getInsertSelectQuery: expected to find SELECT subquery");
617         rtr = (RangeTblRef *) lfirst(parsetree->jointree->fromlist);
618         Assert(IsA(rtr, RangeTblRef));
619         selectrte = rt_fetch(rtr->rtindex, parsetree->rtable);
620         selectquery = selectrte->subquery;
621         if (!(selectquery && IsA(selectquery, Query) &&
622                   selectquery->commandType == CMD_SELECT))
623                 elog(ERROR, "getInsertSelectQuery: expected to find SELECT subquery");
624         if (length(selectquery->rtable) >= 2 &&
625         strcmp(rt_fetch(PRS2_OLD_VARNO, selectquery->rtable)->eref->aliasname,
626                    "*OLD*") == 0 &&
627         strcmp(rt_fetch(PRS2_NEW_VARNO, selectquery->rtable)->eref->aliasname,
628                    "*NEW*") == 0)
629         {
630                 if (subquery_ptr)
631                         *subquery_ptr = &(selectrte->subquery);
632                 return selectquery;
633         }
634         elog(ERROR, "getInsertSelectQuery: can't find rule placeholders");
635         return NULL;                            /* not reached */
636 }
637
638
639 /*
640  * Add the given qualifier condition to the query's WHERE clause
641  */
642 void
643 AddQual(Query *parsetree, Node *qual)
644 {
645         Node       *copy;
646
647         if (qual == NULL)
648                 return;
649
650         if (parsetree->commandType == CMD_UTILITY)
651         {
652                 /*
653                  * There's noplace to put the qual on a utility statement.
654                  *
655                  * If it's a NOTIFY, silently ignore the qual; this means that the
656                  * NOTIFY will execute, whether or not there are any qualifying
657                  * rows. While clearly wrong, this is much more useful than
658                  * refusing to execute the rule at all, and extra NOTIFY events
659                  * are harmless for typical uses of NOTIFY.
660                  *
661                  * If it isn't a NOTIFY, error out, since unconditional execution of
662                  * other utility stmts is unlikely to be wanted.  (This case is
663                  * not currently allowed anyway, but keep the test for safety.)
664                  */
665                 if (parsetree->utilityStmt && IsA(parsetree->utilityStmt, NotifyStmt))
666                         return;
667                 else
668                         elog(ERROR, "Conditional utility statements are not implemented");
669         }
670
671         /* INTERSECT want's the original, but we need to copy - Jan */
672         copy = copyObject(qual);
673
674         parsetree->jointree->quals = make_and_qual(parsetree->jointree->quals,
675                                                                                            copy);
676
677         /*
678          * Make sure query is marked correctly if added qual has sublinks or
679          * aggregates (not sure it can ever have aggs, but sublinks
680          * definitely).
681          */
682         parsetree->hasAggs |= checkExprHasAggs(copy);
683         parsetree->hasSubLinks |= checkExprHasSubLink(copy);
684 }
685
686 /*
687  * Add the given havingQual to the one already contained in the parsetree
688  * just as AddQual does for the normal 'where' qual
689  */
690 void
691 AddHavingQual(Query *parsetree, Node *havingQual)
692 {
693         Node       *copy;
694
695         if (havingQual == NULL)
696                 return;
697
698         if (parsetree->commandType == CMD_UTILITY)
699         {
700                 /*
701                  * There's noplace to put the qual on a utility statement.
702                  *
703                  * See comments in AddQual for motivation.
704                  */
705                 if (parsetree->utilityStmt && IsA(parsetree->utilityStmt, NotifyStmt))
706                         return;
707                 else
708                         elog(ERROR, "Conditional utility statements are not implemented");
709         }
710
711         /* INTERSECT want's the original, but we need to copy - Jan */
712         copy = copyObject(havingQual);
713
714         parsetree->havingQual = make_and_qual(parsetree->havingQual,
715                                                                                   copy);
716
717         /*
718          * Make sure query is marked correctly if added qual has sublinks or
719          * aggregates (not sure it can ever have aggs, but sublinks
720          * definitely).
721          */
722         parsetree->hasAggs |= checkExprHasAggs(copy);
723         parsetree->hasSubLinks |= checkExprHasSubLink(copy);
724 }
725
726
727 /*
728  * Invert the given clause and add it to the WHERE qualifications of the
729  * given querytree.  Inversion means "x IS NOT TRUE", not just "NOT x",
730  * else we will do the wrong thing when x evaluates to NULL.
731  */
732 void
733 AddInvertedQual(Query *parsetree, Node *qual)
734 {
735         BooleanTest *invqual;
736
737         if (qual == NULL)
738                 return;
739
740         /* Need not copy input qual, because AddQual will... */
741         invqual = makeNode(BooleanTest);
742         invqual->arg = (Expr *) qual;
743         invqual->booltesttype = IS_NOT_TRUE;
744
745         AddQual(parsetree, (Node *) invqual);
746 }
747
748
749 /* Find a targetlist entry by resno */
750 static Node *
751 FindMatchingNew(List *tlist, int attno)
752 {
753         List       *i;
754
755         foreach(i, tlist)
756         {
757                 TargetEntry *tle = lfirst(i);
758
759                 if (tle->resdom->resno == attno)
760                         return (Node *) tle->expr;
761         }
762         return NULL;
763 }
764
765 #ifdef NOT_USED
766
767 /* Find a targetlist entry by resname */
768 static Node *
769 FindMatchingTLEntry(List *tlist, char *e_attname)
770 {
771         List       *i;
772
773         foreach(i, tlist)
774         {
775                 TargetEntry *tle = lfirst(i);
776                 char       *resname;
777
778                 resname = tle->resdom->resname;
779                 if (strcmp(e_attname, resname) == 0)
780                         return tle->expr;
781         }
782         return NULL;
783 }
784 #endif
785
786
787 /*
788  * ResolveNew - replace Vars with corresponding items from a targetlist
789  *
790  * Vars matching target_varno and sublevels_up are replaced by the
791  * entry with matching resno from targetlist, if there is one.
792  * If not, we either change the unmatched Var's varno to update_varno
793  * (when event == CMD_UPDATE) or replace it with a constant NULL.
794  */
795
796 typedef struct
797 {
798         int                     target_varno;
799         int                     sublevels_up;
800         List       *targetlist;
801         int                     event;
802         int                     update_varno;
803 } ResolveNew_context;
804
805 static Node *
806 ResolveNew_mutator(Node *node, ResolveNew_context *context)
807 {
808         if (node == NULL)
809                 return NULL;
810         if (IsA(node, Var))
811         {
812                 Var                *var = (Var *) node;
813                 int                     this_varno = (int) var->varno;
814                 int                     this_varlevelsup = (int) var->varlevelsup;
815
816                 if (this_varno == context->target_varno &&
817                         this_varlevelsup == context->sublevels_up)
818                 {
819                         Node       *n;
820
821                         /* band-aid: don't do the wrong thing with a whole-tuple Var */
822                         if (var->varattno == InvalidAttrNumber)
823                                 elog(ERROR, "ResolveNew: can't handle whole-tuple reference");
824
825                         n = FindMatchingNew(context->targetlist, var->varattno);
826
827                         if (n == NULL)
828                         {
829                                 if (context->event == CMD_UPDATE)
830                                 {
831                                         /* For update, just change unmatched var's varno */
832                                         var = (Var *) copyObject(node);
833                                         var->varno = context->update_varno;
834                                         var->varnoold = context->update_varno;
835                                         return (Node *) var;
836                                 }
837                                 else
838                                 {
839                                         /* Otherwise replace unmatched var with a null */
840                                         return (Node *) makeNullConst(var->vartype);
841                                 }
842                         }
843                         else
844                         {
845                                 /* Make a copy of the tlist item to return */
846                                 n = copyObject(n);
847                                 /* Adjust varlevelsup if tlist item is from higher query */
848                                 if (this_varlevelsup > 0)
849                                         IncrementVarSublevelsUp(n, this_varlevelsup, 0);
850                                 return n;
851                         }
852                 }
853                 /* otherwise fall through to copy the var normally */
854         }
855
856         if (IsA(node, Query))
857         {
858                 /* Recurse into RTE subquery or not-yet-planned sublink subquery */
859                 Query      *newnode;
860
861                 context->sublevels_up++;
862                 newnode = query_tree_mutator((Query *) node,
863                                                                          ResolveNew_mutator,
864                                                                          (void *) context,
865                                                                          0);
866                 context->sublevels_up--;
867                 return (Node *) newnode;
868         }
869         return expression_tree_mutator(node, ResolveNew_mutator,
870                                                                    (void *) context);
871 }
872
873 Node *
874 ResolveNew(Node *node, int target_varno, int sublevels_up,
875                    List *targetlist, int event, int update_varno)
876 {
877         ResolveNew_context context;
878
879         context.target_varno = target_varno;
880         context.sublevels_up = sublevels_up;
881         context.targetlist = targetlist;
882         context.event = event;
883         context.update_varno = update_varno;
884
885         /*
886          * Must be prepared to start with a Query or a bare expression tree;
887          * if it's a Query, we don't want to increment sublevels_up.
888          */
889         return query_or_expression_tree_mutator(node,
890                                                                                         ResolveNew_mutator,
891                                                                                         (void *) &context,
892                                                                                         0);
893 }
894
895
896 #ifdef NOT_USED
897
898 /*
899  * HandleRIRAttributeRule
900  *      Replace Vars matching a given RT index with copies of TL expressions.
901  *
902  * Handles 'on retrieve to relation.attribute
903  *                      do instead retrieve (attribute = expression) w/qual'
904  */
905
906 typedef struct
907 {
908         List       *rtable;
909         List       *targetlist;
910         int                     rt_index;
911         int                     attr_num;
912         int                *modified;
913         int                *badsql;
914         int                     sublevels_up;
915 }       HandleRIRAttributeRule_context;
916
917 static Node *
918 HandleRIRAttributeRule_mutator(Node *node,
919                                                            HandleRIRAttributeRule_context * context)
920 {
921         if (node == NULL)
922                 return NULL;
923         if (IsA(node, Var))
924         {
925                 Var                *var = (Var *) node;
926                 int                     this_varno = var->varno;
927                 int                     this_varattno = var->varattno;
928                 int                     this_varlevelsup = var->varlevelsup;
929
930                 if (this_varno == context->rt_index &&
931                         this_varattno == context->attr_num &&
932                         this_varlevelsup == context->sublevels_up)
933                 {
934                         if (var->vartype == 32)
935                         {                                       /* HACK: disallow SET variables */
936                                 *context->modified = TRUE;
937                                 *context->badsql = TRUE;
938                                 return (Node *) makeNullConst(var->vartype);
939                         }
940                         else
941                         {
942                                 char       *name_to_look_for;
943
944                                 name_to_look_for = get_attname(getrelid(this_varno,
945                                                                                                                 context->rtable),
946                                                                                            this_varattno);
947                                 if (name_to_look_for)
948                                 {
949                                         Node       *n;
950
951                                         *context->modified = TRUE;
952                                         n = FindMatchingTLEntry(context->targetlist,
953                                                                                         name_to_look_for);
954                                         if (n == NULL)
955                                                 return (Node *) makeNullConst(var->vartype);
956                                         /* Make a copy of the tlist item to return */
957                                         n = copyObject(n);
958
959                                         /*
960                                          * Adjust varlevelsup if tlist item is from higher
961                                          * query
962                                          */
963                                         if (this_varlevelsup > 0)
964                                                 IncrementVarSublevelsUp(n, this_varlevelsup, 0);
965                                         return n;
966                                 }
967                         }
968                 }
969                 /* otherwise fall through to copy the var normally */
970         }
971
972         if (IsA(node, Query))
973         {
974                 /* Recurse into RTE subquery or not-yet-planned sublink subquery */
975                 Query      *newnode;
976
977                 context->sublevels_up++;
978                 newnode = query_tree_mutator((Query *) node,
979                                                                          HandleRIRAttributeRule_mutator,
980                                                                          (void *) context,
981                                                                          0);
982                 context->sublevels_up--;
983                 return (Node *) newnode;
984         }
985         return expression_tree_mutator(node, HandleRIRAttributeRule_mutator,
986                                                                    (void *) context);
987 }
988
989 void
990 HandleRIRAttributeRule(Query *parsetree,
991                                            List *rtable,
992                                            List *targetlist,
993                                            int rt_index,
994                                            int attr_num,
995                                            int *modified,
996                                            int *badsql)
997 {
998         HandleRIRAttributeRule_context context;
999
1000         context.rtable = rtable;
1001         context.targetlist = targetlist;
1002         context.rt_index = rt_index;
1003         context.attr_num = attr_num;
1004         context.modified = modified;
1005         context.badsql = badsql;
1006         context.sublevels_up = 0;
1007
1008         query_tree_mutator(parsetree,
1009                                            HandleRIRAttributeRule_mutator,
1010                                            (void *) &context,
1011                                            QTW_DONT_COPY_QUERY);
1012 }
1013
1014 #endif   /* NOT_USED */