PostgreSQL 9.5: Error handling exceptions - postgresql

I have developed an Oracle procedure to handle execeptions during a script execution, however, I am trying to figure out how do do the same for PostgreSQL and I can't find any examples, also I tried using IF NOT EXISTS in the creation syntax, but it seems the version of postgresql im using does not recognize it.
Oracle Procedure
CREATE OR REPLACE PROCEDURE exec_neo_command (str IN VARCHAR2)
IS
col_already_exists EXCEPTION;
object_already_exists EXCEPTION;
columns_indexed EXCEPTION;
col_alreadyNull EXCEPTION;
col_alreadyNull2 EXCEPTION;
PRAGMA EXCEPTION_INIT(object_already_exists, -955);
PRAGMA EXCEPTION_INIT(col_already_exists, -01430);
PRAGMA EXCEPTION_INIT(columns_indexed, -1408);
PRAGMA EXCEPTION_INIT(col_alreadyNull, -01442);
PRAGMA EXCEPTION_INIT(col_alreadyNull2, -1451);
BEGIN
-- dbms_output.put_line (str || ' = command in exec_neo_command'); -- FOR DEBUGGING ONLY
EXECUTE IMMEDIATE str;
EXCEPTION
WHEN col_already_exists OR object_already_exists OR columns_indexed THEN
dbms_output.put_line(str || ': Already exists, skipping...');
WHEN col_alreadyNull OR col_alreadyNull2 THEN
dbms_output.put_line(str || ': Already NULL, skipping...');
END exec_neo_command;
and I execute it as following
BEGIN
exec_neo_command ('ALTER TABLE XtkEnumValue ADD iOrder NUMBER(20) DEFAULT 0');
exec_neo_command ('ALTER TABLE XtkReport ADD iDisabled NUMBER(3) DEFAULT 0');
exec_neo_command ('ALTER TABLE XtkWorkflow ADD iDisabled NUMBER(3) DEFAULT 0');
END;
/
However, I need to handle these following exeptions in the same way for postgreSQL
Erreur PostgreSQL : ERROR: column "idisabled" of relation "xtkworkflow" already exists\n.
Erreur PostgreSQL : ERROR: column "imaxpersotime" of relation "nmsdelivery" already exists\n
The error trapping doc seems to point out a way (https://www.postgresql.org/docs/current/plpgsql-control-structures.html#PLPGSQL-ERROR-TRAPPING) but I don't see any codes in the error to create the handles? can someone provide an example?
43.6.8. Trapping Errors
By default, any error occurring in a PL/pgSQL function aborts
execution of the function and the surrounding transaction. You can
trap errors and recover from them by using a BEGIN block with an
EXCEPTION clause. The syntax is an extension of the normal syntax for
a BEGIN block:
[ <<label>> ]
[ DECLARE
declarations ]
BEGIN
statements
EXCEPTION
WHEN condition [ OR condition ... ] THEN
handler_statements
[ WHEN condition [ OR condition ... ] THEN
handler_statements
... ]
END;

Related

How do I overcome type 'exception' does not exist error?

I have function in oracle db and trying to migrate to postgresql.I have not added the full function as rest of the lines throw no errors.
Here's what i have got so far, but i am getting an error
CREATE OR REPLACE FUNCTION int_nightly_jobs_pkg_profs_load () RETURNS varchar AS $body$
DECLARE
load_failed exception;
intTempCount bigint;
strMessage varchar(1000);
intProfsCount bigint;
BEGIN
select count(*)into STRICT intTempCount from profs_temp;
if (intTempCount< 300000) then
strMessage := 'PROFS Load Failed. The Temp Profs table only has '||intTempCount||' records.';
raise load_failed;
end if
This is the error I am getting
Error : type ''exception'' does not exist
Line 3 : load_failed exception;

Postgres function with nested IF & ELSE statement error

I have this function where it looks up all the users and if any one does not exists do not create the function. What is wrong?
RETURNS event_trigger AS $$
DECLARE
audit_query TEXT;
r RECORD;
BEGIN
IF tg_tag IN ('CREATE TABLE', 'CREATE TABLE AS')
THEN
IF EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = 'test' or rolname = 'testa')
THEN
FOR r IN
SELECT * FROM pg_event_trigger_ddl_commands() AS ddl WHERE ddl.schema_name IN ('testb','testc','testd')
LOOP
INSERT INTO user_monitor.ddl_history (ddl_date, ddl_tag, object_name) VALUES (statement_timestamp(), tg_tag, r.object_identity);
END LOOP;
else
RAISE EXCEPTION 'Not all users found';
END IF;
END;
$$ LANGUAGE plpgsql
SQL Error [42601]: ERROR: syntax error at or near ";"
SQL Error [42601]: ERROR: missing "THEN" at end of SQL expression
Position: 286
An excerpt from the manual:
Event triggers are created using the command CREATE EVENT TRIGGER. In order to create an event trigger, you must first create a function with the special return type event_trigger.
This function need not (and may not) return a value; the return type serves merely as a signal that the function is to be invoked as an event trigger.
Emphasis on the second sentence.
You should probably replace your RETURN with RAISE EXCEPTION instead.
https://www.postgresql.org/docs/current/event-trigger-table-rewrite-example.html

Getting Error "query has no destination for result data"

I trying to select query based on condition using IF ElSE in postgres. Below is my query.
DO
$do$
DECLARE res varchar(50) := 'a';
BEGIN
IF (res = 'a') THEN
SELECT "Name" FROM "TestTable";
ELSE
SELECT "ID" FROM "TestTable";
END IF;
END
$do$
but I am getting following error
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 function inline_code_block line 5 at SQL statement
What I am doing wrong here??
DO purpose is to execute anonymous code block and it doesn't return anything (it returns void, to be specific).
You can execute your SELECT statement afterwards (outside of DO block), or perform an INSERT to temporary table which you need to create beforehand (and this can be done within the block).

Firebird run execute statement inside trigger

I wrote a procedure STRING_SESTAVLJEN_ENAKOST_TABEL('MERILA_STRANKE') that generates part of code that i want to execute (some long if statement)
IF ((new.LOKACIJA IS DISTINCT FROM old.LOKACIJA )
OR (new.MODIFIED IS DISTINCT FROM old.MODIFIED )
OR (new.KARAKTERISTIKE IS DISTINCT FROM old.KARAKTERISTIKE )
OR (new.LETNIK IS DISTINCT FROM old.LETNIK )
OR (new.ID_PNS_CERT_POS IS DISTINCT FROM old.ID_PNS_CERT_POS )
OR (new.ID_PNS_CERT_POS IS DISTINCT FROM old.ID_PNS_CERT_POS ))
and I want to call it in trigger, then add some code and run it all together.
The code is:
SET TERM ^ ;
ALTER TRIGGER BI_MERILA_STRANKE ACTIVE
BEFORE INSERT OR UPDATE POSITION 0
AS
declare variable besedilo_primerjave varchar(5000);
BEGIN
begin
if (new.ID_MERILA_STRANKE is null OR new.ID_MERILA_STRANKE = 0) then new.ID_MERILA_STRANKE = gen_id(GEN_ID_MERILA_STRANKE,1);
end
begin
execute procedure STRING_SESTAVLJEN_ENAKOST_TABEL('MERILA_STRANKE')
returning_values :besedilo_primerjave;
execute statement besedilo_primerjave || ' THEN BEGIN INSERT INTO SYNC_INFO(TABLE_NAME,ID_COLUMN_NAME,ID_VALUE,DATETIME)
VALUES (
''MERILA_STRANKE'',
''ID_MERILA_STRANKE'',
NEW.ID_MERILA_STRANKE,
CURRENT_TIMESTAMP
);
END ELSE BEGIN
exception ENAK_RECORD;
END';
end
END^
SET TERM ; ^
Now when I run the update and trigger triggers I get this error:
SQL Message : -104 Invalid token
Engine Code : 335544569 Engine Message : Dynamic SQL Error SQL
error code = -104 Token unknown - line 1, column 1 IF
On the other hand if I write it like this:
SET TERM ^ ;
ALTER TRIGGER BI_MERILA_STRANKE ACTIVE
BEFORE INSERT OR UPDATE POSITION 0
AS
BEGIN
begin
if (new.ID_MERILA_STRANKE is null OR new.ID_MERILA_STRANKE = 0) then new.ID_MERILA_STRANKE = gen_id(GEN_ID_MERILA_STRANKE,1);
end
begin
IF ((new.LOKACIJA IS DISTINCT FROM old.LOKACIJA )
OR (new.MODIFIED IS DISTINCT FROM old.MODIFIED )
OR (new.KARAKTERISTIKE IS DISTINCT FROM old.KARAKTERISTIKE )
OR (new.LETNIK IS DISTINCT FROM old.LETNIK )
OR (new.ID_PNS_CERT_POS IS DISTINCT FROM old.ID_PNS_CERT_POS )
OR (new.ID_PNS_CERT_POS IS DISTINCT FROM old.ID_PNS_CERT_POS ))
THEN BEGIN
INSERT INTO SYNC_INFO(TABLE_NAME,ID_COLUMN_NAME,ID_VALUE,DATETIME)
VALUES (
'MERILA_STRANKE',
'ID_MERILA_STRANKE',
NEW.ID_MERILA_STRANKE,
CURRENT_TIMESTAMP
);
END ELSE BEGIN
exception ENAK_RECORD;
END
end
END^
SET TERM ; ^
It works as it should. I do not understand why it doesn't run if is more or less the same code.
As I also mentioned in your previous question, execute statement cannot be used to execute snippets of PSQL (procedural SQL) like that, it can only execute normal DSQL (dynamic SQL). And as it doesn't understand PSQL, you get the "token unknown - if" error, because if is not valid in DSQL.
execute statement is equivalent to executing SQL yourself from a query tool or application (it uses the same API), you can't use if there either.
There is a loophole by using an execute block statement, but that still would not allow you to gain access to the NEW (or OLD) trigger context variables unless explicitly passed as parameters, which would negate most of the usefulness of dynamically generated code in this context.
The only real solution is to write the trigger and not do it dynamically, maybe using a code generator (I'm not sure if any exist, otherwise you need to write that yourself).

In FirebirdSql, how to return exception message from procedure

I want to return the error message from a procedure when an exception happens. In SQL Server you would select the Error_Number() and Error_Message(). How would I do it in FirebirdSql
SET TERM ^ ;
CREATE PROCEDURE sprocname
( id int )
RETURNS
( gcode int, errmsg varchar(250) )
AS
BEGIN
gcode = 0;
errmsg = '';
-- do procedure code here
WHEN ANY DO
BEGIN
gcode = gdscode; -- ??
errmsg = ??;
END
SUSPEND;
END^
SET TERM ; ^
Unfortunately, you will need to do that at the client side, as it is currently not possible to obtain this in PSQL. There is a feature request in the Firebird tracker, which has been implemented for Firebird 4, which is expected to be released in 2019.
See Firebird 4 release notes, section System Function RDB$ERROR():
The function RDB$ERROR() takes a PSQL error context as input and
returns the specific context of the active exception. Its scope is
confined to the context of the exception-handling block in PSQL.
Outside the exception handling block, RDB$ERROR always contains
NULL.
The type of the return value depends on the context.
Syntax Rules
RDB$ERROR ( context )
context ::= { GDSCODE | SQLCODE | SQLSTATE | EXCEPTION | MESSAGE }
[..]
Example
BEGIN
...
WHEN ANY DO
EXECUTE PROCEDURE P_LOG_EXCEPTION(RDB$ERROR(MESSAGE));
END
CREATE PROCEDURE ADD_COUNTRY (
ACountryName COUNTRYNAME,
ACurrency VARCHAR(10) )
AS
BEGIN
INSERT INTO country (country,
currency)
VALUES (:ACountryName,
:ACurrency);
WHEN ANY DO
BEGIN
-- write an error in log
IN AUTONOMOUS TRANSACTION DO
INSERT INTO ERROR_LOG (PSQL_MODULE,
GDS_CODE,
SQL_CODE,
SQL_STATE)
VALUES ('ADD_COUNTRY',
GDSCODE,
SQLCODE,
SQLSTATE);
-- Re-throw exception
EXCEPTION;
END
END
http://www.firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25-psql-handleexceptions.html