45.2. 在服务器内部报告错误 #
服务器代码内生成的错误、警告和日志消息应使用 ereport,或其更老的近亲 elog 来创建。这一函数的用法相当复杂,因此需要一些解释。
每条消息都必须包含两个元素:严重性级别(范围从
DEBUG 到 PANIC)以及主消息文本。此外还可以有可选元素,其中最常见的是遵循 SQL 规范 SQLSTATE 约定的错误标识符代码。ereport 本身只是一个包装函数,主要为了语法上的便利,使消息生成在 C 源代码中看起来像一次函数调用。ereport 唯一直接接受的参数是严重性级别。主消息文本以及任何可选消息元素都是通过在 ereport 调用中调用辅助函数(例如 errmsg)来生成的。
对ereport的一次典型调用可能如下:
ereport(ERROR,
(errcode(ERRCODE_DIVISION_BY_ZERO),
errmsg("division by zero")));这指定了错误严重性级别ERROR(一种普通错误)。errcode调用使用定义在src/include/utils/errcodes.h中的一个宏指定 SQLSTATE 错误代码。errmsg调用提供主消息文本。注意辅助函数调用外面多了一层圆括号 — 它们虽然烦人,但在语法上是必需的。
这里有一个更复杂的示例:
ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
errmsg("function %s is not unique",
func_signature_string(funcname, nargs,
actual_arg_types)),
errhint("Unable to choose a best candidate function. "
"You might need to add explicit typecasts.")));这展示了如何用格式代码把运行时值嵌入消息文本中。此外还提供了一条可选的“提示”消息。
适用于 ereport 的辅助例程有:
errcode(sqlerrcode)为该条件指定 SQLSTATE 错误标识符代码。如果不调用这个例程,则默认错误标识符在错误严重性级别为ERROR或更高时为ERRCODE_INTERNAL_ERROR,在错误级别为WARNING时为ERRCODE_WARNING,否则(对于NOTICE及以下)为ERRCODE_SUCCESSFUL_COMPLETION。虽然这些默认值常常很方便,但在省略errcode()调用之前,始终要先想想它们是否合适。errmsg(const char *msg, ...)指定主错误消息文本,以及可能要插入其中的运行时值。插入项通过sprintf风格的格式代码指定。除了sprintf接受的标准格式代码外,还可以使用格式代码%m插入strerror为errno当前值返回的错误消息。 [9]%m不需要在errmsg的参数列表中有任何对应项。注意,在处理格式代码之前,消息字符串会先经过gettext以便可能进行本地化。errmsg_internal(const char *msg, ...)与errmsg相同,只是消息字符串不会被翻译,也不会被收入国际化消息字典。这应当用于那些“不可能发生”、大概不值得为之投入翻译精力的情况。errdetail(const char *msg, ...)提供一条可选的“详情”消息;当有额外信息但似乎不适合放在主消息中时,可使用它。消息字符串的处理方式与errmsg完全相同。errhint(const char *msg, ...)提供一条可选的“提示”消息;当要就如何修复问题给出建议(而非关于出了什么问题的事实性详情)时,可使用它。消息字符串的处理方式与errmsg完全相同。errcontext(const char *msg, ...)通常不会直接在ereport消息处调用;它用于error_context_stack回调函数中,提供错误发生时的上下文信息,例如 PL 函数中的当前位置。消息字符串的处理方式与errmsg完全相同。与其他辅助函数不同,它在每次ereport调用中可以被调用多次;这样提供的相继出现的字符串会用换行符分隔拼接起来。errposition(int cursorpos)指定错误在查询字符串中的文本位置。目前,它只对在查询处理的词法和语法分析阶段检测到的错误有用。errcode_for_file_access()是一个便捷函数,用于为与文件访问相关的系统调用失败选择合适的 SQLSTATE 错误标识符。它使用保存下来的errno来确定要生成哪种错误代码。通常应将它与主错误消息文本中的%m结合使用。errcode_for_socket_access()是一个便捷函数,用于为与套接字相关的系统调用失败选择合适的 SQLSTATE 错误标识符。
还有一个较旧的函数elog,至今仍被大量使用。一个elog调用:
elog(level, "format string", ...);
完全等价于:
ereport(level, (errmsg_internal("format string", ...)));注意,SQLSTATE 错误代码总会取默认值,而且消息字符串不会被翻译。因此,elog只应用于内部错误和低层调试日志。凡是普通用户可能感兴趣的消息,都应通过ereport。尽管如此,系统中仍有足够多的内部“不可能发生”错误检查,因此elog依然被广泛使用;对这类消息来说,由于记法更简洁,它更受青睐。
关于如何编写良好的错误消息,可参见第 45.3 节。
[9] 也就是说,是到达 ereport 调用点时的那个值;辅助报告例程内部对 errno 的更改不会影响它。如果显式写出 strerror(errno) 作为
errmsg 的参数列表内容,就不是这样了;因此不要这么做。