Commit 71d60e2a authored by Peter Eisentraut's avatar Peter Eisentraut

Add tg_updatedcols to TriggerData

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: default avatarDaniel Gustafsson <daniel@yesql.se>
Discussion: https://www.postgresql.org/message-id/flat/11c5f156-67a9-0fb5-8200-2a8018eb2e0c@2ndquadrant.com
parent 8f152b6c
......@@ -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
......
......@@ -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);
......
......@@ -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);
......
......@@ -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>
......
......@@ -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);
......
......@@ -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);
}
......@@ -39,6 +39,7 @@ typedef struct TriggerData
TupleTableSlot *tg_newslot;
Tuplestorestate *tg_oldtable;
Tuplestorestate *tg_newtable;
const Bitmapset *tg_updatedcols;
} TriggerData;
/*
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment