Re: noupcol code cleanup - Mailing list pgsql-sql

From Ron Peterson
Subject Re: noupcol code cleanup
Date
Msg-id 20030110184631.GA14535@mtholyoke.edu
Whole thread Raw
In response to Re: insert rule doesn't see id field  (Ron Peterson <rpeterso@mtholyoke.edu>)
List pgsql-sql
Well, I went through this again myself, and fixed a lot of stuff.  I'm
going to drop this thread, but didn't want the last chunk of code I
posted to be so crappy.  This is what I have come up with, FWIW:

#include "executor/spi.h"     /* this is what you need to work with SPI */
#include "commands/trigger.h" /* and triggers */
#include "utils/lsyscache.h"  /* for get_typlenbyval */

extern Datum noupcols (PG_FUNCTION_ARGS);

/*
noupcols () -- revoke permission on column(s)

e.g.

CREATE FUNCTION noupcols ()    RETURNS opaque    AS '/usr/lib/postgresql/lib/noupcols.so'   LANGUAGE 'C';

CREATE TRIGGER person_noupcols   BEFORE UPDATE ON person   FOR EACH ROW   EXECUTE PROCEDURE noupcols( 'name_last', 'id'
);

Based on code from contrib/noup.c

The approach adopted here is to set the values of all of the columns
specified by noupcols to their old values.
*/

PG_FUNCTION_INFO_V1 (noupcols);

Datum
noupcols (PG_FUNCTION_ARGS)
{   TriggerData *trigdata = (TriggerData *) fcinfo->context;   Trigger     *trigger;           /* to get trigger name
*/  Relation    rel;                /* triggered relation */   char        **args;             /* arguments: column
names*/   int         ncols;              /* # of args specified in CREATE TRIGGER */   int         *colindices;
/*array of column indices to modify */   Datum       *oldcolvals;        /* old column values */   Datum
*newcolval;        /* new column value */   HeapTuple   oldtuple = NULL;    /* tuple before being modified */
HeapTuple  newtuple = NULL;    /* new tuple after user-specified update */   HeapTuple   newnewtuple = NULL; /* tuple
toreturn, after restoring newtuple's protected columns to their old values */   TupleDesc   tupdesc;            /*
tupledescription */   bool        isnull;             /* to know is some column NULL or not */   Oid         oid;
        /* is Datum of type ByVal? */   bool        typByVal;           /* is Datum of type ByVal? */   int16
typLen;            /* Datum size */   int         ret;   int         i;
 
   if (!CALLED_AS_TRIGGER (fcinfo))       elog(ERROR, "noup: not fired by trigger manager");
   if (TRIGGER_FIRED_FOR_STATEMENT (trigdata->tg_event))       elog (ERROR, "noup: can't process STATEMENT events");
   if (TRIGGER_FIRED_BY_INSERT (trigdata->tg_event))       elog (ERROR, "noup: can't process INSERT events");
   else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))       elog (ERROR, "noup: can't process DELETE events");
   oldtuple = trigdata->tg_trigtuple;   newtuple = trigdata->tg_newtuple;
   trigger = trigdata->tg_trigger;   rel = trigdata->tg_relation;
   tupdesc = rel->rd_att;
   ncols = trigger->tgnargs;   args = trigger->tgargs;
   colindices = (int *) palloc (ncols * sizeof (int));
   /* Connect to SPI manager */   if ((ret = SPI_connect()) < 0)       elog (ERROR, "noupcol: SPI_connect returned %d",
ret);
   /* Allocate space to place column values */   oldcolvals = (Datum*) palloc (ncols * sizeof (Datum));   newcolval =
(Datum*)palloc (sizeof (Datum));
 
   /* For each column ... */   for (i = 0; i < ncols; i++)   {       /* get index of column in tuple */
colindices[i]= SPI_fnumber (tupdesc, args[i]);
 
       /* Bad guys may give us un-existing column in CREATE TRIGGER */       if (colindices[i] ==
SPI_ERROR_NOATTRIBUTE){           elog (ERROR, "noupcols: there is no attribute '%s' in relation '%s'",
args[i],                SPI_getrelname (rel));       }
 
       /* Get previous value of column */       oldcolvals[i] = SPI_getbinval (oldtuple, tupdesc, colindices[i],
&isnull);      *newcolval = SPI_getbinval (newtuple, tupdesc, colindices[i], &isnull);
 
      /* need this for datumIsEqual, below */       oid = SPI_gettypeid (tupdesc, colindices[i]);
get_typlenbyval(oid, &typLen, &typByVal );
 
        /* if an update is attempted on a locked column, post a notification that it isn't allowed */       if (!
datumIsEqual(oldcolvals[i], *newcolval, typByVal, typLen)) {           elog (NOTICE, "noupcols: attribute '%s' in
relation'%s' is locked",                 args[i],                 SPI_getrelname (rel));       }          }
 
   /* Restore protected columns to their old values */   newnewtuple = SPI_modifytuple (rel, newtuple, ncols,
colindices,oldcolvals, NULL);
 
   if (SPI_result == SPI_ERROR_ARGUMENT) {       elog (ERROR, "noupcols: bad argument to SPI_modifytuple\n");   }
   if (SPI_result == SPI_ERROR_NOATTRIBUTE) {       elog (ERROR, "noupcols: bad attribute value passed to
SPI_modifytuple\n");  }
 
   pfree (oldcolvals);   pfree (newcolval);   pfree (colindices);   SPI_finish ();
   return PointerGetDatum (newnewtuple);
}


-- 
Ron Peterson                          -o)
Network & Systems Manager             /\\
Mount Holyoke College                _\_v
http://www.mtholyoke.edu/~rpeterso   ---- 


pgsql-sql by date:

Previous
From: "Josh Berkus"
Date:
Subject: Re: Table Design Questions
Next
From: "Dave A."
Date:
Subject: function does not exist