PostgreSQL PL/pgSQL syntax error with FOREACH loop - postgresql

I'm trying to learn PL/pgSQL by writing some simple programs. To learn about FOREACH loop, I wrote the following:
CREATE OR REPLACE FUNCTION test(int[]) RETURNS void AS $$
DECLARE
window INT;
BEGIN
FOREACH window IN ARRAY $1
LOOP
EXECUTE 'SELECT $1' USING window;
END LOOP;
$$ LANGUAGE plpgsql;
SELECT test(ARRAY [30,60]);
I expect that this code snippet would first print 30 and then 60. However, I get the following error.
psql:loop.sql:11: ERROR: syntax error at end of input
LINE 7: EXECUTE 'SELECT $1' USING window;
^
psql:loop.sql:13: ERROR: function test(integer[]) does not exist
LINE 1: SELECT test(ARRAY [30,60]);
^
HINT: No function matches the given name and argument types. You might need
to add explicit type casts.
So the function definition has a syntax error, but I don't understand what the error is and how to fix it. I'd appreciate any help. Thanks!

Your function is declared as returns void so you can't return anything from it. If you want to return multiple values, you need to use returns setof integer
But it has more problems than that.
you should give your parameter a name (not an error, but good coding style)
to return a value from a function you need to use return. To return multiple values (because of returns setof) you need to use return next
there is no need for a dynamic SQL to return a value, you can return the variable directly.
Also not not an error, but: window is a keyword, I wouldn't use a variable with that name.
Applying all that, your function should look like this:
CREATE OR REPLACE FUNCTION test(p_input int[])
RETURNS setof integer
as
$$
DECLARE
l_value INT;
BEGIN
FOREACH l_value IN ARRAY p_input
LOOP
return next l_value;
END LOOP;
end;
$$
LANGUAGE plpgsql;
I am not sure if you are aware, but there is already a built-in function which achieves the same thing: unnest().

Related

PostgreSQL - not a known variable in procedure

I'm having trouble with writing a procedure in PostgreSQL. I can create the procedure, yet when i try to execute i get an error.The error i get
ERROR: "v_all_atts" is not a known variable
the query is:
CREATE OR REPLACE FUNCTION rebuild_views_with_extra_atts()
RETURNS VOID AS $$
DECLARE
v_all_atts varchar(4000);
BEGIN
CREATE OR REPLACE FUNCTION add_column(p_table text, p_column text,p_category text) RETURNS VOID AS $nothing$
declare
v_column_exists bigint := false ;
BEGIN
SELECT
string_agg( CASE WHEN owner='alarm' THEN 'ai' WHEN owner='fault' THEN 'fi'
END ||'.'||lower(alias) , ', ' ORDER BY owner, alias) AS string
INTO STRICT
v_all_atts
FROM
extra_attribute_cfg
WHERE
owner NOT LIKE 'virtual' and enable = true and v_column_exists = true;
IF LENGTH(v_all_atts) is not null THEN
v_all_atts := ', '||v_all_atts;
END IF;
v_view:= q'#
CREATE OR REPLACE VIEW alarm_view AS
SELECT
fi.fault_id, ai.alarm_id,
#'||v_all_atts||q'#
FROM
alarm ai
INNER JOIN fault fi
ON fi.fault_id = ai.fault_id
#';
EXECUTE v_view;
END;
$nothing$ language plpgsql;
end;
$$ LANGUAGE plpgsql;
I have taken a long look at Postgres documentation and cannot find what is wrong and didn't find any answer to this specific situation
Your rebuild_views_with_extra_atts() function is creating the add_column() function.
add_column() uses the v_all_atts variable, but it doesn't exist in that function, it exists only in the rebuild_views_with_extra_atts() function.
To resolve this, it really depends on what you're trying to do. If that variable should exist in the add_column() function, then declare it in there. If you're trying to use the value of v_all_atts when creating add_column() (e.g. so that the content of the function's body is dependent on the value of that variable), then you really need to use dynamic sql to generate a TEXT version of the CREATE OR REPLACE ... code, then EXECUTE it.

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).

Postgres - ERROR: syntax error at or near "IF"

I have a function and I need to do different select based on inputid. so I have added the condition as below.
....
begin
IF(iv_binrocess = 1 AND IsNull(iv_binno,'') != '') THEN
...//some process here
END If;
...
End
When I run the function, I got the error "
ERROR: syntax error at or near "IF"
I referred many sites and tutorials, nothing helped me.
Can anyone help me to fix this issue?
Thanks
WAG, since you deleted most of the important information: You're trying to create a function with a PL/pgsql body, but declaring it SQL.
craig=> CREATE FUNCTION test() RETURNS void LANGUAGE sql AS
$$ BEGIN IF TRUE THEN PERFORM 1; END IF; END; $$ ;
ERROR: syntax error at or near "IF"
LINE 1: ...TION test() RETURNS void LANGUAGE sql AS $$ BEGIN IF TRUE TH...
Declare PL/PgSQL functions as LANGUAGE plpgsql.
If you instead want to use an SQL function, use a CASE expression instead of IF.
CREATE FUNCTION test2() RETURNS integer LANGUAGE sql
AS $$ SELECT CASE WHEN TRUE THEN 1 ELSE 0 END; $$;
(Note, I haven't bothered to format these readably. Don't write functions all in one line like this in real code.)

Using ENUM types in functions

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.

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