PostgreSQL7.0手册-程序员手册 -47. 触发器
东西--如果你不想用另一条记录覆盖此记录(INSERT)或忽略操作.
tg_newtuple
如果是 UPDATE,这是一个指向新版本的记录的指针,如果是 INSERT 或 DELETE,就是 NULL这就是你将返回给执行器的东西-- 如果你是 UPDATE 并且你不想用另一条记录替换这条记录或忽略操作.
tg_trigger
是一个指向结构 Trigger 的指针,该结构在 src/include/utils/rel.h 里定义:
typedef struct Trigger
{
Oid tgoid;
char *tgname;
Oid tgfoid;
FmgrInfo tgfunc;
int16 tgtype;
bool tgenabled;
bool tgisconstraint;
bool tgdeferrable;
bool tginitdeferred;
int16 tgnargs;
int16 tgattr[FUNC_MAX_ARGS];
char **tgargs;
} Trigger;
tgname 是触发器的名称,tgnargs 是在 tgargs 里参数的数量,tgargs 是一个指针数组,数组里每个指针指向在 CREATE TRIGGER 语句里声明的参数.其他成员只在内部使用.
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
数据改变的可视性
Postgres 数据修改的可视性规则:在查询执行过程中,由查询本身造成的数据修改(通过 SQL-函数,SPI-函数,触发器)对查询扫描而言是不可见的.例如,在查询
INSERT INTO a SELECT * FROM a
里,插入的记录对 SELECT 的扫描是不可见的.实际上,这么做在数据库内部形成非递归的数据库表的复制(当然是要受到唯一索引规则的制约的)
但是请记住在 SPI 文挡里关于可视性的注释:
由查询 Q 造成的改变可以为查询 Q 以后运行的查询可见,不管这些查询
是在查询 Q 内部开始运行(在 Q 运行期间)的还是Q运行完毕后开始运行的
这些对触发器而言也是正确的,尽管被插入的记录 (tg_trigtuple)对 BEFORE 触发器是不可见的,这个刚被插入的记录却可以被一个 AFTER 触发器看到,并且对所有这个(触发器)以后的所有 BEFORE/AFTER 触发器均可见!
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
例子
在 src/test/regress/regress.c 和 contrib/spi 里有更复杂的例子.
这里是一个非常简单的触发器使用的例子.函数 trigf 报告在被触发的关系 ttest 中记录数量,并且如果查询试图把 NULL 插入到 x 里(例如 -它做为一个 NOT NULL 约束但不退出事务的约束)时略过操作.
#include "executor/spi.h" /* this is what you need to work with SPI */
#include "commands/trigger.h" /* -"- and triggers */
HeapTuple trigf(void);
HeapTuple
trigf()
{
TupleDesc tupdesc;
HeapTuple rettuple;
char *when;
bool checknull = false;
bool isnull;
int ret, i;
if (!CurrentTriggerData)
elog(WARN, "trigf: triggers are not initialized");
/* tuple to return to Executor */
if (TRIGGER_FIRED_BY_UPDATE(CurrentTriggerData->tg_event))
rettuple = CurrentTriggerData->tg_newtuple;
else
rettuple = CurrentTriggerData->tg_trigtuple;
/* check for NULLs ? */
if (!TRIGGER_FIRED_BY_DELETE(CurrentTriggerData->tg_event) &&
TRIGGER_FIRED_BEFORE(CurrentTriggerData->tg_event))
checknull = true;
if (TRIGGER_FIRED_BEFORE(CurrentTriggerData->tg_event))
when = "before";
else
when = "after ";
tupdesc = CurrentTriggerData->tg_relation->rd_att;
CurrentTriggerData = NULL;
/* Connect to SPI manager */
if ((ret = SPI_connect()) < 0)
elog(WARN, "trigf (fired %s): SPI_connect returned %d", when, ret);
/* Get number of tuples in relation */
ret = SPI_exec("select count(*) from ttest", 0);
if (ret < 0)
elog(WARN, "trigf (fired %s): SPI_exec returned %d", when, ret);
i = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull);
elog (NOTICE, "trigf (fired %s): there are %d tuples in ttest", when, i);
SPI_finish();
if (checknull)
{
i = SPI_getbinval(rettuple, tupdesc, 1, &isnull);
if (isnull)
tg_newtuple
如果是 UPDATE,这是一个指向新版本的记录的指针,如果是 INSERT 或 DELETE,就是 NULL这就是你将返回给执行器的东西-- 如果你是 UPDATE 并且你不想用另一条记录替换这条记录或忽略操作.
tg_trigger
是一个指向结构 Trigger 的指针,该结构在 src/include/utils/rel.h 里定义:
typedef struct Trigger
{
Oid tgoid;
char *tgname;
Oid tgfoid;
FmgrInfo tgfunc;
int16 tgtype;
bool tgenabled;
bool tgisconstraint;
bool tgdeferrable;
bool tginitdeferred;
int16 tgnargs;
int16 tgattr[FUNC_MAX_ARGS];
char **tgargs;
} Trigger;
tgname 是触发器的名称,tgnargs 是在 tgargs 里参数的数量,tgargs 是一个指针数组,数组里每个指针指向在 CREATE TRIGGER 语句里声明的参数.其他成员只在内部使用.
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
数据改变的可视性
Postgres 数据修改的可视性规则:在查询执行过程中,由查询本身造成的数据修改(通过 SQL-函数,SPI-函数,触发器)对查询扫描而言是不可见的.例如,在查询
INSERT INTO a SELECT * FROM a
里,插入的记录对 SELECT 的扫描是不可见的.实际上,这么做在数据库内部形成非递归的数据库表的复制(当然是要受到唯一索引规则的制约的)
但是请记住在 SPI 文挡里关于可视性的注释:
由查询 Q 造成的改变可以为查询 Q 以后运行的查询可见,不管这些查询
是在查询 Q 内部开始运行(在 Q 运行期间)的还是Q运行完毕后开始运行的
这些对触发器而言也是正确的,尽管被插入的记录 (tg_trigtuple)对 BEFORE 触发器是不可见的,这个刚被插入的记录却可以被一个 AFTER 触发器看到,并且对所有这个(触发器)以后的所有 BEFORE/AFTER 触发器均可见!
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
例子
在 src/test/regress/regress.c 和 contrib/spi 里有更复杂的例子.
这里是一个非常简单的触发器使用的例子.函数 trigf 报告在被触发的关系 ttest 中记录数量,并且如果查询试图把 NULL 插入到 x 里(例如 -它做为一个 NOT NULL 约束但不退出事务的约束)时略过操作.
#include "executor/spi.h" /* this is what you need to work with SPI */
#include "commands/trigger.h" /* -"- and triggers */
HeapTuple trigf(void);
HeapTuple
trigf()
{
TupleDesc tupdesc;
HeapTuple rettuple;
char *when;
bool checknull = false;
bool isnull;
int ret, i;
if (!CurrentTriggerData)
elog(WARN, "trigf: triggers are not initialized");
/* tuple to return to Executor */
if (TRIGGER_FIRED_BY_UPDATE(CurrentTriggerData->tg_event))
rettuple = CurrentTriggerData->tg_newtuple;
else
rettuple = CurrentTriggerData->tg_trigtuple;
/* check for NULLs ? */
if (!TRIGGER_FIRED_BY_DELETE(CurrentTriggerData->tg_event) &&
TRIGGER_FIRED_BEFORE(CurrentTriggerData->tg_event))
checknull = true;
if (TRIGGER_FIRED_BEFORE(CurrentTriggerData->tg_event))
when = "before";
else
when = "after ";
tupdesc = CurrentTriggerData->tg_relation->rd_att;
CurrentTriggerData = NULL;
/* Connect to SPI manager */
if ((ret = SPI_connect()) < 0)
elog(WARN, "trigf (fired %s): SPI_connect returned %d", when, ret);
/* Get number of tuples in relation */
ret = SPI_exec("select count(*) from ttest", 0);
if (ret < 0)
elog(WARN, "trigf (fired %s): SPI_exec returned %d", when, ret);
i = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull);
elog (NOTICE, "trigf (fired %s): there are %d tuples in ttest", when, i);
SPI_finish();
if (checknull)
{
i = SPI_getbinval(rettuple, tupdesc, 1, &isnull);
if (isnull)

