Решение использует два вложенных блока и фиктивное исключение, которое вызывается при нормальном завершении внутреннего блока. Это дает возможность поместить операторы finally в одно место - обработчик ошибок внешнего блока.
=> DO $$ BEGIN BEGIN RAISE NOTICE 'Операторы try'; -- RAISE NOTICE '...нет исключения'; EXCEPTION WHEN no_data_found THEN RAISE NOTICE 'Операторы catch'; END; RAISE SQLSTATE 'ALLOK'; EXCEPTION WHEN others THEN RAISE NOTICE 'Операторы finally'; IF SQLSTATE != 'ALLOK' THEN RAISE; END IF; END; $$;
NOTICE: Операторы try NOTICE: ...нет исключения NOTICE: Операторы finally DO
=> DO $$ BEGIN BEGIN RAISE NOTICE 'Операторы try'; -- RAISE NOTICE '...исключение, которое обрабатывается'; RAISE no_data_found; EXCEPTION WHEN no_data_found THEN RAISE NOTICE 'Операторы catch'; END; RAISE SQLSTATE 'ALLOK'; EXCEPTION WHEN others THEN RAISE NOTICE 'Операторы finally'; IF SQLSTATE != 'ALLOK' THEN RAISE; END IF; END; $$;
NOTICE: Операторы try NOTICE: ...исключение, которое обрабатывается NOTICE: Операторы catch NOTICE: Операторы finally DO
=> DO $$ BEGIN BEGIN RAISE NOTICE 'Операторы try'; -- RAISE NOTICE '...исключение, которое не обрабатывается'; RAISE division_by_zero; EXCEPTION WHEN no_data_found THEN RAISE NOTICE 'Операторы catch'; END; RAISE SQLSTATE 'ALLOK'; EXCEPTION WHEN others THEN RAISE NOTICE 'Операторы finally'; IF SQLSTATE != 'ALLOK' THEN RAISE; END IF; END; $$;
NOTICE: Операторы try NOTICE: ...исключение, которое не обрабатывается NOTICE: Операторы finally ERROR: division_by_zero CONTEXT: PL/pgSQL function inline_code_block line 7 at RAISE
Но не стоит забывать о накладных расходах на обработку исключений. Это задание - не более, чем просто упражнение.
=> DO $$ DECLARE ctx text; BEGIN RAISE division_by_zero; -- line 5 EXCEPTION WHEN others THEN GET STACKED DIAGNOSTICS ctx = pg_exception_context; RAISE NOTICE E'stacked =\n%', ctx; GET CURRENT DIAGNOSTICS ctx = pg_context; -- line 10 RAISE NOTICE E'current =\n%', ctx; END; $$;
NOTICE: stacked = PL/pgSQL function inline_code_block line 5 at RAISE NOTICE: current = PL/pgSQL function inline_code_block line 10 at GET DIAGNOSTICS DO
GET STACKED DIAGNOSTICS дает стек вызовов, приведший к ошибке.
GET [CURRENT] DIAGNOSTICS дает текущий стек вызовов.
Собственно функция:
=> CREATE FUNCTION getstack() RETURNS text[] AS $$ DECLARE ctx text; BEGIN GET DIAGNOSTICS ctx = pg_context; RETURN (regexp_split_to_array(ctx, E'\n'))[2:]; END; $$ VOLATILE LANGUAGE plpgsql;
CREATE FUNCTION
Чтобы проверить ее работу, создадим несколько функций, которые вызывают друг друга:
=> CREATE FUNCTION f1() RETURNS integer AS $$ BEGIN RETURN f2(); END; $$ VOLATILE LANGUAGE plpgsql;
CREATE FUNCTION
=> CREATE FUNCTION f2() RETURNS integer AS $$ BEGIN RETURN f3(); END; $$ VOLATILE LANGUAGE plpgsql;
CREATE FUNCTION
=> CREATE FUNCTION f3() RETURNS integer AS $$ BEGIN RAISE NOTICE '%', getstack(); RETURN 0; END; $$ VOLATILE LANGUAGE plpgsql;
CREATE FUNCTION
=> SELECT f1();
NOTICE: {"PL/pgSQL function f3() line 3 at RAISE","PL/pgSQL function f2() line 3 at RETURN","PL/pgSQL function f1() line 3 at RETURN"} f1 ---- 0 (1 row)