Wyobraź sobie, że piszesz funkcję, która liczy średnią ocenę studenta. Co się stanie, jeśli spróbujesz podzielić przez zero (na przykład, gdy ocen nie ma)? Wypuścisz taki kod na produkcję — i niespodziewany gość w postaci błędu pojawi się od razu. PL/pgSQL daje ci mocne narzędzia do obsługi takich błędów, dzięki czemu twój kod jest odporny, bezpieczny i przyjemny w użyciu.
Obsługa błędów w PL/pgSQL pozwala na:
- Generowanie komunikatów wyjaśniających, co poszło nie tak.
- Przerywanie wykonania kodu w przypadku krytycznych błędów.
- Logowanie problemów do późniejszej analizy.
Podstawowe poziomy komunikatów PL/pgSQL
PL/pgSQL obsługuje kilka poziomów komunikatów, które pomagają programistom skutecznie diagnozować i rozwiązywać problemy. Oto one:
- NOTICE: wyświetla informacyjny komunikat. Używane do debugowania.
- WARNING: powiadomienie o potencjalnym problemie, które nie przerywa działania programu.
- EXCEPTION: krytyczny błąd, który przerywa działanie programu (i zwraca sterowanie do kodu wywołującego).
Poziomy komunikatów w PL/pgSQL
| Poziom komunikatu | Opis |
|---|---|
NOTICE |
Informacja lub komunikaty debugowania. Nie wpływa na wykonanie |
WARNING |
Ostrzeżenie o możliwych problemach. Działa jako podpowiedź |
EXCEPTION |
Poważny błąd, który kończy działanie programu |
Składnia polecenia RAISE
Do generowania komunikatów i obsługi błędów używa się polecenia RAISE. Oto jego podstawowa składnia:
RAISE <poziom komunikatu> 'treść komunikatu' [, zmienne...];
<poziom komunikatu>—NOTICE,WARNING,EXCEPTION.'treść komunikatu'— opis problemu.[zmienne...]— dodatkowe wartości, które można przekazać do treści komunikatu.
Przykład 1: użycie RAISE NOTICE
Czasem ważne jest, żeby wiedzieć, co się dzieje w środku twojej funkcji. Na przykład do debugowania pętli:
DO $$
BEGIN
FOR i IN 1..5 LOOP
RAISE NOTICE 'Aktualna wartość i: %', i;
END LOOP;
END
$$;
Wynik: W konsoli pojawią się linie Aktualna wartość i: 1, Aktualna wartość i: 2 i tak dalej do 5.
Przykład 2: użycie RAISE EXCEPTION
Teraz wyobraź sobie, że piszesz funkcję, która powinna zakończyć się błędem w określonych warunkach:
DO $$
BEGIN
IF 1 = 1 THEN
RAISE EXCEPTION 'Coś poszło nie tak!';
END IF;
END
$$;
Wynik: wykonanie zostaje przerwane, a komunikat błędu pojawia się w konsoli.
Praca z parametrami w RAISE
Używając parametrów, możesz spersonalizować treść komunikatu. Do tego służą placeholdery %:
Przykład 3: wstawianie zmiennych w RAISE
DO $$
DECLARE
student_name TEXT := 'Ivan';
average_score NUMERIC := NULL;
BEGIN
IF average_score IS NULL THEN
RAISE EXCEPTION 'Student % nie ma średniej oceny!', student_name;
END IF;
END
$$;
Wynik: komunikat Student Ivan nie ma średniej oceny!.
Jak widzisz, % zostaje zastąpiony przez zmienną student_name, co sprawia, że komunikat jest bardziej czytelny.
Generowanie własnych błędów
Błędy to nie tylko przypadki losowe! Czasem trzeba je generować celowo, żeby zabezpieczyć kod przed złymi danymi.
Przykład 4: sprawdzanie wartości wejściowych
Napiszemy funkcję, która sprawdza wartość liczby i rzuca błąd, jeśli jest ujemna:
CREATE OR REPLACE FUNCTION check_positive(value NUMERIC)
RETURNS TEXT AS $$
BEGIN
IF value < 0 THEN
RAISE EXCEPTION 'Liczba % jest ujemna!', value;
END IF;
RETURN 'Liczba poprawna.';
END;
$$ LANGUAGE plpgsql;
Teraz przetestujmy funkcję:
SELECT check_positive(-5);
Wynik: komunikat błędu Liczba -5 jest ujemna!.
Jeśli podasz wartość dodatnią:
SELECT check_positive(10);
Wynik: Liczba poprawna.
Obsługa błędów w kontekście
Fajnie, jeśli umiesz generować błędy. Ale jeszcze lepiej — obsługiwać je w zależności od sytuacji. Do tego służy blok BEGIN ... EXCEPTION.
Struktura obsługi błędów
BEGIN
-- Twój główny kod
EXCEPTION
WHEN TYP_BŁĘDU THEN
-- Co zrobić w przypadku tego błędu
WHEN INNY_BŁĄD THEN
-- Akcje przy innym błędzie
WHEN OTHERS THEN
-- Obsługa wszystkich pozostałych błędów
END;
Rozszyfrujmy składniki:
EXCEPTION— słowo kluczowe oznaczające początek bloku obsługi błędów.WHEN— pozwala wskazać konkretny typ obsługiwanego błędu, np.unique_violationlubdivision_by_zero.OTHERS— używane do obsługi wszystkich błędów, które nie zostały jawnie wymienione w blokachWHEN.
Przykład 5: obsługa dzielenia przez zero
Pokażemy obsługę błędu na prostym przykładzie funkcji dzielenia:
CREATE OR REPLACE FUNCTION safe_divide(a NUMERIC, b NUMERIC)
RETURNS NUMERIC AS $$
BEGIN
-- Próbujemy wykonać dzielenie
RETURN a / b;
EXCEPTION
WHEN division_by_zero THEN
RAISE WARNING 'Próba dzielenia przez zero. Zwracam NULL.';
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
Przetestujmy funkcję:
SELECT safe_divide(10, 2); -- Oczekiwany wynik: 5
SELECT safe_divide(10, 0); -- Oczekiwany wynik: NULL i ostrzeżenie w konsoli
Typowe błędy przy użyciu RAISE
Brak poziomu komunikatu. Jeśli zapomnisz podać poziom, PostgreSQL wyrzuci błąd.
Źle:
RAISE 'Komunikat bez poziomu';
Dobrze:
RAISE NOTICE 'Komunikat z poziomem NOTICE';
Nieprawidłowe parametry. Jeśli używasz %, upewnij się, że przekazujesz odpowiednią liczbę zmiennych.
Źle:
RAISE NOTICE 'Przykład z parametrem %';
Dobrze:
RAISE NOTICE 'Przykład z parametrem %', 'wartość';
Przerywanie. Nadmierne użycie RAISE EXCEPTION może przerywać ważne operacje. Używaj go z głową.
Przydatne wskazówki
- Uważaj na blok
WHEN OTHERS. Jeśli możesz, podawaj konkretne błędy, żeby nie przechwycić błędów, które powinny być obsłużone inaczej. - Używaj
RAISEdo debugowania. Nigdy nie zostawiaj błędów nieobsłużonych. - Nie zapominaj o wydajności. Obsługa błędów może być kosztowna, szczególnie w dużych procedurach.
Jeśli wszystko zrobisz dobrze, twoje procedury będą odporne i poradzą sobie nawet z niespodziewanymi awariami. PM może być z ciebie bardzo dumny!
GO TO FULL VERSION