Як отримати контекст винятку для піднятого вручну винятку в PL / pgSQL?


11

У Postgres ми отримуємо "стек стеження" винятків за допомогою цього коду:

EXCEPTION WHEN others THEN
    GET STACKED DIAGNOSTICS v_error_stack = PG_EXCEPTION_CONTEXT;

Це чудово працює для "природних" винятків, але якщо ми створимо виняток, використовуючи

RAISE EXCEPTION 'This is an error!';

... тоді сліду стека немає. Згідно запису списку розсилки , це може бути навмисним, хоча я не можу на все життя зрозуміти, чому саме. Це змушує мене знайти інший спосіб кинути виняток, крім використання RAISE. Я просто пропускаю щось очевидне? У когось є хитрість для цього? Чи є виняток, який я можу змусити кинути Postgres, який би містив рядок на мій вибір, щоб я отримав не лише свою рядок у повідомленні про помилку, але й повний слід стека?

Ось повний приклад:

CREATE OR REPLACE FUNCTION error_test() RETURNS json AS $$
DECLARE
    v_error_stack text;
BEGIN

    -- Comment this out to see how a "normal" exception will give you the stack trace
    RAISE EXCEPTION 'This exception will not get a stack trace';

    -- This will give a divide by zero error, complete with stack trace
    SELECT 1/0;

-- In case of any exception, wrap it in error object and send it back as json
EXCEPTION WHEN others THEN

    -- If the exception we're catching is one that Postgres threw,
    -- like a divide by zero error, then this will get the full
    -- stack trace of the place where the exception was thrown.
    -- However, since we are catching an exception we raised manually
    -- using RAISE EXCEPTION, there is no context/stack trace!
    GET STACKED DIAGNOSTICS v_error_stack = PG_EXCEPTION_CONTEXT;

    RAISE WARNING 'The stack trace of the error is: "%"', v_error_stack;

    return to_json(v_error_stack);
END;
$$ LANGUAGE plpgsql;

Тут може бути хорошою ідеєю показати простий приклад.
Крейг Рінгер

Хороший момент @CraigRinger. Готово!
Тайтай

Це не є самодостатнім. Що error_info? Схоже на користувацький тип.
Крейг Рінгер

Вибачте - думав, що ви просто хотіли загального контексту. Я видалив сторонні речі.
Тайтай

Відповіді:


9

Здається, така поведінка є дизайном.

У src/pl/plpgsql/src/pl_exec.cконтексті зворотного зв'язку з помилкою явно перевіряється, чи викликається він у контексті RAISEоператора PL / PgSQL і, якщо так, пропускає випромінення контексту помилки:

/*
 * error context callback to let us supply a call-stack traceback
 */
static void
plpgsql_exec_error_callback(void *arg)
{
        PLpgSQL_execstate *estate = (PLpgSQL_execstate *) arg;

        /* if we are doing RAISE, don't report its location */
        if (estate->err_text == raise_skip_msg)
                return;

Я не можу знайти конкретного посилання на те , чому це так.

Внутрішньо на сервері контекстний стек генерується при обробці error_context_stack, яка є ланцюжком зворотного виклику, який додає інформацію до списку при виклику.

Коли PL / PgSQL входить до функції, він додає елемент до стеку зворотного виклику контексту помилок. Залишаючи функцію, він вилучає елемент із цього стеку.

Якщо сервери PostgreSQL повідомляють про помилки, як-от ereportабо elogвикликаються, він викликає зворотний зв'язок контексту помилки. Але в PL / PgSQL, якщо він помічає, що його викликають з RAISEйого зворотних викликів, навмисно нічого не роблять.

Враховуючи це, я не бачу жодного способу досягти того, що ви хочете, без виправлення PostgreSQL. Я пропоную розміщувати пошту на загальному pgsql із запитанням, чому RAISEне надає контекст помилки тепер, коли PL / PgSQL має GET STACKED DIAGNOSTICSвикористовувати її.

(BTW, контекст винятку не є стеком стека як таким. Він схожий на один, оскільки PL / PgSQL додає кожен виклик функції до стеку, але він також використовується для інших деталей на сервері.)


Дуже дякую Крейгу за швидку і грунтовну відповідь. Мені це здається дивним і, безумовно, суперечить моїм очікуванням. Корисність RAISEзменшується за допомогою цієї перевірки. Я їм напишу.
Тайтай

@Taytay Будь ласка, включіть сюди посилання на своє запитання, але переконайтеся, що ваша пошта є повною та її можна зрозуміти, не переходячи за посиланням; багато людей ігнорують повідомлення, що містять лише посилання або в основному посилання. Якщо ви отримаєте можливість перейти посилання на свою публікацію в коментарях тут, через archives.postgresql.org, що було б надзвичайно приємно допомогти іншим людям пізніше.
Крейг Рінгер

Дякую Крейгу. Хороша порада. Я створив тут нитку: postgresql.org/message-id/… На сьогоднішній день вони шукають хорошого вирішення проблеми.
Тайтай

6

Ви можете обійти це обмеження і змусити plpgsql випромінювати контекст помилок за бажанням, зателефонувавши до іншої функції, яка викликає (попередження, повідомлення, ...) помилку для вас.

Я розкрив рішення для цього кілька років тому - в одному з моїх перших дописів тут на dba.SE :

-- helper function to raise an exception with CONTEXT
CREATE OR REPLACE FUNCTION f_raise(_lvl text = 'EXCEPTION'
                                  ,_msg text = 'Default error msg.')
  RETURNS void AS
$func$
BEGIN
   CASE upper(_lvl)
      WHEN 'EXCEPTION' THEN RAISE EXCEPTION '%', _msg;
      WHEN 'WARNING'   THEN RAISE WARNING   '%', _msg;
      WHEN 'NOTICE'    THEN RAISE NOTICE    '%', _msg;
      WHEN 'DEBUG'     THEN RAISE DEBUG     '%', _msg;
      WHEN 'LOG'       THEN RAISE LOG       '%', _msg;
      WHEN 'INFO'      THEN RAISE INFO      '%', _msg;
      ELSE RAISE EXCEPTION 'f_raise(): unexpected raise-level: "%"', _lvl;
   END CASE;
END
$func$  LANGUAGE plpgsql STRICT;

Деталі:

Я розширив ваш розміщений тестовий випадок, щоб продемонструвати, що він працює в Postgres 9.3:

SQL Fiddle.


Дякую тобі, Ервіне! Як не дивно, я насправді експериментував з вашим рішенням перед публікацією, але, мабуть, зробив щось не так, і я не отримав очікуваного контексту. Тепер, коли я побачив цю загадку (дякую, що мені це також показали), я ще раз знім її!
Тайтай

Чудово зроблено; це не повинно бути необхідним, але схоже, що це зробить трюк.
Крейг Рінгер

@CraigRinger: Оскільки винятки повинні бути, ну і виняток , мінімальний вплив на продуктивність також не має значення. У нас є всі варіанти таким чином.
Ервін Брандстеттер

Цілком погоджуюсь, я просто хотів би побачити, що потреба у вирішенні проблеми піде в якийсь момент.
Крейг Рінгер

@CraigRinger: Це правда. Якщо це не відбудеться найближчим часом, ми можемо запропонувати цей спосіб вирішення в посібнику ...
Ервін Брандштеттер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.