Eliminate code duplication in replace_rte_variables callbacks
authorRichard Guo <[email protected]>
Tue, 25 Feb 2025 07:11:34 +0000 (16:11 +0900)
committerRichard Guo <[email protected]>
Tue, 25 Feb 2025 07:11:34 +0000 (16:11 +0900)
The callback functions ReplaceVarsFromTargetList_callback and
pullup_replace_vars_callback are both used to replace Vars in an
expression tree that reference a particular RTE with items from a
targetlist, and they both need to expand whole-tuple references and
deal with OLD/NEW RETURNING list Vars.  As a result, currently there
is significant code duplication between these two functions.

This patch introduces a new function, ReplaceVarFromTargetList, to
perform the replacement and calls it from both callback functions,
thereby eliminating code duplication.

Author: Dean Rasheed <[email protected]>
Author: Richard Guo <[email protected]>
Reviewed-by: Jian He <[email protected]>
Discussion: https://postgr.es/m/CAEZATCWhr=FM4X5kCPvVs-g2XEk+ceLsNtBK_zZMkqFn9vUjsw@mail.gmail.com

src/backend/optimizer/prep/prepjointree.c
src/backend/rewrite/rewriteManip.c
src/include/rewrite/rewriteManip.h

index 8cdacb6aa63b7da2293013f6ba64a72a61c3d1cb..bcc40dd5a84383028a4f9de9e6f5ef6894556202 100644 (file)
@@ -2666,131 +2666,38 @@ pullup_replace_vars_callback(Var *var,
        /* Just copy the entry and fall through to adjust phlevelsup etc */
        newnode = copyObject(rcon->rv_cache[varattno]);
    }
-   else if (varattno == InvalidAttrNumber)
+   else
    {
-       /* Must expand whole-tuple reference into RowExpr */
-       RowExpr    *rowexpr;
-       List       *colnames;
-       List       *fields;
-       bool        save_wrap_non_vars = rcon->wrap_non_vars;
-       int         save_sublevelsup = context->sublevels_up;
-
        /*
-        * 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.  In the latter case, attach column names to
-        * the RowExpr for use of the executor and ruleutils.c.
-        *
-        * In order to be able to cache the results, we always generate the
-        * expansion with varlevelsup = 0, and then adjust below if needed.
+        * Generate the replacement expression.  This takes care of expanding
+        * wholerow references and dealing with non-default varreturningtype.
         */
-       expandRTE(rcon->target_rte,
-                 var->varno, 0 /* not varlevelsup */ ,
-                 var->varreturningtype, var->location,
-                 (var->vartype != RECORDOID),
-                 &colnames, &fields);
-       /* Expand the generated per-field Vars, but don't insert PHVs there */
-       rcon->wrap_non_vars = false;
-       context->sublevels_up = 0;  /* to match the expandRTE output */
-       fields = (List *) replace_rte_variables_mutator((Node *) fields,
-                                                       context);
-       rcon->wrap_non_vars = save_wrap_non_vars;
-       context->sublevels_up = save_sublevelsup;
-
-       rowexpr = makeNode(RowExpr);
-       rowexpr->args = fields;
-       rowexpr->row_typeid = var->vartype;
-       rowexpr->row_format = COERCE_IMPLICIT_CAST;
-       rowexpr->colnames = (var->vartype == RECORDOID) ? colnames : NIL;
-       rowexpr->location = var->location;
-       newnode = (Node *) rowexpr;
-
-       /* Handle any OLD/NEW RETURNING list Vars */
-       if (var->varreturningtype != VAR_RETURNING_DEFAULT)
-       {
-           /*
-            * Wrap the RowExpr in a ReturningExpr node, so that the executor
-            * returns NULL if the OLD/NEW row does not exist.
-            */
-           ReturningExpr *rexpr = makeNode(ReturningExpr);
-
-           rexpr->retlevelsup = 0;
-           rexpr->retold = (var->varreturningtype == VAR_RETURNING_OLD);
-           rexpr->retexpr = (Expr *) newnode;
-
-           newnode = (Node *) rexpr;
-       }
-
-       /*
-        * Insert PlaceHolderVar if needed.  Notice that we are wrapping one
-        * PlaceHolderVar around the whole RowExpr, rather than putting one
-        * around each element of the row.  This is because we need the
-        * expression to yield NULL, not ROW(NULL,NULL,...) when it is forced
-        * to null by an outer join.
-        */
-       if (need_phv)
-       {
-           newnode = (Node *)
-               make_placeholder_expr(rcon->root,
-                                     (Expr *) newnode,
-                                     bms_make_singleton(rcon->varno));
-           /* cache it with the PHV, and with phlevelsup etc not set yet */
-           rcon->rv_cache[InvalidAttrNumber] = copyObject(newnode);
-       }
-   }
-   else
-   {
-       /* Normal case referencing one targetlist element */
-       TargetEntry *tle = get_tle_by_resno(rcon->targetlist, varattno);
-
-       if (tle == NULL)        /* shouldn't happen */
-           elog(ERROR, "could not find attribute %d in subquery targetlist",
-                varattno);
-
-       /* Make a copy of the tlist item to return */
-       newnode = (Node *) copyObject(tle->expr);
-
-       /* Handle any OLD/NEW RETURNING list Vars */
-       if (var->varreturningtype != VAR_RETURNING_DEFAULT)
-       {
-           /*
-            * Copy varreturningtype onto any Vars in the tlist item that
-            * refer to result_relation (which had better be non-zero).
-            */
-           if (rcon->result_relation == 0)
-               elog(ERROR, "variable returning old/new found outside RETURNING list");
-
-           SetVarReturningType((Node *) newnode, rcon->result_relation,
-                               0, var->varreturningtype);
-
-           /*
-            * If the replacement expression in the targetlist is not simply a
-            * Var referencing result_relation, wrap it in a ReturningExpr
-            * node, so that the executor returns NULL if the OLD/NEW row does
-            * not exist.
-            */
-           if (!IsA(newnode, Var) ||
-               ((Var *) newnode)->varno != rcon->result_relation ||
-               ((Var *) newnode)->varlevelsup != 0)
-           {
-               ReturningExpr *rexpr = makeNode(ReturningExpr);
-
-               rexpr->retlevelsup = 0;
-               rexpr->retold = (var->varreturningtype == VAR_RETURNING_OLD);
-               rexpr->retexpr = (Expr *) newnode;
-
-               newnode = (Node *) rexpr;
-           }
-       }
+       newnode = ReplaceVarFromTargetList(var,
+                                          rcon->target_rte,
+                                          rcon->targetlist,
+                                          rcon->result_relation,
+                                          REPLACEVARS_REPORT_ERROR,
+                                          0);
 
        /* Insert PlaceHolderVar if needed */
        if (need_phv)
        {
            bool        wrap;
 
-           if (newnode && IsA(newnode, Var) &&
-               ((Var *) newnode)->varlevelsup == 0)
+           if (varattno == InvalidAttrNumber)
+           {
+               /*
+                * Insert PlaceHolderVar for whole-tuple reference.  Notice
+                * that we are wrapping one PlaceHolderVar around the whole
+                * RowExpr, rather than putting one around each element of the
+                * row.  This is because we need the expression to yield NULL,
+                * not ROW(NULL,NULL,...) when it is forced to null by an
+                * outer join.
+                */
+               wrap = true;
+           }
+           else if (newnode && IsA(newnode, Var) &&
+                    ((Var *) newnode)->varlevelsup == 0)
            {
      &nb