将用户ID传递给PostgreSQL触发器 [英] Passing user id to PostgreSQL triggers

查看:77
本文介绍了将用户ID传递给PostgreSQL触发器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用PostgreSQL 9.1.我的数据库是结构化的,因此有我的应用程序使用的实际表.对于每个表,都有一个历史表,仅存储更改历史记录.历史表包含与实际表加字段相同的字段,这些字段构成一些额外的信息,例如.编辑时间.历史记录表仅由触发器处理.

I'm using PostgreSQL 9.1. My database is structured so that there is actual tables that my application uses. For every table there is history table that stores only change history. History tables contain same fields that actual tables plus fields form some extra information eg. edit time. History tables are only handled by triggers.

我有2种触发器:

  1. Before INSERT触发器在创建表时向表添加一些额外的信息(例如create_time).
  2. Before UPDATE触发器和before DELETE触发器将旧值从实际表复制到历史表.
  1. Before INSERT trigger to add some extra information to tables when they are created (eg. create_time).
  2. Before UPDATE trigger and before DELETE triggers to copy old values from actual table to history table.

问题是我想使用触发器来存储进行这些更改的用户的ID. id是指来自php应用程序的id,而不是PostgreSQL用户id.

Problem is that I'd like to use triggers to store also the id of user who made those changes. And by id I mean id from php application, not PostgreSQL user id.

有什么合理的方法吗?

使用INSERT和UPDATE,可以仅将id的额外字段添加到实际表中,并将用户ID作为SQL查询的一部分传递给SQL.据我所知,这不适用于DELETE.

With INSERT and UPDATE it could be possible to just add extra field for id to actual tables and pass user id to SQL as part of SQL query. As far as I know this doesn't work with DELETE.

所有触发器的结构如下:

All triggers are structured as follows:

CREATE OR REPLACE FUNCTION before_delete_customer() RETURNS trigger AS $BODY$
BEGIN
    INSERT INTO _customer (
        edited_by,
        edit_time,
        field1,
        field2,
        ...,
        fieldN
    ) VALUES (
        -1, // <- This should be user id.
        NOW(),
        OLD.field1,
        OLD.field2,
        ...,
        OLD.fieldN
    );
    RETURN OLD;
END; $BODY$
LANGUAGE plpgsql

推荐答案

选项包括:

  • 打开连接时,CREATE TEMPORARY TABLE current_app_user(username text); INSERT INTO current_app_user(username) VALUES ('the_user');.然后在您的触发器中,SELECT username FROM current_app_user获取当前用户名,可能作为子查询.

  • When you open a connection, CREATE TEMPORARY TABLE current_app_user(username text); INSERT INTO current_app_user(username) VALUES ('the_user');. Then in your trigger, SELECT username FROM current_app_user to get the current username, possibly as a subquery.

postgresql.conf中为自定义GUC (如my_app.username = 'unknown';).每当您创建连接时,运行SET my_app.username = 'the_user'; .然后在触发器中,使用 current_setting('my_app.username')函数获取价值.实际上,您正在滥用GUC机制来提供会话变量. 请阅读适合您服务器版本的文档,因为自定义GUC在9.2中已更改.

In postgresql.conf create an entry for a custom GUC like my_app.username = 'unknown';. Whenever you create a connection run SET my_app.username = 'the_user';. Then in triggers, use the current_setting('my_app.username') function to obtain the value. Effectively, you're abusing the GUC machinery to provide session variables. Read the documentation appropriate to your server version, as custom GUCs changed in 9.2.

调整您的应用程序,使其对每个应用程序用户都有数据库角色. SET ROLE对该用户进行操作之前.这不仅使您可以将内置的current_user类似于变量的函数用于SELECT current_user;,还可以使您加强数据库中的安全性.请参阅此问题.您可以直接以用户身份登录,而不必使用SET ROLE,但这会增加连接池的难度.

Adjust your application so that it has database roles for every application user. SET ROLE to that user before doing work. This not only lets you use the built-in current_user variable-like function to SELECT current_user;, it also allows you to enforce security in the database. See this question. You could log in directly as the user instead of using SET ROLE, but that tends to make connection pooling hard.

在这三种情况下,您都是连接池,必须小心

In both all three cases you're connection pooling you must be careful to DISCARD ALL; when you return a connection to the pool. (Though it is not documented as doing so, DISCARD ALL does a RESET ROLE).

CREATE TABLE tg_demo(blah text);
INSERT INTO tg_demo(blah) VALUES ('spam'),('eggs');

-- Placeholder; will be replaced by demo functions
CREATE OR REPLACE FUNCTION get_app_user() RETURNS text AS $$
SELECT 'unknown';
$$ LANGUAGE sql;

CREATE OR REPLACE FUNCTION tg_demo_trigger() RETURNS trigger AS $$
BEGIN
    RAISE NOTICE 'Current user is: %',get_app_user();
    RETURN NULL;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER tg_demo_tg
AFTER INSERT OR UPDATE OR DELETE ON tg_demo 
FOR EACH ROW EXECUTE PROCEDURE tg_demo_trigger();

使用GUC:

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆