Fix xslt_process() to ensure that it inserts a NULL terminator after the
[PostgreSQL.git] / contrib / spi / moddatetime.c
blob1466c2a9c23465f5d3ecb31c23f7d4e02da03304
1 /*
2 moddatetime.c
4 $PostgreSQL$
6 What is this?
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.
10 Credits
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>
16 #include "postgres.h"
18 #include "catalog/pg_type.h"
19 #include "executor/spi.h"
20 #include "commands/trigger.h"
22 PG_MODULE_MAGIC;
24 extern Datum moddatetime(PG_FUNCTION_ARGS);
26 PG_FUNCTION_INFO_V1(moddatetime);
28 Datum
29 moddatetime(PG_FUNCTION_ARGS)
31 TriggerData *trigdata = (TriggerData *) fcinfo->context;
32 Trigger *trigger; /* to get trigger name */
33 int nargs; /* # of arguments */
34 int attnum; /* positional number of field to change */
35 Datum newdt; /* The current datetime. */
36 char **args; /* arguments */
37 char *relname; /* triggered relation name */
38 Relation rel; /* triggered relation */
39 HeapTuple rettuple = NULL;
40 TupleDesc tupdesc; /* tuple description */
42 if (!CALLED_AS_TRIGGER(fcinfo))
43 /* internal error */
44 elog(ERROR, "moddatetime: not fired by trigger manager");
46 if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
47 /* internal error */
48 elog(ERROR, "moddatetime: cannot process STATEMENT events");
50 if (TRIGGER_FIRED_AFTER(trigdata->tg_event))
51 /* internal error */
52 elog(ERROR, "moddatetime: must be fired before event");
54 if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
55 /* internal error */
56 elog(ERROR, "moddatetime: must be fired before event");
57 else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
58 rettuple = trigdata->tg_newtuple;
59 else
60 /* internal error */
61 elog(ERROR, "moddatetime: cannot process DELETE events");
63 rel = trigdata->tg_relation;
64 relname = SPI_getrelname(rel);
66 trigger = trigdata->tg_trigger;
68 nargs = trigger->tgnargs;
70 if (nargs != 1)
71 /* internal error */
72 elog(ERROR, "moddatetime (%s): A single argument was expected", relname);
74 args = trigger->tgargs;
75 /* must be the field layout? */
76 tupdesc = rel->rd_att;
78 /* Get the current datetime. */
79 newdt = DirectFunctionCall3(timestamp_in,
80 CStringGetDatum("now"),
81 ObjectIdGetDatum(InvalidOid),
82 Int32GetDatum(-1));
85 * This gets the position in the tuple of the field we want. args[0] being
86 * the name of the field to update, as passed in from the trigger.
88 attnum = SPI_fnumber(tupdesc, args[0]);
91 * This is were we check to see if the field we are supposed to update
92 * even exits. The above function must return -1 if name not found?
94 if (attnum < 0)
95 ereport(ERROR,
96 (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
97 errmsg("\"%s\" has no attribute \"%s\"",
98 relname, args[0])));
101 * OK, this is where we make sure the timestamp field that we are
102 * modifying is really a timestamp field. Hay, error checking, what a
103 * novel idea !-)
105 if (SPI_gettypeid(tupdesc, attnum) != TIMESTAMPOID)
106 ereport(ERROR,
107 (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
108 errmsg("attribute \"%s\" of \"%s\" must be type TIMESTAMP",
109 args[0], relname)));
111 /* 1 is the number of items in the arrays attnum and newdt.
112 attnum is the positional number of the field to be updated.
113 newdt is the new datetime stamp.
114 NOTE that attnum and newdt are not arrays, but then a 1 ellement array
115 is not an array any more then they are. Thus, they can be considered a
116 one element array.
118 rettuple = SPI_modifytuple(rel, rettuple, 1, &attnum, &newdt, NULL);
120 if (rettuple == NULL)
121 /* internal error */
122 elog(ERROR, "moddatetime (%s): %d returned by SPI_modifytuple",
123 relname, SPI_result);
125 /* Clean up */
126 pfree(relname);
128 return PointerGetDatum(rettuple);