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