33.8. Обработка ошибок#
33.8. Обработка ошибок #
Этот раздел описывает, как можно обрабатывать исключительные ситуации и предупреждения в программе на встроенном SQL. Для этого существует два неисключающих друг друга механизма.
- Обратные вызовы могут быть настроены для обработки предупреждений и ошибок с использованием команды
WHENEVER
. - Подробную информацию об ошибке или предупреждении можно получить из переменной
sqlca
.
33.8.1. Настройка обратных вызовов #
Один простой способ обнаружить ошибки и предупреждения - установить определенное действие, которое будет выполняться при возникновении определенного условия. Обычно:
EXEC SQL WHENEVERcondition
action
;
condition
можно записать одним из следующих способов:
SQLERROR
#Указанное действие вызывается каждый раз, когда происходит ошибка во время выполнения SQL-запроса.
SQLWARNING
#Указанное действие вызывается каждый раз, когда происходит предупреждение во время выполнения SQL-запроса.
NOT FOUND
#Указанное действие вызывается, когда SQL-запрос извлекает или изменяет ноль строк. (Это условие не является ошибкой, но вам может быть интересно обработать его особым образом).
action
можно записать одним из следующих способов:
CONTINUE
#Это фактически означает, что условие игнорируется. Это значение по умолчанию.
GOTO
label
GO TO
#label
Перейдите к указанной метке (используя оператор C
goto
).SQLPRINT
#Вывести сообщение на стандартный поток ошибок. Это полезно для простых программ или во время прототипирования. Детали сообщения не могут быть настроены.
STOP
#Вызовите
exit(1)
, который завершит программу.DO BREAK
#Выполните оператор C
break
. Это следует использовать только в циклах или операторахswitch
.DO CONTINUE
#Выполните оператор C
continue
. Это следует использовать только в операторах цикла. Если он будет выполнен, управление вернется в начало цикла.CALL
name
(args
)DO
#name
(args
)Вызовите указанные функции C с указанными аргументами. (Это использование отличается от значения
CALL
иDO
в обычной грамматике PostgreSQL).
Стандарт 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 не будет иметь желаемого эффекта:
/* * WRONG */ int main(int argc, char *argv[]) { ... if (verbose) { EXEC SQL WHENEVER SQLWARNING SQLPRINT; } ... EXEC SQL SELECT ...; ... }
/* * WRONG */ int main(int argc, char *argv[]) { ... set_error_handler(); ... EXEC SQL SELECT ...; ... } static void set_error_handler(void) { EXEC SQL WHENEVER SQLERROR STOP; }
33.8.2. sqlca #
Для более мощной обработки ошибок, встроенный SQL-интерфейс предоставляет глобальную переменную с именем sqlca
(SQL communication area), которая имеет следующую структуру:
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
). Если sqlca.sqlwarn[1]
установлено в значение W
, то значение было усечено при сохранении в переменной хоста. sqlca.sqlwarn[0]
устанавливается в значение W
, если любой из других элементов установлен для указания предупреждения.
Поля 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 ===============
33.8.3. SQLSTATE
vs. SQLCODE
#
Поля sqlca.sqlstate
и sqlca.sqlcode
представляют собой две разные схемы, предоставляющие коды ошибок. Оба они производные от стандарта SQL, но SQLCODE
был помечен как устаревший в стандарте SQL-92 и был удален в последующих версиях. Поэтому новым приложениям настоятельно рекомендуется использовать SQLSTATE
.
SQLSTATE
- это массив из пяти символов. Пять символов содержат цифры или заглавные буквы, которые представляют коды различных ошибок и предупреждений.
SQLSTATE
имеет иерархическую схему: первые два символа указывают на общий класс условия, последние три символа указывают на подкласс общего условия. Успешное состояние указывается кодом 00000
. Коды SQLSTATE
в большинстве своем определены в стандарте SQL. Сервер Tantor BE нативно поддерживает коды ошибок SQLSTATE
; поэтому высокая степень согласованности может быть достигнута путем использования этой схемы кодов ошибок во всех приложениях. Дополнительную информацию см. в Предметный указатель A.
SQLCODE
, устаревшая схема кодов ошибок, представляет собой простое целое число. Значение 0 указывает на успешное выполнение, положительное значение указывает на успешное выполнение с дополнительной информацией, отрицательное значение указывает на ошибку. Стандарт SQL определяет только положительное значение +100, которое указывает на то, что последняя команда вернула или затронула ноль строк, и не определяет конкретные отрицательные значения. Поэтому эта схема может обеспечить только низкую переносимость и не имеет иерархического присвоения кодов. Исторически, встроенный процессор SQL для Tantor BE назначал некоторые конкретные значения 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
) # Переменная хоста имеет тип
int
, а данные в базе данных имеют другой тип и содержат значение, которое не может быть интерпретировано какint
. Библиотека использует функциюstrtol()
для этого преобразования. (SQLSTATE 42804)- -205 (
ECPG_UINT_FORMAT
) # Переменная хоста имеет тип
unsigned int
, а данные в базе данных имеют другой тип и содержат значение, которое нельзя интерпретировать какunsigned int
. Библиотека использует функциюstrtoul()
для этого преобразования. (SQLSTATE 42804)- -206 (
ECPG_FLOAT_FORMAT
) # Переменная хоста имеет тип
float
, а данные в базе данных имеют другой тип и содержат значение, которое не может быть интерпретировано какfloat
. Библиотека использует функциюstrtod()
для этого преобразования. (SQLSTATE 42804)- -207 (
ECPG_NUMERIC_FORMAT
) # Переменная хоста имеет тип
numeric
, а данные в базе данных имеют другой тип и содержат значение, которое нельзя интерпретировать как значениеnumeric
. (SQLSTATE 42804)- -208 (
ECPG_INTERVAL_FORMAT
) # Переменная хоста имеет тип
interval
, а данные в базе данных имеют другой тип и содержат значение, которое нельзя интерпретировать как значение типаinterval
. (SQLSTATE 42804)- -209 (
ECPG_DATE_FORMAT
) # Переменная хоста имеет тип
date
, а данные в базе данных имеют другой тип и содержат значение, которое нельзя интерпретировать как значение типаdate
. (SQLSTATE 42804)- -210 (
ECPG_TIMESTAMP_FORMAT
) # Переменная хоста имеет тип
timestamp
, а данные в базе данных имеют другой тип и содержат значение, которое нельзя интерпретировать как значение типаtimestamp
. (SQLSTATE 42804)- -211 (
ECPG_CONVERT_BOOL
) # Это означает, что переменная хоста имеет тип
bool
, а значение в базе данных не является ни't'
, ни'f'
. (SQLSTATE 42804)- -212 (
ECPG_EMPTY
) # Запрос, отправленный на сервер Tantor BE, был пустым. (Это обычно не может произойти во встроенной 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
) # Во время выполнения динамического оператора база данных вернула числовое значение, а хост-переменная не была числовой. (SQLSTATE 07006)
- -244 (
ECPG_VAR_NOT_CHAR
) # Во время выполнения динамического оператора база данных вернула нечисловое значение, а хост-переменная была числовой. (SQLSTATE 07006)
- -284 (
ECPG_INFORMIX_SUBSELECT_NOT_ONE
) # Результат подзапроса не является единственной строкой (режим совместимости с Informix). (SQLSTATE 21000)
- -400 (
ECPG_PGSQL
) # Некоторая ошибка, вызванная сервером Tantor BE. Сообщение содержит сообщение об ошибке от сервера Tantor BE.
- -401 (
ECPG_TRANS
) # Сервер Tantor BE сообщил, что невозможно начать, зафиксировать или откатить транзакцию. (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)