Мы с вами сейчас как тайные агенты — наши функции и процедуры выполняют миссии: обрабатывают данные, выполняют вычисления или просто делают магию внутри базы. Но как узнать, если что-то пойдет не так? Как понять, на каком этапе сломалась "массажная ванночка для данных"? Здесь на помощь приходят логирование и обработка ошибок.
Помните, мы уже начинали знакомиться с тем, как в PostgreSQL и PL/pgSQL можно "общаться" и вести логи:
RAISE NOTICE: спокойный, дружеский тон — "Эй, тут всё хорошо, но тебе, возможно, захочется посмотреть на это".RAISE WARNING: тон чуть громче — "Ой, тут что-то странное, возможно, можешь заглянуть".RAISE EXCEPTION: паническая сирена — "СТОП! Алгоритм в беде! Мы остановили выполнение, чтобы всё не пошло прахом".
Каждый из этих уровней имеет своё предназначение, и важно правильно выбрать, какой из них использовать.
Вот как эти сообщения выглядят в коде:
DO $$
BEGIN
-- Уровень NOTICE (всё нормально, просто уведомление)
RAISE NOTICE 'Просто уведомление: началась обработка данных';
-- Уровень WARNING (что-то подозрительное)
RAISE WARNING 'Предупреждение: формат данных в столбце может быть некорректным';
-- Уровень EXCEPTION (критическая ошибка)
RAISE EXCEPTION 'Ошибка: входное значение недопустимо!';
END $$;
Когда использовать:
RAISE NOTICE— для отладки и спокойного вывода информации.RAISE WARNING— чтобы предупредить о потенциально неправильных данных.RAISE EXCEPTION— когда происходят критические ошибки, из-за которых выполнение функции должно быть остановлено.
Обработка ошибок с RAISE EXCEPTION
RAISE EXCEPTION — это ваш стоп-кран. Если что-то идет не так, вы сможете остановить выполнение функции и сообщить об ошибке.
Напомним, базовое использование выглядит так:
RAISE EXCEPTION 'Ваше сообщение об ошибке';
Но чтобы сделать сообщения более информативными, можно использовать переменные:
DECLARE
input_value INTEGER;
BEGIN
input_value := NULL;
IF input_value IS NULL THEN
RAISE EXCEPTION 'Ошибка: входное значение NULL. Ожидалось значение INTEGER';
END IF;
END;
Форматирование сообщений
Вы можете подставлять переменные прямо в текст сообщения:
DECLARE
var1 TEXT := 'Данные';
var2 INTEGER := 42;
BEGIN
RAISE EXCEPTION 'Ошибка при обработке % с ID %', var1, var2;
END;
Вывод: Ошибка при обработке Данные с ID 42.
Пример: валидация данных
Представьте, что у вас есть процедура, которая принимает возраст человека. Если возраст отрицательный, логично вызвать ошибку:
CREATE OR REPLACE FUNCTION validate_age(age INTEGER)
RETURNS VOID AS $$
BEGIN
IF age < 0 THEN
RAISE EXCEPTION 'Возраст не может быть отрицательным: %', age;
END IF;
END;
$$ LANGUAGE plpgsql;
-- Вызов функции
SELECT validate_age(-5); -- Вызовет ошибку
Информирование с RAISE NOTICE
Если RAISE EXCEPTION — это сирена, то RAISE NOTICE — это дружеское похлопывание по плечу. С помощью этого уровня можно добавлять комментарии для понимания того, что происходит внутри функции.
Когда использовать RAISE NOTICE:
- Вывод отладочной информации (например, текущее состояние переменных).
- Сообщение о начале выполнения этапа или результата вычислений.
Пример: информационные сообщения
CREATE OR REPLACE FUNCTION calculate_discount(price NUMERIC, discount_rate NUMERIC)
RETURNS NUMERIC AS $$
DECLARE
final_price NUMERIC;
BEGIN
RAISE NOTICE 'Цена перед скидкой: %', price;
RAISE NOTICE 'Ставка скидки: %', discount_rate;
final_price := price - (price * discount_rate);
RAISE NOTICE 'Итоговая цена: %', final_price;
RETURN final_price;
END;
$$ LANGUAGE plpgsql;
-- Вызов функции
SELECT calculate_discount(100, 0.2);
-- Выводит:
-- NOTICE: Цена перед скидкой: 100
-- NOTICE: Ставка скидки: 0.2
-- NOTICE: Итоговая цена: 80
Практическое применение: планирование и логирование
Допустим, у вас есть сложная процедура обработки данных, и вы хотите знать, на каком этапе она сейчас находится:
CREATE OR REPLACE FUNCTION process_data_step_by_step()
RETURNS VOID AS $$
BEGIN
RAISE NOTICE 'Шаг 1: Подготовка данных';
-- Ваша логика для первого этапа
RAISE NOTICE 'Шаг 2: Валидация данных';
-- Ваша логика для второго этапа
RAISE NOTICE 'Шаг 3: Сохранение данных';
-- Ваша логика для третьего этапа
END;
$$ LANGUAGE plpgsql;
-- Вызов функции
SELECT process_data_step_by_step();
-- В логах отобразится пошаговое выполнение
Приведём ещё один пример. Давайте представим магазин, который предлагает скидки только для заказов выше определенной суммы:
CREATE OR REPLACE FUNCTION apply_discount(order_amount NUMERIC)
RETURNS NUMERIC AS $$
BEGIN
IF order_amount < 50 THEN
RAISE EXCEPTION 'Ошибка: сумма заказа должна быть не менее 50, текущая сумма: %', order_amount;
END IF;
RETURN order_amount * 0.9; -- Применяем 10% скидку
END;
$$ LANGUAGE plpgsql;
-- Вызов функции
SELECT apply_discount(30); -- Ошибка: сумма заказа должна быть не менее 50
Типичные ошибки
Ошибка 1: логирование сообщений без параметров.
Выглядит неинформативно, особенно в больших процедурах:
RAISE NOTICE 'Ошибка произошла'; -- Почему? Где? Как?
Рекомендация: всегда добавляйте контекст:
RAISE NOTICE 'Ошибка в функции process_data(): входное значение: %', input_value;
Ошибка 2: использование RAISE EXCEPTION там, где достаточно RAISE WARNING.
Если вы перестараетесь с исключениями, код будет прерываться по любому поводу, что затруднит обработку данных.
Совет: используйте уровни логирования осознано. Для отладки выбирайте NOTICE, а для критических моментов — EXCEPTION.
Ошибка 3: отсутствие логирования совсем.
Это как пытаться найти ключи в темной комнате. Без логов отладка сложных процессов становится почти невозможной.
Совет: добавляйте RAISE NOTICE на ключевых этапах выполнения функции, особенно если она большая и сложная.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ