Reputation: 17
My program registers everything correctly when all the requirements for the transaction are fulfilled and logs the transaction information.
But when faulty information is given the information is not logged but the transaction still goes through and I am not sure what I did wrong.
First up is the stored procedure the logs the transaction info
/****** Object: StoredProcedure [dbo].[LogTransactionAttempt] Script Date: 8/1/2017 9:57:24 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[LogTransactionAttempt]
(@ToUserAccountId int,
@FromUserAccountId int,
@TransferAmount money,
@CreditorWithdrawl nvarchar(50),
@TransferCode numeric(18,0))
AS
BEGIN
INSERT INTO Transactions (FromUserAccountId, ToUserAccountId, TransferAmount,
CreditorWithdrawal, TransferCode, TransactionDateTime)
VALUES (@FromUserAccountId, @ToUserAccountId, @TransferAmount,
@CreditorWithdrawl, @TransferCode, GETDATE())
RETURN @@IDENTITY
END
Here is the overall procedure
/****** Object: StoredProcedure [dbo].[performTransaction] Script Date: 8/1/2017 9:57:05 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[performTransaction]
(@ToUserAccountId int,
@FromUserAccountId int,
@TransferAmount money,
@CreditorWithdrawl nvarchar(50),
@TransferCode numeric(18,0))
AS
BEGIN
DECLARE @FromUserAccountTest INT
SELECT @FromUserAccountTest = (SELECT Count(*)
FROM dbo.UserAccount
WHERE UserAccountId = @FromUserAccountID)
IF @FromUserAccountTest <> 1
BEGIN
RAISERROR('From Account Does Not Exist', 10, 1)
END
DECLARE @ToUserAccountTest INT
SELECT @ToUserAccountTest = (SELECT Count(*)
FROM dbo.UserAccount
WHERE UserAccountId = @ToUserAccountID)
IF @ToUserAccountTest <> 1
BEGIN
RAISERROR('To Account Does Not Exist', 10, 2)
END
-- At this point we can log the transaction
DECLARE @TransactionID INT
EXEC @TransactionID = dbo.LogTransactionAttempt @ToUserAccountId, @FromUserAccountId, @TransferAmount, @CreditorWithdrawl, @TransferCode
-- Check to see if sufficient balance
DECLARE @FromAccountBalance MONEY
SELECT @FromAccountBalance = (SELECT CreditBalance
FROM dbo.UserAccount
WHERE UserAccountID = @FromUserAccountID)
IF (@FromAccountBalance < @TransferAmount)
BEGIN
RAISERROR('Insufficient Balance', 10, 3)
END
-- Now we can start the transaction
BEGIN TRANSACTION
UPDATE UserAccount
SET CreditBalance = CreditBalance - @TransferAmount
WHERE UserAccountId = @FromUserAccountID
IF (@@ERROR <> 0)
BEGIN
ROLLBACK TRANSACTION /* if errors - rollback transaction */
RETURN
END
UPDATE UserAccount
SET CreditBalance = CreditBalance + @TransferAmount
WHERE UserAccountID = @ToUserAccountID
IF (@@ERROR <> 0)
BEGIN
ROLLBACK TRANSACTION /*if errors - rollback transaction */
RETURN
END
UPDATE dbo.Transactions
SET Successful = 1
WHERE TransactionID = @TransactionID
IF (@@ERROR <> 0)
BEGIN
ROLLBACK TRANSACTION /* if errors - rollback transaction */
RETURN
END
-- Transaction
COMMIT TRANSACTION
END -- Procedure
Like I said everything works perfect when the inputs are correct but if there is a bad input say a non existent account or lack of funds, it doesn't log the failure but the transaction still goes through
Edit tried switching to 16 from 10 no effect
example of a correct transaction moving $100 from account 11 with a balance of $100 to account 22 with no balance
USE [Transaction]
GO
DECLARE @return_value int
EXEC @return_value = [dbo].[performTransaction]
@ToUserAccountId = 22,
@FromUserAccountId = 11,
@TransferAmount = 100,
@CreditorWithdrawl = N'credit',
@TransferCode = 1
SELECT 'Return Value' = @return_value
GO
that correct entry returns
1017 22 11 100.0000 credit 1 2017-08-02 10:19:23.110 yes
in the transaction log
entering faulty information such as a transfering $150 from an account with a balance of only $100 raises the error codes properly but does not cancel the transaction even though the requested money is not all there
USE [Transaction]
GO
DECLARE @return_value int
EXEC @return_value = [dbo].[performTransaction]
@ToUserAccountId = 22,
@FromUserAccountId = 11,
@TransferAmount = 150,
@CreditorWithdrawl = N'credit',
@TransferCode = 2
SELECT 'Return Value' = @return_value
GO
returns the following in the messages
(1 row(s) affected)
Msg 50000, Level 16, State 3, Procedure performTransaction, Line 72 [Batch Start Line 2]
Insufficient Balance
(1 row(s) affected)
(1 row(s) affected)
(1 row(s) affected)
(1 row(s) affected)
So there errors are seen but the faulty transaction is not logged, but the transaction still goes through resulting in a negative balance
USE [Transaction]
GO
/****** Object: StoredProcedure [dbo].[LogTransactionAttempt] Script Date: 8/2/2017 9:20:54 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[LogTransactionAttempt]
(
@ToUserAccountId int,
@FromUserAccountId int,
@TransferAmount money,
@TransferCode numeric(18,0)
)
AS
BEGIN
INSERT INTO Transactions
(
UserAccountId,
TransferAmount,
CreditorWithdrawal,
TransferCode,
TransactionDateTime
)
VALUES
(
@FromUserAccountId,
@TransferAmount,
'Withdrawl',
@TransferCode,
GETDATE()
)
,
(
@ToUserAccountId,
@TransferAmount,
'Credit',
@TransferCode,
GETDATE()
)
RETURN @@IDENTITY
END
Upvotes: 1
Views: 459
Reputation: 1559
Raiserror does not set @@Error unless your severity level is 16 or higher. Messages less than 16 are considered informational, not errors. Try the following code to verify:
RAISERROR('Insufficient Balance', 10, 3)
select @@Error; -- will be 0
RAISERROR('Insufficient Balance', 16, 3)
select @@Error; -- will be 50000
Also, note that RAISERROR will NEVER break execution, unless you do something like raise a fatal error that causes the connection to end immediately (you'll need sysadmin privileges). So with exception of where you use an explicit "return", expect all code after RAISERROR to execute.
Read this for more about RAISERROR and breaking: https://social.msdn.microsoft.com/Forums/sqlserver/en-US/483d6be7-6518-41c9-8138-aa1412ae8252/raiseerror-to-stop-execution?forum=transactsql.
In addition you need to
DECLARE @errcode INT
after
DECLARE @FromUserAccountTest INT;
the add
select @errcode = @@ERROR
immediately after each raiserror
Reason is any successful event will reset @@ERROR to 0 - so all of the following resets your @@ERROR variable to 0;
SELECT @FromUserAccountTest = (SELECT Count(*)
FROM dbo.UserAccount
WHERE UserAccountId = @FromUserAccountID)
SELECT @FromUserAccountTest = (SELECT Count(*)
FROM dbo.UserAccount
WHERE UserAccountId = @FromUserAccountID)
EXEC @TransactionID = dbo.LogTransactionAttempt @ToUserAccountId, @FromUserAccountId, @TransferAmount, @CreditorWithdrawl, @TransferCode
UPDATE UserAccount
SET CreditBalance = CreditBalance - @TransferAmount
WHERE UserAccountId = @FromUserAccountID
Then finally change your first occurrence of
IF @@ERROR <> 0
to
IF @@errcode <> 0
leave the second occurrence as
IF @@ERROR <> 0
because it's testing the result of the update statement immediately preceding.
Upvotes: 2