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

Generated by: LCOV version 1.14