mirror of
https://github.com/postgres/postgres.git
synced 2025-04-21 12:05:57 +03:00
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
This commit is contained in:
parent
8f152b6c50
commit
71d60e2aa0
@ -36,6 +36,14 @@ SELECT lo_get(43214);
|
|||||||
\x
|
\x
|
||||||
(1 row)
|
(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;
|
DELETE FROM image;
|
||||||
SELECT lo_get(43214);
|
SELECT lo_get(43214);
|
||||||
ERROR: large object 43214 does not exist
|
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
|
* Here, if the value of the monitored attribute changes, then the large
|
||||||
* object associated with the original value is unlinked.
|
* 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 *orig = SPI_getvalue(trigtuple, tupdesc, attnum);
|
||||||
char *newv = SPI_getvalue(newtuple, 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(43213);
|
||||||
SELECT lo_get(43214);
|
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;
|
DELETE FROM image;
|
||||||
|
|
||||||
SELECT lo_get(43214);
|
SELECT lo_get(43214);
|
||||||
|
@ -517,6 +517,7 @@ typedef struct TriggerData
|
|||||||
TupleTableSlot *tg_newslot;
|
TupleTableSlot *tg_newslot;
|
||||||
Tuplestorestate *tg_oldtable;
|
Tuplestorestate *tg_oldtable;
|
||||||
Tuplestorestate *tg_newtable;
|
Tuplestorestate *tg_newtable;
|
||||||
|
const Bitmapset *tg_updatedcols;
|
||||||
} TriggerData;
|
} TriggerData;
|
||||||
</programlisting>
|
</programlisting>
|
||||||
|
|
||||||
@ -759,6 +760,30 @@ typedef struct Trigger
|
|||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</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>
|
</variablelist>
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
@ -2591,6 +2591,7 @@ ExecBSUpdateTriggers(EState *estate, ResultRelInfo *relinfo)
|
|||||||
LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE |
|
LocTriggerData.tg_event = TRIGGER_EVENT_UPDATE |
|
||||||
TRIGGER_EVENT_BEFORE;
|
TRIGGER_EVENT_BEFORE;
|
||||||
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
|
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
|
||||||
|
LocTriggerData.tg_updatedcols = updatedCols;
|
||||||
for (i = 0; i < trigdesc->numtriggers; i++)
|
for (i = 0; i < trigdesc->numtriggers; i++)
|
||||||
{
|
{
|
||||||
Trigger *trigger = &trigdesc->triggers[i];
|
Trigger *trigger = &trigdesc->triggers[i];
|
||||||
@ -2699,6 +2700,7 @@ ExecBRUpdateTriggers(EState *estate, EPQState *epqstate,
|
|||||||
TRIGGER_EVENT_BEFORE;
|
TRIGGER_EVENT_BEFORE;
|
||||||
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
|
LocTriggerData.tg_relation = relinfo->ri_RelationDesc;
|
||||||
updatedCols = GetAllUpdatedColumns(relinfo, estate);
|
updatedCols = GetAllUpdatedColumns(relinfo, estate);
|
||||||
|
LocTriggerData.tg_updatedcols = updatedCols;
|
||||||
for (i = 0; i < trigdesc->numtriggers; i++)
|
for (i = 0; i < trigdesc->numtriggers; i++)
|
||||||
{
|
{
|
||||||
Trigger *trigger = &trigdesc->triggers[i];
|
Trigger *trigger = &trigdesc->triggers[i];
|
||||||
@ -3255,6 +3257,7 @@ typedef struct AfterTriggerSharedData
|
|||||||
Oid ats_relid; /* the relation it's on */
|
Oid ats_relid; /* the relation it's on */
|
||||||
CommandId ats_firing_id; /* ID for firing cycle */
|
CommandId ats_firing_id; /* ID for firing cycle */
|
||||||
struct AfterTriggersTableData *ats_table; /* transition table access */
|
struct AfterTriggersTableData *ats_table; /* transition table access */
|
||||||
|
Bitmapset *ats_modifiedcols; /* modified columns */
|
||||||
} AfterTriggerSharedData;
|
} AfterTriggerSharedData;
|
||||||
|
|
||||||
typedef struct AfterTriggerEventData *AfterTriggerEvent;
|
typedef struct AfterTriggerEventData *AfterTriggerEvent;
|
||||||
@ -3954,6 +3957,8 @@ AfterTriggerExecute(EState *estate,
|
|||||||
LocTriggerData.tg_event =
|
LocTriggerData.tg_event =
|
||||||
evtshared->ats_event & (TRIGGER_EVENT_OPMASK | TRIGGER_EVENT_ROW);
|
evtshared->ats_event & (TRIGGER_EVENT_OPMASK | TRIGGER_EVENT_ROW);
|
||||||
LocTriggerData.tg_relation = rel;
|
LocTriggerData.tg_relation = rel;
|
||||||
|
if (TRIGGER_FOR_UPDATE(LocTriggerData.tg_trigger->tgtype))
|
||||||
|
LocTriggerData.tg_updatedcols = evtshared->ats_modifiedcols;
|
||||||
|
|
||||||
MemoryContextReset(per_tuple_context);
|
MemoryContextReset(per_tuple_context);
|
||||||
|
|
||||||
@ -5641,6 +5646,7 @@ AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo,
|
|||||||
new_shared.ats_table = transition_capture->tcs_private;
|
new_shared.ats_table = transition_capture->tcs_private;
|
||||||
else
|
else
|
||||||
new_shared.ats_table = NULL;
|
new_shared.ats_table = NULL;
|
||||||
|
new_shared.ats_modifiedcols = modifiedCols;
|
||||||
|
|
||||||
afterTriggerAddEvent(&afterTriggers.query_stack[afterTriggers.query_depth].events,
|
afterTriggerAddEvent(&afterTriggers.query_stack[afterTriggers.query_depth].events,
|
||||||
&new_event, &new_shared);
|
&new_event, &new_shared);
|
||||||
|
@ -2416,6 +2416,7 @@ tsvector_update_trigger(PG_FUNCTION_ARGS, bool config_column)
|
|||||||
bool isnull;
|
bool isnull;
|
||||||
text *txt;
|
text *txt;
|
||||||
Oid cfgId;
|
Oid cfgId;
|
||||||
|
bool update_needed;
|
||||||
|
|
||||||
/* Check call context */
|
/* Check call context */
|
||||||
if (!CALLED_AS_TRIGGER(fcinfo)) /* internal error */
|
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");
|
elog(ERROR, "tsvector_update_trigger: must be fired BEFORE event");
|
||||||
|
|
||||||
if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
|
if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
|
||||||
|
{
|
||||||
rettuple = trigdata->tg_trigtuple;
|
rettuple = trigdata->tg_trigtuple;
|
||||||
|
update_needed = true;
|
||||||
|
}
|
||||||
else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
|
else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
|
||||||
|
{
|
||||||
rettuple = trigdata->tg_newtuple;
|
rettuple = trigdata->tg_newtuple;
|
||||||
|
update_needed = false; /* computed below */
|
||||||
|
}
|
||||||
else
|
else
|
||||||
elog(ERROR, "tsvector_update_trigger: must be fired for INSERT or UPDATE");
|
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",
|
errmsg("column \"%s\" is not of a character type",
|
||||||
trigger->tgargs[i])));
|
trigger->tgargs[i])));
|
||||||
|
|
||||||
|
if (bms_is_member(numattr - FirstLowInvalidHeapAttributeNumber, trigdata->tg_updatedcols))
|
||||||
|
update_needed = true;
|
||||||
|
|
||||||
datum = SPI_getbinval(rettuple, rel->rd_att, numattr, &isnull);
|
datum = SPI_getbinval(rettuple, rel->rd_att, numattr, &isnull);
|
||||||
if (isnull)
|
if (isnull)
|
||||||
continue;
|
continue;
|
||||||
@ -2530,6 +2540,8 @@ tsvector_update_trigger(PG_FUNCTION_ARGS, bool config_column)
|
|||||||
pfree(txt);
|
pfree(txt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (update_needed)
|
||||||
|
{
|
||||||
/* make tsvector value */
|
/* make tsvector value */
|
||||||
datum = TSVectorGetDatum(make_tsvector(&prs));
|
datum = TSVectorGetDatum(make_tsvector(&prs));
|
||||||
isnull = false;
|
isnull = false;
|
||||||
@ -2540,6 +2552,7 @@ tsvector_update_trigger(PG_FUNCTION_ARGS, bool config_column)
|
|||||||
&datum, &isnull);
|
&datum, &isnull);
|
||||||
|
|
||||||
pfree(DatumGetPointer(datum));
|
pfree(DatumGetPointer(datum));
|
||||||
|
}
|
||||||
|
|
||||||
return PointerGetDatum(rettuple);
|
return PointerGetDatum(rettuple);
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,7 @@ typedef struct TriggerData
|
|||||||
TupleTableSlot *tg_newslot;
|
TupleTableSlot *tg_newslot;
|
||||||
Tuplestorestate *tg_oldtable;
|
Tuplestorestate *tg_oldtable;
|
||||||
Tuplestorestate *tg_newtable;
|
Tuplestorestate *tg_newtable;
|
||||||
|
const Bitmapset *tg_updatedcols;
|
||||||
} TriggerData;
|
} TriggerData;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user