CodeGym /Kurslar /SQL SELF /Xətaların işlənməsi və ilkin vəziyyətə qaytarma: EXCEPTIO...

Xətaların işlənməsi və ilkin vəziyyətə qaytarma: EXCEPTION, RAISE

SQL SELF
Səviyyə , Dərs
Mövcuddur

PostgreSQL-də errorlar bir çox səbəbdən baş verə bilər: constraint pozuntusu (NOT NULL, UNIQUE, CHECK), sintaksis səhvləri, təkrarlanan dəyərlər və s. Əgər belə errorları tutub işləməsən, bütün xarici transaction tam rollback olacaq. Dayanıqlı biznes əməliyyatları üçün onları düzgün işləmək vacibdir.

PL/pgSQL-də BEGIN ... EXCEPTION ... END blokları ilə funksiyalarda və prosedurlarda error handling üçün güclü mexanizm var. Bunlar Python və ya Java-dakı try-catch-ə bənzəyir, amma PostgreSQL transaction-larında öz xüsusiyyətləri var.

Vacib məqam:

Hər BEGIN ... EXCEPTION ... END bloku “virtual savepoint” kimi işləyir. Əgər exception baş verirsə — bu blokdakı bütün dəyişikliklər avtomatik rollback olur. Bu, PL/pgSQL funksiyaları və prosedurlarında qismən rollback üçün yeganə düzgün üsuldur.

EXCEPTION ilə error handling sintaksisi

BEGIN
    -- əsas kod
EXCEPTION
    WHEN XƏTA_NÖVÜ THEN
        -- konkret errorun işlənməsi
    WHEN BAŞQA_XƏTA_NÖVÜ THEN
        -- başqa handler
    WHEN OTHERS THEN
        -- qalan bütün errorların işlənməsi
END;

Funksiya/prosedurlarda nümunə

DO $$
BEGIN
    RAISE NOTICE 'İndi error olacaq...';
    PERFORM 1 / 0;
EXCEPTION
    WHEN division_by_zero THEN
        RAISE NOTICE 'Sıfıra bölmə tutuldu!';
END;
$$;

Nümunə: error handling və rollback ilə update

Güman edək ki, orders adlı cədvəlimiz var:

CREATE TABLE orders (
    id SERIAL PRIMARY KEY,
    amount NUMERIC NOT NULL,
    status TEXT NOT NULL
);

Bir funksiya yaradaq ki, order statusunu yeniləyir və error olsa heç nə dəyişmir:

CREATE OR REPLACE FUNCTION update_order_status(order_id INT, new_status TEXT)
RETURNS VOID AS $$
BEGIN
    BEGIN
        UPDATE orders
        SET status = new_status
        WHERE id = order_id;

        -- Error simulyasiyası
        IF new_status = 'FAIL' THEN
            RAISE EXCEPTION 'Error simulyasiya edirik!';
        END IF;

        RAISE NOTICE 'Order statusu % yeniləndi', order_id;

    EXCEPTION
        WHEN OTHERS THEN
            RAISE NOTICE 'Orderun yenilənməsində error %: %', order_id, SQLERRM;
            -- Blokdakı bütün dəyişikliklər avtomatik rollback olacaq!
            -- Lazım olsa erroru xaricə ötürmək üçün təkrar atırıq
            RAISE;
    END;
END;
$$ LANGUAGE plpgsql;

Transaction-lara explicit control ilə prosedurlarda necə işləyir

Prosedurlarda (CREATE PROCEDURE) COMMIT, ROLLBACK, SAVEPOINT istifadə edə bilərsən, amma etmək olmaz ROLLBACK TO SAVEPOINT. Əgər prosedurda yalnız bir hissəni rollback etmək lazımdırsa, yenə də BEGIN ... EXCEPTION ... END istifadə et:

CREATE OR REPLACE PROCEDURE pay_order(order_id INT, amount NUMERIC)
LANGUAGE plpgsql
AS $$
BEGIN
    -- Bütün prosedur COMMIT/ROLLBACK istifadə edə bilər, amma ayrıca mərhələ üçün:
    BEGIN
        UPDATE accounts
        SET balance = balance - amount
        WHERE id = (SELECT account_id FROM orders WHERE id = order_id);

        -- error
        IF amount < 0 THEN
            RAISE EXCEPTION 'Məbləğ mənfi ola bilməz!';
        END IF;

        UPDATE orders SET status = 'PAID' WHERE id = order_id;

    EXCEPTION
        WHEN OTHERS THEN
            RAISE NOTICE 'Orderun ödənişində error %: %', order_id, SQLERRM;
            -- bu blokdakı dəyişikliklər avtomatik rollback olacaq
    END;

    COMMIT; -- Transaction-u explicit bitirmək yalnız prosedurlarda olar!
END;
$$;

Error logging

Errorları sadəcə tutmaq yox, həm də sonradan analiz üçün saxlamaq vacibdir.

CREATE TABLE error_log (
    id SERIAL PRIMARY KEY,
    order_id INT,
    error_message TEXT,
    error_time TIMESTAMP DEFAULT now()
);

Funksiya və ya prosedurda:

EXCEPTION
    WHEN OTHERS THEN
        INSERT INTO error_log (order_id, error_message)
        VALUES (order_id, SQLERRM);
        RAISE NOTICE 'Error log-a yazıldı: %', SQLERRM;
        RAISE;

PostgreSQL 17-də vacib məhdudiyyətlər və nüanslar

Funksiyaların içində (CREATE FUNCTION ... ) transaction control komandalarını (BEGIN, COMMIT, ROLLBACK, SAVEPOINT, ROLLBACK TO SAVEPOINT, RELEASE SAVEPOINT) istifadə etmək olmaz! Bütün funksiyalar tamamilə xarici transaction daxilində işləyir.

Prosedurların içində (CREATE PROCEDURE ... ) explicit SAVEPOINT, RELEASE SAVEPOINT, COMMIT, ROLLBACK yazmaq olar. AMMA: ROLLBACK TO SAVEPOINT — QADAĞANDIR! (Əgər PL/pgSQL prosedurunda ROLLBACK TO SAVEPOINT istifadə etməyə çalışsan, syntax error alacaqsan).

Funksiya və prosedurlarda "kodun bir hissəsini" rollback etmək üçün BEGIN ... EXCEPTION ... END bloklarından istifadə olunur. Error baş verəndə blokun içindəki hər şey avtomatik rollback olur və icra davam edə bilər.

Prosedurları (CREATE PROCEDURE) funksiya içində və ya SELECT ilə çağırmaq olmaz — yalnız ayrıca CALL ... komandası ilə çağırmaq olar.

PL/pgSQL-də "qismən rollback" necə etmək olar?

Yeganə işlək yol — error handling üçün BEGIN ... EXCEPTION ... END bloklarından istifadə etməkdir. Belə blok avtomatik savepoint yaradır və error baş verəndə blokun içindəki dəyişiklikləri rollback edir, qalan hissəyə toxunmur.

EXCEPTION ilə nümunə (tövsiyə olunan yanaşma):

CREATE OR REPLACE PROCEDURE demo_savepoint()
LANGUAGE plpgsql
AS $$
BEGIN
    -- Hansısa kod
    BEGIN
        -- Burada error bütün proseduru yox,
        -- yalnız bu bloku rollback edəcək!
        INSERT INTO demo VALUES ('pis data'); -- ola bilər error versin
    EXCEPTION
        WHEN OTHERS THEN
            RAISE NOTICE 'Error tutuldu, blokdakı dəyişikliklər ləğv olundu';
    END;
    -- Burada icra davam edir!
END;
$$;

Nümunə: bütün prosesi rollback-dan qoruyan batch data yüklənməsi

CREATE OR REPLACE PROCEDURE load_big_batch()
LANGUAGE plpgsql
AS $$
DECLARE
    rec RECORD;
BEGIN
    FOR rec IN SELECT * FROM import_table LOOP
        BEGIN
            INSERT INTO target_table (col1, col2)
            VALUES (rec.col1, rec.col2);
        EXCEPTION WHEN OTHERS THEN
            INSERT INTO import_errors (err_msg)
            VALUES ('Sətirdə error: ' || rec.col1 || ': ' || SQLERRM);
            -- bu blokdakı dəyişikliklər ləğv olundu!
        END;
    END LOOP;
    COMMIT;  -- yalnız prosedur explicit transaction-dan kənarda işləyirsə icazəlidir!
END;
$$;

-- Prosedurun çağırılması
CALL load_big_batch();

Diqqət et: əgər belə proseduru artıq transaction başlamış client-dən (məsələn, Python-da autocommit=False) çağırsan, prosedurda COMMIT və ya SAVEPOINT icrası error verəcək.

Daxili savepoint-lər və EXCEPTION ilə işləmək üçün məsləhətlər

  1. PL/pgSQL-də ROLLBACK TO SAVEPOINT istifadə etmə! Bu, syntax error verəcək.
  2. Qismən rollback üçün həmişə iç-içə BEGIN ... EXCEPTION ... END bloklarından istifadə et.
  3. Unutma ki, prosedurda COMMIT və ROLLBACK transaction-u yenidən başladır — onları yalnız prosedur autocommit rejimində işləyəndə çağırmaq olar!
  4. Errorları ayrıca cədvələ log et ki, səhv sətrlər barədə məlumat itməsin.
  5. Əgər biznes əməliyyatı tam atomik olmalıdırsa (ya hamısı, ya heç nə) — onu funksiya kimi yaz, içində COMMIT/ROLLBACK olmasın; mərhələ-mərhələ işləmək lazımdırsa — prosedur kimi yaz.

Nümunə: batch import və qismən işləmə

CREATE OR REPLACE PROCEDURE import_batch()
LANGUAGE plpgsql
AS $$
DECLARE
    rec RECORD;
BEGIN
    FOR rec IN SELECT * FROM staging_table LOOP
        BEGIN
            INSERT INTO data_table (data)
            VALUES (rec.data);
        EXCEPTION
            WHEN unique_violation THEN
                INSERT INTO import_log (msg)
                VALUES ('Dublikat: ' || rec.data);
            WHEN OTHERS THEN
                INSERT INTO import_log (msg)
                VALUES ('Error: ' || rec.data || ' — ' || SQLERRM);
        END;
    END LOOP;
END;
$$;

PostgreSQL 17 üçün əsas yadda saxlamalılar:

PL/pgSQL prosedurlarında SAVEPOINT, RELEASE SAVEPOINT, COMMIT, ROLLBACK icazəlidir, amma ROLLBACK TO SAVEPOINT — olmaz.

"Qismən rollback" funksiyalarda və prosedurlarda yalnız iç-içə BEGIN ... EXCEPTION ... END blokları ilə edilir.

Transaction-ları daha yaxşısı çöldən idarə et (connection parametrləri və autocommit ilə), prosedur daxilində isə yuxarıda göstərilən error handling mexanizmlərindən istifadə et.

Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION