Create Redshift UDF with default Parameters - amazon-redshift

I am trying to create a Redshift UDF with function Parameters as below:
create or replace function function1(srctimezone VARCHAR,desttimezone VARCHAR,flag = 'nr') returns datetime
The last Parameter would be a defualt Parameter, i.e if user does not pass any value, it should take 'nr' by default. But I am not able to create function with default parameters. Any suggestions if Redshfit does / does not allow function creation with default Parameters. If it does, then what will be the correct syntax for the same?

In Redshift, you can create a Python UDF with an arbitrary number of arguments but you have to pass the same number of arguments when you execute the function. The workaround for optional parameters would be passing nulls and setting the parameter to the default value at the beginning of the function body similar to this:
create function f_optional_test(a int, b int)
returns int
stable as $$
if b==None:
return 999 # set default
else:
return b
$$ language plpythonu;
select f_optional_test(1,2); -- returns 2
select f_optional_test(1,null); -- returns 999
Also, you can create multiple functions with the same name but different number of parameters, so the function that is selected for execution will be picked up by the database engine based on the number of parameters and their data type:
create function f_optional_test(a int)
returns int
stable as $$
return 999 # default for b
$$ language plpythonu;
select f_optional_test(1,2); -- returns 2
select f_optional_test(1); -- returns 999
It's up to you to choose whether you'd like to have a single function at the expense of passing nulls for default parameters or have optional parameters at the expense of maintaining two versions of the same function if there is one optional parameter (with more optional parameters and their variable order it's more complicated and the first option is obviously better).

You can do:
CREATE OR REPLACE FUNCTION public.emulate_optional_arg(env_name varchar, optional varchar)
RETURNS varchar
LANGUAGE sql
IMMUTABLE
AS $$
SELECT $1 || ', ' || $2
$$
;
CREATE OR REPLACE FUNCTION public.emulate_optional_arg(env_name varchar)
RETURNS varchar
LANGUAGE sql
IMMUTABLE
AS $$
SELECT public.emulate_optional_arg($1,'default_value_here')
$$
;
SELECT public.emulate_optional_arg('dev');
/*
emulate_optional_arg |
-----------------------|
dev, default_value_here|
*/
SELECT public.emulate_optional_arg('dev','newvalue');
/*
emulate_optional_arg|
--------------------|
dev, newvalue |
*/

Related

how do I pass a user defined type variable to a function as a parameter?

I want to pass a user defined type parameter to a PLPGSQL function, but I am getting this error at runtime:
dev=# select process_shapes();
ERROR: invalid input syntax for integer: "(,,7)"
CONTEXT: PL/pgSQL function process_shapes() line 9 at SQL statement
dev=#
For some reason, the parameters are not passed correctly and I have no idea why it doesn't work.
My functions are:
CREATE OR REPLACE FUNCTION join_shapes(first_shape shape_t,second_shape shape_t,OUT new_shape shape_t)
AS $$
DECLARE
BEGIN -- simplified join_shape()s function
new_shape.num_lines:=first_shape.num_lines+second_shape.num_lines;
END;
$$ LANGUAGE PLPGSQL;
CREATE OR REPLACE FUNCTION process_shapes()
RETURNS void AS $$
DECLARE
rectangle shape_t;
triangle shape_t;
produced_shape shape_t;
BEGIN
rectangle.num_lines:=4;
triangle.num_lines:=3;
SELECT join_shapes(rectangle,triangle) INTO produced_shape;
RAISE NOTICE 'produced shape = %s',produced_shape;
END;
$$ LANGUAGE PLPGSQL;
Type definition:
CREATE TYPE shape_t AS (
shape_id integer,
shape_name varchar,
num_lines integer
);
Postgres version: 9.6.1
When the target of a SELECT ... INTO statement is of a composite type, it will assign each of the columns returned by the SELECT to a different field in the target.
However, SELECT join_shapes(rectangle,triangle) returns a single column of type shape_t, and it's trying to cram the whole thing into the first column of the target, i.e. produced_shape.shape_id (hence the error message about a failed integer conversion).
Instead, you need a SELECT statement which returns three columns. Just replace
SELECT join_shapes(rectangle,triangle)
with
SELECT * FROM join_shapes(rectangle,triangle)
Alternatively, you could use
produced_shape := (SELECT join_shapes(rectangle,triangle));
which performs a single assignment, rather than trying to assign the target fields individually.
For other people whom want to pass composite types to functions:
create type pref_public.create_test_row_input as (
name text
);
create or replace function pref_public.create_test_row(test_row pref_public.create_test_row_input) returns pref_public.test_rows as $$
insert into pref_public.test_rows (name)
values
(test_row.name)
returning *;
$$ language sql strict security definer;
grant execute on function pref_public.create_test_row to pref_user;
You'll need to use row()
select * from pref_public.create_test_row(row('new row'));
More info here

Stored procedure return type?

Is it possible for a stored procedure to return two different types?
CREATE OR REPLACE FUNCTION test_return_type(p_decide integer)
RETURNS t_my_type AS
$BODY$DECLARE
v_my_type t_my_type;
BEGIN
IF p_decide = 1 THEN
RETURN p_decide;
ELSE
--some code which will fill columns in v_my_type;
RETURN v_my_type;
END IF;
END;$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
Definition of 't_my_type':
CREATE TYPE t_my_type AS
(id integer,
owner integer,
type integer,
time bigint,
text character varying);
Code which uses procedure:
SELECT * FROM test_return_type(1);
SELECT * FROM test_return_type(2);
If this is not possible, should I fill v_my_type with some bogus data (when 1 is passed) just so I can return t_my_type as declared in procedure return type? Is there any better way than this?
I am using PostgreSQL v9.3 and pgAdmin III v1.18.0.
You can define your function with OUT parameters. In your example above the p_decide value would flag whether or not the v_my_type parameter has a meaningful value or not.
CREATE OR REPLACE FUNCTION test_return_type(OUT p_decide integer, OUT v_my_type t_my_type) RETURNS record AS $$
...
See the documentation on other options for returning data from functions.

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.

PostgreSQL: store function in column as value

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.
Your title asks something else than your example.
A function has to be created before you can call it. (title)
An expression has to be evaluated. You would need a meta-function for that. (example)
Here are 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.
CREATE OR REPLACE FUNCTION f1(int)
RETURNS int
LANGUAGE sql IMMUTABLE AS
'SELECT $1 * 100;';
CREATE OR REPLACE FUNCTION f2(text)
RETURNS text
LANGUAGE sql IMMUTABLE AS
$$SELECT $1 || '_foo';$$;
CREATE TABLE my_expr (
expr text PRIMARY KEY
, def text
, rettype regtype
);
INSERT INTO my_expr VALUES
('x', 'f1(3)' , 'int')
, ('y', $$f2('bar')$$, 'text')
, ('z', 'now()' , 'timestamptz')
;
CREATE OR REPLACE FUNCTION f_eval(text, _type anyelement = 'NULL'::text, OUT _result anyelement)
LANGUAGE plpgsql AS
$func$
BEGIN
EXECUTE
'SELECT ' || (SELECT def FROM my_expr WHERE expr = $1)
INTO _result;
END
$func$;
Related:
Refactor a PL/pgSQL function to return the output of various SELECT queries
Call:
SQL is strictly typed, the same result column can only have one data type. For multiple rows with possibly heterogeneous data types, you might settle for type text, as every data type can be cast to and from text:
SELECT *, f_eval(expr) AS result -- default to type text
FROM my_expr;
Or return multplce columns like:
SELECT *
, CASE WHEN rettype = 'text'::regtype THEN f_eval(expr) END AS text_result -- default to type text
, CASE WHEN rettype = 'int'::regtype THEN f_eval(expr, NULL::int) END AS int_result
, CASE WHEN rettype = 'timestamptz'::regtype THEN f_eval(expr, NULL::timestamptz) END AS tstz_result
-- , more?
FROM my_expr;
db<>fiddle here
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:
CREATE TABLE my_func (func text PRIMARY KEY, def text);
INSERT INTO my_func VALUES
('f'
, $$CREATE OR REPLACE FUNCTION f(int)
RETURNS int
LANGUAGE sql IMMUTABLE AS
'SELECT $1 * 100;'$$);
CREATE OR REPLACE FUNCTION f_create_func(text)
RETURNS void
LANGUAGE plpgsql AS
$func$
BEGIN
EXECUTE (SELECT def FROM my_func WHERE func = $1);
END
$func$;
Call:
SELECT f_create_func('f');
SELECT f(3);
db<>fiddle here
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.

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

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.