53.2. 报告服务器内的错误

服务器代码中生成的错误,警告和日志消息应使用ereport或其较旧的表亲elog创建。此功能的使用非常复杂,需要进行一些解释。

每条消息都有两个必需的元素:严重性级别(范围从DEBUGPANIC)和主要消息文本。此外,还有一些可选元素,其中最常见的是遵循 SQL 规范的 SQLSTATE 约定的错误标识符代码。 ereport本身只是一个 shell 函数,主要是为了使消息生成看起来像 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,
                                      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 错误标识符代码。如果未调用此例程,则当错误严重级别为ERROR或更高时,错误标识符默认为ERRCODE_INTERNAL_ERROR,当错误级别为WARNING时错误标识符默认为ERRCODE_WARNING,否则(对于NOTICE及以下)ERRCODE_SUCCESSFUL_COMPLETION。尽管这些默认设置通常很方便,但是在忽略errcode()调用之前,请务必考虑它们是否合适。

  • errmsg(const char *msg, ...)指定主要错误消息文本,以及可能要插入其中的运行时值。插入由sprintf样式格式代码指定。除了sprintf接受的标准格式代码外,格式代码%m还可用于插入strerror返回的错误消息,表示errno的当前值。 [13] %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 54.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 54.2.2

  • errdetail_log(const char *msg, ...)errdetail相同,除了此字符串仅用于服务器日志,而不用于 Client 端。如果同时使用errdetail(或上面的等效项之一)和errdetail_log,则一个字符串进入 Client 端,另一字符串进入日志。这对于对安全性过于敏感或过于庞大而无法包含在发送给 Client 端的报告中的错误详细信息很有用。

  • errdetail_log_plural(const char *fmt_singular, const char *fmt_plural, unsigned long n, ...)类似于errdetail_log,但支持各种复数形式的消息。有关更多信息,请参见Section 54.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)以指定对邮件 Management 员日志中邮件的STATEMENT:部分的禁止显示。通常,如果消息文本已经包含当前语句,则这是适当的。

  • 可以调用errhidecontext(bool hide_ctx)以指定对邮件 Management 员日志中邮件的CONTEXT:部分的禁止显示。这仅应用于冗长的调试消息,其中重复包含上下文会使日志量过大。

Note

ereport调用中,最多应使用功能errtableerrtablecolerrtableconstrainterrdatatypeerrdomainconstraint中的一个。存在这些功能是为了允许应用程序提取与错误条件关联的数据库对象的名称,而不必检查可能本地化的错误消息文本。这些功能应在错误报告中使用,应用程序可能希望对其进行自动错误处理。从 PostgreSQL 9.3 开始,仅针对 SQLSTATE 类 23(完整性约束违规)中的错误提供了完整的覆盖范围,但是将来可能会扩大覆盖范围。

有一个较旧的功能elog仍在大量使用。 elog通话:

elog(level, "format string", ...);

完全等同于:

ereport(level, (errmsg_internal("format string", ...)));

请注意,SQLSTATE 错误代码始终是默认值,并且消息字符串不受翻译。因此,elog仅应用于内部错误和底层调试日志记录。任何普通用户可能感兴趣的消息都应通过ereport。但是,由于系统中有足够的内部“无法发生”错误检查,因此elog仍被广泛使用。对于这些消息,由于其符号简洁性而被首选。

可以在Section 53.3中找到有关编写良好错误消息的建议。


[13]即达到ereport调用时的当前值;辅助报告例程中errno的更改不会影响它。如果要在errmsg的参数列表中显式写入strerror(errno),那将是不正确的。因此,请勿这样做。