35.8. Обработка ошибок #
В этом разделе описывается, как можно обрабатывать исключительные условия и предупреждения в программе со встраиваемым SQL. Для этого предназначены два средства, которые могут дополнять друг друга.
- Можно настроить функции-обработчики для обработки предупреждений и ошибок, воспользовавшись командой
WHENEVER. - Подробную информацию об ошибке или предупреждении можно получить через переменную
sqlca.
35.8.1. Установка обработчиков #
Один простой метод перехвата ошибок и предупреждений заключается в назначении определённого действия, которое будет выполняться при некотором условии. В общем виде:
EXEC SQL WHENEVERусловиедействие;
Здесь условие может быть следующим:
SQLERROR#Указанное действие вызывается, когда при выполнении SQL-оператора происходит ошибка.
SQLWARNING#Указанное действие вызывается, когда при выполнении SQL-оператора выдаётся предупреждение.
NOT FOUND#Указанное действие вызывается, когда SQL-оператор получает или обрабатывает ноль строк. (Это обстоятельство не считается ошибкой, но бывает полезно отследить его.)
действие может быть следующим:
CONTINUE#Это фактически означает, что условие игнорируется. Это поведение по умолчанию.
GOTOметкаGO TO#меткаПерейти к указанной метке (используя оператор
gotoязыка C).SQLPRINT#Вывести сообщение в устройство стандартного вывода. Это полезно для простых программ или при разработке прототипов. Содержание этого сообщения не настраивается.
STOP#Вызвать
exit(1), что приведёт к завершению программы.DO BREAK#Выполнить оператор
breakязыка C. Этот вариант следует использовать только в циклах или операторахswitch.DO CONTINUE#Выполнить оператор
continueязыка C. Этот вариант следует использовать только в циклах. Данный оператор передаёт управление в начало цикла.CALLимя(аргументы)DO#имя(аргументы)Вызвать указанные функции C с заданными аргументами. (Эти вызовы имеют смысловые отличия от
CALLиDOв обычной грамматике Postgres Pro.)
В стандарте SQL описаны только действия CONTINUE и GOTO (и GO TO).
Ниже показан простой пример использования этих команд. Эта конструкция выводит простое сообщение при выдаче предупреждения и прерывает программу в случае ошибки:
EXEC SQL WHENEVER SQLWARNING SQLPRINT; EXEC SQL WHENEVER SQLERROR STOP;
Оператор EXEC SQL WHENEVER является директивой препроцессора SQL, а не оператором языка C. Устанавливаемое им действие при ошибках или предупреждениях применяется ко всем встраиваемым SQL-операторам ниже точки, где устанавливается обработчик, если только это действие не было изменено после первой команды EXEC SQL WHENEVER, и до SQL-оператора, вызвавшего это условие, вне зависимости от хода выполнения программы на C. Поэтому обе следующие программы на C не дадут желаемого эффекта:
/*
* НЕПРАВИЛЬНО
*/
int main(int argc, char *argv[])
{
...
if (verbose) {
EXEC SQL WHENEVER SQLWARNING SQLPRINT;
}
...
EXEC SQL SELECT ...;
...
}/*
* НЕПРАВИЛЬНО
*/
int main(int argc, char *argv[])
{
...
set_error_handler();
...
EXEC SQL SELECT ...;
...
}
static void set_error_handler(void)
{
EXEC SQL WHENEVER SQLERROR STOP;
}35.8.2. sqlca #
Для более гибкой обработки ошибок в интерфейсе встраиваемого SQL представлена глобальная переменная с именем sqlca (SQL Communication Area, Область сведений SQL), имеющая следующую структуру:
struct
{
char sqlcaid[8];
long sqlabc;
long sqlcode;
struct
{
int sqlerrml;
char sqlerrmc[SQLERRMC_LEN];
} sqlerrm;
char sqlerrp[8];
long sqlerrd[6];
char sqlwarn[8];
char sqlstate[5];
} sqlca; (В многопоточной программе каждый поток автоматически получает собственную копию sqlca. Это работает подобно стандартной в C глобальной переменной errno.)
Структура sqlca покрывает и предупреждения, и ошибки. Если в процессе выполнения оператора выдаётся несколько предупреждений или ошибок, sqlca будет содержать сведения только о последнем(ей) из них.
Если последний оператор SQL выполняется без ошибки, sqlca.sqlcode будет содержать 0, а sqlca.sqlstate — "00000". Если выдаётся предупреждение или ошибка, в sqlca.sqlcode будет содержаться отрицательное число, а sqlca.sqlstate будет отличаться от "00000". Положительное значение sqlca.sqlcode устанавливается при нейтральном событии, например, когда последний запрос возвращает ноль строк. Поля sqlcode и sqlstate представляют две различные схемы кодов ошибок; подробнее они описаны ниже.
Если последний SQL-оператор был успешным, в sqlca.sqlerrd[1] содержится OID обработанной строки (если это уместно), а в sqlca.sqlerrd[2] количество обработанных или возвращённых строк (если это уместно для команды).
В случае ошибки или предупреждения sqlca.sqlerrm.sqlerrmc будет содержать строку, описывающую ошибку. Поле sqlca.sqlerrm.sqlerrml содержит длину сообщения об ошибке, которое хранится в sqlca.sqlerrm.sqlerrmc (результат функции strlen(), который не очень интересен для программиста C). Заметьте, что некоторые сообщения могут не умещаться в массив sqlerrmc фиксированного размера; они будут обрезаться.
В случае предупреждения, в sqlca.sqlwarn[2] записывается символ W. (Во всех других случаях значение будет отличным от W.) Символ W в sqlca.sqlwarn[1] показывает, что значение было обрезано при сохранении в переменной среды С. W в sqlca.sqlwarn[0] устанавливается, если предупреждение отмечается в каком-либо другом элементе массива.
Поля sqlcaid, sqlabc, sqlerrp и остальные элементы sqlerrd и sqlwarn в настоящее время не содержат полезной информации.
Структура sqlca не определена в стандарте SQL, но реализована в нескольких других СУБД SQL. Принципиально она определяется одинаково, но если вы хотите, чтобы ваши приложения были переносимыми, тщательно изучите различия реализаций.
В следующем примере, демонстрирующем применение WHENEVER в сочетании с sqlca, выводится содержимое sqlca при возникновении ошибки. Это может быть полезно для отладки или в прототипах, пока не реализован более «дружественный пользователю» обработчик ошибок.
EXEC SQL WHENEVER SQLERROR CALL print_sqlca();
void
print_sqlca()
{
fprintf(stderr, "==== sqlca ====\n");
fprintf(stderr, "sqlcode: %ld\n", sqlca.sqlcode);
fprintf(stderr, "sqlerrm.sqlerrml: %d\n", sqlca.sqlerrm.sqlerrml);
fprintf(stderr, "sqlerrm.sqlerrmc: %s\n", sqlca.sqlerrm.sqlerrmc);
fprintf(stderr, "sqlerrd: %ld %ld %ld %ld %ld %ld\n", sqlca.sqlerrd[0],sqlca.sqlerrd[1],sqlca.sqlerrd[2],
sqlca.sqlerrd[3],sqlca.sqlerrd[4],sqlca.sqlerrd[5]);
fprintf(stderr, "sqlwarn: %d %d %d %d %d %d %d %d\n", sqlca.sqlwarn[0], sqlca.sqlwarn[1], sqlca.sqlwarn[2],
sqlca.sqlwarn[3], sqlca.sqlwarn[4], sqlca.sqlwarn[5],
sqlca.sqlwarn[6], sqlca.sqlwarn[7]);
fprintf(stderr, "sqlstate: %5s\n", sqlca.sqlstate);
fprintf(stderr, "===============\n");
}Результат может выглядеть следующим образом (при ошибке, вызванной опечаткой в имени таблицы):
==== sqlca ==== sqlcode: -400 sqlerrm.sqlerrml: 49 sqlerrm.sqlerrmc: relation "pg_databasep" does not exist on line 38 sqlerrd: 0 0 0 0 0 0 sqlwarn: 0 0 0 0 0 0 0 0 sqlstate: 42P01 ===============
35.8.3. SQLSTATE и SQLCODE #
Поля sqlca.sqlstate и sqlca.sqlcode отражают две различные схемы, представляющие коды ошибок. Обе схемы пришли из стандарта SQL, но схема SQLCODE была признана устаревшей в редакции SQL-92 и исключена в последующих редакциях. Поэтому в новых приложениях настоятельно рекомендуется использовать SQLSTATE.
SQLSTATE задаётся в массиве из пяти символов. Эти пять символов содержат цифры или буквы в верхнем регистре, представляющие коды различных условий ошибок и предупреждений. SQLSTATE определяется по иерархической схеме: первые два символа обозначают общий класс условия, а следующие три — подкласс общего условия. Успешное состояние обозначается кодом 00000. По большей части коды SQLSTATE определены в стандарте SQL. Сервер Postgres Pro поддерживает коды ошибок SQLSTATE естественным образом; поэтому используя во всех приложениях именно эту схему кодов ошибок, можно добиться высокой степени согласованности. За дальнейшими сведениями обратитесь к Приложению A.
SQLCODE — устаревшая схема, в которой коды ошибок представлены просто целыми числами. Значение 0 обозначает успешное завершение, положительное значение — успешное завершение с дополнительной информацией, а отрицательное говорит об ошибке. В стандарте SQL определено только положительное значение +100, показывающее, что последняя команда вернула или затронула ноль строк, но отрицательные значения не определены. Таким образом, с этой схемой нельзя рассчитывать на переносимость и она не имеет иерархической структуры. Исторически сложилось, что процессор встраиваемого SQL для Postgres Pro назначает некоторые определённые значения SQLCODE для собственного использования; они перечислены ниже с числовыми значениями и символьными именами. Помните, что эти коды несовместимы с другими реализациями SQL. Поэтому для упрощения перевода приложений на схему SQLSTATE вместе с этими кодами перечисляются соответствующие значения SQLSTATE. Однако однозначного соответствия один-к-одному или один-ко-многим между этими двумя схемами не существует (на самом деле это соответствие многие-ко-многим), поэтому следует свериться со списком SQLSTATE в Приложении A в каждом случае.
SQLCODE может принимать следующие значения:
- 0 (
ECPG_NO_ERROR) # Показывает, что ошибки нет. (SQLSTATE 00000)
- 100 (
ECPG_NOT_FOUND) # Это нейтральное условие, показывающее, что последняя команда вернула или обработала ноль строк, либо курсор достиг конца. (SQLSTATE 02000)
Выбирая данные из курсора в цикле, можно проверять этот код, чтобы понять, когда нужно прервать цикл, следующим образом:
while (1) { EXEC SQL FETCH ... ; if (sqlca.sqlcode == ECPG_NOT_FOUND) break; }Но
WHENEVER NOT FOUND DO BREAKвнутри по сути делает это же, поэтому такое явное условие обычно ничем не лучше.- -12 (
ECPG_OUT_OF_MEMORY) # Указывает, что закончилась виртуальная память. Числовое значение определено как
-ENOMEM. (SQLSTATE YE001)- -200 (
ECPG_UNSUPPORTED) # Указывает, что препроцессор сгенерировал код, который не понимает библиотека. Возможно, вы используете несовместимые версии препроцессора и библиотеки. (SQLSTATE YE002)
- -201 (
ECPG_TOO_MANY_ARGUMENTS) # Это означает, что в команде было указано больше переменных среды, чем она ожидает. (SQLSTATE 07001 или 07002)
- -202 (
ECPG_TOO_FEW_ARGUMENTS) # Это означает, что в команде было указано меньше переменных среды, чем она ожидает. (SQLSTATE 07001 или 07002)
- -203 (
ECPG_TOO_MANY_MATCHES) # Это означает, что запрос вернул несколько строк, но оператор был подготовлен только для одной строки результата (например, потому что переданные переменные — не массивы). (SQLSTATE 21000)
- -204 (
ECPG_INT_FORMAT) # Переменная среды C типа
intи данные в базе имеют разные типы, и в этих данных содержится значение, которое нельзя преобразовать вint. Для этого преобразования библиотека использует функциюstrtol(). (SQLSTATE 42804)- -205 (
ECPG_UINT_FORMAT) # Переменная среды C типа
unsigned intи данные в базе имеют разные типы, и в этих данных содержится значение, которое нельзя преобразовать вunsigned int. Для этого преобразования библиотека использует функциюstrtoul(). (SQLSTATE 42804)- -206 (
ECPG_FLOAT_FORMAT) # Переменная среды C типа
floatи данные в базе имеют разные типы, и в этих данных содержится значение, которое нельзя преобразовать воfloat. Для этого преобразования библиотека использует функциюstrtod(). (SQLSTATE 42804)- -207 (
ECPG_NUMERIC_FORMAT) # Переменная среды C типа
numericи данные в базе имеют разные типы, и в этих данных содержится значение, которое нельзя преобразовать вnumeric. (SQLSTATE 42804)- -208 (
ECPG_INTERVAL_FORMAT) # Переменная среды C типа
intervalи данные в базе имеют разные типы, и в этих данных содержится значение, которое нельзя преобразовать вinterval. (SQLSTATE 42804)- -209 (
ECPG_DATE_FORMAT) # Переменная среды C типа
dateи данные в базе имеют разные типы, и в этих данных содержится значение, которое нельзя преобразовать вdate. (SQLSTATE 42804)- -210 (
ECPG_TIMESTAMP_FORMAT) # Переменная среды C типа
timestampи данные в базе имеют разные типы, и в этих данных содержится значение, которое нельзя преобразовать вtimestamp. (SQLSTATE 42804)- -211 (
ECPG_CONVERT_BOOL) # Это означает, что переменная среды C имеет тип
bool, а значение в базе данных отличается от't'или'f'. (SQLSTATE 42804)- -212 (
ECPG_EMPTY) # Серверу Postgres Pro был передан пустой оператор. (Этого обычно не должно происходить в программе со встраиваемым SQL, так что это может указывать на внутреннюю ошибку.) (SQLSTATE YE002)
- -213 (
ECPG_MISSING_INDICATOR) # Возвращено значение NULL, но переменная-индикатор NULL не задана. (SQLSTATE 22002)
- -214 (
ECPG_NO_ARRAY) # Там, где требуется массив, была передана обычная переменная. (SQLSTATE 42804)
- -215 (
ECPG_DATA_NOT_ARRAY) # База данных возвратила обычную переменную там, где требуется значение-массив. (SQLSTATE 42804)
- -216 (
ECPG_ARRAY_INSERT) # Не удалось вставить значение в массив. (SQLSTATE 42804)
- -220 (
ECPG_NO_CONN) # Программа попыталась использовать несуществующее подключение. (SQLSTATE 08003)
- -221 (
ECPG_NOT_CONN) # Программа попыталась использовать подключение, которое существует, но не было открыто. (Это внутренняя ошибка.) (SQLSTATE YE002)
- -230 (
ECPG_INVALID_STMT) # Оператор, который вы пытаетесь выполнить, не был подготовлен. (SQLSTATE 26000)
- -239 (
ECPG_INFORMIX_DUPLICATE_KEY) # Ошибка повторяющегося ключа, нарушение ограничения уникальности (режим совместимости с Informix). (SQLSTATE 23505)
- -240 (
ECPG_UNKNOWN_DESCRIPTOR) # Указанный дескриптор не найден. Оператор, который вы пытаетесь использовать, не был подготовлен. (SQLSTATE 33000)
- -241 (
ECPG_INVALID_DESCRIPTOR_INDEX) # Указанный индекс дескриптора вне диапазона. (SQLSTATE 07009)
- -242 (
ECPG_UNKNOWN_DESCRIPTOR_ITEM) # Запрошен неверный элемент дескриптора. (Это внутренняя ошибка.) (SQLSTATE YE002)
- -243 (
ECPG_VAR_NOT_NUMERIC) # При выполнении динамического оператора база данных возвратила числовое значение, тогда как переменная среды C — не числовая. (SQLSTATE 07006)
- -244 (
ECPG_VAR_NOT_CHAR) # При выполнении динамического оператора база данных возвратила не числовое значение, тогда как переменная среды C — числовая. (SQLSTATE 07006)
- -284 (
ECPG_INFORMIX_SUBSELECT_NOT_ONE) # Результат подзапроса представлен не одной строкой (режим совместимости с Informix). (SQLSTATE 21000)
- -400 (
ECPG_PGSQL) # Ошибка произошла на стороне сервера Postgres Pro. В тексте ошибки содержится сообщение об ошибке от сервера Postgres Pro.
- -401 (
ECPG_TRANS) # Сервер Postgres Pro сообщает, что клиент не может запускать, фиксировать или отменять транзакцию. (SQLSTATE 08007)
- -402 (
ECPG_CONNECT) # Попытка подключения к базе данных была неудачной. (SQLSTATE 08001)
- -403 (
ECPG_DUPLICATE_KEY) # Ошибка повторяющегося ключа, нарушение ограничения уникальности. (SQLSTATE 23505)
- -404 (
ECPG_SUBSELECT_NOT_ONE) # Результат подзапроса представлен не одной строкой. (SQLSTATE 21000)
- -602 (
ECPG_WARNING_UNKNOWN_PORTAL) # Указано неверное имя курсора. (SQLSTATE 34000)
- -603 (
ECPG_WARNING_IN_TRANSACTION) # Транзакция в процессе выполнения. (SQLSTATE 25001)
- -604 (
ECPG_WARNING_NO_TRANSACTION) # Нет активной (выполняющейся) транзакции. (SQLSTATE 25P01)
- -605 (
ECPG_WARNING_PORTAL_EXISTS) # Было указано имя существующего курсора. (SQLSTATE 42P03)