如何向 pg_class 添加触发器?

How do I add a trigger to pg_class?

在 postgres 中,我想为每一行的插入动态添加一个触发器到我的所有 tables。我希望能够为所有未来添加的 table 添加触发器,而不需要数据库用户做其他事情来创建 table (比如调用函数来创建 table而不是使用 CREATE TABLE 语法)。为此,我认为最简单的方法是向 pg_class 添加触发器,但是当我这样做时,出现错误:

ERROR: permission denied: "pg_class" is a system catalog

这是我的代码

> docker run --name pg -e POSTGRES_PASSWORD=password postgres:13
> docker exec -ti pg psql -Upostgres
# then run this...

CREATE FUNCTION my_func() RETURNS trigger AS $$
BEGIN
   RAISE NOTICE 'This is a test';
   RETURN NULL;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER my_pg_trigger
AFTER INSERT ON pg_catalog.pg_class
FOR EACH ROW
WHEN (NEW.relkind = 'r')
EXECUTE FUNCTION my_func();

我在文档中没有看到任何说明您无法添加触发器的内容。我发现文档说它们是“常规 table”,但是编辑它们可能会严重搞砸:https://www.postgresql.org/docs/13/catalogs.html。但是没有关于权限或限制的内容。

PostgreSQL's system catalogs are regular tables. You can drop and recreate the tables, add columns, insert and update values, and severely mess up your system that way.

如果这不可能,您能否指出相关文档以帮助我理解?更好的是,让我知道是否有另一种方法可以将触发器动态添加到 tables?

我没有向 pg_class table 添加触发器,而是按照@Adrian Klaver 和@a_horse_with_no_name 对事件触发器的建议解决了问题。

CREATE OR REPLACE FUNCTION report() RETURNS event_trigger AS $$
DECLARE
    obj record;
    rel_kind char;
BEGIN
    IF tg_tag = 'CREATE TABLE' THEN
        FOR obj IN SELECT * FROM pg_event_trigger_ddl_commands()
        LOOP
            SELECT relkind INTO rel_kind FROM pg_class WHERE oid = obj.objid;
            -- only log on tables (exclude indices)
            IF rel_kind = 'r' THEN
                RAISE NOTICE 'metric: %', obj.object_identity;
            END IF;
        END LOOP;
    END IF;
END;
$$ LANGUAGE plpgsql;

CREATE EVENT TRIGGER report_trigger ON ddl_command_end EXECUTE FUNCTION report();

对于那些需要用例的人:

假设您需要在创建 table 时进行报告,并且您需要使用现有的基础设施,并且不能使用 pgbadger 或类似的东西。或者您想要更多实时警报,并且不想等到日志以 10 米或 1 小时的间隔聚合。