LCOV - code coverage report
Current view: top level - contrib/spi - moddatetime.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 0.0 % 37 0
Test Date: 2026-02-17 17:20:33 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              :     Trigger    *trigger;        /* to get trigger name */
      37              :     int         nargs;          /* # of arguments */
      38              :     int         attnum;         /* positional number of field to change */
      39              :     Oid         atttypid;       /* type OID of field to change */
      40              :     Datum       newdt;          /* The current datetime. */
      41              :     bool        newdtnull;      /* null flag for it */
      42              :     char      **args;           /* arguments */
      43              :     char       *relname;        /* triggered relation name */
      44              :     Relation    rel;            /* triggered relation */
      45            0 :     HeapTuple   rettuple = NULL;
      46              :     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              :         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              : }
        

Generated by: LCOV version 2.0-1