Skip to content

Commit dd3bab5

Browse files
committed
Ensure that whole-row junk Vars are always of composite type.
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
1 parent 91572ee commit dd3bab5

File tree

5 files changed

+38
-27
lines changed

5 files changed

+38
-27
lines changed

src/backend/nodes/makefuncs.c

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -121,11 +121,17 @@ makeVarFromTargetEntry(Index varno,
121121
* with error cases, but it's not worth changing now.) The vartype indicates
122122
* a rowtype; either a named composite type, or RECORD. This function
123123
* encapsulates the logic for determining the correct rowtype OID to use.
124+
*
125+
* If allowScalar is true, then for the case where the RTE is a function
126+
* returning a non-composite result type, we produce a normal Var referencing
127+
* the function's result directly, instead of the single-column composite
128+
* value that the whole-row notation might otherwise suggest.
124129
*/
125130
Var *
126131
makeWholeRowVar(RangeTblEntry *rte,
127132
Index varno,
128-
Index varlevelsup)
133+
Index varlevelsup,
134+
bool allowScalar)
129135
{
130136
Var *result;
131137
Oid toid;
@@ -157,39 +163,34 @@ makeWholeRowVar(RangeTblEntry *rte,
157163
InvalidOid,
158164
varlevelsup);
159165
}
160-
else
166+
else if (allowScalar)
161167
{
162-
/*
163-
* func returns scalar; instead of making a whole-row Var,
164-
* just reference the function's scalar output. (XXX this
165-
* seems a tad inconsistent, especially if "f.*" was
166-
* explicitly written ...)
167-
*/
168+
/* func returns scalar; just return its output as-is */
168169
result = makeVar(varno,
169170
1,
170171
toid,
171172
-1,
173+
exprCollation(rte->funcexpr),
174+
varlevelsup);
175+
}
176+
else
177+
{
178+
/* func returns scalar, but we want a composite result */
179+
result = makeVar(varno,
180+
InvalidAttrNumber,
181+
RECORDOID,
182+
-1,
172183
InvalidOid,
173184
varlevelsup);
174185
}
175186
break;
176-
case RTE_VALUES:
177-
toid = RECORDOID;
178-
/* returns composite; same as relation case */
179-
result = makeVar(varno,
180-
InvalidAttrNumber,
181-
toid,
182-
-1,
183-
InvalidOid,
184-
varlevelsup);
185-
break;
186187
default:
187188

188189
/*
189-
* RTE is a join or subselect. We represent this as a whole-row
190-
* Var of RECORD type. (Note that in most cases the Var will be
191-
* expanded to a RowExpr during planning, but that is not our
192-
* concern here.)
190+
* RTE is a join, subselect, or VALUES. We represent this as a
191+
* whole-row Var of RECORD type. (Note that in most cases the Var
192+
* will be expanded to a RowExpr during planning, but that is not
193+
* our concern here.)
193194
*/
194195
result = makeVar(varno,
195196
InvalidAttrNumber,

src/backend/optimizer/prep/preptlist.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,8 @@ preprocess_targetlist(PlannerInfo *root, List *tlist)
129129
/* Not a table, so we need the whole row as a junk var */
130130
var = makeWholeRowVar(rt_fetch(rc->rti, range_table),
131131
rc->rti,
132-
0);
132+
0,
133+
false);
133134
snprintf(resname, sizeof(resname), "wholerow%u", rc->rowmarkId);
134135
tle = makeTargetEntry((Expr *) var,
135136
list_length(tlist) + 1,

src/backend/parser/parse_expr.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2059,8 +2059,15 @@ transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte, int location)
20592059
/* Find the RTE's rangetable location */
20602060
vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
20612061

2062-
/* Build the appropriate referencing node */
2063-
result = makeWholeRowVar(rte, vnum, sublevels_up);
2062+
/*
2063+
* Build the appropriate referencing node. Note that if the RTE is a
2064+
* function returning scalar, we create just a plain reference to the
2065+
* function value, not a composite containing a single column. This is
2066+
* pretty inconsistent at first sight, but it's what we've done
2067+
* historically. One argument for it is that "rel" and "rel.*" mean the
2068+
* same thing for composite relations, so why not for scalar functions...
2069+
*/
2070+
result = makeWholeRowVar(rte, vnum, sublevels_up, true);
20642071

20652072
/* location is not filled in by makeWholeRowVar */
20662073
result->location = location;

src/backend/rewrite/rewriteHandler.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1188,7 +1188,8 @@ rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte,
11881188
*/
11891189
var = makeWholeRowVar(target_rte,
11901190
parsetree->resultRelation,
1191-
0);
1191+
0,
1192+
false);
11921193

11931194
attrname = "wholerow";
11941195
}

src/include/nodes/makefuncs.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ extern Var *makeVarFromTargetEntry(Index varno,
3535

3636
extern Var *makeWholeRowVar(RangeTblEntry *rte,
3737
Index varno,
38-
Index varlevelsup);
38+
Index varlevelsup,
39+
bool allowScalar);
3940

4041
extern TargetEntry *makeTargetEntry(Expr *expr,
4142
AttrNumber resno,

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy