我正在使用PostgreSQL 9.1。我的数据库是结构化的,因此我的应用程序使用的是实际的表。对于每个表,都有一个仅存储更改历史的历史记录表。历史记录表包含与实际表相同的字段,加上字段形成一些额外信息,如编辑时间。历史记录表仅由触发器处理。
我有两种触发器:
在INSERT
触发器之前,可以在表创建时向其添加一些额外的信息(例如create_time)。 UPDATE
触发器之前和DELETE触发器之前将旧值从实际表复制到历史表。问题是我想使用触发器来存储做出这些更改的用户的id。我所说的id是指来自php应用程序的id,而不是PostgreSQL用户id。
有什么合理的方法可以做到这一点吗?
使用INSERT和UPDATE,可以只为实际表添加id的额外字段,并将用户id作为SQL查询的一部分传递给SQL。据我所知,这不适用于删除。
所有触发器的结构如下所示:
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
另一个选项是在被审计的表中有一个last_updated_user_id
。该值可以由PHP/Webapp轻松设置,并且可以在要添加到审计表的News.last_updated_user_id
中使用
Set有一个这里没有提到的变体set会话。这很可能是应用程序开发人员通常真正想要的,而不是普通设置或本地设置。
set session trolol.userr = 'Lol';
我的测试触发器设置有点简单,但是这个想法和克雷格·林格的选项2是一样的。
create table lol (
pk varchar(3) not null primary key,
createuser varchar(20) not null);
CREATE OR REPLACE function update_created() returns trigger as $$
begin new.createuser := current_setting('trolol.userr'); return new; end; $$ language plpgsql;
create trigger lol_update before update on lol for each row execute procedure update_created();
create trigger lol_insert before insert on lol for each row execute procedure update_created();
在这一点上,我认为这是可以接受的。没有DDL语句,如果由于某种原因意外未设置会话变量,则insert/update将不会成功。
使用DISCARD ALL
可能不是一个好主意,因为它会丢弃所有内容。例如,SqlKorma根本不喜欢这样。相反,您可以使用
SET software.theuser TO DEFAULT
我简要考虑了第四种选择。在标准变量集中有“应用程序名称”,可以使用。此解决方案有一些局限性,但也有一些明显的优势,具体取决于上下文。
有关这第四个选项的更多信息,请参阅以下内容:
通过JDBC设置应用程序名称
Postgre留档application_name
选择包括:
>
打开连接时,创建临时表current\u app\u user(用户名文本);在当前用户(用户名)值(“用户”)中插入
。然后在触发器中,
选择当前应用程序用户的用户名
以获取当前用户名,可能作为子查询。
在
postgresql中。conf
为自定义GUC(如my_应用程序)创建一个条目。用户名='未知'
。无论何时创建连接,请运行
设置我的应用程序。用户名='the_user'
。然后在触发器中,使用
current\u设置('my\u app.username')
函数获取值。实际上,您正在滥用GUC机制来提供会话变量。阅读适合您的服务器版本的文档,因为自定义GUC在9.2中发生了更改。
调整应用程序,使其具有每个应用程序用户的数据库角色<代码>在执行工作之前为该用户设置角色
。这不仅允许您使用内置的current\u user
类变量函数选择current\u user
,它还允许您在数据库中强制执行安全性。看这个问题。您可以直接以用户身份登录,而不必使用
设置角色
,但这往往会使连接池变得困难。
在这三种情况下,您都是连接池,您必须小心地
放弃所有连接池
当您返回到池的连接时。(尽管没有记录这样做,
放弃所有
执行一个重置角色
)。
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();
在postgresql.conf
的CUSTOMIZED OPTIONS
部分,添加一行,如myapp.username='unknown_user'
。对于9.2以上的PostgreSQL版本,您还必须设置custom_variable_classes='myapp'
。
重启PostgreSQL。您现在可以SHOWmyapp.username
并获得值unknown_user
。
现在,您可以在建立连接时使用
SETmyapp.username='the_user';
,或者在BEGIN
后使用SET LOCALmyapp.username='the_user';
是事务本地的,这便于池连接。
get\u app\u用户
函数定义:
CREATE OR REPLACE FUNCTION get_app_user() RETURNS text AS $$
SELECT current_setting('myapp.username');
$$ LANGUAGE sql;
使用事务本地当前用户名的
SET LOCAL
演示:
regress=> BEGIN;
BEGIN
regress=> SET LOCAL myapp.username = 'test_user';
SET
regress=> INSERT INTO tg_demo(blah) VALUES ('42');
NOTICE: Current user is: test_user
INSERT 0 1
regress=> COMMIT;
COMMIT
regress=> SHOW myapp.username;
myapp.username
----------------
unknown_user
(1 row)
如果使用
SET
而不是SET LOCAL
,则在提交/回滚时不会恢复该设置,因此它在整个会话中是持久的。它仍然由放弃所有
重置:
regress=> SET myapp.username = 'test';
SET
regress=> SHOW myapp.username;
myapp.username
----------------
test
(1 row)
regress=> DISCARD ALL;
DISCARD ALL
regress=> SHOW myapp.username;
myapp.username
----------------
unknown_user
(1 row)
另外,请注意,不能将
SET
或SET LOCAL
与服务器端绑定参数一起使用。如果您想使用绑定参数(“准备语句”),请考虑使用函数窗体<代码> SETIONCOFIG(…)<代码>。请参阅系统管理功能
这种方法需要使用触发器(最好是触发器调用的帮助函数),它试图从每个会话应该具有的临时表中读取值。如果找不到临时表,则提供默认值。这可能会有些缓慢。仔细测试。
get_app_user定义:
CREATE OR REPLACE FUNCTION get_app_user() RETURNS text AS $$
DECLARE
cur_user text;
BEGIN
BEGIN
cur_user := (SELECT username FROM current_app_user);
EXCEPTION WHEN undefined_table THEN
cur_user := 'unknown_user';
END;
RETURN cur_user;
END;
$$ LANGUAGE plpgsql VOLATILE;
演示:
regress=> CREATE TEMPORARY TABLE current_app_user(username text);
CREATE TABLE
regress=> INSERT INTO current_app_user(username) VALUES ('testuser');
INSERT 0 1
regress=> INSERT INTO tg_demo(blah) VALUES ('42');
NOTICE: Current user is: testuser
INSERT 0 1
regress=> DISCARD ALL;
DISCARD ALL
regress=> INSERT INTO tg_demo(blah) VALUES ('42');
NOTICE: Current user is: unknown_user
INSERT 0 1
还有人建议在PostgreSQL中添加“安全会话变量”。这些有点像包变量。截至PostgreSQL 12,该功能尚未包括在内,但请密切注意,如果您需要,请在黑客列表中发言。
对于高级使用,您甚至可以让自己的C扩展注册共享内存区域,并使用读取/写入DSA段中的值的C函数调用在后端之间进行通信。有关详细信息,请参见PostgreSQL编程示例。你需要知识、时间和耐心。
我尝试从JPA/Hibernate环境向plpgsql存储过程传递整数数组值。但我总是得到一个执行:函数fn_test_array(bytea)不存在。 我编写了一个简短的演示应用程序,演示了这个问题。(Wildfly AS,JPA/Hibernate,PostgreSQL 12) 是的,我知道,int[]。课堂是垃圾,那么解决办法是什么呢?:) 存储过程: 从java调用: 例外情况: 提前感谢
嘿,伙计们,我是一个完全的初学者,在Java,我正在尝试编写一个程序,以这样的方式:油漆公司已经确定,每115平方英尺的墙壁空间,一加仑油漆和8小时的劳动将需要。 这家公司的人工费为每小时18美元。 编写一个程序,允许用户输入需要粉刷的房间数量和每加仑油漆的价格。它还应该要求每个房间有平方英尺的墙壁空间。程序应该具有返回以下数据的方法: 所需油漆的加仑数 所需劳动时数 油漆成本 人工费 喷漆作业的
问题内容: 我的类型为: 使用类型数组作为输入参数进行一些处理的过程如下: 最后,我查询该过程为: 在pgadmin中一切正常。稍后,当我尝试使用 Hibernate本机SQL查询Ka Boom 调用相同的代码时 !!! 显示以下内容: 最后一个问题:既与做同样的工作吗? 问题答案: 使用 数组文字 ( 数组的 文本表示形式),因为数组构造函数必须由Postgres求值: 甚至没有显式的强制转换:
问题内容: 考虑一个非fx的现有应用程序,将其称为。 公开一个对象,该对象又公开了一些属性。也接受这些属性的侦听器。 我的问题是关于 将 JavaFx gui 添加 到此类应用程序。将明显延长,将需要一个参考对象。 在寻找将非String参数传递给我的解决方案时,我发现了几种不同的方法: 静态方法:例如,已初始化对in 的静态引用。在这里可以看到使用静电的一个示例。 JavaFx 9方法:如此处所
PostgreSQL 触发器是数据库的回调函数,它会在指定的数据库事件发生时自动执行/调用。 下面是关于 PostgreSQL 触发器几个比较重要的点: PostgreSQL 触发器可以在下面几种情况下触发: 在执行操作之前(在检查约束并尝试插入、更新或删除之前)。 在执行操作之后(在检查约束并插入、更新或删除完成之后)。 更新操作(在对一个视图进行插入、更新、删除时)。 触发器的 FOR EAC
我想把它从适配器传递到这个,这是我的适配器 这是我的适配器,它保存着RecycerView,我正在尝试获取考勤活动上查看的用户id RecycerView