Сообщений 15    Оценка 20 [+0/-2]         Оценить  
Система Orphus

Общий метод формирования сообщений об ошибках при работе с базами данных и его использование для БД Firebird

Автор: Лихачёв Владимир Николаевич
Источник: RSDN Magazine #4-2008
Опубликовано: 26.04.2009
Исправлено: 10.12.2016
Версия текста: 1.0
1. Основные этапы формирования сообщений об ошибках БД
2. Формирование сообщений об ошибках для СУБД Firebird
2.1. Обработка ошибок, обусловленных ограничениями БД
2.1.1. Не указано значение поля, обязательного для заполнения (ограничение NOT NULL)
2.1.2. Уникальность значения поля или набора полей
2.1.3. Ошибки, связанные с нарушением ссылочной целостности
2.1.3.1. Добавление или изменение значения поля в подчинённой таблице, для которого нет соответствующего значения в главной таблице
2.1.3.2. Удаление или изменение поля главной таблицы, входящего во внешний ключ с правилом обновления NO ACTION
2.2. Реализация специальных сообщений об ошибках уровня БД
2.3. Формирование сообщений об ошибках для проверок (CHECK)
2.4. Отсутствие библиотеки UDF, используемой в БД
Заключение

При разработке программ, работающих с базами данных (БД), важным элементом является информативность сообщений об ошибках, предназначенных для пользователя. Но количество причин, которые могут приводить к возникновению ошибок, обычно возрастает с увеличением сложности БД. Это часто приводит к тому, что достаточно информативные сообщения формируются только для некоторой части возможных ошибок, в остальных же случаях пользователю остается довольствоваться в лучшем случае сообщениями от самого сервера баз данных. Информативность таких сообщений в большинстве случаев недостаточна для устранения причины возникшей проблемы. В ряде же случаев сообщение может еще больше запутать пользователя, так как в таких сообщениях указываются имена внутренних объектов БД, о которых ему, скорее всего, ничего не известно.

В данной статье рассматривается метод формирования сообщений для пользователя об ошибочных ситуациях БД, который позволяет:

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

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

В ряде случаев может возникнуть впечатление, что с помощью интерфейса программы можно исключить большинство ошибок, связанных с изменением данных в БД. Рассмотрим несколько ситуаций. Например, в БД есть таблицы “Товары” и “Продажи”, которые имеют взаимосвязь один (таблица “Товары”) ко многим (таблица “Продажи”). Для её реализации в таблице “Продажи” создан внешний ключ, ссылающийся на таблицу “Товары”. Работа с БД ведется одновременно с нескольких клиентских мест: ведутся продажи и редактирование справочника товаров. Рассмотрим более подробно процесс заполнения таблицы “Продажи”. Допустим, для этого программа использует интерфейс, в котором пользователю предоставляется содержание справочника товаров. Это позволяет избежать указания некорректного значения, но не всегда. И связано это с тем, что между получением программой содержания справочника товаров из БД и добавления товара в таблицу продаж есть некоторый промежуток времени. Если в этот промежуток времени товар из справочника будет удален, то при попытке добавления этого товара в таблицу “Продажи” возникнет ошибка ограничения внешнего ключа для таблицы “Продажи”, который не допускает добавления в подчинённую таблицу значений, отсутствующих в главной таблице.

Другой пример. Допустим, в свойствах того же внешнего ключа указано, что запись из таблицы “Товары” можно удалять только в том случае, если в таблице “Продажи” нет на неё ссылок. Такое ограничение может потребоваться, например, для возможности построения отчетов о продажах. Предположим, что программа, в которой ведется редактирование справочника товаров, при отображении каждого товара проверяет наличие его продаж. Пусть, для того чтобы уменьшить нагрузку на сервер и не запрашивать с сервера наличие продаж по каждому товару, используются триггеры таблицы “Продажи”, и в одном из полей таблицы “Товары” сохраняется признак наличия продаж этого товара. К сожалению, такой вариант интерфейса тоже не гарантирует того, что пользователь будет видеть актуальные данные о том, для каких товаров нет продаж. И связано это снова с тем, что между получением данных с сервера и выполнением операции пользователем будет некоторый временной интервал, в течение которого ситуация может измениться. Часто именно в таких случаях пользователь и получает “непонятное” сообщение об ошибке. Если проанализировать ошибки ограничений БД, то интерфейс программы позволяет исключить только ошибку, связанную с указанием значений для полей, требующих обязательного заполнения (not null).

Описываемый в статье метод формирования сообщений об ошибках БД ориентирован на реализацию в клиентском приложении. В то же время он может использоваться полностью или частично и на стороне сервера БД. Ограничение его использования на стороне сервера баз данных может быть вызвано несколькими причинами:

  1. Для реализации такого подхода в полной мере сервер БД должен предоставлять возможность создания общего обработчика для всех ошибок. Если такой возможности нет, то можно использовать описываемый метод формирования сообщений при обработке ошибок в хранимых процедурах, функциях и триггерах БД.
  2. Для однозначного определения причины ошибки требуется не только код ошибки, но и текстовое сообщение об ошибке. К сожалению, возможность получения текстового сообщения об ошибке на стороне сервера предоставляют не все СУБД. Если такой возможности нет, то использование описываемого метода возможно, но с довольно большими ограничениями. Они в первую очередь связаны с тем, что код ошибки позволяет определить только тип ошибки, но не конкретные ограничения БД, которые её вызвали. Поэтому, если таблица содержит несколько однотипных ограничений, выявление причины ошибки может оказаться невозможным.
  3. Для формирования специальных сообщений программ на стороне сервера БД необходима возможность определения подключения, в контексте которого произошла ошибка. Если такая возможность отсутствует, то формирование специальных сообщений для программы будет возможна только в самом клиентском приложении.

1. Основные этапы формирования сообщений об ошибках БД

Среди основных причин возникновения сообщений об ошибках БД можно выделить несколько:

Ограничения БД.

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

Понятные для пользователя сообщения об ошибках ограничений БД могут быть сформированы на основе информации об ошибке от сервера БД и о структуре БД, которая может быть получена из её системного каталога. В зависимости от сервера БД для этого могут использоваться системные представления, системные таблицы или хранимые процедуры и функции. В большинстве случаев в текстовых сообщениях об ошибках от сервера БД присутствуют названия объектов, ставших причиной её возникновения. Это позволяет на основе анализа структуры БД формировать более информативные сообщения, чем те, которые генерирует сам сервер БД. Примеры формирования сообщений об ошибках ограничений БД для сервера Firebird рассмотрены в разделе 2.

В большинстве случаев ошибки связаны с изменением данных в таблицах, и в сообщении об ошибке для пользователя необходимо в том или ином виде указывать названия таблиц и их полей. Проблема связана с тем, что используемые в ПО названия таблиц и полей (пользовательские названия) обычно отличаются от их имен в БД. Например, в БД таблица может иметь название “CLIENTS”, а в программе данные этой таблицы могут отображаться в справочнике с названием “Клиенты” или “Заказчики”.

СОВЕТ

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

Информация для сопоставления имен объектов БД с их пользовательскими названиями может храниться в самой БД или в программе. Лучше хранить эту информацию в БД, так как в этом случае она будет доступна всем программам, работающим с данной БД. Для хранения этих данных в БД могут использоваться различные варианты. В простейшем случае для этого может использоваться отдельная таблица, в которой будут перечислены поля таблиц и соответствующие им пользовательские названия. Другим вариантом является использование для этого описания объектов БД. Создание описаний для таблиц и их полей, а также ряда других объектов БД предоставляют, например, такие сервера баз данных как Oracle, Firebird, Microsoft SQL Server.

Использование информации о структуре базы данных и соответствии имен объектов БД их пользовательским названиям позволяет формировать достаточно информативные для пользователя сообщения. Но в ряде случаев для отдельных ошибок могут потребоваться специальные сообщения. Примером может являться связь между таблицами “многие ко многим”, которая в большинстве серверов БД не может быть реализована непосредственно средствами самого сервера. Она обычно реализуется с помощью дополнительной таблицы, в которой создаются внешние ключи, ссылающиеся на те таблицы, между которыми осуществляется связь “многие ко многим”. Понятно, что сообщение об ошибке, вызванное ограничением внешнего ключа, которое создается на основе анализа структуры БД (см. раздел 2.1.3), не сможет отразить информацию об имеющей логической связи “многие ко многим” между таблицами. В такой ситуации только специальное сообщение об ошибке сможет более адекватно отразить реальную причину её возникновения. Другим случаем использования специальных сообщений об ошибках может являться невозможность или большая сложность формирования сообщений на основе структуры БД. Примерами таких ситуаций могут являться ограничения для таблиц и доменов баз данных Firebird (раздел 2.3).

Можно выделить две группы специальных сообщений об ошибках БД. Первый тип специальных сообщений предназначен для использования во всех приложениях, работающих c БД. Их можно условно назвать “специальные сообщения об ошибках уровня базы данных”. Вторая группа сообщений специфична для конкретного приложения, так как такие сообщения требуются только определенному приложению. Их можно условно назвать “специальные сообщения об ошибках уровня приложения”. Информацию о первой группе сообщений можно хранить в самой БД. В этом случае такая информация будет доступна всем приложениям, которые работают с этой БД. Для этого можно, например, использовать специальную таблицу. Пример такой таблицы описан в разделе 2.2. Идентификация ошибки может осуществляться на основе кода ошибки и одного или нескольких ключевых слов из сообщения об ошибке. Сообщения, специфичные для программы, могут храниться в самой программе, например, в её ресурсах или в виде отдельного файла. Идентификация таких сообщений может выполняться аналогичным образом.

Формирование сообщения об ошибках при работе с БД должно осуществляться в несколько этапов (рисунок 1):

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

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

Вывод сообщения на основе анализа структуры БД. Если специальных сообщений об ошибке не сформировано на предыдущих этапах, формируется сообщение на основе анализа структуры БД. Оно выводится пользователю и на этом формирование сообщения завершается.

Вывод сообщения от сервера БД. Если на трех предыдущих этапах сообщение не было сформировано, то отображается сообщение об ошибке от самого сервера БД. Такая ситуация может возникнуть по нескольким причинам. Например, при возникновении ошибки или исключения, которые были преднамеренно сгенерированы в хранимой процедуре или триггере, и изменение содержания которых не требуется. Например, такие СУБД как Firebird, MS SQL Server, Oracle Database позволяют формировать произвольные сообщения об ошибках, которые могут без изменения передаваться пользователю. В ряде случаев такие сообщения могут быть даже более информативными, чем сформированные на предыдущих этапах. Другой причиной возникновения такой ситуации может быть получение от сервера сообщения об ошибке, формирование специального сообщения о которой не предусмотрено.


Рис. 1. Последовательность формирования сообщения об ошибке БД в приложении.

Описанная последовательность формирования специальных сообщений об ошибках и сообщений на основе анализа структуры БД позволяют реализовать гибкий подход для создания информативных сообщений.

2. Формирование сообщений об ошибках для СУБД Firebird

Сервер БД Firebird имеет ряд особенностей, которые необходимо учитывать при реализации системного подхода в формировании сообщений об ошибках. В частности, он не реализует общего обработчика ошибок и не дает возможности формирования текстового сообщения об ошибке на стороне сервера. Поэтому в полной мере описываемый метод формирования сообщений об ошибках может быть реализован только в клиентском приложении (к которому в данном случае относится и ПО промежуточного уровня). Этот вариант реализации и обсуждается далее.

В приводимом ниже варианте формирования сообщений об ошибках для сопоставления имен таблиц и полей и их пользовательских названий используются описания системных объектов БД, которые сервер Firebird позволяет указывать для большинства системных объектов.

Одна из особенностей Firebird в формировании текстовых сообщений об ошибках требует особого внимания. Заключается она в том, что в ряде сообщений об ошибках сервер указывает только названия полей таблиц, изменение которых привело к возникновению ошибки (см. раздел 2.1.1). Название же самой таблицы, к которой относится поле, в сообщении не указывается. Этот нюанс необходимо учитывать ещё на этапе проектирования БД и задавать имена для полей таблиц таким образом, чтобы они были уникальными в пределах всей БД, а не только для таблицы, как этого требует сервер Firebird.

ПРЕДУПРЕЖДЕНИЕ

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

Для задания уникальных имен полей можно, например, использовать короткий уникальный префикс из нескольких символов для каждой таблицы в названии её полей. В этом случае достаточно будет проконтролировать уникальность таких префиксов. Примеры такого именования полей таблиц приведены в разделе 2.1. Например, для полей таблицы COUNTRY используется префикс “CTR_” (скрипт 2.5), а для полей таблицы JOB – префикс “JOB_” (скрипт 2.8).

Для проверки отсутствия полей с одинаковыми названиями в пределах всех базы данных можно использовать запрос 2.1.

      SELECT
  A.RDB$FIELD_NAME FIELD_NAME,
  COUNT(A.RDB$FIELD_NAME)
FROM
  RDB$RELATION_FIELDS A,
  RDB$FIELDS B,
  RDB$RELATIONS REL
WHERE
  (REL.RDB$SYSTEM_FLAG = 0)
  AND (A.RDB$FIELD_SOURCE = B.RDB$FIELD_NAME)
  AND (A.RDB$RELATION_NAME = REL.RDB$RELATION_NAME)
GROUPBY A.RDB$FIELD_NAME
HAVING (COUNT(A.RDB$FIELD_NAME) > 1)
ORDERBY A.RDB$FIELD_NAME

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

Чтобы проверить, существуют ли описания таблиц БД, можно использовать запрос 2.2.

      SELECT
  REL.RDB$RELATION_NAME TABLE_NAME
FROM
  RDB$RELATIONS REL
WHERE
  (REL.RDB$SYSTEM_FLAG = 0)
  AND ((REL.RDB$DESCRIPTION ISNULL) OR (REL.RDB$DESCRIPTION = ''))
ORDERBY
  REL.RDB$RELATION_NAME

В результате запроса будет выведен список таблиц, для которых не указано описание.

Для получения списка полей таблиц, у которых отсутствует описание, можно использовать запрос 2.3.

      SELECT
  REL.RDB$RELATION_NAME TABLE_NAME,
  A.RDB$FIELD_NAME
FROM
  RDB$RELATIONS REL,
  RDB$RELATION_FIELDS A
WHERE
  (REL.RDB$SYSTEM_FLAG = 0)
  AND (A.RDB$RELATION_NAME = REL.RDB$RELATION_NAME)
  AND ((A.RDB$DESCRIPTION = '') OR (A.RDB$DESCRIPTION ISNULL))
ORDERBY REL.RDB$RELATION_NAME, A.RDB$FIELD_NAME

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

СОВЕТ

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

2.1. Обработка ошибок, обусловленных ограничениями БД

Для баз данных Firebird можно выделить ряд наиболее часто встречающихся ошибок, обусловленных ограничениями БД:

  1. Не указано значение поля, обязательного для заполнения (ограничение NOT NULL).
  2. Нарушена уникальность поля или набора полей.
  3. Нарушена внешняя целостность таблиц при изменении данных в подчиненной таблице.
  4. Нарушена внешняя целостность таблиц при удалении данных из главной таблицы.
  5. Нарушен критерий условия CHECK для таблицы или домена.

2.1.1. Не указано значение поля, обязательного для заполнения (ограничение NOT NULL)

При возникновении данной ситуации в результате добавления или редактирования записи сервер генерирует сообщение об ошибке с кодом SQLCode -625 и текстом:

The insert failed because a column definition includes validation constraints. Validation error for column <ПОЛЕ ТАБЛИЦЫ>, 
value "*** null ***”.

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

        SELECT
    REL.RDB$RELATION_NAME   TABLE_NAME,
    REL.RDB$DESCRIPTION     TABLE_DESCRIPTION,
    A.RDB$FIELD_NAME        FIELD_NAME,
    A.RDB$DESCRIPTION       FIELD_DESCRIPTION
  FROM
    RDB$RELATIONS REL,
    RDB$RELATION_FIELDS A
  WHERE
    (REL.RDB$SYSTEM_FLAG = 0)
    AND (A.RDB$RELATION_NAME = REL.RDB$RELATION_NAME)
    AND (A.RDB$FIELD_NAME = :FIELD_NAME)
  ORDERBY REL.RDB$RELATION_NAME, A.RDB$FIELD_NAME

В качестве параметра запроса FIELD_NAMEнеобходимо указать имя поля таблицы из сообщения об ошибке. Имя таблицы, к которой относится поле, будет отражено в поле TABLE_NAME, описание таблицы – в поле TABLE_DESCRIPTION, а описание поля – в поле FIELD_DESCRIPTION.

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

Необходимо указать значение поля “Описание поля” в таблице “Описание  таблицы”.

Пример 1

Использование описываемого метода формирования сообщений об ошибках можно рассмотреть на примере базы данных EMPLOYEE.FDB, которая входит в дистрибутив Firebird в качестве демонстрационной базы данных. Если для неё выполнить запрос 2.1, то он вернет список полей, названия которых дублируются в различных таблицах (табл. 2.1).

Таблица.2.1. Список полей демонстрационной БД EMPLOYEE.FDB, названия которых повторяются в нескольких таблицах.

FIELD_NAME COUNT
COUNTRY 2
CUST_NO 2
DEPT_NO 3
EMP_NO 4
FIRST_NAME 2
JOB_CODE 2
JOB_COUNTRY 2
JOB_GRADE 2
LAST_NAME 2
LOCATION 2
PHONE_EXT 2
PHONE_NO 3
PROJ_ID 3

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

В качестве примера для ограничения NOT NULL можно рассмотреть практически любую таблицу. Например, в таблице COUNTRY ограничение NOT NULL используется для обоих её полей. Чтобы не возникало неоднозначности в определении принадлежности поля к таблице, названия полей в этой таблице будут изменены: в их названия будет добавлен префикс “CTR_” и указаны описания для таблицы и её полей (скрипт 2.5).

          CREATE DOMAIN D_COUNTRYNAME AS VARCHAR(15);
CREATETABLE COUNTRY (
    CTR_COUNTRY   COUNTRYNAME NOTNULL,
    CTR_CURRENCY  VARCHAR(10) NOTNULL
);
ALTERTABLE COUNTRY ADDCONSTRAINT PK_COUNTRY PRIMARYKEY (CTR_COUNTRY);
COMMENT ONTABLE COUNTRY IS'Страны';
COMMENT ONCOLUMN COUNTRY.CTR_COUNTRY IS'Название страны';
COMMENT ONCOLUMN COUNTRY.CTR_CURRENCY IS'Валюта страны';

Если попытаться добавить запись со значением NULL для поля CTR_COUNTRY с помощью запроса:

          INSERT
          INTO COUNTRY (CTR_COUNTRY, CTR_CURRENCY) VALUES (NULL, 'rub')

то сервер сгенерирует сообщение об ошибке:

 The insert failed because a column definition includes validation constraints. validation error for column CTR_COUNTRY, value "*** null ***".

Если выполнить запрос 2.4 со значением параметра CTR_COUNTRY, то он вернет в качестве результата одну запись (табл. 2.2):

Таблица.2.2. Результат выполнения запроса 1.2 со значением параметра CTR_COUNTRY.

TABLE_NAME TABLE_DESCRIPTION FIELD_NAME FIELD_DESCRIPTION
COUNTRY Страны CTR_COUNTRY Название страны

Используя описания поля и таблицы, полученные из этого запроса, можно сформировать сообщение для пользователя программы, например, следующего содержания:

Необходимо указать значение поля “Название страны” в таблице “Страны”.

2.1.2. Уникальность значения поля или набора полей

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

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

Invalid insert or update value(s): object columns are constrained - no 2 table rows can have duplicate column values. Violation of PRIMARY or UNIQUE KEY constraint "УНИКАЛЬНЫЙ КЛЮЧ" on table "ТАБЛИЦА"

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

В третьем случае сообщение отличается:

Invalid insert or update value(s): object columns are constrained - no 2 table rows can have duplicate column values. Attempt to store duplicate value (visible to active transactions) in unique index "УНИКАЛЬНЫЙ ИНДЕКС".

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

        SELECT
    INDS.RDB$FIELD_NAME FIELD_NAME,
    RF.RDB$DESCRIPTION FIELD_DESCRIPTION,
    R.RDB$DESCRIPTION TABLE_DESCRIPTION
FROM
    RDB$RELATION_CONSTRAINTS RC, RDB$INDEX_SEGMENTS INDS,
    RDB$RELATION_FIELDS RF, RDB$RELATIONS R
WHERE
    (RC.RDB$CONSTRAINT_NAME = :CONSTRAINT_NAME)
    AND (RC.RDB$INDEX_NAME = INDS.RDB$INDEX_NAME)
    AND (RF.RDB$SYSTEM_FLAG = 0)
    AND (RF.RDB$FIELD_NAME = INDS.RDB$FIELD_NAME)
   --AND (RF.RDB$RELATION_NAME = :TABLE_NAME)AND (R.RDB$RELATION_NAME = RC.RDB$RELATION_NAME) 

Если в БД названия всех полей уникальны, то для корректного выполнения запроса достаточно указать параметр INDEX_NAME – название уникального ключа из сообщения об ошибке. Если есть опасения, что названия полей в БД могут повторяться, то можно убрать комментарий с предпоследней строки в запросе и указать в качестве параметра TABLE_NAME название таблицы из сообщения об ошибке. Если уникальный ключ создан для нескольких полей, то запрос вернет соответствующее количество записей. Поля в результирующем запросе имеют следующие значения: TABLE_NAME – название таблицы; FIELD_NAME и FIELD_DESCRIPTION – название и описание поля, входящего в уникальный ключ; TABLE_DESCRIPTION – описание таблицы.

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

        SELECT
    I.RDB$RELATION_NAME TABLE_NAME,
    ISG.RDB$FIELD_NAME FIELD_NAME,
    RF.RDB$DESCRIPTION FIELD_DESCRIPTION,
    R.RDB$DESCRIPTION TABLE_DESCRIPTION
FROM
    RDB$INDICES I,
    RDB$INDEX_SEGMENTS ISG,
    RDB$RELATION_FIELDS RF,
    RDB$RELATIONS R
WHERE
    (I.RDB$INDEX_NAME = :INDEX_NAME)
    AND (ISG.RDB$INDEX_NAME = I.RDB$INDEX_NAME)
    AND (RF.RDB$SYSTEM_FLAG = 0)
    AND (RF.RDB$FIELD_NAME = ISG.RDB$FIELD_NAME)
    AND (R.RDB$RELATION_NAME = I.RDB$RELATION_NAME)

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

Значение поля “Описание поля” в таблице “ Описание таблицы” должно быть уникальным.

Для второго случая в сообщении потребуется указать информацию о нескольких полях:

Сочетание значений полей “Описание поля 1”, “Описание поля 2” в таблице “Описание таблицы” должны быть уникальными.

Пример 2

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

Invalid insert or update value(s): object columns are constrained - no 2 table rows can have duplicate column values. violation of PRIMARY or UNIQUE KEY constraint "PK_COUNTRY" on table "COUNTRY".

Запрос 2.6 даст результат, приведенный в таблице 2.3.

Таблица 2.3. Результат выполнения запроса 2.6 со значением параметра PK_COUNTRY.

FIELD_NAME FIELD_DESCRIPTION TABLE_DESCRIPTION
CTR_COUNTRY Название страны Страны

На основе этих данных можно сформировать сообщение следующего содержания:

Значение поля “Название страны” в таблице “Страны” должно быть уникальным.

В качестве примера, когда в главный ключ входит несколько полей, можно рассмотреть таблицу JOB. Как и в случае с таблицей COUNTRY, её придется несколько изменить: к некоторым полям добавить префикс “JOB_” и дополнить её описанием для таблицы и полей. Для создания измененного варианта таблицы JOB может использоваться скрипт 2.8.

          CREATE DOMAIN D_JOBCODE AS VARCHAR(5) CHECK (VALUE > '99999');
CREATE DOMAIN D_JOBGRADE AS SMALLINT CHECK (VALUE BETWEEN 0 AND 6);
CREATE DOMAIN D_SALARY AS NUMERIC(10,2) DEFAULT 0 CHECK (VALUE > 0);
CREATETABLE JOB (
    JOB_CODE          JOBCODE NOTNULL,
    JOB_GRADE         JOBGRADE NOTNULL,
    JOB_COUNTRY       COUNTRYNAME NOTNULL,
    JOB_TITLE         VARCHAR(25) NOTNULL,
    JOB_MIN_SALARY    SALARY NOTNULL,
    JOB_MAX_SALARY    SALARY NOTNULL,
    JOB_REQUIREMENT   BLOB SUB_TYPE 1 SEGMENT SIZE 400,
    JOB_LANGUAGE_REQ  VARCHAR(15) [1:5]
);
ALTERTABLE JOB ADDCONSTRAINT PK_JOB PRIMARYKEY (JOB_CODE, JOB_GRADE, JOB_COUNTRY);
ALTERTABLE JOB ADDCONSTRAINT CK_SALARY CHECK (JOB_MIN_SALARY < JOB_MAX_SALARY);
ALTERTABLE JOB ADDCONSTRAINT FK_JOB_COUNTRY FOREIGNKEY (JOB_COUNTRY) REFERENCES COUNTRY (CTR_COUNTRY);
CREATE DESCENDING INDEX MAXSALX ON JOB (JOB_COUNTRY, JOB_MAX_SALARY);
CREATEINDEX MINSALX ON JOB (JOB_COUNTRY, JOB_MIN_SALARY);
COMMENT ONTABLE JOB IS'Должности';
COMMENT ONCOLUMN JOB.JOB_CODE IS'Код';
COMMENT ONCOLUMN JOB.JOB_GRADE IS'Уровень образования';
COMMENT ONCOLUMN JOB.JOB_COUNTRY IS'Страна';
COMMENT ONCOLUMN JOB.JOB_TITLE IS'Должность';
COMMENT ONCOLUMN JOB.JOB_MIN_SALARY IS'Минимальная оплата';
COMMENT ONCOLUMN JOB.JOB_MAX_SALARY IS'Максимальная оплата';
COMMENT ONCOLUMN JOB.JOB_REQUIREMENT IS'Требования';
COMMENT ONCOLUMN JOB.JOB_LANGUAGE_REQ IS'Иностранный язык';

В таблице JOB в главный ключ входят несколько полей: JOB_CODE, JOB_GRADE, JOB_COUNTRY. Если сочетание этих полей не является уникальным, то сервер генерирует сообщение об ошибке следующего содержания:

Invalid insert or update value(s): object columns are constrained - no 2 table rows can have duplicate column values. violation of PRIMARY or UNIQUE KEY constraint "PK_JOB" on table "JOB".

Если выполнить запрос 2.6 со значением параметра PK_JOB, который указан в сообщении об ошибке в качестве ограничения, то в результате будут получены три записи с информацией о полях (табл. 2.4) .

Таблица 2.4. Результат выполнения запроса 2.6 со значением параметра PK_JOB.

FIELD_NAME FIELD_DESCRIPTION TABLE_DESCRIPTION
JOB_COUNTRY Страна Должности
JOB_GRADE Уровень Должности
JOB_CODE Код Должности

На основе этих данных может быть сформировано сообщение об ошибке:

Сочетание значений полей “Страна”, “Уровень образования”, “Код” в таблице “Должности” должны быть уникальными.

2.1.3. Ошибки, связанные с нарушением ссылочной целостности

Сервер FireBird позволяет определять связи между таблицами (ограничения ссылочной целостности). При выполнении операций над данными связанных таблиц можно выделить несколько ситуаций, когда могут возникать ошибки.

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

2.1.3.1. Добавление или изменение значения поля в подчинённой таблице, для которого нет соответствующего значения в главной таблице

При такой ситуации сервер генерирует сообщение об ошибке с кодом SQLCode -530 и текстом:

violation of FOREIGN KEY constraint "".
violation of FOREIGN KEY constraint "ВНЕШНИЙ КЛЮЧ" on table "ТАБЛИЦА".
Foreign key reference target does not exist.

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

        SELECT
    INDS.RDB$FIELD_NAME FIELD_NAME,
    RF.RDB$DESCRIPTION FIELD_DESCRIPTION,
    R.RDB$DESCRIPTION TABLE_DESCRIPTION,
    I.RDB$FOREIGN_KEY UNQ_TABLE_MASTER,
    B.RDB$UPDATE_RULE UPDATE_RULE,
    B.RDB$DELETE_RULE DELETE_RULE
FROM
    RDB$RELATION_CONSTRAINTS RC, RDB$INDEX_SEGMENTS INDS,
    RDB$RELATION_FIELDS RF, RDB$RELATIONS R,
    RDB$INDICES I,
    RDB$REF_CONSTRAINTS B
WHERE
    (RC.RDB$CONSTRAINT_NAME = :CONSTRAINT_NAME)
    AND (INDS.RDB$INDEX_NAME = I.RDB$INDEX_NAME)
    AND (RC.RDB$INDEX_NAME = INDS.RDB$INDEX_NAME)
    AND (RF.RDB$SYSTEM_FLAG = 0)
    AND (RF.RDB$FIELD_NAME = INDS.RDB$FIELD_NAME)
    --AND (RF.RDB$RELATION_NAME = :TABLE_NAME)AND (R.RDB$RELATION_NAME = RC.RDB$RELATION_NAME)
    AND (B.RDB$CONSTRAINT_NAME= RC.RDB$CONSTRAINT_NAME)

В качестве параметра запроса CONSTRAINT_NAME необходимо указать название внешнего ключа из сообщения об ошибке. Поля в результирующем запросе имеют следующее значение:

Если в ключ входит несколько полей, то запрос вернет несколько записей, в которых во всех записях значения полей TABLE_DESCRIPTION, UNQ_TABLE_MASTER, UPDATE_RULE, DELETE_RULE будут совпадать.

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

        SELECT
    I.RDB$RELATION_NAME TABLE_NAME,
    ISG.RDB$FIELD_NAME FIELD_NAME,
    RF.RDB$DESCRIPTION FIELD_DESCRIPTION,
    R.RDB$DESCRIPTION TABLE_DESCRIPTION
FROM
    RDB$INDICES I,
    RDB$INDEX_SEGMENTS ISG,
    RDB$RELATION_FIELDS RF,
    RDB$RELATIONS R
WHERE
    (I.RDB$INDEX_NAME = :INDEX_NAME)
    AND (ISG.RDB$INDEX_NAME = I.RDB$INDEX_NAME)
    AND (RF.RDB$FIELD_NAME = ISG.RDB$FIELD_NAME)
    AND (RF.RDB$SYSTEM_FLAG = 0)
    AND (R.RDB$RELATION_NAME = I.RDB$RELATION_NAME)

Для этого необходимо в качестве параметра запроса указать значение поля UNQ_TABLE_MASTER из результата выполнения запроса 2.9. Поля TABLE_NAME и TABLE_DESCRIPTION будут содержать название и описание главной таблицы, поля FIELD_NAME и FIELD_DESCRIPTION – название и описание поля таблицы, используемого во внешнем ключе.

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

Значение поля “Описание поля подчиненной таблицы” таблицы “Описание подчинённой таблицы” должны соответствовать значениям поля “Описание поля главной таблицы” таблицы “Описание главной таблицы”.

Пример 3

Если в таблицу JOB попробовать добавить запись, в которой для поля JOB_COUNTRY нет соответствующего значения в таблице COUNTRY, то сервер сгенерирует сообщение об ошибке:

violation of FOREIGN KEY constraint "".
violation of FOREIGN KEY constraint "FK_JOB_COUNTRY" on table "JOB".
Foreign key reference target does not exist.

Если выполнить запрос 2.9 со значением FK_JOB_COUNTRY из сообщения об ошибке для параметра запроса CONSTRAINT_NAME, то можно получить следующий результат (табл. 2.5).

Таблица 2.5. Результат выполнения запроса 2.9 со значением параметра FK_JOB_COUNTRY.

FIELD_NAME FIELD_DESCRIPTION TABLE_DESCRIPTION UNQ_TABLE_MASTER UPDATE_RULE DELETE_RULE
JOB_COUNTRY Страна Должности PK_COUNTRY RESTRICT RESTRICT

При выполнении запроса 2.10 со значением PK_COUNTRY из результата запроса 2.9 для параметра запроса INDEX_NAME будет получена одна запись (табл. 2.6).

Таблица 2.6. Результат выполнения запроса 2.10 со значением параметра PK_COUNTRY.

TABLE_NAME FIELD_NAME FIELD_DESCRIPTION TABLE_DESCRIPTION
COUNTRY CTR_COUNTRY Название страны Страны

На основе этих данных может быть сформировано сообщение для пользователя следующего содержания:

Значение поля “Страна” таблицы “Должности” должно соответствовать значению поля “Название страны” таблицы “Страны”.

2.1.3.2. Удаление или изменение поля главной таблицы, входящего во внешний ключ с правилом обновления NO ACTION

Если для внешнего ключа указано правило удаления NO ACTION и в этой таблице имеются записи, ссылающиеся на удаляемую запись из внешней таблицы, то сервер Firebird сгенерирует сообщение об ошибке с кодом SQLCode -530 и следующим содержанием:

violation of FOREIGN KEY constraint "".
violation of FOREIGN KEY constraint "ВНЕШНИЙ КЛЮЧ" on table "ТАБЛИЦА".
Foreign key references are present for the record.

Как можно заметить, что хотя код ошибки совпадает с предыдущим случаем, но сообщения различаются. Для определения взаимосвязи между главной и подчиненной таблицей можно использовать запросы из предыдущего раздела (запросы 2.9 и 2.10).

Если связь между таблицами осуществляется по одному полю, то сообщение для пользователя может выглядеть следующим образом:

Нельзя модифицировать записи из таблицы “Описание главной таблицы”, для которых значения поля “Описание поля главной таблицы” используются в подчиненной таблице “Описание подчиненной таблицы” в качестве значений для поля “Описание поля подчиненной таблицы”.

Если же связь между таблицами осуществляется по нескольким полям, то сообщение может иметь, например, следующее содержание:

Нельзя модифицировать записи из таблицы “Описание главной таблицы”, для которых значения полей “Описание поля 1 главной таблицы”, Описание поля 2 главной таблицы” используются в подчиненной таблице “Описание подчиненной таблицы” в качестве значений для полей “Описание поля 1 подчиненной таблицы”, “Описание поля 2 подчиненной таблицы”.

Аналогичная ситуация происходит, если ограничение NO ACTION установлено для операции изменения данных. В сообщении об ошибке сервер не предоставляет информации о том, при какой именно операции она возникла: при удалении записи в главной таблице или при её изменении. Для определения операции, при которой произошла ошибка, можно использовать значения полей UPDATE_RULE и DELETE_RULE результата запроса 2.9. Правилу обновления NO ACTION для этих полей соответствует значение RESTRICT. Если значение RESTRICT является результатом только одного поля результата запроса, то в сообщении можно точно указать операцию, которая привела к возникновению ошибки.

Пример 4

При попытке удаления записи из таблицы COUNTRY, которая является главной для таблицы JOB, и связь между которыми осуществляется соответственно по полям CTR_COUNTRY и JOB_COUNTRY, сервер сгенерирует сообщение следующего содержания:

violation of FOREIGN KEY constraint "".
violation of FOREIGN KEY constraint "FK_JOB_COUNTRY" on table "JOB".
Foreign key references are present for the record.

После анализа структуры БД с помощью запросов 2.9 и 2.10 (табл. 2.5 и 2.6) можно сформировать сообщение следующего содержания:

Нельзя модифицировать запись из таблицы “Страны”, значения поля “Название страны” которой используются в подчиненной таблице “Должности” в качестве значений для поля “Страна”.

2.2. Реализация специальных сообщений об ошибках уровня БД

Как уже описывалось в разделе 1, в некоторых случаях необходимо формирование специальных сообщений об ошибках уровня БД. Они могут определяться кодом ошибки SQLCode и одним или двумя ключевыми словами из сообщения об ошибке. Информацию о таких сообщениях можно хранить в базе данных и использовать для этого таблицу, например, следующей структуры:

      CREATE DOMAIN D_INT AS INTEGER;
CREATE DOMAIN D_STR100 AS VARCHAR(100);
CREATETABLE DB_ERRORS (
    DBE_SQLCODE  D_INT NOTNULL,
    DBE_SQLMSG   D_STR100 NOTNULL,
    DBE_MSG      BLOB SUB_TYPE 1 SEGMENT SIZE 80 NOTNULL
);
ALTERTABLE DB_ERRORS ADDCONSTRAINT PK_DB_ERRORS PRIMARYKEY (DBE_SQLCODE, DBE_SQLMSG);
CREATEINDEX IDX_DBE_SQLCODE ON DB_ERRORS (DBE_SQLCODE);
CREATEINDEX IDX_DBE_SQLMSG ON DB_ERRORS (DBE_SQLMSG);

Поле DBE_SQLCODE предназначено для кода ошибки SQLCode, поле DBE_SQLMSG – для сохранения одного или нескольких фрагментов сообщения (они могут разделяться, например, точкой с запятой), по которым можно однозначно идентифицировать ошибку. В поле DBE_MSG – текст сообщения об ошибке.

2.3. Формирование сообщений об ошибках для проверок (CHECK)

Сервер Firebird позволяет создать ограничения CHECK двух типов: для таблиц и для доменов. При возникновении ошибки ограничения для таблицы сервер генерирует сообщение об ошибке с кодом SQLCode -297 и следующим текстом:

Operation violates CHECK constraint  on view or table.
Operation violates CHECK constraint “ОГРАНИЧЕНИЕ” on view or table “имя таблицы”.
At trigger “СИСТЕМНЫЙ ТРИГЕР”.

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

При нарушении ограничения, созданного для домена, сервер генерирует сообщение об ошибке с кодом SQLCode -625 и текстом:

The insert failed because a column definition includes validation constraints. validation error for column “ПОЛЕ ТАБЛИЦЫ”, value "ЗНАЧЕНИЕ".

Синтаксис ограничений CHECK довольно разнообразен, логика использования ограничений также может быть довольно сложной. Поэтому для них удобнее использовать специальные сообщения. Для обработки ошибки ограничения домена также потребуется получить информацию о названии домена, так как в сообщении об ошибке оно не указывается. Для этого можно использовать системную таблицу RDB$RELATION_FIELDS, в поле FIELD_SOURCE которой хранится информации об имени домена, на основе которого создано поле (запрос 2.15).

      SELECT RDB$FIELD_SOURCE
FROM  RDB$RELATION_FIELDS
WHERE RDB$FIELD_NAME = :FIELD_NAME

Пример 6

Для таблицы JOB создана проверка CK_SALARY для значений полей JOB_MIN_SALARY и JOB_MAX_SALARY. Если в таблицу JOB попробовать добавить запись, в которой значение поля JOB_MAX_SALARY не будет больше значения поля JOB_MIN_SALARY, то сервер сгенерирует сообщение об ошибке:

Operation violates CHECK constraint  on view or table.
Operation violates CHECK constraint CK_SALARY on view or table JOB.
At trigger 'CHECK_4'.

Для ограничения CK_SALARY запись в таблице DB_ERRORS может иметь следующее содержание:

        INSERT
        INTO DB_ERRORS (DBE_SQLCODE, DBE_SQLMSG, DBE_MSG) VALUES (-297, 'CK_SALARY', ‘Значение минимальной оплаты должно быть меньше значения максимальной оплаты в таблице “Должности”’);

Сообщение для пользователя в этом случае будет формироваться на основе значения поля DBE_MSG таблицы DB_ERRORS:

Значение минимальной оплаты должно быть меньше значения максимальной оплаты в таблице “Должности”

Пример 7

В таблице JOB для поля JOB_CODE используется ограничение домена D_JOBCODE. Если попробовать добавить в таблицу запись с числовым значением для этого поля, например, равным 1, то сервер сгенерирует сообщение об ошибке следующего содержания:

The insert failed because a column definition includes validation constraints. validation error for column JOB_CODE, value "1".

Если выполнить запрос 2.15 со значением параметра JOB_CODE, он вернет значение домена “D_JOBCODE” для этого поля. Для сообщения об этой ошибке в таблице DB_ERRORS может быть создана запись следующего содержания:

        INSERT
        INTO DB_ERRORS (DBE_SQLCODE, DBE_SQLMSG, DBE_MSG) VALUES (-625, 'column definition includes validation constraints; JOB_CODE', ‘Значение должно быть символьным’);

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

Для поля “Код” таблицы “Должности” значение должно быть символьным (попытка задать значение “1”).

2.4. Отсутствие библиотеки UDF, используемой в БД

Данную ошибку можно считать примером специфичной ошибки сервера Firebird (раздел 1). Ошибка возникает при отсутствии динамической библиотеки UDF, которая используется при вызове функций. Библиотека UDF загружается сервером Firebird только при вызове функции из неё в запросе, триггере или хранимой процедуре. Это часто приводит к тому, что об отсутствии библиотеки становится известно только при использовании функции, которая содержится в ней. Если функции из библиотеки используются при сохранении данных, то это может приводить к их потере.

Во избежание потерь данных из-за отсутствия библиотеки UDF необходимо выполнять проверку её наличия непосредственно при запуске программы. Так как сервер пытается получить доступ к библиотекам UDF только при вызове функций из неё, то для этого можно использовать хранимую процедуру, в которой выполняется вызов хотя бы одной функции из каждой библиотеки UDF. В приложении достаточно будет выполнить эту хранимую процедуру при начале работы программы.

При отсутствии библиотеки UDF сервер Firebird генерирует сообщение об ошибке с кодом SQLCode -104 и текстом, содержащим название функции, которую не удалось выполнить:

Invalid request BLR at offset “ЧИСЛО”. Function “НАЗВАНИЕ ФУНКЦИИ” is not defined. Module name or entrypoint could not be found.

Для получения информации о названии библиотеки UDF можно использовать системную таблицу RDB$FUNCTIONS, в которой хранится информация о функциях БД (запрос 2.11):

      SELECT  RDB$MODULE_NAME
FROM RDB$FUNCTIONS
WHERE RDB$FUNCTION_NAME = :FUNCTION_NAME

В ряде случаев может также возникать путаница из-за того, что сервер Firebird был переустановлен, а используемые им ранее файлы или каталоги не были удалены. Это часто приводит к тому, что библиотеки UDF копируются пользователем в “старые” каталоги, в результате это еще больше вводит в заблуждение.

Во избежание такой ситуации можно рекомендовать информировать пользователя не только о необходимых библиотеках UDF, но и о требуемом месте их расположения. Последнюю информацию можно получить с помощью сервисных функций клиентской библиотеки сервера БД Firebird.

Пример 5

Например, пусть в БД используется функция генерирования уникального идентификатора GUID, которая содержится в библиотеке UDF GUID_UDF.DLL:

        DECLARE EXTERNAL FUNCTION GUID_CREATE
    INTEGER,
    CSTRING(36)
RETURNS PARAMETER 2
ENTRY_POINT 'CreateReverseGUID' MODULE_NAME 'GUID_UDF.dll';

Для проверки наличия библиотеки GUID_UDF.DLL при запуске программы выполняется хранимая процедура, которая вызывает эту функцию:

        SET TERM ^ ;
CREATEPROCEDURE UDF_TEST 
asdeclare variable var_guid varchar(36);
beginSELECT GUID_CREATE(0) FROM RDB$DATABASEINTO VAR_GUID;
  suspend;
end^

Если библиотека GUID_UDF.DLL отсутствует в каталоге UDF сервера Firebird, то сервер сгенерирует следующее сообщение об ошибке (числовое значение в сообщении зависит от конкретной БД):

Invalid token.
invalid request BLR at offset 4462.
function GUID_CREATE is not defined.
module name or entrypoint could not be found.

Выполнение запроса 2.11 со значением параметра “GUID_CREATE” вернет название библиотеки “GUID_UDF.DLL”, в которой содержится данная функция. В конечном варианте сообщение об ошибке может выглядеть, например, следующим образом:

Для работы с базой данных "c:\database.fdb" необходимо наличие библиотеки пользовательских функций (UDF) "GUID_UDF.dll" в каталоге сервера Firebird "C:\Program Files\Firebird\Firebird_2_0\UDF" на компьютере "PC01"'

Заключение

Целью данной статьи является показать основные идеи метода, который может использоваться для формирования информативных сообщений об ошибках БД. Хотя за рамками статьи остались некоторые моменты его реализации, хочется надеяться, что описанный в статье подход позволит уменьшить трудозатраты при разработке программного обеспечения и повысить его надежность.

Литература

  1. Борри Х. Firebird: руководство разработчика баз данных: Пер. с англ.-СПб.:БХВ-Петербург, 2006-1104 с.
  2. Кузьменко Д. Запросы к системным таблицам IB. http://www.ibase.ru/devinfo/sysqry.htm
  3. Смирнов А. Заметки о системных таблицах InterBase. http://www.citforum.ru/database/interbase/index.shtml
  4. Лихачев В. Н. Локализация ошибок в приложениях Delphi c помощью библиотеки Jedi Code Library // RSDN Magazine. - 2005. - № 3. - C. 23-27.

Эта статья опубликована в журнале RSDN Magazine #4-2008. Информацию о журнале можно найти здесь
    Сообщений 15    Оценка 20 [+0/-2]         Оценить