I have some MariaDb maintenance scripts that have the form:
DELIMITER //
CREATE PROCEDURE SAMPLE_TASK(OUT RESULT BOOLEAN)
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
SELECT 'Error running SAMPLE_TASK';
SHOW ERRORS;
SET RESULT=FALSE;
END;
-- DO SOMETHING HERE
SET RESULT=TRUE;
END //
DELIMITER ;
DELIMITER //
CREATE PROCEDURE RUN_TASKS()
BEGIN
DECLARE SUCCESS BOOLEAN DEFAULT TRUE;
IF (SUCCESS) THEN
CALL SAMPLE_TASK(SUCCESS);
END IF;
IF (SUCCESS) THEN
CALL SAMPLE_TASK2(SUCCESS);
END IF;
END //
DELIMITER ;
CALL RUN_TASKS();
The gist of it is that there are a series of tasks that are broken out into stored procedures. We want to execute each in turn aborting if we encounter an error.
This has worked well for us until now due to the fact one of our tasks we want to add will modify triggers, and it seems that creating a trigger within a stored procedure is not supported.
Since we can't add triggers in the stored procedures we are looking to do it outside of them, such as by adding a CREATE TRIGGER statement after the call to RUN_TASKS. The problem we have with that is that we can't find a way to handle errors at that level. From what I've been able to find declaring handlers can only be done at the procedure level, and MariaDb doesn't seem to offer try/catch like some some other DB products.
So the questions are: 1) Does anyone know how to create a trigger from within a stored procedure? 2) How do you add error handling outside of a stored procedure? Thanks
Related
I use Spring Repository, And Oracle DB.
I have a table and trigger that fire on insert/update/delete. If I execute some insert/delete/update against the table and trigger got an SQL error (locked resources or something else) will the Repository method got an exception? Or Oracle trigger executes as separated part of insert/delete/update statements?
I don't know Spring Repository, but I do know that in - for example: Oracle Forms, Application Express, Reports - trigger error propagates all the way up. Everything stops in that case.
Suppose that I have a form and enter some data. Database trigger fires and tries to do whatever it does, and it fails with raise_application_error. Nothing gets completed, and I see an error in my form, e.g. "ORA-20001: my custom error".
Therefore, I presume that you'd experience the same in Spring Repository. After all, wouldn't cost much to test it, right? I would if I could, but I can't so - do it yourself.
Your Spring Repository will be error out when you trying to insert data into table. Our DML command will not performed if there is any error in our associated trigger.
Mostly i use DML in my PLSQL procedure for insert and use GET_LOCKED_TRANSACTION() procedure to check if any resource is busy.
FUNCTION GET_LOCKED_TRANSACTION
(
P_WIP_ENTITY_ID IN NUMBER,
P_PRODUCTION_NOTE_NUMBER IN NUMBER
) RETURN BOOLEAN IS
ROW_LOCKED EXCEPTION;
PRAGMA EXCEPTION_INIT(ROW_LOCKED, -54);
BEGIN
/* cURSOR WITH nOWAIT Attribute */
FOR CC IN (SELECT *
FROM myTable
WHERE WIP_ENTITY_ID = P_WIP_ENTITY_ID
FOR UPDATE NOWAIT) LOOP
NULL;
END LOOP;
RETURN FALSE;
EXCEPTION
WHEN ROW_LOCKED THEN
RETURN TRUE;
END GET_LOCKED_TRANSACTION;
I would like to create four databases in for-loop. However, I got an error. Could you help me to resolve this problem ?
DO $$
BEGIN
FOR counter IN 1..2 LOOP
CREATE DATABASE 'database_name_%', counter;
END LOOP;
END; $$
ERROR: syntax error at or near "'Counter: %'"
LINE 4: CREATE DATABASE 'Counter: %', counter;
From documentation for the CREATE DATABASE command:
CREATE DATABASE cannot be executed inside a transaction block.
And since:
PostgreSQL actually treats every SQL statement as being executed within a transaction. If you do not issue a BEGIN command, then each individual statement has an implicit BEGIN and (if successful) COMMIT wrapped around it. A group of statements surrounded by BEGIN and COMMIT is sometimes called a transaction block
You can't create database in functions. So even if you can handle current problem, you won't be able to execute this function.
Here is one scenario i came across,
- I have a SQL job which has around four SQL stored procedure
- These are getting executed sequentially one after another
- Now Case is : If any of the stored procedure fails or raise an exception, entire Job should get halt.
How I could do that ?
Make yourself a little procedure with something like this:
BEGIN TRY
DECLARE #Return INTEGER
-- Run first procedure
EXEC #Return = firstProcedure
IF (#Return <> 0)
BEGIN
-- Do some error handling
END
-- Run second procedure
EXEC #Return = secondProcedure
IF (#Return <> 0)
BEGIN
-- Do some error handling
END
-- etc...
END TRY
BEGIN CATCH
-- Do some error handling
END CATCH
Although there are several different ways to do this, I would suggest that you use the facilities in SQL Server Agent. Make each of the calls a separate step in the job.
This will allow you to move from one step to the next when successful. You'll also be able to use SQL Server Agent's logging and error handling mechanisms to determine the error and handle it.
Using basic SQL, I'm populating a table from another DB. This uses basic Delete statements to remove old data and Insert statements with a FROM clause using a DBLink. I'm attempting to transfer this to a package and have come up with this:
Package:
CREATE OR REPLACE
PACKAGE LOADDATA AS
procedure POPULATETABLE;
END LOADDATA;
PL/SQL (Package Body):
CREATE OR REPLACE
PACKAGE BODY LOADDATA AS
procedure POPULATETABLE AS
BEGIN
DELETE FROM DATATRANSFER;
INSERT INTO DATATRANSFER
SELECT VALUENUM, DATACONTENT, sysdate AS TRANSFER_DATA
FROM TRANSFERTABLE#DATALINK;
COMMIT;
NULL;
END POPULATETABLE;
END LOADDATA;
And to run the command, I would run:
exec LOADDATA.POPULATETABLE();
My question is should the procedure have an input/output parameter or declared variables? It has compiled and worked correctly but I'm unsure if I'm following PL/SQL methodology.
There is no rule to provide parameters.
Additonally you can insert record to log table which will store the start date, end date, number of records inserted and number of records deleted in case you need to track the batch execution periodically.
Also if this package is called by some web page or some application then you may need to create an exception block and send a Error Message as an output parameter in a User Readable Form.
EDIT:
Package Specification
CREATE OR REPLACE
PACKAGE LOADDATA AS
procedure POPULATETABLE(out_variable OUT VARCHAR2);
END LOADDATA;
Package Body
CREATE OR REPLACE
PACKAGE BODY LOADDATA AS
procedure POPULATETABLE(out_variable OUT VARCHAR2) AS
BEGIN
DELETE FROM DATATRANSFER
-- <TODO:INSERT records deleted and date into a log table>
INSERT INTO DATATRANSFER
SELECT VALUENUM, DATACONTENT, sysdate AS TRANSFER_DATA
FROM TRANSFERTABLE#DATALINK;
-- <TODO:INSERT records inserted and date into a log table>
COMMIT;
NULL;
-- Assign out_variable as success if comletes successfully
out_variable := 'SUCCESS';
EXCEPTION
-- WHEN OTHERS catches all exceptions Oracle error message is displayed in SQLERRM
WHEN OTHERS THEN
-- Assign out_variable with Error message if errors out
out_variable := 'Error :'||SQLERRM;
END POPULATETABLE;
END LOADDATA;
I have 10 stored procedures.
For example -
stored procedure fetches the rows from table A
then stored procedure runs and then third...
How can I do error handling in this.. for example I have to check with if first stored procedure executed successfully run second else throw error. If first executed successfully run second stored procedure if second runs successfully run third otherwise throw error.
ALTER PROCEDURE [dbo].[MASTER_PROCEDURE] AS
EXEC QRY_STEP3
EXEC QRY_STEP_3_1_1
EXEC OQRY_STEP_3_1_1
I would add logic to each of your subsidiary stored procedures to determine whether they have succeeded or not. eg test for existence of the temporary table. Then use a return value to indicate success of the proc. Typically this would be 0 for succes and non-zero for failure.
You would then call the procs from your master proc like this
DECLARE #ReturnValue INT
EXEC #ReturnValue = QRY_STEP1
IF(#ReturnValue = 0)
BEGIN
EXEC #ReturnValue = QRY_STEP2
END
ELSE
BEGIN
--REPORT ERROR
END
Using this approach, your master proc doesnt need to know about the inner workings of each child proc, and your master proc code will be cleaner and more readable.
use ##error.. Can be done like this
ALTER PROCEDURE [dbo].[MASTER_PROCEDURE] AS
EXEC QRY_STEP3
IF ##error =0
begin
EXEC QRY_STEP_3_1_1
else
begin
print "error in proc name"
return 1
End
if (##error=0)
Begin
EXEC OQRY_STEP_3_1_1
Else
print "error in proc name"
return 1
End
END
END
First to do this correctly you should use TRY CATCH blocks in the child packages. Those should return to the calling proc if there is an error. This way you can also return an error code to the calling proc if results are unexpected, such as a temp table with zero records which is not an error but which might make the subsequent procs fail.
Next, why are you using child procs at all? Honestly this is something that is probably better done in one proc. You say for instance that you are creating temp tables in one proc that you use in subsequent procs. To do this you need global temp tables. The problem is that global temp tables are not specific to the orginal connection that called them and thus two people trying to do this simluatnaeously might have their data mixedup. Whereas if you use one proc and local temp tables that can't happen.