Procedury składowane w PostgreSQL/Język PLpgSQL/Wyjątki
Łapanie wyjątków
edytujDomyślnie, gdy pojawi się wyjątek funkcja jest przerywana, a błąd zgłaszany aplikacji.
W procedurze można przechwycić wyjątek, w sposób analogiczny do znanego z C++ czy Javy bloku try { } catch {}. Odpowiada za to rozszerzony blok kodu o następującej składni:
[ <<etykieta>> ] [ DECLARE deklaracje zmiennych ] BEGIN instrukcje EXCEPTION WHEN lista wyjątków1 THEN instrukcje [ WHEN lista wyjątków2 THEN instrukcje ] END;
W bloku obsługiwane są wyłącznie błędy zgłoszone przez instrukcje umieszczone po słowie BEGIN, błędy zgłoszone w części deklaracyjnej są przekazywane do bloku nadrzędnego.
Lista wyjątków zawiera nazwy symboliczne lub kody błędów, rozdzielone słowem kluczowym OR. Instrukcje podane po THEN są wykonywane, gdy zgłoszony wyjątek znajduje się na liście. Zamiast listy wyjątków można podać słowo kluczowe OTHERS, które łapie wszystkie niewychwycone wcześniej wyjątki.
Jeśli żaden blok WHEN nie obsłuży wyjątku, jest on przekazywany do bloku nadrzędnego.
Należy pamiętać, że bloki z obsługą wyjątków są wolniejsze, niż tradycyjne - patrz Transakcje.
Przykład 1
edytujCREATE TABLE osoby (
id integer PRIMARY KEY,
imie text NOT NULL,
nazwisko text NOT NULL,
wiek integer NOT NULL CHECK (wiek > 0)
);
CREATE OR REPLACE FUNCTION dodaj_osobe(integer, text, text, integer) RETURNS text AS $$
BEGIN
INSERT INTO osoby VALUES ($1, $2, $3, $4);
RETURN 'OK!';
EXCEPTION
WHEN UNIQUE_VIOLATION THEN
RETURN 'powtórzony identyfikator osoby';
WHEN CHECK_VIOLATION THEN
RETURN 'wiek nie może być mniejszy ani równy zero';
WHEN OTHERS THEN
-- UWAGA: bezsensowny komunikat! (ale to tylko przykład)
RETURN 'inny błąd';
END;
$$ LANGUAGE 'plpgsql';
TRUNCATE osoby;
SELECT dodaj_osobe(1, 'Jan', 'Kowalski', 33);
SELECT dodaj_osobe(1, 'Jan', 'Kowalski', 33);
SELECT dodaj_osobe(2, 'Kazimierz', 'Nowak', 0);
SELECT dodaj_osobe(3, NULL, 'Nowak', 0);
Poniżej wynik. Komunikat "inny błąd" jest wynikiem naruszenie ograniczenia NOT NULL na kolumnie imie.
dodaj_osobe ------------- OK! (1 row) dodaj_osobe -------------------------------- powtórzony identyfikator osoby (1 row) dodaj_osobe ------------------------------------------- wiek nie może być mniejszy ani równy zero (1 row) dodaj_osobe ------------- inny błąd (1 row)
Przykład 2
edytujBłąd z części deklaracyjnej nie zostanie złapany.
CREATE OR REPLACE FUNCTION test() RETURNS text AS $$
DECLARE
k integer := 1/0;
BEGIN
RETURN 'ok';
EXCEPTION
WHEN division_by_zero THEN
RETURN 'Dzielenie przez zero!';
END;
$$ LANGUAGE 'plpgsql';
Informacje o wyjątku
edytujW bloku po WHEN dostępne są dwie predefiniowane zmienne:
- SQLSTATE - kod błędu,
- SQLERRM - komunikat błędu.
Poprawiony przykład
BEGIN
...
EXCEPION
...
WHEN OTHERS THEN
-- teraz lepiej
RETURN 'błąd ' || SQLSTATE || ': ' || SQLERRM;
i efekt:
dodaj_osobe ---------------------------------------------------------------------- błąd 23502: null value in column "imie" violates not-null constraint
Rozszerzone informacje o wyjątku
edytujOd wersji 9.2 dostępna będzie instrukcja GET STACKED DIAGNOSTICS, która pozwoli odczytać szczegółowe informacje o złapanym wyjątku. Składnia:
GET STACKED DIAGNOSTICS zmienna = nazwa_parametru [, zmienna = nazwa_parametru [, ... ] ];
Gdzie zmienna jest typu napisowego (text), natomiast nazwą parametru może być:
- RETURNED_SQLSTATE - kod błędu; odpowiada zmiennej SQLSTATE;
- MESSAGE_TEXT - komunikat błędu; odpowiada zmiennej SQLERRM;
- PG_EXCEPTION_DETAIL - szczegóły błędu;
- PG_EXCEPTION_HINT - podpowiedź;
- PG_EXCEPTION_CONTEXT - ślad stosu: w kolejnych wierszach zawiera instrukcję, która spowodowała błąd, oraz nazwę funkcji i numer wiersza, w którym ta instrukcja się znajduje.
Przykład
edytujCREATE OR REPLACE FUNCTION funkcja_a() RETURNS void AS $$
BEGIN
RAISE 'Błąd' USING DETAIL = 'Szczegóły', HINT = 'Podpowiedź';
END;
$$ LANGUAGE 'plpgsql';
CREATE OR REPLACE FUNCTION funkcja_b() RETURNS void AS $$
BEGIN
PERFORM funkcja_a();
END;
$$ LANGUAGE 'plpgsql';
CREATE OR REPLACE FUNCTION test() RETURNS text AS $$
BEGIN
PERFORM funkcja_b();
RETURN 'ok';
EXCEPTION
WHEN OTHERS THEN
DECLARE
state text;
msg text;
detail text;
hint text;
context text;
BEGIN
GET STACKED DIAGNOSTICS
state = RETURNED_SQLSTATE,
msg = MESSAGE_TEXT,
detail = PG_EXCEPTION_DETAIL,
hint = PG_EXCEPTION_HINT,
context = PG_EXCEPTION_CONTEXT;
RETURN 'RETURNED_SQLSTATE = ' || state || E'\n' ||
'MESSAGE_TEXT = ' || msg || E'\n' ||
'PG_EXCEPTION_DETAIL = ' || detail || E'\n' ||
'PG_EXCEPTION_HINT = ' || hint || E'\n' ||
E'PG_EXCEPTION_CONTEXT = \n' || context;
END;
END;
$$ LANGUAGE 'plpgsql';
Wynik:
$ SELECT test(); test ------------------------------------------------- RETURNED_SQLSTATE = P0001 + MESSAGE_TEXT = Błąd + PG_EXCEPTION_DETAIL = Szczegóły + PG_EXCEPTION_HINT = Podpowiedź + PG_EXCEPTION_CONTEXT = + SQL statement "SELECT funkcja_a()" + PL/pgSQL function "funkcja_b" line 3 at PERFORM+ SQL statement "SELECT funkcja_b()" + PL/pgSQL function "test" line 3 at PERFORM
Zgłaszanie wyjątków
edytujInstrukcja RAISE pozwala zgłosić dowolne wyjątki. Przeczytaj jej dokładny opis.
Ponowne podnoszenie wyjątków (rethrow)
edytujBezparametrowa instrukcja RAISE użyta w bloku WHEN powoduje podniesienie wyłapanego wyjątku. Np.
WHEN unique_violation OR not_null_violation THEN
-- komunikat
RAISE NOTICE 'powtórzony identyfikator osoby lub pusta wartość';
-- zgłoszenie unique_violation albo not_null_violation
RAISE;
...