Using ENUM types in functions - postgresql

I have a function defined as follows:
CREATE OR REPLACE FUNCTION public.somefcn(
_somevar enum_my_type
)
RETURNS integer AS
$body$
DECLARE
ret_id INTEGER
BEGIN
INSERT INTO mytable(somevar) VALUES (_somevar) RETURNING id INTO ret_id;
RETURN ret_id;
END;
$body$
LANGUAGE 'plpgsql'
When I call this like this
SELECT somefcn('validenumitem');
I get this error:
ERROR: column "somevar" is of type enum_my_type but expression is of type text
How should I update my function or call to make it work?

Found it. I had another, incorrect function as
CREATE OR REPLACE FUNCTION public.somefcn(
_somevar text
)
...
Apparently, PostgreSQL took that one, because it fits better.

Related

PostgreSQL: function with array argument

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

Postgres: ERROR: value too long for type character(4)

I'm new to Postgres and currently trying to write a function that takes in an integer and then calls another (definitely working) pre-written function with that input. If I call the pre-written function directly, e.g.:
select * from func(3456789);
I get the result I expect, with no errors. However, if I call it inside my function:
create or replace function my_func(_id integer)
returns setof Record
as $$
declare
rec Record;
begin
for rec in
select * from func(_id)
loop
return next rec;
end loop;
end;
$$ language plpgsql
;
where I call it with exactly the same parameter:
select * from my_func(3456789);
I get the following error:
ERROR: value too long for type character(4)
CONTEXT: PL/pgSQL function my_func(integer) line 5 at FOR over SELECT rows
Why are these two cases different, and how can I fix it?
EDIT:
func(_sid) returns this:
CREATE FUNCTION func(_sid integer) RETURNS SETOF Record
LANGUAGE plpgsql
AS $$ ...
and Record is defines like this:
create type Record as (code char(8), name char(4), num integer, work char(4));
Every line that is returned by your function func is mapped to a Record.
So the error you are getting says that one of the lines in the function result contains at column positions of name and/or work a value bigger than the one required by the columns (wich is 4).

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

Rename the column name of a stored function

I've got a postgresql stored procedure, which is returning an integer.
When I call that function, the result is returned with the function name as column name.
For example the name of the function is: "add-person". The column name, when invoking the function, is "add-person".
Is there a way to make the database return the integer with a self-choosen column name? For example "id"?
I think it is pretty easy, but I currently miss the forests for the trees..
Edit:
What i'd missed to tell, is that the return value is a variable, like so:
CREATE OR REPLACE FUNCTION "scheme"."add-person"(arggivenname character varying, argfamilyname character varying) RETURNS integer AS
$BODY$
DECLARE
varResponse integer;
BEGIN
-- Operations before
INSERT INTO "scheme"."table"
(
given_name,
family_name
)
VALUES
(
arggivenname,
argfamilyname
)
RETURNING
"id"
INTO
varResponse;
-- Operations after
RETURN varResponse;
END;
$BODY$
LANGUAGE plpgsql VOLATILE COST 100;
You can us the AS statement for that. That means:
Select add-person() AS yourcolumnname
To have a named column from a function it is necessary to create a type and return that type from the function
create type mytype as (mycolumn integer);
create or replace function ri()
returns mytype as $$
select 1;
$$ language sql;
select * from ri();
mycolumn
----------
1
Edit
Or much simpler without the type creation as in #pozs comment:
create or replace function ri(out mycolumn integer)
as $$
select 1;
$$ language sql;

Declare variable of composite type in PostgreSQL using %TYPE

Question: How can I declare a variable of the same type a parameter in a stored function?
The simple answer is use %TYPE, this works:
CREATE OR REPLACE FUNCTION test_function_1(param1 text)
RETURNS integer AS
$BODY$
DECLARE
myVariable param1%TYPE;
BEGIN
return 1;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
But the problem is when param1 is a composite type:
CREATE TYPE comp_type as
(
field1 text
)
CREATE OR REPLACE FUNCTION test_function_2(param1 comp_type)
RETURNS integer AS
$BODY$
DECLARE
myVariable param1%TYPE;
BEGIN
return 1;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
This doesn't work:
ERROR: type comp_type does not exist [SQL State=42704]
So how can I do when param1 is a composite type?
(Note: Just myVariable comp_type is not a good option because my function is slightly more complex.)
Edited:
I had a mistake on copy&paste, the real error is:
ERROR: invalid type name "param1%TYPE"
Position: 130 [SQL State=42601]
And using param1%ROWTYPE the error is:
ERROR: relation "param1" does not exist
Where: compilation of PL/pgSQL function "test_function_2" near line 3 [SQL State=42P01]
Use %ROWTYPE in that case.
Simple case
Tests by A.H. and DavidEG have shown this won't work. Interesting problem!
You could try a workaround. As long as your definition is like the example you can simply resort to
CREATE FUNCTION test(param1 comp_type)
RETURNS integer
LANGUAGE plpgsql VOLATILE AS
$func$
DECLARE
myvar comp_type;
BEGIN
RETURN 1;
END
$func$;
But your real problem is probably not as simple?
The real problem
As expected, the real problem turns out to be more complex: a polymorphic input type.
Workaround for that scenario was harder, but should work flawlessly:
CREATE FUNCTION test(param1 anyelement, OUT a integer, OUT myvar anyelement)
RETURNS record
LANGUAGE plpgsql VOLATILE AS
$func$
BEGIN
myvar := $1; -- myvar has the required type now.
-- do stuff with myvar.
myvar := NULL; -- reset if you don't want to output ..
a := 1;
END;
$func$;
Call:
SELECT a FROM test('("foo")'::comp_type); -- just retrieve a, ignore myvar
See full output:
SELECT * FROM test('("foo")'::comp_type);
Note for Postgres 9.0+
There has been a crucial update in Postgres 9.0. The release notes:
Allow input parameters to be assigned values within PL/pgSQL functions
(Steve Prentice)
Formerly, input parameters were treated as being declared CONST, so
the function's code could not change their values. This restriction
has been removed to simplify porting of functions from other DBMSes
that do not impose the equivalent restriction. An input parameter now
acts like a local variable initialized to the passed-in value.
In addition to my workaround, you can (ab)use input variables directly now.
Dynamic field names
See:
How to clone a RECORD in PostgreSQL
How to set value of composite variable field using dynamic SQL