Question

Perhaps I am missing something, but even though the RAISERRORs 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?

Was it helpful?

Solution

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.

OTHER TIPS

You should be using a "THROW" statement versus the RAISERROR approach.

Use a BEGIN TRY and BEGIN CATCH and commit the transaction normally or rollback in the CATCH block.

BEGIN TRY -- Do your inserts or throw error -- Commit transaction END TRY BEGIN CATCH -- Rollback END CATCH;

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top