我正在Postgres中开发一个函数,旨在为查询的每个记录恢复一组函数中包含的检查结果的值。这些函数中只有一个会返回正确的值。这些函数有一个comun前缀'fn_condicao_',并作为参数接收'my_table'类型的对象。
由于检查的函数数量未知,我决定查阅Postgres目录,从表pg_catalog.pg_proc
中搜索带有前缀'fn_condicao_'的函数,并使用EXECUTE动态执行它们。
我的问题是如何为EXECUTE传递正确的形状参数。
create or replace function test_conditions()
returns void as
$$
declare
v_record my_table%rowtype;
v_function pg_proc%rowtype;
begin
set search_path = 'pg_catalog';
for v_record in (select * from my_table where id in (1,2,3)) loop
for v_function in (
SELECT p.proname
FROM pg_namespace n
JOIN pg_proc p
ON p.pronamespace = n.oid
WHERE n.nspname = 'operacional'
and p.proname like ('fn_condition\\_%')
order by p.proname)
loop
--execute 'select ' || v_function.proname || '(' || v_record || ')';
end loop;
end loop;
end;
$$
language plpgsql;
如何在上述函数中的注释v_record
命令中正确传递EXECUTE
?
execute 'select ' || v_function.proname || '(' || v_record || ')'; -- ???
示例功能:
create or replace function fn_condition_1(p_record my_table)
returns bigint as
$$
begin
if ($1.atributo1 > $1.atributo2) then
return 1;
end if;
return null;
end;
$$
language plpgsql;
答案 0 :(得分:2)
我相信你的问题是你的函数中的execute
命令试图插入v_record
的值,它实际上将它变成一个离散的参数列表而不是函数的本机行类型期待。
如果您愿意更改每个功能的参数类型,这可能是处理此问题的最简单方法。如果没有,那么您需要一些方法将本机行类型传递给动态函数调用。尽管看起来很糟糕,我认为这样的事情会起作用:
create or replace function test_conditions()
returns void as
$$
declare
v_record my_table%rowtype;
v_function pg_proc%rowtype;
begin
set search_path = 'pg_catalog';
for v_record in (select * from my_table where id in (1,2,3)) loop
for v_function in (
SELECT p.proname
FROM pg_namespace n
JOIN pg_proc p
ON p.pronamespace = n.oid
WHERE n.nspname = 'operacional'
and p.proname like ('fn_condition_%')
order by p.proname)
loop
execute '
do $ZOOM$
declare
v_rec my_table%rowtype;
begin
select *
into v_rec
from my_table
where id = ' || v_record.id || ';
perform ' || func_name || '(v_rec);
end;
$ZOOM$
';
end loop;
end loop;
end;
$$
此外,我认为您需要将select
更改为perform
(如上所述)......或者select into
。
答案 1 :(得分:0)
DataBase Administrators在Erwin Brandstetter中回答了这个问题。所以,我想与大家分享决议。
在Postgres 8.4或更高版本中,您将使用USING
的{{1}}子句安全有效地传递值。但是,您的版本8.3中没有这个功能。在你的版本中,它可以像这样工作:
EXECUTE
呼叫:
CREATE OR REPLACE FUNCTION test_conditions()
RETURNS SETOF bigint AS
$func$
DECLARE
_rec record;
_func text;
_result bigint;
BEGIN
FOR _func in
SELECT p.proname
FROM pg_catalog.pg_namespace n
JOIN pg_catalog.pg_proc p ON p.pronamespace = n.oid
WHERE n.nspname = 'operacional'
AND p.proname LIKE E'fn\\_condition\\_%' -- no parens, proper string
ORDER BY p.proname -- no parens
LOOP
FOR _rec in
SELECT * FROM my_table WHERE id IN (1,2,3) -- no parens needed
LOOP
EXECUTE 'SELECT ' || quote_ident(_func) || '(' || quote_literal(_rec) || ')'
INTO _result;
RETURN NEXT _result;
END LOOP;
END LOOP;
END
$func$ LANGUAGE plpgsql SET search_path = 'public';
如果在函数体中使用SELECT * FROM test_conditions();
,那么set search_path = 'pg_catalog';
架构中的表格将不再可见。全局public
搜索路径是一个非常糟糕的主意。效果在设置期间保持不变。您可以使用SET
将其包含在事务中,但这仍然是一个坏主意。相反,如果你真的需要,只设置功能的环境,如演示
有关Postgres搜索路径的更多信息:
只是在没有分配或返回结果的情况下执行SET LOCAL
将毫无意义。使用SELECT
的{{1}}子句,然后INTO
。在现代Postgres中,您将用EXECUTE
替换内循环。
在构建动态查询字符串时,使用RETURN NEXT
和RETURN QUERY EXECUTE
正确转义标识符和文字。在现代Postgres中,您将使用quote_ident()
。
将整行投射到它的字符串表示,转义和退回是不是非常有效。这种替代方法必须重复读取表格,否则更清晰(行直接作为值传递):
quote_literal()
您还可以使用此SQL函数从根本上简化示例函数:
format()