Bəzən trigger-lər gözlənilməz davranır və bu aşağıdakılarla bağlı ola bilər:
- Trigger-ə bağlı funksiyanın məntiqində səhv.
- Verilənlər bazası məhdudiyyətlərinin pozulması (məsələn, unikallığın pozulması və ya data type uyğun gəlməməsi).
- Tranzaksiya problemləri, trigger səhv səbəbindən dəyişiklikləri geri qaytarır.
- Rekursiya, əgər trigger özü-özünü çağırırsa (çox vaxt təsadüfən).
Belə problemlərin qarşısını almaq üçün PostgreSQL trigger-lərin və onların funksiyalarının içində səhvlərin işlənməsi imkanı verir. Bu alətlərə EXCEPTION blokları və RAISE operatoru daxildir, bu gün nümunələrlə baxacağıq.
EXCEPTION bloku ilə səhvlərin işlənməsi
EXCEPTION bloku bizə səhvləri tutmağa və onları işləmək üçün kod yazmağa imkan verir. Bu, Python və ya Java kimi proqramlaşdırma dillərində try-catch istifadəsinə bənzəyir.
EXCEPTION bloku PL/pgSQL funksiyalarında belə istifadə olunur:
BEGIN
-- Funksiyanın əsas kodu
EXCEPTION
WHEN <sehv_novu> THEN
-- Səhvin işlənməsi üçün kod
END;
Burada <sehv_novu> — işləmək istədiyin konkret səhv və ya səhvlər qrupudur (məsələn, unique_violation, division_by_zero və s.).
Nümunə: trigger-lərdə səhvlərin loglanması
Təsəvvür elə ki, səndə logs adlı cədvəl var və ora students cədvəlinə insert zamanı yaranan səhvləri yazmaq istəyirsən. Məsələn:
Loglar üçün cədvəl yaradırıq
CREATE TABLE logs (
id SERIAL PRIMARY KEY,
error_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
error_message TEXT
);
Səhvlərin işlənməsi ilə funksiya yaradırıq
CREATE OR REPLACE FUNCTION track_insert_errors()
RETURNS TRIGGER AS $$
BEGIN
-- Əsas kodu icra etməyə çalışırıq
BEGIN
-- "Səhv" əməliyyat nümunəsi: 0-a bölmə
PERFORM 1 / (NEW.some_value - NEW.some_value);
EXCEPTION
WHEN division_by_zero THEN
-- Əgər 0-a bölmə səhvi baş veribsə, onu loglara yazırıq
INSERT INTO logs (error_message) VALUES ('students-ə insert zamanı 0-a bölmə səhvi');
END;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
Trigger yaradırıq
CREATE TRIGGER before_insert_students
BEFORE INSERT ON students
FOR EACH ROW
EXECUTE FUNCTION track_insert_errors();
İndi, əgər students cədvəlinə insert zamanı 0-a bölmə səhvi baş verərsə, bu səhv işlənəcək və məlumat logs cədvəlinə yazılacaq.
Diaqnostika və debug üçün RAISE istifadəsi
RAISE operatoru xəbərdarlıq, səhv və ya debug üçün mesajlar çıxarmağa imkan verir. Trigger-in necə işlədiyini (və ya işləmədiyini!) başa düşmək üçün bu, çox faydalı alətdir.
RAISE mesaj növləri:
DEBUG— debug üçün mesaj.NOTICE— adi informasiya mesajı.WARNING— xəbərdarlıq.EXCEPTION— səhv mesajı, funksiyanın icrasını dayandırır.
RAISE sintaksisi:
RAISE <mesaj_novu> 'Mesaj';
Həmçinin dəyişənlərin dəyərlərini də ötürə bilərsən:
RAISE NOTICE 'NEW.id dəyəri = %', NEW.id;
Nümunə: trigger-də dəyərlərin debug-u
Tutaq ki, students cədvəlində update zamanı səhv çıxır və sən bilmək istəyirsən ki, hansı NEW və OLD dəyərləri problem yaradır. Bunun üçün RAISE istifadə edirik:
CREATE OR REPLACE FUNCTION debug_student_update()
RETURNS TRIGGER AS $$
BEGIN
RAISE NOTICE 'OLD.id = %, NEW.id = %', OLD.id, NEW.id;
-- Səhv yaradan şərt nümunəsi:
IF NEW.some_field IS NULL THEN
RAISE EXCEPTION 'some_field sahəsi NULL ola bilməz';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER after_update_students
AFTER UPDATE ON students
FOR EACH ROW
EXECUTE FUNCTION debug_student_update();
İndi hər update zamanı OLD və NEW dəyərlərini görəcəksən və əgər səhv baş verərsə, aydın səhv mesajı alacaqsan.
Trigger-lərdə tranzaksiyalar
Trigger-lər tranzaksiya kontekstində işləyir. Yəni, əgər trigger-in və ya onun funksiyasının içində haradasa səhv baş verərsə, bütün tranzaksiya geri qaytarılır. Bu, verilənlər bazasını natamam dəyişikliklərdən qoruyur.
Amma bu davranış bəzən çətinlik yaradır:
- Əgər trigger-in içindəki səhv səhv datalarla bağlıdırsa, bəzən yalnız bir hissəni geri qaytarmaq faydalı olardı.
- Başa düşmək lazımdır ki, tranzaksiyanın geri qaytarılması təkcə trigger-i yox, onu çağıran bütün əməliyyatı əhatə edir.
Nümunə: trigger-də tranzaksiyalardan istifadə
Məsələn, biz istəyirik ki, müəyyən biznes məntiqi icra olunsun: həm students cədvəlində update, həm də logs-a log yazmaq. Əgər bu əməliyyatlardan biri alınmasa, bütün tranzaksiya geri qaytarılır.
CREATE OR REPLACE FUNCTION transactional_student_update()
RETURNS TRIGGER AS $$
BEGIN
-- Update cəhdi loglanır
INSERT INTO logs (error_message) VALUES ('id-si ' || NEW.id || ' olan student-in update cəhdi');
-- Biznes şərtləri yoxlanılır
IF NEW.some_value IS NULL THEN
RAISE EXCEPTION 'some_value sahəsi NULL ola bilməz';
END IF;
-- Hər şey uğurlu olarsa, NEW qaytarılır
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER before_update_students
BEFORE UPDATE ON students
FOR EACH ROW
EXECUTE FUNCTION transactional_student_update();
Trigger-lərlə işləyərkən tipik səhvlər və onların qarşısı
Developer-lərin tez-tez etdiyi səhvlər:
Rekursiv trigger-lər. Bu, trigger dəyişiklik edib yenidən özünü işə salanda baş verir. Həll nümunəsi: WHEN şərtindən istifadə və ya təkrar çağırışların qarşısını almaq üçün flag əlavə etmək.
Səhvlərə görə bütün tranzaksiyanın geri qaytarılması. Çox vaxt bu arzuolunmazdır, əgər trigger əsas datalarla birbaşa bağlı deyilsə. Həll nümunəsi: EXCEPTION bloklarından düzgün istifadə etmək.
Artıq debug məlumatı. Logları doldurur və analiz etməyi çətinləşdirir. Həll nümunəsi: RAISE-ı yalnız development və test zamanı istifadə etmək.
Performansın aşağı düşməsi. Çətin trigger-lər INSERT, UPDATE və ya DELETE əməliyyatlarını ləngidə bilər. Həll nümunəsi: trigger məntiqini minimuma endirmək və ağır sorğulardan qaçmaq.
GO TO FULL VERSION