38.8. Обработка ошибок #
В этом разделе описывается, как можно обрабатывать исключительные условия и предупреждения в программе со встраиваемым SQL. Для этого предназначены два средства, которые могут дополнять друг друга.
- Можно настроить функции-обработчики для обработки предупреждений и ошибок, воспользовавшись командой
WHENEVER
. - Подробную информацию об ошибке или предупреждении можно получить через переменную
sqlca
.
38.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; }
38.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 ===============
38.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)