我在一个终端 A 中打开psql
(Arch Linux上的PostgreSQL 9.5.4)并发出LISTEN "notif";
在另一个终端 B 中,我运行以下脚本(psql -f myscript.sql
)。
这会创建一个包含两个触发器的表。如果在send
为TRUE的情况下添加了一行,则触发一个触发器,如果更新了一行,则另一个触发器触发,使send
从FALSE变为TRUE。两个触发器都发送通知。
DROP TRIGGER IF EXISTS do_notif ON notif;
DROP TRIGGER IF EXISTS do_notif2 ON notif;
DROP TABLE IF EXISTS notif;
CREATE TABLE notif (id INT PRIMARY KEY, send BOOLEAN, msg TEXT);
CREATE OR REPLACE FUNCTION post() RETURNS TRIGGER AS $$
BEGIN
PERFORM pg_notify('notif', '+' || NEW.id::TEXT || ',' || coalesce(NEW.msg, '(null)'));
RETURN NEW;
END $$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION post2() RETURNS TRIGGER AS $$
BEGIN
PERFORM pg_notify('notif', '~' || NEW.id::TEXT || ',' || coalesce(NEW.msg, '(null)'));
RETURN NEW;
END $$ LANGUAGE plpgsql;
CREATE TRIGGER do_notif AFTER INSERT ON notif
FOR EACH ROW WHEN (NEW.send)
EXECUTE PROCEDURE post();
CREATE TRIGGER do_notif2 AFTER UPDATE OF send ON notif
FOR EACH ROW WHEN (NEW.send AND NOT OLD.send)
EXECUTE PROCEDURE post2();
-- LISTEN "notif";
INSERT INTO notif VALUES (1, FALSE, 'update');
INSERT INTO notif VALUES (2, TRUE, 'insert');
UPDATE notif SET send = TRUE;
UPDATE notif SET send = FALSE;
UPDATE notif SET send = TRUE;
START TRANSACTION;
INSERT INTO notif VALUES (10, FALSE, 'a'), (11, TRUE, 'b'), (12, TRUE, 'c');
UPDATE notif SET send = TRUE WHERE id = 10;
COMMIT
我希望相关的INSERT和UPDATE查询应该会调用触发器,这些触发器会在终端 A 中接收发送通知。
这不会发生。我必须在终端 A 中再次手动运行LISTEN "notif";
,这会立即向我提供缺少的通知。
如果我取消对该脚本中的LISTEN "notif";
发表评论,那么运行该脚本的psql
实例( B )会在相关位置向终端发送通知(在非事务性插入/更新之后将send
设置为TRUE,并且在事务之后也是如此。
但 A 仍然不会显示这些内容,除非我在 A (或任何其他查询,例如LISTEN "notif";
)中再次运行SELECT TRUE;
。它不是一个终端缓冲问题,因为仅仅在 A 中点击ENTER不会导致通知出现。
PostgreSQL似乎不会立即通过不同的连接或不同的进程传递通知,但是当侦听器与生成通知的进程相同时,它会立即传递。
答案 0 :(得分:3)
psql 在每个命令之后检查通知队列(更具体地说,在从服务器接收任何结果之后)。因此,您可以使用listen channel
一次,每个后续命令都会检查是否有新通知。
基于 libpq.c 标准库的程序以相同的方式运行,在从服务器接收数据后检查通知队列the function PQnotifies
。他们还有另一种可能性。如果在特定时间没有与服务器通信,则可以使用函数PQconsumeInput
刷新通知队列。通过这种方式,他们以异步方式接收通知。司机的通知支持当然可以在更高层次上实施。