PostgreSQL using NO_DATA_FOUND exception in a sored Procedure - postgresql

When I try to run a PostgreSQL stored procedure including the "STRICT" option and a record variable (rec) as I try to save (run the create statement) I get the following error:
ERROR: syntax error at or near "rec"
LINE 14: INTO STRICT rec
I' am following the guidelines stated on the official documentation https://www.postgresql.org/docs/current/plpgsql-statements.html#PLPGSQL-STATEMENTS-SQL-ONEROW
where the only reference missing is that this can or can't be used inside a function or a stored procedure. I guess there should be a reasonable answer to this issue I couldn't found. I was able to succeed running an equivalent code inside a Do block, so it seams there is some incompatibility declaring a record type variable and functions.
This is the function code I was running:
CREATE OR REPLACE FUNCTION test_nodata()
RETURNS TABLE(id int, active boolean)
LANGUAGE plpgsql
AS $$
DECLARE
rec record;
tab VARCHAR ='text_maintenance_news';
BEGIN
RETURN QUERY
SELECT tm.id, tm.active
INTO STRICT rec
FROM text_maintenance_news tm
WHERE tm.active
LIMIT 1;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE 'Query from % has no data', tab;
END;
$$
As mentioned at the official documents, postgreSQL doesn't consider no data as an error but as a warning that the programmer must handle. Main reason to include the STRICT option.
Do block code (It works!) and result:
DO
LANGUAGE plpgsql
$$
DECLARE
rec record;
tab varchar = 'text_maintenance_news';
BEGIN
SELECT *
INTO STRICT rec
FROM text_maintenance_news
WHERE active
LIMIT 1;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE 'Query from % return no data', tab;
END;
$$
ERROR: Query from text_maintenance_news return no data
CONTEXT: PL/pgSQL function inline_code_block line 13 at RAISE
SQL state: P0001

Related

Unit testing plpgsql procedure which has TYPE data

we have a TYPE t_string_array in our postgres database defined as character varying[].
We have a stored procedure which takes list of names and gives the result out in a cursor.
the procedure definition is
create or replace procedure test_lookup( in_speed t_string_array, inout display refcursor,
inout out_error_text TEXT);
I am trying to test this procedure with the block
do
$$
declare
display refcursor;
out_error_text text;
begin
call test_lookup(['Basic']::t_string_array,display::refcursor,out_error_text::text);
RAISE NOTICE 'mymsg1 = %', out_error_text;
RAISE NOTICE 'myvar1 = %', out_error_code;
END;
$$
I am getting error ERROR: syntax error at or near "["
I tried using call test_lookup('basic'::t_string_array,display::refcursor,out_error_text::text)
call test_lookup(array['basic']::character varying[],display::refcursor,out_error_text::text);
all gave me error.
Can you please suggest a way to test this procedure.

How to raise an error if a select query returns rows

I have a view that returns 'bad' rows. I would like a procedure to raise an exception if the view returns any records. I will call this from an external program. How can this be implemented? Pseudo code follows:
create procedure pr_bad_records_check()
language sql
as
$$
if
select count(*) from vw_my_bad_records > 0
then
raise error 'some bad rows were found, run select * from vw_my_bad_records for details'
end if
$$;
I know there is an accepted answer, but let me show you another approach.
You won't need to declare any variables since you can use the special variable FOUND.
Also, would be better to add a LIMIT clause to your select, since one row is enough to throw the exception:
CREATE OR REPLACE FUNCTION pr_bad_records_check() RETURNS void AS $$
BEGIN
PERFORM * FROM vw_my_bad_records LIMIT 1;
IF FOUND THEN
RAISE EXCEPTION 'some bad rows were found, run select * from vw_my_bad_records for details';
END IF;
END;
$$ LANGUAGE plpgsql;
PLpgSQL allows to use SQL queries inside a expressions. So your task can to have a easy, readable and fast solution:
CREATE OR REPLACE FUNCTION pr_bad_records_check()
RETURNS void AS $$
BEGIN
IF EXISTS(SELECT * FROM vw_my_bad_records) THEN
RAISE EXCEPTION
USING MESSAGE='some bad rows were found',
HINT='Run select * from vw_my_bad_records for details.';
END IF;
END;
$$ LANGUAGE plpgsql;

Postgresql function execution error [duplicate]

This question already has an answer here:
PL/PgSQL calling a function inside a loop giving error
(1 answer)
Closed 6 years ago.
I have created two functions. I want to run that functions inside a main function. When I run the main function I get an error. How can I solve this problem?
NOTICE: The rows affected scrap_v15=0
CONTEXT: SQL statement "Select update_scrap_v15()"
PL/pgSQL function all_functions() line 15 at SQL statement
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT: PL/pgSQL all_functions() line 15 at SQL statement
This is my first function
CREATE OR REPLACE FUNCTION update_scrap_v15() RETURNS void AS
$BODY$
DECLARE
scrap_d integer;
BEGIN
update public.scrap_v15_src t2 set scrap = 66 where scrap= 0 and il like '%src%';
GET DIAGNOSTICS scrap_d = ROW_COUNT;
RAISE NOTICE 'The rows affected scrap d=%', scrap_d;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION update_scrap_v15()
OWNER TO postgres;
This is my second function
CREATE OR REPLACE FUNCTION scrap_type() RETURNS void AS
$BODY$
DECLARE
scrap_t integer;
BEGIN
update public.tmz_001 set scrap_type = '55';
update public.tmz_001 set scrap_dtu = '55';
GET DIAGNOSTICS scrap_t = ROW_COUNT;
RAISE NOTICE 'The rows affected scrap d=%', scrap_t;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION scrap_type()
OWNER TO postgres;
This is my main function
CREATE OR REPLACE FUNCTION all_functions() RETURNS void AS
$BODY$
DECLARE
adm_1 integer; adm_2 integer;
BEGIN
Select update_scrap_v15();
GET DIAGNOSTICS adm_1 = ROW_COUNT;
RAISE NOTICE 'affected=%', adm_1;
Select scrap_type();
GET DIAGNOSTICS adm_2 = ROW_COUNT;
RAISE NOTICE 'affected=%', adm_2;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION all_functions()
OWNER TO postgres;
In postgres if function returning void type that invoke by PERFORM.
Instead:
Select update_scrap_v15();
Use that:
PERFORM update_scrap_v15();

How to use EXECUTE FORMAT ... USING in postgres function

CREATE OR REPLACE FUNCTION dummytest_insert_trigger()
RETURNS trigger AS
$BODY$
DECLARE
v_partition_name VARCHAR(32);
BEGIN
IF NEW.datetime IS NOT NULL THEN
v_partition_name := 'dummyTest';
EXECUTE format('INSERT INTO %I VALUES ($1,$2)',v_partition_name)using NEW.id,NEW.datetime;
END IF;
RETURN NULL;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION dummytest_insert_trigger()
OWNER TO postgres;
I'm trying to insert using
insert into dummyTest values(1,'2013-01-01 00:00:00+05:30');
But it's showing error as
ERROR: function format(unknown) does not exist
SQL state: 42883
Hint: No function matches the given name and argument types. You might need to add explicit type casts.
Context: PL/pgSQL function "dummytest_insert_trigger" line 8 at EXECUTE statement
I'm unable get the error.
Your function could look like this in Postgres 9.0 or later:
CREATE OR REPLACE FUNCTION dummytest_insert_trigger()
RETURNS trigger AS
$func$
DECLARE
v_partition_name text := quote_ident('dummyTest'); -- assign at declaration
BEGIN
IF NEW.datetime IS NOT NULL THEN
EXECUTE
'INSERT INTO ' || v_partition_name || ' VALUES ($1,$2)'
USING NEW.id, NEW.datetime;
END IF;
RETURN NULL; -- You sure about this?
END
$func$ LANGUAGE plpgsql;
About RETURN NULL:
To ignore result in BEFORE TRIGGER of PostgreSQL?
I would advice not to use mixed case identifiers. With format( .. %I ..) or quote_ident(), you'd get a table named "dummyTest", which you'll have to double quote for the rest of its existence. Related:
Are PostgreSQL column names case-sensitive?
Use lower case instead:
quote_ident('dummytest')
There is really no point in using dynamic SQL with EXECUTE as long as you have a static table name. But that's probably just the simplified example?
You need explicit cast to text:
EXECUTE format('INSERT INTO %I VALUES ($1,$2)'::text ,v_partition_name) using NEW.id,NEW.datetime;

How to execute PostgreSQL RAISE command dynamically

How to raise error from PostgreSQL SQL statement if some condition is met?
I tried code below but got error.
CREATE OR REPLACE FUNCTION "exec"(text)
RETURNS text AS
$BODY$
BEGIN
EXECUTE $1;
RETURN $1;
END;
$BODY$
LANGUAGE plpgsql VOLATILE;
-- ERROR: syntax error at or near "raise"
-- LINE 1: raise 'test'
SELECT exec('raise ''test'' ') WHERE TRUE
In real application TRUE is replaced by some condition.
Update
I tried to extend answer to pass exception message parameters.
Tried code below but got syntax error.
How to pass message parameters ?
CREATE OR REPLACE FUNCTION exec(text, variadic )
RETURNS void LANGUAGE plpgsql AS
$BODY$
BEGIN
RAISE EXCEPTION $1, $2;
END;
$BODY$;
SELECT exec('Exception Param1=% Param2=%', 'param1', 2 );
You cannot call RAISE dynamically (with EXECUTE) in PL/pgSQL - that only works for SQL statements, and RAISE is a PL/pgSQL command.
Use this simple function instead:
CREATE OR REPLACE FUNCTION f_raise(text)
RETURNS void
LANGUAGE plpgsql AS
$func$
BEGIN
RAISE EXCEPTION '%', $1;
END
$func$;
Call:
SELECT f_raise('My message is empty!');
Related:
Generate an exception with a Context
Additional answer to comment
CREATE OR REPLACE FUNCTION f_raise1(VARIADIC text[])
RETURNS void
LANGUAGE plpgsql AS
$func$
BEGIN
RAISE EXCEPTION 'Reading % % %!', $1[1], $1[2], $1[3];
END
$func$;
Call:
SELECT f_raise1('the','manual','educates');
VARIADIC is not a data type, but an argument mode.
Elements have to be handled like any other array element.
To use multiple variables in a RAISE statement, put multiple % into the message text.
The above example will fail if no $3 is passed. You'd have to assemble a string from the variable number of input elements. Example:
CREATE OR REPLACE FUNCTION f_raise2(VARIADIC _arr text[])
RETURNS void
LANGUAGE plpgsql AS
$func$
DECLARE
_msg text := array_to_string(_arr, ' and '); -- simple string construction
BEGIN
RAISE EXCEPTION 'Reading %!', _msg;
END
$func$;
Call:
SELECT f_raise2('the','manual','educates');
I doubt you need a VARIADIC parameter for this at all. Read the manual here.
Instead, define all parameters, maybe add defaults:
CREATE OR REPLACE FUNCTION f_raise3(_param1 text = ''
, _param2 text = ''
, _param3 text = 'educates')
RETURNS void
LANGUAGE plpgsql AS
$func$
BEGIN
RAISE EXCEPTION 'Reading % % %!', $1, $2, $3;
END
$func$;
Call:
SELECT f_raise3('the','manual','educates');
Or:
SELECT f_raise3(); -- defaults kick in