4 contrib/spi/moddatetime.c
7 It is a function to be called from a trigger for the purpose of updating
8 a modification datetime stamp in a record when that record is UPDATEd.
11 This is 95%+ based on autoinc.c, which I used as a starting point as I do
12 not really know what I am doing. I also had help from
13 Jan Wieck <jwieck@debis.com> who told me about the timestamp_in("now") function.
14 OH, me, I'm Terry Mackintosh <terry@terrym.com>
18 #include "access/htup_details.h"
19 #include "catalog/pg_type.h"
20 #include "commands/trigger.h"
21 #include "executor/spi.h"
22 #include "utils/fmgrprotos.h"
23 #include "utils/rel.h"
27 PG_FUNCTION_INFO_V1(moddatetime
);
30 moddatetime(PG_FUNCTION_ARGS
)
32 TriggerData
*trigdata
= (TriggerData
*) fcinfo
->context
;
33 Trigger
*trigger
; /* to get trigger name */
34 int nargs
; /* # of arguments */
35 int attnum
; /* positional number of field to change */
36 Oid atttypid
; /* type OID of field to change */
37 Datum newdt
; /* The current datetime. */
38 bool newdtnull
; /* null flag for it */
39 char **args
; /* arguments */
40 char *relname
; /* triggered relation name */
41 Relation rel
; /* triggered relation */
42 HeapTuple rettuple
= NULL
;
43 TupleDesc tupdesc
; /* tuple description */
45 if (!CALLED_AS_TRIGGER(fcinfo
))
47 elog(ERROR
, "moddatetime: not fired by trigger manager");
49 if (!TRIGGER_FIRED_FOR_ROW(trigdata
->tg_event
))
51 elog(ERROR
, "moddatetime: must be fired for row");
53 if (!TRIGGER_FIRED_BEFORE(trigdata
->tg_event
))
55 elog(ERROR
, "moddatetime: must be fired before event");
57 if (TRIGGER_FIRED_BY_INSERT(trigdata
->tg_event
))
59 elog(ERROR
, "moddatetime: cannot process INSERT events");
60 else if (TRIGGER_FIRED_BY_UPDATE(trigdata
->tg_event
))
61 rettuple
= trigdata
->tg_newtuple
;
64 elog(ERROR
, "moddatetime: cannot process DELETE events");
66 rel
= trigdata
->tg_relation
;
67 relname
= SPI_getrelname(rel
);
69 trigger
= trigdata
->tg_trigger
;
71 nargs
= trigger
->tgnargs
;
75 elog(ERROR
, "moddatetime (%s): A single argument was expected", relname
);
77 args
= trigger
->tgargs
;
78 /* must be the field layout? */
79 tupdesc
= rel
->rd_att
;
82 * This gets the position in the tuple of the field we want. args[0] being
83 * the name of the field to update, as passed in from the trigger.
85 attnum
= SPI_fnumber(tupdesc
, args
[0]);
88 * This is where we check to see if the field we are supposed to update
93 (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION
),
94 errmsg("\"%s\" has no attribute \"%s\"",
98 * Check the target field has an allowed type, and get the current
99 * datetime as a value of that type.
101 atttypid
= SPI_gettypeid(tupdesc
, attnum
);
102 if (atttypid
== TIMESTAMPOID
)
103 newdt
= DirectFunctionCall3(timestamp_in
,
104 CStringGetDatum("now"),
105 ObjectIdGetDatum(InvalidOid
),
107 else if (atttypid
== TIMESTAMPTZOID
)
108 newdt
= DirectFunctionCall3(timestamptz_in
,
109 CStringGetDatum("now"),
110 ObjectIdGetDatum(InvalidOid
),
115 (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION
),
116 errmsg("attribute \"%s\" of \"%s\" must be type TIMESTAMP or TIMESTAMPTZ",
118 newdt
= (Datum
) 0; /* keep compiler quiet */
122 /* Replace the attnum'th column with newdt */
123 rettuple
= heap_modify_tuple_by_cols(rettuple
, tupdesc
,
124 1, &attnum
, &newdt
, &newdtnull
);
129 return PointerGetDatum(rettuple
);