Postgres calling Function in functio does not what I expect - postgresql

Hi,
the function F_B() returns in bracket "(i,m)" instead of 2 columns "i" and "m"!
CREATE FUNCTION F_A
(
i_productionsitename IN TEXT,
i_printjobid IN TEXT,
i_isnewlycreated IN INT,
o_returncode OUT TEXT,
o_message OUT TEXT
) AS $$
DECLARE
BEGIN
Select 'i', 'm' into o_ReturnCode, o_message;
END;
$$ LANGUAGE plpgsql;
SELECT * FROM F_A('DRM', '42',1);
=> i & m
CREATE FUNCTION F_B
(
i_productionsitename IN TEXT,
i_printjobid IN TEXT,
i_isnewlycreated IN INT,
o_returncode OUT TEXT,
o_message OUT TEXT
) AS $$
DECLARE
rec RECORD;
l_ReturnCode TEXT;
l_Message TEXT;
BEGIN
o_ReturnCode := 'l_OK';
o_Message := 'l_Kein Problem';
Select F_A (i_ProductionSiteName, i_PrintJobID , i_isnewlycreated) INTO l_ReturnCode, l_Message ; --rec ;
o_ReturnCode := l_ReturnCode; --' rec.o_message;
o_Message := l_Message;
END;
$$ LANGUAGE plpgsql;
SELECT * FROM F_B('DRM', '42',1);
=> gives (i,m) & null
Why is second column empty, but first has the 2 values?
What is wrong?
Thanks

Related

How can create one function from two functions in PostgreSQL?

I would like to create another function in PostgreSQL, function_c, from function_a and function_b.
function_a
CREATE OR REPLACE FUNCTION function_a(year_ INTEGER, col_b INTEGER)
RETURNS INTEGER AS
$$
DECLARE
vbl INTEGER;
BEGIN
SELECT COUNT(col_b) INTO vbl
FROM table1
WHERE date_part('year',table1.col_aa) = year_ AND table1.col_bb = col_b;
RETURN vbl;
END;
$$
LANGUAGE plpgsql;
function_b
CREATE OR REPLACE FUNCTION function_b(year_ INTEGER)
RETURNS INTEGER AS
$$
DECLARE
vbl_b integer;
BEGIN
SELECT COUNT(col_aa) INTO vble_b
FROM table1
WHERE date_part('year',table1.col_aa) = year_;
RETURN vbl_b
END;
$$
LANGUAGE plpgsql;
I want to create function c that including function a and b, returns:
year: year_ parameter of function_b
median: vbl of function_a divided vbl_b of function_b
How can create one function from two functions in PostgreSQL?
If I understood correctly below mentioned definition will work for you
CREATE OR REPLACE FUNCTION function_c(year_ INTEGER, col_b INTEGER)
RETURNS table (year1 integer, median numeric) AS
$$
DECLARE
var1 INTEGER;
var2 INTEGER;
var3 numeric;
BEGIN
var1=(select * from function_a(year_));
var2=(select * from function_b(year_, vol_b));
var3=var1/var2;
return query
select year_, var3;
END;
$$
LANGUAGE plpgsql;

PostgreSQL calling another function

I have a PostgreSQL function with the following signature..
CREATE OR REPLACE FUNCTION GetResults (Weight numeric, documents boolean, country_code text)
RETURNS TABLE (
itemcode varchar(20)
,kilocode varchar(20)
) AS $$
DECLARE
.....
END; $$
LANGUAGE 'plpgsql'
Now I need to create another function and use the results from the above function (itemcode, kilocode). This is how I tried to use the function above
CREATE OR REPLACE FUNCTION GetProductList(
country_code text,
maxdim numeric,
weight numeric,
volume numeric,
promo_code character varying,
documents boolean)
RETURNS TABLE(code varchar (20)
, cms_code varchar (20)
, service_code varchar (20)
, service_name varchar (10)
, service_group varchar (1000)
, signature_required boolean
, service_promo_code varchar (10))
LANGUAGE 'plpgsql'
AS $$
DECLARE
var_pg_rec record;
sqlToExecute varchar(1000);
val_true boolean := 't';
BEGIN
.....
FOR var_pg_rec IN EXECUTE sqlToExecute USING Weight, country_code, val_true
LOOP
Select itemcode , kilocode from GetResults (Weight, documents, country_code);
code := var_pg_rec.ProductGroupName || itemcode;
cms_code := var_pg_rec.ProductGroupName || kilocode;
service_code := 'DummyService';
service_name := 'Dummy Service 10.0kg';
service_group := 'Dummy Service 10.0kg';
signature_required := false;
service_promo_code := 'dummyPro';
RETURN NEXT;
END LOOP;
END $$;
when I do the above, I get the following error
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
Thanks
Your statement "Select itemcode , kilocode ..." does not do anything with the selected values. So Postgres doesn't know at that point that you intend on using them later. To so tell Postgres this fact you need to provide a location to save the results. You accomplish this via the looping through the result set of the GetResults function (functions that return tables can return multiple rows) or changing to "Select ... into ..." format in the function. Below is a demonstration of each. Since you didn't post your full routine I used some local routines I have to generate random data elements; those routines all begin SOQ. I've further commented where I changed your routines;
Data Generation Routines:
create or replace function soq.random_int(
l integer,
h integer)
returns integer
language sql strict
as $$
select floor(random() * (h-l+1) + l)::integer;
$$;
create or replace function soq.random_keyboard_char()
returns text
language sql
as $$
with keyboard_char as
( select array[ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_', '$', '#'
, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'] ca
)
select ca[soq.random_int(1,array_length(ca,1))]::text from keyboard_char;
$$;
create or replace function soq.random_text(max_length_in integer)
returns text
language sql strict
as $$
with recursive t(n,cl) as
( select 1, soq.random_keyboard_char()
union all
select t.n+1, concat(t.cl,soq.random_keyboard_char())
from t
where n<=max_length_in
)
select cl from t where n = max_length_in;
$$;
With that out of the way, now your routines. First assuming GetResuts returns multiple rows.
CREATE OR REPLACE FUNCTION GetResults (Weight numeric, documents boolean, country_code text)
RETURNS TABLE (
itemcode varchar(20)
,kilocode varchar(20)
)
AS $$
declare
lcnt integer :=1;
begin
if documents
then
lcnt = weight::integer;
end if;
while lcnt > 0
loop
select soq.random_text(20),soq.random_text(20) into itemcode,kilocode;
return next;
end loop;
return;
END; $$
LANGUAGE 'plpgsql' ;
CREATE OR REPLACE FUNCTION GetProductList(
country_code text,
maxdim numeric,
weight numeric,
volume numeric,
promo_code character varying,
documents boolean)
RETURNS TABLE(code varchar (20)
, cms_code varchar (20)
, service_code varchar (20)
, service_name varchar (10)
, service_group varchar (1000)
, signature_required boolean
, service_promo_code varchar (10))
LANGUAGE 'plpgsql'
AS $$
DECLARE
var_pg_rec record;
sqlToExecute varchar(1000);
val_true boolean := 't';
results record; -- added
BEGIN
sqlToExecute = $stmt$ -- added
with gl as (select * from generate_series (1, coalesce($1::integer,1) )) -- added
Select 'Group_' || row_number() over() || '::' as ProductGroupName -- added
, $2 -- added
, $3 -- added
from gl ; -- added
$stmt$; -- added
--..... -- commented
FOR var_pg_rec IN EXECUTE sqlToExecute USING Weight, country_code, val_true
LOOP
for results in ( select * from GetResults (Weight, documents, country_code) ) -- changed
loop -- added
code := var_pg_rec.ProductGroupName || results.itemcode; -- changed
cms_code := var_pg_rec.ProductGroupName || results.kilocode; -- changed
service_name := 'Dummy Service 10.0kg';
service_group := 'Dummy Service 10.0kg';
signature_required := false;
service_promo_code := 'dummyPro';
RETURN NEXT;
end loop; -- added
END LOOP;
return ; -- added
end;
$$ ;
Finally, the alternative for GetProductList function if you know the GetProducts function always returns at lost 1 row.
CREATE OR REPLACE FUNCTION GetProductList(
country_code text,
maxdim numeric,
weight numeric,
volume numeric,
promo_code character varying,
documents boolean)
RETURNS TABLE(code varchar (20)
, cms_code varchar (20)
, service_code varchar (20)
, service_name varchar (10)
, service_group varchar (1000)
, signature_required boolean
, service_promo_code varchar (10))
LANGUAGE 'plpgsql'
AS $$
DECLARE
var_pg_rec record;
sqlToExecute varchar(1000);
val_true boolean := 't';
results record;
l_itemcode text;
l_kilocode text; -- added
BEGIN
sqlToExecute = $stmt$ -- added
with gl as (select * from generate_series (1, coalesce($1::integer,1) )) -- added
Select 'Group_' || row_number() over() || '::' as ProductGroupName -- added
, $2 -- added
, $3 -- added
from gl ; -- added
$stmt$; -- added
--..... -- commented
FOR var_pg_rec IN EXECUTE sqlToExecute USING Weight, country_code, val_true
LOOP
select itemcode,kilocode -- changed
into l_itemcode, l_kilocode -- changed
from GetResults (Weight, documents, country_code); -- changed
code := var_pg_rec.ProductGroupName || l_itemcode; -- changed
cms_code := var_pg_rec.ProductGroupName || l_kilocode; -- changed
service_name := 'Dummy Service 10.0kg';
service_group := 'Dummy Service 10.0kg';
signature_required := false;
service_promo_code := 'dummyPro';
RETURN NEXT; -- added
END LOOP;
return ; -- added
end;
$$ ;
Take note of how each version handles the data returned from GetResults function.

Pass a Table Column Name in a PostgreSQL Function

I've read some posts about using table column names in a PostgreSQL function but I couldn't make it work for me.
I have this simple function
DROP FUNCTION IF EXISTS public.benchmark(CHARACTER VARYING, CHARACTER VARYING, BIGINT, BIGINT, BIGINT);
CREATE OR REPLACE FUNCTION benchmark(params CHARACTER VARYING, colName CHARACTER VARYING, idFrom BIGINT, idTo BIGINT, testNumber BIGINT) RETURNS SETOF RECORD AS $$
DECLARE
elemArray TEXT[] := ARRAY(SELECT colName FROM public.test WHERE test.id BETWEEN idFrom AND idTo);
selectedElem RECORD;
elem TEXT;
BEGIN
FOREACH elem IN ARRAY elemArray LOOP
raise notice 'elem Value: %', elem;
SELECT elem INTO selectedElem;
RETURN NEXT selectedElem;
END LOOP;
END;
$$ LANGUAGE plpgsql;
When I execute it with
SELECT * FROM public.benchmark('ad','name',1,2,1) AS x(Item TEXT);
I get
and what I should be getting are the name column values between idFrom and idTo. How can I use the colName variable as an actual column name in elemArray TEXT[] := ARRAY(SELECT colName FROM public.test WHERE test.id BETWEEN idFrom AND idTo);
You may use RETURNS TABLE + RETURN QUERY EXECUTE for dynamic columns.
CREATE OR REPLACE FUNCTION benchmark(params CHARACTER VARYING, colName
CHARACTER VARYING, idFrom BIGINT, idTo BIGINT, testNumber BIGINT)
RETURNS TABLE (colvalue TEXT) AS
$$
BEGIN
RETURN QUERY EXECUTE --dynamic query
format('SELECT %I::TEXT FROM test WHERE test.id BETWEEN $1 AND $2',colName)
--dynamic cols --bind parameters
USING idFrom,idTo;
END;
$$ LANGUAGE plpgsql;
Demo
EDIT
I just want to populate it with the elements of colName and use the
array later in the extended code
You could use ARRAY_AGG & load it into an array variable instead.
CREATE OR REPLACE FUNCTION benchmark(params CHARACTER VARYING, colName
CHARACTER VARYING, idFrom BIGINT, idTo BIGINT, testNumber BIGINT)
RETURNS void AS
$$
DECLARE
elemArray TEXT[];
elem TEXT;
BEGIN
EXECUTE format('SELECT array_agg(%I::TEXT) FROM test
WHERE test.id BETWEEN $1 AND $2',colName)
USING idFrom,idTo INTO elemArray ;
FOREACH elem IN ARRAY elemArray LOOP
raise notice 'elem Value: %', elem;
END LOOP;
END;
$$ LANGUAGE plpgsql;
knayak=# DO $$
knayak$# BEGIN
knayak$# PERFORM benchmark('ad','name',1,2,1);
knayak$# END
knayak$# $$;
NOTICE: elem Value: TalG
NOTICE: elem Value: John Doe
DO

Got syntax error can't understand what is wrong

Need you help please tell me what i am doing wrong in that function ?
According to docs examples all should work .. docs
create or replace function loginValidator(nickname varchar, email varchar, u_password varchar) returns boolean as $$
DECLARE
checked boolean := false; n_regex varchar; e_regex varchar; p_regex varchar;
BEGIN
BEGIN;
select nickname_r into n_regex from regex;
select email_r into e_regex from regex;
select password_r into p_regex from regex;
IF n_regex ~ nickname AND e_regex ~ email AND p_regex ~ u_password THEN checked := true;
COMMIT;
return checked;
END;
$$ language plpgsql;
ERROR: syntax error at or near ";"
LINE 6: BEGIN;
^
SQL state: 42601
Character: 222
You are missing END IF. I would rewrite it as:
create or replace function loginValidator(nickname varchar,
email varchar,
u_password varchar)
returns boolean
as
$$
DECLARE
checked boolean := false; n_regex varchar; e_regex varchar; p_regex varchar;
BEGIN
-- removed BEGIN
select nickname_r, email_r, password_r into n_regex, e_regex, p_regex from regex;
IF n_regex ~ nickname AND e_regex ~ email AND p_regex ~ u_password
THEN checked := true;
END IF; -- added END IF;
-- removed COMMIT;
RETURN checked;
END;
$$ language plpgsql;
or even simpler:
create or replace function loginValidator(nickname varchar,
email varchar,
u_password varchar)
returns boolean
as
$$
BEGIN
RETURN (select COUNT(*)
from regex
WHERE nickname_r ~ nickname
AND email_r ~ email
AND password_r ~ u_password)::boolean;
END;
$$ language plpgsql;
I hope that you are not stroring passwords as clear text.

PostgreSQL 9.5: Return columns based on input parameter

I have the following table.
Table:
CREATE TABLE tblTest
(
Column1 int,
Column2 int,
Column3 int,
Column11 int,
Column111 int,
Column1111 int,
Column22 int,
Column222 int,
Column33 int
);
Records:
INSERT INTO tblTest VALUES(1,2,3,11,111,1111,22,222,33);
I am writting FUNCTION to return the result from above table based on passed parameter.
The parameter p_ColumnName is used to pass the column name. Based on column name the list of columns needs to be display.
Function:
CREATE OR REPLACE FUNCTION ufn_test
(
p_ColumnName text
)
RETURNS -- ? How to specify the dynamic return type or dynamic column list ?
AS
$BODY$
DECLARE v_ColumnsList text;
v_query text;
BEGIN
IF p_ColumnName = 'Column1'
THEN
v_ColumnsList := 'Column11,Column111,Column1111';
ELSIF p_ColumnName = 'Column2'
THEN
v_ColumnsList := 'Column22,Column222';
ELSIF p_ColumnName = 'Column3'
THEN
v_ColumnsList := 'Column33';
END IF;
v_query := 'SELECT '|| v_ColumnsList ||' FROM tblTest';
RETURN QUERY EXECUTE v_query;
END;
$BODY$
LANGUAGE PLPGSQL;
I suggest you are using a type RefCursor in Returns.
Base on the language you are using (SQL, Java ...), you can get data from this Cursor.
So, your function will become to
CREATE OR REPLACE FUNCTION ufn_test( p_ColumnName text )
RETURNS refcursor AS
$BODY$
DECLARE
v_ColumnsList text;
v_query text;
ref_cursor refcursor;
BEGIN
IF (p_ColumnName = 'Column1') THEN
v_ColumnsList := 'Column11,Column111,Column1111';
ELSIF (p_ColumnName = 'Column2') THEN
v_ColumnsList := 'Column22,Column222';
ELSIF (p_ColumnName = 'Column3') THEN
v_ColumnsList := 'Column33';
END IF;
v_query := 'SELECT '|| v_ColumnsList ||' FROM tblTest';
OPEN ref_cursor FOR EXECUTE (v_query);
RETURN ref_cursor;
END;
$BODY$
LANGUAGE plpgsql;
Hopefully it will help you.