Skip to content

Commit 71d60e2

Browse files
committed
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: Daniel Gustafsson <daniel@yesql.se> Discussion: https://www.postgresql.org/message-id/flat/11c5f156-67a9-0fb5-8200-2a8018eb2e0c@2ndquadrant.com
1 parent 8f152b6 commit 71d60e2

File tree

7 files changed

+68
-9
lines changed

7 files changed

+68
-9
lines changed

contrib/lo/expected/lo.out

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ SELECT lo_get(43214);
3636
\x
3737
(1 row)
3838

39+
-- test updating of unrelated column
40+
UPDATE image SET title = 'beautiful picture' WHERE title = 'beautiful image';
41+
SELECT lo_get(43214);
42+
lo_get
43+
--------
44+
\x
45+
(1 row)
46+
3947
DELETE FROM image;
4048
SELECT lo_get(43214);
4149
ERROR: large object 43214 does not exist

contrib/lo/lo.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ lo_manage(PG_FUNCTION_ARGS)
7474
* Here, if the value of the monitored attribute changes, then the large
7575
* object associated with the original value is unlinked.
7676
*/
77-
if (newtuple != NULL)
77+
if (newtuple != NULL &&
78+
bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, trigdata->tg_updatedcols))
7879
{
7980
char *orig = SPI_getvalue(trigtuple, tupdesc, attnum);
8081
char *newv = SPI_getvalue(newtuple, tupdesc, attnum);

contrib/lo/sql/lo.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ UPDATE image SET raster = 43214 WHERE title = 'beautiful image';
1818
SELECT lo_get(43213);
1919
SELECT lo_get(43214);
2020

21+
-- test updating of unrelated column
22+
UPDATE image SET title = 'beautiful picture' WHERE title = 'beautiful image';
23+
24+
SELECT lo_get(43214);
25+
2126
DELETE FROM image;
2227

2328
SELECT lo_get(43214);

doc/src/sgml/trigger.sgml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,7 @@ typedef struct TriggerData
517517
TupleTableSlot *tg_newslot;
518518
Tuplestorestate *tg_oldtable;
519519
Tuplestorestate *tg_newtable;
520+
const Bitmapset *tg_updatedcols;
520521
} TriggerData;
521522
</programlisting>
522523

@@ -759,6 +760,30 @@ typedef struct Trigger
759760
</listitem>
760761
</varlistentry>
761762

763+
<varlistentry>
764+
<term><structfield>tg_updatedcols</structfield></term>
765+
<listitem>
766+
<para>
767+
For <literal>UPDATE</literal> triggers, a bitmap set indicating the
768+
columns that were updated by the triggering command. Generic trigger
769+
functions can use this to optimize actions by not having to deal with
770+
columns that were not changed.
771+
</para>
772+
773+
<para>
774+
As an example, to determine whether a column with attribute number
775+
<varname>attnum</varname> (1-based) is a member of this bitmap set,
776+
call <literal>bms_is_member(attnum -
777+
FirstLowInvalidHeapAttributeNumber,
778+
trigdata->tg_updatedcols))</literal>.
779+
</para>
780+
781+
<para>
782+
For triggers other than <literal>UPDATE</literal> triggers, this will
783+
be <symbol>NULL</symbol>.
784+
</para>
785+
</listitem>
786+
</varlistentry>
762787
</variablelist>
763788
</para>
764789

src/backend/commands/trigger.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2591,6 +2591,7 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
25912591
LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE |
25922592
TRIGGER_EVENT_BEFORE;
25932593
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
2594+
LocTriggerData.tg_updatedcols = updatedCols;
25942595
for (i = 0; i < trigdesc->numtriggers; i++)
25952596
{
25962597
Trigger *trigger = &trigdesc->triggers[i];
@@ -2699,6 +2700,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
26992700
TRIGGER_EVENT_BEFORE;
27002701
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
27012702
updatedCols = GetAllUpdatedColumns(relinfo, estate);
2703+
LocTriggerData.tg_updatedcols = updatedCols;
27022704
for (i = 0; i < trigdesc->numtriggers; i++)
27032705
{
27042706
Trigger *trigger = &trigdesc->triggers[i];
@@ -3255,6 +3257,7 @@ typedef struct AfterTriggerSharedData
32553257
Oid ats_relid; /* the relation it's on */
32563258
CommandId ats_firing_id; /* ID for firing cycle */
32573259
struct AfterTriggersTableData *ats_table; /* transition table access */
3260+
Bitmapset *ats_modifiedcols; /* modified columns */
32583261
} AfterTriggerSharedData;
32593262

32603263
typedef struct AfterTriggerEventData *AfterTriggerEvent;
@@ -3954,6 +3957,8 @@ AfterTriggerExecute(EState *estate,
39543957
LocTriggerData.tg_event =
39553958
evtshared->ats_event & (TRIGGER_EVENT_OPMASK | TRIGGER_EVENT_ROW);
39563959
LocTriggerData.tg_relation = rel;
3960+
if (TRIGGER_FOR_UPDATE(LocTriggerData.tg_trigger->tgtype))
3961+
LocTriggerData.tg_updatedcols = evtshared->ats_modifiedcols;
39573962

39583963
MemoryContextReset(per_tuple_context);
39593964

@@ -5641,6 +5646,7 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
56415646
new_shared.ats_table = transition_capture->tcs_private;
56425647
else
56435648
new_shared.ats_table = NULL;
5649+
new_shared.ats_modifiedcols = modifiedCols;
56445650

56455651
afterTriggerAddEvent(&afterTriggers.query_stack[afterTriggers.query_depth].events,
56465652
&new_event, &new_shared);

src/backend/utils/adt/tsvector_op.c

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2416,6 +2416,7 @@ tsvector_update_trigger(PG_FUNCTION_ARGS, bool config_column)
24162416
bool isnull;
24172417
text *txt;
24182418
Oid cfgId;
2419+
bool update_needed;
24192420

24202421
/* Check call context */
24212422
if (!CALLED_AS_TRIGGER(fcinfo)) /* internal error */
@@ -2428,9 +2429,15 @@ tsvector_update_trigger(PG_FUNCTION_ARGS, bool config_column)
24282429
elog(ERROR, "tsvector_update_trigger: must be fired BEFORE event");
24292430

24302431
if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
2432+
{
24312433
rettuple = trigdata->tg_trigtuple;
2434+
update_needed = true;
2435+
}
24322436
else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
2437+
{
24332438
rettuple = trigdata->tg_newtuple;
2439+
update_needed = false; /* computed below */
2440+
}
24342441
else
24352442
elog(ERROR, "tsvector_update_trigger: must be fired for INSERT or UPDATE");
24362443

@@ -2518,6 +2525,9 @@ tsvector_update_trigger(PG_FUNCTION_ARGS, bool config_column)
25182525
errmsg("column \"%s\" is not of a character type",
25192526
trigger->tgargs[i])));
25202527

2528+
if (bms_is_member(numattr - FirstLowInvalidHeapAttributeNumber, trigdata->tg_updatedcols))
2529+
update_needed = true;
2530+
25212531
datum = SPI_getbinval(rettuple, rel->rd_att, numattr, &isnull);
25222532
if (isnull)
25232533
continue;
@@ -2530,16 +2540,19 @@ tsvector_update_trigger(PG_FUNCTION_ARGS, bool config_column)
25302540
pfree(txt);
25312541
}
25322542

2533-
/* make tsvector value */
2534-
datum = TSVectorGetDatum(make_tsvector(&prs));
2535-
isnull = false;
2543+
if (update_needed)
2544+
{
2545+
/* make tsvector value */
2546+
datum = TSVectorGetDatum(make_tsvector(&prs));
2547+
isnull = false;
25362548

2537-
/* and insert it into tuple */
2538-
rettuple = heap_modify_tuple_by_cols(rettuple, rel->rd_att,
2539-
1, &tsvector_attr_num,
2540-
&datum, &isnull);
2549+
/* and insert it into tuple */
2550+
rettuple = heap_modify_tuple_by_cols(rettuple, rel->rd_att,
2551+
1, &tsvector_attr_num,
2552+
&datum, &isnull);
25412553

2542-
pfree(DatumGetPointer(datum));
2554+
pfree(DatumGetPointer(datum));
2555+
}
25432556

25442557
return PointerGetDatum(rettuple);
25452558
}

src/include/commands/trigger.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ typedef struct TriggerData
3939
TupleTableSlot *tg_newslot;
4040
Tuplestorestate *tg_oldtable;
4141
Tuplestorestate *tg_newtable;
42+
const Bitmapset *tg_updatedcols;
4243
} TriggerData;
4344

4445
/*

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy