]> granicus.if.org Git - postgresql/commitdiff
Ensure that whole-row junk Vars are always of composite type.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 28 Nov 2011 03:27:24 +0000 (22:27 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 28 Nov 2011 03:27:24 +0000 (22:27 -0500)
The EvalPlanQual machinery assumes that whole-row Vars generated for the
outputs of non-table RTEs will be of composite types.  However, for the
case where the RTE is a function call returning a scalar type, we were
doing the wrong thing, as a result of sharing code with a parser case
where the function's scalar output is wanted.  (Or at least, that's what
that case has done historically; it does seem a bit inconsistent.)

To fix, extend makeWholeRowVar's API so that it can support both use-cases.
This fixes Belinda Cussen's report of crashes during concurrent execution
of UPDATEs involving joins to the result of UNNEST() --- in READ COMMITTED
mode, we'd run the EvalPlanQual machinery after a conflicting row update
commits, and it was expecting to get a HeapTuple not a scalar datum from
the "wholerowN" variable referencing the function RTE.

Back-patch to 9.0 where the current EvalPlanQual implementation appeared.

In 9.1 and up, this patch also fixes failure to attach the correct
collation to the Var generated for a scalar-result case.  An example:
regression=# select upper(x.*) from textcat('ab', 'cd') x;
ERROR:  could not determine which collation to use for upper() function

src/backend/nodes/makefuncs.c
src/backend/optimizer/prep/preptlist.c
src/backend/parser/parse_expr.c
src/backend/rewrite/rewriteHandler.c
src/include/nodes/makefuncs.h

index 9070cd2d739abecc60db22efa1cfb842ce20b417..683e751148d0907232c9c01dba643f33fd65a25a 100644 (file)
@@ -121,11 +121,17 @@ makeVarFromTargetEntry(Index varno,
  * with error cases, but it's not worth changing now.)  The vartype indicates
  * a rowtype; either a named composite type, or RECORD.  This function
  * encapsulates the logic for determining the correct rowtype OID to use.
+ *
+ * If allowScalar is true, then for the case where the RTE is a function
+ * returning a non-composite result type, we produce a normal Var referencing
+ * the function's result directly, instead of the single-column composite
+ * value that the whole-row notation might otherwise suggest.
  */
 Var *
 makeWholeRowVar(RangeTblEntry *rte,
                                Index varno,
-                               Index varlevelsup)
+                               Index varlevelsup,
+                               bool allowScalar)
 {
        Var                *result;
        Oid                     toid;
@@ -157,39 +163,34 @@ makeWholeRowVar(RangeTblEntry *rte,
                                                                 InvalidOid,
                                                                 varlevelsup);
                        }
-                       else
+                       else if (allowScalar)
                        {
-                               /*
-                                * func returns scalar; instead of making a whole-row Var,
-                                * just reference the function's scalar output.  (XXX this
-                                * seems a tad inconsistent, especially if "f.*" was
-                                * explicitly written ...)
-                                */
+                               /* func returns scalar; just return its output as-is */
                                result = makeVar(varno,
                                                                 1,
                                                                 toid,
                                                                 -1,
+                                                                exprCollation(rte->funcexpr),
+                                                                varlevelsup);
+                       }
+                       else
+                       {
+                               /* func returns scalar, but we want a composite result */
+                               result = makeVar(varno,
+                                                                InvalidAttrNumber,
+                                                                RECORDOID,
+                                                                -1,
                                                                 InvalidOid,
                                                                 varlevelsup);
                        }
                        break;
-               case RTE_VALUES:
-                       toid = RECORDOID;
-                       /* returns composite; same as relation case */
-                       result = makeVar(varno,
-                                                        InvalidAttrNumber,
-                                                        toid,
-                                                        -1,
-                                                        InvalidOid,
-                                                        varlevelsup);
-                       break;
                default:
 
                        /*
-                        * RTE is a join or subselect.  We represent this as a whole-row
-                        * Var of RECORD type.  (Note that in most cases the Var will be
-                        * expanded to a RowExpr during planning, but that is not our
-                        * concern here.)
+                        * RTE is a join, subselect, or VALUES.  We represent this as a
+                        * whole-row Var of RECORD type. (Note that in most cases the Var
+                        * will be expanded to a RowExpr during planning, but that is not
+                        * our concern here.)
                         */
                        result = makeVar(varno,
                                                         InvalidAttrNumber,
index 077ac49631f00bc4f14fbfc77d6de61e8ead7b2a..dccc557f031eebc00037edefbc929653b73a2f68 100644 (file)
@@ -129,7 +129,8 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
                        /* Not a table, so we need the whole row as a junk var */
                        var = makeWholeRowVar(rt_fetch(rc->rti, range_table),
                                                                  rc->rti,
-                                                                 0);
+                                                                 0,
+                                                                 false);
                        snprintf(resname, sizeof(resname), "wholerow%u", rc->rowmarkId);
                        tle = makeTargetEntry((Expr *) var,
                                                                  list_length(tlist) + 1,
index 79328c997974ad5c50a016b7ade3c3eb4d64156e..75236c76a1f5aabdaff0d0710a5c5d40d08a64d8 100644 (file)
@@ -2059,8 +2059,15 @@ transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte, int location)
        /* Find the RTE's rangetable location */
        vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
 
-       /* Build the appropriate referencing node */
-       result = makeWholeRowVar(rte, vnum, sublevels_up);
+       /*
+        * Build the appropriate referencing node.  Note that if the RTE is a
+        * function returning scalar, we create just a plain reference to the
+        * function value, not a composite containing a single column.  This is
+        * pretty inconsistent at first sight, but it's what we've done
+        * historically.  One argument for it is that "rel" and "rel.*" mean the
+        * same thing for composite relations, so why not for scalar functions...
+        */
+       result = makeWholeRowVar(rte, vnum, sublevels_up, true);
 
        /* location is not filled in by makeWholeRowVar */
        result->location = location;
index 3b311087756fecb2bc3778bf9678f4d61b1c1e63..a6f4141dd7b677785e8cd45af0aeee7a70a657f7 100644 (file)
@@ -1188,7 +1188,8 @@ rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte,
                 */
                var = makeWholeRowVar(target_rte,
                                                          parsetree->resultRelation,
-                                                         0);
+                                                         0,
+                                                         false);
 
                attrname = "wholerow";
        }
index 532681f8c015e5606d9de395e97d51745a10b5f7..c5ed6cb4133c8d4376f4d8a587405cdc9f4a5996 100644 (file)
@@ -35,7 +35,8 @@ extern Var *makeVarFromTargetEntry(Index varno,
 
 extern Var *makeWholeRowVar(RangeTblEntry *rte,
                                Index varno,
-                               Index varlevelsup);
+                               Index varlevelsup,
+                               bool allowScalar);
 
 extern TargetEntry *makeTargetEntry(Expr *expr,
                                AttrNumber resno,