sqlpostgresqlplpgsqlpostgresql-8.3

Passing ROWTYPE parameter on EXECUTE


I am developing a function in Postgres which aims to recover for each record of a query the value of a result of a check contained in a set of functions. Only one of these functions will return the correct value. These functions have a comun prefix 'fn_condicao_' and receives as a parameter an object of type 'my_table'.

As the number of functions that make the check is unknown, I decided to consult the Postgres catalog, from the table pg_catalog.pg_proc searching for functions with the prefix 'fn_condicao_' and dynamically execute them with EXECUTE.

My problem is how to pass the correct shape parameter for 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;

How to pass v_record properly in the commented EXECUTE command in the function above?

execute 'select ' || v_function.proname || '(' || v_record || ')';  -- ???

Example function:

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;

Solution

  • This question was answered in DataBase Administrators by Erwin Brandstetter. So, I would like to share with you the resolution.


    DataBase Administrators answer

    In Postgres 8.4 or later you would use the USING clause of EXECUTE to pass values safely and efficiently. That's not available in your version 8.3, yet. In your version it could could work like this:

    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';
    

    Call:

    SELECT * FROM test_conditions();
    

    Example function

    You can also radically simplify your example function with this SQL function:

    CREATE OR REPLACE FUNCTION fn_condition_1(p_record my_table)
      RETURNS bigint AS
    $func$
       SELECT CASE WHEN $1.atributo1 > $1.atributo2 THEN bigint '1' END
    $func$  LANGUAGE sql;