Procedura z TRY...CATCH przechwytuje błędy wykonania i pozwala zareagować na nie zamiast przerwać działanie.
Jak działa blok TRY...CATCH w T-SQL
Procedura z TRY...CATCH w języku T-SQL, stosowanym przez Microsoft SQL Server, dzieli obsługę operacji na dwa bloki. Instrukcje, które mogą wygenerować błąd wykonania, umieszcza się między BEGIN TRY i END TRY. Jeżeli SQL Server wykryje błąd możliwy do przechwycenia, przerywa wykonywanie bieżącego bloku i przechodzi do BEGIN CATCH. Instrukcje zapisane między BEGIN CATCH i END CATCH mogą zarejestrować problem, wycofać transakcję albo przekazać błąd wyżej.
Najczęściej mechanizm umieszcza się wewnątrz procedury składowanej definiowanej poleceniem CREATE PROCEDURE lub CREATE OR ALTER PROCEDURE. Sam blok nie naprawia błędu i nie ponawia automatycznie operacji. Określa tylko kontrolowany przepływ po wystąpieniu błędu. Jeżeli w bloku CATCH nie ma odpowiedniej reakcji, procedura może zakończyć się bez przekazania pełnej informacji do aplikacji, dlatego obsługa powinna świadomie zdecydować, czy błąd zapisać, zwrócić, czy ponownie zgłosić.
Informacje dostępne w części CATCH
SQL Server udostępnia w bloku CATCH funkcje opisujące ostatni przechwycony błąd: ERROR_NUMBER() zwraca jego numer, ERROR_MESSAGE() treść komunikatu, ERROR_SEVERITY() poziom ważności, ERROR_STATE() stan, ERROR_PROCEDURE() nazwę procedury lub wyzwalacza, w którym wystąpił błąd, a ERROR_LINE() numer wiersza. Funkcje te zwracają informacje związane z bieżącym kontekstem obsługi i poza blokiem CATCH zwracają wartość NULL.
Dane można zapisać w tabeli dziennika wraz z czasem wystąpienia, nazwą procedury i parametrami diagnostycznymi. Trzeba jednak uważać, aby sama obsługa błędu nie przesłoniła pierwotnej przyczyny. Do ponownego zgłoszenia bieżącego błędu służy w SQL Server instrukcja THROW; użyta bez parametrów w bloku CATCH. Zachowuje ona przechwycony numer, komunikat i stan błędu. Możliwe jest także użycie składni THROW numer, komunikat, stan;, ale numer błędu musi być równy co najmniej 50000, komunikat ma ograniczoną długość, a stan jest liczbą z zakresu od 0 do 255.
Transakcja, wycofanie i stan danych
W procedurze wykonującej kilka powiązanych zmian często łączy się TRY...CATCH z transakcją rozpoczętą przez BEGIN TRANSACTION. Po pomyślnym wykonaniu wszystkich operacji procedura wywołuje COMMIT TRANSACTION. Po błędzie należy ustalić, czy transakcję można wycofać. Funkcja XACT_STATE() zwraca 1, gdy istnieje aktywna i zatwierdzalna transakcja, -1, gdy transakcja jest niezatwierdzalna i można ją tylko wycofać, oraz 0, gdy nie ma aktywnej transakcji.
Typowy blok CATCH sprawdza wartość XACT_STATE() i wykonuje ROLLBACK TRANSACTION, jeżeli wynik jest różny od zera, a następnie używa THROW;. Nie każda procedura powinna jednak bezwarunkowo wycofywać transakcję należącą do kodu wywołującego. W zagnieżdżonych wywołaniach trzeba uwzględnić @@TRANCOUNT, ponieważ wewnętrzny COMMIT zmniejsza licznik, ale faktyczne zatwierdzenie następuje dopiero po zatwierdzeniu transakcji zewnętrznej. Dla bardziej złożonych przepływów można stosować punkty zapisu za pomocą SAVE TRANSACTION, o ile transakcja pozostaje zatwierdzalna.
Opcja SET XACT_ABORT ON powoduje, że błąd wykonania może wycofać całą bieżącą transakcję, a nie tylko pojedynczą instrukcję. W połączeniu z TRY...CATCH nadal można przechwycić błąd i wykonać czynności porządkowe, ale przy transakcji w stanie -1 dozwolone jest zasadniczo tylko jej wycofanie. Ustawienie tej opcji powinno wynikać z wymagań procedury, ponieważ zmienia zachowanie transakcji przy błędach.
Kontrola procedury podczas diagnostyki
Przy analizie procedury z TRY...CATCH należy sprawdzić, które instrukcje rzeczywiście znajdują się w bloku TRY, czy blok CATCH odczytuje funkcje błędu przed wykonaniem kolejnych operacji oraz czy błąd jest ponownie zgłaszany do aplikacji. Warto również zweryfikować ścieżkę sukcesu, ścieżkę błędu walidacji danych, zachowanie przy naruszeniu klucza unikatowego oraz stan transakcji po błędzie. Błędy składni i błędy kompilacji występujące na tym samym poziomie wykonania nie zawsze są przechwytywane przez lokalny TRY...CATCH; podobnie nie są obsługiwane przerwania połączenia, polecenie KILL ani część błędów kończących sesję.
Diagnostyka powinna obejmować także kod wywołujący procedurę. Aplikacja może oczekiwać konkretnego numeru błędu, komunikatu albo kodu zwrotnego, podczas gdy procedura zgłasza inną informację lub kończy działanie po częściowym wykonaniu zmian. Należy sprawdzić, czy parametry wejściowe są walidowane przed modyfikacją danych, czy logowanie nie powoduje kolejnego błędu oraz czy po nieudanej operacji połączenie nie pozostaje w aktywnej transakcji. W środowisku serwisowym analiza takiego przepływu pomaga odróżnić problem samej procedury SQL Server od błędu aplikacji, konfiguracji połączenia lub danych wejściowych.