on_conflict_do_select.patch
text/x-patch
Filename: on_conflict_do_select.patch
Type: text/x-patch
Part: 0
Message:
Re: INSERT ON CONFLICT and RETURNING
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
| File | + | − |
|---|---|---|
| src/backend/commands/explain.c | 4 | 2 |
| src/backend/executor/nodeModifyTable.c | 46 | 27 |
| src/backend/parser/gram.y | 10 | 0 |
| src/backend/rewrite/rewriteHandler.c | 5 | 0 |
| src/backend/utils/adt/ruleutils.c | 4 | 0 |
| src/include/nodes/nodes.h | 2 | 1 |
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index c98c9b5..8a22b8c 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -3755,8 +3755,10 @@ show_modifytable_info(ModifyTableState *mtstate, List *ancestors,
if (node->onConflictAction != ONCONFLICT_NONE)
{
ExplainPropertyText("Conflict Resolution",
- node->onConflictAction == ONCONFLICT_NOTHING ?
- "NOTHING" : "UPDATE",
+ node->onConflictAction == ONCONFLICT_NOTHING
+ ? "NOTHING"
+ : node->onConflictAction == ONCONFLICT_SELECT
+ ? "SELECT" : "UPDATE",
es);
/*
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 20a4c47..8e64061 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -545,6 +545,8 @@ ExecInsert(ModifyTableState *mtstate,
else
goto vlock;
}
+ /* committed conflict tuple found */
+
else
{
/*
@@ -558,11 +560,26 @@ ExecInsert(ModifyTableState *mtstate,
* type. As there's no conflicting usage of
* ExecGetReturningSlot() in the DO NOTHING case...
*/
- Assert(onconflict == ONCONFLICT_NOTHING);
+ Assert(onconflict == ONCONFLICT_NOTHING || onconflict == ONCONFLICT_SELECT);
ExecCheckTIDVisible(estate, resultRelInfo, &conflictTid,
ExecGetReturningSlot(estate, resultRelInfo));
InstrCountTuples2(&mtstate->ps, 1);
- return NULL;
+ if (onconflict == ONCONFLICT_SELECT && resultRelInfo->ri_projectReturning)
+ {
+ TupleTableSlot *existing = resultRelInfo->ri_onConflict->oc_Existing;
+ TM_FailureData tmfd;
+ TM_Result test = table_tuple_lock(resultRelInfo->ri_RelationDesc, &conflictTid,
+ estate->es_snapshot,
+ existing, estate->es_output_cid,
+ LockTupleShare, LockWaitBlock, 0,
+ &tmfd);
+ if (test == TM_Ok)
+ {
+ result = ExecProcessReturning(resultRelInfo, existing, planSlot);
+ }
+ ExecClearTuple(existing);
+ }
+ return result;
}
}
@@ -2542,7 +2559,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
* If needed, Initialize target list, projection and qual for ON CONFLICT
* DO UPDATE.
*/
- if (node->onConflictAction == ONCONFLICT_UPDATE)
+ if (node->onConflictAction == ONCONFLICT_UPDATE || node->onConflictAction == ONCONFLICT_SELECT)
{
ExprContext *econtext;
TupleDesc relationDesc;
@@ -2566,35 +2583,37 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
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.
- */
- tupDesc = ExecTypeFromTL((List *) node->onConflictSet);
- resultRelInfo->ri_onConflict->oc_ProjSlot =
- ExecInitExtraTupleSlot(mtstate->ps.state, tupDesc,
- table_slot_callbacks(resultRelInfo->ri_RelationDesc));
-
- /* build UPDATE SET projection state */
- resultRelInfo->ri_onConflict->oc_ProjInfo =
- ExecBuildProjectionInfo(node->onConflictSet, econtext,
- resultRelInfo->ri_onConflict->oc_ProjSlot,
- &mtstate->ps,
- relationDesc);
-
- /* initialize state to evaluate the WHERE clause, if any */
- if (node->onConflictWhere)
+ if (node->onConflictAction == ONCONFLICT_UPDATE)
{
- ExprState *qualexpr;
+ /*
+ * 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.
+ */
+ tupDesc = ExecTypeFromTL((List *) node->onConflictSet);
+ resultRelInfo->ri_onConflict->oc_ProjSlot =
+ ExecInitExtraTupleSlot(mtstate->ps.state, tupDesc,
+ table_slot_callbacks(resultRelInfo->ri_RelationDesc));
+
+ /* build UPDATE SET projection state */
+ resultRelInfo->ri_onConflict->oc_ProjInfo =
+ ExecBuildProjectionInfo(node->onConflictSet, econtext,
+ resultRelInfo->ri_onConflict->oc_ProjSlot,
+ &mtstate->ps,
+ relationDesc);
+
+ /* initialize state to evaluate the WHERE clause, if any */
+ if (node->onConflictWhere)
+ {
+ ExprState *qualexpr;
- qualexpr = ExecInitQual((List *) node->onConflictWhere,
+ qualexpr = ExecInitQual((List *) node->onConflictWhere,
&mtstate->ps);
- resultRelInfo->ri_onConflict->oc_WhereClause = qualexpr;
+ resultRelInfo->ri_onConflict->oc_WhereClause = qualexpr;
+ }
}
}
-
/*
* If we have any secondary relations in an UPDATE or DELETE, they need to
* be treated like non-locked relations in SELECT FOR UPDATE, ie, the
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index dbb47d4..1e815d1 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -10874,6 +10874,16 @@ opt_on_conflict:
$$->whereClause = NULL;
$$->location = @1;
}
+ |
+ ON CONFLICT opt_conf_expr DO SELECT
+ {
+ $$ = makeNode(OnConflictClause);
+ $$->action = ONCONFLICT_SELECT;
+ $$->infer = $3;
+ $$->targetList = NIL;
+ $$->whereClause = NULL;
+ $$->location = @1;
+ }
| /*EMPTY*/
{
$$ = NULL;
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index fe777c3..e96f568 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -3612,6 +3612,11 @@ RewriteQuery(Query *parsetree, List *rewrite_events)
rt_entry_relation,
parsetree->resultRelation);
}
+ if (parsetree->onConflict &&
+ parsetree->onConflict->action == ONCONFLICT_SELECT)
+ {
+ parsetree->onConflict->onConflictSet =parsetree->targetList;
+ }
}
else if (event == CMD_UPDATE)
{
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 60dd80c..cd40985 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -6276,6 +6276,10 @@ get_insert_query_def(Query *query, deparse_context *context)
{
appendStringInfoString(buf, " DO NOTHING");
}
+ else if (confl->action == ONCONFLICT_SELECT)
+ {
+ appendStringInfoString(buf, " DO SELECT");
+ }
else
{
appendStringInfoString(buf, " DO UPDATE SET ");
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 381d84b..c23d3d2 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -823,7 +823,8 @@ typedef enum OnConflictAction
{
ONCONFLICT_NONE, /* No "ON CONFLICT" clause */
ONCONFLICT_NOTHING, /* ON CONFLICT ... DO NOTHING */
- ONCONFLICT_UPDATE /* ON CONFLICT ... DO UPDATE */
+ ONCONFLICT_UPDATE, /* ON CONFLICT ... DO UPDATE */
+ ONCONFLICT_SELECT /* ON CONFLICT ... DO SELECT */
} OnConflictAction;
/*