【问题标题】:Using NEW and COPY in a Postgres Trigger在 Postgres 触发器中使用 NEW 和 COPY
【发布时间】:2021-03-07 07:15:14
【问题描述】:

我正在尝试使用触发器将最后插入的行从表中复制到 csv 文件中。

CREATE TRIGGER new_tbc_order
  AFTER INSERT
  ON trig_test
  FOR EACH ROW
  EXECUTE PROCEDURE write_last_tbc_order();



CREATE OR REPLACE FUNCTION write_last_tbc_order()
RETURNS TRIGGER 
LANGUAGE plpgsql
as $$
BEGIN
    EXECUTE 'COPY (
             select i.id, i.paid, i.no_items FROM (SELECT NEW.*) AS i 
         ) TO ''/Users/fred/Desktop/last_tbc_order.csv'' csv;' USING NEW;
    RETURN NEW;
END; $$

我已经在各种版本中尝试过这个,有或没有 EXECUTE,但我仍然收到错误。

ERROR:  missing FROM-clause entry for table "new"
LINE 1: ...opy (select i.id, i.paid, i.no_items FROM (SELECT NEW.*) AS ...
                                                         ^

只是无法让它访问新数据。

我哪里错了?

【问题讨论】:

  • 试试... from (select ($1).*) ..' using new
  • 我做到了。也没有用。
  • 我会再试一次,以防我错过了一个括号
  • 错误:没有参数 $1 LINE 1: ...py (select i.id, i.paid, i.no_items FROM (select ($1).*) AS ... ^
  • 我不确定您要完成什么,但使用这样的触发器很可能是个坏主意。首先 - 复制到文件需要数据库超级用户权限,因此不建议用于生产。如果多个连接会并行执行插入

标签: sql postgresql triggers


【解决方案1】:

我可以让它工作的唯一方法是这样的:

CREATE OR REPLACE FUNCTION write_last_tbc_order()
RETURNS TRIGGER 
LANGUAGE plpgsql
as $$
BEGIN
    EXECUTE 'COPY (
             select id, paid, no_items FROM trig_test WHERE id = ' || NEW.id ||  
         ') TO ''/Users/fred/Desktop/last_tbc_order.csv'' csv;';
    RETURN NEW;
END; $$

COPY 似乎没有“看到”新记录。请注意,如果 Postgres 服务器对 /Users/fred/Desktop/ 没有权限,则上述操作将失败,因为 COPY 以服务器用户身份运行。我个人认为更好的解决方案是写入审计表并定期从那里收集记录。

【讨论】:

  • 哈。是的。使用新记录的目的是避免查询,但正如您所说,写入一个小表并从中选择将解决很多问题,这是一个好主意。不过我想接下来我会尝试编写一个 Python 触发器。
  • 如果你打算在 Python 函数中使用COPY,你会遇到同样的问题。
【解决方案2】:

Adrian 的回答激发了我对实际可用的新唱片进行更多试验。 和往常一样,实际的答案很简单:

CREATE OR REPLACE FUNCTION write_last_tbc_order()
RETURNS TRIGGER 
AS 
$$
BEGIN
    EXECUTE 'copy (select '''||NEW.id||''','''||NEW.paid||''','''||NEW.no_items||''') 
to ''/Users/shaun/Desktop/last_tbc_order.csv'' csv;' USING NEW;
    RETURN NEW;
END; 
$$
LANGUAGE plpgsql;

因此将 NEW 记录保留在复制语句之外并在执行之前动态构建它。

这仍然需要最后使用 USING NEW 部分才能使其正常工作。 还需要所有的报价。

更新:

上面的代码确实要求几乎所有东西都以 Postgres 超级用户身份运行(正如一些评论者所提到的),这并不理想。

要解决这个问题,您可以创建一个 PlPython 函数,如下所示:

CREATE OR REPLACE FUNCTION write_last_tbc_order() 
RETURNS TRIGGER AS
'
import os
row = str(TD["new"]["id"]) + "," + TD["new"]["paid"] + "," + str(TD["new"]["noitems"])
path = "/tmp/db-data/last_order.csv";
with open(path,"w") as o:
    os.chmod(path, 0o644)
    o.write(row)
return None
'
LANGUAGE 'plpythonu';

此函数必须以超级用户身份创建,但它可以在标准用户创建的触发器中使用,并且当标准用户触发插入时,这些触发器也可以工作。

文件写入的地方必须对路径的每一部分都有postgres用户的写权限,所以我在/tmp下创建了一个777权限的子目录。

还需要添加 os chmod 644 语句以使系统上的其他用户可以读取该文件。没有这个,文件创建的权限为 600。

补充说明:

最后 Apache 不喜欢你在 /tmp 目录中设置一个虚拟目录,所以最后不得不为此专门创建另一个名为 /tmp-wh 的目录。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-11-20
    • 1970-01-01
    • 2012-06-15
    • 2016-12-10
    • 1970-01-01
    • 1970-01-01
    • 2018-12-19
    • 2012-04-11
    相关资源
    最近更新 更多