4

Can functions be stored as anonymous functions directly in column as its value?

Let's say I want this function be stored in column. Example (pseudocode):

Table my_table: pk (int), my_function (func)

func ( x ) { return x * 100 }

And later use it as:

select 
    t.my_function(some_input) AS output
from 
    my_table as t 
where t.pk = 1999

Function may vary for each pk.

Erwin Brandstetter
  • 539,169
  • 125
  • 977
  • 1,137
Peter
  • 874
  • 1
  • 12
  • 26
  • Presumably you mean `my_function()` or `some_actual_schema.my_function()` instead of `t.my_function()`, since `t` is a table alias. As long as we're not trying that bit of insanity, then as long as the function outputs a single non-set, non-record value (ie it doesn't output a row or a table), then yes, it should be fine. –  Nov 09 '11 at 17:21
  • 2
    No, it sounds like t.my_function() is EXACTLY what's he's asking for. Basically, the DB equivalent of a function pointer or clousure. – Tyler Eaves Nov 09 '11 at 18:36
  • Yes, I meant something like closure. – Peter Nov 09 '11 at 18:53

1 Answers1

7

Your title asks something else than your example.

  1. A function has to be created before you can call it. (title)
  2. An expression has to be evaluated. You would need a meta-function for that. (example)

I'll present solutions for both.

1. Evaluate expressions dynamically

You have to take into account that the resulting type can vary. I use polymorphic types for that.

-- DROP SCHEMA x CASCADE;
CREATE SCHEMA x;

CREATE OR REPLACE FUNCTION x.f1(int)
  RETURNS int AS
$$SELECT $1 * 100;$$
  LANGUAGE sql IMMUTABLE;

CREATE OR REPLACE FUNCTION x.f2(text)
  RETURNS text AS
$$SELECT $1 || '_foo';$$
  LANGUAGE sql IMMUTABLE;

CREATE TABLE x.my_expr (expr text PRIMARY KEY, def text, rettype text);

INSERT INTO x.my_expr VALUES
 ('x', $$x.f1(3)$$, 'int')
,('y', $$x.f2('bar')$$, 'text')
;

CREATE OR REPLACE FUNCTION x.f_eval(text, _type anyelement, OUT _result anyelement)
  AS
$x$
BEGIN

EXECUTE
'SELECT ' || (SELECT def FROM x.my_expr WHERE expr = $1)
INTO _result;

END;
$x$
LANGUAGE plpgsql;

Call:

SELECT x.f_eval('x', (SELECT rettype FROM x.my_expr WHERE expr = 'x'));
 f_eval
--------
 300
(1 row)

SELECT x.f_eval('y', (SELECT rettype FROM x.my_expr WHERE expr = 'y'));
 f_eval
---------
 bar_foo
(1 Zeile)

2. Create and use functions dynamically

It is possible to create functions dynamically and then use them. You cannot do that with plain SQL, however. You will have to use another function to do that or at least an anonymous code block (DO statement), introduced in PostgreSQL 9.0.

It can work like this:

-- DROP SCHEMA x CASCADE;
CREATE SCHEMA x;
CREATE TABLE x.my_func (func text PRIMARY KEY, def text);

INSERT INTO x.my_func VALUES('f'
, $$CREATE OR REPLACE FUNCTION f(int)
  RETURNS int AS
'SELECT $1 * 100;'
  LANGUAGE sql IMMUTABLE$$);

CREATE OR REPLACE FUNCTION x.f_create_func(text)
RETURNS void AS $x$
BEGIN

EXECUTE (SELECT def FROM x.my_func WHERE func = $1);

END;
$x$
LANGUAGE plpgsql;

Call:

select x.f_create_func('f');
SELECT f(3);

You may want to drop the function afterwards.

In most cases you should just create the functions instead and be done with it. Use separate schemas if you have problems with multiple versions or privileges.

For more information on the features I used here, see my related answer on dba.stackexchange.com.

Community
  • 1
  • 1
Erwin Brandstetter
  • 539,169
  • 125
  • 977
  • 1,137
  • Thank you Erwin for this example. I suspected that what I'm asking might not work, so this or something like this, was plan B. – Peter Nov 09 '11 at 18:57
  • How are you assigning the return datatype in `x.f_eval`? I see you pass `_type anyelement` as the second argument; however, I don't see how it is being used in the function definition. – losthorse May 04 '16 at 15:54
  • disregard the above comment... details found at http://stackoverflow.com/questions/11740256/refactor-a-pl-pgsql-function-to-return-the-output-of-various-select-queries/11751557#11751557 – losthorse May 04 '16 at 16:09