插入具有存储过程的列列表

时间:2019-02-15 09:01:28

标签: postgresql plpgsql dynamic-sql

我有一个Matlab脚本,我想在其中使用存储过程,而不是将所有指令发送到服务器。问题是我必须处理这样的包含变量列的字符串变量,例如:

listColumns = '(column1, column2, column3, ... columnN)';

我想知道的是如何使用动态SQL来创建存储过程,并使用列列表作为字符串输入参数来进行插入?有可能还是我需要更改脚本以使其更简单?

对我来说这是很新的东西,我尝试了更简单的存储过程,而我只需要一列就可以了,但是我暂时对此感到困惑:

CREATE OR REPLACE FUNCTION myfunction(
    tablename regclass, 
    listColumns regclass,
    listColumnsTarget regclass)
    RETURNS void
    LANGUAGE = 'plpgsql'
AS $$
BEGIN
    EXECUTE format('INSERT INTO ' %s %s '(SELECT ' %s ' FROM anotherTable)',
    tablename, listeColumns, listColumnsTarget);
END
$$;

我希望存储过程能够正常工作,但是失败了,并且出现语法错误...

1 个答案:

答案 0 :(得分:0)

regclass类型只能保存某个数据库对象的一个​​标识(由pg_class系统表的一行描述)。您不能使用regclass类型来传递列名,因为只是列不是表。 regclass类型很特殊,请对照系统表检查该值。

postgres=# select 'xxx'::regclass;
ERROR:  relation "xxx" does not exist
LINE 1: select 'xxx'::regclass;
               ^

Postgres具有SQL标识符的特殊类型-name。而且,如果您想传递更多的值,则应该使用名称数组-name[]。但是对于外部输入,最好使用常规的text类型并在函数内部处理必要的操作。在这种情况下,使用name类型对我们无济于事。

什么是必要的?转义-清除语法错误或SQL注入。例如-标识符fubu, boo-boo, AA应该转换为标识符fubi, "boo-boo", "AA"的安全列表。这一步很重要

Postgres对此列表的fork没有任何功能,但是我们可以编写自己的功能:

CREATE OR REPLACE FUNCTION sanitize_identifiers(text)
RETURNS text AS $$
  SELECT string_agg(quote_ident(v), ',') 
    FROM unnest(string_to_array($1, ',')) g(v)
$$ LANGUAGE sql;

现在,我们可以编写如下函数:

CREATE OR REPLACE FUNCTION foo(tablename regclass,
                               columns text)
RETURNS void AS $$
BEGIN
  -- should to print valid SQL
  RAISE NOTICE '%',
    FORMAT('INSERT INTO %s SELECT %s FROM tab',
            tablename,
            sanitize_identifiers(columns));
END;
$$ LANGUAGE plpgsql;

结果:

postgres=# select foo('Test','a,b,c,C');
NOTICE:  INSERT INTO test SELECT a,b,c,"C" FROM tab