Выбрать главу

EXCEPTION reassign_sales;

! ! !

ПРИМЕЧАНИЕ. В хранимых процедурах выбора выходные строки, которые уже были получены клиентом в предыдущих циклах FOR SELECT ... DO ... SUSPEND, остаются доступными для клиента. О механизме, работающем в этом случае, см. далее разд. "Оператор WHERE".

. ! .

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

Обработка исключений

Код PSQL может перехватывать ошибки при их появлении и затем их обрабатывать в подпрограмме обработки исключений. Если исключение будет обработано в вашем коде- вы обеспечите исправление или обход ошибки и позволите продолжить выполнение, - то клиенту не возвращается никакого сообщения об исключении. Рис. 32.2 иллюстрирует логику перехвата и обработки ошибок.

Как и раньше, исключение приводит к прекращению выполнения в блоке. Вместо того чтобы передать выполнение на конечный оператор END, теперь процедура отыскивает уровни во вложенных блоках, начиная с блока, где была выявлена ошибка, и переходит на внешние блоки, чтобы найти код обработчика, который "знает" о таком исключении. Она отыскивает первый оператор WHEN, который может обработать эту ошибку.

Оператор WHEN

Оператор WHEN имеет следующую форму:

WHEN <исключение> DO <составной-оператор>

Здесь исключение может быть одним из следующих:

<имя-исключения> | GDSCODE код | SQLCODE код \ ANY

<составной-оператор> может быть одним оператором или множеством обычных операторов PSQL, заключенным в блок BEGIN ... END.

Рис. 32.2. Логика перехвата и обработки ошибок

Область видимости типов исключений

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

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

Следующим по глубине является GDSCODE. В версии 1.0.x это контекстная переменная, в которой процедура может только получить код и сравнить его с кодом, заданным в предикате WHEN:

WHEN GDSCODE foreign_key DO

BEGIN

. . .

END

Начиная с версии 1.5, GDSCODE является "полнофункциональной" контекстной переменной. При ее чтении внутри блока, где возникло исключение, вы можете сохранить этот код в записи протокола.

! ! !

СОВЕТ. Все коды GDSCODE имеют символические константы, которые являются более или менее понятными для людей, знающих английский язык. Именно эти символические константы вы должны использовать в операторах WHEN GDSCODE, а не числовые коды. Если вы посмотрите заголовочный файл iberror.h в вашем каталоге /include, вы увидите, что определение символических констант имеют префикс isc_. Этот префикс должен быть опущен в операторах WHEN GDSCODE.

. ! .

Некоторые ошибки, выявленные в GDSCODE, могут быть исправлены внутри области действия блока, где они появились. Если это так, то оператор WHEN для обработки таких ошибок должен находиться здесь, иначе он должен быть помещен во внешний цикл для обработки там ошибок.

Код SQLCODE является довольно общим, он не всегда соответствует типу ошибки. Операции SQL передают SQLCODE 0 при успешном завершении и SQLCODE 100 при достижении конца файла. Диапазон неиспользуемых "сегментов" находится между 1 и 99 для предупреждающих и информационных сообщений. Диапазон ошибок SQLERROR - отрицательные числа, которые больше -1000. Такие коды ошибок SQLERROR в SQLCODE обычно являются группировкой на более высоком уровне нескольких кодов GDSCODE.

Как и GDSCODE, SQLCODE допускает только программное чтение в версии 1.0.x, но становится настоящей контекстной переменной в версии 1.5 и более поздних. Вы можете поместить это значение в протокол.

! ! !

ПРИМЕЧАНИЕ. Вы можете использовать либо GDSCODE, либо SQLCODE. Вы получите код для одного и NULL для другого.

. ! .

Коды SQLCODE менее детализированы и в большинстве случаев менее всего подходят для условий, которые могут быть исправлены внутри модуля. Посмотрите коды в приложении 10, обратите внимание, что один код SQLCODE чаще всего группирует несколько кодов GDSCODE. Обычно они более полезны во внешнем блоке модуля.

Ключевое слово ANY- это "оставшиеся исключения". Оно означает перехват любого определенного внутренне исключения, которое не было обработано. В версии 1.5, наряду с возможностью чтения GDSCODE или SQLCODE (в рамках области действия), у ANY

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

Размещение блоков WHEN

Всегда помещайте ваши блоки WHEN непосредственно перед оператором END, закрывающим тот блок, исключения которого вы хотите обрабатывать. Не помещайте никакие другие операторы - даже SUSPEND или EXIT - между концом ваших обработчиков и завершающим оператором END. Вернитесь к рис. 32.2, где описан этот поток.

! ! !

СОВЕТ. Если вы хотите использовать оператор EXIT непосредственно перед финальным оператором END вашего модуля с целью документирования, то это нормально. В этом месте оператор EXIT не может повлиять на поток выполнения.

. ! .

Когда процедура встречает ошибку в цикле курсора процедуры выбора, отменяются все операторы, начиная с последнего и до предыдущего SUSPEND. Эта отмена не влияет на строки, уже выведенные предыдущими вызовами SUSPEND; они остаются доступными для клиента.

Оператор SUSPEND не должен использоваться в выполняемых процедурах. Пусть логика вашего выполнения определяет, когда блоки завершаются и когда ошибки должны возвращаться в виде исключений.

Вложенные исключения в качестве точек сохранения

Вложенная архитектура блоков выполнения модулей PSQL означает, конечно, что PSQL поддерживает "вложенные" транзакции. Деятельность каждого модуля PSQL выполняется в контексте той транзакции, в которой он был вызван. Стандартный поток выполнения гарантирует, что работа либо завершится как единое целое, либо вся будет отменена. В случае хранимых процедур исключение приводит к отмене всей работы, выполненной во время вызова. В случае триггеров исключение приводит к отмене DML и всех уже выполненных действий, связанных с этой операцией.

Обработка исключений предоставляет средства для разбиения выполнения на этапы, которые могут быть отменены до определенной точки без необходимости отмены всей работы модуля. Уровень отмены определяется в точке появления ошибки и ближайшего предложения обработчика WHEN. Такая "точка сохранения" - эквивалент именованной точки сохранения в управляемой клиентом транзакции - создается в коде блока, в котором выполняется код обработчика WHEN.

Обработка исключения REASSIGN_SALES

Теперь вернемся к нашей процедуре DELETE_EMPLOYEE. В главе 30, когда эта процедура сталкивалась с ситуацией, где удаляемый служащий имеет заказы в картотеке, она выдавала пользовательское исключение REASSIGN_SALES и просто прекращала работу, отменяла выполненные действия и посылала сообщение исключения работающему с ней человеку.

Однако мы можем заставить процедуру обработать эту ситуацию и позволить процедуре завершиться. Например, обработчик может обнулить ключ SALES_REP и послать сообщение другой процедуре, которая создаст записи в таблице протокола для каждой соответствующей записи продаж.

Мы начинаем с создания таблицы протокола: