I had a stupid error today, because I did not call a SPI_finish on function
exit. I can't see it in Your code either. Too late for analyze code, my ayes
are closing...
Regards !
----- Original Message -----
From: "Alastair Bell Turner" <bell@tangent.co.za>
To: <pgsql-interfaces@postgresql.org>
Sent: Wednesday, June 23, 2004 4:48 PM
Subject: [INTERFACES] SPI problem
> I'm trying to build a c trigger with the Server Programming Interface and
> not having a lot of joy.
>
> The code is based mainly on the example code on the documentation site.
>
> If it return trigdata->tg_trigtuple with PointerGetDatum it seems to work.
> Trying to return TempTuple with PointerGetDatum gives me no error at
> execution but I get an error the next time I try to select from the table
> which I have to DROP DATABASE to get rid of which leads me to believe that
> it returns bad data. If I try to return trigdata->tg_trigtuple or
> TempTuple using TupleGetDatum it fails at insert time with 'server closed
> the connection unexpectedly ...'.
>
> I can't see how my code doesn't match the prototypes, hopefully it's a
> dumb error that someone can point out to me quickly ... :)
>
> + //Trigger to add hashes to rows on insert
> +
> + #include "postgres.h"
> + #include "funcapi.h"
> + #include "executor/spi.h"
> + #include "commands/trigger.h"
> + #include <openssl/sha.h>
> +
> + extern Datum hashtrigger(PG_FUNCTION_ARGS);
> +
> + //Field number of versioning data
> + #define VERSIONFIELD 5
> + #define WHOLEHASHFIELD 36
> + #define ETLHASHFIELD 37
> +
> + PG_FUNCTION_INFO_V1(hashtrigger);
> +
> + Datum hashtrigger(PG_FUNCTION_ARGS) {
> + TriggerData *trigdata = (TriggerData *) fcinfo->context;
> + HeapTuple TempTuple;
> + TupleTableSlot *TupleSlot;
> + TupleDesc TupleDescription;
> + int Looper;
> +
> + int Oops = SPI_connect();
> +
> + static char *hexdigits = "0123456789abcdef";
> + static int HashColumns[] = {6, 7};
> + static int ColumnsAlteredVersioning[] = {1, 5, 6, 7};
> + //Suggested change here - insert checks for if this is null and only
> do the first operation of it is
> + static int ColumnsAlteredETL[] = {1, 3, 5, 6, 7};
> +
> + // make sure it's called as a trigger at all
> + if (!CALLED_AS_TRIGGER(fcinfo))
> + elog(ERROR, "hashtrigger: not called by trigger manager");
> +
> + //palloc memory for hashes
> + char **hashes = (char **) palloc(4 * sizeof(char *));
> + //The hashes will be put into the 20 arrays, and then expanded into th
> 40s into hex
> + hashes[0] = (char *) palloc(20 * sizeof(char));
> + hashes[1] = (char *) palloc(40 * sizeof(char));
> + hashes[2] = (char *) palloc(40 * sizeof(char));
> +
> + //Palloc array values for nulling out unused fields in tuple
> + Datum *NewColumnValues = (Datum *) palloc(sizeof(ColumnsAlteredETL) /
> sizeof(int) * sizeof(Datum));
> +
> + //Since I cant get the nulls parameter of SPI_modifytuple to work it
> appears we're going to have to overwrite all the entries we want null
> + for (Looper = 0; Looper < (sizeof(ColumnsAlteredETL) / sizeof(int));
> Looper++) {
> + NewColumnValues[Looper] = PointerGetDatum("");
> + }
> +
> + //Use SPI_modifytuple to null out versioning field
> + TempTuple = SPI_modifytuple(trigdata->tg_relation,
> trigdata->tg_trigtuple, sizeof(ColumnsAlteredVersioning) / sizeof(int),
> ColumnsAlteredVersioning, NewColumnValues, NULL);
> +
> + //Determine hash of TempTuple->t_len bytes at pointer
> TempTuple->t_data and store the first element of allocated array
> + SHA1((const unsigned char *) TempTuple->t_data, TempTuple->t_len,
> hashes[0]);
> + for (Looper = 0; Looper < 20; Looper++) {
> + //Spread hash out into hex coded form
> + hashes[1][Looper * 2] = hexdigits[(hashes[0][Looper] >> 4) & 0x0f];
> + hashes[1][(Looper * 2) + 1] = hexdigits[hashes[0][Looper] & 0x0f];
> + }
> +
> + //Display hash for debug
> + elog (INFO, "hashtrigger returns %s for versionless hash of row",
> hashes[1]);
> +
> + //Use SPI_modifytuple to null out non etl fields
> + TempTuple = SPI_modifytuple(trigdata->tg_relation,
> trigdata->tg_trigtuple, sizeof(ColumnsAlteredETL) / sizeof(int),
> ColumnsAlteredETL, NewColumnValues, NULL);
> +
> + //Determine hash of TempTuple->t_len bytes at pointer
> TempTuple->t_data and store the first element of allocated array
> + SHA1((const unsigned char *) TempTuple->t_data, TempTuple->t_len,
> hashes[0]);
> + for (Looper = 0; Looper < 20; Looper++) {
> + //Spread hash out into hex coded form
> + hashes[2][Looper * 2] = hexdigits[(hashes[0][Looper] >> 4) & 0x0f];
> + hashes[2][(Looper * 2) + 1] = hexdigits[hashes[0][Looper] & 0x0f];
> + }
> +
> + //Display hash for debug
> + elog (INFO, "hashtrigger returns %s for material hash of row",
> hashes[2]);
> +
> + //Use SPI_modifytuple to write hash into fields on record into result
> tuple
> + // NewColumnValues[0] = PointerGetDatum(hashes[1]);
> + // NewColumnValues[1] = PointerGetDatum(hashes[2]);
> +
> + TempTuple = SPI_modifytuple(trigdata->tg_relation,
> trigdata->tg_trigtuple, 2, HashColumns, NewColumnValues, NULL);
> +
> + //Free allocated memory
> +
> + //Return result tuple
> + TupleSlot = TupleDescGetSlot(trigdata->tg_relation->rd_att);
> + // return TupleGetDatum(TupleDestination, TempTuple);
> + return TupleGetDatum(TupleSlot, trigdata->tg_trigtuple);
> + // return PointerGetDatum(trigdata->tg_trigtuple);
> + }
>
> Thank you very much
>
> Alastair "Bell" Turner
>
> ---------------------------(end of broadcast)---------------------------
> TIP 4: Don't 'kill -9' the postmaster
>