У меня есть сервер PostgreSQL 9.0, и я использую наследие для некоторых таблиц, поэтому мне приходится моделировать внешние ключи с помощью триггеров, подобных этому:
CREATE OR REPLACE FUNCTION othertable_before_update_trigger()
RETURNS trigger AS
$BODY$
DECLARE
sql VARCHAR;
rows SMALLINT;
BEGIN
IF (NEW.parenttable_id IS DISTINCT FROM OLD.parenttable_id) THEN
sql := 'SELECT id '
|| 'FROM parentTable '
|| 'WHERE id = ' || NEW.parenttable_id || ';';
BEGIN
EXECUTE sql;
GET DIAGNOSTICS rows = ROW_COUNT;
EXCEPTION
WHEN OTHERS THEN
RAISE EXCEPTION 'Error when I try find in parentTable the id %. SQL: %. ERROR: %',
NEW.parenttable_id,sql,SQLERRM;
END;
IF rows = 0 THEN
RAISE EXCEPTION 'Not found a row in parentTable with id %. SQL: %.',NEW.parenttable_id,sql;
END IF;
END IF;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;
Но из-за производительности я пытаюсьсоздать эквивалентный триггер в коде C:
#include "postgres.h"
#include "executor/spi.h" /* this is what you need to work with SPI */
#include "commands/trigger.h" /* ... and triggers */
#ifdef PG_MODULE_MAGIC
PG_MODULE_MAGIC;
#endif
extern Datum othertable_before_update_trigger(PG_FUNCTION_ARGS);
PG_FUNCTION_INFO_V1(othertable_before_update_trigger);
Datum
othertable_before_update_trigger(PG_FUNCTION_ARGS) {
TriggerData *trigdata = (TriggerData *) fcinfo->context;
TupleDesc tupdesc;
HeapTuple rettuple;
bool isnull;
int ret, i;
/* make sure it's called as a trigger at all */
if (!CALLED_AS_TRIGGER(fcinfo))
elog(ERROR, "othertable_before_update_trigger: not called by trigger manager");
/* tuple to return to executor */
if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
rettuple = trigdata->tg_newtuple;
else
rettuple = trigdata->tg_trigtuple;
tupdesc = trigdata->tg_relation->rd_att;
/* connect to SPI manager */
if ((ret = SPI_connect()) < 0)
elog(ERROR, "othertable_before_update_trigger (fired %s): SPI_connect returned %d", "before", ret);
[A]
[B]
return PointerGetDatum(rettuple);
}
Мне нужно заполнить код:
[A] : получить предыдущий иновые значения для parenttable_id .С помощью:
int32 att = DatumGetInt32 (heap_getattr (rettuple, 1, tupdesc, & isnull));
или
int32 att = DatumGetInt32 (SPI_getbinval (rettuple, tupdes), tupdes, & isnull));
Я могу получить только старое значение parenttable_id , но не новое значение.Даже если я попытаюсь использовать имя столбца вместо номера с:
GetAttributeByName (rettuple->t_data, "parenttable_id", &isnull);
Ошибка получения: тип записи не зарегистрирован
- [B] : выполнить запрос SELECT id FROM parentTable WHERE id = NEW.parenttable_id
Я нашел функцию SPI_execute_with_args , но яне нашел примеров этого для моего случая.
Заранее спасибо.