Сообщения об ошибках, предупреждения и обычные сообщения, выдаваемые в коде сервера должны создаваться функцией 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
предлагаются следующие вспомогательные функции:
errcode(sqlerrcode)
задаёт код идентификатора ошибки SQLSTATE для данной ошибки. Если эта функция не вызывается, подразумевается идентификатор ошибки ERRCODE_INTERNAL_ERROR
при уровне важности ERROR
или выше, либо ERRCODE_WARNING
при уровне важности WARNING
, иначе (при уровне NOTICE
или ниже) — ERRCODE_SUCCESSFUL_COMPLETION
. Хотя эти значения по умолчанию довольно разумны, всегда стоит подумать, насколько они уместны, прежде чем опустить вызов errcode()
.
errmsg(const char *msg, ...)
задаёт основной текст сообщения об ошибке и, возможно, значения времени выполнения, которые будут в него включаться. Эти включения записываются кодами формата в стиле sprintf
. В дополнение к стандартным кодам формата, принимаемым функцией sprintf
, можно использовать код формата %m
, который вставит сообщение об ошибке, возвращённое строкой strerror
для текущего значения errno
. [13] Для %m
не требуется соответствующая запись в списке параметров errmsg
. Заметьте, что эта строка будет пропущена через gettext
, то есть может быть локализована, до обработки кодов формата.
errmsg_internal(const char *msg, ...)
действует как errmsg
, но её строка сообщения не будет переводиться и включаться в словарь сообщений для интернационализации. Это следует использовать для случаев, которые «не происходят никогда», так что тратить силы на их перевод не стоит.
errmsg_plural(const char *fmt_singular, const char *fmt_plural, unsigned long n, ...)
действует подобно errmsg
, но поддерживает различные формы сообщения с множественными числами. Параметр fmt_singular
задаёт строку формата на английском для единственного числа, fmt_plural
— формат для множественного числа, n
задаёт целое число, определяющее, какая именно форма множественного числа требуется, а остальные аргументы форматируются согласно выбранной строке формата. За дополнительными сведениями обратитесь к Подразделу 55.2.2.
errdetail(const char *msg, ...)
задаёт дополнительное «подробное» сообщение; оно должно использоваться, когда есть дополнительная информация, которую неуместно включать в основное сообщение. Строка сообщения обрабатывается так же, как и для errmsg
.
errdetail_internal(const char *msg, ...)
действует как errdetail
, но её строка сообщения не будет переводиться и включаться в словарь сообщений для интернационализации. Это следует использовать для подробных сообщений, на перевод которых не стоит тратить силы, например, когда это техническая информация, непонятная большинству пользователей.
errdetail_plural(const char *fmt_singular, const char *fmt_plural, unsigned long n, ...)
действует подобно errdetail
, но поддерживает различные формы сообщения с множественными числами. За дополнительными сведениями обратитесь к Подразделу 55.2.2.
errdetail_log(const char *msg, ...)
подобна errdetail
, но выводимая строка попадает только в журнал сервера, и никогда не передаётся клиенту. Если используется и errdetail
(или один из её эквивалентов), и errdetail_log
, тогда одна строка передаётся клиенту, а другая отправляется в журнал. Это полезно для вывода подробных сообщений, имеющих конфиденциальный характер или большой размер, так что передавать их клиенту нежелательно.
errdetail_log_plural(const char *fmt_singular, const char *fmt_plural, unsigned long n, ...)
действует подобно errdetail_log
, но поддерживает различные формы сообщения с множественными числами. За дополнительными сведениями обратитесь к Подразделу 55.2.2.
errhint(const char *msg, ...)
передаёт дополнительное сообщение «подсказки»; это позволяет предложить решение проблемы, а не просто сообщить факты, связанные с ней. Строка сообщения обрабатывается так же, как и для errmsg
.
errcontext(const char *msg, ...)
обычно не вызывается непосредственно с места вызова ereport
, а используется в функциях обратного вызова error_context_stack
и выдаёт информацию о контексте, в котором произошла ошибка, например, о текущем положении в функции PL. Строка сообщения обрабатывается так же, как и для errmsg
. В отличие от других вспомогательных функций, внутри вызова ereport
её можно вызывать неоднократно; добавляемые таким образом последовательные сообщения складываются через символы перевода строк.
errposition(int cursorpos)
задаёт положение ошибки в тексте запроса. В настоящее время это полезно только для ошибок, выявляемых на этапах лексического и синтаксического анализа запроса.
errtable(Relation rel)
определяет отношение, имя и схема которого должны быть включены во вспомогательные поля сообщения об ошибке.
errtablecol(Relation rel, int attnum)
определяет столбец, имя которого, вместе с именем таблицы и схемы, должно быть включено во вспомогательные поля сообщения об ошибке.
errtableconstraint(Relation rel, const char *conname)
задаёт имя ограничения таблицы, которое вместе с именем таблицы и схемы должно быть включено во вспомогательные поля сообщения об ошибке. В данном контексте индекс считается ограничением, независимо от того, имеется ли для него запись в pg_constraint
. Заметьте, что при этом в качестве rel
нужно передавать нижележащее отношение, а не сам индекс.
errdatatype(Oid datatypeOid)
задаёт тип данных, имя которого, вместе с именем схемы, должно включаться во вспомогательные поля сообщения об ошибке.
errdomainconstraint(Oid datatypeOid, const char *conname)
задаёт имя ограничения домена, которое вместе с именем домена и схемы должно включаться во вспомогательные поля сообщения об ошибке.
errcode_for_file_access()
— вспомогательная функция, выбирающая подходящий идентификатор SQLSTATE при сбое в системном вызове, в котором происходит обращение к файловой системе. Какой код ошибки генерировать, она определяет по сохранённому значению errno
. Обычно это используется в сочетании с %m
в основном сообщении об ошибке.
errcode_for_socket_access()
— вспомогательная функция, выбирающая подходящий идентификатор SQLSTATE при сбое в системном вызове, в котором происходит обращение к сокетам.
errhidestmt(bool hide_stmt)
может быть вызвана для подавления вывода поля ОПЕРАТОР:
(STATEMENT:
) в журнал сервера. Обычно это уместно, когда само сообщение включает текст текущего оператора.
errhidecontext(bool hide_ctx)
может быть вызвана для подавления вывода поля КОНТЕКСТ:
(CONTEXT:
) в журнал сервера. Это следует использовать только для подробных отладочных сообщений, в которых одна и та же информация о контексте, выводимая в журнал, будет только чрезмерно замусоривать его.
В вызове 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
; для таких сообщений эта функция предпочитается из-за простоты записи.
Советы по написанию хороших сообщений об ошибках можно найти в Разделе 54.3.
[13] То есть значение, которое было текущим, когда была вызвана ereport
; изменения errno
во вспомогательных функциях выдачи сообщений на него не повлияют. Это будет не так, если вы запишете strerror(errno)
явно в списке параметров errmsg
; поэтому делать так не нужно.