When try to use the function below with SELECT * FROM t WHERE checkrange(3,r), returns an error "No function matches"
CREATE FUNCTION checkRange(
p_val anyelement, p_range anyarray
) RETURNS boolean AS $f$
SELECT bool_or(p_val <# r) FROM unnest(p_range) t(r);
$f$ LANGUAGE SQL IMMUTABLE;
Suppose
CREATE TABLE t (id serial, r int4range[]);
INSERT INTO t (r) VALUES
('{"[2,5]","[100,200]"}'::int4range[]),
('{"[6,9]","[201,300]"}'::int4range[]);
PS: I remember that we need to do some workwaround with PostgreSQL's anyelement, but not what... The error message is not obvious.
I think that's why:
from
https://www.postgresql.org/docs/current/static/extend-type-system.html#extend-types-polymorphic
Each position (either argument or return value) declared as anyelement
is allowed to have any specific actual data type, but in any given
call they must all be the same actual type. Each position declared as
anyarray can have any array data type, but similarly they must all be
the same type. And similarly, positions declared as anyrange must all
be the same range type. Furthermore, if there are positions declared
anyarray and others declared anyelement, the actual array type in the
anyarray positions must be an array whose elements are the same type
appearing in the anyelement positions.
So this will be ok:
select * from checkRange('[100,200]'::int4range, '{"[6,9]","[201,300]"}'::int4range[])
but that won't:
select * from checkRange(1, '{"[6,9]","[201,300]"}'::int4range[])
If you want your function to work with this anyarray just define first of it as integer to keep polymorphic arguments tied:
CREATE FUNCTION checkRange(
p_val bigint, p_range anyarray
) RETURNS boolean AS $f$
SELECT bool_or(p_val <# r) FROM unnest(p_range) t(r);
$f$ LANGUAGE SQL IMMUTABLE;
Related
For the first time I'm trying to create function in PostgreSQL:
This function must accept parameter with array type. Array contains sequence of biginteger values. Size of array not arbitrary, but known in advance.
create function get_total (cols ARRAY) returns biginteger AS
$BODY$
begin
// Some logics
end;
$BODY$
Usage in query
select
stats.value1,
stats.value2,
get_total(array_agg(ARRAY[stats.value2, stats.value1]))
from stats;
It returns error:
type cols[] does not exist
SQL state: 42704
When I run in Select only array_agg(ARRAY[stats.value2, stats.value1]), I see, that array created successfully. So the problem in function parameter.
What am I doing wrong?
You have to declare the parameter as bigint[], which reads an array of type bigint e.g.
CREATE OR REPLACE FUNCTION get_total (bigint[])
RETURNS bigint AS
$$
BEGIN
-- your fancy logic goes here
END;
$$ LANGUAGE plpgsql
Function call:
SELECT get_total(ARRAY[1111111111111,1111111111111]);
An elegant alternative is to declare the parameter as VARIADIC. Doing so you may call your function with multiple parameters,e.g.:
CREATE OR REPLACE FUNCTION get_total (VARIADIC bigint[])
RETURNS bigint AS
$$
BEGIN
-- your fancy logic goes here
END;
$$ LANGUAGE plpgsql;
Function call:
SELECT get_total(1111111111111,1111111111111,1111111111111);
Demo: db<>fiddle
The syntax cols ARRAY is the same as cols[] and means “an array with elements of type cols”. That's why you get that error message for the function definition.
If the element type is bigint, the function should be defined as
CREATE FUNCTION get_total(cols bigint ARRAY) RETURNS bigint
Say, I have composite type id_and_name.
CREATE TYPE id_and_name AS (id integer, name text);
I want to use this (id, name) structure as parameter in function, but I want that function user wouldn't need to cast manually: SELECT fun((1, 'name')::id_and_name); user should be good with SELECT fun((1, 'name')). That means that my function parameter will be anyelement or similar and I should cast it to id_and_name in function:
CREATE OR REPLACE FUNCTION fun(anyelement)
RETURNS id_and_name AS $$
SELECT $1::id_and_name;
$$ LANGUAGE sql;
For some reason SELECT fun((1, 'name')) gives me error:
ERROR: cannot cast type record to id_and_name
LINE 2: SELECT $1::id_and_name;
^
While the same query from function with substituted parameter works fine:
SELECT (1, 'name')::id_and_name;
How should I do such casting in functions?
I'm on PostgreSQL 10
As mentioned in the comments, it is preferable to specify argument. Otherwise you may use row_to_json to extract individual record elements and cast them
CREATE OR REPLACE FUNCTION fun(anyelement)
RETURNS id_and_name AS $$
SELECT (j->>'f1',j->>'f2')::id_and_name from row_to_json($1) as j;
$$ LANGUAGE sql;
DEMO
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
The following code that I use returns an integer 1:
CREATE TYPE my_test AS (
foo Integer
);
CREATE FUNCTION foo_out()
RETURNS SETOF Integer AS
$$
BEGIN
RETURN QUERY
SELECT 1 as foo;
END
$$
LANGUAGE plpgsql;
CREATE FUNCTION foo1()
RETURNS SETOF my_test
AS $$
DECLARE
x my_test;
BEGIN
FOR x IN
SELECT foo_out()
LOOP
RETURN NEXT x;
END LOOP;
END;
$$
LANGUAGE 'plpgsql';
select * from foo1();
But why does the same code return:
ERROR: invalid input syntax for integer: (1)
if I change the return type to:
CREATE FUNCTION foo_out()
RETURNS SETOF my_test
Which also should be an integer!? It could be the case that the system differs between the type integer and a custom type that includes an integer. What should be changed that the use of custom types is possible here?
my_test is a composite type, a row type, that contains a single integer field. It's not an integer.
When assigning to a record or row type in a FOR loop all output columns from the query form the row. Your SELECT needs to return a single, unnested integer column, which is then nested into a row type matching your custom type my_test.
You can also assign to a list of scalar variables in a FOR loop, in which case columns from the query are assigned left to right to variables as is - not forming a row.
If the column itself is a row type, you have one level of nesting to many. The text representation of a row containing an integer field is '(1)' (with parentheses!), and that's what you see in the error message.
You can fix that by extracting the integer field from the row with attribute notation:
SELECT (foo_out()).*
Or (more efficiently for multiple columns) by decomposing with:
SELECT * FROM foo_out()
Example Code
CREATE FUNCTION foo_out()
RETURNS SETOF my_test
LANGUAGE sql AS
'SELECT ''(1)''::my_test';
CREATE FUNCTION foo1()
RETURNS SETOF my_test
LANGUAGE plpgsql AS
$func$
DECLARE
x my_test;
BEGIN
FOR x IN
SELECT * FROM foo_out()
LOOP
RETURN NEXT x;
END LOOP;
END
$func$;
db<>fiddle here
Old sqlfiddle
Don't quote the language name plpgsql. It's an identifier.
Remember that looping is rarely needed, since most problems are more efficiently solved with a set-based approach (SQL only).
Related answers by Craig and Pavel:
Passing array of a composite type to stored procedure
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.