mirror of
https://github.com/postgres/postgres.git
synced 2026-10-23 13:29:09 +03:00
Add support for INSERT ... ON CONFLICT DO SELECT.
This adds a new ON CONFLICT action DO SELECT [FOR UPDATE/SHARE], which returns the pre-existing rows when conflicts are detected. The INSERT statement must have a RETURNING clause, when DO SELECT is specified. The optional FOR UPDATE/SHARE clause allows the rows to be locked before they are are returned. As with a DO UPDATE conflict action, an optional WHERE clause may be used to prevent rows from being selected for return (but as with a DO UPDATE action, rows filtered out by the WHERE clause are still locked). Bumps catversion as stored rules change. Author: Andreas Karlsson <andreas@proxel.se> Author: Marko Tiikkaja <marko@joh.to> Author: Viktor Holmberg <v@viktorh.net> Reviewed-by: Joel Jacobson <joel@compiler.org> Reviewed-by: Kirill Reshke <reshkekirill@gmail.com> Reviewed-by: Dean Rasheed <dean.a.rasheed@gmail.com> Reviewed-by: Jian He <jian.universality@gmail.com> Discussion: https://postgr.es/m/d631b406-13b7-433e-8c0b-c6040c4b4663@Spark Discussion: https://postgr.es/m/5fca222d-62ae-4a2f-9fcb-0eca56277094@Spark Discussion: https://postgr.es/m/2b5db2e6-8ece-44d0-9890-f256fdca9f7e@proxel.se Discussion: https://postgr.es/m/CAL9smLCdV-v3KgOJX3mU19FYK82N7yzqJj2HAwWX70E=P98kgQ@mail.gmail.com
This commit is contained in:
@@ -147,12 +147,24 @@ static void ExecCrossPartitionUpdateForeignKey(ModifyTableContext *context,
|
||||
ItemPointer tupleid,
|
||||
TupleTableSlot *oldslot,
|
||||
TupleTableSlot *newslot);
|
||||
static bool ExecOnConflictLockRow(ModifyTableContext *context,
|
||||
TupleTableSlot *existing,
|
||||
ItemPointer conflictTid,
|
||||
Relation relation,
|
||||
LockTupleMode lockmode,
|
||||
bool isUpdate);
|
||||
static bool ExecOnConflictUpdate(ModifyTableContext *context,
|
||||
ResultRelInfo *resultRelInfo,
|
||||
ItemPointer conflictTid,
|
||||
TupleTableSlot *excludedSlot,
|
||||
bool canSetTag,
|
||||
TupleTableSlot **returning);
|
||||
static bool ExecOnConflictSelect(ModifyTableContext *context,
|
||||
ResultRelInfo *resultRelInfo,
|
||||
ItemPointer conflictTid,
|
||||
TupleTableSlot *excludedSlot,
|
||||
bool canSetTag,
|
||||
TupleTableSlot **returning);
|
||||
static TupleTableSlot *ExecPrepareTupleRouting(ModifyTableState *mtstate,
|
||||
EState *estate,
|
||||
PartitionTupleRouting *proute,
|
||||
@@ -274,7 +286,7 @@ ExecCheckPlanOutput(Relation resultRel, List *targetList)
|
||||
*
|
||||
* context: context for the ModifyTable operation
|
||||
* resultRelInfo: current result rel
|
||||
* cmdType: operation/merge action performed (INSERT, UPDATE, or DELETE)
|
||||
* isDelete: true if the operation/merge action is a DELETE
|
||||
* oldSlot: slot holding old tuple deleted or updated
|
||||
* newSlot: slot holding new tuple inserted or updated
|
||||
* planSlot: slot holding tuple returned by top subplan node
|
||||
@@ -283,12 +295,15 @@ ExecCheckPlanOutput(Relation resultRel, List *targetList)
|
||||
* econtext's scan tuple and its old & new tuples are not needed (FDW direct-
|
||||
* modify is disabled if the RETURNING list refers to any OLD/NEW values).
|
||||
*
|
||||
* Note: For the SELECT path of INSERT ... ON CONFLICT DO SELECT, oldSlot and
|
||||
* newSlot are both the existing tuple, since it's not changed.
|
||||
*
|
||||
* Returns a slot holding the result tuple
|
||||
*/
|
||||
static TupleTableSlot *
|
||||
ExecProcessReturning(ModifyTableContext *context,
|
||||
ResultRelInfo *resultRelInfo,
|
||||
CmdType cmdType,
|
||||
bool isDelete,
|
||||
TupleTableSlot *oldSlot,
|
||||
TupleTableSlot *newSlot,
|
||||
TupleTableSlot *planSlot)
|
||||
@@ -298,23 +313,17 @@ ExecProcessReturning(ModifyTableContext *context,
|
||||
ExprContext *econtext = projectReturning->pi_exprContext;
|
||||
|
||||
/* Make tuple and any needed join variables available to ExecProject */
|
||||
switch (cmdType)
|
||||
if (isDelete)
|
||||
{
|
||||
case CMD_INSERT:
|
||||
case CMD_UPDATE:
|
||||
/* return new tuple by default */
|
||||
if (newSlot)
|
||||
econtext->ecxt_scantuple = newSlot;
|
||||
break;
|
||||
|
||||
case CMD_DELETE:
|
||||
/* return old tuple by default */
|
||||
if (oldSlot)
|
||||
econtext->ecxt_scantuple = oldSlot;
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(ERROR, "unrecognized commandType: %d", (int) cmdType);
|
||||
/* return old tuple by default */
|
||||
if (oldSlot)
|
||||
econtext->ecxt_scantuple = oldSlot;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* return new tuple by default */
|
||||
if (newSlot)
|
||||
econtext->ecxt_scantuple = newSlot;
|
||||
}
|
||||
econtext->ecxt_outertuple = planSlot;
|
||||
|
||||
@@ -1158,6 +1167,26 @@ ExecInsert(ModifyTableContext *context,
|
||||
else
|
||||
goto vlock;
|
||||
}
|
||||
else if (onconflict == ONCONFLICT_SELECT)
|
||||
{
|
||||
/*
|
||||
* In case of ON CONFLICT DO SELECT, optionally lock the
|
||||
* conflicting tuple, fetch it and project RETURNING on
|
||||
* it. Be prepared to retry if locking fails because of a
|
||||
* concurrent UPDATE/DELETE to the conflict tuple.
|
||||
*/
|
||||
TupleTableSlot *returning = NULL;
|
||||
|
||||
if (ExecOnConflictSelect(context, resultRelInfo,
|
||||
&conflictTid, slot, canSetTag,
|
||||
&returning))
|
||||
{
|
||||
InstrCountTuples2(&mtstate->ps, 1);
|
||||
return returning;
|
||||
}
|
||||
else
|
||||
goto vlock;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
@@ -1329,7 +1358,7 @@ ExecInsert(ModifyTableContext *context,
|
||||
}
|
||||
}
|
||||
|
||||
result = ExecProcessReturning(context, resultRelInfo, CMD_INSERT,
|
||||
result = ExecProcessReturning(context, resultRelInfo, false,
|
||||
oldSlot, slot, planSlot);
|
||||
|
||||
/*
|
||||
@@ -1890,7 +1919,7 @@ ldelete:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rslot = ExecProcessReturning(context, resultRelInfo, CMD_DELETE,
|
||||
rslot = ExecProcessReturning(context, resultRelInfo, true,
|
||||
slot, NULL, context->planSlot);
|
||||
|
||||
/*
|
||||
@@ -2692,56 +2721,37 @@ redo_act:
|
||||
|
||||
/* Process RETURNING if present */
|
||||
if (resultRelInfo->ri_projectReturning)
|
||||
return ExecProcessReturning(context, resultRelInfo, CMD_UPDATE,
|
||||
return ExecProcessReturning(context, resultRelInfo, false,
|
||||
oldSlot, slot, context->planSlot);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* ExecOnConflictUpdate --- execute UPDATE of INSERT ON CONFLICT DO UPDATE
|
||||
* ExecOnConflictLockRow --- lock the row for ON CONFLICT DO SELECT/UPDATE
|
||||
*
|
||||
* Try to lock tuple for update as part of speculative insertion. If
|
||||
* a qual originating from ON CONFLICT DO UPDATE is satisfied, update
|
||||
* (but still lock row, even though it may not satisfy estate's
|
||||
* snapshot).
|
||||
* Try to lock tuple for update as part of speculative insertion for ON
|
||||
* CONFLICT DO UPDATE or ON CONFLICT DO SELECT FOR UPDATE/SHARE.
|
||||
*
|
||||
* Returns true if we're done (with or without an update), or false if
|
||||
* the caller must retry the INSERT from scratch.
|
||||
* Returns true if the row is successfully locked, or false if the caller must
|
||||
* retry the INSERT from scratch.
|
||||
*/
|
||||
static bool
|
||||
ExecOnConflictUpdate(ModifyTableContext *context,
|
||||
ResultRelInfo *resultRelInfo,
|
||||
ItemPointer conflictTid,
|
||||
TupleTableSlot *excludedSlot,
|
||||
bool canSetTag,
|
||||
TupleTableSlot **returning)
|
||||
ExecOnConflictLockRow(ModifyTableContext *context,
|
||||
TupleTableSlot *existing,
|
||||
ItemPointer conflictTid,
|
||||
Relation relation,
|
||||
LockTupleMode lockmode,
|
||||
bool isUpdate)
|
||||
{
|
||||
ModifyTableState *mtstate = context->mtstate;
|
||||
ExprContext *econtext = mtstate->ps.ps_ExprContext;
|
||||
Relation relation = resultRelInfo->ri_RelationDesc;
|
||||
ExprState *onConflictSetWhere = resultRelInfo->ri_onConflict->oc_WhereClause;
|
||||
TupleTableSlot *existing = resultRelInfo->ri_onConflict->oc_Existing;
|
||||
TM_FailureData tmfd;
|
||||
LockTupleMode lockmode;
|
||||
TM_Result test;
|
||||
Datum xminDatum;
|
||||
TransactionId xmin;
|
||||
bool isnull;
|
||||
|
||||
/*
|
||||
* Parse analysis should have blocked ON CONFLICT for all system
|
||||
* relations, which includes these. There's no fundamental obstacle to
|
||||
* supporting this; we'd just need to handle LOCKTAG_TUPLE like the other
|
||||
* ExecUpdate() caller.
|
||||
*/
|
||||
Assert(!resultRelInfo->ri_needLockTagTuple);
|
||||
|
||||
/* Determine lock mode to use */
|
||||
lockmode = ExecUpdateLockMode(context->estate, resultRelInfo);
|
||||
|
||||
/*
|
||||
* Lock tuple for update. Don't follow updates when tuple cannot be
|
||||
* Lock tuple with lockmode. Don't follow updates when tuple cannot be
|
||||
* locked without doing so. A row locking conflict here means our
|
||||
* previous conclusion that the tuple is conclusively committed is not
|
||||
* true anymore.
|
||||
@@ -2786,7 +2796,7 @@ ExecOnConflictUpdate(ModifyTableContext *context,
|
||||
(errcode(ERRCODE_CARDINALITY_VIOLATION),
|
||||
/* translator: %s is a SQL command name */
|
||||
errmsg("%s command cannot affect row a second time",
|
||||
"ON CONFLICT DO UPDATE"),
|
||||
isUpdate ? "ON CONFLICT DO UPDATE" : "ON CONFLICT DO SELECT"),
|
||||
errhint("Ensure that no rows proposed for insertion within the same command have duplicate constrained values.")));
|
||||
|
||||
/* This shouldn't happen */
|
||||
@@ -2834,6 +2844,50 @@ ExecOnConflictUpdate(ModifyTableContext *context,
|
||||
}
|
||||
|
||||
/* Success, the tuple is locked. */
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* ExecOnConflictUpdate --- execute UPDATE of INSERT ON CONFLICT DO UPDATE
|
||||
*
|
||||
* Try to lock tuple for update as part of speculative insertion. If
|
||||
* a qual originating from ON CONFLICT DO UPDATE is satisfied, update
|
||||
* (but still lock row, even though it may not satisfy estate's
|
||||
* snapshot).
|
||||
*
|
||||
* Returns true if we're done (with or without an update), or false if
|
||||
* the caller must retry the INSERT from scratch.
|
||||
*/
|
||||
static bool
|
||||
ExecOnConflictUpdate(ModifyTableContext *context,
|
||||
ResultRelInfo *resultRelInfo,
|
||||
ItemPointer conflictTid,
|
||||
TupleTableSlot *excludedSlot,
|
||||
bool canSetTag,
|
||||
TupleTableSlot **returning)
|
||||
{
|
||||
ModifyTableState *mtstate = context->mtstate;
|
||||
ExprContext *econtext = mtstate->ps.ps_ExprContext;
|
||||
Relation relation = resultRelInfo->ri_RelationDesc;
|
||||
ExprState *onConflictSetWhere = resultRelInfo->ri_onConflict->oc_WhereClause;
|
||||
TupleTableSlot *existing = resultRelInfo->ri_onConflict->oc_Existing;
|
||||
LockTupleMode lockmode;
|
||||
|
||||
/*
|
||||
* Parse analysis should have blocked ON CONFLICT for all system
|
||||
* relations, which includes these. There's no fundamental obstacle to
|
||||
* supporting this; we'd just need to handle LOCKTAG_TUPLE like the other
|
||||
* ExecUpdate() caller.
|
||||
*/
|
||||
Assert(!resultRelInfo->ri_needLockTagTuple);
|
||||
|
||||
/* Determine lock mode to use */
|
||||
lockmode = ExecUpdateLockMode(context->estate, resultRelInfo);
|
||||
|
||||
/* Lock tuple for update */
|
||||
if (!ExecOnConflictLockRow(context, existing, conflictTid,
|
||||
resultRelInfo->ri_RelationDesc, lockmode, true))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Verify that the tuple is visible to our MVCC snapshot if the current
|
||||
@@ -2875,11 +2929,13 @@ ExecOnConflictUpdate(ModifyTableContext *context,
|
||||
* security barrier quals (if any), enforced here as RLS checks/WCOs.
|
||||
*
|
||||
* The rewriter creates UPDATE RLS checks/WCOs for UPDATE security
|
||||
* quals, and stores them as WCOs of "kind" WCO_RLS_CONFLICT_CHECK,
|
||||
* but that's almost the extent of its special handling for ON
|
||||
* CONFLICT DO UPDATE.
|
||||
* quals, and stores them as WCOs of "kind" WCO_RLS_CONFLICT_CHECK.
|
||||
* Since SELECT permission on the target table is always required for
|
||||
* INSERT ... ON CONFLICT DO UPDATE, the rewriter also adds SELECT RLS
|
||||
* checks/WCOs for SELECT security quals, using WCOs of the same kind,
|
||||
* and this check enforces them too.
|
||||
*
|
||||
* The rewriter will also have associated UPDATE applicable straight
|
||||
* The rewriter will also have associated UPDATE-applicable straight
|
||||
* RLS checks/WCOs for the benefit of the ExecUpdate() call that
|
||||
* follows. INSERTs and UPDATEs naturally have mutually exclusive WCO
|
||||
* kinds, so there is no danger of spurious over-enforcement in the
|
||||
@@ -2924,6 +2980,141 @@ ExecOnConflictUpdate(ModifyTableContext *context,
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* ExecOnConflictSelect --- execute SELECT of INSERT ON CONFLICT DO SELECT
|
||||
*
|
||||
* If SELECT FOR UPDATE/SHARE is specified, try to lock tuple as part of
|
||||
* speculative insertion. If a qual originating from ON CONFLICT DO SELECT is
|
||||
* satisfied, select (but still lock row, even though it may not satisfy
|
||||
* estate's snapshot).
|
||||
*
|
||||
* Returns true if we're done (with or without a select), or false if the
|
||||
* caller must retry the INSERT from scratch.
|
||||
*/
|
||||
static bool
|
||||
ExecOnConflictSelect(ModifyTableContext *context,
|
||||
ResultRelInfo *resultRelInfo,
|
||||
ItemPointer conflictTid,
|
||||
TupleTableSlot *excludedSlot,
|
||||
bool canSetTag,
|
||||
TupleTableSlot **returning)
|
||||
{
|
||||
ModifyTableState *mtstate = context->mtstate;
|
||||
ExprContext *econtext = mtstate->ps.ps_ExprContext;
|
||||
Relation relation = resultRelInfo->ri_RelationDesc;
|
||||
ExprState *onConflictSelectWhere = resultRelInfo->ri_onConflict->oc_WhereClause;
|
||||
TupleTableSlot *existing = resultRelInfo->ri_onConflict->oc_Existing;
|
||||
LockClauseStrength lockStrength = resultRelInfo->ri_onConflict->oc_LockStrength;
|
||||
|
||||
/*
|
||||
* Parse analysis should have blocked ON CONFLICT for all system
|
||||
* relations, which includes these. There's no fundamental obstacle to
|
||||
* supporting this; we'd just need to handle LOCKTAG_TUPLE appropriately.
|
||||
*/
|
||||
Assert(!resultRelInfo->ri_needLockTagTuple);
|
||||
|
||||
/* Fetch/lock existing tuple, according to the requested lock strength */
|
||||
if (lockStrength == LCS_NONE)
|
||||
{
|
||||
if (!table_tuple_fetch_row_version(relation,
|
||||
conflictTid,
|
||||
SnapshotAny,
|
||||
existing))
|
||||
elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT");
|
||||
}
|
||||
else
|
||||
{
|
||||
LockTupleMode lockmode;
|
||||
|
||||
switch (lockStrength)
|
||||
{
|
||||
case LCS_FORKEYSHARE:
|
||||
lockmode = LockTupleKeyShare;
|
||||
break;
|
||||
case LCS_FORSHARE:
|
||||
lockmode = LockTupleShare;
|
||||
break;
|
||||
case LCS_FORNOKEYUPDATE:
|
||||
lockmode = LockTupleNoKeyExclusive;
|
||||
break;
|
||||
case LCS_FORUPDATE:
|
||||
lockmode = LockTupleExclusive;
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "Unexpected lock strength %d", (int) lockStrength);
|
||||
}
|
||||
|
||||
if (!ExecOnConflictLockRow(context, existing, conflictTid,
|
||||
resultRelInfo->ri_RelationDesc, lockmode, false))
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Verify that the tuple is visible to our MVCC snapshot if the current
|
||||
* isolation level mandates that. See comments in ExecOnConflictUpdate().
|
||||
*/
|
||||
ExecCheckTupleVisible(context->estate, relation, existing);
|
||||
|
||||
/*
|
||||
* Make tuple and any needed join variables available to ExecQual. The
|
||||
* EXCLUDED tuple is installed in ecxt_innertuple, while the target's
|
||||
* existing tuple is installed in the scantuple. EXCLUDED has been made
|
||||
* to reference INNER_VAR in setrefs.c, but there is no other redirection.
|
||||
*/
|
||||
econtext->ecxt_scantuple = existing;
|
||||
econtext->ecxt_innertuple = excludedSlot;
|
||||
econtext->ecxt_outertuple = NULL;
|
||||
|
||||
if (!ExecQual(onConflictSelectWhere, econtext))
|
||||
{
|
||||
ExecClearTuple(existing); /* see return below */
|
||||
InstrCountFiltered1(&mtstate->ps, 1);
|
||||
return true; /* done with the tuple */
|
||||
}
|
||||
|
||||
if (resultRelInfo->ri_WithCheckOptions != NIL)
|
||||
{
|
||||
/*
|
||||
* Check target's existing tuple against SELECT-applicable USING
|
||||
* security barrier quals (if any), enforced here as RLS checks/WCOs.
|
||||
*
|
||||
* The rewriter creates WCOs from the USING quals of SELECT policies,
|
||||
* and stores them as WCOs of "kind" WCO_RLS_CONFLICT_CHECK. If FOR
|
||||
* UPDATE/SHARE was specified, UPDATE permissions are required on the
|
||||
* target table, and the rewriter also adds WCOs built from the USING
|
||||
* quals of UPDATE policies, using WCOs of the same kind, and this
|
||||
* check enforces them too.
|
||||
*/
|
||||
ExecWithCheckOptions(WCO_RLS_CONFLICT_CHECK, resultRelInfo,
|
||||
existing,
|
||||
mtstate->ps.state);
|
||||
}
|
||||
|
||||
/* RETURNING is required for DO SELECT */
|
||||
Assert(resultRelInfo->ri_projectReturning);
|
||||
|
||||
*returning = ExecProcessReturning(context, resultRelInfo, false,
|
||||
existing, existing, context->planSlot);
|
||||
|
||||
if (canSetTag)
|
||||
context->estate->es_processed++;
|
||||
|
||||
/*
|
||||
* Before releasing the existing tuple, make sure that the returning slot
|
||||
* has a local copy of any pass-by-reference values.
|
||||
*/
|
||||
ExecMaterializeSlot(*returning);
|
||||
|
||||
/*
|
||||
* Clear out existing tuple, as there might not be another conflict among
|
||||
* the next input rows. Don't want to hold resources till the end of the
|
||||
* query.
|
||||
*/
|
||||
ExecClearTuple(existing);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform MERGE.
|
||||
*/
|
||||
@@ -3549,7 +3740,7 @@ lmerge_matched:
|
||||
case CMD_UPDATE:
|
||||
rslot = ExecProcessReturning(context,
|
||||
resultRelInfo,
|
||||
CMD_UPDATE,
|
||||
false,
|
||||
resultRelInfo->ri_oldTupleSlot,
|
||||
newslot,
|
||||
context->planSlot);
|
||||
@@ -3558,7 +3749,7 @@ lmerge_matched:
|
||||
case CMD_DELETE:
|
||||
rslot = ExecProcessReturning(context,
|
||||
resultRelInfo,
|
||||
CMD_DELETE,
|
||||
true,
|
||||
resultRelInfo->ri_oldTupleSlot,
|
||||
NULL,
|
||||
context->planSlot);
|
||||
@@ -4329,7 +4520,8 @@ ExecModifyTable(PlanState *pstate)
|
||||
Assert((resultRelInfo->ri_projectReturning->pi_state.flags & EEO_FLAG_HAS_OLD) == 0 &&
|
||||
(resultRelInfo->ri_projectReturning->pi_state.flags & EEO_FLAG_HAS_NEW) == 0);
|
||||
|
||||
slot = ExecProcessReturning(&context, resultRelInfo, operation,
|
||||
slot = ExecProcessReturning(&context, resultRelInfo,
|
||||
operation == CMD_DELETE,
|
||||
NULL, NULL, context.planSlot);
|
||||
|
||||
return slot;
|
||||
@@ -5031,49 +5223,60 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
|
||||
}
|
||||
|
||||
/*
|
||||
* If needed, Initialize target list, projection and qual for ON CONFLICT
|
||||
* DO UPDATE.
|
||||
* For ON CONFLICT DO SELECT/UPDATE, initialize the ON CONFLICT action
|
||||
* state.
|
||||
*/
|
||||
if (node->onConflictAction == ONCONFLICT_UPDATE)
|
||||
if (node->onConflictAction == ONCONFLICT_UPDATE ||
|
||||
node->onConflictAction == ONCONFLICT_SELECT)
|
||||
{
|
||||
OnConflictSetState *onconfl = makeNode(OnConflictSetState);
|
||||
ExprContext *econtext;
|
||||
TupleDesc relationDesc;
|
||||
OnConflictActionState *onconfl = makeNode(OnConflictActionState);
|
||||
|
||||
/* already exists if created by RETURNING processing above */
|
||||
if (mtstate->ps.ps_ExprContext == NULL)
|
||||
ExecAssignExprContext(estate, &mtstate->ps);
|
||||
|
||||
econtext = mtstate->ps.ps_ExprContext;
|
||||
relationDesc = resultRelInfo->ri_RelationDesc->rd_att;
|
||||
|
||||
/* create state for DO UPDATE SET operation */
|
||||
/* action state for DO SELECT/UPDATE */
|
||||
resultRelInfo->ri_onConflict = onconfl;
|
||||
|
||||
/* lock strength for DO SELECT [FOR UPDATE/SHARE] */
|
||||
onconfl->oc_LockStrength = node->onConflictLockStrength;
|
||||
|
||||
/* initialize slot for the existing tuple */
|
||||
onconfl->oc_Existing =
|
||||
table_slot_create(resultRelInfo->ri_RelationDesc,
|
||||
&mtstate->ps.state->es_tupleTable);
|
||||
|
||||
/*
|
||||
* Create the tuple slot for the UPDATE SET projection. We want a slot
|
||||
* of the table's type here, because the slot will be used to insert
|
||||
* into the table, and for RETURNING processing - which may access
|
||||
* system attributes.
|
||||
* For ON CONFLICT DO UPDATE, initialize target list and projection.
|
||||
*/
|
||||
onconfl->oc_ProjSlot =
|
||||
table_slot_create(resultRelInfo->ri_RelationDesc,
|
||||
&mtstate->ps.state->es_tupleTable);
|
||||
if (node->onConflictAction == ONCONFLICT_UPDATE)
|
||||
{
|
||||
ExprContext *econtext;
|
||||
TupleDesc relationDesc;
|
||||
|
||||
/* build UPDATE SET projection state */
|
||||
onconfl->oc_ProjInfo =
|
||||
ExecBuildUpdateProjection(node->onConflictSet,
|
||||
true,
|
||||
node->onConflictCols,
|
||||
relationDesc,
|
||||
econtext,
|
||||
onconfl->oc_ProjSlot,
|
||||
&mtstate->ps);
|
||||
econtext = mtstate->ps.ps_ExprContext;
|
||||
relationDesc = resultRelInfo->ri_RelationDesc->rd_att;
|
||||
|
||||
/*
|
||||
* Create the tuple slot for the UPDATE SET projection. We want a
|
||||
* slot of the table's type here, because the slot will be used to
|
||||
* insert into the table, and for RETURNING processing - which may
|
||||
* access system attributes.
|
||||
*/
|
||||
onconfl->oc_ProjSlot =
|
||||
table_slot_create(resultRelInfo->ri_RelationDesc,
|
||||
&mtstate->ps.state->es_tupleTable);
|
||||
|
||||
/* build UPDATE SET projection state */
|
||||
onconfl->oc_ProjInfo =
|
||||
ExecBuildUpdateProjection(node->onConflictSet,
|
||||
true,
|
||||
node->onConflictCols,
|
||||
relationDesc,
|
||||
econtext,
|
||||
onconfl->oc_ProjSlot,
|
||||
&mtstate->ps);
|
||||
}
|
||||
|
||||
/* initialize state to evaluate the WHERE clause, if any */
|
||||
if (node->onConflictWhere)
|
||||
|
||||
Reference in New Issue
Block a user