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 | |