Postgres触发程序似乎已被记忆

时间:2015-11-11 13:19:47

标签: sql database postgresql plpgsql

我试图在postgresql中创建一个触发器,以便在插入之前自动设置一些字段,但我的每个会话只调用一次计算值的过程而不是每次插入。以下是我试图实现这一目标的方法,

创建测试表

>> create table test (
    id varchar(16) unique not null
);

尝试插入表格而不提供id

的值
>> insert into test DEFAULT VALUES;
ERROR:  null value in column "id" violates not-null constraint

正如预期的那样,id不能是null

创建一个函数并触发在插入之前自动设置id

>> create or replace function set_id() returns trigger language plpgsql as $$                                                             
begin                                                            
    new.id = gen_custom_unique_id();
    return new;
end $$;


>> CREATE TRIGGER client_update_trigger BEFORE INSERT ON test FOR EACH ROW EXECUTE PROCEDURE set_id();

gen_custom_unique_id是另一个自定义函数。

尝试在表格中插入行

>> insert into test DEFAULT VALUES;
INSERT 0 1

>> select id from test;
    id
------------------
  0_EQOEatMNaXO2a-
(1 row)

..按预期工作。

问题

尝试插入另一行

>> insert into test DEFAULT VALUES;
ERROR:  duplicate key value violates unique constraint "test_id_key"
DETAIL:  Key (id)=(0_EQOEatMNaXO2a-) already exists.

我的理解是,这应该再次调用过程set_id并生成一个新的唯一ID,但每次会话只调用set_id一次。如果我从数据库断开连接并尝试新会话,INSERT将再次运行。

EXECUTE PROCEDURE set_id();确实看起来很可疑,因为我们正在调用内联程序。直觉上,我认为它应该是EXECUTE PROCEDURE set_id;然后触发器应该"调用"在插入每一行之前set_id,但这不起作用。

如何将EXECUTE PROCEDURE set_id();变成EXECUTE PROCEDURE lazy(set_id());之类的内容?

更新

我确定gen_custom_unique_id工作正常。代码太冗长,与IMO分享无关。为了这个例子,我们可以假设它只返回一个UUID或时间戳。我测试过它按预期工作,这是输出。

>> select * from gen_custom_unique_id();
   gen_custom_unique_id
------------------
 0_Elu4sdjRxW6s_s
(1 row)

>> select * from gen_custom_unique_id();
   gen_custom_unique_id
------------------
 0_EluVMKwLjJQP2s
(1 row)

>> select * from gen_custom_unique_id();
   gen_custom_unique_id
------------------
 0_ElutuejkWhR07N
(1 row)

>> select * from gen_custom_unique_id();
   gen_custom_unique_id
------------------
 0_ElvHrif6X2pQH-
(1 row)

>> select * from gen_custom_unique_id();
   gen_custom_unique_id
------------------
 0_ElvgXWgGIMYeHk
(1 row)

此函数会生成类似firebase或simpleflake ID(http://akmanalp.com/simpleflake_presentation/#/

的内容

更新2

我做了一些进一步的测试,看起来确实缓存了get_custom_unique_idset_id的输出。

我向get_custom_unique_id添加了一条NOTICE语句,它仅在触发器第一次调用该函数时打印。

连续3次呼叫gen_custom_unique_id

>> select * from gen_custom_unique_id();
NOTICE:  generating id: 0_Il_GKmrwCwkUJJ
   gen_custom_unique_id
------------------
 0_Il_GKmrwCwkUJJ
(1 row)

>> select * from gen_custom_unique_id();
NOTICE:  generating id: 0_Il_xBXWXvLuwBk
   gen_custom_unique_id
------------------
 0_Il_xBXWXvLuwBk
(1 row)

>> select * from gen_custom_unique_id();
NOTICE:  generating id: 0_IlbKk0ixgnkIdA
   gen_custom_unique_id
------------------
 0_IlbKk0ixgnkIdA
(1 row)

请注意它如何打印NOTICE: generating id: {generated_id}

从TRIGGER打电话

>> insert into test DEFAULT VALUES;
NOTICE:  generating id: 0_IsgcquLRup1KxF
CONTEXT:  SQL statement "SELECT gen_custom_unique_id()"
PL/pgSQL function set_id() line 3 at assignment
INSERT 0 1

>> insert into test DEFAULT VALUES;
ERROR:  duplicate key value violates unique constraint "test_pkey"
DETAIL:  Key (id)=(0_IsgcquLRup1KxF) already exists.

来自gen_custom_unique_id的通知仅在第一次调用时打印,因此每次都不会调用它。

1 个答案:

答案 0 :(得分:0)

有关您的信息:将(调用a)自定义函数作为列的默认值连接,可以像在postgres中实现连续符的方式一样。它不需要触发器。我将clock_timestamp()演示为序列生成函数:

CREATE TABLE omg
              -- NOTE: clock_timestamp is volatile by nature,
              -- which means that the query-optimiser cannot presume
              -- that calling it twice would result in the same result
              -- (THUS: the function is called anytime a value is needed)
    ( id timestamp NOT NULL PRIMARY KEY DEFAULT clock_timestamp()
    , payload text
    );

INSERT INTO omg(payload) VALUES ('one' );
INSERT INTO omg(payload) VALUES ('two' );
SELECT * FROM omg;

结果:

CREATE TABLE
INSERT 0 1
INSERT 0 1
             id             | payload 
----------------------------+---------
 2015-11-11 15:29:07.330905 | one
 2015-11-11 15:29:07.338803 | two
(2 rows)