Add API of sorts for transition table handling in trigger.c

Preparatory patch for further additions in this area, particularly to
allow MERGE to have separate transition tables for each action.

Author: Pavan Deolasee <pavan.deolasee@gmail.com>
Reviewed-by: Álvaro Herrera <alvherre@alvh.no-ip.org>
Discussion: https://postgr.es/m/CABOikdNj+8HEJ5D8tu56mrPkjHVRrBb2_cdKWwpiYNcjXgDw8g@mail.gmail.com
Discussion: https://postgr.es/m/20201231134736.GA25392@alvherre.pgsql
This commit is contained in:
Alvaro Herrera 2022-03-11 20:40:03 -03:00
parent 641f3dffcd
commit 3a46a45f6f
No known key found for this signature in database
GPG Key ID: 1C20ACB9D5C564AE
1 changed files with 119 additions and 53 deletions

View File

@ -3772,6 +3772,16 @@ static AfterTriggersTableData *GetAfterTriggersTableData(Oid relid,
CmdType cmdType);
static TupleTableSlot *GetAfterTriggersStoreSlot(AfterTriggersTableData *table,
TupleDesc tupdesc);
static Tuplestorestate *GetAfterTriggersTransitionTable(int event,
TupleTableSlot *oldslot,
TupleTableSlot *newslot,
TransitionCaptureState *transition_capture);
static void TransitionTableAddTuple(EState *estate,
TransitionCaptureState *transition_capture,
ResultRelInfo *relinfo,
TupleTableSlot *slot,
TupleTableSlot *original_insert_tuple,
Tuplestorestate *tuplestore);
static void AfterTriggerFreeQuery(AfterTriggersQueryData *qs);
static SetConstraintState SetConstraintStateCreate(int numalloc);
static SetConstraintState SetConstraintStateCopy(SetConstraintState state);
@ -5158,6 +5168,92 @@ AfterTriggerEndSubXact(bool isCommit)
}
}
/*
* Get the transition table for the given event and depending on whether we are
* processing the old or the new tuple.
*/
static Tuplestorestate *
GetAfterTriggersTransitionTable(int event,
TupleTableSlot *oldslot,
TupleTableSlot *newslot,
TransitionCaptureState *transition_capture)
{
Tuplestorestate *tuplestore = NULL;
bool delete_old_table = transition_capture->tcs_delete_old_table;
bool update_old_table = transition_capture->tcs_update_old_table;
bool update_new_table = transition_capture->tcs_update_new_table;
bool insert_new_table = transition_capture->tcs_insert_new_table;
/*
* For INSERT events NEW should be non-NULL, for DELETE events OLD should
* be non-NULL, whereas for UPDATE events normally both OLD and NEW are
* non-NULL. But for UPDATE events fired for capturing transition tuples
* during UPDATE partition-key row movement, OLD is NULL when the event is
* for a row being inserted, whereas NEW is NULL when the event is for a
* row being deleted.
*/
Assert(!(event == TRIGGER_EVENT_DELETE && delete_old_table &&
TupIsNull(oldslot)));
Assert(!(event == TRIGGER_EVENT_INSERT && insert_new_table &&
TupIsNull(newslot)));
if (!TupIsNull(oldslot))
{
Assert(TupIsNull(newslot));
if (event == TRIGGER_EVENT_DELETE && delete_old_table)
tuplestore = transition_capture->tcs_private->old_tuplestore;
else if (event == TRIGGER_EVENT_UPDATE && update_old_table)
tuplestore = transition_capture->tcs_private->old_tuplestore;
}
else if (!TupIsNull(newslot))
{
Assert(TupIsNull(oldslot));
if (event == TRIGGER_EVENT_INSERT && insert_new_table)
tuplestore = transition_capture->tcs_private->new_tuplestore;
else if (event == TRIGGER_EVENT_UPDATE && update_new_table)
tuplestore = transition_capture->tcs_private->new_tuplestore;
}
return tuplestore;
}
/*
* Add the given heap tuple to the given tuplestore, applying the conversion
* map if necessary.
*
* If original_insert_tuple is given, we can add that tuple without conversion.
*/
static void
TransitionTableAddTuple(EState *estate,
TransitionCaptureState *transition_capture,
ResultRelInfo *relinfo,
TupleTableSlot *slot,
TupleTableSlot *original_insert_tuple,
Tuplestorestate *tuplestore)
{
TupleConversionMap *map;
/*
* Nothing needs to be done if we don't have a tuplestore.
*/
if (tuplestore == NULL)
return;
if (original_insert_tuple)
tuplestore_puttupleslot(tuplestore, original_insert_tuple);
else if ((map = ExecGetChildToRootMap(relinfo)) != NULL)
{
AfterTriggersTableData *table = transition_capture->tcs_private;
TupleTableSlot *storeslot;
storeslot = GetAfterTriggersStoreSlot(table, map->outdesc);
execute_attr_map_slot(map->attrMap, slot, storeslot);
tuplestore_puttupleslot(tuplestore, storeslot);
}
else
tuplestore_puttupleslot(tuplestore, slot);
}
/* ----------
* AfterTriggerEnlargeQueryState()
*
@ -5650,7 +5746,6 @@ AfterTriggerPendingOnRel(Oid relid)
return false;
}
/* ----------
* AfterTriggerSaveEvent()
*
@ -5709,68 +5804,39 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
*/
if (row_trigger && transition_capture != NULL)
{
TupleTableSlot *original_insert_tuple = transition_capture->tcs_original_insert_tuple;
TupleConversionMap *map = ExecGetChildToRootMap(relinfo);
bool delete_old_table = transition_capture->tcs_delete_old_table;
bool update_old_table = transition_capture->tcs_update_old_table;
bool update_new_table = transition_capture->tcs_update_new_table;
bool insert_new_table = transition_capture->tcs_insert_new_table;
/*
* For INSERT events NEW should be non-NULL, for DELETE events OLD
* should be non-NULL, whereas for UPDATE events normally both OLD and
* NEW are non-NULL. But for UPDATE events fired for capturing
* transition tuples during UPDATE partition-key row movement, OLD is
* NULL when the event is for a row being inserted, whereas NEW is
* NULL when the event is for a row being deleted.
* Capture the old tuple in the appropriate transition table based on
* the event.
*/
Assert(!(event == TRIGGER_EVENT_DELETE && delete_old_table &&
TupIsNull(oldslot)));
Assert(!(event == TRIGGER_EVENT_INSERT && insert_new_table &&
TupIsNull(newslot)));
if (!TupIsNull(oldslot) &&
((event == TRIGGER_EVENT_DELETE && delete_old_table) ||
(event == TRIGGER_EVENT_UPDATE && update_old_table)))
if (!TupIsNull(oldslot))
{
Tuplestorestate *old_tuplestore;
old_tuplestore = transition_capture->tcs_private->old_tuplestore;
if (map != NULL)
{
AfterTriggersTableData *table = transition_capture->tcs_private;
TupleTableSlot *storeslot;
storeslot = GetAfterTriggersStoreSlot(table, map->outdesc);
execute_attr_map_slot(map->attrMap, oldslot, storeslot);
tuplestore_puttupleslot(old_tuplestore, storeslot);
}
else
tuplestore_puttupleslot(old_tuplestore, oldslot);
old_tuplestore = GetAfterTriggersTransitionTable(event,
oldslot,
NULL,
transition_capture);
TransitionTableAddTuple(estate, transition_capture, relinfo,
oldslot, NULL, old_tuplestore);
}
if (!TupIsNull(newslot) &&
((event == TRIGGER_EVENT_INSERT && insert_new_table) ||
(event == TRIGGER_EVENT_UPDATE && update_new_table)))
/*
* Capture the new tuple in the appropriate transition table based on
* the event.
*/
if (!TupIsNull(newslot))
{
Tuplestorestate *new_tuplestore;
new_tuplestore = transition_capture->tcs_private->new_tuplestore;
if (original_insert_tuple != NULL)
tuplestore_puttupleslot(new_tuplestore,
original_insert_tuple);
else if (map != NULL)
{
AfterTriggersTableData *table = transition_capture->tcs_private;
TupleTableSlot *storeslot;
storeslot = GetAfterTriggersStoreSlot(table, map->outdesc);
execute_attr_map_slot(map->attrMap, newslot, storeslot);
tuplestore_puttupleslot(new_tuplestore, storeslot);
}
else
tuplestore_puttupleslot(new_tuplestore, newslot);
new_tuplestore = GetAfterTriggersTransitionTable(event,
NULL,
newslot,
transition_capture);
TransitionTableAddTuple(estate, transition_capture, relinfo,
newslot,
transition_capture->tcs_original_insert_tuple,
new_tuplestore);
}
/*