Perhaps I am missing something, but even though the RAISERROR
s below have severity of 16 (as per documentation) the transaction is still committed as if XACT_ABORT ON
has no effect.
CREATE PROCEDURE [ExploringGroups].[RemoveMember]
@groupId uniqueidentifier,
@adminUsername nvarchar(50),
@targetUsername nvarchar(50)
AS
SET XACT_ABORT ON
BEGIN TRANSACTION
DECLARE
@adminUserId uniqueidentifier = dbo.fn_userId(@adminUsername),
@targetUserId uniqueidentifier = dbo.fn_userId(@targetUsername)
IF @targetUserId IS NULL OR ExploringGroups.IsMember(@groupId, @targetUserId) = 0
RAISERROR('Target user was not located', 16, 1)
IF ExploringGroups.IsInRole(@groupId, @adminUserId, 'adm') = 0
RAISERROR('Specified user is not an administrator of this group', 16, 2)
IF @adminUserId = @targetUserId
RAISERROR('You cannot remove yourself', 16, 3)
-- statements below still execute and commit even though there was an error raised above
DELETE FROM ExploringGroups.MemberRole WHERE GroupId = @groupId AND UserId = @targetUserId
DELETE FROM ExploringGroups.Membership WHERE GroupId = @groupId AND UserId = @targetUserId
COMMIT
RETURN 0
Calling
exec exploringgroups.removemember '356048C5-BAB3-45C9-BE3C-A7227225DFDD', 'Crypton', 'Crypton'
Produces
Msg 50000, Level 16, State 2, Procedure RemoveMember, Line 20
Specified user is not an administrator of this group
Msg 50000, Level 16, State 3, Procedure RemoveMember, Line 24
You cannot remove yourself
I thought XACT_ABORT
was supposed to roll back the whole transaction if it's set to ON
?
Actually, it behaves exactly as it was supposed to. XACT_ABORT
really caused the transaction to roll back, so were there any data modifications up to the point of the error they will be rolled back. However, it didn't affect the flow of execution, and it didn't stop running the stored procedure, so the following two DELETEs were executed as implicite transactions. Explicit RAISERRORs don't abort the batch.
See this simplified version:
create table #t(i int);
insert #t values(1);
go
alter procedure sp
as
set xact_abort on
begin tran
raiserror ('x', 16, 1);
print 'deleting';
delete #t;
commit;
go
exec sp
go
select * from #t
go
The only funny thing was that the error about COMMIT not having a corresponding BEGIN TRAN was swallowed.
With SEH, it would jump into CATCH block.