aboutsummaryrefslogtreecommitdiff
path: root/src/backend/nodes/makefuncs.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2025-03-12 11:47:19 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2025-03-12 11:47:19 -0400
commitca0830e5a27a1a7e58bc62b4b7950c1bf96d882c (patch)
tree63e95759f4991019e4ecaf0e0513f8f6ee7b6c73 /src/backend/nodes/makefuncs.c
parentade976f8b48193c7d57f475033d60bc5fa185a4a (diff)
downloadpostgresql-ca0830e5a27a1a7e58bc62b4b7950c1bf96d882c.tar.gz
postgresql-ca0830e5a27a1a7e58bc62b4b7950c1bf96d882c.zip
Build whole-row Vars the same way during parsing and planning.
makeWholeRowVar() has different rules for constructing a whole-row Var depending on the kind of RTE it's representing. This turns out to be problematic because the rewriter and planner can convert view RTEs and set-returning-function RTEs into subquery RTEs; so a whole-row Var made during planning might look different from one made by the parser. In isolation this doesn't cause any problem, but if a query contains Vars made both ways for the same varno, there are cross-checks in the executor that will complain. This manifests for UPDATE, DELETE, and MERGE queries that use whole-row table references. To fix, we need makeWholeRowVar() to produce the same result from an inlined RTE as it would have for the original. For an inlined view, we can use RangeTblEntry.relid to detect that this had been a view RTE. For inlined SRFs, make a data structure definition change akin to commit 47bb9db75, and say that we won't clear RangeTblEntry.functions until the end of planning. That allows makeWholeRowVar() to repeat what it would have done with the unmodified RTE. Reported-by: Duncan Sands <duncan.sands@deepbluecap.com> Reported-by: Dean Rasheed <dean.a.rasheed@gmail.com> Diagnosed-by: Tender Wang <tndrwang@gmail.com> Author: Tom Lane <tgl@sss.pgh.pa.us> Reviewed-by: Dean Rasheed <dean.a.rasheed@gmail.com> Discussion: https://postgr.es/m/3518c50a-ab18-482f-b916-a37263622501@deepbluecap.com Backpatch-through: 13
Diffstat (limited to 'src/backend/nodes/makefuncs.c')
-rw-r--r--src/backend/nodes/makefuncs.c51
1 files changed, 49 insertions, 2 deletions
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index 61ac172a857..9f3b4f200fb 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -159,6 +159,53 @@ makeWholeRowVar(RangeTblEntry *rte,
varlevelsup);
break;
+ case RTE_SUBQUERY:
+
+ /*
+ * For a standard subquery, the Var should be of RECORD type.
+ * However, if we're looking at a subquery that was expanded from
+ * a view or SRF (only possible during planning), we must use the
+ * appropriate rowtype, so that the resulting Var has the same
+ * type that we would have produced from the original RTE.
+ */
+ if (OidIsValid(rte->relid))
+ {
+ /* Subquery was expanded from a view */
+ toid = get_rel_type_id(rte->relid);
+ if (!OidIsValid(toid))
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("relation \"%s\" does not have a composite type",
+ get_rel_name(rte->relid))));
+ }
+ else if (rte->functions)
+ {
+ /*
+ * Subquery was expanded from a set-returning function. That
+ * would not have happened if there's more than one function
+ * or ordinality was requested. We also needn't worry about
+ * the allowScalar case, since the planner doesn't use that.
+ * Otherwise this must match the RTE_FUNCTION code below.
+ */
+ Assert(!allowScalar);
+ fexpr = ((RangeTblFunction *) linitial(rte->functions))->funcexpr;
+ toid = exprType(fexpr);
+ if (!type_is_rowtype(toid))
+ toid = RECORDOID;
+ }
+ else
+ {
+ /* Normal subquery-in-FROM */
+ toid = RECORDOID;
+ }
+ result = makeVar(varno,
+ InvalidAttrNumber,
+ toid,
+ -1,
+ InvalidOid,
+ varlevelsup);
+ break;
+
case RTE_FUNCTION:
/*
@@ -215,8 +262,8 @@ makeWholeRowVar(RangeTblEntry *rte,
default:
/*
- * RTE is a join, subselect, tablefunc, or VALUES. We represent
- * this as a whole-row Var of RECORD type. (Note that in most
+ * RTE is a join, tablefunc, VALUES, CTE, etc. We represent these
+ * cases 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.)
*/