服务器代码内生成的错误、警告和日志消息应使用 ereport, 或其更老的近亲 elog 来创建。这一函数的用法相当复杂, 因此需要一些解释。
每条消息都必须包含两个元素:严重性级别(范围从 DEBUG 到 PANIC,定义在 src/include/utils/elog.h 中)以及主消息文本。 此外还可以有可选元素,其中最常见的是遵循 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_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 错误标识符代码。 如果不调用这个例程,则默认错误标识符在错误严重性级别为 ERROR 或更高时为 ERRCODE_INTERNAL_ERROR,在错误级别为 WARNING 时为 ERRCODE_WARNING, 否则(对于 NOTICE 及以下)为 ERRCODE_SUCCESSFUL_COMPLETION。虽然这些默认值常常很方便, 但在省略 errcode() 调用之前,始终要先想想它们是否合适。
errmsg(const char *msg, ...) 指定主错误消息文本, 以及可能要插入其中的运行时值。插入项通过 sprintf 风格的格式代码指定。 除了 sprintf 接受的标准格式代码外,还可以使用格式代码 %m 插入 strerror 为 errno 当前值返回的错误消息。 [17] %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 是决定需要哪种复数形式的整数值, 其余参数按所选格式字符串进行格式化。更多信息见 Section 53.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, 但支持消息的各种复数形式。更多信息见 Section 53.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,但支持消息的各种复数形式。 更多信息见 Section 53.2.2。
errhint(const char *msg, ...) 提供一条可选的 “提示”消息;它用于给出如何修复该问题的建议,而不是说明究竟哪里出了错的事实细节。 消息字符串的处理方式与 errmsg 完全相同。
errhint_plural(const char *fmt_singular, const char *fmt_plural, unsigned long n, ...) 类似于 errhint, 但支持消息的各种复数形式。更多信息见 Section 53.2.2。
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) 来指定抑制 postmaster 日志中消息的 STATEMENT: 部分。 一般来说,如果消息文本本身已经包含当前语句,这么做就是合适的。
可以调用 errhidecontext(bool hide_ctx) 来指定抑制 postmaster 日志中消息的 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 依然被广泛使用;对这类消息来说, 由于记法更简洁,它更受青睐。
关于如何编写良好的错误消息,可参见 Section 52.3。
[17] 也就是说,是到达 ereport 调用点时的那个值; 辅助报告例程内部对 errno 的更改不会影响它。 如果显式写出 strerror(errno) 作为 errmsg 的参数列表内容,就不是这样了;因此不要这么做。
如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。