当前位置:   article > 正文

PostgreSQL中的事件触发器_postgresql 事件触发器

postgresql 事件触发器

作者:瀚高PG实验室(Highgo PG Lab) 丹心明月

注:本文章主要翻译自《PostgreSQL 13.0 Documentation》第三十九章

PostgreSQL还提供了事件触发器实现第38章探讨的触发器机制。与普通的触发器不同,事件触发器针对整个数据库,且可捕获DDL事件。

 

事件触发器可使用具有事件触发器支持的过程语言或C编写,但不可使用纯SQL编写。

39.1 事件触发器概览

事件触发器在相关事件发生时触发。当前,支持的事件有ddl_command_start,ddl_command_end,table_rewrite和sql_drop。

 

ddl_command_start事件在执行CREATE,ALTER,DROP,SECURITY LABEL,COMMENT,GRANT或REVOKE命令前发生。在事件触发器触发之前,不会检查所影响的对象是否存在。不过,针对共享对象(如数据库,角色和表空间)或对事件触发器本身的DDL命令不会触发该事件。事件触发器机制不支持此类对象类型。因为SELECT INTO命令等价于CREATE TABLE AS,故也会触发ddl_command_start事件。

 

ddl_comand_end事件在相同DDL命令之后触发。触发器在操作发生后(事务提交前)触发。

 

sql_drop在删除数据库对象时触发(在ddl_comand_end之前)。可使用pg_event_trigger_dropped_objects()列出删除的数据库对象。该触发器是在将对象从系统视图删除后执行的。

 

table_rewrite事件在表被ALTER TABLE和ALTER TYPE重写前触发。

 

事件触发器支持的命令列表,请参见第39.2节

 

事件触发器使用命令CREATE EVENT TRIGGER命令创建。创建事件触发器之前,需要先创建返回event_trigger类型的触发器函数。

 

触发器定义中,也可使用WHEN指定触发的特定命令。

39.2 事件触发器触发矩阵

表39.1列出了事件触发器支持的所有命令。

39.3 使用C编写事件触发器函数

本节介绍使用C编写事件触发器函数相关信息。

 

事件触发器函数必须使用“version  1”函数管理接口。

 

事件触发器管理者调用函数时,不会传递普通参数,而是一个指向EventTriggerData架构的上下文指针。C函数可使用如下宏检查是否由时间触发器管理者调用:

CALLED_AS_EVENT_TRIGGER(fcinfo)

扩展为:

((fcinfo)->context != NULL && IsA((fcinfo)->context, EventTriggerData))

如果返回true,则可将fcinfo->context转换为EventTriggerData *并使用EventTriggerData结构。函数不可对EventTriggerData结构或其指向的任何数据进行修改。

 

struct EventTriggerData在commands/event_trigger.h中定义:

  1. typedef struct EventTriggerData
  2. {
  3. NodeTag type;
  4. const char* event; /* event name */
  5. Node* parsetree; /* parse tree */
  6. CommandTag tag; /* command tag */
  7. } EventTriggerData;

参数解释:

type

    T_EventTriggerData。

event

    触发事件。

parsetree

    指向命令解析树的指针。

tag

    命令标签。

 

事件触发器仅可返回NULL指针。

39.4 事件触发器示例

此处为C编写的事件触发器函数示例。

 

函数noddl每次调用时均返回异常。触发事件为ddl_command_start。其作用为禁止运行所有的DDL命令。

 

以下为触发器函数的源码:

  1. #include "postgres.h"
  2. #include "commands/event_trigger.h"
  3. PG_MODULE_MAGIC;
  4. PG_FUNCTION_INFO_V1(noddl);
  5. Datum
  6. noddl(PG_FUNCTION_ARGS)
  7. {
  8. EventTriggerData* trigdata;
  9. if (!CALLED_AS_EVENT_TRIGGER(fcinfo)) /* internal error */
  10. elog(ERROR, "not fired by event trigger manager");
  11. trigdata = (EventTriggerData*)fcinfo->context;
  12. ereport(ERROR,
  13. (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
  14. errmsg("command \"%s\" denied", trigdata->tag)));
  15. PG_RETURN_NULL();
  16. }

编译源码后,声明函数和触发器:

  1. CREATE FUNCTION noddl() RETURNS event_trigger
  2. AS 'noddl' LANGUAGE C;
  3. CREATE EVENT TRIGGER noddl ON ddl_command_start
  4. EXECUTE FUNCTION noddl();

测试:

  1. =# \dy
  2. List of event triggers
  3. Name | Event | Owner | Enabled | Function | Tags
  4. -------+-------------------+-------+---------+----------+------
  5. noddl | ddl_command_start | dim | enabled | noddl |
  6. (1 row)
  7. =# CREATE TABLE foo(id serial);
  8. ERROR: command "CREATE TABLE" denied

这种情形下,如果需要执行DDL,要么删掉该触发器,要么暂时禁用该触发器。可仅在事务期间禁用该触发器:

  1. BEGIN;
  2. ALTER EVENT TRIGGER noddl DISABLE;
  3. CREATE TABLE foo (id serial);
  4. ALTER EVENT TRIGGER noddl ENABLE;
  5. COMMIT;

39.5 表重写事件触发器示例

以下示例,使用table_event事件,实现仅在维护期间可进行表重写:

  1. CREATE OR REPLACE FUNCTION no_rewrite()
  2. RETURNS event_trigger
  3. LANGUAGE plpgsql AS
  4. $$
  5. ---
  6. --- Implement local Table Rewriting policy:
  7. --- public.foo is not allowed rewriting, ever
  8. --- other tables are only allowed rewriting between 1am and 6am
  9. --- unless they have more than 100 blocks
  10. ---
  11. DECLARE
  12. table_oid oid := pg_event_trigger_table_rewrite_oid();
  13. current_hour integer := extract('hour' from current_time);
  14. pages integer;
  15. max_pages integer := 100;
  16. BEGIN
  17. IF pg_event_trigger_table_rewrite_oid() = 'public.foo'::regclass
  18. THEN
  19. RAISE EXCEPTION 'you''re not allowed to rewrite the table
  20. %',
  21. table_oid::regclass;
  22. END IF;
  23. SELECT INTO pages relpages FROM pg_class WHERE oid = table_oid;
  24. IF pages > max_pages
  25. THEN
  26. RAISE EXCEPTION 'rewrites only allowed for table with less
  27. than % pages',
  28. max_pages;
  29. END IF;
  30. IF current_hour NOT BETWEEN 1 AND 6
  31. THEN
  32. RAISE EXCEPTION 'rewrites only allowed between 1am and
  33. 6am';
  34. END IF;
  35. END;
  36. $$;
  37. CREATE EVENT TRIGGER no_rewrite_allowed
  38. ON table_rewrite
  39. EXECUTE FUNCTION no_rewrite();

 

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号