| 1 | /*------------------------------------------------------------------------- |
| 2 | * |
| 3 | * trigfuncs.c |
| 4 | * Builtin functions for useful trigger support. |
| 5 | * |
| 6 | * |
| 7 | * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group |
| 8 | * Portions Copyright (c) 1994, Regents of the University of California |
| 9 | * |
| 10 | * src/backend/utils/adt/trigfuncs.c |
| 11 | * |
| 12 | *------------------------------------------------------------------------- |
| 13 | */ |
| 14 | #include "postgres.h" |
| 15 | |
| 16 | #include "access/htup_details.h" |
| 17 | #include "commands/trigger.h" |
| 18 | #include "utils/builtins.h" |
| 19 | #include "utils/rel.h" |
| 20 | |
| 21 | |
| 22 | /* |
| 23 | * suppress_redundant_updates_trigger |
| 24 | * |
| 25 | * This trigger function will inhibit an update from being done |
| 26 | * if the OLD and NEW records are identical. |
| 27 | */ |
| 28 | Datum |
| 29 | suppress_redundant_updates_trigger(PG_FUNCTION_ARGS) |
| 30 | { |
| 31 | TriggerData *trigdata = (TriggerData *) fcinfo->context; |
| 32 | HeapTuple newtuple, |
| 33 | oldtuple, |
| 34 | rettuple; |
| 35 | HeapTupleHeader , |
| 36 | ; |
| 37 | |
| 38 | /* make sure it's called as a trigger */ |
| 39 | if (!CALLED_AS_TRIGGER(fcinfo)) |
| 40 | ereport(ERROR, |
| 41 | (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), |
| 42 | errmsg("suppress_redundant_updates_trigger: must be called as trigger" ))); |
| 43 | |
| 44 | /* and that it's called on update */ |
| 45 | if (!TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) |
| 46 | ereport(ERROR, |
| 47 | (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), |
| 48 | errmsg("suppress_redundant_updates_trigger: must be called on update" ))); |
| 49 | |
| 50 | /* and that it's called before update */ |
| 51 | if (!TRIGGER_FIRED_BEFORE(trigdata->tg_event)) |
| 52 | ereport(ERROR, |
| 53 | (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), |
| 54 | errmsg("suppress_redundant_updates_trigger: must be called before update" ))); |
| 55 | |
| 56 | /* and that it's called for each row */ |
| 57 | if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) |
| 58 | ereport(ERROR, |
| 59 | (errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED), |
| 60 | errmsg("suppress_redundant_updates_trigger: must be called for each row" ))); |
| 61 | |
| 62 | /* get tuple data, set default result */ |
| 63 | rettuple = newtuple = trigdata->tg_newtuple; |
| 64 | oldtuple = trigdata->tg_trigtuple; |
| 65 | |
| 66 | newheader = newtuple->t_data; |
| 67 | oldheader = oldtuple->t_data; |
| 68 | |
| 69 | /* if the tuple payload is the same ... */ |
| 70 | if (newtuple->t_len == oldtuple->t_len && |
| 71 | newheader->t_hoff == oldheader->t_hoff && |
| 72 | (HeapTupleHeaderGetNatts(newheader) == |
| 73 | HeapTupleHeaderGetNatts(oldheader)) && |
| 74 | ((newheader->t_infomask & ~HEAP_XACT_MASK) == |
| 75 | (oldheader->t_infomask & ~HEAP_XACT_MASK)) && |
| 76 | memcmp(((char *) newheader) + SizeofHeapTupleHeader, |
| 77 | ((char *) oldheader) + SizeofHeapTupleHeader, |
| 78 | newtuple->t_len - SizeofHeapTupleHeader) == 0) |
| 79 | { |
| 80 | /* ... then suppress the update */ |
| 81 | rettuple = NULL; |
| 82 | } |
| 83 | |
| 84 | return PointerGetDatum(rettuple); |
| 85 | } |
| 86 | |