Having RETURN issue in PLPGSQL - plpgsql

I have created this store procedure to return a row_version if the organization id is in the database.
CREATE OR REPLACE FUNCTION sote.validate_row_version(a BIGINT, b BIGINT)
RETURNS text AS $$
DECLARE
ret RECORD;
v_error_stack text;
BEGIN
SELECT row_version INTO ret
FROM sote.organizations
WHERE organization_id = a;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE EXCEPTION 'Organization Id (%) was not found', myOrganizationID;
GET STACKED DIAGNOSTICS v_error_stack = PG_EXCEPTION_CONTEXT;
RETURN to_json(v_error_stack);
RETURN '0';
END;$$ LANGUAGE plpgsql;
When I run this command,
SELECT sote.validate_row_version(2,4);
I get the following error. I don't understand why. Any help would be great.
ERROR: control reached end of function without RETURN

There are 2 problems:
your select statement will not raise an exception unless you specify SELECT ... INTO STRICT
your RETURN '0' statement is part of the exception handler and will never be executed.
So you can rewrite your function as
CREATE OR REPLACE FUNCTION sote.validate_row_version(a BIGINT, b BIGINT)
RETURNS text AS $$
DECLARE
ret RECORD;
v_error_stack text;
BEGIN
SELECT row_version INTO STRICT ret
FROM sote.organizations
WHERE organization_id = a;
RETURN '0';
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE EXCEPTION 'Organization Id (%) was not found', myOrganizationID;
GET STACKED DIAGNOSTICS v_error_stack = PG_EXCEPTION_CONTEXT;
RETURN to_json(v_error_stack);
END;$$ LANGUAGE plpgsql;

You need to move the RETURN '0' so it's executed before the EXCEPTION handler:
CREATE OR REPLACE FUNCTION sote.validate_row_version(a BIGINT, b BIGINT)
RETURNS text AS $$
DECLARE
ret RECORD;
v_error_stack text;
BEGIN
SELECT row_version INTO ret
FROM sote.organizations
WHERE organization_id = a;
RETURN '0';
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE EXCEPTION 'Organization Id (%) was not found', myOrganizationID;
GET STACKED DIAGNOSTICS v_error_stack = PG_EXCEPTION_CONTEXT;
RETURN to_json(v_error_stack);
END;$$ LANGUAGE plpgsql;
The code below EXCEPTION is all part of the exception block and won't be executed unless an exception occurs.
Best of luck.

Related

When exception raised rollingback the insert

I am calling the function
SELECT sbc_schemaA.func1(customernum => '5566789',custlocID => 25)
Whenever there is an exception raised in sbc_schemaD.func5 the insert statemet of sbc_schemaC.func4 is rolledback.
Even exception is raised it should not rollback the into custgartbl.
Please help me.
The code of the function sbc_schemaA.func1 as below.
It is calling another function "sbc_schemaB.func2" inside it.
create or replace function sbc_schemaA.func1(
customernum in VARCHAR default null,
custlocID in NUMERIC default null
) RETURNS void AS
$$
declare
ajsid numeric;
begin
select * from sbc_schemaB.func2(customernum, custlocID) into strict ajsid;
exception
when raise_exception then
RAISE info '%','ErrMsg: ' || substr(sqlerrm,1,instr(sqlerrm,e'\n',1,1)) ;
end $$ language plpgsql;
The code of the function sbc_schemaB.func2 as below.
It is calling another function "sbc_schemaC.func3" inside it.
create or replace function sbc_schemaB.func2(
customernumber IN VARCHAR,
custlocID IN NUMERIC,
brsid out NUMERIC) AS
$$
declare
v_ermsg text;
v_cnx text;
begin
select * from sbc_schemaC.func3(customernumber => customernumber,custlocID => custlocID) into strict brsid;
exception
when raise_exception then
GET STACKED DIAGNOSTICS v_ermsg = MESSAGE_TEXT,
v_cnx = PG_EXCEPTION_CONTEXT;
v_ermsg := set_context(v_ermsg::text, v_cnx::Text);
raise exception e'%', v_ermsg;
end $$ language plpgsql;
The code of the function sbc_schemaC.func3 as below.
It is calling another function "sbc_schemaC.func4" and "sbc_schemaD.func5" inside it.
create or replace function sbc_schemaC.func3(customernumber IN VARCHAR,
custlocID IN NUMERIC,
ajsid out NUMERIC,
brsid out NUMERIC) AS
$$
declare
v_ajsid numeric;
btpID numeric:=0;
select * from sbc_schemaC.func4(p_customernum => customernumber,
p_custlocID => custlocID
) into v_ajsid;
ajsid := v_ajsid;
if v_ajsid = 0 then
begin
select * from sbc_schemaD.func5(customernumber,btpID);
exception
when raise_exception then
GET STACKED DIAGNOSTICS v_ermsg = MESSAGE_TEXT,
v_cnx = PG_EXCEPTION_CONTEXT;
v_ermsg := set_context(v_ermsg::text, v_cnx::Text);
raise exception e'%', v_ermsg;
end;
end if;
end $$ language plpgsql;
The code of the function sbc_schemaC.func4 as below.
CREATE OR REPLACE FUNCTION sbc_schemaC.func4(
p_customernum VARCHAR,
p_custlocID numeric,
p_ajsid OUT numeric
) AS
$$
declare
v_cnx text;
v_ermsg text;
begin
insert into custgartbl(account_num,custlocID) values(p_customernum,p_custlocID);
exception
when raise_exception then
GET STACKED DIAGNOSTICS v_ermsg = MESSAGE_TEXT,
v_cnx = PG_EXCEPTION_CONTEXT;
v_ermsg := set_context(v_ermsg::text, v_cnx::Text);
raise exception e'%', v_ermsg;
end $$ language plpgsql;
The code of the function sbc_schemaD.func5 as below.
CREATE OR REPLACE FUNCTION sbc_schemaD.func5 (customernumber text, btpID numeric) AS $body$
DECLARE
errmsg text;
v_ermsg text;
v_cnx text;
BEGIN
if btpID = 0 then
errmsg := cermssg('error')
raise exception e'%',errmsg;
end if;
exception
when raise_exception then
GET STACKED DIAGNOSTICS
v_ermsg = MESSAGE_TEXT,
v_cnx = PG_EXCEPTION_CONTEXT;
v_ermsg := v_ermsg||E'\n'||v_cnx;
raise exception e'%', v_ermsg;
END;
$body$
LANGUAGE PLPGSQL
Thanks in advance.
The code you post for func3 is buggy; it is missing the initial BEGIN.
Anyway, when simplified, your code looks like this:
SELECT * FROM func4(...) INTO ...;
IF v_ajsid = 0 THEN
BEGIN
SELECT * FROM func5(...);
EXCEPTION
WHEN raise_exception THEN
RAISE EXCEPTION ...;
END;
END IF;
Since you raise an exception in the exception handler, the whole (sub?)transaction in which func3 is executed gets rolled back, so the effects of the call to func4 are undone.
You have to something other than raising an exception in the exception handler if you want to preserve the effects of the call to func4.

function not returning any value and not raising exception

The below function neither returning the value nor raising the exception.
create or replace function get_custid(p_customerNum varchar2)
RETURNS text AS $$
DECLARE
cust_id customer.customer_num%TYPE;
begin
raise notice '%', message_text;
select customer_num into cust_id
from customer
where customer_num = p_customerNum;
return cust_id;
exception
when OTHERS then
raise notice '%', message_text;
raise;
end $$ language plpgsql;
select get_custid('Ab12345') from dual;
-- the customer number is existed but not returning any rows.
select get_custid('DDDDDDD') from dual;
-- the customer number is not existed but not going to exception block
I think that is you really use postgresql this code is more likely what you need (or at list it's running...)
create table customer (cust_id int, customer_num varchar);
insert into customer values (1, 'Ab12345');
drop function get_custid(varchar);
create or replace function get_custid(p_customerNum varchar)
RETURNS int AS $$
DECLARE
out_cust_id int;
begin
--raise notice '%', message_text;
select cust_id into out_cust_id
from customer
where customer_num = p_customerNum;
if out_cust_id is null
then raise exception 'your exception';
end if;
return out_cust_id;
end $$ language plpgsql;
select get_custid('Ab12345');
In PL/pgSQL, SELECT INTO only throws an exception on the wrong number of rows if STRICT is specified.
create or replace function get_custid(p_customerNum varchar)
RETURNS text AS $$
DECLARE
cust_id customer.customer_num%TYPE;
begin
raise notice '%', 'message_text';
select customer_num into strict cust_id
from customer
where customer_num = p_customerNum;
return cust_id;
exception
when OTHERS then
raise notice '%', 'message_text';
raise;
end $$ language plpgsql;

postgreSQL : oracle sqlerrm equivalent of postgres

I'm new to PostgreSQL. I have experience in oracle. In oracle , to find the exact error, I use code 'dbms_output.put_line(sqlerrm)' . Here I have a postgresql function returning an integer value
CREATE OR REPLACE FUNCTION public.fn_sqltest(
p_id integer)
RETURNS integer
LANGUAGE 'plpgsql'
COST 100
VOLATILE
AS $BODY$
Declare
n integer;
begin
select off_id into n from office
where per_id=p_id;
return n ;
exception when others then
return -1;
end;
$BODY$;
ALTER FUNCTION public.fn_sqltest(character varying)
OWNER TO postgres;
I call this function as below
DO $$
DECLARE
ae integer;
BEGIN
ae:=fn_sqltest(10);
RAISE NOTICE 'exception: % % ', sqlstate , sqlerrm ;
RAISE NOTICE 'Return value is: % ', ae;
END $$;
and I get the error
ERROR: column "sqlstate" does not exist
How can I show the exact error message like sqlerrm in oracle.
I updated the function and wrote a code in function exception section
CREATE OR REPLACE FUNCTION public.fn_sqltest(
p_id integer)
RETURNS integer
LANGUAGE 'plpgsql'
COST 100
VOLATILE
AS $BODY$
Declare
n integer;
text_var1 text;
text_var2 text;
text_var3 text;
begin
select off_id into n from office
where per_id=p_id;
return n ;
exception when others then
GET STACKED DIAGNOSTICS text_var1 = MESSAGE_TEXT,
text_var2 = PG_EXCEPTION_DETAIL,
text_var3 = PG_EXCEPTION_HINT;
RAISE NOTICE 'Return value is: % % %',text_var1 , text_var2, text_var3;
return -1;
end;
$BODY$;
ALTER FUNCTION public.fn_sqltest(character varying)
OWNER TO postgres;
Another method is by changing the return type. I changed the return type to text and rewrite the exception section code as below
return sqlerrm;
Unlike PL/SQL, in PL/pgSQL SQLSTATE and SQLERRM are not defined outside an exception handler. See the documentation, section "Obtaining Information About An Error".
This also means that you can't get SQLSTATE and SQLERRM of a successful operation, unlike PL/SQL.
So, if you want to use these special variables outside an exception handler, you have to store them in variables.
I don't know how would you like to return them from the function. I can demonstrate this idea inside your function code:
CREATE OR REPLACE FUNCTION public.fn_sqltest(
p_id integer)
RETURNS integer
LANGUAGE 'plpgsql'
COST 100
VOLATILE
AS $BODY$
Declare
n integer;
v_sqlerrm text;
v_sqlstate text;
begin
begin
select off_id into n from office
where per_id=p_id;
return n ;
exception when others then
v_sqlerrm := sqlerrm;
v_sqlstate := sqlstate;
end;
RAISE NOTICE 'exception: % % ', v_sqlstate , v_sqlerrm ;
return -1;
end;
$BODY$;
I know this is old but just for the record: The easiest way to output the state and message of error is to raise them from within the exception clause. You don't need to declare extra variables.
CREATE OR REPLACE FUNCTION fn_sqltest(p_id integer)
RETURNS integer
AS $$
DECLARE
n integer;
BEGIN
SELECT off_id
INTO n
FROM office
WHERE per_id = p_id;
RETURN n;
EXCEPTION WHEN OTHERS THEN
RAISE NOTICE 'exception: % - %', SQLSTATE, SQLERRM;
RETURN -1;
END;
$$ LANGUAGE plpgsql;

Invalid for loop in a function

I am trying to make the following function working:
CREATE OR REPLACE FUNCTION validate_count(devices TEXT[], campaign_id INTEGER) RETURNS void AS $$
DECLARE
devices_array TEXT[] := devices;
devices_count INTEGER := array_length(devices, 1);
row_id INTEGER := campaign_id;
BEGIN
FOR device IN unnest(devices_array) LOOP
IF my_count('my_table', device, row_id) != 1;
RAISE EXCEPTION 'invalid_count %', row_id
ENDIF
END LOOP;
END
$$ LANGUAGE plpgsql;
my_count is a working function which returns INTEGER.
The definition fails with the error:
ERROR: syntax error at or near "unnest"
LINE 7: FOR device IN unnest(devices_array) LOOP
Could you spot the issue? Thanks!
I am planning to call the function as follows:
select validate_count('{foo, bar}', 1)
Use a FOREACH loop:
CREATE OR REPLACE FUNCTION validate_count(devices TEXT[], campaign_id INTEGER)
RETURNS void
AS
$$
DECLARE
device text;
devices_count INTEGER := array_length(devices, 1);
BEGIN
FOREACH device IN ARRAY devices LOOP
IF my_count('my_table', device, campaign_id) <> 1 then
RAISE EXCEPTION 'invalid_count %', campaign_id;
END IF;
END LOOP;
END
$$
LANGUAGE plpgsql;
A few things need to be fixed to make this valid.
The FOR loop needs SELECT before the unnest, i.e.:
FOR device IN SELECT unnest(devices_array) LOOP
You need to declare device up top, for example:
DECLARE device RECORD;
The IF statement needs a THEN instead of a ;, i.e.:
IF my_count('my_table', device, row_id) != 1 THEN
The RAISE EXCEPTION statement needs a semicolon at the end of the line, i.e.:
RAISE EXCEPTION 'invalid_count %', row_id;
The END for the IF statement should be END IF;.
The END for the LOOP statement should be END LOOP;.
Here's the net result:
CREATE OR REPLACE FUNCTION validate_count(devices TEXT[], campaign_id INTEGER) RETURNS void AS $$
DECLARE
devices_array TEXT[] := devices;
devices_count INTEGER := array_length(devices, 1);
row_id INTEGER := campaign_id;
device RECORD;
BEGIN
FOR device IN SELECT unnest(devices_array) LOOP
IF my_count('my_table', device, row_id) != 1 THEN
RAISE EXCEPTION 'invalid_count %', row_id;
END IF;
END LOOP;
END
$$ LANGUAGE plpgsql;

PostgreSQL 9.3: isnumeric() in a condition

I need to check whether the given text is numeric or not from the
function.
Creating function for isnumeric():
CREATE OR REPLACE FUNCTION isnumeric(text) RETURNS BOOLEAN AS $$
DECLARE x NUMERIC;
BEGIN
x = $1::NUMERIC;
RETURN TRUE;
EXCEPTION WHEN others THEN
RETURN FALSE;
END;
$$ LANGUAGE plpgsql IMMUTABLE;
Function from which I am calling the isnumeric() function:
create or replace function tm(var text)
returns varchar as
$$
begin
if (select isnumeric(var))=t::BOOLEAN then
raise info 'Is numeric value';
else
raise info 'Not numeric';
end if;
end;
$$
language plpgsql;
Calling functon:
select tm('1');
Getting an error:
Here is the error details:
ERROR: column "t" does not exist
LINE 1: SELECT (select isnumeric(var))=t::BOOLEAN
You don't need a select (and it's actually wrong, as the error indicates) - just call isnumeric directly.
Also, by the way, your function is missing a return statement.
To sum it all up:
create or replace function tm(var text)
returns varchar as
$$
begin
if (isnumeric(var)) then -- call isnumeric directly
raise info 'Is numeric value';
else
raise info 'Not numeric';
end if;
return '0'; -- missing return value in the OP
end;
$$
language plpgsql;
this will help you to identify your field is numeric or not:
select * from Table where field_name ~ '^[0-9]*$'
for decimal values you can use^[0-9.]*$ instead ^[0-9]*$
select getDataType('2021'); == Number
select getDataType('2021-05-12 23:12:10'); == Date
select getDataType('2021-05-12'); == Date
select getDataType('2X'); == String
CREATE
OR REPLACE FUNCTION getDataType ( TEXT ) RETURNS TEXT AS $$ DECLARE
x VARCHAR;
BEGIN
x = $1 :: NUMERIC;
RETURN 'Number';
EXCEPTION
WHEN OTHERS THEN
BEGIN
x = $1 :: DATE;
RETURN 'Date';
EXCEPTION
WHEN OTHERS THEN
RETURN 'String';
END;
END;
$$ STRICT LANGUAGE plpgsql IMMUTABLE;