Khối CATCH được kích hoạt khi không nên


7

Trong hành trình tìm kiếm không ngừng nghỉ của mình để tự bắn vào chân mình bằng giao dịch tiết kiệm, tôi dường như đã tìm thấy nhiều cách hơn để hoàn thành mục đích tìm kiếm. Điều khoản giao dịch lưu chính lần này là không cần thiết, nhưng đó là vì tôi đã viết mã dưới đây.

Xem xét ví dụ đầy đủ sau đây với các trình xử lý lỗi lồng nhau:

begin try

  begin try
    select 'Step 1';
  end try
  begin catch
    select 'Step 1 handler - handling ''' + error_message() + '''';
    goto commit_and_exit;
  end catch;

  begin try
    select 'Step 2';
    raiserror('Step 2 error', 16, 1);
  end try
  begin catch
    select 'Step 2 handler - handling ''' + error_message() + '''';
    goto commit_and_exit;
  end catch;

end try
begin catch
  select 'Outer handler - handling ''' + error_message() + '''';
  goto commit_and_exit;
end catch

commit_and_exit:

raiserror('Error raised for the caller to see', 16, 1);

Nó được ghi lại rằng

GOTOcác câu lệnh có thể được sử dụng để chuyển đến một nhãn bên trong cùng TRYhoặc CATCHchặn hoặc để lại một TRYhoặc một CATCHkhối.

Hay nó có thể?

Với mã ở trên, một lập trình viên lành mạnh sẽ cho biết đầu ra sẽ là

Bước 1
Bước 2
Trình xử lý bước 2 - xử lý 'Lỗi bước 2'
<Lỗi được đưa ra để người gọi thấy>

Trong thực tế những gì đang xảy ra là:

Bước 1
Bước 2
Trình xử lý bước 2
- xử lý 'Lỗi bước 2' Trình xử lý bên ngoài - xử lý 'Lỗi được đưa ra cho người gọi để xem'
<Lỗi đưa ra cho người gọi để xem>

Khi gỡ lỗi từng bước, tôi có thể thấy điều khiển đó hoàn toàn rời khỏi khối thử / bắt, sau đó xảy ra lỗi, điều khiển được đưa trở lại catchkhối ngoài cùng , khối đó thực thi, điều khiển lại chuyển sang commit_and_exit:và khối cuối cùng được thực thi một lần nữa .

Nếu bạn có một số commit trans hoặc rollback transs commit_and_exit:, bạn sẽ cố gắng thực hiện tran hai lần. Với kết quả có thể tưởng tượng, thực tế có thể có các giao dịch bên ngoài được bắt đầu bởi người gọi.

Tôi cũng đã thử tạo một nhãn khác end_of_outer:, ngay trước lớp ngoài end try, để điều khiển rời khỏi trykhối bên ngoài "bình thường". Đủ thú vị, điều đó đã không làm cho bất kỳ sự khác biệt.

Cái quái gì đang xảy ra, và cách làm đúng là gì?


2
Tôi không nhìn thấy các "handler Outer .." lỗi trên SQL Server 2012, nhưng tôi đang nhìn thấy nó trên SQL Server 2008 R2 Express.
gbn

Điều này cũng giúp? stackoverflow.com/a/2074139/27535
gbn

1
@gbn Khi được áp dụng cụ thể cho các giao dịch, vâng, đúng vậy (mẫu sẽ phức tạp hơn một chút, vì chúng tôi có các lỗi sắp xếp ổn XACT_STATE() = 1, do đó chúng tôi quay lại điểm lưu và tiếp tục). Nhưng tôi quan tâm nhiều hơn đến chính nguyên tắc xử lý lỗi ở đây, ngay cả khi có giao dịch. Bất kỳ mã sau commit_and_exit:sẽ thực hiện hai lần.
GSerg

@gbn Bạn có nói đây là lỗi năm 2008 đã được sửa vào năm 2012 không? Có một mục trên kết nối?
GSerg

Câu trả lời:


10

Có vẻ như hành vi SQL Server 2012 trước là một lỗi.

Tôi suy ra điều này từ bài viết Kết nối này:

https://connect.microsoft.com/QueryServer/feedback/details/712003/sql-server-2012-error-in-exception-handling-mechanism

Tôi có thể sai, tất nhiên...


Có nghĩa với tôi. Bối cảnh của một trykhối tồn tại sai, do đó, các hàm lỗi có thể trả về giá trị của chúng và một hậu quả khác là lỗi sẽ đưa việc thực thi vào xử lý trybối cảnh tồn tại sai .
GSerg
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.