No więc wyobraź sobie, że napisałeś skomplikowaną funkcję albo procedurę. Już widzisz, jak Twoja baza danych śmiga, ale nagle — bach! — dane są nie takie, zapytania chodzą wolno, a szef zaczyna się denerwować. I wtedy na scenę wchodzi debugowanie.
Debugowanie w PL/pgSQL jest potrzebne, żeby:
- Znaleźć błędy w logice, na przykład gdy funkcja zwraca coś innego, niż się spodziewałeś.
- Ogarnąć nieprawidłowe dane wejściowe. Bo czasem użytkownicy bazy wpisują nie tylko dane, ale i... coś totalnie niezrozumiałego!
- Usunąć problemy z wydajnością. Bo kod pisany na szybko potrafi działać jak żółw szukający Wi-Fi na pustyni Sahara.
Serio, debugowanie to nie tylko szukanie i naprawianie błędów. To sposób na ulepszenie Twojego kodu, żeby był szybszy, wydajniejszy i bardziej czytelny.
Podstawowe podejścia do debugowania PL/pgSQL
Debugowanie w PL/pgSQL można ogarnąć na kilka sposobów. Zobaczmy je po kolei.
- Korzystanie z wbudowanych narzędzi PostgreSQL
PostgreSQL daje kilka wbudowanych opcji do diagnostyki, w tym funkcje logowania (RAISE NOTICE i RAISE EXCEPTION), a także analizę planu wykonania zapytań (EXPLAIN ANALYZE). Te narzędzia pomagają zrozumieć, co się dzieje w środku Twojej funkcji.
- Logowanie za pomocą
RAISE NOTICE
RAISE NOTICE — to Twój ziomek, jeśli chcesz sprawdzić, jakie dane przechodzą przez funkcję, na którym etapie coś się wywaliło, albo podejrzeć wartości zmiennych. W przeciwieństwie do RAISE EXCEPTION, nie przerywa działania funkcji. Na przykład możesz wypisywać zawartość zmiennej na każdym etapie wykonania.
DO $$
DECLARE
counter INT := 0;
BEGIN
FOR counter IN 1..5 LOOP
RAISE NOTICE 'Aktualna wartość licznika: %', counter;
END LOOP;
END $$;
Ten kod wypisuje wartości counter od 1 do 5. Prosta magia, ale mega przydatna do debugowania!
- Korzystanie z narzędzi zewnętrznych
Debugowanie PL/pgSQL można robić też przy pomocy takich narzędzi jak pgAdmin (z GUI). Pozwala on stawiać breakpointy i podglądać wartości zmiennych na żywo. Jeśli lubisz wizualnych pomocników, pgAdmin będzie świetnym wsparciem.
Etapy debugowania
Kiedy zaczynasz debugować funkcję albo procedurę, ważne jest, żeby trzymać się pewnej kolejności. Zatrzymajmy się na każdym etapie:
- Analiza danych wejściowych
Pierwsze, co trzeba ogarnąć, to dane wejściowe. Upewnij się, że dane, które dostaje Twoja funkcja, nie mają błędów ani dziwnych wartości. Na przykład możesz sprawdzić wszystkie parametry wejściowe za pomocą RAISE NOTICE:
CREATE FUNCTION check_input(x INTEGER) RETURNS VOID AS $$
BEGIN
IF x IS NULL THEN
RAISE EXCEPTION 'Wartość wejściowa nie może być NULL!';
END IF;
RAISE NOTICE 'Wartość wejściowa: %', x;
END;
$$ LANGUAGE plpgsql;
Ten przykład pokazuje, jak ostrzegać użytkowników o problemach z danymi wejściowymi.
- Sprawdzanie wykonania każdego etapu
Podziel swoją funkcję na logiczne bloki i dodaj RAISE NOTICE w kluczowych miejscach. To pomoże Ci ogarnąć, gdzie dokładnie coś się wykrzaczyło.
CREATE FUNCTION calculate_discount(price NUMERIC, discount NUMERIC) RETURNS NUMERIC AS $$
BEGIN
RAISE NOTICE 'Start funkcji: cena %, zniżka %', price, discount;
IF price <= 0 THEN
RAISE EXCEPTION 'Cena nie może być ujemna ani równa zeru!';
END IF;
IF discount < 0 OR discount > 100 THEN
RAISE EXCEPTION 'Zniżka musi być między 0 a 100!';
END IF;
RETURN price - (price * discount / 100);
END;
$$ LANGUAGE plpgsql;
Tu na każdym etapie debugowania wypisywane są przydatne info o stanie procesu.
- Optymalizacja i usuwanie problemów
- Jak już znajdziesz błąd, popraw go. Jeśli problem dotyczy wydajności, użyj narzędzi do analizy, takich jak
EXPLAIN ANALYZE, żeby zoptymalizować zapytania.
Praktyczne zastosowanie umiejętności debugowania
Weźmy realne zadanie: mamy funkcję, która dodaje rekord do tabeli i zwraca wygenerowany identyfikator. Niby wszystko logiczne i proste, ale czasem funkcja kończy się błędem i chcemy wiedzieć dlaczego.
Funkcja bazowa:
CREATE FUNCTION add_student(name TEXT, age INTEGER) RETURNS INTEGER AS $$
DECLARE
new_id INTEGER;
BEGIN
INSERT INTO students (name, age) VALUES (name, age) RETURNING id INTO new_id;
RETURN new_id;
END;
$$ LANGUAGE plpgsql;
Gdy wywołasz tę funkcję z nieprawidłowymi danymi, np. age < 0, wywali błąd. Ulepszmy ją przy pomocy narzędzi do debugowania.
Ulepszona funkcja z logowaniem:
CREATE FUNCTION add_student(name TEXT, age INTEGER) RETURNS INTEGER AS $$
DECLARE
new_id INTEGER;
BEGIN
-- Logujemy dane wejściowe
RAISE NOTICE 'Dodawanie studenta: imię %, wiek %', name, age;
-- Sprawdzamy poprawność wieku
IF age < 0 THEN
RAISE EXCEPTION 'Wiek nie może być ujemny!';
END IF;
-- Dodajemy studenta i zwracamy jego ID
INSERT INTO students (name, age) VALUES (name, age) RETURNING id INTO new_id;
-- Logujemy sukces
RAISE NOTICE 'Student dodany z ID %', new_id;
RETURN new_id;
END;
$$ LANGUAGE plpgsql;
Teraz, jeśli pojawi się błąd, będziesz dokładnie wiedzieć, co poszło nie tak, dzięki komunikatom z RAISE NOTICE.
Przydatne porady na koniec
- Nie zapominaj usuwać zbędnego logowania w kodzie produkcyjnym.
RAISE NOTICEto świetne narzędzie do debugowania, ale jeśli zostawisz go za dużo, zaśmieci logi na produkcji. - Pracuj na małych kawałkach kodu. Jeśli funkcja jest za bardzo rozbudowana, podziel ją na kilka części. Debugowanie będzie łatwiejsze.
- Ćwicz regularnie. Im więcej piszesz i debugujesz kod, tym szybciej wyłapujesz i naprawiasz błędy.
Debugowanie to trochę jak zabawa w detektywa, tylko zamiast lupy masz zapytania SQL i logikę PL/pgSQL. Mistrzostwo przychodzi z doświadczeniem, ale każdy naprawiony bug robi z Ciebie lepszego programistę!
GO TO FULL VERSION