Документация по PostgreSQL 9.4.1 | |||
---|---|---|---|
Пред. | Уровень выше | Глава 50. Соглашения по оформлению кода PostgreSQL | След. |
50.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
предлагаются следующие вспомогательные функции:
errcode(sqlerrcode)
задаёт код идентификатора ошибки SQLSTATE для данной ошибки. Если эта функция не вызывается, подразумевается идентификатор ошибки ERRCODE_INTERNAL_ERROR при уровне важности ERROR или выше, либо ERRCODE_WARNING при уровне важности WARNING, иначе (при уровне NOTICE или ниже) — ERRCODE_SUCCESSFUL_COMPLETION. Хотя эти значения по умолчанию довольно разумны, всегда стоит подумать, насколько они уместны, прежде чем опустить вызовerrcode()
.errmsg(const char *msg, ...)
задаёт основной текст сообщения об ошибке и, возможно, значения времени выполнения, которые будут в него включаться. Эти включения записываются кодами формата в стилеsprintf
. В дополнение к стандартным кодам формата, принимаемым функциейsprintf
, можно использовать код формата %m, который вставит сообщение об ошибке, возвращённое строкойstrerror
для текущего значения errno. [1] Для %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 задаёт целое число, определяющее, какая именно форма множественного числа требуется, а остальные аргументы форматируются согласно выбранной строке формата. За дополнительными сведениями обратитесь к Подразделу 51.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
, но поддерживает различные формы сообщения с множественными числами. За дополнительными сведениями обратитесь к Подразделу 51.2.2.errdetail_log(const char *msg, ...)
подобнаerrdetail
, но выводимая строка попадает только в журнал сервера, и никогда не передаётся клиенту. Если используется иerrdetail
(или один из её эквивалентов), иerrdetail_log
, тогда одна строка передаётся клиенту, а другая отправляется в журнал. Это полезно для вывода подробных сообщений, имеющих конфиденциальный характер или большой размер, так что передавать их клиенту нежелательно.errdetail_log_plural(const char *fmt_singuar, const char *fmt_plural, unsigned long n, ...)
действует подобноerrdetail_log
, но поддерживает различные формы сообщения с множественными числами. За дополнительными сведениями обратитесь к Подразделу 51.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: в журнал главного процесса. Обычно это уместно, когда само сообщение включает текст текущего оператора.
Замечание: В вызове
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
; для таких сообщений эта функция предпочитается из-за простоты записи.
Советы по написанию хороших сообщений об ошибках можно найти в Разделе 50.3.
Примечания
[1] | То есть значение, которое было текущим, когда была вызвана |
Пред. | Начало | След. |
Форматирование | Уровень выше | Руководство по стилю сообщений об ошибках |
10.1. Overview #
SQL is a strongly typed language. That is, every data item has an associated data type which determines its behavior and allowed usage. Postgres Pro has an extensible type system that is more general and flexible than other SQL implementations. Hence, most type conversion behavior in Postgres Pro is governed by general rules rather than by ad hoc heuristics. This allows the use of mixed-type expressions even with user-defined types.
The Postgres Pro scanner/parser divides lexical elements into five fundamental categories: integers, non-integer numbers, strings, identifiers, and key words. Constants of most non-numeric types are first classified as strings. The SQL language definition allows specifying type names with strings, and this mechanism can be used in Postgres Pro to start the parser down the correct path. For example, the query:
SELECT text 'Origin' AS "label", point '(0,0)' AS "value"; label | value --------+------- Origin | (0,0) (1 row)
has two literal constants, of type text
and point
. If a type is not specified for a string literal, then the placeholder type unknown
is assigned initially, to be resolved in later stages as described below.
There are four fundamental SQL constructs requiring distinct type conversion rules in the Postgres Pro parser:
- Function calls
Much of the Postgres Pro type system is built around a rich set of functions. Functions can have one or more arguments. Since Postgres Pro permits function overloading, the function name alone does not uniquely identify the function to be called; the parser must select the right function based on the data types of the supplied arguments.
- Operators
Postgres Pro allows expressions with prefix (one-argument) operators, as well as infix (two-argument) operators. Like functions, operators can be overloaded, so the same problem of selecting the right operator exists.
- Value Storage
SQL
INSERT
andUPDATE
statements place the results of expressions into a table. The expressions in the statement must be matched up with, and perhaps converted to, the types of the target columns.-
UNION
,CASE
, and related constructs Since all query results from a unionized
SELECT
statement must appear in a single set of columns, the types of the results of eachSELECT
clause must be matched up and converted to a uniform set. Similarly, the result expressions of aCASE
construct must be converted to a common type so that theCASE
expression as a whole has a known output type. Some other constructs, such asARRAY[]
and theGREATEST
andLEAST
functions, likewise require determination of a common type for several subexpressions.
The system catalogs store information about which conversions, or casts, exist between which data types, and how to perform those conversions. Additional casts can be added by the user with the CREATE CAST command. (This is usually done in conjunction with defining new data types. The set of casts between built-in types has been carefully crafted and is best not altered.)
An additional heuristic provided by the parser allows improved determination of the proper casting behavior among groups of types that have implicit casts. Data types are divided into several basic type categories, including boolean
, numeric
, string
, bitstring
, datetime
, timespan
, geometric
, network
, and user-defined. (For a list see Table 51.67; but note it is also possible to create custom type categories.) Within each category there can be one or more preferred types, which are preferred when there is a choice of possible types. With careful selection of preferred types and available implicit casts, it is possible to ensure that ambiguous expressions (those with multiple candidate parsing solutions) can be resolved in a useful way.
All type conversion rules are designed with several principles in mind:
Implicit conversions should never have surprising or unpredictable outcomes.
There should be no extra overhead in the parser or executor if a query does not need implicit type conversion. That is, if a query is well-formed and the types already match, then the query should execute without spending extra time in the parser and without introducing unnecessary implicit conversion calls in the query.
Additionally, if a query usually requires an implicit conversion for a function, and if then the user defines a new function with the correct argument types, the parser should use this new function and no longer do implicit conversion to use the old function.