I have some SQL:
BEGIN TRY
DECLARE #RowsInserted int;
SET #RowsInserted = ##ROWCOUNT;
SELECT #RowsInserted+'test' as [SUCCESS];
END TRY
BEGIN CATCH
SELECT ERROR_MESSAGE() AS [ERROR]
END CATCH
To my surprise, this actually produces two batches (data sets):
I can't use GO to split the batches due to the way TRY...CATCH works. So does that mean there's always some dummy result set if there's some error caught?
What I'd really like to do is throw away the SUCCESS batch (or other dummy batches like this when caught). Otherwise I'd have to navigate through some garbage batches to find the Error reporting batch in the catch statement which seems confusing.
What you need to do in the catch block is raise an error rather than selecting it.
BEGIN CATCH
-- rethrow error
DECLARE #ErrorMessage NVARCHAR(4000) = ERROR_MESSAGE()
DECLARE #ErrorSeverity INT = ERROR_SEVERITY()
DECLARE #ErrorState INT = ERROR_STATE()
RAISERROR (#ErrorMessage, #ErrorSeverity, #ErrorState)
END CATCH
There's a separation there: result sets (select) are ment for data, while errors (raiserror) are ment to communicate failures.
I don't think it's possible - the reference to the resultset is returned to the client before the enumeration begins and before it's possible to know that any record evaluation will produce an error.
Related
I wrote this piece of code in PL/PGSQL.
WHILE nbStatus1 < nbStatus0 AND StatusExec = 1 LOOP
BEGIN
SELECT Id, query INTO idQuery, Selectquery FROM temp_table_test WHERE Status = 0 LIMIT 1;
EXECUTE Selectquery;
UPDATE temp_table_test
SET Status = 1
WHERE Id = idQuery;
nbStatus1 := nbStatus1 + 1;
EXCEPTION
WHEN others THEN
UPDATE LOGS_TEST_DETAILS
SET ENDDATE = NOW(),
ERRORMESSAGE = SQLERRM,
ERRORCODE = SQLSTATE
WHERE TRAITEMENTID = lastTraitementId AND QUERYID = idQuery;
StatusExec := 0;
ROLLBACK;
END;
COMMIT;
END LOOP;
I use EXECUTE to CALL a list of procedure, one after the other in the while loop.
Each CALL is stored in the variable Selectquery.
It works fine but...
I'm trying to implement a kind of try catch in case i made a mistake in one of the procedure i call in the loop.
So i did a mistake (on purpose) inside one of the procedure i call. But the ROLLBACK, remove all the changes i did, even before the while loop. it kinda propagates to all my code
Is there a way to ROLLBACK only the changes that occured within the EXECUTE command and then exit the loop ?
There is something to deal with savepoint i guess, but i can't sort it out
Thanks for your help
Consider the following stored procedure
CREATE PROCEDURE AssignCodeToCustomer (#customerId int)
AS
BEGIN
DECLARE #code NVARCHAR(255)
BEGIN TRY
BEGIN TRANSACTION
SELECT #code = (
UPDATE
Codes
SET
CustomerId = #customerId
OUTPUT
INSERTED.Code
FROM (
SELECT TOP 1
Code
FROM
Codes
) AS c
WHERE
c.Code = Codes.Code
-- Other stuff
COMMIT TRANSACTION
END TRY
BEGIN CATCH
BEGIN
ROLLBACK TRANSACTION
EXEC spLogSQLError
END
END CATCH
END
GO
I get an error 'Incorrect syntax near the keyword UPDATE' on line 10 (which holds the keyword UPDATE). I could also first select a code and then assign it, but with concurrency in mind I want only one query. The query works if I don't try to set the output value into the variable. How can I fix this error or should I use another approach?
Say, I have a pgplsql function (e.g. buyItem) which returns something (e.g. bought item parameters). In some cases I want to indicate that something went wrong so the result is empty (e.g. not enough money to buy desired item). It's just a usual result, I'd not call it an exception (and definitely would not print an error about it on the db server, as raise exception does).
So the question is: what's the best practice to handle such cases?
Your two options are either to raise an exception, or return a record with both success and error information:
CREATE OR REPLACE FUNCTION buyItem(itemId integer)
RETURNS RECORD
AS $$
DECLARE
result RECORD;
BEGIN
IF itemId > 0 THEN
SELECT 'purchase successful', NULL INTO result;
ELSE
SELECT NULL, 'purchase failed' INTO INTO result;
END IF;
RETURN result;
END;
$$ LANGUAGE plpgsql;
There are more examples of returning records here: Return multiple fields as a record in PostgreSQL with PL/pgSQL
My application uses pl/pgsql functions that return integers.
The returned integer is used as a return code, to distinguish between different errors.
For example, if a function that insert datas returns -1, it means that some datas exist already and this is forbidden to try to insert the same data again. But if it returns -2, it means something else. That way the application knows the error and can display a useful error message to the user.
My problem now is that i want, at some points in the function, to return immediately when i detect an error, and rollback everything done so far in the function. If i use "raise exception", it will rollback, but not return an integer. If i use "return -1;", it will return an integer, but not rollback modifications. So i'm stuck, because obviously i can't do both
Here's a phony example function:
CREATE OR REPLACE FUNCTION create_flight_and_passengers(
_date timetamp,
_name text,
_passengers integer[]
)
RETURNS integer
LANGUAGE plpgsql
AS $$
DECLARE
return_code integer;
BEGIN
INSERT INTO flights
VALUES (_name);
SELECT function_1(_date, _passengers) into return_code;
if (return_code = -1) then
-- [1] rollback everything done since beginning of function
-- create_flight_and_passengers() and return -1
end if;
SELECT function_2(_date, _passengers) into return_code;
if (return_code = -1) then
-- [2] rollback everything done since beginning of function
-- create_flight_and_passengers() and return -2
end if;
return 0;
END;
$$;
In [1] and [2] i could use raise exception to rollback, but when i do this i don't have a returned integer.
I could set a local variable in [1] and [2], then raise exception, and in EXCEPTION do tests on this variable to know where the exception come from, but this is bloated, there must be something better !
I'm not even sure you can rollback effects of a function you have called and that has terminated (function_1() and function_2() in my example)
Any ideas ?
This is a pretty bizarre thing to want to do. If you really need to, you could do it like this:
DECLARE
retval integer;
BEGIN
retval := 0;
BEGIN
... make my changes ...
IF (... is something wrong? ...) THEN
RAISE EXCEPTION SQLSTATE '0U001';
END IF;
EXCEPTION
WHEN '0U001' THEN
retval := -1;
END;
END;
The concept here is that a BEGIN ... EXCEPTION block defines a subtransaction. A RAISE EXCEPTION within the block rolls back the subtransaction. We catch it at the outer level, preventing the exception from propagating outside the function and aborting the whole transaction.
See the PL/PgSQL documentation.
I'm having a problem with TRY...CATCH blocks. Can someone explain why the following code will not execute my sp?
DECLARE #Result int
SET #Result = 0
BEGIN TRY
SELECT * FROM TableNoExist
END TRY
BEGIN CATCH
SET #Result = ERROR_NUMBER()
END CATCH
EXEC dbo.spSecurityEventAdd #pSecurityEventTypeID = 11, #pResult = #Result
But this code does work:
DECLARE #Result int
SET #Result = 0
BEGIN TRY
SELECT 1/0
END TRY
BEGIN CATCH
SET #Result = ERROR_NUMBER()
END CATCH
EXEC dbo.spSecurityEventAdd #pSecurityEventTypeID = 11, #pResult = #Result
I'd like to make sure I catch all errors.
Thanks
Compile and Statement-level Recompile Errors
There are two types of errors that will not be handled by TRY…CATCH if the error occurs in the same execution level as the TRY…CATCH construct:
Compile errors, such as syntax errors that prevent a batch from executing.
Errors that occur during statement-level recompilation, such as object name resolution errors that happen after compilation due to deferred name resolution.
http://msdn.microsoft.com/en-us/library/ms179296.aspx
It looks like this thread answers your question.