Обработка ошибок

Статья основана на двадцать шестом видео из 31 темы курса SQL 2.0 — PL/pgSQL в PostgreSQL от Аристова Евгения, который является логическим продолжением курса SQL c 0. Ссылки на видео на платформах RUTUBE и VK video.

В данной статье подробно разбираются обработка ошибок для команд INSERT и как получить информацию об ошибке.

В прошлой статье мы разобрали условия возникновения исключений (секция EXCEPTION), которая помогает перехватить и обработать ошибки.

Презентация и исходники доступны по ссылке.

Обработка ошибок для команд INSERT

Например, обработка исключений помогает выполнить либо команду UPDATE, либо INSERT, в зависимости от ситуации.

Однако в современных приложениях вместо этого приёма рекомендуется использовать INSERT с ON CONFLICT DO UPDATE, так как обработка ошибок очень дорога с точки зрения потребления ресурсов. Но иногда без обработки ошибок не обойтись.

Получение информации об ошибке

При обработке исключений часто бывает необходимым получить детальную информацию о произошедшей ошибке. Для этого в PL/pgSQL есть два способа: использование специальных переменных и команда получения стека диагностики GET STACKED DIAGNOSTICS.

Внутри секции EXCEPTION специальная переменная SQLSTATE содержит код ошибки, для которой было вызвано исключение. Специальная переменная SQLERRM содержит сообщение об ошибке, связанное с исключением. Эти переменные являются неопределёнными вне секции EXCEPTION.

Также в обработчике исключения можно получить информацию о текущем исключении командой GET STACKED DIAGNOSTICS, которая имеет вид:

GET STACKED DIAGNOSTICS переменная { = | := } элемент [ , ... ];

Каждый элемент представляется ключевым словом, указывающим, какое значение состояния нужно присвоить заданной переменной.

Получение информации о месте возникновения ошибки

Команда GET DIAGNOSTICS, получает информацию о текущем состоянии выполнения кода (тогда как команда GET STACKED DIAGNOSTICS, рассмотренная ранее, выдаёт информацию о состоянии выполнения в момент предыдущей ошибки). Её элемент состояния PG_CONTEXT позволяет определить текущее место выполнения кода. PG_CONTEXT возвращает текст с несколькими строками, описывающий стек вызова. В первой строке отмечается текущая функция и выполняемая в данный момент команда GET DIAGNOSTICS, а во второй и последующих строках отмечаются функции выше по стеку вызовов.

Exception пример

DECLARE
  text_var1 text;
  text_var2 text;
  text_var3 text;
BEGIN
  -- здесь происходит обработка, которая может вызвать исключение
  ...
EXCEPTION WHEN OTHERS THEN
  GET STACKED DIAGNOSTICS text_var1 = MESSAGE_TEXT,
    text_var2 = PG_EXCEPTION_DETAIL,
    text_var3 = PG_EXCEPTION_HINT;
END;

Потом например записать в таблицу с логами проблему. Или сделать raise notice — вывести сообщение в консоль. И если повысить детализацию системы логирования, эта информация также попадет в журнал.

При выполнении команд в секции EXCEPTION локальные переменные функции на PL/pgSQL сохраняют те значения, которые были на момент возникновения ошибки. Однако, будут отменены все изменения в базе данных, выполненные в блоке

Наличие секции EXCEPTION значительно увеличивает накладные расходы на вход/выход из блока, поэтому не используйте EXCEPTION без надобности.

Наличие секции EXCEPTION не позволит создавать транзакции внутри блока.

Примеры кода с обработкой ошибок:

-- Процедура с комплексной обработкой ошибок:
CREATE OR REPLACE PROCEDURE safe_data_operation()
AS $$
DECLARE
    error_context TEXT;
BEGIN
    -- Блок 1: Основные операции
    BEGIN
        UPDATE important_table SET status = 'processing';
        CALL critical_operation();
        
    EXCEPTION
        WHEN others THEN
            GET STACKED DIAGNOSTICS error_context = PG_EXCEPTION_CONTEXT;
            RAISE NOTICE 'Ошибка в основном блоке: %, контекст: %', SQLERRM, error_context;
            
            -- Откат всей процедуры
            RAISE EXCEPTION 'Критическая ошибка, откат операции';
    END;
    
    -- Блок 2: Не критические операции
    BEGIN
        CALL non_critical_operation();
        
    EXCEPTION
        WHEN others THEN
            RAISE NOTICE 'Не критическая ошибка проигнорирована: %', SQLERRM;
    END;
    
    -- Блок 3: Логирование (всегда выполняется)
    BEGIN
        INSERT INTO process_log (operation, status, details)
        VALUES ('safe_data_operation', 'completed', 'Process finished at ' || CURRENT_TIMESTAMP);
        
    EXCEPTION
        WHEN others THEN
            RAISE NOTICE 'Ошибка логирования: %', SQLERRM;
    END;
END;
$$ LANGUAGE plpgsql;

Больше примеров доступно на гитхабе и в видео.

В следующей статье мы разберём функции для работы с массивами, в т.ч. многомерными.


Опубликовано

в

Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

11 + 2 =