Сохранить исключение PL/SQL и вызвать его позже?

У меня есть процедура PL/SQL (в базе данных Oracle 12c), которая пытается вставить некоторые данные. Если это не удается, он должен проверить таблицу, чтобы узнать, можно ли найти там какую-либо информацию, которая поможет решить проблему. Если он находит информацию, все в порядке, если нет, он должен повторно вызвать ошибку.

Это мой код:

BEGIN
  -- Try to insert some data.
  INSERT INTO table VALUES x;
EXCEPTION
  WHEN OTHERS THEN
    BEGIN
      -- Check a table to fins some info to help solve the problem.
      -- If we find a row here, we can fix it.
      -- If not, we should reraise the error.
      SELECT * INTO y FROM table WHERE a = b;
      -- Do some more stuff here to fix the problem.
    EXCEPTION
      WHEN NO_DATA_FOUND THEN
        -- We could not find anything in the table,
        -- so we could not handle the situation.
        -- Reraise the error.
        RAISE;
    END;
END;

Проблема здесь в том, что оператор RAISE; вызывает самое последнее исключение, то есть NO_DATA_FOUND, которое выдало оператор SELECT. Исходное исключение из INSERT находится ниже в стеке, но не наверху.

Можно ли как-то "спасти" ошибку от INSERT и переподнять? Или я могу запустить SELECT INTO, который не выдает ошибку, если ничего не находит? Моя цель здесь - повторно вызвать исходное исключение INSERT без каких-либо следов исключения NO_DATA_FOUND.

EDIT: См. комментарии о том, почему это не дубликат.


person Anders    schedule 12.11.2015    source источник
comment
Что может пойти не так во вставке и как потом исправить. Ты не можешь проверить, прежде чем что-то пойдет не так?   -  person Rene    schedule 12.11.2015
comment
Я пытался избежать большого контекста в вопросе, чтобы сосредоточиться на проблеме. Что я хочу сделать, так это вести журнал, когда нарушаются определенные ограничения внешнего ключа, и создавать собственные сообщения об ошибках, если это произойдет. Пользовательские сообщения об ошибках поступают из таблицы, и я выполняю поиск ограничения внешнего ключа, которое было нарушено.   -  person Anders    schedule 12.11.2015
comment
Возможный дубликат Как повторно поднять pl/ sql в блоке обработки исключений?   -  person Stawros    schedule 12.11.2015
comment
Спасибо за внимание к моему вопросу, @Stawros! Это не дубликат. Решение вопроса, которое вы предлагаете, - просто использовать RAISE. Я уже делаю это, но я хочу вызвать исходную ошибку, а не ту, которая возникла в блоке исключений.   -  person Anders    schedule 12.11.2015
comment
Вы всегда можете повторить попытку вставки в конце, чтобы она не удалась во второй раз.   -  person Rene    schedule 12.11.2015


Ответы (2)


Вытащите оператор raise из блока, пытаясь решить проблему. Если вам нужно повторно поднять контингент до отказа исправления, установите флаг во внутреннем обработчике исключений и выполняйте raise только тогда, когда этот флаг истинен.

Как код:

DECLARE
   b_reraise BOOLEAN := FALSE;
BEGIN
  -- Try to insert some data.
  INSERT INTO table VALUES x;
EXCEPTION
  WHEN OTHERS THEN
    BEGIN
      -- Check a table to fins some info to help solve the problem.
      -- If we find a row here, we can fix it.
      -- If not, we should reraise the error.
      SELECT * INTO y FROM table WHERE a = b;
      -- Do some more stuff here to fix the problem.
    EXCEPTION
      WHEN NO_DATA_FOUND THEN
        -- We could not find anything in the table,
        -- so we could not handle the situation.
        -- Reraise the error.
        b_reraise := TRUE;
    END;

    IF b_reraise THEN
        RAISE;
    END IF;
END;
person collapsar    schedule 12.11.2015
comment
Кстати, это не работает, когда я помещаю часть во внешнем EXCEPTION в хранимую процедуру для повторного использования в других местах. Тогда я не могу вызвать RAISE из процедуры, так как она не находится в исключении. Но на данный момент это решает мою проблему и вопрос, который я задал, поэтому принятый ответ. - person Anders; 12.11.2015
comment
Вы всегда можете заключить свой код дополнительным BEGIN..EXCEPTION... END;, так что это должно быть возможно. - person Wernfried Domscheit; 12.11.2015

Может быть что-то вроде этого:

DECLARE
  l_sqlerrm  VARCHAR2(4000);
  l_sqlerrc  NUMBER;
  l_exc      EXCEPTION;
BEGIN
  -- some code with errors
EXCEPTION
  WHEN OTHERS THEN
    l_sqlerrm := SQLERRM;
    l_sqlerrc := SQLCODE;
    -- loggin
    INSERT INTO my_log (code, text) VALUES (l_sqlerrc, l_sqlerrm);
    COMMIT;
    -- some your code "Check a table to fins some info to help solve the problem"
    -- some code to SELECT code INTO l_sqlerrc FROM my_log
    PRAGMA exception_init(l_exc, l_sqlerrc);
    RAISE l_exc;
END;
person hinotf    schedule 12.11.2015
comment
Второй аргумент PRAGMA exception_init не может быть переменной, поэтому я думаю, что этот подход не работает. Хотя идея интересная. - person Matthew McPeak; 12.11.2015
comment
@MatthewMcPeak хм ... но почему тогда это сработало? СОЗДАТЬ ТАБЛИЦУ t1 (ИДЕНТИФИКАЦИОННЫЙ НОМЕР ПЕРВИЧНОГО КЛЮЧА); вставить в t1 значения (1); совершить; ОБЪЯВИТЬ l_sqlerrm VARCHAR2(4000); l_sqlerrc ЧИСЛО; l_tmp ЧИСЛО; l_exc ИСКЛЮЧЕНИЕ; НАЧНИТЕ ВСТАВИТЬ В t1 ЗНАЧЕНИЯ (1); СОВЕРШИТЬ; ИСКЛЮЧЕНИЕ, КОГДА ДРУГИЕ, ТОГДА l_sqlerrm := SQLERRM; l_sqlerrc := SQLCODE; PRAGMA exception_init(l_exc, l_sqlerrc); BEGIN SELECT id INTO l_tmp FROM t1 WHERE 1 = 2; ИСКЛЮЧЕНИЕ, КОГДА NO_DATA_FOUND THEN RAISE l_exc; КОНЕЦ; КОНЕЦ; - person hinotf; 18.11.2015
comment
Попытки использовать переменную во втором аргументе могут привести к ошибке PLS-00702. Прочитайте документацию Oracle по этой ошибке - ясно (из этого единственного источника), что они не ожидают и не поддерживают переменную в этой позиции, даже если она работает в вашем примере. Я бы не стал использовать это для производственного кода без предварительной регистрации SR в Oracle, чтобы убедиться, что он поддерживается. Потому что вещи, которые работают, но не поддерживаются, могут не работать в более поздних версиях. - person Matthew McPeak; 18.11.2015
comment
Я также хотел бы отметить, что ваш код имеет точно такую ​​​​же функцию без PRAGMA. - person Matthew McPeak; 18.11.2015