Add tg_updatedcols to TriggerData
authorPeter Eisentraut <peter@eisentraut.org>
Mon, 9 Mar 2020 08:22:22 +0000 (09:22 +0100)
committerPeter Eisentraut <peter@eisentraut.org>
Mon, 9 Mar 2020 08:34:55 +0000 (09:34 +0100)
This allows a trigger function to determine for an UPDATE trigger
which columns were actually updated.  This allows some optimizations
in generic trigger functions such as lo_manage and
tsvector_update_trigger.

Reviewed-by: Daniel Gustafsson <daniel@yesql.se>
Discussion: https://www.postgresql.org/message-id/flat/11c5f156-67a9-0fb5-8200-2a8018eb2e0c@2ndquadrant.com

contrib/lo/expected/lo.out
contrib/lo/lo.c
contrib/lo/sql/lo.sql
doc/src/sgml/trigger.sgml
src/backend/commands/trigger.c
src/backend/utils/adt/tsvector_op.c
src/include/commands/trigger.h

index f7104aee3f08eb1d85dd7bafb1efc0601b74188b..c63e4b1c704fb4f20380339d6abb5597d9da12af 100644 (file)
@@ -36,6 +36,14 @@ SELECT lo_get(43214);
  \x
 (1 row)
 
+-- test updating of unrelated column
+UPDATE image SET title = 'beautiful picture' WHERE title = 'beautiful image';
+SELECT lo_get(43214);
+ lo_get 
+--------
+ \x
+(1 row)
+
 DELETE FROM image;
 SELECT lo_get(43214);
 ERROR:  large object 43214 does not exist
index 4585923ee2b5aa257905a29ab9102dd50ec6daa1..b9847081db6c6cfc29a15ad641bcf3b630e6b7db 100644 (file)
@@ -74,7 +74,8 @@ lo_manage(PG_FUNCTION_ARGS)
         * Here, if the value of the monitored attribute changes, then the large
         * object associated with the original value is unlinked.
         */
-       if (newtuple != NULL)
+       if (newtuple != NULL &&
+               bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, trigdata->tg_updatedcols))
        {
                char       *orig = SPI_getvalue(trigtuple, tupdesc, attnum);
                char       *newv = SPI_getvalue(newtuple, tupdesc, attnum);
index 34ba6f00ec0b91ab9725e4437e05fc92bf94dbb6..770395092450802cd867bdec4febe70c0119e9ef 100644 (file)
@@ -18,6 +18,11 @@ UPDATE image SET raster = 43214 WHERE title = 'beautiful image';
 SELECT lo_get(43213);
 SELECT lo_get(43214);
 
+-- test updating of unrelated column
+UPDATE image SET title = 'beautiful picture' WHERE title = 'beautiful image';
+
+SELECT lo_get(43214);
+
 DELETE FROM image;
 
 SELECT lo_get(43214);
index 6f323a903b535c844079cf42495b205a4c825fda..7d9ad4763aa05c74c35f0fb85290b68b48f7f0cd 100644 (file)
@@ -517,6 +517,7 @@ typedef struct TriggerData
     TupleTableSlot  *tg_newslot;
     Tuplestorestate *tg_oldtable;
     Tuplestorestate *tg_newtable;
+    const Bitmapset *tg_updatedcols;
 } TriggerData;
 </programlisting>
 
@@ -759,6 +760,30 @@ typedef struct Trigger
       </listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><structfield>tg_updatedcols</structfield></term>
+      <listitem>
+       <para>
+        For <literal>UPDATE</literal> triggers, a bitmap set indicating the
+        columns that were updated by the triggering command.  Generic trigger
+        functions can use this to optimize actions by not having to deal with
+        columns that were not changed.
+       </para>
+
+       <para>
+        As an example, to determine whether a column with attribute number
+        <varname>attnum</varname> (1-based) is a member of this bitmap set,
+        call <literal>bms_is_member(attnum -
+        FirstLowInvalidHeapAttributeNumber,
+        trigdata->tg_updatedcols))</literal>.
+       </para>
+
+       <para>
+        For triggers other than <literal>UPDATE</literal> triggers, this will
+        be <symbol>NULL</symbol>.
+       </para>
+      </listitem>
+     </varlistentry>
     </variablelist>
    </para>
 
index 325228c4eb1969b38d1393830505d689a2475723..513427edf146ae5102cafad9e40eb5b6f2132d25 100644 (file)
@@ -2591,6 +2591,7 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
        LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE |
                TRIGGER_EVENT_BEFORE;
        LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
+       LocTriggerData.tg_updatedcols = updatedCols;
        for (i = 0; i < trigdesc->numtriggers; i++)
        {
                Trigger    *trigger = &trigdesc->triggers[i];
@@ -2699,6 +2700,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
                TRIGGER_EVENT_BEFORE;
        LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
        updatedCols = GetAllUpdatedColumns(relinfo, estate);
+       LocTriggerData.tg_updatedcols = updatedCols;
        for (i = 0; i < trigdesc->numtriggers; i++)
        {
                Trigger    *trigger = &trigdesc->triggers[i];
@@ -3255,6 +3257,7 @@ typedef struct AfterTriggerSharedData
        Oid                     ats_relid;              /* the relation it's on */
        CommandId       ats_firing_id;  /* ID for firing cycle */
        struct AfterTriggersTableData *ats_table;       /* transition table access */
+       Bitmapset  *ats_modifiedcols;   /* modified columns */
 } AfterTriggerSharedData;
 
 typedef struct AfterTriggerEventData *AfterTriggerEvent;
@@ -3954,6 +3957,8 @@ AfterTriggerExecute(EState *estate,
        LocTriggerData.tg_event =
                evtshared->ats_event & (TRIGGER_EVENT_OPMASK | TRIGGER_EVENT_ROW);
        LocTriggerData.tg_relation = rel;
+       if (TRIGGER_FOR_UPDATE(LocTriggerData.tg_trigger->tgtype))
+               LocTriggerData.tg_updatedcols = evtshared->ats_modifiedcols;
 
        MemoryContextReset(per_tuple_context);
 
@@ -5641,6 +5646,7 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
                        new_shared.ats_table = transition_capture->tcs_private;
                else
                        new_shared.ats_table = NULL;
+               new_shared.ats_modifiedcols = modifiedCols;
 
                afterTriggerAddEvent(&afterTriggers.query_stack[afterTriggers.query_depth].events,
                                                         &new_event, &new_shared);
index 790355082d97ea0e67f9c04451335f222d00c2c0..108dd998c7a5bd4c853c1e585d96f341d7f26aa2 100644 (file)
@@ -2416,6 +2416,7 @@ tsvector_update_trigger(PG_FUNCTION_ARGS, bool config_column)
        bool            isnull;
        text       *txt;
        Oid                     cfgId;
+       bool            update_needed;
 
        /* Check call context */
        if (!CALLED_AS_TRIGGER(fcinfo)) /* internal error */
@@ -2428,9 +2429,15 @@ tsvector_update_trigger(PG_FUNCTION_ARGS, bool config_column)
                elog(ERROR, "tsvector_update_trigger: must be fired BEFORE event");
 
        if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
+       {
                rettuple = trigdata->tg_trigtuple;
+               update_needed = true;
+       }
        else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
+       {
                rettuple = trigdata->tg_newtuple;
+               update_needed = false;  /* computed below */
+       }
        else
                elog(ERROR, "tsvector_update_trigger: must be fired for INSERT or UPDATE");
 
@@ -2518,6 +2525,9 @@ tsvector_update_trigger(PG_FUNCTION_ARGS, bool config_column)
                                         errmsg("column \"%s\" is not of a character type",
                                                        trigger->tgargs[i])));
 
+               if (bms_is_member(numattr - FirstLowInvalidHeapAttributeNumber, trigdata->tg_updatedcols))
+                       update_needed = true;
+
                datum = SPI_getbinval(rettuple, rel->rd_att, numattr, &isnull);
                if (isnull)
                        continue;
@@ -2530,16 +2540,19 @@ tsvector_update_trigger(PG_FUNCTION_ARGS, bool config_column)
                        pfree(txt);
        }
 
-       /* make tsvector value */
-       datum = TSVectorGetDatum(make_tsvector(&prs));
-       isnull = false;
+       if (update_needed)
+       {
+               /* make tsvector value */
+               datum = TSVectorGetDatum(make_tsvector(&prs));
+               isnull = false;
 
-       /* and insert it into tuple */
-       rettuple = heap_modify_tuple_by_cols(rettuple, rel->rd_att,
-                                                                                1, &tsvector_attr_num,
-                                                                                &datum, &isnull);
+               /* and insert it into tuple */
+               rettuple = heap_modify_tuple_by_cols(rettuple, rel->rd_att,
+                                                                                        1, &tsvector_attr_num,
+                                                                                        &datum, &isnull);
 
-       pfree(DatumGetPointer(datum));
+               pfree(DatumGetPointer(datum));
+       }
 
        return PointerGetDatum(rettuple);
 }
index 5d691926433e8aab2b33a3e56606da89950fd00b..a40ddf5db52cab4771a3dfdd94703d11ca1e532b 100644 (file)
@@ -39,6 +39,7 @@ typedef struct TriggerData
        TupleTableSlot *tg_newslot;
        Tuplestorestate *tg_oldtable;
        Tuplestorestate *tg_newtable;
+       const Bitmapset *tg_updatedcols;
 } TriggerData;
 
 /*