我有一个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
$$;
我希望存储过程能够正常工作,但是失败了,并且出现语法错误...
答案 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