Исключительные ситуации, определяемые пользователем




Кроме встроенных могут быть использованы собственные исключительные ситуации, имена которых необходимо описать в разделе DECLARE блока PL/SQL (например, err_stavka EXCEPTION). В разделе EXCEPTION блока должен быть описан соответствующий обработчик исключительной ситуации, например

WHEN err_stavka THEN

SELECT error_number,error_text INTO errnum,errtext FROM usererr

WHERE errtext LIKE 'Число ставок должно находиться%';

RAISE_APPLICATION_ERROR(errnum,errtext);

В теле основной программы определяемые пользователем ошибки обычно проверяются с помощью операторов условия (IF…THEN). Для передачи управления обработчику пользовательской исключительной ситуации в случае обнаружения ошибки используется оператор RAISE имя_пользовательского_исключения Например

IF:new.stavka NOT BETWEEN 0.25 AND 100 THEN

RAISE err_stavka;

END IF;

Обработчик OTHERS

Если исключительная ситуация не обрабатывается явным образом в блоке и для ее перехвата не используется обработчик OTHERS, то PL/SQL отменяет выполняемые блоком транзакции и возвращает необработанную исключительную ситуацию обратно в вызывающую среду.
Обработчик особых ситуаций OTHERS описывается последним в программе (блоке) для перехвата всех исключительных ситуаций, которые не были описаны в этой программе (блоке). Он может иметь вид

WHEN OTHERS THEN

RAISE_APPLICATION_ERROR(-20011,'Какая-то другая ошибка');

 

Преимущества исключений

 

Использование исключений для обработки ошибок имеет несколько

преимуществ. Не имея таких средств, вы должны были бы проверять

на ошибки выполнения при каждом выполнении команды, как

показывает следующий пример:

 

BEGIN

SELECT...

-- проверить на ошибку 'no data found'

SELECT...

-- проверить на ошибку 'no data found'

SELECT...

-- проверить на ошибку 'no data found'

END;

 

Обработка ошибок при этом не является ни четко отделенной от

нормальной обработки, ни концептуально осмысленной. Если вы

пренебрежете проверкой в одном месте, ошибка пройдет

необнаруженной и может вызвать другие, внешне не связанные

ошибки.

 

При помощи исключений вы можете обрабатывать ошибки, не кодируя

многочисленных проверок, как показывает следующий пример:

 

BEGIN

SELECT...

SELECT...

SELECT...

EXCEPTION

WHEN NO_DATA_FOUND THEN -- этот обработчик перехватывает

-- все ошибки 'no data found'

END;

 

Заметьте, как исключения улучшают читабельность, позволяя вам

изолировать программы обработки ошибок. Основной алгоритм не

затемняется алгоритмами восстановления от ошибок.

 

Исключения также повышают надежность. Вам нет необходимости

беспокоиться о проверке ошибки в любой точке, где она может

возникнуть. Просто добавьте соответствующий обработчик

исключений в ваш блок PL/SQL. Если данное исключение когда-либо

будет возбуждено в этом блоке (или любом его подблоке), у вас

есть гарантия, что оно будет обработано.

 

Предопределенные исключения

 

Внутреннее исключение возбуждается неявно всякий раз, когда ваша

программа PL/SQL нарушает правило ORACLE или превышает

установленный системой лимит. Каждая ошибка ORACLE имеет номер,

однако исключения должны обрабатываться по их именам. Поэтому

PL/SQL внутренне определяет некоторые распространенные ошибки

ORACLE как исключения. Например, предопределенное исключение

NO_DATA_FOUND возбуждается, когда предложение SELECT INTO не

возвращает ни одной строки.

 

Для обработки других ошибок ORACLE вы можете использовать общий

обработчик OTHERS. Функции сообщений об ошибках SQLCODE и

SQLERRM особенно полезны в обработчике OTHERS, так как они

возвращают код ошибки ORACLE и текст сообщения об ошибке.

Альтернативно, вы можете использовать прагму EXCEPTION_INIT,

чтобы ассоциировать имена исключений с номерами ошибок ORACLE.

Подробнее об этом см. в разделе раздел "Использование

EXCEPTION_INIT" ниже в этой главе.

 

PL/SQL объявляет предопределенные исключения глобально, в пакете

STANDARD, который определяет окружение PL/SQL. Поэтому вы не

обязаны объявлять их сами. Вы можете писать обработчики для

предопределенных исключений, используя имена, приведенные в

следующей таблице. Эта таблица показывает также коды ошибок

ORACLE и значения возврата функции SQLCODE.

 

Имя исключения Ошибка ORACLE Код SQLCODE

----------------------------------------------------------------

CURSOR_ALREADY_OPEN ORA-06511 -6511

DUP_VAL_ON_INDEX ORA-00001 -1

INVALID_CURSOR ORA-01001 -1001

INVALID_NUMBER ORA-01722 -1722

LOGIN_DENIED ORA-01017 -1017

NO_DATA_FOUND ORA-01403 +100

NOT_LOGGED_ON ORA-01012 -1012

PROGRAM_ERROR ORA-06501 -6501

STORAGE_ERROR ORA-06500 -6500

TIMEOUT_ON_RESOURCE ORA-00051 -51

TOO_MANY_ROWS ORA-01422 -1422

TRANSACTION_BACKED_OUT ORA-00061 -61

VALUE_ERROR ORA-06502 -6502

ZERO_DIVIDE ORA-01476 -1476

 

 

Ниже приведены краткие описания предопределенных исключений:

 

CURSOR_ALREADY_OPEN

 

Возбуждается при попытке открыть уже открытый курсор. Вы должны

закрыть (CLOSE) курсор, прежде чем открывать его повторно.

 

Курсорный цикл FOR автоматически открывает свой курсор. Поэтому

вы не можете войти в такой цикл, если данный курсор уже открыт.

Нельзя также явно открывать курсор внутри цикла FOR.

 

DUP_VAL_ON_INDEX

 

Возбуждается, когда операция INSERT или UPDATE пытается создать

повторяющееся значение в столбце, ограниченном опцией UNIQUE.

 

INVALID_CURSOR

 

Возбуждается, когда вызов PL/SQL специфицирует некорректный

курсор (например, при попытке закрыть неоткрытый курсор).

 

INVALID_NUMBER

 

Возбуждается в предложении SQL, когда преобразование символьной

строки в число сбивается из-за того, что строка не содержит

правильного представления числа. Например, следующее

предложение INSERT возбудит исключение INVALID_NUMBER, когда

ORACLE попытается преобразовать 'HALL' в число:

 

INSERT INTO emp (empno,ename,deptno) VALUES ('HALL', 7888, 20);

 

В процедурных предложениях вместо этого исключения возбуждается

VALUE_ERROR.

 

LOGIN_DENIED

 

Возбуждается при некорректном имени пользователя или пароле при

попытке подключения к ORACLE.

 

NO_DATA_FOUND

 

Возбуждается, когда предложение SELECT INTO не возвращает ни

одной строки, или при обращении к неинициализированной строке

таблицы PL/SQL.

 

Групповые функции SQL, такие как AVG или SUM, ВСЕГДА возвращают

значение, даже если это значение есть NULL. Поэтому предложение

SELECT INTO, вызывающее групповую функцию, никогда не возбудит

исключение NO_DATA_FOUND.

 

Поскольку NO_DATA_FOUND возбуждается, когда предложение SELECT

INTO не возвращает строк, вы можете проверять значение

SQL%NOTFOUND только в обработчике исключений. Однако, значение

атрибута имя_курсора%NOTFOUND можно проверять после каждой

операции FETCH. От операции FETCH ожидается, что в конце концов

она не сможет возвратить очередную строку, так что, когда это

происходит, никакого исключения не возбуждается.

 

NOT_LOGGED_ON

 

Возбуждается, когда ваша программа PL/SQL выдает вызов ORACLE,

не будучи подключена к ORACLE.

 

PROGRAM_ERROR

 

Возбуждается, когда PL/SQL встретился с внутренней проблемой.

 

STORAGE_ERROR

 

Возбуждается, когда PL/SQL исчерпал доступную память, или когда

память запорчена.

 

TIMEOUT_ON_RESOURCE

 

Возбуждается при возникновении таймаута, когда ORACLE ожидает

ресурса.

 

TOO_MANY_ROWS

 

Возбуждается, когда предложение SELECT INTO возвращает больше

одной строки.

 

TRANSACTION_BACKED_OUT

 

Обычно возбуждается, если удаленная часть транзакции была

подвергнута неявному или явному откату. Причиной может быть

несогласованность данных ORACLE в каких-нибудь узлах. В таких

случаях выдайте ROLLBACK, а затем повторите транзакцию.

Смотрите раздел "Повторение транзакции" ниже в этой главе.

 

VALUE_ERROR

 

Возбуждается при возникновении арифметической ошибки, ошибки

преобразования, ошибки усечения или ошибки ограничения.

Например, VALUE_ERROR возбуждается при усечении строкового

значения, присваиваемого переменной PL/SQL. (Однако при

усечении строкового значения, присваиваемого хост-переменной,

никакого исключения не возбуждается.)

 

В процедурных предложениях VALUE_ERROR возбуждается при ошибке

преобразования символьной строки в число. Например, следующее

предложение присваивания вызовет RAISE_ERROR, когда PL/SQL

попытается преобразовать 'HALL' в числовое значение:

 

DECLARE

my_empno NUMBER(4);

my_ename CHAR(10);

BEGIN

my_empno:= 'HALL';

...

 

В предложениях SQL в таких случаях возбуждается INVALID_NUMBER.

 

ZERO_DIVIDE

 

Возбуждается при попытке деления числа на 0.

 

 

Пользовательские исключения

 

PL/SQL позволяет вам определять ваши собственные исключения. В

отличие от внутренних исключений, пользовательские исключения

должны быть объявлены и должны явно возбуждаться предложениями

RAISE.

 

Объявление исключений

---------------------

 

Вы можете объявлять исключения только в декларативной части

блока, подпрограммы или пакета PL/SQL. Вы объявляете

исключение, вводя его имя, за которым следует ключевое слово

EXCEPTION. В следующем примере объявляется исключение с именем

past_due:

 

DECLARE

past_due EXCEPTION;

acct_num NUMBER(5);

BEGIN

...

 

Объявления исключений выглядят так же, как и объявления

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

ошибки, но не объект. В отличие от переменных, исключениям

нельзя присваивать значений, и они не могут использоваться в

предложениях SQL. Однако к исключениям применимы те же правила

сферы, что и к переменным.

 

Правила сферы

-------------

 

Нельзя объявлять исключение дважды в одном и том же блоке.

Однако вы можете объявить то же самое исключение в двух

различных блоках.

 

Исключения, объявленные в блоке, считаются локальными в этом

блоке и глобальными во всех его подблоках. Поскольку блок может

обращаться лишь к локальным или глобальным исключениям,

окружающие блоки не могут ссылаться на исключения, объявленные в

подблоках.

 

Вы можете переобъявить глобальное исключение в подблоке. В

таком случае локальное объявление имеет преимущество, и подблок

не может обращаться к глобальному исключению (если только оно не

было объявлено в помеченном блоке; в этом случае можно

использовать синтаксис метка_блока.имя_исключения).

 

Следующий пример иллюстрирует правила сферы для исключений:

 

DECLARE

past_due EXCEPTION;

acct_num NUMBER;

BEGIN

...

---------------- начало подблока -------------------------

DECLARE

past_due EXCEPTION; -- имеет преимущество в подблоке

acct_num NUMBER;

BEGIN

...

IF... THEN

RAISE past_due; -- это не обрабатывается

END IF;

...

END;

---------------- конец подблока --------------------------

...

EXCEPTION

WHEN past_due THEN -- это не относится к исключению,

... -- возбуждаемому в подблоке

END;

 

Здесь окружающий блок не обрабатывает исключение, возбуждаемое в

подблоке, потому что объявление past_due в подблоке имеет

преимущество. Два исключения past_due, хотя их имена одинаковы,

являются разными, точно так же, как различны две объявленные в

этом примере переменные acct_num. Поэтому предложение RAISE в

подблоке и фраза WHEN в окружающем блоке относятся к РАЗНЫМ

исключениям.

 

Чтобы заставить окружающий блок обрабатывать исключение, вы

должны определить в нем обработчик OTHERS, либо удалить

объявление исключения из подблока.

 

 

Использование EXCEPTION_INIT

----------------------------

 

Для обработки непоименованных внутренних исключений вы должны

использовать обработчик OTHER либо прагму EXCEPTION_INIT.

ПРАГМА - это директива (указание) компилятору. Прагмы

(называемые также ПСЕВДОИНСТРУКЦИЯМИ) обрабатываются во время

компиляции, а не во время выполнения. Они не изменяют смысла

программы, а лишь поставляют информацию компилятору. Например,

в языке Ada следующая предопределенная прагма указывает

компилятору, что необходимо оптимизировать использование памяти:

 

pragma OPTIMIZE(space);

 

В PL/SQL, предопределенная прагма EXCEPTION_INIT сообщает

компилятору имя исключения, которое вы ассоциируете с конкретным

кодом ошибки ORACLE. Это позволяет вам обращаться к любому

внутреннему исключению по имени, написав для него специальный

обработчик.

 

Вы кодируете прагму EXCEPTION_INIT в декларативной части блока,

подпрограммы или пакета PL/SQL, используя следующий синтаксис:

 

PRAGMA EXCEPTION_INIT(имя_исключения, код_ошибки_ORACLE);

 

Здесь имя_исключения - это имя исключения, ранее уже

объявленного в этом блоке. Прагма должна появиться в той же

декларативной части, что и соответствующее исключение, как

показано в следующем примере:

 

DECLARE

insufficient_privileges EXCEPTION;

PRAGMA EXCEPTION_INIT(insufficient_privileges, -1031);

--------------------------------------------------------

-- ORACLE возвращает код ошибки -1031, если, например,

-- вы пытаетесь обновить таблицу, для которой имеете

-- лишь полномочия SELECT.

--------------------------------------------------------

BEGIN

...

EXCEPTION

WHEN insufficient_privileges THEN

-- обработать ошибку

...

END;

 

Использование raise_application_error

-------------------------------------

 

Пакет с именем DBMS_STANDARD, входящий в состав Процедурного

расширения базы данных, предоставляет средства языка, которые

помогают вашему приложению взаимодействовать с ORACLE. Этот

пакет включает процедуру raise_application_error, которая

позволяет вам выдавать определенные вами сообщения об ошибках из

хранимой подпрограммы или триггера базы данных.

 

Вызывающее приложение получает при этом исключение PL/SQL,

которое оно может обработать с помощью функций сообщений об

ошибках SQLCODE и SQLERRM. Более того, оно может использовать

прагму EXCEPTION_INIT, чтобы сопоставить специфические номера

ошибок, которые возвращает raise_application_error, своим

собственным исключениям. Для более подробной информации о

процедуре raise_application_error обратитесь к разделу "Пакеты,

специфичные для продуктов" в главе 7.

 

Переобъявление предопределенных исключений

------------------------------------------

 

Вспомним, что PL/SQL объявляет свои предопределенные исключения

глобально, в пакете STANDARD, так что вам не требуется объявлять

их самим. Однако переобъявление предопределенного исключения не

приведет к ошибке, так как ваше локальное объявление перекрывает

глобальное внутреннее объявление. Например, если вы объявите

исключение с именем invalid_number, а затем PL/SQL внутренне

возбудит предопределенное исключение INVALID_NUMBER, то

обработчик, написанный вами для вашего invalid_number, не увидит

внутреннего исключения. В таком случае вы должны обращаться к

предопределенному исключению с помощью квалифицированной ссылки,

как показывает следующий пример:

 

...

EXCEPTION

WHEN invalid_number OR STANDARD.INVALID_NUMBER THEN

-- обработать ошибку

...

END;

 

 

Как возбуждаются исключения

 

Внутренние исключения возбуждаются неявно исполнительной

системой, как и те пользовательские исключения, которые вы

ассоциировали с кодами ошибок ORACLE с помощью прагмы

EXCEPTION_INIT. Однако остальные пользовательские исключения

должны возбуждаться явно, посредством предложений RAISE.

 

 

Использование предложения RAISE

-------------------------------

 

Блоки и подпрограммы PL/SQL должны явно возбуждать исключение

лишь в том случае, когда ошибка делает невозможным или

нежелательным продолжение обработки. Вы можете закодировать

предложение RAISE для данного исключения в любом месте сферы

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

блоке PL/SQL пользовательское исключение с именем out_of_stock:

 

DECLARE

out_of_stock EXCEPTION;

number_on_hand NUMBER(4);

BEGIN

...

IF number_on_hand < 1 THEN

RAISE out_of_stock;

END IF;

...

EXCEPTION

WHEN out_of_stock THEN

-- обработать ошибку

END;

 

Вы можете также явно возбуждать предопределенное исключение, как

показывает следующий пример:

 

RAISE INVALID_NUMBER;

 

Это позволяет вам использовать обработчики, созданные для

предопределенных исключений, для обработки других ошибок, как

показывает следующий пример:

 

DECLARE

acct_type NUMBER;

...

BEGIN

...

IF acct_type NOT IN (1, 2, 3) THEN

RAISE INVALID_NUMBER;

END IF;

...

EXCEPTION

WHEN INVALID_NUMBER THEN

ROLLBACK;

...

END;

 

Как распространяются исключения

Что происходит, если в текущем блоке нет обработчика исключений

для возбужденного исключения? В этом случае блок завершается, а

исключение ПРОДВИГАЕТСЯ в окружающий блок. Теперь окружающий

блок становится текущим, а исключение воспроизводит себя в нем.

Если обработчик исключения не находится и в этом блоке, процесс

поиска повторяется. Если текущий блок не имеет окружающего

блока, PL/SQL возвращает ошибку "необрабатываемое исключение" в

хост-окружение.

Повторное возбуждение исключения-------------------------------- Иногда вам требуется повторно возбудить исключение, т.е. сначала обработать его локально, а затем заставить его продвинуться в окружающий блок. Например, вы можете захотеть отменить транзакцию в текущем блоке, а потом зарегистрировать ошибку в окружающем блоке. Чтобы повторно возбудить исключение, поместите предложение RAISE в локальный обработчик этого исключения, как показано в следующем примере: DECLARE out_of_balance EXCEPTION; BEGIN... ---------------- начало подблока ------------------------- BEGIN... IF... THEN RAISE out_of_balance; -- возбудить исключение END IF;... EXCEPTION WHEN out_of_balance THEN -- обработать ошибку RAISE; -- повторно возбудить текущее исключение... END; ---------------- конец подблока -------------------------- EXCEPTION WHEN out_of_balance THEN -- обработать ошибку иным способом... END; Если опустить имя исключения в предложении RAISE (что допускается только в обработчике исключений), то подразумевается текущее исключение. Обработка возбуждаемых исключений Когда возбуждается исключение, нормальное исполнение вашего блока PL/SQL или подпрограммы останавливается, и управление передается на обработчик исключений этого блока или подпрограммы, что оформляется следующим образом:... EXCEPTION WHEN имя_исключения1 THEN -- обработчик ряд_предложений1 WHEN имя_исключения2 THEN -- другой обработчик ряд_предложений2... WHEN OTHERS THEN -- необязательный обработчик ряд_предложений3 END; Чтобы перехватывать возбуждаемые исключения, вы должны написать обработчики исключений. Каждый обработчик состоит, во-первых, из фразы WHEN, которая специфицирует имя исключения, и, во-вторых, из последовательности предложений, которые будут выполняться при возбуждении этого исключения. Эти предложения завершат исполнение блока или подпрограммы при исключении - управление больше не вернется в точку, где возникло исключение. Иными словами, вы не сможете возобновить работу с того места, где возникло исключение. Необязательный обработчик исключений OTHERS - всегда последний обработчик исключений в блоке; он действует как обработчик для всех исключений, не перечисленных персонально в этом блоке. Таким образом, блок или подпрограмма может содержать только один обработчик OTHERS. Рассмотрим следующий пример:... EXCEPTION WHEN... THEN -- обработать ошибку WHEN... THEN -- обработать ошибку WHEN... THEN -- обработать ошибку WHEN OTHERS THEN -- обработать все прочие ошибки END; Использование обработчика OTHERS гарантирует, что ни одно исключение не пройдет необработанным. Если вы хотите выполнять одну и ту же последовательность предложений для двух или более исключений, перечислите имена этих исключений в фразе WHEN, разделяя их ключевым словом OR, как показано ниже:... EXCEPTION WHEN over_limit OR under_limit OR VALUE_ERROR THEN -- обработать ошибку... END; Если будет возбуждено любое из перечисленных в фразе WHEN исключений, соответствующий обработчик получит управление. Включение ключевого слова OTHERS в список имен исключений фразы WHEN НЕ ДОПУСКАЕТСЯ. Слово OTHERS может появиться только само по себе. Вы можете иметь сколько угодно обработчиков исключений, а каждый обработчик может ассоциировать последовательность предложений с любым списком исключений. Однако любое имя исключения может появиться лишь один раз в части обработки исключений блока или подпрограммы PL/SQL. В обработчике исключений действуют обычные правила сферы видимости идентификаторов, определенные для переменных PL/SQL, так что обработчик может обращаться лишь к локальным и глобальным переменным. Однако, когда исключение возбуждается внутри курсорного цикла FOR, соответствующий курсор неявно закрывается перед вызовом обработчика. Поэтому значения явных атрибутов курсора НЕДОСТУПНЫв обработчике. Исключения, возбужденные в объявлениях-------------------------------------- Исключения могут также возбуждаться некорректными выражениями инициализации в объявлениях. Например, следующее объявление неявно возбуждает исключение VALUE_ERROR, потому что limit не может хранить числа, большие 999: DECLARE limit CONSTANT NUMBER(3):= 5000; -- возбуждает исключение VALUE_ERROR BEGIN... EXCEPTION WHEN VALUE_ERROR THEN -- не перехватит это исключение... END; Обработчики в текущем блоке не могут перехватывать исключений, возбужденных в объявлениях, потому что такое исключение НЕМЕДЛЕННО продвигается в окружающий блок. Исключения, возбужденные в обработчиках--------------------------------------- Лишь одно исключение в каждый момент времени может быть активно в части обработки исключений блока или подпрограммы. Так, исключение, возбужденное внутри обработчика, немедленно продвигается в окружающий блок, который просматривается на предмет обнаружения обработчика для вновь возбужденного исключения. С этого момента исключение продвигается обычным образом. Переходы в обработчик и из него------------------------------- Предложение GOTO нельзя использовать для перехода в обработчик исключений либо для перехода из обработчика исключений в текущий блок. Например, следующее предложение GOTO НЕЗАКОННО: DECLARE pe_ratio NUMBER(3,1); BEGIN DELETE FROM stats WHERE symbol = 'XYZ'; SELECT price / NVL(earnings, 0) INTO pe_ratio FROM stocks WHERE symbol = 'XYZ'; <> INSERT INTO stats (symbol, ratio) VALUES ('XYZ', pe_ratio); EXCEPTION WHEN ZERO_DIVIDE THEN pe_ratio:= 0; GOTO my_label; -- незаконный переход в текущий блок END; Однако предложение GOTO можно использовать для перехода из обработчика исключения в окружающий блок. Использование SQLCODE и SQLERRM------------------------------- В обработчике исключений можно использовать функции SQLCODE и SQLERRM, чтобы узнать, какая ошибка произошла и получить сообщение об ошибке. Для внутренне определенных исключений, SQLCODE возвращает номер ошибки ORACLE, передавшей управление обработчику. Этот номер отрицателен, исключая случай ошибки ORACLE "no data found", когда SQLCODE возвращает +100. SQLERRM возвращает сообщение, ассоциированное с возникшей ошибкой ORACLE. Это сообщение начинается с кода ошибки ORACLE. Для пользовательских исключений, SQLCODE возвращает +1, а SQLERRM возвращает сообщение User-Defined Exception если вы не использовали прагму EXCEPTION_INIT, чтобы ассоциировать ваше исключение с номером ошибки ORACLE; в этом случае SQLCODE возвращает этот номер ошибки, а SQLERRM возвращает соответствующее сообщение об ошибке. Заметим, что максимальная длина сообщения ORACLE об ошибке составляет 512 символов, включая код ошибки, вложенные сообщения и подстановки, такие как имена таблиц и столбцов. Если не возбуждено никакое исключение, то SQLCODE возвращает 0, а SQLERRM возвращает сообщение ORA-0000: normal, successful completion Вы можете передать функции SQLERRM номер ошибки; в этом случае SQLERRM возвратит сообщение, ассоциированное с этим номером ошибки. Номер ошибки, передаваемый SQLERRM, должен быть отрицателен. Нулевой код, передаваемый SQLERRM, всегда возвращает сообщение ORA-0000: normal, successful completion Передача SQLERRM положительного номера ошибки (за исключением +100) всегда возвратит сообщение User-Defined Exception а передача SQLERRM кода +100 возвратит сообщение ORA-01403: no data found В следующем примере, SQLERRM возвратит не то, что ожидалось, потому что передается положительное значение вместо отрицательного: DECLARE msg CHAR(100); BEGIN FOR num IN 1..9999 LOOP msg:= SQLERRM(num); -- надо задавать SQLERRM(-num) INSERT INTO errors VALUES (msg); END LOOP; END; Вы не можете использовать функции SQLCODE и SQLERRM непосредственно в предложениях SQL. Например, следующее предложение незаконно: INSERT INTO errors VALUES (SQLCODE, SQLERRM); Вместо этого вы должны присвоить значения этих функций локальным переменным, а затем использовать эти переменные в ваших предложениях SQL, как показывает следующий пример: DECLARE err_num NUMBER; err_msg CHAR(100); BEGIN... EXCEPTION... WHEN OTHERS THEN err_num:= SQLCODE; err_msg:= SUBSTR(SQLERRM, 1, 100); INSERT INTO errors VALUES (err_num, err_msg); END; Строковая функция SUBSTR() гарантирует, что возможное усечение при присваивании переменной err_msg не возбудит исключения VALUE_ERROR. Функции SQLCODE и SQLERRM особенно полезны в исключении OTHERS, потому что они позволяют установить, какое внутреннее исключение было возбуждено. Необработанные исключения------------------------- Как объяснялось выше, при невозможности найти обработчик для возбужденного исключения PL/SQL возвращает ошибку "необработанное исключение" в хост-окружение, которое определяет, что делать дальше. Например, в среде прекомпиляторов ORACLE выполняется откат всех изменений в базе данных, сделанных сбившимся предложением SQL или блоком PL/SQL. Необработанные исключения могут влиять на транзакции. Перед выполнением блока PL/SQL или хранимой подпрограммы ORACLE устанавливает неявную точку сохранения. Если блок или подпрограмма сбивается в результате необработанного исключения, ORACLE осуществляет откат к этой точке сохранения. Тем самым отменяется вся работа, проделанная блоком или подпрограммой. Необработанные исключения могут также влиять на подпрограммы. При успешном выходе из подпрограммы PL/SQL присваивает значения параметрам OUT. Однако, если вы выходите в результате необработанного исключения, значения параметрам OUT НЕ присваиваются. Кроме того, как уже сказано, если подпрограмма сбивается в результате необработанного исключения, ORACLE неявно отменяет ее работу. Однако, если подпрограмма выдала COMMIT до возбуждения необрабатываемого исключения, отменена будет лишь неподтвержденная часть работы. Вы можете избежать необрабатываемых исключений, кодируя обработчик OTHERS на самом верхнем уровне каждого блока PL/SQL и подпрограммы. ---------------- Полезные приемы В этом разделе вы узнаете два полезных приема: как продолжить работу после исключения и как повторить транзакцию. Продолжение работы после возбуждения исключения----------------------------------------------- Обработчик исключений позволяет вам исправить ошибку, которая иначе могла бы стать фатальной, до выхода из блока. Однако если завершить обработчик, то блок больше не получит управления. Вы не можете вернуться в текущий блок из обработчика исключений. Так, в следующем примере, если предложение SELECT INTO возбуждает исключение ZERO_DIVIDE (деление на 0), то вы не можете вернуть управление на INSERT: DECLARE pe_ratio NUMBER(3,1); BEGIN DELETE FROM stats WHERE symbol = 'XYZ'; SELECT price / NVL(earnings, 0) INTO pe_ratio FROM stocks WHERE symbol = 'XYZ'; INSERT INTO stats (symbol, ratio) VALUES ('XYZ', pe_ratio); EXCEPTION WHEN ZERO_DIVIDE THEN... END; Тем не менее, существует способ обработать исключение для предложения, а затем продолжить работу со следующего предложения. Для этого просто поместите предложение в его собственный подблок вместе с его собственными обработчиками исключений, как показано в следующем примере: DECLARE pe_ratio NUMBER(3,1); BEGIN DELETE FROM stats WHERE symbol = 'XYZ'; ---------------- начало подблока ------------------ BEGIN SELECT price / NVL(earnings,0) INTO pe_ratio FROM stocks WHERE symbol = 'XYZ'; EXCEPTION WHEN ZERO_DIVIDE THEN pe_ratio:= 0; END; ---------------- конец подблока ------------------- INSERT INTO stats (symbol, ratio) VALUES ('XYZ', pe_ratio); EXCEPTION... END; Если в подблоке произойдет ошибка, локальный обработчик сможет обработать это исключение. После завершения подблока окружающий блок продолжит выполнение с той точки, где завершился подблок. В последнем примере, если предложение SELECT INTO вызовет исключение ZERO_DIVIDE, локальный обработчик перехватит его и установит нулевое значение переменной pe_ratio. После завершения обработчика подблок завершится, и выполнение продолжится с предложения INSERT. Повторение транзакции--------------------- Когда возникает исключение, вы можете захотеть повторить транзакцию, вместо того чтобы отказаться от нее. Для этого существует простой способ. Сначала заключите транзакцию в подблок. Затем поместите этот подблок в цикл, который повторяет транзакцию. Рассмотрим следующий пример. Прежде чем начать транзакцию, вы отмечаете точку сохранения. Если транзакция выполнится успешно, вы выполняете COMMIT и выходите из цикла. Если транзакция сбивается, управление передается обработчику исключений, в котором выполняете откат к точке сохранения, а затем пытаетесь исправить проблему. Когда обработчик исключений заканчивается, подблок завершается. После этого управление передается на предложение LOOP в окружающем блоке, подблок исполняется повторно, и транзакция повторяется. Используя цикл FOR или WHILE, вы можете лимитировать число попыток. DECLARE name CHAR(20); ans1 CHAR(3); ans2 CHAR(3); ans3 CHAR(3); suffix NUMBER:= 1; BEGIN... LOOP -- можно написать "FOR i IN 1..10 LOOP", чтобы -- ограничиться максимально десятью попытками ------------------- начало подблока ------------------- BEGIN... SAVEPOINT start_transaction; -- точка сохранения /* Удалить результаты опроса. */ DELETE FROM results WHERE answer1 = 'NO'; /* Добавить имя и ответы респондента. */ INSERT INTO results VALUES (name, ans1, ans2, ans3); /* Это может дать исключение DUP_VAL_ON_INDEX, * * если два респондента имеют одинаковые имена,* * так как индекс по столбцу name уникальный. */ COMMIT; EXIT; EXCEPTION WHEN DUP_VAL_ON_INDEX THEN ROLLBACK TO start_transaction; -- откат suffix:= suffix + 1; -- попробуем исправить имя name:= name || TO_CHAR(suffix);... END; ------------------- конец подблока -------------------- END LOOP; END;

 



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2017-12-29 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: