Сообщения об ошибках на сервере
Сообщения об ошибках, предупреждения и обычные сообщения, выдаваемые в коде сервера должны создаваться функцией ereport
или родственной ее предшественницей elog
. Использование этой функции достаточно сложно и требует некоторых пояснений.
Для каждого сообщения есть два обязательных элемента: уровень серьезности (от DEBUG
до PANIC
) и основной текст сообщения. Кроме того, есть необязательные элементы, наиболее распространенным из которых является код идентификатора ошибки, который соответствует SQL-соглашениям SQLSTATE. Сам 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_DIVISION_BY_ZERO),
errmsg("division by zero")));
Дополнительные скобки требовались до PostgreSQL 12, но теперь они не обязательны.
Более сложный пример:
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."));
Это иллюстрирует использование кодов формата для вставки значений времени выполнения в текст сообщения. Также предусмотрено необязательное сообщение-подсказка. Вызовы вспомогательных функций могут быть записаны в любом порядке, но по традиции errcode
и errmsg
появляются первыми.
Если уровень серьезности равен 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
(то есть, значение, которое было текущим, когда был достигнут вызовereport
; измененияerrno
в вспомогательных процедурах отчетности не повлияют на него. Это было бы неверно, если бы написалиstrerror
(errno
) явно в списке параметровerrmsg
; соответственно, не делайте этого).%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 - целочисленное значение, которое определяет, какая форма множественного числа необходима, а остальные аргументы форматируются в соответствии с выбранной строкой формата. Подробнее см. в разделе «Рекомендации по написанию сообщений»;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
, но с поддержкой различных множественных форм сообщения. Подробнее см. раздел «Рекомендации по написанию сообщений»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
, но с поддержкой различных множественных форм сообщения. Подробнее см. раздел «Рекомендации по написанию сообщений»;errhint(const char \*msg, ...)
предоставляет дополнительное сообщение «подсказка»; это должно использоваться при предложении советов о том, как исправить проблему, а не фактических подробностей о том, что пошло не так. Строка сообщения обрабатывается так же, как и дляerrmsg
;errhint_plural(const char \*fmt\_singular, const char \*fmt\_plural, unsigned long n, ...)
похож наerrhint
, но с поддержкой различных множественных форм сообщения. Подробнее см. раздел «Рекомендации по написанию сообщений»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
; для таких сообщений эта функция предпочитается из-за простоты записи.
Советы по написанию хороших сообщений об ошибках можно найти в разделе «Руководство по стилю сообщений об ошибках»