diff options
Diffstat (limited to 'src/backend/parser/analyze.c')
-rw-r--r-- | src/backend/parser/analyze.c | 149 |
1 files changed, 129 insertions, 20 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 2d320d100b..3eb4feabfd 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -52,6 +52,8 @@ static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt); static Query *transformInsertStmt(ParseState *pstate, InsertStmt *stmt); static List *transformInsertRow(ParseState *pstate, List *exprlist, List *stmtcols, List *icolumns, List *attrnos); +static OnConflictExpr *transformOnConflictClause(ParseState *pstate, + OnConflictClause *onConflictClause); static int count_rowexpr_columns(ParseState *pstate, Node *expr); static Query *transformSelectStmt(ParseState *pstate, SelectStmt *stmt); static Query *transformValuesClause(ParseState *pstate, SelectStmt *stmt); @@ -62,6 +64,8 @@ static void determineRecursiveColTypes(ParseState *pstate, Node *larg, List *nrtargetlist); static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt); static List *transformReturningList(ParseState *pstate, List *returningList); +static List *transformUpdateTargetList(ParseState *pstate, + List *targetList); static Query *transformDeclareCursorStmt(ParseState *pstate, DeclareCursorStmt *stmt); static Query *transformExplainStmt(ParseState *pstate, @@ -419,6 +423,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) ListCell *icols; ListCell *attnos; ListCell *lc; + bool isOnConflictUpdate; + AclMode targetPerms; /* There can't be any outer WITH to worry about */ Assert(pstate->p_ctenamespace == NIL); @@ -434,6 +440,9 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) qry->hasModifyingCTE = pstate->p_hasModifyingCTE; } + isOnConflictUpdate = (stmt->onConflictClause && + stmt->onConflictClause->action == ONCONFLICT_UPDATE); + /* * We have three cases to deal with: DEFAULT VALUES (selectStmt == NULL), * VALUES list, or general SELECT input. We special-case VALUES, both for @@ -478,8 +487,11 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) * mentioned in the SELECT part. Note that the target table is not added * to the joinlist or namespace. */ + targetPerms = ACL_INSERT; + if (isOnConflictUpdate) + targetPerms |= ACL_UPDATE; qry->resultRelation = setTargetTable(pstate, stmt->relation, - false, false, ACL_INSERT); + false, false, targetPerms); /* Validate stmt->cols list, or build default list if no list given */ icolumns = checkInsertTargets(pstate, stmt->cols, &attrnos); @@ -740,6 +752,11 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt) attnos = lnext(attnos); } + /* Process ON CONFLICT, if any. */ + if (stmt->onConflictClause) + qry->onConflict = transformOnConflictClause(pstate, + stmt->onConflictClause); + /* * If we have a RETURNING clause, we need to add the target relation to * the query namespace before processing it, so that Var references in @@ -850,6 +867,85 @@ transformInsertRow(ParseState *pstate, List *exprlist, } /* + * transformSelectStmt - + * transforms an OnConflictClause in an INSERT + */ +static OnConflictExpr * +transformOnConflictClause(ParseState *pstate, + OnConflictClause *onConflictClause) +{ + List *arbiterElems; + Node *arbiterWhere; + Oid arbiterConstraint; + List *onConflictSet = NIL; + Node *onConflictWhere = NULL; + RangeTblEntry *exclRte = NULL; + int exclRelIndex = 0; + List *exclRelTlist = NIL; + OnConflictExpr *result; + + /* Process the arbiter clause, ON CONFLICT ON (...) */ + transformOnConflictArbiter(pstate, onConflictClause, &arbiterElems, + &arbiterWhere, &arbiterConstraint); + + /* Process DO UPDATE */ + if (onConflictClause->action == ONCONFLICT_UPDATE) + { + exclRte = addRangeTableEntryForRelation(pstate, + pstate->p_target_relation, + makeAlias("excluded", NIL), + false, false); + 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. + */ + exclRelTlist = expandRelAttrs(pstate, exclRte, + exclRelIndex, 0, -1); + exclRte->requiredPerms = 0; + exclRte->selectedCols = NULL; + + /* + * Add EXCLUDED and the target RTE to the namespace, so that they can + * be used in the UPDATE statement. + */ + addRTEtoQuery(pstate, exclRte, false, true, true); + addRTEtoQuery(pstate, pstate->p_target_rangetblentry, + false, true, true); + + onConflictSet = + transformUpdateTargetList(pstate, onConflictClause->targetList); + + onConflictWhere = transformWhereClause(pstate, + onConflictClause->whereClause, + EXPR_KIND_WHERE, "WHERE"); + } + + /* Finally, build ON CONFLICT DO [NOTHING | UPDATE] expression */ + result = makeNode(OnConflictExpr); + + result->action = onConflictClause->action; + result->arbiterElems = arbiterElems; + result->arbiterWhere = arbiterWhere; + result->constraint = arbiterConstraint; + result->onConflictSet = onConflictSet; + result->onConflictWhere = onConflictWhere; + result->exclRelIndex = exclRelIndex; + result->exclRelTlist = exclRelTlist; + + return result; +} + + +/* * count_rowexpr_columns - * get number of columns contained in a ROW() expression; * return -1 if expression isn't a RowExpr or a Var referencing one. @@ -1899,10 +1995,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) { Query *qry = makeNode(Query); ParseNamespaceItem *nsitem; - RangeTblEntry *target_rte; Node *qual; - ListCell *origTargetList; - ListCell *tl; qry->commandType = CMD_UPDATE; pstate->p_is_update = true; @@ -1937,23 +2030,41 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) nsitem->p_lateral_only = false; nsitem->p_lateral_ok = true; - qry->targetList = transformTargetList(pstate, stmt->targetList, - EXPR_KIND_UPDATE_SOURCE); - qual = transformWhereClause(pstate, stmt->whereClause, EXPR_KIND_WHERE, "WHERE"); qry->returningList = transformReturningList(pstate, stmt->returningList); + /* + * Now we are done with SELECT-like processing, and can get on with + * transforming the target list to match the UPDATE target columns. + */ + qry->targetList = transformUpdateTargetList(pstate, stmt->targetList); + qry->rtable = pstate->p_rtable; qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->hasSubLinks = pstate->p_hasSubLinks; - /* - * Now we are done with SELECT-like processing, and can get on with - * transforming the target list to match the UPDATE target columns. - */ + assign_query_collations(pstate, qry); + + return qry; +} + +/* + * transformUpdateTargetList - + * handle SET clause in UPDATE/INSERT ... ON CONFLICT UPDATE + */ +static List * +transformUpdateTargetList(ParseState *pstate, List *origTlist) +{ + List *tlist = NIL; + RangeTblEntry *target_rte; + ListCell *orig_tl; + ListCell *tl; + + tlist = transformTargetList(pstate, origTlist, + EXPR_KIND_UPDATE_SOURCE); /* Prepare to assign non-conflicting resnos to resjunk attributes */ if (pstate->p_next_resno <= pstate->p_target_relation->rd_rel->relnatts) @@ -1961,9 +2072,9 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) /* Prepare non-junk columns for assignment to target table */ target_rte = pstate->p_target_rangetblentry; - origTargetList = list_head(stmt->targetList); + orig_tl = list_head(origTlist); - foreach(tl, qry->targetList) + foreach(tl, tlist) { TargetEntry *tle = (TargetEntry *) lfirst(tl); ResTarget *origTarget; @@ -1981,9 +2092,9 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) tle->resname = NULL; continue; } - if (origTargetList == NULL) + if (orig_tl == NULL) elog(ERROR, "UPDATE target count mismatch --- internal error"); - origTarget = (ResTarget *) lfirst(origTargetList); + origTarget = (ResTarget *) lfirst(orig_tl); Assert(IsA(origTarget, ResTarget)); attrno = attnameAttNum(pstate->p_target_relation, @@ -2005,14 +2116,12 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) target_rte->updatedCols = bms_add_member(target_rte->updatedCols, attrno - FirstLowInvalidHeapAttributeNumber); - origTargetList = lnext(origTargetList); + orig_tl = lnext(orig_tl); } - if (origTargetList != NULL) + if (orig_tl != NULL) elog(ERROR, "UPDATE target count mismatch --- internal error"); - assign_query_collations(pstate, qry); - - return qry; + return tlist; } /* |