Postgresql 如何在触发器函数中从信息模式动态复制表

Postgresql 如何在触发器函数中从信息模式动态复制表,postgresql,Postgresql,我有一个insert触发器函数,其中NEW.schema_name引用一个模式。我想动态地将该模式中的表('foobaz','barbaz')复制为'foo'和'bar'。然后,我可以在没有动态sql的情况下执行查询 我如何创建一个函数,或者简单地复制/粘贴相同的代码块来实现它 编辑: 我无法使动态查询正常工作。 WITH语句中的部分正在工作。 不是底部的“执行”部分。我不知道这是否是语法问题,或者是错误的强制转换,或者是pgsql中的任何约束导致它无法工作 WITH info_schema_s

我有一个insert触发器函数,其中NEW.schema_name引用一个模式。我想动态地将该模式中的表('foobaz','barbaz')复制为'foo'和'bar'。然后,我可以在没有动态sql的情况下执行查询

我如何创建一个函数,或者简单地复制/粘贴相同的代码块来实现它

编辑:

我无法使动态查询正常工作。 WITH语句中的部分正在工作。 不是底部的“执行”部分。我不知道这是否是语法问题,或者是错误的强制转换,或者是pgsql中的任何约束导致它无法工作

WITH info_schema_subset_table as (SELECT table_schema, table_name, 
     array_to_string((regexp_split_to_array(table_name,'_'))[4:array_length(regexp_split_to_array(table_name,'_'),1)-1] as new_table
     FROM information_schema.tables
     where table_schema = "schema_searched"
     ORDER BY new_table ASC)

  EXECUTE 'CREATE TABLE $2 as (SELECT * FROM $1)'
  USING info_schema_subset_table.table_schema || '.' ||info_schema_subset_table.table_name,info_schema_subset_table.new_table;
编辑2

。。。已删除损坏的代码

在下面的代码中,我不确定语法是否正确,我从触发器中得到以下内容

提供程序错误: 添加功能时PostGIS错误:ERREUR:l'opérateur n'existe pas:record~~unknown 第1行:选择像“%ens%”这样的旧表 ^ 提示:所有操作都对应于名称和参数类型。 Vous devez AJAOUTER des转换明确了de类型。 查询:选择像“%ens%”这样的旧表 上下文:fontion PL/pgsql validation\u sio.afi\u validation\u sio(),ligne 18áCASE

编辑3:

CREATE OR REPLACE FUNCTION foo.foo()
RETURNS TRIGGER AS
$BODY$

DECLARE 
old_table record;
new_table record;
dynamic_query text;

BEGIN

IF TG_OP = 'INSERT'
THEN

FOR old_table IN SELECT table_schema|| '.' ||table_name
FROM information_schema.tables
where table_schema = NEW.nom_schema
LOOP

CASE 
 WHEN
  old_table LIKE '%ens%' THEN
  new_table := concat('SIT_',array_to_string((regexp_split_to_array(info_schema.old_table,'_'))[4:array_length(regexp_split_to_array(info_schema.old_table,'_'),1)-1],'_'));
 ELSE 
  new_table := concat('SID_',array_to_string((regexp_split_to_array(info_schema.old_table,'_'))[4:array_length(regexp_split_to_array(info_schema.old_table,'_'),1)-1],'_'));
END CASE;

dynamic_query := format('SELECT * FROM' || old_table ||);
EXECUTE dynamic_query
INTO new_table;

END LOOP;

RETURN NEW;

END IF;
END; 
$BODY$

LANGUAGE plpgsql VOLATILE;


CREATE TRIGGER foo
AFTER INSERT ON validation.validationfoo
FOR EACH ROW EXECUTE PROCEDURE foo.foo();

SQL语句中不能有
EXECUTE
,它是PL/pgSQL语句

循环浏览表格,并为每个表格发出一个
EXECUTE

请注意,您不能将模式或表名作为参数使用
USING
,因为需要在解析时知道这些名称


使用
format
函数构造动态语句,这样可以避免恶意创建具有奇怪名称的表的用户进行SQL注入。

我对触发器函数进行了一些重新格式化,并更改了一些内容,看看这是否有效

CREATE OR REPLACE FUNCTION foo.foo()
    RETURNS TRIGGER AS
$BODY$
DECLARE 
    old_table record;
    new_table record;
    dynamic_query text;
BEGIN
    IF TG_OP = 'INSERT' THEN
        FOR old_table IN
            SELECT table_schema || '.' || table_name AS old_table_name
            FROM information_schema.tables
            WHERE table_schema = NEW.nom_schema
        LOOP
            new_table := concat(CASE WHEN old_table.old_table_name LIKE '%ens%' THEN 'SIT_' ELSE 'SID_' END,array_to_string((regexp_split_to_array(info_schema.old_table,'_'))[4:array_length(regexp_split_to_array(info_schema.old_table,'_'),1)-1],'_'));
            dynamic_query := 'CREATE TABLE ' || new_table || ' AS SELECT * FROM ' || old_table.old_table_name;
            EXECUTE dynamic_query;
        END LOOP;

        RETURN NEW;
    END IF;
END; 
$BODY$
LANGUAGE plpgsql VOLATILE;
所以主要的事情是:

  • old_table
    是一条记录,因此将其与
    LIKE
    字符串进行比较失败。您需要使用字段名。因此,我给了您的字段一个名称,并在
    类似的比较中使用了该字段名称
  • 更改了
    new_table
    赋值,将
    CASE
    语句仅放在发生更改的一项上,以使差异更加明显,代码更加简洁。请注意,我不知道这句话的其余部分是否有效,我只是保持原样
  • 更改了创建
    动态\u查询
    。正如我在评论中所说的,
    格式
    函数被错误地使用,所以我只使用了标准的字符串连接
  • dynamic\u query
    的SQL更改为我认为您实际上希望它执行的操作。您希望它将表的内容复制到新表中,对吗?这样就可以了

不要将
is
用作别名,它是postgres关键字。您使用的
格式不正确。执行
dynamic_query:='SELECT*FROM'| | old_table
或执行
dynamic_query:=format('SELECT*FROM%s',old_table)
。我根本不懂[format]。谢谢它类似于python。你能看看我的编辑并告诉我它是否正确吗?编辑的代码充满了语法错误、没有结束分号的语句、没有结束循环的循环等等,所以很难说。但你正朝着正确的方向前进。此外,您仍然容易受到奇怪表名的SQL注入的攻击。我对
DO
语句的建议是错误的-我没有意识到您在PL/pgSQL代码中(您没有发布整个函数)。所以你不需要一个
DO
语句。好吧,我就是这么想的。格式是否阻止注入?据我所知,表名可以对应于正在注入的查询。格式是否通过引用所有名称来防止这种情况?我或多或少是sql新手……如果TG_OP='INSERT',那么在FOR之后我会得到一个错误,我的语法正确吗?对于选择表| | |'.| |表|名称为旧_tableNo中的信息_schema,您的语法已损坏,无法修复。首先删除
DO
语句。每个语句都必须以
结尾
,每个
IF
都需要一个
结束IF
,每个
循环
需要和
结束循环。从这些方面开始,然后继续。这很容易受到SQL注入的影响。如果我用正确的名称创建一个表,我可以破坏你的函数或滥用它。当然,尽管我不太在意,因为首先,这只是对OP的代码进行一些更改以实现他想要的,这不是完美触发器的全面示例;第二,用于创建动态sql语句的数据完全来自表中的现有数据,我们不知道用户是否有权写入这些表等。好的,第一部分,但我们知道哪些用户有权写入这些表:所有能够创建表的人。我不是说你的答案不好,我只是觉得可以改进。有足够多的人盲目地复制像您这样的代码示例,并将其用作金标准。这不是你的错,但我认为我们应该尽可能提高对SQL注入的认识。