Header And Logo

PostgreSQL
| The world's most advanced open source database.

moddatetime.c

Go to the documentation of this file.
00001 /*
00002 moddatetime.c
00003 
00004 contrib/spi/moddatetime.c
00005 
00006 What is this?
00007 It is a function to be called from a trigger for the purpose of updating
00008 a modification datetime stamp in a record when that record is UPDATEd.
00009 
00010 Credits
00011 This is 95%+ based on autoinc.c, which I used as a starting point as I do
00012 not really know what I am doing.  I also had help from
00013 Jan Wieck <[email protected]> who told me about the timestamp_in("now") function.
00014 OH, me, I'm Terry Mackintosh <[email protected]>
00015 */
00016 #include "postgres.h"
00017 
00018 #include "catalog/pg_type.h"
00019 #include "executor/spi.h"
00020 #include "commands/trigger.h"
00021 #include "utils/rel.h"
00022 #include "utils/timestamp.h"
00023 
00024 PG_MODULE_MAGIC;
00025 
00026 extern Datum moddatetime(PG_FUNCTION_ARGS);
00027 
00028 PG_FUNCTION_INFO_V1(moddatetime);
00029 
00030 Datum
00031 moddatetime(PG_FUNCTION_ARGS)
00032 {
00033     TriggerData *trigdata = (TriggerData *) fcinfo->context;
00034     Trigger    *trigger;        /* to get trigger name */
00035     int         nargs;          /* # of arguments */
00036     int         attnum;         /* positional number of field to change */
00037     Oid         atttypid;       /* type OID of field to change */
00038     Datum       newdt;          /* The current datetime. */
00039     char      **args;           /* arguments */
00040     char       *relname;        /* triggered relation name */
00041     Relation    rel;            /* triggered relation */
00042     HeapTuple   rettuple = NULL;
00043     TupleDesc   tupdesc;        /* tuple description */
00044 
00045     if (!CALLED_AS_TRIGGER(fcinfo))
00046         /* internal error */
00047         elog(ERROR, "moddatetime: not fired by trigger manager");
00048 
00049     if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
00050         /* internal error */
00051         elog(ERROR, "moddatetime: must be fired for row");
00052 
00053     if (!TRIGGER_FIRED_BEFORE(trigdata->tg_event))
00054         /* internal error */
00055         elog(ERROR, "moddatetime: must be fired before event");
00056 
00057     if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
00058         /* internal error */
00059         elog(ERROR, "moddatetime: cannot process INSERT events");
00060     else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
00061         rettuple = trigdata->tg_newtuple;
00062     else
00063         /* internal error */
00064         elog(ERROR, "moddatetime: cannot process DELETE events");
00065 
00066     rel = trigdata->tg_relation;
00067     relname = SPI_getrelname(rel);
00068 
00069     trigger = trigdata->tg_trigger;
00070 
00071     nargs = trigger->tgnargs;
00072 
00073     if (nargs != 1)
00074         /* internal error */
00075         elog(ERROR, "moddatetime (%s): A single argument was expected", relname);
00076 
00077     args = trigger->tgargs;
00078     /* must be the field layout? */
00079     tupdesc = rel->rd_att;
00080 
00081     /*
00082      * This gets the position in the tuple of the field we want. args[0] being
00083      * the name of the field to update, as passed in from the trigger.
00084      */
00085     attnum = SPI_fnumber(tupdesc, args[0]);
00086 
00087     /*
00088      * This is where we check to see if the field we are supposed to update
00089      * even exists. The above function must return -1 if name not found?
00090      */
00091     if (attnum < 0)
00092         ereport(ERROR,
00093                 (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
00094                  errmsg("\"%s\" has no attribute \"%s\"",
00095                         relname, args[0])));
00096 
00097     /*
00098      * Check the target field has an allowed type, and get the current
00099      * datetime as a value of that type.
00100      */
00101     atttypid = SPI_gettypeid(tupdesc, attnum);
00102     if (atttypid == TIMESTAMPOID)
00103         newdt = DirectFunctionCall3(timestamp_in,
00104                                     CStringGetDatum("now"),
00105                                     ObjectIdGetDatum(InvalidOid),
00106                                     Int32GetDatum(-1));
00107     else if (atttypid == TIMESTAMPTZOID)
00108         newdt = DirectFunctionCall3(timestamptz_in,
00109                                     CStringGetDatum("now"),
00110                                     ObjectIdGetDatum(InvalidOid),
00111                                     Int32GetDatum(-1));
00112     else
00113     {
00114         ereport(ERROR,
00115                 (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
00116                  errmsg("attribute \"%s\" of \"%s\" must be type TIMESTAMP or TIMESTAMPTZ",
00117                         args[0], relname)));
00118         newdt = (Datum) 0;      /* keep compiler quiet */
00119     }
00120 
00121 /* 1 is the number of items in the arrays attnum and newdt.
00122     attnum is the positional number of the field to be updated.
00123     newdt is the new datetime stamp.
00124     NOTE that attnum and newdt are not arrays, but then a 1 element array
00125     is not an array any more then they are.  Thus, they can be considered a
00126     one element array.
00127 */
00128     rettuple = SPI_modifytuple(rel, rettuple, 1, &attnum, &newdt, NULL);
00129 
00130     if (rettuple == NULL)
00131         /* internal error */
00132         elog(ERROR, "moddatetime (%s): %d returned by SPI_modifytuple",
00133              relname, SPI_result);
00134 
00135 /* Clean up */
00136     pfree(relname);
00137 
00138     return PointerGetDatum(rettuple);
00139 }