Skip to content

Commit

Permalink
Add tg_updatedcols to TriggerData
Browse files Browse the repository at this point in the history
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
  • Loading branch information
petere committed Mar 9, 2020
1 parent 8f152b6 commit 71d60e2
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 9 deletions.
8 changes: 8 additions & 0 deletions contrib/lo/expected/lo.out
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion contrib/lo/lo.c
Expand Up @@ -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);
Expand Down
5 changes: 5 additions & 0 deletions contrib/lo/sql/lo.sql
Expand Up @@ -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);
Expand Down
25 changes: 25 additions & 0 deletions doc/src/sgml/trigger.sgml
Expand Up @@ -517,6 +517,7 @@ typedef struct TriggerData
TupleTableSlot *tg_newslot;
Tuplestorestate *tg_oldtable;
Tuplestorestate *tg_newtable;
const Bitmapset *tg_updatedcols;
} TriggerData;
</programlisting>

Expand Down Expand Up @@ -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>

Expand Down
6 changes: 6 additions & 0 deletions src/backend/commands/trigger.c
Expand Up @@ -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];
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);
Expand Down
29 changes: 21 additions & 8 deletions src/backend/utils/adt/tsvector_op.c
Expand Up @@ -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 */
Expand All @@ -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");

Expand Down Expand Up @@ -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;
Expand All @@ -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);
}
1 change: 1 addition & 0 deletions src/include/commands/trigger.h
Expand Up @@ -39,6 +39,7 @@ typedef struct TriggerData
TupleTableSlot *tg_newslot;
Tuplestorestate *tg_oldtable;
Tuplestorestate *tg_newtable;
const Bitmapset *tg_updatedcols;
} TriggerData;

/*
Expand Down

0 comments on commit 71d60e2

Please sign in to comment.