diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/optimizer/plan/setrefs.c | 29 | ||||
-rw-r--r-- | src/backend/parser/analyze.c | 76 | ||||
-rw-r--r-- | src/backend/parser/parse_relation.c | 7 |
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); |