Remove small inefficiency in ExecARDeleteTriggers/ExecARUpdateTriggers.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 31 Mar 2021 00:01:27 +0000 (20:01 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 31 Mar 2021 00:01:31 +0000 (20:01 -0400)
Whilst poking at nodeModifyTable.c, I chanced to notice that while
its calls to ExecBR*Triggers and ExecIR*Triggers are protected by
tests to see if there are any relevant triggers to fire, its calls
to ExecAR*Triggers are not; the latter functions do the equivalent
tests themselves.  This seems possibly reasonable given the more
complex conditions involved, but what's less reasonable is that
the ExecAR* functions aren't careful to do no work when there is
no work to be done.  ExecARInsertTriggers gets this right, but the
other two will both force creation of a slot that the query may
have no use for.  ExecARUpdateTriggers additionally performed a
usually-useless ExecClearTuple() on that slot.  This is probably
all pretty microscopic in real workloads, but a cycle shaved is a
cycle earned.

src/backend/commands/trigger.c

index 7383d5994ebedc2d831fc1da6e14a004e8aa2e7d..0ee318b3409910c4a0775cce6681c444fe1960d4 100644 (file)
@@ -2546,11 +2546,12 @@ ExecARDeleteTriggers(EState *estate, ResultRelInfo *relinfo,
                                         TransitionCaptureState *transition_capture)
 {
        TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
-       TupleTableSlot *slot = ExecGetTriggerOldSlot(estate, relinfo);
 
        if ((trigdesc && trigdesc->trig_delete_after_row) ||
                (transition_capture && transition_capture->tcs_delete_old_table))
        {
+               TupleTableSlot *slot = ExecGetTriggerOldSlot(estate, relinfo);
+
                Assert(HeapTupleIsValid(fdw_trigtuple) ^ ItemPointerIsValid(tupleid));
                if (fdw_trigtuple == NULL)
                        GetTupleForTrigger(estate,
@@ -2829,9 +2830,6 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
                                         TransitionCaptureState *transition_capture)
 {
        TriggerDesc *trigdesc = relinfo->ri_TrigDesc;
-       TupleTableSlot *oldslot = ExecGetTriggerOldSlot(estate, relinfo);
-
-       ExecClearTuple(oldslot);
 
        if ((trigdesc && trigdesc->trig_update_after_row) ||
                (transition_capture &&
@@ -2844,6 +2842,8 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
                 * separately for DELETE and INSERT to capture transition table rows.
                 * In such case, either old tuple or new tuple can be NULL.
                 */
+               TupleTableSlot *oldslot = ExecGetTriggerOldSlot(estate, relinfo);
+
                if (fdw_trigtuple == NULL && ItemPointerIsValid(tupleid))
                        GetTupleForTrigger(estate,
                                                           NULL,
@@ -2854,6 +2854,8 @@ ExecARUpdateTriggers(EState *estate, ResultRelInfo *relinfo,
                                                           NULL);
                else if (fdw_trigtuple != NULL)
                        ExecForceStoreHeapTuple(fdw_trigtuple, oldslot, false);
+               else
+                       ExecClearTuple(oldslot);
 
                AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_UPDATE,
                                                          true, oldslot, newslot, recheckIndexes,