在EXECUTE

时间:2016-01-21 20:32:14

标签: sql postgresql plpgsql postgresql-8.3

我正在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;

2 个答案:

答案 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 AdministratorsErwin 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 NEXTRETURN QUERY EXECUTE正确转义标识符和文字。在现代Postgres中,您将使用quote_ident()

  • 将整行投射到它的字符串表示,转义和退回是不是非常有效。这种替代方法必须重复读取表格,否则更清晰(行直接作为值传递):

    quote_literal()

示例功能

您还可以使用此SQL函数从根本上简化示例函数:

format()