0

I would like to be able to get values of function variables whose names are queried from a table

Edited to show querying a table instead of query from static values:

    create table __test__
(
_col text
);
insert into __test__
(_col)
values('_a');

   create or replace function __test() 
    returns void
    language 'plpgsql' as
    $$
    declare
      _r record;
      _a int;
      _b int;
      _sql text;
    begin

      _a = 1;
      _b = 0;

      for _r in select _col as _nam from __test__ a loop
      -- query returns one row valued "_a"
        _sql = 'select ' || _r._nam ;
        execute _sql into _b;
      end loop;

      raise info 'value of _b %', _b;

    end;
    $$;
select __test()

when function executes so that _b = 1. Is it possible?

same error ...

ERROR:  column "_a" does not exist
LINE 1: select _a
               ^
QUERY:  select _a
CONTEXT:  PL/pgSQL function "__test" line 15 at EXECUTE statement
5
  • Seems possible. Haven't you tried it yet? Commented Sep 6, 2012 at 10:46
  • I'm not sure exactly what the question here is. Commented Sep 6, 2012 at 13:09
  • Sure have. I get NOTICE: ErrCode (42703) (column "...var name, which is _a in the example" does not exist) Commented Sep 6, 2012 at 16:58
  • If I remove the quotes from around _a in the VALUES expression, it outputs INFO: value of _b 1 for me. Commented Sep 6, 2012 at 19:39
  • @deszo: you are right. in real program the values come from a query. Please see my code edit, it queries a table now and throws the same error. Commented Sep 7, 2012 at 1:55

2 Answers 2

2

You could create a temporary table, insert your variable names and values in it, and then execute a select against that. Just clean up after. I have used approaches like that before. It works ok. It does have extra overhead though.

Edit: adding an example

CREATE FUNCTION switch (in_var text) RETURNS text
LANGUAGE PLPGSQL VOLATILE AS $$

declare t_test text;
    switch_vals text[];

BEGIN

   CREATE TEMPORARY TABLE switch_values (var text, value text);

   EXECUTE $e$ INSERT INTO switch_values VALUES 
       ('a', '1'), ('b', '2'), ('c', '3') $e$;

   EXECUTE $e$ SELECT value FROM switch_values WHERE var = $e$ || quote_literal(in_var)
       INTO t_test;

   DROP TABLE switch_values;

   RETURN t_test;

END; $$; 

postgres=# select switch('a');
  switch 
 --------
   1
 (1 row)
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks! It's slightly much overhead for a real app imo.
Honestly, I don't like it either although I have used the temp table solution in the past for very complex areas. If I needed to do a real app, I'd do similar but: 1) I'd create a type to represent my conditions 2) I'd represent my conditions in the stored proc as an array of tuples of that type and 3) I'd use a select c.value from unnest(conditions) c instead of selecting from the temp table. However, that's more developer overhead. Otherwise it is exactly the same principle at work.
The problem becomes that name of the variable is available after "for _r in select _col as _nam from __ test __ a loop" query, which returns variable name that exists somewhere in the plpgsql function. I don't see how putting it into array and using unnest would "eval()" its value. There is no knowing what variable name the query returns. In your example variable names are static values + one that is passed as input, not quite the same imo. Your example would work after adding all function variables into arrays/temptable.
The point is you have to have some representation in the function of your conditions and output. Then you can select from your conditions and produce the output. This gets around the scoping issue because you creating, in effect, a lookup table (but one serialized as an array instead of stored to a temp table). The only differences between that and the solution I offered is: 1) schema-wise it is more complex because you need the custom type, and 2) since the table format is defined in a serialized form in the function, you don't need the overhead of actual table creation/cleanup.
2

Let's try to reframe the question: what you're after would be the equivalent of Perl eval() function, with its ability to execute a dynamically generated piece of code for which "any outer lexical variables are visible to it". In your example, the variable would be _a, but as you can see from the error message, it can't be interpolated by a dynamic SQL statement. The reason is that the SQL interpreter has no visibility on the current pl/pgsql variables, or even the knowledge that such variables exist. They are confined to pl/pgsql.

What would be needed here is a context-aware dynamically-generated pl/pgsql statement, but this language does not have this feature. It's doubtful that a trick could be found to achieve the result without this feature. For all its ability to interface nicely with SQL, other than that it's a fairly static language.

On the other hand, this would be no problem for pl/perl.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.