]> granicus.if.org Git - postgresql/blobdiff - src/backend/rewrite/rewriteManip.c
Implement SEMI and ANTI joins in the planner and executor. (Semijoins replace
[postgresql] / src / backend / rewrite / rewriteManip.c
index 0d3dbe7d6e6b9ecc43e4fed0929cd9d098f400d8..60492fe3d091fa52b349f5190a082531ec5277ea 100644 (file)
@@ -2,24 +2,24 @@
  *
  * rewriteManip.c
  *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.72 2003/06/06 15:04:02 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.108 2008/08/14 18:47:59 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
+#include "catalog/pg_type.h"
 #include "nodes/makefuncs.h"
 #include "optimizer/clauses.h"
-#include "optimizer/tlist.h"
+#include "parser/parse_coerce.h"
+#include "parser/parse_relation.h"
 #include "parser/parsetree.h"
-#include "parser/parse_clause.h"
 #include "rewrite/rewriteManip.h"
-#include "utils/lsyscache.h"
 
 
 typedef struct
@@ -28,7 +28,7 @@ typedef struct
 } checkExprHasAggs_context;
 
 static bool checkExprHasAggs_walker(Node *node,
-                                                                       checkExprHasAggs_context *context);
+                                               checkExprHasAggs_context *context);
 static bool checkExprHasSubLink_walker(Node *node, void *context);
 static Relids offset_relid_set(Relids relids, int offset);
 static Relids adjust_relid_set(Relids relids, int oldrelid, int newrelid);
@@ -36,8 +36,7 @@ static Relids adjust_relid_set(Relids relids, int oldrelid, int newrelid);
 
 /*
  * checkExprHasAggs -
- *     Queries marked hasAggs might not have them any longer after
- *     rewriting. Check it.
+ *     Check if an expression contains an aggregate function call.
  *
  * The objective of this routine is to detect whether there are aggregates
  * belonging to the initial query level.  Aggregates belonging to subqueries
@@ -51,9 +50,10 @@ checkExprHasAggs(Node *node)
        checkExprHasAggs_context context;
 
        context.sublevels_up = 0;
+
        /*
-        * Must be prepared to start with a Query or a bare expression tree;
-        * if it's a Query, we don't want to increment sublevels_up.
+        * Must be prepared to start with a Query or a bare expression tree; if
+        * it's a Query, we don't want to increment sublevels_up.
         */
        return query_or_expression_tree_walker(node,
                                                                                   checkExprHasAggs_walker,
@@ -69,8 +69,7 @@ checkExprHasAggs_walker(Node *node, checkExprHasAggs_context *context)
        if (IsA(node, Aggref))
        {
                if (((Aggref *) node)->agglevelsup == context->sublevels_up)
-                       return true;            /* abort the tree traversal and return
-                                                                * true */
+                       return true;            /* abort the tree traversal and return true */
                /* else fall through to examine argument */
        }
        if (IsA(node, Query))
@@ -91,14 +90,13 @@ checkExprHasAggs_walker(Node *node, checkExprHasAggs_context *context)
 
 /*
  * checkExprHasSubLink -
- *     Queries marked hasSubLinks might not have them any longer after
- *     rewriting. Check it.
+ *     Check if an expression contains a SubLink.
  */
 bool
 checkExprHasSubLink(Node *node)
 {
        /*
-        * If a Query is passed, examine it --- but we will not recurse into
+        * If a Query is passed, examine it --- but we need not recurse into
         * sub-Queries.
         */
        return query_or_expression_tree_walker(node,
@@ -113,8 +111,7 @@ checkExprHasSubLink_walker(Node *node, void *context)
        if (node == NULL)
                return false;
        if (IsA(node, SubLink))
-               return true;                    /* abort the tree traversal and return
-                                                                * true */
+               return true;                    /* abort the tree traversal and return true */
        return expression_tree_walker(node, checkExprHasSubLink_walker, context);
 }
 
@@ -154,6 +151,14 @@ OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context)
                }
                return false;
        }
+       if (IsA(node, CurrentOfExpr))
+       {
+               CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
+
+               if (context->sublevels_up == 0)
+                       cexpr->cvarno += context->offset;
+               return false;
+       }
        if (IsA(node, RangeTblRef))
        {
                RangeTblRef *rtr = (RangeTblRef *) node;
@@ -171,19 +176,30 @@ OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context)
                        j->rtindex += context->offset;
                /* fall through to examine children */
        }
-       if (IsA(node, InClauseInfo))
+       if (IsA(node, FlattenedSubLink))
        {
-               InClauseInfo   *ininfo = (InClauseInfo *) node;
+               FlattenedSubLink *fslink = (FlattenedSubLink *) node;
 
                if (context->sublevels_up == 0)
                {
-                       ininfo->lefthand = offset_relid_set(ininfo->lefthand,
+                       fslink->lefthand = offset_relid_set(fslink->lefthand,
                                                                                                context->offset);
-                       ininfo->righthand = offset_relid_set(ininfo->righthand,
+                       fslink->righthand = offset_relid_set(fslink->righthand,
                                                                                                 context->offset);
                }
                /* fall through to examine children */
        }
+       if (IsA(node, AppendRelInfo))
+       {
+               AppendRelInfo *appinfo = (AppendRelInfo *) node;
+
+               if (context->sublevels_up == 0)
+               {
+                       appinfo->parent_relid += context->offset;
+                       appinfo->child_relid += context->offset;
+               }
+               /* fall through to examine children */
+       }
        if (IsA(node, Query))
        {
                /* Recurse into subselects */
@@ -208,28 +224,33 @@ OffsetVarNodes(Node *node, int offset, int sublevels_up)
        context.sublevels_up = sublevels_up;
 
        /*
-        * Must be prepared to start with a Query or a bare expression tree;
-        * if it's a Query, go straight to query_tree_walker to make sure that
+        * Must be prepared to start with a Query or a bare expression tree; if
+        * it's a Query, go straight to query_tree_walker to make sure that
         * sublevels_up doesn't get incremented prematurely.
         */
        if (node && IsA(node, Query))
        {
                Query      *qry = (Query *) node;
-               List       *l;
 
                /*
-                * If we are starting at a Query, and sublevels_up is zero, then
-                * we must also fix rangetable indexes in the Query itself ---
-                * namely resultRelation and rowMarks entries.  sublevels_up
-                * cannot be zero when recursing into a subquery, so there's no
-                * need to have the same logic inside OffsetVarNodes_walker.
+                * If we are starting at a Query, and sublevels_up is zero, then we
+                * must also fix rangetable indexes in the Query itself --- namely
+                * resultRelation and rowMarks entries.  sublevels_up cannot be zero
+                * when recursing into a subquery, so there's no need to have the same
+                * logic inside OffsetVarNodes_walker.
                 */
                if (sublevels_up == 0)
                {
+                       ListCell   *l;
+
                        if (qry->resultRelation)
                                qry->resultRelation += offset;
                        foreach(l, qry->rowMarks)
-                               lfirsti(l) += offset;
+                       {
+                               RowMarkClause *rc = (RowMarkClause *) lfirst(l);
+
+                               rc->rti += offset;
+                       }
                }
                query_tree_walker(qry, OffsetVarNodes_walker,
                                                  (void *) &context, 0);
@@ -247,9 +268,7 @@ offset_relid_set(Relids relids, int offset)
 
        tmprelids = bms_copy(relids);
        while ((rtindex = bms_first_member(tmprelids)) >= 0)
-       {
                result = bms_add_member(result, rtindex + offset);
-       }
        bms_free(tmprelids);
        return result;
 }
@@ -291,6 +310,15 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
                }
                return false;
        }
+       if (IsA(node, CurrentOfExpr))
+       {
+               CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
+
+               if (context->sublevels_up == 0 &&
+                       cexpr->cvarno == context->rt_index)
+                       cexpr->cvarno = context->new_index;
+               return false;
+       }
        if (IsA(node, RangeTblRef))
        {
                RangeTblRef *rtr = (RangeTblRef *) node;
@@ -310,21 +338,34 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
                        j->rtindex = context->new_index;
                /* fall through to examine children */
        }
-       if (IsA(node, InClauseInfo))
+       if (IsA(node, FlattenedSubLink))
        {
-               InClauseInfo   *ininfo = (InClauseInfo *) node;
+               FlattenedSubLink *fslink = (FlattenedSubLink *) node;
 
                if (context->sublevels_up == 0)
                {
-                       ininfo->lefthand = adjust_relid_set(ininfo->lefthand,
+                       fslink->lefthand = adjust_relid_set(fslink->lefthand,
                                                                                                context->rt_index,
                                                                                                context->new_index);
-                       ininfo->righthand = adjust_relid_set(ininfo->righthand,
+                       fslink->righthand = adjust_relid_set(fslink->righthand,
                                                                                                 context->rt_index,
                                                                                                 context->new_index);
                }
                /* fall through to examine children */
        }
+       if (IsA(node, AppendRelInfo))
+       {
+               AppendRelInfo *appinfo = (AppendRelInfo *) node;
+
+               if (context->sublevels_up == 0)
+               {
+                       if (appinfo->parent_relid == context->rt_index)
+                               appinfo->parent_relid = context->new_index;
+                       if (appinfo->child_relid == context->rt_index)
+                               appinfo->child_relid = context->new_index;
+               }
+               /* fall through to examine children */
+       }
        if (IsA(node, Query))
        {
                /* Recurse into subselects */
@@ -350,30 +391,33 @@ ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up)
        context.sublevels_up = sublevels_up;
 
        /*
-        * Must be prepared to start with a Query or a bare expression tree;
-        * if it's a Query, go straight to query_tree_walker to make sure that
+        * Must be prepared to start with a Query or a bare expression tree; if
+        * it's a Query, go straight to query_tree_walker to make sure that
         * sublevels_up doesn't get incremented prematurely.
         */
        if (node && IsA(node, Query))
        {
                Query      *qry = (Query *) node;
-               List       *l;
 
                /*
-                * If we are starting at a Query, and sublevels_up is zero, then
-                * we must also fix rangetable indexes in the Query itself ---
-                * namely resultRelation and rowMarks entries.  sublevels_up
-                * cannot be zero when recursing into a subquery, so there's no
-                * need to have the same logic inside ChangeVarNodes_walker.
+                * If we are starting at a Query, and sublevels_up is zero, then we
+                * must also fix rangetable indexes in the Query itself --- namely
+                * resultRelation and rowMarks entries.  sublevels_up cannot be zero
+                * when recursing into a subquery, so there's no need to have the same
+                * logic inside ChangeVarNodes_walker.
                 */
                if (sublevels_up == 0)
                {
+                       ListCell   *l;
+
                        if (qry->resultRelation == rt_index)
                                qry->resultRelation = new_index;
                        foreach(l, qry->rowMarks)
                        {
-                               if (lfirsti(l) == rt_index)
-                                       lfirsti(l) = new_index;
+                               RowMarkClause *rc = (RowMarkClause *) lfirst(l);
+
+                               if (rc->rti == rt_index)
+                                       rc->rti = new_index;
                        }
                }
                query_tree_walker(qry, ChangeVarNodes_walker,
@@ -439,6 +483,13 @@ IncrementVarSublevelsUp_walker(Node *node,
                        var->varlevelsup += context->delta_sublevels_up;
                return false;                   /* done here */
        }
+       if (IsA(node, CurrentOfExpr))
+       {
+               /* this should not happen */
+               if (context->min_sublevels_up == 0)
+                       elog(ERROR, "cannot push down CurrentOfExpr");
+               return false;
+       }
        if (IsA(node, Aggref))
        {
                Aggref     *agg = (Aggref *) node;
@@ -473,8 +524,8 @@ IncrementVarSublevelsUp(Node *node, int delta_sublevels_up,
        context.min_sublevels_up = min_sublevels_up;
 
        /*
-        * Must be prepared to start with a Query or a bare expression tree;
-        * if it's a Query, we don't want to increment sublevels_up.
+        * Must be prepared to start with a Query or a bare expression tree; if
+        * it's a Query, we don't want to increment sublevels_up.
         */
        query_or_expression_tree_walker(node,
                                                                        IncrementVarSublevelsUp_walker,
@@ -509,6 +560,15 @@ rangeTableEntry_used_walker(Node *node,
                        return true;
                return false;
        }
+       if (IsA(node, CurrentOfExpr))
+       {
+               CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
+
+               if (context->sublevels_up == 0 &&
+                       cexpr->cvarno == context->rt_index)
+                       return true;
+               return false;
+       }
        if (IsA(node, RangeTblRef))
        {
                RangeTblRef *rtr = (RangeTblRef *) node;
@@ -528,16 +588,11 @@ rangeTableEntry_used_walker(Node *node,
                        return true;
                /* fall through to examine children */
        }
-       if (IsA(node, InClauseInfo))
-       {
-               InClauseInfo   *ininfo = (InClauseInfo *) node;
+       /* Shouldn't need to handle planner auxiliary nodes here */
+       Assert(!IsA(node, FlattenedSubLink));
+       Assert(!IsA(node, SpecialJoinInfo));
+       Assert(!IsA(node, AppendRelInfo));
 
-               if (context->sublevels_up == 0 &&
-                       (bms_is_member(context->rt_index, ininfo->lefthand) ||
-                        bms_is_member(context->rt_index, ininfo->righthand)))
-                       return true;
-               /* fall through to examine children */
-       }
        if (IsA(node, Query))
        {
                /* Recurse into subselects */
@@ -562,8 +617,8 @@ rangeTableEntry_used(Node *node, int rt_index, int sublevels_up)
        context.sublevels_up = sublevels_up;
 
        /*
-        * Must be prepared to start with a Query or a bare expression tree;
-        * if it's a Query, we don't want to increment sublevels_up.
+        * Must be prepared to start with a Query or a bare expression tree; if
+        * it's a Query, we don't want to increment sublevels_up.
         */
        return query_or_expression_tree_walker(node,
                                                                                   rangeTableEntry_used_walker,
@@ -626,8 +681,8 @@ attribute_used(Node *node, int rt_index, int attno, int sublevels_up)
        context.sublevels_up = sublevels_up;
 
        /*
-        * Must be prepared to start with a Query or a bare expression tree;
-        * if it's a Query, we don't want to increment sublevels_up.
+        * Must be prepared to start with a Query or a bare expression tree; if
+        * it's a Query, we don't want to increment sublevels_up.
         */
        return query_or_expression_tree_walker(node,
                                                                                   attribute_used_walker,
@@ -670,33 +725,33 @@ getInsertSelectQuery(Query *parsetree, Query ***subquery_ptr)
         * query.  If they're not there, it must be an INSERT/SELECT in which
         * they've been pushed down to the SELECT.
         */
-       if (length(parsetree->rtable) >= 2 &&
-        strcmp(rt_fetch(PRS2_OLD_VARNO, parsetree->rtable)->eref->aliasname,
-                       "*OLD*") == 0 &&
-        strcmp(rt_fetch(PRS2_NEW_VARNO, parsetree->rtable)->eref->aliasname,
-                       "*NEW*") == 0)
+       if (list_length(parsetree->rtable) >= 2 &&
+               strcmp(rt_fetch(PRS2_OLD_VARNO, parsetree->rtable)->eref->aliasname,
+                          "*OLD*") == 0 &&
+               strcmp(rt_fetch(PRS2_NEW_VARNO, parsetree->rtable)->eref->aliasname,
+                          "*NEW*") == 0)
                return parsetree;
        Assert(parsetree->jointree && IsA(parsetree->jointree, FromExpr));
-       if (length(parsetree->jointree->fromlist) != 1)
-               elog(ERROR, "getInsertSelectQuery: expected to find SELECT subquery");
-       rtr = (RangeTblRef *) lfirst(parsetree->jointree->fromlist);
+       if (list_length(parsetree->jointree->fromlist) != 1)
+               elog(ERROR, "expected to find SELECT subquery");
+       rtr = (RangeTblRef *) linitial(parsetree->jointree->fromlist);
        Assert(IsA(rtr, RangeTblRef));
        selectrte = rt_fetch(rtr->rtindex, parsetree->rtable);
        selectquery = selectrte->subquery;
        if (!(selectquery && IsA(selectquery, Query) &&
                  selectquery->commandType == CMD_SELECT))
-               elog(ERROR, "getInsertSelectQuery: expected to find SELECT subquery");
-       if (length(selectquery->rtable) >= 2 &&
-       strcmp(rt_fetch(PRS2_OLD_VARNO, selectquery->rtable)->eref->aliasname,
-                  "*OLD*") == 0 &&
-       strcmp(rt_fetch(PRS2_NEW_VARNO, selectquery->rtable)->eref->aliasname,
-                  "*NEW*") == 0)
+               elog(ERROR, "expected to find SELECT subquery");
+       if (list_length(selectquery->rtable) >= 2 &&
+               strcmp(rt_fetch(PRS2_OLD_VARNO, selectquery->rtable)->eref->aliasname,
+                          "*OLD*") == 0 &&
+               strcmp(rt_fetch(PRS2_NEW_VARNO, selectquery->rtable)->eref->aliasname,
+                          "*NEW*") == 0)
        {
                if (subquery_ptr)
                        *subquery_ptr = &(selectrte->subquery);
                return selectquery;
        }
-       elog(ERROR, "getInsertSelectQuery: can't find rule placeholders");
+       elog(ERROR, "could not find rule placeholders");
        return NULL;                            /* not reached */
 }
 
@@ -718,19 +773,33 @@ AddQual(Query *parsetree, Node *qual)
                 * There's noplace to put the qual on a utility statement.
                 *
                 * If it's a NOTIFY, silently ignore the qual; this means that the
-                * NOTIFY will execute, whether or not there are any qualifying
-                * rows. While clearly wrong, this is much more useful than
-                * refusing to execute the rule at all, and extra NOTIFY events
-                * are harmless for typical uses of NOTIFY.
+                * NOTIFY will execute, whether or not there are any qualifying rows.
+                * While clearly wrong, this is much more useful than refusing to
+                * execute the rule at all, and extra NOTIFY events are harmless for
+                * typical uses of NOTIFY.
                 *
                 * If it isn't a NOTIFY, error out, since unconditional execution of
-                * other utility stmts is unlikely to be wanted.  (This case is
-                * not currently allowed anyway, but keep the test for safety.)
+                * other utility stmts is unlikely to be wanted.  (This case is not
+                * currently allowed anyway, but keep the test for safety.)
                 */
                if (parsetree->utilityStmt && IsA(parsetree->utilityStmt, NotifyStmt))
                        return;
                else
-                       elog(ERROR, "Conditional utility statements are not implemented");
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                         errmsg("conditional utility statements are not implemented")));
+       }
+
+       if (parsetree->setOperations != NULL)
+       {
+               /*
+                * There's noplace to put the qual on a setop statement, either. (This
+                * could be fixed, but right now the planner simply ignores any qual
+                * condition on a setop query.)
+                */
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
        }
 
        /* INTERSECT want's the original, but we need to copy - Jan */
@@ -740,52 +809,16 @@ AddQual(Query *parsetree, Node *qual)
                                                                                           copy);
 
        /*
-        * Make sure query is marked correctly if added qual has sublinks or
-        * aggregates (not sure it can ever have aggs, but sublinks
-        * definitely).
+        * We had better not have stuck an aggregate into the WHERE clause.
         */
-       parsetree->hasAggs |= checkExprHasAggs(copy);
-       parsetree->hasSubLinks |= checkExprHasSubLink(copy);
-}
-
-/*
- * Add the given havingQual to the one already contained in the parsetree
- * just as AddQual does for the normal 'where' qual
- */
-void
-AddHavingQual(Query *parsetree, Node *havingQual)
-{
-       Node       *copy;
-
-       if (havingQual == NULL)
-               return;
-
-       if (parsetree->commandType == CMD_UTILITY)
-       {
-               /*
-                * There's noplace to put the qual on a utility statement.
-                *
-                * See comments in AddQual for motivation.
-                */
-               if (parsetree->utilityStmt && IsA(parsetree->utilityStmt, NotifyStmt))
-                       return;
-               else
-                       elog(ERROR, "Conditional utility statements are not implemented");
-       }
-
-       /* INTERSECT want's the original, but we need to copy - Jan */
-       copy = copyObject(havingQual);
-
-       parsetree->havingQual = make_and_qual(parsetree->havingQual,
-                                                                                 copy);
+       Assert(!checkExprHasAggs(copy));
 
        /*
-        * Make sure query is marked correctly if added qual has sublinks or
-        * aggregates (not sure it can ever have aggs, but sublinks
-        * definitely).
+        * Make sure query is marked correctly if added qual has sublinks. Need
+        * not search qual when query is already marked.
         */
-       parsetree->hasAggs |= checkExprHasAggs(copy);
-       parsetree->hasSubLinks |= checkExprHasSubLink(copy);
+       if (!parsetree->hasSubLinks)
+               parsetree->hasSubLinks = checkExprHasSubLink(copy);
 }
 
 
@@ -811,44 +844,6 @@ AddInvertedQual(Query *parsetree, Node *qual)
 }
 
 
-/* Find a targetlist entry by resno */
-static Node *
-FindMatchingNew(List *tlist, int attno)
-{
-       List       *i;
-
-       foreach(i, tlist)
-       {
-               TargetEntry *tle = lfirst(i);
-
-               if (tle->resdom->resno == attno)
-                       return (Node *) tle->expr;
-       }
-       return NULL;
-}
-
-#ifdef NOT_USED
-
-/* Find a targetlist entry by resname */
-static Node *
-FindMatchingTLEntry(List *tlist, char *e_attname)
-{
-       List       *i;
-
-       foreach(i, tlist)
-       {
-               TargetEntry *tle = lfirst(i);
-               char       *resname;
-
-               resname = tle->resdom->resname;
-               if (strcmp(e_attname, resname) == 0)
-                       return tle->expr;
-       }
-       return NULL;
-}
-#endif
-
-
 /*
  * ResolveNew - replace Vars with corresponding items from a targetlist
  *
@@ -856,17 +851,75 @@ FindMatchingTLEntry(List *tlist, char *e_attname)
  * entry with matching resno from targetlist, if there is one.
  * If not, we either change the unmatched Var's varno to update_varno
  * (when event == CMD_UPDATE) or replace it with a constant NULL.
+ *
+ * The caller must also provide target_rte, the RTE describing the target
+ * relation.  This is needed to handle whole-row Vars referencing the target.
+ * We expand such Vars into RowExpr constructs.
+ *
+ * Note: the business with inserted_sublink is needed to update hasSubLinks
+ * in subqueries when the replacement adds a subquery inside a subquery.
+ * Messy, isn't it?  We do not need to do similar pushups for hasAggs,
+ * because it isn't possible for this transformation to insert a level-zero
+ * aggregate reference into a subquery --- it could only insert outer aggs.
  */
 
 typedef struct
 {
        int                     target_varno;
        int                     sublevels_up;
+       RangeTblEntry *target_rte;
        List       *targetlist;
        int                     event;
        int                     update_varno;
+       bool            inserted_sublink;
 } ResolveNew_context;
 
+static Node *
+resolve_one_var(Var *var, ResolveNew_context *context)
+{
+       TargetEntry *tle;
+
+       tle = get_tle_by_resno(context->targetlist, var->varattno);
+
+       if (tle == NULL)
+       {
+               /* Failed to find column in insert/update tlist */
+               if (context->event == CMD_UPDATE)
+               {
+                       /* For update, just change unmatched var's varno */
+                       var = (Var *) copyObject(var);
+                       var->varno = context->update_varno;
+                       var->varnoold = context->update_varno;
+                       return (Node *) var;
+               }
+               else
+               {
+                       /* Otherwise replace unmatched var with a null */
+                       /* need coerce_to_domain in case of NOT NULL domain constraint */
+                       return coerce_to_domain((Node *) makeNullConst(var->vartype,
+                                                                                                                  var->vartypmod),
+                                                                       InvalidOid, -1,
+                                                                       var->vartype,
+                                                                       COERCE_IMPLICIT_CAST,
+                                                                       false,
+                                                                       false);
+               }
+       }
+       else
+       {
+               /* Make a copy of the tlist item to return */
+               Node       *n = copyObject(tle->expr);
+
+               /* Adjust varlevelsup if tlist item is from higher query */
+               if (var->varlevelsup > 0)
+                       IncrementVarSublevelsUp(n, var->varlevelsup, 0);
+               /* Report it if we are adding a sublink to query */
+               if (!context->inserted_sublink)
+                       context->inserted_sublink = checkExprHasSubLink(n);
+               return n;
+       }
+}
+
 static Node *
 ResolveNew_mutator(Node *node, ResolveNew_context *context)
 {
@@ -881,53 +934,73 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context)
                if (this_varno == context->target_varno &&
                        this_varlevelsup == context->sublevels_up)
                {
-                       Node       *n;
-
-                       /* band-aid: don't do the wrong thing with a whole-tuple Var */
                        if (var->varattno == InvalidAttrNumber)
-                               elog(ERROR, "ResolveNew: can't handle whole-tuple reference");
-
-                       n = FindMatchingNew(context->targetlist, var->varattno);
-
-                       if (n == NULL)
-                       {
-                               if (context->event == CMD_UPDATE)
-                               {
-                                       /* For update, just change unmatched var's varno */
-                                       var = (Var *) copyObject(node);
-                                       var->varno = context->update_varno;
-                                       var->varnoold = context->update_varno;
-                                       return (Node *) var;
-                               }
-                               else
-                               {
-                                       /* Otherwise replace unmatched var with a null */
-                                       return (Node *) makeNullConst(var->vartype);
-                               }
-                       }
-                       else
                        {
-                               /* Make a copy of the tlist item to return */
-                               n = copyObject(n);
-                               /* Adjust varlevelsup if tlist item is from higher query */
-                               if (this_varlevelsup > 0)
-                                       IncrementVarSublevelsUp(n, this_varlevelsup, 0);
-                               return n;
+                               /* Must expand whole-tuple reference into RowExpr */
+                               RowExpr    *rowexpr;
+                               List       *fields;
+
+                               /*
+                                * If generating an expansion for a var of a named rowtype
+                                * (ie, this is a plain relation RTE), then we must include
+                                * dummy items for dropped columns.  If the var is RECORD (ie,
+                                * this is a JOIN), then omit dropped columns.
+                                */
+                               expandRTE(context->target_rte,
+                                                 this_varno, this_varlevelsup,
+                                                 (var->vartype != RECORDOID),
+                                                 NULL, &fields);
+                               /* Adjust the generated per-field Vars... */
+                               fields = (List *) ResolveNew_mutator((Node *) fields,
+                                                                                                        context);
+                               rowexpr = makeNode(RowExpr);
+                               rowexpr->args = fields;
+                               rowexpr->row_typeid = var->vartype;
+                               rowexpr->row_format = COERCE_IMPLICIT_CAST;
+
+                               return (Node *) rowexpr;
                        }
+
+                       /* Normal case for scalar variable */
+                       return resolve_one_var(var, context);
                }
                /* otherwise fall through to copy the var normally */
        }
+       else if (IsA(node, CurrentOfExpr))
+       {
+               CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
+               int                     this_varno = (int) cexpr->cvarno;
 
-       if (IsA(node, Query))
+               if (this_varno == context->target_varno &&
+                       context->sublevels_up == 0)
+               {
+                       /*
+                        * We get here if a WHERE CURRENT OF expression turns out to apply
+                        * to a view.  Someday we might be able to translate the
+                        * expression to apply to an underlying table of the view, but
+                        * right now it's not implemented.
+                        */
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                  errmsg("WHERE CURRENT OF on a view is not implemented")));
+               }
+               /* otherwise fall through to copy the expr normally */
+       }
+       else if (IsA(node, Query))
        {
                /* Recurse into RTE subquery or not-yet-planned sublink subquery */
                Query      *newnode;
+               bool            save_inserted_sublink;
 
                context->sublevels_up++;
+               save_inserted_sublink = context->inserted_sublink;
+               context->inserted_sublink = false;
                newnode = query_tree_mutator((Query *) node,
                                                                         ResolveNew_mutator,
                                                                         (void *) context,
                                                                         0);
+               newnode->hasSubLinks |= context->inserted_sublink;
+               context->inserted_sublink = save_inserted_sublink;
                context->sublevels_up--;
                return (Node *) newnode;
        }
@@ -937,143 +1010,40 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context)
 
 Node *
 ResolveNew(Node *node, int target_varno, int sublevels_up,
+                  RangeTblEntry *target_rte,
                   List *targetlist, int event, int update_varno)
 {
+       Node       *result;
        ResolveNew_context context;
 
        context.target_varno = target_varno;
        context.sublevels_up = sublevels_up;
+       context.target_rte = target_rte;
        context.targetlist = targetlist;
        context.event = event;
        context.update_varno = update_varno;
+       context.inserted_sublink = false;
 
        /*
-        * Must be prepared to start with a Query or a bare expression tree;
-        * if it's a Query, we don't want to increment sublevels_up.
+        * Must be prepared to start with a Query or a bare expression tree; if
+        * it's a Query, we don't want to increment sublevels_up.
         */
-       return query_or_expression_tree_mutator(node,
-                                                                                       ResolveNew_mutator,
-                                                                                       (void *) &context,
-                                                                                       0);
-}
-
-
-#ifdef NOT_USED
-
-/*
- * HandleRIRAttributeRule
- *     Replace Vars matching a given RT index with copies of TL expressions.
- *
- * Handles 'on retrieve to relation.attribute
- *                     do instead retrieve (attribute = expression) w/qual'
- */
-
-typedef struct
-{
-       List       *rtable;
-       List       *targetlist;
-       int                     rt_index;
-       int                     attr_num;
-       int                *modified;
-       int                *badsql;
-       int                     sublevels_up;
-}      HandleRIRAttributeRule_context;
-
-static Node *
-HandleRIRAttributeRule_mutator(Node *node,
-                                                          HandleRIRAttributeRule_context * context)
-{
-       if (node == NULL)
-               return NULL;
-       if (IsA(node, Var))
-       {
-               Var                *var = (Var *) node;
-               int                     this_varno = var->varno;
-               int                     this_varattno = var->varattno;
-               int                     this_varlevelsup = var->varlevelsup;
-
-               if (this_varno == context->rt_index &&
-                       this_varattno == context->attr_num &&
-                       this_varlevelsup == context->sublevels_up)
-               {
-                       if (var->vartype == 32)
-                       {                                       /* HACK: disallow SET variables */
-                               *context->modified = TRUE;
-                               *context->badsql = TRUE;
-                               return (Node *) makeNullConst(var->vartype);
-                       }
-                       else
-                       {
-                               char       *name_to_look_for;
-
-                               name_to_look_for = get_attname(getrelid(this_varno,
-                                                                                                               context->rtable),
-                                                                                          this_varattno);
-                               if (name_to_look_for)
-                               {
-                                       Node       *n;
-
-                                       *context->modified = TRUE;
-                                       n = FindMatchingTLEntry(context->targetlist,
-                                                                                       name_to_look_for);
-                                       if (n == NULL)
-                                               return (Node *) makeNullConst(var->vartype);
-                                       /* Make a copy of the tlist item to return */
-                                       n = copyObject(n);
-
-                                       /*
-                                        * Adjust varlevelsup if tlist item is from higher
-                                        * query
-                                        */
-                                       if (this_varlevelsup > 0)
-                                               IncrementVarSublevelsUp(n, this_varlevelsup, 0);
-                                       return n;
-                               }
-                       }
-               }
-               /* otherwise fall through to copy the var normally */
-       }
+       result = query_or_expression_tree_mutator(node,
+                                                                                         ResolveNew_mutator,
+                                                                                         (void *) &context,
+                                                                                         0);
 
-       if (IsA(node, Query))
+       if (context.inserted_sublink)
        {
-               /* Recurse into RTE subquery or not-yet-planned sublink subquery */
-               Query      *newnode;
+               if (IsA(result, Query))
+                       ((Query *) result)->hasSubLinks = true;
 
-               context->sublevels_up++;
-               newnode = query_tree_mutator((Query *) node,
-                                                                        HandleRIRAttributeRule_mutator,
-                                                                        (void *) context,
-                                                                        0);
-               context->sublevels_up--;
-               return (Node *) newnode;
+               /*
+                * Note: if we're called on a non-Query node then it's the caller's
+                * responsibility to update hasSubLinks in the ancestor Query. This is
+                * pretty fragile and perhaps should be rethought ...
+                */
        }
-       return expression_tree_mutator(node, HandleRIRAttributeRule_mutator,
-                                                                  (void *) context);
-}
-
-void
-HandleRIRAttributeRule(Query *parsetree,
-                                          List *rtable,
-                                          List *targetlist,
-                                          int rt_index,
-                                          int attr_num,
-                                          int *modified,
-                                          int *badsql)
-{
-       HandleRIRAttributeRule_context context;
 
-       context.rtable = rtable;
-       context.targetlist = targetlist;
-       context.rt_index = rt_index;
-       context.attr_num = attr_num;
-       context.modified = modified;
-       context.badsql = badsql;
-       context.sublevels_up = 0;
-
-       query_tree_mutator(parsetree,
-                                          HandleRIRAttributeRule_mutator,
-                                          (void *) &context,
-                                          QTW_DONT_COPY_QUERY);
+       return result;
 }
-
-#endif   /* NOT_USED */