52.2. Вывод сообщений об ошибках в коде сервера

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

У каждого сообщения есть два обязательных элемента: уровень важности (от DEBUG до PANIC) и основной текст сообщения. В дополнение к ним есть необязательные элементы, из которых часто используется код идентификатора ошибки, соответствующий определению SQLSTATE в спецификации SQL. Функция ereport сама по себе является просто оболочкой, которая существует в основном для синтаксического удобства, чтобы выдача сообщения выглядела как вызов функции в коде C. Единственный параметр, который принимает непосредственно функция ereport, это уровень важности. Основной текст и любые дополнительные элементы сообщения генерируются в результате вызова вспомогательных функций, таких как errmsg, в вызове ereport.

Типичный вызов ereport выглядит примерно так:

ereport(ERROR,
        (errcode(ERRCODE_DIVISION_BY_ZERO),
         errmsg("division by zero")));

В нём задаётся уровень важности ERROR (заурядная ошибка). В вызове errcode указывается код ошибки SQLSTATE по макросу, определённому в src/include/utils/errcodes.h. Вызов errmsg даёт текст основного сообщения. Обратите внимание на дополнительный набор скобок, окружающих вызовы вспомогательных функций — они загромождают код, но требуются синтаксисом.

Более сложный пример:

ereport(ERROR,
        (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
         errmsg("function %s is not unique",
                func_signature_string(funcname, nargs,
                                      NIL, actual_arg_types)),
         errhint("Unable to choose a best candidate function. "
                 "You might need to add explicit typecasts.")));

В нём демонстрируется использование кодов форматирования для включения значений времени выполнения в текст сообщения. Также в нём добавляется дополнительное сообщение «подсказки».

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

Для ereport предлагаются следующие вспомогательные функции:

Примечание

В вызове ereport следует использовать максимум одну из функций errtable, errtablecol, errtableconstraint, errdatatype или errdomainconstraint. Данные функции существуют для того, чтобы приложения могли извлечь имя объекта базы данных, связанного с условием ошибки, так, чтобы для этого им не требовалось разбирать текст ошибки, возможно локализованный. Эти функции должны использоваться в случае ошибок, для которых может быть желательной автоматическая обработка. Для версии PostgreSQL 9.3 этот подход распространяется полностью только на ошибки класса SQLSTATE 23 (нарушение целостности ограничения), но в будущем область его применения может быть расширена.

Существует также более старая, но тем не менее активно используемая функция elog. Вызов elog:

elog(level, "format string", ...);

полностью равнозначен вызову:

ereport(level, (errmsg_internal("format string", ...)));

Заметьте, что код ошибки SQLSTATE всегда определяется неявно, а строка сообщения не подлежит переводу. Таким образом, elog следует использовать только для внутренних ошибок и отладки на низком уровне. Любое сообщение, которое может представлять интерес для обычных пользователей, должно проходить через ereport. Тем не менее, в системе есть достаточно много внутренних проверок для случаев, «которые не должны происходить», и в них по-прежнему широко используется elog; для таких сообщений эта функция предпочитается из-за простоты записи.

Советы по написанию хороших сообщений об ошибках можно найти в Разделе 52.3.



[13] То есть значение, которое было текущим, когда была вызвана ereport; изменения errno во вспомогательных функциях выдачи сообщений на него не повлияют. Это будет не так, если вы запишете strerror(errno) явно в списке параметров errmsg; поэтому делать так не нужно.