postgreSQL触发器

postgreSQL触发器,第1张

PostgreSQL 提供按行与按语句触发触发器。按行触发的触发器函数为触发语句影响的每一行执行一次;按语句触发的触发器函数为每条触发语句执行一次,而不管影响的行数。特别是,一个影响零行的语句将仍然导致按语句触发的触发器执行。这两种类型的触发器有时候分别叫做行级触发器和语句级触发器。触发器还通常分成 before 触发器和 after 触发器。语句级别的"before"触发器通常在语句开始做任何事情之前触发,而语句级别的"after"触发器在语句结束时触发。行级别的"before"触发器在对特定行进行 *** 作之前触发,而行级别的"after"触发器在语句结束的时候触发(但是在任何语句级别的"after"触发器之前)。

一个触发器是一种声明,告诉数据库应该在执行特定的 *** 作的时候执行特定的函数。触发器可以定义在一个 INSERT, UPDATE, DELETE 命令之前或者之后执行,要么是对每行执行一次,要么是对每条 SQL 语句执行一次。如果发生触发器事件,那么将在合适的时刻调用触发器函数以处理该事件。触发器函数必须在创建触发器之前,作为一个没有参数并且返回 trigger 类型的函数定义。触发器函数通过特殊的 TriggerData 结构接收其输入,而不是用普通的函数参数方式.

注意:

一.按语句触发的触发器应该总是返回 NULL.

二.如果必要,按行触发的触发器函数可以给调用它的执行者返回一行数据(一个类型为 HeapTuple 的数值),那些在 *** 作之前触发的触发器有以下选择

1. 它可以返回 NULL 以忽略对当前行的 *** 作。这就指示执行器不要执行调用该触

发器的行级别 *** 作(对特定行的插入或者更改)。

2.只用于 INSERT 和 UPDATE 行触发器:返回的行将成为被插入的行或者是成为

将要更新的行。这样就允许触发器函数修改将要被插入或者更新的行。

一个无意导致任何这类行为的在 *** 作之前触发的行级触发器必须仔细返回那个被当作新行传进来的行。也就是说,对于 INSERT 和 UPDATE 触发器而言,是 NEW 行,对于 DELETE触发器而言,是 OLD 行。

三. 对于在 *** 作之后触发的行级触发器,其返回值会被忽略,因此可以回NULL。

下面通过具体的例子来说明在postgresql中触发器的建立和使用(老规矩先写代码然后讲解)

#include <postgres.h>

#include <executor/spi.h>

#include <funcapi.h>

#include <commands/trigger.h>

#include <fmgr.h>

extern Datum pg_trigf(PG_FUNCTION_ARGS)

#ifdef PG_MODULE_MAGIC

PG_MODULE_MAGIC

#endif

PG_FUNCTION_INFO_V1(pg_trigf)

Datum

pg_trigf(PG_FUNCTION_ARGS)

{

TriggerData *trigdata = (TriggerData *)fcinfo->context

HeapTuple rettuple = NULL

int ret

int proc/* to store the value of SPI_processed (actual row number)*/

/* to be sure this function will be called by trigger */

if (!(CALLED_AS_TRIGGER(fcinfo))) {

elog(ERROR, "trigf: not called by triggermanager")

}

/* should be fired by statement */

if (TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) {

elog(ERROR, "cannot process row events")

}

/* should be fired before event */

if (TRIGGER_FIRED_AFTER(trigdata->tg_event)) {

elog(ERROR, "must be fired before event")

}

/* connect spi manager */

if ((ret = SPI_connect()) <0) {

elog(INFO, "SPI_connect failed: SPI_connectreturned: %d", ret)

return PointerGetDatum(rettuple)

}

/* check the permanent table name(perm_user) existsor not*/

ret = SPI_exec("SELECT tablename FROM pg_tables WHERE tablename LIKE'perm!_user' ESCAPE '!'", 1)

proc = SPI_processed

if (ret != SPI_OK_SELECT) {

elog(INFO, "SPI_exec execute error: user table.")

SPI_finish()

return PointerGetDatum(rettuple)

}

/* create the permanent table(perm_user) if it does not exist */

if (proc <1) {

/* create permanent table: perm_user */

ret = SPI_exec("CREATE TABLE perm_user ASSELECT * FROM tbl_user", 0)

if (ret != SPI_OK_SELINTO ) {

elog(INFO, "SPI_execexecute error: fail to create perm_user")

SPI_finish()

returnPointerGetDatum(rettuple)

}

/* set attribute to perm_user */

ret = SPI_exec("ALTER TABLE perm_user ADD PRIMARYKEY (user_name)", 0)

if (ret != SPI_OK_SELINTO) {

elog(INFO, "SPI_execexecute error: fail to add primary key to perm_user")

SPI_finish()

returnPointerGetDatum(rettuple)

}

/* set attribute to perm_user*/

ret = SPI_exec("ALTER TABLE perm_user ALTERuser_passwd SET NOT NULL", 0)

if (ret != SPI_OK_SELINTO) {

elog(INFO, "SPI_execexecute error: fail to set attribute to password.")

SPI_finish()

returnPointerGetDatum(rettuple)

}

}

.....

/* check the permanent table name(perm_member) exists or not */

ret = SPI_exec("SELECT tablename FROM pg_tables WHERE tablename LIKE'perm!_member' ESCAPE '!'", 1)

proc = SPI_processed

if (ret != SPI_OK_SELECT) {

elog(INFO, "SPI_exec execute errortbl_member")

SPI_finish()

return PointerGetDatum(rettuple)

}

/* create the permanent table(perm_member) if it does not exist */

if (proc <1) {

/* create permanent table: perm_member */

ret = SPI_exec("CREATE TABLE perm_member ASSELECT * FROM tbl_member", 0)

if (ret != SPI_OK_SELINTO) {

elog(INFO, "SPI_execexecute error")

SPI_finish()

returnPointerGetDatum(rettuple)

}

/* set attribute to perm_member */

ret = SPI_exec("ALTER TABLE perm_member ADDCONSTRAINT user_fk FOREIGN KEY (user_name) REFERENCES perm_user(user_name) ONDELETE CASCADE ON UPDATE CASCADE", 0)

if (ret != SPI_OK_UTILITY) {

elog(INFO, "SPI_execexecute error: fail to set attribute to user_name.")

SPI_finish()

returnPointerGetDatum(rettuple)

}

/* set attribute to perm_member */

ret = SPI_exec("ALTER TABLE perm_member ADDCONSTRAINT group_fk FOREIGN KEY (grp_name) REFERENCES perm_group(grp_name) ONDELETE CASCADE ON UPDATE CASCADE", 0)

if (ret != SPI_OK_UTILITY) {

elog(INFO, "SPI_execexecute error: fail to set attribute to grp_name.")

SPI_finish()

return PointerGetDatum(rettuple)

}

/* add primary key to perm_member */

ret = SPI_exec("ALTER TABLE perm_member ADDPRIMARY KEY (user_name, grp_name)", 0)

if (ret != SPI_OK_UTILITY) {

elog(INFO, "SPI_execexecute error: fail to add primary key to perm_member.")

SPI_finish()

returnPointerGetDatum(rettuple)

}

}

/*close connect with SPI manager */

SPI_finish()

/* return back must be NULL*/

return PointerGetDatum(rettuple)

}

这个函数写法与postgresql服务端函数的写法很相似, 但是不完全相同.具体需要注意的地方是:

1. 需要多添加头文件:#include <commands/trigger.h>

2. 这个函数的返回值一定是trigger类型的.

3. 函数的开始最好确认这个函数是供触发器调用的并且明确一下自己要写的触发器的类型是什么,然后做一下判断,以免别的语句也触发的触发器.

二. 接下来的事情是编译:

gcc -fpic -c trigger.c-I/usr/local/postgreSQL/include/postgresql/server

gcc -shared -o trigger.so trigger.o

如果不明白可以参考手册(说句题外话,手册的作用实在是太大了,在手册中也提供了一例子).

三. 在数据库中创建函数和触发器:

/* create a trigger used to write memory and configmemory */

CREATE OR REPLACE FUNCTION pg_trigf() RETURNS trigger

AS 'filename'

LANGUAGE C IMMUTABLESTRICT

CREATE TRIGGER tbuser BEFORE INSERT OR UPDATE OR DELETE

ON tbl_user FOR EACH STATEMENT

EXECUTE PROCEDURE pg_trigf()

CREATE TRIGGER tbgroup BEFORE INSERT OR UPDATE OR DELETE

创建的触发器是语句触发器,这个和手册上的不一样, 手册上的是行触发器.

然后在数据库中使用SQL语句就可以看到触发器的效果了.

PostgreSQL中大概是这样创建触发器:

首先需要创建触发器调用的函数:

create or replace function tg_update()

returns trigger

as $$

begin

    -- 更新SQL, 可以使用NEW和OLD分别取新记录和旧记录

    update student1_stats src

    set ....

    where ...

    return null   -- 要返回null

end

$$ language plpgsql

然后,创建触发器:

create trigger trigger_name

after update on studen1

for each row execute procedure tg_update()


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/bake/11724859.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-18
下一篇 2023-05-18

发表评论

登录后才能评论

评论列表(0条)

保存