在服务器代码内生成的错误、警告和日志消息应当用
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."));
这展示了使用格式代码把运行时值嵌入消息文本的做法。
此外,还提供了一个可选的
“hint” 消息。
辅助函数调用可以按任意顺序书写,但按照惯例
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 当前
值返回的错误消息。
[12]
%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 56.2.2。
errdetail(const char *msg, ...) 提供一条可选的
“detail” 消息;当有额外的信息不适合放进主要消息中时
使用它。
消息字符串的处理方式与
errmsg 完全相同。
errdetail_internal(const char *msg, ...) 与
errdetail 相同,只是消息字符串不会被翻译,
也不会被包含进国际化消息字典。
它应当用于那些不值得花费翻译精力的 detail 消息,例如因为它们
太过技术性而对大多数用户没有用处。
errdetail_plural(const char *fmt_singular, const char *fmt_plural,
unsigned long n, ...) 类似
errdetail,但支持消息的多种复数形式。
更多信息见 Section 56.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 56.2.2。
errhint(const char *msg, ...) 提供一条可选的
“hint” 消息;当提供关于如何修复问题的建议时使用
它,与关于出了什么问题的事实性细节相对。
消息字符串的处理方式与
errmsg 完全相同。
errhint_plural(const char *fmt_singular, const char *fmt_plural,
unsigned long n, ...) 类似
errhint,但支持消息的多种复数形式。
更多信息见 Section 56.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 55.3。
[12]
即 ereport 调用被到达时
errno 的当前值;辅助报告例程内
errno 的变化不会影响它。
如果您在 errmsg 的参数列表中显式写出
strerror(errno) 就不会有这种性质;因此
不要这样做。