Updated November, 23 2023

Functions#

Not Implemented#

1. Not default parameters after default params in proc declarations.

2. Cast params in the function’s call.

3. Forward declaration default params.

Functions with OUT parameters#

Postgres supports functions with OUT parameters, but they should be invoked in a different manner than regular functions. Output parameters in Postgres automatically make function returns kind of record with fields equal to OUT parameters. So we’ll add an additional OUT parameter for the function result.

Declaration example:

 1--Oracle
 2CREATE OR REPLACE FUNCTION fun (a IN NUMBER,
 3                                b OUT NUMBER,
 4                                c IN OUT NUMBER)
 5RETURN NUMBER
 6IS
 7BEGIN
 8  b := a + 10;
 9  c := c + a;
10  RETURN( 100 );
11END;

For invoke, we create a temporal variable and select into it the result from a function. Also, replace function call with variable and before expression put select into with a real function call.

Invoking example:

1--Oracle
2DECLARE
3  x NUMBER := 10;
4  y NUMBER := 20;
5  z NUMBER;
6BEGIN
7  z := fun(x,z,y);
8  DBMS_OUTPUT.PUT_LINE(' x'||x||' y'||y||' z'||z);
9END;

Limitations#

One of the limitations of this approach is multiple invoke of the function in a single expression with changing INOUT variables.

 1--Oracle
 2CREATE OR REPLACE FUNCTION fun (a IN NUMBER,
 3                                c IN OUT NUMBER)
 4RETURN NUMBER
 5IS
 6BEGIN
 7  c := c + 1;
 8  RETURN c;
 9END;
10/
11
12DECLARE
13  a NUMBER := 10;
14  c NUMBER := 20;
15BEGIN
16  c := c + fun(a, c) + fun(a, c);
17  -- C=64. Second call of fun(a,c) doesn't
18  -- affect c variable in the begining
19  -- of addition operation
20  DBMS_OUTPUT.PUT_LINE('a '||a||'  c '||c);
21END;

Function with no arguments#

If the function doesn’t have any argument or has all defaults, Oracle can call this function without parentheses. In Postgres, you should always have parentheses. As we don’t have type inference, we create synonyms that add parentheses to a call.

 1--Oracle
 2CREATE FUNCTION test_func
 3RETURN test_table.id%TYPE
 4IS
 5  retVal test_table.id%TYPE:=-7777;
 6BEGIN
 7  RETURN retVal;
 8END;
 9
10
11SELECT id, txt, test_func AS "test"
12  FROM test_table
13 WHERE test_func = -7777;

NO_DATA_FOUND inside functions#

In Oracle, NO_DATA_FOUND is an error and a state at the same time, depending on what is calling function is.

If the function is called by PlSQL, NO_DATA_FOUND is considered to be an error and an exception is raised. If the function is called by SQL, NO_DATA_FOUND is considered to be a state and no exception is raised. In PostgreSQL, it is always an error. To emulate this for functions that can rise NO_DATA_FOUND (rise explicitly or in select into), we create a special wrapper with the original function name to suppress an error. In the original function, we change the name to this template lbr$<function_name>$throw_no_data and call this in PLSQL instead of the original name.

1--Oracle
2CREATE OR REPLACE FUNCTION no_data_func
3RETURN VARCHAR2
4AS
5BEGIN
6  RAISE NO_DATA_FOUND;
7  RETURN 0;
8END;

PIPELINED functions#

Pipelined table functions are table functions that return or “pipe” rows back to the calling query as the function produces the data in the desired form - and before the function has completed all of its processing. For emulation, we add to every nested table or VARRAY helper field elem with the type of collection element.

 1--Oracle
 2CREATE FUNCTION test_func(par1 test_table.id%TYPE)
 3-- 1
 4RETURN test_type_set PIPELINED
 5IS
 6  outRec test_type;
 7  CURSOR cur (cur_id test_table.id%TYPE, cur_dt test_table.dt%TYPE) IS
 8    SELECT id,
 9           txt
10      FROM test_table
11     WHERE id <= cur_id
12       AND dt >= cur_dt;
13BEGIN
14  FOR vRec IN cur(par1, TRUNC(SYSDATE)) LOOP
15    outRec.tabId := vRec.id;
16    outRec.tabTxt := vRec.txt;
17    outRec.recType := varConstType;
18    outRec.recDesc := varConstDesc;
19    -- 2
20    PIPE ROW(outRec);
21  END LOOP;
22  RETURN;
23END test_func;

Nested functions#

To support this feature, we declare each nested function in the top scope before the parent block/function.

Each nested function has a name <parent_function/block_id>$nested_func_id. Also, for each nested function, we capture closure variables from the outer scope and insert them in the function’s call.

Each anonymous nested function we create in pg_temp schema.

Examples

 1--Oracle
 2
 3--Block
 4DECLARE
 5  a NUMBER := 1000;
 6  FUNCTION f1(b NUMBER) RETURN NUMBER AS
 7  BEGIN
 8    RETURN a + b + 100;
 9  END;
10BEGIN
11  DECLARE a NUMBER := 10;
12  BEGIN
13    DBMS_OUTPUT.PUT_LINE(f1(a));
14  END;
15END;
16
17--Global
18CREATE OR REPLACE FUNCTION a2(v1 INT) RETURN INT AS
19BEGIN
20  DECLARE
21    FUNCTION b(v2 INT) RETURN INT AS
22    BEGIN
23      RETURN v1 + v2;
24    END;
25  BEGIN
26    RETURN b(100);
27  END;
28END;
29/
30
31SELECT a2(1000) FROM DUAL;
32-- 1100
33
34
35--Overloading
36DECLARE
37  a NUMBER := 1000;
38  FUNCTION f1(b NUMBER) RETURN NUMBER AS
39  BEGIN
40    RETURN a + b + 100;
41  END;
42  FUNCTION f1(b VARCHAR2) RETURN VARCHAR2 AS
43  BEGIN
44    RETURN b;
45  END;
46BEGIN
47  DECLARE a NUMBER := 10;
48  BEGIN
49    DBMS_OUTPUT.PUT_LINE(f1(a)||'-'||f1('a'));
50  END;
51END;