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;