How get row count from return type in postgres? - postgresql

I have a function that return a table (my customer type).
How I can get row count of return object?
CREATE TYPE observer_holder AS ("CustomerName" TEXT,"CustomerFamily" TEXT)
CREATE OR REPLACE FUNCTION getItem(i_callStateText TEXT, i_maxText TEXT)
RETURNS SETOF observer_holder
LANGUAGE plpgsql
AS $$
declare
sendOffset INTEGER;
fetchRecord RECORD;
result observer_holder;
runQuery2 BOOLEAN := lower(trim(both ' ' from i_callStateText)) ='notnull' AND (position('::tsrange' in i_maxText) < 1);
query1 TEXT := '...';
query2 TEXT := '...';
BEGIN
FOR fetchRecord IN EXECUTE CASE WHEN runQuery2 THEN query2 ELSE query1 END LOOP
result."CustomerFamily" := customerRecord.family;
result."CustomerName" := customerRecord.name;
.
.
.
RETURN next result;
END LOOP;
-- my question , how get result count?
RAISE notice 'row count: %', length(result::observer_holder); >> Exception
RETURN;
END; $$;
I need result row count. Please help me.

Related

Postgresql: fetch all not showing any data

Data is not showing when I tried to fetch data from cursor. dbo.show_cities_multiple2 is procedure which returns two cursors.
DO $$
<<first_block>>
DECLARE
counter refcursor := 0;
ca_cur refcursor:=null;
tx_cur refcursor:=null;
BEGIN
call dbo.show_cities_multiple2(ca_cur, tx_cur);
EXECUTE 'FETCH ALL from "' || tx_cur || '"';
RAISE NOTICE 'The current value of counter is %', ca_cur;
END first_block $$;
Your problem here is that you aren't actually doing anything with the results of your EXECUTE FETCH ... EXECUTE merely runs the statement that comes after it. A plpgsql function does not return anything unless you explicitly specify RETURN ...,RETURN NEXT ... or RETURN QUERY .... Furthermore, an anonymous DO $$ block cannot return any results. So if you want to actually retrieve rows from the cursor, you need to name your cursors and execute your fetch outside of the do block:
DO $$
<<first_block>>
DECLARE
counter integer := 0;
ca_cur refcursor:='ca_cur'; --name your cursors first, otherwise they will be anonymous!
tx_cur refcursor:='tx_cur';
BEGIN
call dbo.show_cities_multiple2(ca_cur, tx_cur);
...
END first_block $$;
--Fetch
FETCH ALL FROM tx_cur;
Alternatively, you can define a standard function and use RETURN NEXT OR RETURN QUERY to return the result sets from the cursors:
CREATE FUNCTION return_city_data() RETURNS SETOF cities AS $$
DECLARE
counter integer = 0;
tx_cur refcursor = 'tx_cur';
ca_cur refcursor = 'ca_cur';
rec RECORD;
BEGIN
CALL dbo.show_cities_multiple(tx_cur,ca_cur);
-- Iterate over each record in the cursor
FOR rec in EXECUTE 'FETCH ALL FROM ' || tx_cur LOOP
counter = counter + 1;
-- RETURN NEXT means "Append each value to the function's result set".
RETURN NEXT rec;
END LOOP;
FOR rec in EXECUTE 'FETCH ALL FROM ' || ca_cur LOOP
counter = counter + 1;
RETURN NEXT rec;
END LOOP;
--All records have been appended to the result set. the cursors can now be closed.
CLOSE tx_cur;
CLOSE ca_cur;
RAISE NOTICE 'The current value of counter is %', "counter";
END
$$ LANGUAGE plpgsql;
OR
CREATE FUNCTION return_city_data() RETURNS SETOF cities AS $$
DECLARE
counter integer = 0;
tx_cur refcursor = 'tx_cur';
ca_cur refcursor = 'ca_cur';
rec RECORD;
BEGIN
CALL dbo.show_cities_multiple(tx_cur,ca_cur);
-- RETURN QUERY means "append all rows from the query to the function's result set"
RETURN QUERY EXECUTE 'FETCH ALL FROM ' || tx_cur;
RETURN QUERY EXECUTE 'FETCH ALL FROM ' || ca_cur;
CLOSE tx_cur;
CLOSE ca_cur;
END
$$ LANGUAGE plpgsql;
You can use either of these forms with a simple SELECT statement:
SELECT * FROM return_city_data();
Note that unlike RETURN, RETURN NEXT and RETURN QUERY do not terminate your function and you can use them multiple times in your blocks. The function will then naturally terminate at the end of the last block.

postgresql plpgsql: not able to iterate over a array of varchar[] type

i am trying to check if a element is present in an array using plpgsql.
and i am receiving "array subscript must have integer" error while executing the function.
select test('IND') should return true and select test('ING') should return false
Below is the code
create or replace function test(country varchar)
returns varchar
language plpgsql
AS $function$
declare
results varchar;
countryarr varchar[3];
i varchar[];
begin
countryarr := array['IND','USA','MEX'];
foreach i slice 1 in array countryarr
loop
if countryarr[i]=country
then results := 'TRUE';
else
results := 'FALSE';
end if;
end loop;
return results;
end;
$function$
;
Your i is defined as a varchar[] in your code. It cannot be used as an integer.
You want something like this:
create or replace function test(country varchar)
returns varchar
language plpgsql
AS $function$
declare
countryarr varchar[3];
i text;
begin
countryarr := array['IND','USA','MEX'];
foreach i in array countryarr
loop
if i = country
then return 'TRUE';
end if;
end loop;
return 'FALSE';
end;
$function$
;
A better solution for what you are trying to achieve is:
create or replace function test(country varchar)
returns varchar
language sql
AS $function$
select case
when country = any(array['IND', 'USA', 'MEX']) then 'TRUE'
else 'FALSE'
end;
$function$;
Let your function return a boolean. Then it reduces to a single SQL statement.
create or replace function is_valid_country(country_in varchar)
returns boolean
language sql
immutable strict
AS $$
select country_in = any(array['IND', 'USA', 'MEX']) ;
$$;
with test(country) as
( values ('IND'), ('USA'), ('MEX'), ('CAN'),('UK') )
select country, is_valid_country(country) is_valid
from test;
This can be used in any subsequent sql statement. And the optimizer can in-line it.

How do I loop through a list of columns in pg/PLSQL?

I have a table with about 3 dozen VARCHAR columns that all need the same cleanup before the record is inserted into the database (convert empty string to NULL). There are multiple apps accessing the DB, so I'd like to do this in a trigger. I could write code to check and set each column, but I'd like to loop through the columns instead. The function I wrote does not produce an error, but it also does not set empty columns to NULL.
CREATE OR REPLACE FUNCTION public.validate_flds() RETURNS trigger AS
$BODY$
DECLARE
coldata VARCHAR;
collist VARCHAR[];
BEGIN
collist := ARRAY[NEW.fld01,NEW.fld02,NEW.fld03];
FOREACH coldata IN ARRAY collist LOOP
IF coldata = '' THEN coldata := NULL; END IF;
END LOOP;
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql;
What am I missing?
In
IF coldata = '' THEN coldata := NULL; END IF;
you alter the value of the variable while the NEW record remains unchanged. There is no easy way to do what you want in a loop. I would suggest using NULLIF().
begin
new.fld01:= nullif(new.fld01, '');
new.fld02:= nullif(new.fld02, '');
new.fld03:= nullif(new.fld03, '');
return new;
end;

Push result of each query into array

I've created function:
CREATE FUNCTION citiesById(integer[]) RETURNS text[] AS
$$
DECLARE
element int;
result text[];
BEGIN
FOREACH element IN ARRAY $1
LOOP
WITH t1 as (SELECT city FROM cities WHERE id = element)
SELECT city FROM t1 INTO result;
END LOOP;
RETURN result;
END
$$
LANGUAGE plpgsql;
I'm trying to execute queries in a loop and insert result of each query into array to get something like ['London', 'Paris', 'Moscow']. But I'm getting an error:
Is there a correct way to do that?
CREATE FUNCTION citiesById(integer[]) RETURNS text[] AS
$$
DECLARE
element int;
result text[];
BEGIN
FOREACH element IN ARRAY $1
LOOP
result := array_append(result, (SELECT city FROM cities WHERE id = element)::text);
END LOOP;
RETURN result;
END
$$
LANGUAGE plpgsql;

Removing else-if-elseif.. in postgresql function

I would like to know if and there is some way to get rid code like this:
CREATE FUNCTION parent_function (json json, OUT response_status integer, OUT response json) RETURNS record
LANGUAGE plpgsql
AS $$
DECLARE
BEGIN
if <condition> then
<function_call1(json,response_status,response)>;
return;
elsif <condition> then
<function_call2(json,response_status,response)>;
return;
elsif <condition> then
<function_call3(json,response_status,response)>;
return;
end if;
END;
$$
I would like to move these conditions into separate functions where I would like based on what the function returns(there are some output parameters I need) terminate parent function without throwing exceptions and catch them in parent function?
What I would like to achive code like this:
CREATE FUNCTION parent_function (json json, OUT response_status integer, OUT response json) RETURNS record
LANGUAGE plpgsql
AS $$
DECLARE
BEGIN
<check_function1(json,response_status,response)>;
<check_function2(json,response_status,response)>;
<check_function3(json,response_status,response)>;
END;
$$
After calling message and output parameters set from function terminate further processing. Is it somehow posible?
Thanks,
Lukas
you want something like this example?
CREATE OR REPLACE FUNCTION aa_test_check1( IN test_value integer)
RETURNS TABLE( problem boolean , problemmessage text ) AS
$BODY$
begin
problem := false;
if $1 <10 then
problem := true;
problemmessage := 'integer is smaller than 10';
end if;
return next;
end;
$BODY$ LANGUAGE plpgsql IMMUTABLE;
CREATE OR REPLACE FUNCTION aa_test_check2( IN test_value integer)
RETURNS TABLE( problem boolean , problemmessage text ) AS
$BODY$
begin
problem := false;
if $1 >20 then
problem := true;
problemmessage := 'integer is greater than 20';
end if;
return next;
end;
$BODY$ LANGUAGE plpgsql IMMUTABLE;
CREATE OR REPLACE FUNCTION aa_test_check3( IN test_value integer)
RETURNS TABLE( problem boolean , problemmessage text ) AS
$BODY$
begin
problem := false;
if $1 =10 then
problem := true;
problemmessage := 'something is wrong, it cant be 10';
end if;
return next;
end;
$BODY$ LANGUAGE plpgsql IMMUTABLE;
CREATE OR REPLACE FUNCTION aa_test_main( IN test_value integer)
RETURNS TABLE( status_id integer , problemmessage text ) AS
$BODY$
declare
temp_problem boolean;
temp_problemmessage text;
begin
status_id := 0;
problemmessage := 'no problem, none at all';
select * from aa_test_check1($1)
into temp_problem, temp_problemmessage;
if temp_problem then
status_id := 1;
problemmessage := temp_problemmessage;
return next;
return;
end if;
select * from aa_test_check2($1)
into temp_problem, temp_problemmessage;
if temp_problem then
status_id := 2;
problemmessage := temp_problemmessage;
return next;
return;
end if;
select * from aa_test_check3($1)
into temp_problem, temp_problemmessage;
if temp_problem then
status_id := 3;
problemmessage := temp_problemmessage;
return next;
return;
end if;
return next;
end;
$BODY$
LANGUAGE plpgsql IMMUTABLE
COST 100
ROWS 100;
select * from aa_test_main(10)