Чтение онлайн

ЖАНРЫ

Мир InterBase. Архитектура, администрирование и разработка приложений баз данных в InterBase/FireBird/Yaffil

Востриков С М

Шрифт:

Это значит, что если в процедуре-выборке есть цикл, в котором производятся какие-то действия, и в теле цикла есть SUSPEND, то при возбуждении исключения О1меня1ся все дейс1вия, выполненные в этом цикле до последнего оператора SUSPEND.

Надо сказать, что в исключениях было бы мало пользы, если бы у разработчика СУБД не было возможности обработать их на уровне базы данных. Чтобы разработчик смог обработать возникшее исключение, применяется следующая конструкция языка XП и триггеров:

WHEN EXCEPTION <имя_исключения> DO

BEGIN

/*обработка исключения*/

END

Использование этой конструкции помогает избежать

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

Когда возбуждается исключение, происходит следующее: выполнение хранимой процедуры (или триггера) прерывается. InterBase начинает искать конструкцию WHEN EXCEPTION...DO для обработки возникшего исключения в текущем блоке BEGIN...END. Если не находит, то поднимается на уровень выше (выше в том смысле, если имеются вложенные блоки BEGIN...END или когда одна ХП вызвана из другой) и ищет обработчик исключения там и т. д., пока либо не найдет подходящий обработчик исключения, либо не закончится вложенность уровней хранимой процедуры. Если обработчик исключения так и не был найден, то возвращается стандартное сообщение об ошибке, включающее текст исключения. Если обработчик найден, то выполняются действия в его блоке BEGIN...END и управление передается на первый оператор, следующий за END обработчика.

Давайте рассмотрим пример обработки исключения, возбуждаемого в нашей процедуре SP_DIVIDE. Предположим, что мы имеем некоторую внешнюю процедуру SP_divide_all, вызывающую SPJDFVIDE для того, чтобы поделить два числа. Конечно, пример сильно утрирован, но он позволяет пояснить способ и смысл использования исключений.

Итак, вот текст нашей хранимой процедуры:

CREATE PROCEDURE sp_test_except(Delltel DOUBLE PRECISION)

RETURNS (rslt DOUBLE PRECISION, status VARCHAR(SO))

AS

BEGIN

Status='Everything is Ok';

SELECT result FROM sp_divide(12,:Delitel) INTO :rslt;

SUSPEND;

WHEN EXCEPTION Zero_divide DO

BEGIN

Status='zero value found!';

rslt=-l;

SUSPEND;

END

END

Эта процедура вызывает процедуру SP_DIVIDE. Если параметр Delitel не равен нулю, то процедура SP_DIVIDE выполняется без проблем и в возвращаемое значение rslt помещается частное от деления, а статусная переменная status принимает значение 'Everything is Ok'. В случае, если возникла исключительная ситуация деления на нуль, то результирующая переменная rslt будет равна -1, а в переменной status будет содержаться сообщение об ошибке - 'zero value found!'. Разумеется, в обработчике исключения можно произвести и более сложную обработку, например записать некорректные данные в особую таблицу или попытаться изменить данные для выполнения операции и вновь попробовать ее выполнить и т. д.

Обработка ошибок SQL и InterBase

Разобравшись с обработкой исключений, определяемых пользователем, можем перейти к обработке ошибок InterBase. Ошибка - это фактически то же самое исключение, только возбуждаемое InterBase. Принцип обработки ошибок тот же самый что и исключений: если возникает какая-то ошибка, то сервер ищет ее обработчик, последовательно просматривая все уровни вложенности (если они есть) хранимой процедуры, начиная с того уровня, на котором возникла ошибка.

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

Конструкция, с помощью которой производится обработка ошибок, такая же, как и для обработки исключений, только вместо EXCEPTION стоит либо GDSCODE, либо SQLCODE:

WHEN GDSCODE SQLCODE <код_ошибки> DO

BEGIN

/*обрабатываем ошибку*/

END

В зависимости от того, стоит ли в конструкции

обработки ошибок GDSCODE или SQLCODE, обрабатываются различные ошибки. Если стоит SQLCODE, то обрабатываются ошибки SQL, а если GDSCODE - то ошибки InterBase. Примером ошибки SQL является ошибка с SQLCODE=-802 "Arithmetic exception, numeric overflow, or string truncation" или SQLCODE=-817 "Attempted update during read-only transaction" Список ошибок SQL и соответствующих им значений SQLCODE приведен в таблице "SQLCODE codes and messages'. Примером ошибки InterBase является ошибка isc_bad_dbkey 335544322L "invalid database key".

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

А что делать, если нужно прореагировать на любую ошибку? Не прописывать же обработчики всех сотен возможных ошибок? Конечно же, нет. Для того чтобы написать безусловный обработчик, реагирующий на любую ошибку - SQL, InterBase или исключение, следует воспользоваться конструкцией WHEN DO с ключевым словом ANY:

WHEN ANY DO

BEGIN

/*действия при любой нестандартной операции ошибке

или исключении */

END

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

Работа с массивами в хранимых процедурах

Массивы, как было сказано в главе "Типы данных", позволяют хранить в одном поле набор данных какого-нибудь одного элементарного типа. Однако "простым" SQL-запросом данные не извлечь и не изменить. Необходим особый подход для работы с массивами InteiBase в клиентских приложениях - см paздел "Поддержка array-полей в FIBPIus" в главе (ч 2) и "Разработка клиентских приложений СУБД InterBase с использованием технологии Microsoft OLE DB" (ч. 3). К тому же не все библиотеки доступа поддерживают работу с массивами.

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

CREATE TABLE table_array(

ID_table INTEGER,

Arrayl INTEGER[3,2]);

Теперь можно продемонстрировать, как можно просмотреть данные из этого массива. Для этого внутри хранимой процедуры применяется конструкция вида

SELECT Arrayl[:i, :j] FROM table_array INTO :ElemValue;

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

CREATE PROCEDURE SelectFromArr(ID_row INTEGER,

x INTEGER, у INTEGER, vl integer)

Returns (ElemValue INTEGER)

AS

BEGIN

SELECT arrayl[:x,:y]

FROM table_array

WHERE id_cable=:ID_row

INTO :ElemValue;

SUSPEND;

END

Как видите, текст ХП очевиден - просто извлекаем нужные значения из элемента массива с заданными индексами х и у в строке таблицы table_array с идентификатором ID_table=ID_row. К сожалению, такой синтаксис доступа к элементам массива доступен только внутри хранимых процедур и только для чтения. Для заполнения массива придется воспользоваться программой, применяющей либо InterBase API, либо библиотеки доступа, поддерживающие работу с массивами InterBase - FIBPIus для Delphi/C-H-Builder/Kylix или IBProvider для продуктов Microsoft.

Поделиться с друзьями: