trigger functions and access to datatypes

Brook Milligan <brook@trillium.nmsu.edu>

From: Brook Milligan <brook@trillium.NMSU.Edu>
To: hackers@postgreSQL.org
Date: 1999-06-14T21:32:11Z
Lists: pgsql-hackers
I'm trying to write some trigger functions that fill in the values of
some fields with functions of other fields.  Everything is fine,
except that I am unsure how to create Datum representations of the new
fields.  Presumably I need to do something like the following:

	Point f (int32 i, int32 j);
	int32 i, j;
	Point p;
	Datum d;
	p = f (i, j);			/* ??? - how to create/access Point? */
	d = PointGetDatum (&p);		/* ??? - how to convert to Datum? */
	rettuple = SPI_modifytuple (rel, rettuple, 1, attnum, &d, NULL);

But I don't know how to get at the internal representations I need.
In this case I need to create a Point data type.  I can use the
structure in geo_decls.h fine, but is that the "correct" procedure?
Once I have a Point structure created, how do I convert that to a
Datum (or Datum*) for an argument to SPI_modifytuple?

So that I haven't missed any details, I'm including the full version
of my trigger below.

Thanks for your help.

Cheers,
Brook

===========================================================================

/*
 * plot_corners.c
 *
 * trigger for keeping corners in sync with distances
 */


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

HeapTuple plot_corners (void);

				/* coordinants of corners */
typedef struct _corners
{
  Point LL;
  Point LR;
  Point UL;
  Point UR;
} Corners;

				/* calculate corner positions */
static Corners calc_corners (int n_distances, const int32 * d);

#define N_DISTANCES 6
static char * distance_names [N_DISTANCES] = { "d12", "d13", "d14", "d23", "d24", "d34" };

#define N_CORNERS 4
static char * corner_names [N_CORNERS] = { "ll", "lr", "ul", "ur" };

HeapTuple plot_corners ()
{
  Relation rel;			/* triggered relation */
  char *relname;		/* triggered relation name */
  TupleDesc tupdesc;		/* tuple description */

  HeapTuple rettuple = NULL;	/* tuple to return */

  int d_attnum [N_DISTANCES];	/* distance attribute numbers */
  int32 d [N_DISTANCES];	/* distances between corners */

  int c_attnum [N_CORNERS];	/* corner attribute numbers */
  Corners corners;		/* coordinants of corners */
  Datum c [N_CORNERS];		/* coordinants of corners */

  bool isnull;
  int i;

  if (!CurrentTriggerData)
    elog (ERROR, "plot_corners: triggers are not initialized");

  rel = CurrentTriggerData -> tg_relation;
  relname = SPI_getrelname (rel);
  tupdesc = rel -> rd_att;

  if (CurrentTriggerData -> tg_trigger -> tgnargs != 0)
    elog (ERROR, "plot_corners (%s): no arguments were expected", relname);

  if (!TRIGGER_FIRED_FOR_ROW (CurrentTriggerData -> tg_event))
    elog (ERROR, "plot_corners (%s): must process ROW events", relname);
  if (TRIGGER_FIRED_FOR_STATEMENT (CurrentTriggerData -> tg_event))
    elog (ERROR, "plot_corners (%s): can't process STATEMENT events", relname);

  if (!TRIGGER_FIRED_BEFORE (CurrentTriggerData -> tg_event))
    elog (ERROR, "plot_corners (%s): must be fired before event", relname);
  if (TRIGGER_FIRED_AFTER (CurrentTriggerData -> tg_event))
    elog (ERROR, "plot_corners (%s): can't be fired after event", relname);

  if (TRIGGER_FIRED_BY_INSERT (CurrentTriggerData -> tg_event))
    rettuple = CurrentTriggerData -> tg_trigtuple;
  else if (TRIGGER_FIRED_BY_UPDATE (CurrentTriggerData -> tg_event))
    rettuple = CurrentTriggerData -> tg_newtuple;
  else if (TRIGGER_FIRED_BY_DELETE (CurrentTriggerData -> tg_event))
    elog (ERROR, "plot_corners (%s): can't process DELETE events", relname);
  else
    elog (ERROR, "plot_corners (%s): unknown trigger event", relname);

  CurrentTriggerData = NULL;

  for (i = 0; i < N_DISTANCES; i++)
    {
      d_attnum [i] = SPI_fnumber (tupdesc, distance_names [i]);
      if (d_attnum [i] < 0)
	elog (ERROR, "plot_corners (%s): there is no attribute %s", relname, distance_names [i]);
      if (SPI_gettypeid (tupdesc, d_attnum [i]) != INT4OID)
	elog (ERROR, "plot_corners (%s): attribute %s must be of INT4 type",
	      relname, distance_names [i]);
      d [i] = DatumGetInt32 (SPI_getbinval (rettuple, tupdesc, d_attnum [i], &isnull));
      if (isnull)
	elog (ERROR, "plot_corners (%s): attribute %s must not be NULL",
	      relname, distance_names [i]);
    }

  for (i = 0; i < N_CORNERS; i++)
    {
      c_attnum [i] = SPI_fnumber (tupdesc, corner_names [i]);
      if (c_attnum [i] < 0)
	elog (ERROR, "plot_corners (%s): there is no attribute %s", relname, corner_names [i]);
      if (SPI_gettypeid (tupdesc, c_attnum [i]) != POINTOID)
	elog (ERROR, "plot_corners (%s): attribute %s must be of POINT type",
	      relname, distance_names [i]);
    }

  corners = calc_corners (N_DISTANCES, d);

  c [0] = PointGetDatum (&corners.LL); /* ??? - how to convert Point to Datum? */
  c [1] = PointGetDatum (&corners.LR);
  c [2] = PointGetDatum (&corners.UL);
  c [3] = PointGetDatum (&corners.UR);

  rettuple = SPI_modifytuple (rel, rettuple, N_CORNERS, c_attnum, c, NULL);
  if (rettuple == NULL)
    elog (ERROR, "plot_corners (%s): %d returned by SPI_modifytuple",
	  relname, SPI_result);

  pfree (relname);

  return (rettuple);
}

				/* calculate corner positions */
Corners calc_corners (int n_distances, const int32 * d)
{
  Corners c;

  c.LL.x = 0;			/* ??? - how to initialize/access Point values? */
  c.LL.y = 0;

  c.LR.x = 1;
  c.LR.y = 0;

  c.UL.x = 0;
  c.UL.y = 1;

  c.UR.x = 1;
  c.UR.y = 1;

  return c;
}