aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/optimizer/plan/setrefs.c29
-rw-r--r--src/backend/parser/analyze.c76
-rw-r--r--src/backend/parser/parse_relation.c7
3 files changed, 84 insertions, 28 deletions
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 90c4a26b995..e6fbe6acde1 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -1929,16 +1929,21 @@ search_indexed_tlist_for_sortgroupref(Node *node,
* relation target lists. Also perform opcode lookup and add
* regclass OIDs to root->glob->relationOids.
*
- * This is used in two different scenarios: a normal join clause, where all
- * the Vars in the clause *must* be replaced by OUTER_VAR or INNER_VAR
- * references; and a RETURNING clause, which may contain both Vars of the
- * target relation and Vars of other relations. In the latter case we want
- * to replace the other-relation Vars by OUTER_VAR references, while leaving
- * target Vars alone.
- *
- * For a normal join, acceptable_rel should be zero so that any failure to
- * match a Var will be reported as an error. For the RETURNING case, pass
- * inner_itlist = NULL and acceptable_rel = the ID of the target relation.
+ * This is used in three different scenarios:
+ * 1) a normal join clause, where all the Vars in the clause *must* be
+ * replaced by OUTER_VAR or INNER_VAR references. In this case
+ * acceptable_rel should be zero so that any failure to match a Var will be
+ * reported as an error.
+ * 2) RETURNING clauses, which may contain both Vars of the target relation
+ * and Vars of other relations. In this case we want to replace the
+ * other-relation Vars by OUTER_VAR references, while leaving target Vars
+ * alone. Thus inner_itlist = NULL and acceptable_rel = the ID of the
+ * target relation should be passed.
+ * 3) ON CONFLICT UPDATE SET/WHERE clauses. Here references to EXCLUDED are
+ * to be replaced with INNER_VAR references, while leaving target Vars (the
+ * to-be-updated relation) alone. Correspondingly inner_itlist is to be
+ * EXCLUDED elements, outer_itlist = NULL and acceptable_rel the target
+ * relation.
*
* 'clauses' is the targetlist or list of join clauses
* 'outer_itlist' is the indexed target list of the outer join relation,
@@ -1981,7 +1986,7 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
{
Var *var = (Var *) node;
- /* First look for the var in the input tlists */
+ /* Look for the var in the input tlists, first in the outer */
if (context->outer_itlist)
{
newvar = search_indexed_tlist_for_var(var,
@@ -1992,7 +1997,7 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context)
return (Node *) newvar;
}
- /* Then in the outer */
+ /* then in the inner. */
if (context->inner_itlist)
{
newvar = search_indexed_tlist_for_var(var,
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index a0dfbf900a9..3ecb790cebe 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -891,33 +891,81 @@ transformOnConflictClause(ParseState *pstate,
/* Process DO UPDATE */
if (onConflictClause->action == ONCONFLICT_UPDATE)
{
+ Relation targetrel = pstate->p_target_relation;
+ Var *var;
+ TargetEntry *te;
+ int attno;
+
/*
* All INSERT expressions have been parsed, get ready for potentially
* existing SET statements that need to be processed like an UPDATE.
*/
pstate->p_is_insert = false;
+ /*
+ * Add range table entry for the EXCLUDED pseudo relation; relkind is
+ * set to composite to signal that we're not dealing with an actual
+ * relation.
+ */
exclRte = addRangeTableEntryForRelation(pstate,
- pstate->p_target_relation,
+ targetrel,
makeAlias("excluded", NIL),
false, false);
+ exclRte->relkind = RELKIND_COMPOSITE_TYPE;
exclRelIndex = list_length(pstate->p_rtable);
/*
- * Build a targetlist for the EXCLUDED pseudo relation. Out of
- * simplicity we do that here, because expandRelAttrs() happens to
- * nearly do the right thing; specifically it also works with views.
- * It'd be more proper to instead scan some pseudo scan node, but it
- * doesn't seem worth the amount of code required.
- *
- * The only caveat of this hack is that the permissions expandRelAttrs
- * adds have to be reset. markVarForSelectPriv() will add the exact
- * required permissions back.
+ * Build a targetlist for the EXCLUDED pseudo relation. Have to be
+ * careful to use resnos that correspond to attnos of the underlying
+ * relation.
+ */
+ Assert(pstate->p_next_resno == 1);
+ for (attno = 0; attno < targetrel->rd_rel->relnatts; attno++)
+ {
+ Form_pg_attribute attr = targetrel->rd_att->attrs[attno];
+ char *name;
+
+ if (attr->attisdropped)
+ {
+ /*
+ * can't use atttypid here, but it doesn't really matter what
+ * type the Const claims to be.
+ */
+ var = (Var *) makeNullConst(INT4OID, -1, InvalidOid);
+ name = "";
+ }
+ else
+ {
+ var = makeVar(exclRelIndex, attno + 1,
+ attr->atttypid, attr->atttypmod,
+ attr->attcollation,
+ 0);
+ var->location = -1;
+
+ name = NameStr(attr->attname);
+ }
+
+ Assert(pstate->p_next_resno == attno + 1);
+ te = makeTargetEntry((Expr *) var,
+ pstate->p_next_resno++,
+ name,
+ false);
+
+ /* don't require select access yet */
+ exclRelTlist = lappend(exclRelTlist, te);
+ }
+
+ /*
+ * Additionally add a whole row tlist entry for EXCLUDED. That's
+ * really only needed for ruleutils' benefit, which expects to find
+ * corresponding entries in child tlists. Alternatively we could do
+ * this only when required, but that doesn't seem worth the trouble.
*/
- exclRelTlist = expandRelAttrs(pstate, exclRte,
- exclRelIndex, 0, -1);
- exclRte->requiredPerms = 0;
- exclRte->selectedCols = NULL;
+ var = makeVar(exclRelIndex, InvalidAttrNumber,
+ RelationGetRelid(targetrel),
+ -1, InvalidOid, 0);
+ te = makeTargetEntry((Expr *) var, 0, NULL, true);
+ exclRelTlist = lappend(exclRelTlist, te);
/*
* Add EXCLUDED and the target RTE to the namespace, so that they can
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 0b2dacfd593..0c4ed65afa2 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -686,9 +686,12 @@ scanRTEForColumn(ParseState *pstate, RangeTblEntry *rte, char *colname,
return result;
/*
- * If the RTE represents a real table, consider system column names.
+ * If the RTE represents a real relation, consider system column names.
+ * Composites are only used for pseudo-relations like ON CONFLICT's
+ * excluded.
*/
- if (rte->rtekind == RTE_RELATION)
+ if (rte->rtekind == RTE_RELATION &&
+ rte->relkind != RELKIND_COMPOSITE_TYPE)
{
/* quick check to see if name could be a system column */
attnum = specialAttNum(colname);