在服务器代码里生成的错误,警告以及日志信息应该用 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 may 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 值返回的错误信息。 [1] %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; 因为这些信息最好还是表示得简单些好。
书写好的错误消息的建议可以在 Section 43.3 找到。
[1] | 也就是说,在到达 ereport 调用的时候当前的数值; 在附属报告过程里对 errno 的修改将不会影响他。 但是如果你在 errmsg 的参数列表里明确地写 strerror(errno), 这一点就不能保证了,因此,请不要这么做。 |