LCOV - code coverage report
Current view: top level - contrib/spi - moddatetime.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 0.0 % 37 0
Test Date: 2026-07-03 19:57:34 Functions: 0.0 % 3 0
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 0.0 % 36 0

             Branch data     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