Try-catch-finally

Решение использует два вложенных блока и фиктивное исключение, которое вызывается при нормальном завершении внутреннего блока. Это дает возможность поместить операторы 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

Но не стоит забывать о накладных расходах на обработку исключений. Это задание - не более, чем просто упражнение.

GET DIAGNOSTICS

=> 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)