I have an exception in Oracle PL/SQL that needs to be ported to PostgreSQL pl/pgsql. Below is the oracle variant
EXCEPTION
WHEN OTHERS THEN
NULL;
What is the PL/PGSQL variant?
It's the same syntax. Compare the 2 following executions:
DO
$$
BEGIN
RAISE division_by_zero;
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
$$
DO
And:
DO
$$
BEGIN
RAISE division_by_zero;
END;
$$
ERROR: division_by_zero
CONTEXT: PL/pgSQL function inline_code_block line 3 at RAISE
DO NOT port this as it is. It is a BUG ALWAYS. If you do nothing else at least log the error. If where it occurs is interactive, or data loading, or report generating, or a whole host of others then create some kind of message indicating the process failed. If users are depending on this data and it is not there it is your application that is broken, not the users expectations. I understand your are migrating a system but while doing so you should not carry forward obvious bugs. This is one.
First, avoid trapping "any exception"s like that with no specific behavior at all. Except for very simple functions, consider logging it somewhere, or rewrite the code in a more elegant manner.
I found it here: apparently, you don't need to put anything at all.
You should try the following:
EXCEPTION
WHEN OTHERS THEN
-- Do nothing
Hope it helps.
Related
I am using Postgres 13.5 and I am unsure how to combine commit and error handling in a stored procedure or DO block. I know that if I include the EXCEPTION clause in my block, then I cannot include a commit.
I am new to Postgres. It has also been over 15 years since I have written SQL that was working with transactions. When I was working with transactions I was using Oracle and recall using AUTONOMOUS_TRANSACTION to resolve some of these issues. I am just not sure how to do something like that in Postgres.
Here is a very simplified DO block. As I said above, I know that the Commits will cause the procedure to throw and exception. But, if I remove the EXCEPTION clause, then how will I trap an error if it happens? After reading many things, I still have not found a solution. So, I am not understanding something that will lead me to the solution.
Do
$$
DECLARE
v_Start timestamptz;
v_id integer;
v_message_type varchar(500);
Begin
select current_timestamp into start;
select q.id, q.message_type into (v_id, v_message_type) from message_queue;
call Load_data(v_id, v_message_type);
commit; -- if Load_Data completes successfully, I want to commmit the data
insert into log (id, message_type, Status, start, end)
values (v_id, v_message_type, 'Success', v_start, Currrent_Timestamp);
commit; -- commit the log issert for success
EXCEPTION
WHEN others THEN
insert into log (id, message_type, status, start, end, error_message)
values (v_id, v_message_type, 'Failue', v_start, Currrent_Timestamp, SQLERRM || '', ' ||
SQLSTATE );
commit; -- commit the log insert for failure.
end;
$$
Thanks!
Since this is a pattern that I will have to do tens of times, I want to understand the right way to do this.
Since you cannot use transaction management statements in a subtransaction, you will have to move part of the processing to the client side.
But your sample code doesn't need any transaction management at all! Simply remove all the COMMIT statements, and the procedure will work just as you want it to. Remember that PostgreSQL uses the autocommit mode, so your procedure call from the client will automatically run in its own transaction and commit when it is done.
But perhaps your sample code is simplified, and you would like more complicated processing (looping etc.) in your actual use cases. So let's discuss your options:
One option is to remove the EXCEPTION handler and move only that part to the client side: if the procedure causes an error, roll back and insert a log message. Another, perhaps cleaner, method is to move the whole transaction management to the client side. In that case, you would replace the complete procedure with client code and call load_data directly from client code.
I need to ignore an error when running a procedure, but the error still seems to propagate. Is it possible to just swallow it somehow?
CREATE OR REPLACE PROCEDURE myproc()
AS $$
BEGIN
EXECUTE 'bad_statement;';
EXCEPTION WHEN OTHERS THEN
NULL;
END;
$$ LANGUAGE plpgsql;
Then
call myproc();
will result in:
[2021-01-06 17:08:42] [42601][500310] Amazon Invalid operation: syntax error at or near "bad_statement";
Tested it in postgres and it ignores the error correctly.
Unfortunately I don't think there is a way to do this. Amazon's documentation states
In an Amazon Redshift stored procedure, the only supported handler_statement is RAISE. Any error encountered during the execution automatically ends the entire stored procedure call and rolls back the transaction. This occurs because subtransactions are not supported.
If an error occurs in the exception handling block, it is propagated out and can be caught by an outer exception handling block, if one exists.
stored-procedure-trapping-errors
I'm trying to write a PL/pgSQL function in which I first validate parameters (mostly check whether the supplied ids exist).
When one of this validations fails, I raise an exception stating the reason so the client code can try again.
The problem I'm facing is that, for safety reasons (I can provide more context if needed, but basically I want to leave the app in a non-functional state until specialized intervention), I'd like to write some values to a table before raising the exception and rolling back the changes. It's only some of these changes that I'd like persisted (not rolled back).
I understand transactions cannot be used inside the function because there's no context, and I found that I could probably do what I want to do using dblink (which I just found out).
The thing is it really feels hackish, so I'd like to ask if this is a reasonable idea or not.
Here's some pseudocode to illustrate:
CREATE FUNCTION func(x_id INT) RETURNS INT AS $$
DECLARE
BEGIN
PERFORM * FROM x_table WHERE id = x_id;
IF NOT FOUND THEN
-- write persisting values that will prevent further
-- use, probably using dblink
RAISE EXCEPTION 'Invalid x_id: %', x_id
END IF;
-- function logic
END;
$$ LANGUAGE plpgsql;
I tried looking up for the solution but failed to find one. I understand that postgres rollbacks all the transactions once an exception is raised. But my requirement is to log a certain exception into a database table.
Following is a sample code:
CREATE OR REPLACE FUNCTION fn_test(
in_var numeric DEFAULT NULL::numeric)
RETURNS numeric AS
$BODY$
DECLARE
BEGIN
IF in_var=0
THEN
RAISE EXCEPTION using errcode='INELG';
ELSE IF in_var=1
THEN
RAISE EXCEPTION using errcode='INVAL';
ELSE
RETURN in_var;
END IF;
begin
EXCEPTION
WHEN sqlstate 'INELG'
THEN
INSERT INTO LOG_TBL(in_par,error_reason)
VALUES(in_var,'VALUE INELGIBLE');
RAISE EXCEPTION 'Unable to Process: Parameter Not Eligible';
WHEN sqlstate 'INVAL'
THEN
INSERT INTO LOG_TBL(in_par,error_reason)
VALUES(in_var,'VALUE INValid');
RAISE EXCEPTION 'Unable to Process: Parameter Invalid';
end;
END;
$BODY$
LANGUAGE plpgsql VOLATILE SECURITY DEFINER
COST 100;
The problem I face is as soon as an exception is raised the records in LOG_TBL in the exception section gets rolled back. Is there an alternative solution where I can insert the record into database table as well as raise an application error?
In a simple way - you can't. Once you are inside a function, that's it.
You either have to do this in the client layer or loop back in on another connection to log the error. Traditionally you would loop back in via dblink, but it might be possible to use Foreign Data Wrappers too - I have to admit I'm not sure.
Edit: Nope, it looks like postgres_fdw syncs the transactions between local and remote. Not helpful in this case. Looks like dblink is the obvious choice.
With SQL Server, I can execute code ad hoc T-SQL code with full procedural logic through SQL Server Management Studio, or any other client. I've begun working with PostgreSQL and have run into a bit of a difference in that PGSQL requires any logic to be embedded in a function.
Is there a way to execute PL/PGSQL code without creating an executing a function?
Postgres 9
DO $$
-- declare
BEGIN
/* pl/pgsql here */
END $$;
No, not yet. Version 9.0 (still alpha) will have this option (do), you a have to wait until it's released.
I struggled to get this working because it's fairly strict about adding semi colons in exactly the right places. But once you get used to that it works well. Besides the inability to return records of course, however you can raise notices & exceptions and do the other workarounds like using temp tables as #ErwinBrandstetter pointed out in a comment above.
e.g.:
DO
$$
BEGIN
IF EXISTS(SELECT 'any rows?'
FROM {your_table}
WHERE {your_column} = 'blah')
THEN
RAISE NOTICE 'record exists';
ELSE
RAISE EXCEPTION 'record does not exist';
END IF;
DROP TABLE IF EXISTS foo;
CREATE TEMP TABLE foo AS
SELECT 'bar'::character varying(5) as baz;
END
$$;
SELECT * FROM foo;