Dzisiaj przeanalizujemy typowe błędy przy pisaniu funkcji, ich przyczyny i jak sobie z nimi radzić. Bo tylko przez debugowanie prawdziwy ninja kodu ogarnia swój fach — kodowanie! Let's debug it!
Pisanie funkcji, zwłaszcza na początku przygody z PL/pgSQL, może wydawać się trudne. Nawet doświadczeni programiści w PostgreSQL potrafią się potknąć. Przejdźmy przez te pułapki po kolei.
1. Pominięcie słowa kluczowego RETURNS
PL/pgSQL jest bardzo wymagający jeśli chodzi o opis funkcji. Jedno z najczęstszych nieporozumień — zapomnieć podać typ danych, który funkcja ma zwracać. Zobacz przykład:
-- Błąd: brak słowa kluczowego RETURNS
CREATE FUNCTION incorrect_function() AS $$
BEGIN
RETURN 1;
END;
$$ LANGUAGE plpgsql;
PostgreSQL nie ogarnie, co ta funkcja ma zwracać. RETURNS to obowiązkowy element składni, który określa typ zwracanych danych (np. RETURNS INT, RETURNS TEXT, albo nawet RETURNS VOID).
Poprawka: dodaj słowo kluczowe RETURNS z odpowiednim typem danych:
CREATE FUNCTION correct_function() RETURNS INT AS $$
BEGIN
RETURN 1;
END;
$$ LANGUAGE plpgsql;
2. Zwracanie wyniku bez RETURN
Początkujący często zapominają, że w PL/pgSQL, żeby coś zwrócić, trzeba użyć operatora RETURN. Przykład:
-- Błąd: brak RETURN
CREATE FUNCTION missing_return() RETURNS TEXT AS $$
BEGIN
'Hello, World!'; -- Tylko tekst, ale nie zwrócony
END;
$$ LANGUAGE plpgsql;
Tutaj tekst 'Hello, World!' po prostu jest, ale nie jest zwracany. PostgreSQL potraktuje to jako brak wyniku i rzuci błąd.
Poprawka: dodaj jawnie operator RETURN:
CREATE FUNCTION fixed_return() RETURNS TEXT AS $$
BEGIN
RETURN 'Hello, World!';
END;
$$ LANGUAGE plpgsql;
3. Próba zapisania danych do niezadeklarowanej zmiennej
W PL/pgSQL przed użyciem zmiennej trzeba ją zadeklarować w bloku DECLARE. Przykład:
-- Błąd: zmienna my_var nie zadeklarowana
CREATE FUNCTION missing_variable() RETURNS VOID AS $$
BEGIN
my_var := 'Hello, World!';
END;
$$ LANGUAGE plpgsql;
PostgreSQL nie wie o istnieniu zmiennej my_var, bo nie została zadeklarowana w bloku DECLARE.
Poprawka: zawsze deklaruj zmienne w DECLARE:
CREATE FUNCTION declared_variable() RETURNS VOID AS $$
DECLARE
my_var TEXT;
BEGIN
my_var := 'Hello, World!';
END;
$$ LANGUAGE plpgsql;
4. Nieprawidłowe użycie typu zwrotu VOID
Typ VOID oznacza, że funkcja nic nie zwraca. Czasem ktoś próbuje użyć RETURN w funkcji z typem VOID, co kończy się błędem:
-- Błąd: RETURN w funkcji z VOID
CREATE FUNCTION void_example() RETURNS VOID AS $$
BEGIN
RETURN 1; -- Zwracanie wartości niedozwolone
END;
$$ LANGUAGE plpgsql;
Funkcje z typem VOID nie powinny zwracać wartości. Możesz użyć RETURN, ale bez żadnej wartości.
Poprawka: albo usuń RETURN, albo napisz go bez wartości:
CREATE FUNCTION correct_void() RETURNS VOID AS $$
BEGIN
-- Po prostu wykonujemy akcje
RAISE NOTICE 'Ta funkcja nic nie zwraca';
RETURN; -- Zakończenie funkcji
END;
$$ LANGUAGE plpgsql;
5. Nieprawidłowe użycie RAISE do debugowania
Debugowanie w PL/pgSQL często robi się przez RAISE NOTICE. Ale złe formaty i zmienne mogą wywołać błąd.
Przykład:
-- Błąd: zły format
CREATE FUNCTION debug_example() RETURNS VOID AS $$
BEGIN
RAISE NOTICE 'The value is %'; -- Brak zmiennej
END;
$$ LANGUAGE plpgsql;
Operator RAISE oczekuje, że po % będzie zmienna lub wartość. Jeśli zostawisz % bez niczego, PostgreSQL nie ogarnie polecenia.
Poprawka: upewnij się, że zmienne lub wartości są podane poprawnie:
CREATE FUNCTION fixed_debug() RETURNS VOID AS $$
DECLARE
my_var TEXT := 'PostgreSQL';
BEGIN
RAISE NOTICE 'The value is %', my_var; -- Podana zmienna
END;
$$ LANGUAGE plpgsql;
6. Problemy z nazwami zmiennych i kolumn
Jeśli nazwa zmiennej jest taka sama jak kolumny, mogą wyjść dziwne rzeczy. Przykład:
-- Błąd: konflikt nazw zmiennej i kolumny
CREATE FUNCTION name_conflict() RETURNS TEXT AS $$
DECLARE
name TEXT;
BEGIN
SELECT name INTO name FROM students LIMIT 1; -- Które name jest używane?
RETURN name;
END;
$$ LANGUAGE plpgsql;
PL/pgSQL wybiera zmienne zamiast kolumn, jeśli nazwy się pokrywają.
Poprawka: używaj aliasów tabel albo unikaj takich samych nazw.
CREATE FUNCTION fixed_conflict() RETURNS TEXT AS $$
DECLARE
student_name TEXT;
BEGIN
SELECT s.name INTO student_name FROM students s LIMIT 1;
RETURN student_name;
END;
$$ LANGUAGE plpgsql;
7. Nieprawidłowe wykonywanie zapytań w pętli
Błędy często pojawiają się przy próbie wykonania zapytań SQL w pętlach. Przykład:
-- Błąd: złe zapytanie w pętli
CREATE FUNCTION cycle_error() RETURNS VOID AS $$
BEGIN
FOR rec IN SELECT * FROM students LOOP
EXECUTE 'UPDATE students SET active = TRUE WHERE id = ' || rec.id;
END LOOP;
END;
$$ LANGUAGE plpgsql;
SQL-injection... Niebezpieczne! Sklejanie stringów w zapytaniach SQL to zły pomysł. Może prowadzić do podatności.
Poprawka: używaj parametrów:
CREATE FUNCTION safe_cycle() RETURNS VOID AS $$
BEGIN
FOR rec IN SELECT * FROM students LOOP
EXECUTE 'UPDATE students SET active = TRUE WHERE id = $1' USING rec.id;
END LOOP;
END;
$$ LANGUAGE plpgsql;
8. Błędy typów danych
Przykład błędu:
-- Błąd: niezgodność typów danych
CREATE FUNCTION type_error() RETURNS INT AS $$
DECLARE
my_var TEXT := 'not_a_number';
BEGIN
RETURN my_var; -- Błąd przy zwrocie tekstu zamiast INT
END;
$$ LANGUAGE plpgsql;
PostgreSQL oczekuje INT, a dostaje TEXT. Typy danych są tu pilnowane bardzo dokładnie.
Jak naprawić? Upewnij się, że typy się zgadzają, albo zrób jawne rzutowanie:
CREATE FUNCTION type_correct() RETURNS INT AS $$
DECLARE
my_var TEXT := '42';
BEGIN
RETURN my_var::INT; -- Rzutowanie tekstu na liczbę
END;
$$ LANGUAGE plpgsql;
Najlepsze praktyki i tipy
- Dziel skomplikowane funkcje na mniejsze. Łatwiej wtedy debugować i testować.
- Używaj komentarzy w funkcjach, żeby opisać trudniejsze fragmenty.
- Zawsze testuj funkcje na małych danych zanim odpalisz je na prawdziwych tabelach.
- Debuguj przez
RAISE NOTICE, żeby ogarnąć co się dzieje w środku. - Unikaj SQL-injection: używaj parametrów w zapytaniach.
-- Użycie RAISE do debugowania
DO $$
DECLARE
total_students INT;
BEGIN
SELECT COUNT(*) INTO total_students FROM students;
RAISE NOTICE 'Total students: %', total_students; -- Komunikat debugowy
END;
$$;
Te tipy pozwolą Ci uniknąć masy bólu głowy i raz na zawsze rozwalić "pułapki" PL/pgSQL!
GO TO FULL VERSION