Skip to content

Commit ca76727

Browse files
committed
Support ON CONFLICT DO SELECT on hypertables for PostgreSQL 19
PostgreSQL 19 added INSERT ... ON CONFLICT DO SELECT, which returns the conflicting row instead of updating it and can optionally lock it. Upstream changes: Add support for INSERT ... ON CONFLICT DO SELECT. postgres/postgres@88327092ff0
1 parent 62575c3 commit ca76727

6 files changed

Lines changed: 709 additions & 37 deletions

File tree

.unreleased/pr_10279

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Implements: #10279 Support ON CONFLICT DO SELECT with hypertables

src/chunk_insert_state.c

Lines changed: 50 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -230,14 +230,17 @@ setup_on_conflict_state(ResultRelInfo *ht_rri, ModifyTableState *mtstate, ChunkI
230230
Relation hyper_rel = ht_rri->ri_RelationDesc;
231231
ModifyTable *mt = castNode(ModifyTable, mtstate->ps.plan);
232232

233+
/* DO UPDATE has a SET clause; DO SELECT only fetches the conflicting row. */
234+
bool do_update = mt->onConflictAction == ONCONFLICT_UPDATE;
235+
233236
OnConflictActionState *onconfl = makeNode(OnConflictActionState);
234237
memcpy(onconfl, ht_rri->ri_onConflict, sizeof(OnConflictActionState));
235238
chunk_rri->ri_onConflict = onconfl;
236239

237240
chunk_rri->ri_RootToChildMap = map;
238241
chunk_rri->ri_RootToChildMapValid = true;
239242

240-
Assert(mt->onConflictSet);
243+
Assert(!do_update || mt->onConflictSet);
241244
Assert(ht_rri->ri_onConflict != NULL);
242245

243246
/*
@@ -271,18 +274,6 @@ setup_on_conflict_state(ResultRelInfo *ht_rri, ModifyTableState *mtstate, ChunkI
271274
}
272275
else
273276
{
274-
List *onconflset;
275-
List *onconflcols;
276-
277-
/*
278-
* Translate expressions in onConflictSet to account for
279-
* different attribute numbers. For that, map partition
280-
* varattnos twice: first to catch the EXCLUDED
281-
* pseudo-relation (INNER_VAR), and second to handle the main
282-
* target relation (firstVarno).
283-
*/
284-
onconflset = copyObject(mt->onConflictSet);
285-
286277
Assert(map->outdesc == RelationGetDescr(chunk_rel));
287278

288279
if (!chunk_map)
@@ -291,33 +282,52 @@ setup_on_conflict_state(ResultRelInfo *ht_rri, ModifyTableState *mtstate, ChunkI
291282
convert_tuples_by_name(RelationGetDescr(chunk_rel), RelationGetDescr(hyper_rel));
292283
}
293284

294-
onconflset = translate_clause(onconflset, chunk_map, ht_rri->ri_RangeTableIndex, chunk_rel);
295-
296285
chunk_rri->ri_ChildToRootMap = chunk_map;
297286
chunk_rri->ri_ChildToRootMapValid = true;
298287

299-
/* Finally, adjust the target colnos to match the chunk. */
300-
if (chunk_map)
288+
if (do_update)
301289
{
302-
onconflcols = adjust_chunk_colnos(mt->onConflictCols, chunk_rri);
290+
List *onconflset;
291+
List *onconflcols;
292+
293+
/*
294+
* Translate expressions in onConflictSet to account for
295+
* different attribute numbers. For that, map partition
296+
* varattnos twice: first to catch the EXCLUDED
297+
* pseudo-relation (INNER_VAR), and second to handle the main
298+
* target relation (firstVarno).
299+
*/
300+
onconflset = copyObject(mt->onConflictSet);
301+
onconflset =
302+
translate_clause(onconflset, chunk_map, ht_rri->ri_RangeTableIndex, chunk_rel);
303+
304+
/*
305+
* Finally, adjust the target colnos to match the chunk. When the
306+
* descriptors match (e.g. a direct chunk insert) there is no
307+
* child-to-root map and the parent colnos apply unchanged.
308+
*/
309+
if (chunk_map)
310+
{
311+
onconflcols = adjust_chunk_colnos(mt->onConflictCols, chunk_rri);
312+
}
313+
else
314+
{
315+
onconflcols = mt->onConflictCols;
316+
}
317+
318+
/* create the tuple slot for the UPDATE SET projection */
319+
onconfl->oc_ProjSlot = table_slot_create(chunk_rel, NULL);
320+
state->conflproj_slot = onconfl->oc_ProjSlot;
321+
322+
/* build UPDATE SET projection state */
323+
onconfl->oc_ProjInfo = ExecBuildUpdateProjection(onconflset,
324+
true,
325+
onconflcols,
326+
RelationGetDescr(chunk_rel),
327+
mtstate->ps.ps_ExprContext,
328+
onconfl->oc_ProjSlot,
329+
&mtstate->ps);
303330
}
304-
else
305-
{
306-
onconflcols = mt->onConflictCols;
307-
}
308-
309-
/* create the tuple slot for the UPDATE SET projection */
310-
onconfl->oc_ProjSlot = table_slot_create(chunk_rel, NULL);
311-
state->conflproj_slot = onconfl->oc_ProjSlot;
312-
313-
/* build UPDATE SET projection state */
314-
onconfl->oc_ProjInfo = ExecBuildUpdateProjection(onconflset,
315-
true,
316-
onconflcols,
317-
RelationGetDescr(chunk_rel),
318-
mtstate->ps.ps_ExprContext,
319-
onconfl->oc_ProjSlot,
320-
&mtstate->ps);
321331

322332
Node *onconflict_where = mt->onConflictWhere;
323333

@@ -406,7 +416,11 @@ adjust_projections(ResultRelInfo *ht_rri, ModifyTableState *mtstate, ChunkInsert
406416
{
407417
set_arbiter_indexes(cis, ht_rri->ri_onConflictArbiterIndexes);
408418

409-
if (onConflictAction == ONCONFLICT_UPDATE)
419+
if (onConflictAction == ONCONFLICT_UPDATE
420+
#if PG19_GE
421+
|| onConflictAction == ONCONFLICT_SELECT
422+
#endif
423+
)
410424
{
411425
setup_on_conflict_state(ht_rri, mtstate, cis, chunk_map);
412426
}

0 commit comments

Comments
 (0)