Improve implementation of CRE-stack-flattening in map_variable_attnos().
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 13 Oct 2017 17:43:55 +0000 (13:43 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 13 Oct 2017 17:43:55 +0000 (13:43 -0400)
I (tgl) objected to the obscure implementation introduced in commit
1c497fa72.  This one seems a bit less action-at-a-distance-y, at the
price of repeating a few lines of code.

Improve the comments about what the function is doing, too.

Amit Khandekar, whacked around a bit more by me

Discussion: https://postgr.es/m/CAJ3gD9egYTyHUH0nTMxm8-1m3RvdqEbaTyGC-CUNtYf7tKNDaQ@mail.gmail.com

src/backend/rewrite/rewriteManip.c

index 9290c7f793600b621b929b5ea0462ca053053d56..6579b2446d1832b0265f0f7f16e7ef06c54333ca 100644 (file)
@@ -1203,9 +1203,11 @@ replace_rte_variables_mutator(Node *node,
  * appear in the expression.
  *
  * If the expression tree contains a whole-row Var for the target RTE,
- * *found_whole_row is returned as TRUE.  In addition, if to_rowtype is
- * not InvalidOid, we modify the Var's vartype and insert a ConvertRowTypeExpr
- * to map back to the orignal rowtype.  Callers that don't provide to_rowtype
+ * *found_whole_row is set to TRUE.  In addition, if to_rowtype is
+ * not InvalidOid, we replace the Var with a Var of that vartype, inserting
+ * a ConvertRowTypeExpr to map back to the rowtype expected by the expression.
+ * (Therefore, to_rowtype had better be a child rowtype of the rowtype of the
+ * RTE we're changing references to.)  Callers that don't provide to_rowtype
  * should report an error if *found_row_type is true; we don't do that here
  * because we don't know exactly what wording for the error message would
  * be most appropriate.  The caller will be aware of the context.
@@ -1221,10 +1223,8 @@ typedef struct
    int         sublevels_up;   /* (current) nesting depth */
    const AttrNumber *attno_map;    /* map array for user attnos */
    int         map_length;     /* number of entries in attno_map[] */
-   /* Target type when converting whole-row vars */
-   Oid         to_rowtype;
+   Oid         to_rowtype;     /* change whole-row Vars to this type */
    bool       *found_whole_row;    /* output flag */
-   bool        coerced_var;    /* var is under ConvertRowTypeExpr */
 } map_variable_attnos_context;
 
 static Node *
@@ -1244,7 +1244,8 @@ map_variable_attnos_mutator(Node *node,
            Var        *newvar = (Var *) palloc(sizeof(Var));
            int         attno = var->varattno;
 
-           *newvar = *var;
+           *newvar = *var;     /* initially copy all fields of the Var */
+
            if (attno > 0)
            {
                /* user-defined column, replace attno */
@@ -1259,39 +1260,29 @@ map_variable_attnos_mutator(Node *node,
                /* whole-row variable, warn caller */
                *(context->found_whole_row) = true;
 
-               /* If the callers expects us to convert the same, do so. */
-               if (OidIsValid(context->to_rowtype))
+               /* If the caller expects us to convert the Var, do so. */
+               if (OidIsValid(context->to_rowtype) &&
+                   context->to_rowtype != var->vartype)
                {
-                   /* No support for RECORDOID. */
+                   ConvertRowtypeExpr *r;
+
+                   /* This certainly won't work for a RECORD variable. */
                    Assert(var->vartype != RECORDOID);
 
-                   /* Don't convert unless necessary. */
-                   if (context->to_rowtype != var->vartype)
-                   {
-                       /* Var itself is converted to the requested type. */
-                       newvar->vartype = context->to_rowtype;
-
-                       /*
-                        * If this var is already under a ConvertRowtypeExpr,
-                        * we don't have to add another one.
-                        */
-                       if (!context->coerced_var)
-                       {
-                           ConvertRowtypeExpr *r;
-
-                           /*
-                            * And a conversion node on top to convert back to
-                            * the original type.
-                            */
-                           r = makeNode(ConvertRowtypeExpr);
-                           r->arg = (Expr *) newvar;
-                           r->resulttype = var->vartype;
-                           r->convertformat = COERCE_IMPLICIT_CAST;
-                           r->location = -1;
-
-                           return (Node *) r;
-                       }
-                   }
+                   /* Var itself is changed to the requested type. */
+                   newvar->vartype = context->to_rowtype;
+
+                   /*
+                    * Add a conversion node on top to convert back to the
+                    * original type expected by the expression.
+                    */
+                   r = makeNode(ConvertRowtypeExpr);
+                   r->arg = (Expr *) newvar;
+                   r->resulttype = var->vartype;
+                   r->convertformat = COERCE_IMPLICIT_CAST;
+                   r->location = -1;
+
+                   return (Node *) r;
                }
            }
            return (Node *) newvar;
@@ -1301,24 +1292,43 @@ map_variable_attnos_mutator(Node *node,
    else if (IsA(node, ConvertRowtypeExpr))
    {
        ConvertRowtypeExpr *r = (ConvertRowtypeExpr *) node;
+       Var        *var = (Var *) r->arg;
 
        /*
-        * If this is coercing a var (which is typical), convert only the var,
-        * as against adding another ConvertRowtypeExpr over it.
+        * If this is coercing a whole-row Var that we need to convert, then
+        * just convert the Var without adding an extra ConvertRowtypeExpr.
+        * Effectively we're simplifying var::parenttype::grandparenttype into
+        * just var::grandparenttype.  This avoids building stacks of CREs if
+        * this function is applied repeatedly.
         */
-       if (IsA(r->arg, Var))
+       if (IsA(var, Var) &&
+           var->varno == context->target_varno &&
+           var->varlevelsup == context->sublevels_up &&
+           var->varattno == 0 &&
+           OidIsValid(context->to_rowtype) &&
+           context->to_rowtype != var->vartype)
        {
            ConvertRowtypeExpr *newnode;
+           Var        *newvar = (Var *) palloc(sizeof(Var));
+
+           /* whole-row variable, warn caller */
+           *(context->found_whole_row) = true;
+
+           *newvar = *var;     /* initially copy all fields of the Var */
+
+           /* This certainly won't work for a RECORD variable. */
+           Assert(var->vartype != RECORDOID);
+
+           /* Var itself is changed to the requested type. */
+           newvar->vartype = context->to_rowtype;
 
            newnode = (ConvertRowtypeExpr *) palloc(sizeof(ConvertRowtypeExpr));
-           *newnode = *r;
-           context->coerced_var = true;
-           newnode->arg = (Expr *) map_variable_attnos_mutator((Node *) r->arg, context);
-           context->coerced_var = false;
+           *newnode = *r;      /* initially copy all fields of the CRE */
+           newnode->arg = (Expr *) newvar;
 
            return (Node *) newnode;
        }
-       /* Else fall through the expression tree mutator */
+       /* otherwise fall through to process the expression normally */
    }
    else if (IsA(node, Query))
    {
@@ -1351,7 +1361,6 @@ map_variable_attnos(Node *node,
    context.map_length = map_length;
    context.to_rowtype = to_rowtype;
    context.found_whole_row = found_whole_row;
-   context.coerced_var = false;
 
    *found_whole_row = false;