LCOV - code coverage report
Current view: top level - contrib/spi - moddatetime.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 0 37 0.0 %
Date: 2025-04-01 14:15:22 Functions: 0 3 0.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 1.14