C中的等效plpgsql触发器

我有一个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); } 

我需要填写以下代码:

  1. [A] :获取parenttable_id的先前值和新值。 附:

    int32 att = DatumGetInt32(heap_getattr(rettuple,1,tupdesc,&isnull));

    要么

    int32 att = DatumGetInt32(SPI_getbinval(rettuple,tupdesc,1,&isnull));

我只能得到parenttable_id的旧值,但不能得到新值。 即使我尝试使用列名而不是其编号:

 GetAttributeByName (rettuple->t_data, "parenttable_id", &isnull); 

获取错误: 记录类型尚未注册

  1. [B] :执行查询SELECT id FROM parentTable WHERE id = NEW.parenttable_id

我找到了函数SPI_execute_with_args ,但我没有找到这个例子。

提前致谢。

这并没有让我感觉到这种触发器会从转移到C中受益。您可以利用pl / pgsql中的大量缓存计划,这可能比移动到C更有助于加快速度。 此外,这里有两个重要的性能红旗,这让我觉得值得一试。

首先,EXCEPTION块具有显着的性能成本。 你在这里所做的只是以更友好的方式报告例外情况。 如果性能问题,你最好只删除它。

第二个是您的EXECUTE,这意味着永远不会缓存查询计划。 你真的应该把它改成直接查询。

当你把它与C语言触发器导致崩溃或后端更糟糕的可能性结合起来时,我认为你将花费大量精力来重写触发器,以获得比通过在pl中重写它所获得的性能提升更少的性能。 / pgsql的。