LCOV - code coverage report
Current view: top level - contrib/spi - moddatetime.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 49 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 3 0
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*
       2              : moddatetime.c
       3              : 
       4              : contrib/spi/moddatetime.c
       5              : 
       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.
       9              : 
      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>
      15              : */
      16              : #include "postgres.h"
      17              : 
      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"
      24              : 
      25            0 : PG_MODULE_MAGIC_EXT(
      26              :                                         .name = "moddatetime",
      27              :                                         .version = PG_VERSION
      28              : );
      29              : 
      30            0 : PG_FUNCTION_INFO_V1(moddatetime);
      31              : 
      32              : Datum
      33            0 : moddatetime(PG_FUNCTION_ARGS)
      34              : {
      35            0 :         TriggerData *trigdata = (TriggerData *) fcinfo->context;
      36            0 :         Trigger    *trigger;            /* to get trigger name */
      37            0 :         int                     nargs;                  /* # of arguments */
      38            0 :         int                     attnum;                 /* positional number of field to change */
      39            0 :         Oid                     atttypid;               /* type OID of field to change */
      40            0 :         Datum           newdt;                  /* The current datetime. */
      41            0 :         bool            newdtnull;              /* null flag for it */
      42            0 :         char      **args;                       /* arguments */
      43            0 :         char       *relname;            /* triggered relation name */
      44            0 :         Relation        rel;                    /* triggered relation */
      45            0 :         HeapTuple       rettuple = NULL;
      46            0 :         TupleDesc       tupdesc;                /* tuple description */
      47              : 
      48            0 :         if (!CALLED_AS_TRIGGER(fcinfo))
      49              :                 /* internal error */
      50            0 :                 elog(ERROR, "moddatetime: not fired by trigger manager");
      51              : 
      52            0 :         if (!TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
      53              :                 /* internal error */
      54            0 :                 elog(ERROR, "moddatetime: must be fired for row");
      55              : 
      56            0 :         if (!TRIGGER_FIRED_BEFORE(trigdata->tg_event))
      57              :                 /* internal error */
      58            0 :                 elog(ERROR, "moddatetime: must be fired before event");
      59              : 
      60            0 :         if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
      61              :                 /* internal error */
      62            0 :                 elog(ERROR, "moddatetime: cannot process INSERT events");
      63            0 :         else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
      64            0 :                 rettuple = trigdata->tg_newtuple;
      65              :         else
      66              :                 /* internal error */
      67            0 :                 elog(ERROR, "moddatetime: cannot process DELETE events");
      68              : 
      69            0 :         rel = trigdata->tg_relation;
      70            0 :         relname = SPI_getrelname(rel);
      71              : 
      72            0 :         trigger = trigdata->tg_trigger;
      73              : 
      74            0 :         nargs = trigger->tgnargs;
      75              : 
      76            0 :         if (nargs != 1)
      77              :                 /* internal error */
      78            0 :                 elog(ERROR, "moddatetime (%s): A single argument was expected", relname);
      79              : 
      80            0 :         args = trigger->tgargs;
      81              :         /* must be the field layout? */
      82            0 :         tupdesc = rel->rd_att;
      83              : 
      84              :         /*
      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.
      87              :          */
      88            0 :         attnum = SPI_fnumber(tupdesc, args[0]);
      89              : 
      90              :         /*
      91              :          * This is where we check to see if the field we are supposed to update
      92              :          * even exists.
      93              :          */
      94            0 :         if (attnum <= 0)
      95            0 :                 ereport(ERROR,
      96              :                                 (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
      97              :                                  errmsg("\"%s\" has no attribute \"%s\"",
      98              :                                                 relname, args[0])));
      99              : 
     100              :         /*
     101              :          * Check the target field has an allowed type, and get the current
     102              :          * datetime as a value of that type.
     103              :          */
     104            0 :         atttypid = SPI_gettypeid(tupdesc, attnum);
     105            0 :         if (atttypid == TIMESTAMPOID)
     106            0 :                 newdt = DirectFunctionCall3(timestamp_in,
     107              :                                                                         CStringGetDatum("now"),
     108              :                                                                         ObjectIdGetDatum(InvalidOid),
     109              :                                                                         Int32GetDatum(-1));
     110            0 :         else if (atttypid == TIMESTAMPTZOID)
     111            0 :                 newdt = DirectFunctionCall3(timestamptz_in,
     112              :                                                                         CStringGetDatum("now"),
     113              :                                                                         ObjectIdGetDatum(InvalidOid),
     114              :                                                                         Int32GetDatum(-1));
     115              :         else
     116              :         {
     117            0 :                 ereport(ERROR,
     118              :                                 (errcode(ERRCODE_TRIGGERED_ACTION_EXCEPTION),
     119              :                                  errmsg("attribute \"%s\" of \"%s\" must be type TIMESTAMP or TIMESTAMPTZ",
     120              :                                                 args[0], relname)));
     121            0 :                 newdt = (Datum) 0;              /* keep compiler quiet */
     122              :         }
     123            0 :         newdtnull = false;
     124              : 
     125              :         /* Replace the attnum'th column with newdt */
     126            0 :         rettuple = heap_modify_tuple_by_cols(rettuple, tupdesc,
     127              :                                                                                  1, &attnum, &newdt, &newdtnull);
     128              : 
     129              :         /* Clean up */
     130            0 :         pfree(relname);
     131              : 
     132            0 :         return PointerGetDatum(rettuple);
     133            0 : }
        

Generated by: LCOV version 2.3.2-1