Can Postgres stored functions have both a return value AND out parameters? - postgresql

I know Oracle and PL/SQL
Compared to what I know about Oracle PL/SQL, I'm not very familiar with PostgreSQL's stored procedures and plpgsql. In Oracle, there are two types of callables:
Procedures. They can have IN, OUT and IN OUT parameters, but no return values
Functions. They can have IN, OUT and IN OUT parameters and they MUST return a value
But I'm new to plpgsql
I understand that in plpgsql, all stored procedures are considered functions. To my understanding, this means, they can (but don't have to) always return a value. Now I see on the documentation page, that I can also declare OUT parameters on functions, a thing that's not possible in Oracle. But I don't see an example or any clear statement about whether OUT parameters can be combined with return values. Neither can I see whether IN OUT parameters are possible.
So these are my questions:
Does plpgsql allow IN OUT parameters?
Does plpgsql allow OUT parameters to be combined with return values? Is this a common practice? Do you have examples for that?

IN and OUT are basically aliases for older syntax.
old way:
create function test(param int)
returns integer as
$$ select 1 $$
language sql;
equivalent:
create function test(in param int, out int)
as $$ select 1 $$
langauge sql;
What params do provide is type information which basically creates an anonymous type for your return:
create function test(in param, out int, out int)
as $$ select 1, 2 $$
langauge sql;
now you can write:
select * from test(1);
column1 | column2
---------+---------
1 | 2
Without the out params you would have have had to create a type or table that had two ints to cast the data to the right type:
create or replace function test(in a int)
returns record as
as $$ select 1, 2 $$
language sql;
^
select * from test(1);
ERROR: a column definition list is required
for functions returning "record"

... actually I should have searched a bit more myself. The answer is not far away on the documentations page:
http://www.postgresql.org/docs/current/static/sql-createfunction.html

if u specified out parameter, it means structure of your result
eg.
create function test(in param, out int, out int)
will return 2 columns of int. in postgre so far i know 2 way to do it.
1 return setof refcursor and use app to read it.
create function test(in param) RETURNS setof refcursor AS
declare result refcursor;
declare parameters refcursor;
begin
open result for select * from mytable;
return next result;
open parameter for select 11 as a, 22 as b;
return next parameters;
end;
2 use raise notice. In npgsql notice is an event which u can add handler to recieve.
raise notice 'my parameter = %', 11;
return query select * from mytable;
sorry that i didn't make it clear.
1 using 'out' parameter is to specifiy return query structure. u cannot return data + variable. 'out' in postgre doens't mean passing parameter reference.
2 if u want to return data + variable, either method 1 or 2.

Related

How to return diferent table data based on an ID passed to an SQL function

Based on this question I would like to know if it is possible to return different table data based on an ID passed to the function.
Something like (pseudocode):
CREATE FUNCTION schemaB.testFunc(p_id INT, select_param INT)
RETURNS setof schemaZ.Table_1
AS
$$
CASE
WHEN select_param = 1 THEN SELECT * FROM schemaZ.Table_1 WHERE id = p_id
WHEN select_param = 2 THEN SELECT * FROM schemaZ.Table_2 WHERE id = p_id
END;
$$
language sql;
Table_1 and Table_2 share no same columns and that invalidates the above RETURNS clause.
This is generally impossible with SQL functions. Even with a polymorphic return type, the actual return type must be determined at call time. But all statements in an SQL function are planned before the function is executed. So you'd always end up with an error message for one of the SELECT statements returning data that doesn't fit the return type.
The same can be done with dynamic SQL in a PL/pgSQL function - with some trickery:
CREATE OR REPLACE FUNCTION f_demo(_tabletype anyelement, _id int)
RETURNS SETOF anyelement LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY EXECUTE
format('SELECT * FROM %s WHERE id = $1', pg_typeof(_tabletype))
USING _id;
END
$func$;
Call (important!):
SELECT * FROM f_demo(null::schemaZ.Table_1, 1);
The "trick" is to cast a null value to the desired table type, thereby defining the return type and choosing from which table to select. Detailed explanation:
Refactor a PL/pgSQL function to return the output of various SELECT queries
Take this as proof of concept. Typically, there are better (safer, less confusing, more performant) solutions ...
Related:
Difference between language sql and language plpgsql in PostgreSQL functions

Defining global constants in Postgresql stored function/procedures?

I have a set of functions I have created in PostgreSql. I would like to be able to configure some behavior and limits with global constants.
So far, I have implemented functions like these, which my functions call to retrieve the constant value:
CREATE OR REPLACE FUNCTION slice_length()
RETURNS integer AS $$
BEGIN
RETURN 50;
END; $$
LANGUAGE plpgsql IMMUTABLE;
I was wondering, is there a better/smarter way to achieve this?
I had a similar problem: store some sort of configuration and access it from my function.
To solve the problem I created a function which returns a constant JSONB containing my configuration.
The function looks like this:
create or replace function config()
returns jsonb
language plpgsql
immutable
as $BODY$
declare
job_manager_config constant jsonb := '{
"notify": {
"channels": {
"all_available": "all.available",
"all_status": "all.status",
"task_available": "task.%s.available",
"task_status": "task.%s.status",
"job": "job.%s"
},
}
"allowed_pets": {
"dogs": 6,
"cats": 6,
"rabbits": 3,
"lions": 2,
"sweet_piranha": 8
}
}';
begin
return job_manager_config;
end;
$BODY$;
To access configuration elements quickly I defined a second function to query the configuration. This function accepts a path as a list of strings and return the value (or JSON object) found at the path.
Note that the returned value is text but you can easily cast it to the actual type.
create or replace function job_manager.config(
variadic path_array text[])
returns text
language plpgsql
immutable
as $BODY$
begin
-- Return selected object as text (instead of json or jsonb)
return jsonb_extract_path_text(job_manager.config(), variadic path_array);
end;
$BODY$;
This is a usage example:
test=# select job_manager.config('notify', 'channels', 'job');
config
--------------------
job_manager.job.%s
(1 row)
I would create a table for that:
create table constant (c1 int, c2 numeric);
insert into constant (c1, c2) values (100, 33.2);
The function, SQL not PL/pgSQL, would retrieve the single row:
create or replace function get_constants()
returns constant as $$
select *
from constant;
$$ language sql immutable;
And would be called for each constant:
select (get_constants()).c1, (get_constants()).c2;
All data would be in a single place and retrieved with a single function.
If a table is really that bad then place all the values in a single function:
create or replace function get_constants (
c1 out int, c2 out numeric
) returns record as $$
select 100, 33.5;
$$ language sql immutable;
And use it as above.
Take a look at this other answer. It uses the same approach that you do.
Create constant string for entire database
You can declare a table named constants for that purpose as mentioned that answer where each column corresponds to one setting.
To ensure that no more than one row can be added to the table, this answer might be helpful.
As come comments have pointed to the increased I/O when constants are stored like this as opposed to storing constants as functions, this might be a good reeding.

How to return uncertain number columns of a table from a postgresql function?

As we know, plpgsql functions can return a table like this:
RETURNS table(int, char(1), ...)
But how to write this function, when the list of columns is uncertain at the time of creating the function.
When a function returns anonymous records
RETURNS SETOF record
you have to provide a column definition list when calling it with SELECT * FROM. SQL demands to know column names and types to interpret *. For registered tables and types this is provided by the system catalog. For functions you need to declare it yourself one way or the other. Either in the function definition or in the call. The call could look like #Craig already provided. You probably didn't read his answer carefully enough.
Depending on what you need exactly, there are a number of ways around this, though:
1) Return a single anonymous record
Example:
CREATE OR REPLACE FUNCTION myfunc_single() -- return a single anon rec
RETURNS record AS
$func$
DECLARE
rec record;
BEGIN
SELECT into rec 1, 'foo'; -- note missing type for 'foo'
RETURN rec;
END
$func$ LANGUAGE plpgsql;
This is a very limited niche. Only works for a single anonymous record from a function defined with:
RETURNS record
Call without * FROM:
SELECT myfunc_single();
Won't work for a SRF (set-returning function) and only returns a string representation of the whole record (type record). Rarely useful.
To get individual cols from a single anonymous record, you need to provide a column definition list again:
SELECT * FROM myfunc_single() AS (id int, txt unknown); -- note "unknown" type
2) Return well known row type with a super-set of columns
Example:
CREATE TABLE t (id int, txt text, the_date date);
INSERT INTO t VALUES (3, 'foz', '2014-01-13'), (4, 'baz', '2014-01-14');
CREATE OR REPLACE FUNCTION myfunc_tbl() -- return well known table
RETURNS SETOF t AS
$func$
BEGIN
RETURN QUERY
TABLE t;
-- SELECT * FROM t; -- equivalent
END
$func$ LANGUAGE plpgsql;
The function returns all columns of the table. This is short and simple and performance won't suffer as long as your table doesn't hold a huge number of columns or huge columns.
Select individual columns on call:
SELECT id, txt FROM myfunc_tbl();
SELECT id, the_date FROM myfunc_tbl();
-> SQLfiddle demonstrating all.
3) Advanced solutions
This answer is long enough already. And this closely related answer has it all:
Refactor a PL/pgSQL function to return the output of various SELECT queries
Look to the last chapter in particular: Various complete table types
If the result is of uncertain/undefined format you must use RETURNS record or (for a multi-row result) RETURNS SETOF record.
The calling function must then specify the table format, eg:
SELECT my_func() AS result(a integer, b char(1));
BTW, char is an awful data type with insane space-padding rules that date back to the days of fixed-width file formats. Don't use it. Always just use text or varchar.
Given comments, let's make this really explicit:
regress=> CREATE OR REPLACE FUNCTION f_something() RETURNS SETOF record AS $$
SELECT 1, 2, TEXT 'a';
$$ LANGUAGE SQL;
CREATE FUNCTION
regress=> SELECT * FROM f_something();
ERROR: a column definition list is required for functions returning "record"
LINE 1: SELECT * FROM f_something();
regress=> SELECT * FROM f_something() AS x(a integer, b integer, c text);
a | b | c
---+---+---
1 | 2 | a
(1 row)

Declaring the tuple structure of a record in PL/pgSQL

I can't find anything in the PostgreSQL documentation that shows how to declare a record, or row, while declaring the tuple structure at the same time. If you don't define you tuple structure you get the error "The tuple structure of a not-yet-assigned record is indeterminate".
This is what I'm doing now, which works fine, but there must be a better way to do it.
CREATE OR REPLACE FUNCTION my_func()
RETURNS TABLE (
"a" integer,
"b" varchar
) AS $$
DECLARE r record;
BEGIN
CREATE TEMP TABLE tmp_t (
"a" integer,
"b" varchar
);
-- Define the tuple structure of r by SELECTing an empty row into it.
-- Is there a more straight-forward way of doing this?
SELECT * INTO r
FROM tmp_t;
-- Now I can assign values to the record.
r.a := at.something FROM "another_table" at
WHERE at.some_id = 1;
-- A related question is - how do I return the single record 'r' from
-- this function?
-- This works:
RETURN QUERY
SELECT * FROM tmp_t;
-- But this doesn't:
RETURN r;
-- ERROR: RETURN cannot have a parameter in function returning set
END; $$ LANGUAGE plpgsql;
You are mixing the syntax for returning SETOF values with syntax for returning a single row or value.
-- A related question is - how do I return the single record 'r' from
When you declare a function with RETURNS TABLE, you have to use RETURN NEXT in the body to return a row (or scalar value). And if you want to use a record variable with that it has to match the return type. Refer to the code examples further down.
Return a single value or row
If you just want to return a single row, there is no need for a record of undefined type. #Kevin already demonstrated two ways. I'll add a simplified version with OUT parameters:
CREATE OR REPLACE FUNCTION my_func(OUT a integer, OUT b text)
AS
$func$
BEGIN
a := ...;
b := ...;
END
$func$ LANGUAGE plpgsql;
You don't even need to add RETURN; in the function body, the value of the declared OUT parameters will be returned automatically at the end of the function - NULL for any parameter that has not been assigned.
And you don't need to declare RETURNS RECORD because that's already clear from the OUT parameters.
Return a set of rows
If you actually want to return multiple rows (including the possibility for 0 or 1 row), you can define the return type as RETURNS ...
SETOF some_type, where some_type can be any registered scalar or composite type.
TABLE (col1 type1, col2 type2) - an ad-hoc row type definition.
SETOF record plus OUT parameters to define column names andtypes.
100% equivalent to RETURNS TABLE.
SETOF record without further definition. But then the returned rows are undefined and you need to include a column definition list with every call (see example).
The manual about the record type:
Record variables are similar to row-type variables, but they have no
predefined structure. They take on the actual row structure of the
row they are assigned during a SELECT or FOR command.
There is more, read the manual.
You can use a record variable without assigning a defined type, you can even return such undefined records:
CREATE OR REPLACE FUNCTION my_func()
RETURNS SETOF record AS
$func$
DECLARE
r record;
BEGIN
r := (1::int, 'foo'::text); RETURN NEXT r; -- works with undefined record
r := (2::int, 'bar'::text); RETURN NEXT r;
END
$func$ LANGUAGE plpgsql;
Call:
SELECT * FROM my_func() AS x(a int, b text);
But this is very unwieldy as you have to provide the column definition list with every call. It can generally be replaced with something more elegant:
If you know the type at time of function creation, declare it right away (RETURNS TABLE or friends).
CREATE OR REPLACE FUNCTION my_func()
RETURNS SETOF tbl_or_type AS
$func$
DECLARE
r tbl_or_type;
BEGIN
SELECT INTO tbl_or_type * FROM tbl WHERE id = 10;
RETURN NEXT r; -- type matches
SELECT INTO tbl_or_type * FROM tbl WHERE id = 12;
RETURN NEXT r;
-- Or simpler:
RETURN QUERY
SELECT * FROM tbl WHERE id = 14;
END
$func$ LANGUAGE plpgsql;
If you know the type at time of the function call, there are more elegant ways using polymorphic types:
Refactor a PL/pgSQL function to return the output of various SELECT queries
Your question is unclear as to what you need exactly.
There might be some way that avoids the explicit type declaration, but offhand the best I can come up with is:
CREATE TYPE my_func_return AS (
a integer,
b varchar
);
CREATE OR REPLACE FUNCTION my_func()
RETURNS my_func_return AS $$
DECLARE
r my_func_return;
BEGIN
SELECT 1, 'one' INTO r.a, r.b;
RETURN r;
END; $$ LANGUAGE plpgsql;
Oh, I almost forgot the simplest way to do this:
CREATE OR REPLACE FUNCTION my_func2(out a int, out b text)
RETURNS RECORD AS $$
BEGIN
SELECT 1, 'one' INTO a, b;
RETURN;
END; $$ LANGUAGE plpgsql;
It is much easier to use OUT parameters rather than a record. If iteratively building a set of records (a table) use RETURN NEXT. If generating from a query, use RETURN QUERY. See:
https://stackoverflow.com/a/955289/398670
and:
http://www.postgresql.org/docs/current/static/plpgsql-declarations.html
http://www.postgresql.org/docs/current/static/sql-createfunction.html
http://www.postgresonline.com/journal/archives/129-Use-of-OUT-and-INOUT-Parameters.html
Think:
CREATE OR REPLACE FUNCTION my_func(OUT a integer, OUT b varchar) RETURNS SETOF RECORD AS $$
BEGIN
-- Assign a and b, RETURN NEXT, repeat. when done, RETURN.
END;
$$ LANGUAGE 'plpgsql';

Optional argument in PL/pgSQL function

I am trying to write a PL/pgSQL function with optional arguments. It performs a query based on a filtered set of records (if specified), otherwise performs a query on the entire data set in a table.
For example (PSEUDO CODE):
CREATE OR REPLACE FUNCTION foofunc(param1 integer, param2 date, param2 date, optional_list_of_ids=[]) RETURNS SETOF RECORD AS $$
IF len(optional_list_of_ids) > 0 THEN
RETURN QUERY (SELECT * from foobar where f1=param1 AND f2=param2 AND id in optional_list_of_ids);
ELSE
RETURN QUERY (SELECT * from foobar where f1=param1 AND f2=param2);
ENDIF
$$ LANGUAGE SQL;
What would be the correct way to implement this function?
As an aside, I would like to know how I could call such a function in another outer function. This is how I would do it - is it correct, or is there a better way?
CREATE FUNCTION foofuncwrapper(param1 integer, param2 date, param2 date) RETURNS SETOF RECORD AS $$
BEGIN
CREATE TABLE ids AS SELECT id from foobar where id < 100;
RETURN QUERY (SELECT * FROM foofunc(param1, param2, ids));
END
$$ LANGUAGE SQL
Since PostgreSQL 8.4 (which you seem to be running), there are default values for function parameters. If you put your parameter last and provide a default, you can simply omit it from the call:
CREATE OR REPLACE FUNCTION foofunc(_param1 integer
, _param2 date
, _ids int[] DEFAULT '{}')
RETURNS SETOF foobar -- declare return type!
LANGUAGE plpgsql AS
$func$
BEGIN -- required for plpgsql
IF _ids <> '{}'::int[] THEN -- exclude empty array and NULL
RETURN QUERY
SELECT *
FROM foobar
WHERE f1 = _param1
AND f2 = _param2
AND id = ANY(_ids); -- "IN" is not proper syntax for arrays
ELSE
RETURN QUERY
SELECT *
FROM foobar
WHERE f1 = _param1
AND f2 = _param2;
END IF;
END -- required for plpgsql
$func$;
Major points:
The keyword DEFAULT is used to declare parameter defaults. Short alternative: =.
I removed the redundant param1 from the messy example.
Since you return SELECT * FROM foobar, declare the return type as RETURNS SETOF foobar instead of RETURNS SETOF record. The latter form with anonymous records is very unwieldy, you'd have to provide a column definition list with every call.
I use an array of integer (int[]) as function parameter. Adapted the IF expression and the WHERE clause accordingly.
IF statements are not available in plain SQL. Has to be LANGUAGE plpgsql for that.
Call with or without _ids:
SELECT * FROM foofunc(1, '2012-1-1'::date);
Effectively the same:
SELECT * FROM foofunc(1, '2012-1-1'::date, '{}'::int[]);
You have to make sure the call is unambiguous. If you have another function of the same name and two parameters, Postgres might not know which to pick. Explicit casting (like I demonstrate) narrows it down. Else, untyped string literals work, too, but being explicit never hurts.
Call from within another function:
CREATE FUNCTION foofuncwrapper(_param1 integer, _param2 date)
RETURNS SETOF foobar
LANGUAGE plgpsql AS
$func$
DECLARE
_ids int[] := '{1,2,3}';
BEGIN
-- whatever
RETURN QUERY
SELECT * FROM foofunc(_param1, _param2, _ids);
END
$func$;
Elaborating on Frank's answer on this thread:
The VARIADIC agument doesn't have to be the only argument, only the last one.
You can use VARIADIC for functions that may take zero variadic arguments, it's just a little fiddlier in that it requires a different calling style for zero args. You can provide a wrapper function to hide the ugliness. Given an initial varardic function definition like:
CREATE OR REPLACE FUNCTION foofunc(param1 integer, param2 date, param2 date, optional_list_of_ids VARIADIC integer[]) RETURNS SETOF RECORD AS $$
....
$$ language sql;
For zero args use a wrapper like:
CREATE OR REPLACE FUNCTION foofunc(integer, date, date) RETURNS SETOF RECORD AS $body$
SELECT foofunc($1,$2,$3,VARIADIC ARRAY[]::integer[]);
$body$ LANGUAGE 'sql';
or just call the main func with an empty array like VARIADIC '{}'::integer[] directly. The wrapper is ugly, but it's contained ugliness, so I'd recommend using a wrapper.
Direct calls can be made in variadic form:
SELECT foofunc(1,'2011-01-01','2011-01-01', 1, 2, 3, 4);
... or array call form with array ctor:
SELECT foofunc(1,'2011-01-01','2011-01-01', VARIADIC ARRAY[1,2,3,4]);
... or array text literal form:
SELECT foofunc(1,'2011-01-01','2011-01-01', VARIADIC '{1,2,3,4}'::int[]);
The latter two forms work with empty arrays.
You mean SQL Functions with Variable Numbers of Arguments? If so, use VARIADIC.