Redrock Postgres 搜索 英文
版本: 9.3 / 9.4 / 9.5 / 9.6 / 10 / 11 / 12 / 13 / 14 / 15 / 16 / 17

34.8. 错误处理 #

34.8.1. 设置回调
34.8.2. sqlca
34.8.3. SQLSTATESQLCODE

本节旨在描述如何在嵌入式 SQL 程序中处理异常情况和警告。为此,有两个互斥设施。

34.8.1. 设置回调 #

捕捉错误和警告的一种简单方法是在发生特定情况时设置具体操作让其执行。一般来说

EXEC SQL WHENEVER condition action;

condition 可以是以下之一

SQLERROR #

每当 SQL 语句执行过程中发生错误时都会调用指定操作。

SQLWARNING #

每当 SQL 语句执行过程中发生错误时都会调用指定操作。

NOT FOUND #

每当 SQL 语句检索或影响零行时都会调用指定操作。(该情况并非错误,但您或许有兴趣专门处理它。)

action 可以是以下之一

CONTINUE #

这意味着会忽略该情况。这是默认设置。

GOTO label
GO TO label #

跳转到指定标签(使用 C goto 语句)。

SQLPRINT #

将消息打印到标准错误。对于简单程序或在构建原型期间,此方法非常有用。无法配置消息的详细信息。

STOP #

调用 exit(1),该命令会终止程序。

DO BREAK #

执行 C 语句 break。这应仅用于循环或 switch 语句。

DO CONTINUE #

执行 C 语句 continue。这应仅用于循环语句中。如果执行,将导致控制流返回到循环开头。

CALL name (args)
DO name (args) #

使用指定参数调用指定 C 函数。(此用途与正常 PostgreSQL 语法中 CALLDO 的含义不同。)

SQL 标准只为动作 CONTINUEGOTO(以及 GO TO)提供支持。

以下示例可在简单程序中使用。当出现警告时,它会打印简单消息,当发生错误时,它会中止程序。

EXEC SQL WHENEVER SQLWARNING SQLPRINT;
EXEC SQL WHENEVER SQLERROR STOP;

语句 EXEC SQL WHENEVER 是 SQL 预处理器的指令,而不是 C 语句。它设置的错误或警告动作适用于它所在的点下方显示的所有内嵌 SQL 语句,除非针对同一条件在第一个 EXEC SQL WHENEVER 与导致该条件的 SQL 语句之间设定了不同的动作,而不管 C 程序的控制流如何。因此,以下两个 C 程序摘录都不会产生所需的效果。

/*
 * WRONG
 */
int main(int argc, char *argv[])
{
    ...
    if (verbose) {
        EXEC SQL WHENEVER SQLWARNING SQLPRINT;
    }
    ...
    EXEC SQL SELECT ...;
    ...
}
/*
 * WRONG
 */
int main(int argc, char *argv[])
{
    ...
    set_error_handler();
    ...
    EXEC SQL SELECT ...;
    ...
}

static void set_error_handler(void)
{
    EXEC SQL WHENEVER SQLERROR STOP;
}

34.8.2. sqlca #

为了得到更强大的错误处理功能,内嵌 SQL 接口提供了一个名为 sqlca(SQL 通信区域)的全局变量,它具有以下结构。

struct
{
    char sqlcaid[8];
    long sqlabc;
    long sqlcode;
    struct
    {
        int sqlerrml;
        char sqlerrmc[SQLERRMC_LEN];
    } sqlerrm;
    char sqlerrp[8];
    long sqlerrd[6];
    char sqlwarn[8];
    char sqlstate[5];
} sqlca;

(在多线程程序中,每个线程都会自动获取自己的一份 sqlca 副本。这类似于标准 C 全局变量 errno 的处理方式。)

sqlca 涵盖警告和错误。如果在执行语句期间发生多个警告或错误,那么 sqlca 只会包含有关最后一个警告或错误的信息。

如果在最后SQL语句中未发生错误,那么 sqlca.sqlcode 会为 0,sqlca.sqlstate 会为 "00000"。如果发生了警告或错误,那么 sqlca.sqlcode 会为负数,sqlca.sqlstate 会与 "00000" 不同。正数 sqlca.sqlcode 指示无害条件,例如上个查询未返回任何行。sqlcodesqlstate 是两种不同的错误代码方案;详情如下。

如果上个 SQL 语句成功,那么 sqlca.sqlerrd[1] 将包含已处理行的 OID(如果适用),sqlca.sqlerrd[2] 将包含已处理或返回的行数(如果该命令适用)。

如果出现错误或警告,sqlca.sqlerrm.sqlerrmc 将包含一个描述错误的字符串。字段 sqlca.sqlerrm.sqlerrml 包含存储在 sqlca.sqlerrm.sqlerrmc 中的错误消息的长度(strlen() 的结果,对于 C 程序员来说不是很有趣)。请注意,某些消息太长,无法放入固定大小的 sqlerrmc 数组中;它们将被截断。

如果出现警告,将把 sqlca.sqlwarn[2] 设置为 W。(在所有其他情况下,都将其设置为与 W 不同的内容。)如果将 sqlca.sqlwarn[1] 设置为 W,则在将值存储到宿主变量中时将截断该值。如果将任何其他元素设置为表示警告,则将 sqlca.sqlwarn[0] 设置为 W

字段 sqlcaidsqlabcsqlerrp 以及 sqlerrdsqlwarn 的其余元素当前不包含任何有用的信息。

结构 sqlca 未在 SQL 标准中定义,但在其他几个 SQL 数据库系统中实现。这些定义的核心类似,但如果你想要编写可移植的应用程序,则应该仔细研究不同的实现。

以下是一个结合使用 WHENEVERsqlca 的示例,在发生错误时输出 sqlca 的内容。在安装更“用户友好”错误处理程序之前,这可能有助于调试或试用应用程序。

EXEC SQL WHENEVER SQLERROR CALL print_sqlca();

void
print_sqlca()
{
    fprintf(stderr, "==== sqlca ====\n");
    fprintf(stderr, "sqlcode: %ld\n", sqlca.sqlcode);
    fprintf(stderr, "sqlerrm.sqlerrml: %d\n", sqlca.sqlerrm.sqlerrml);
    fprintf(stderr, "sqlerrm.sqlerrmc: %s\n", sqlca.sqlerrm.sqlerrmc);
    fprintf(stderr, "sqlerrd: %ld %ld %ld %ld %ld %ld\n", sqlca.sqlerrd[0],sqlca.sqlerrd[1],sqlca.sqlerrd[2],
                                                          sqlca.sqlerrd[3],sqlca.sqlerrd[4],sqlca.sqlerrd[5]);
    fprintf(stderr, "sqlwarn: %d %d %d %d %d %d %d %d\n", sqlca.sqlwarn[0], sqlca.sqlwarn[1], sqlca.sqlwarn[2],
                                                          sqlca.sqlwarn[3], sqlca.sqlwarn[4], sqlca.sqlwarn[5],
                                                          sqlca.sqlwarn[6], sqlca.sqlwarn[7]);
    fprintf(stderr, "sqlstate: %5s\n", sqlca.sqlstate);
    fprintf(stderr, "===============\n");
}

结果可能如下所示(此处是由于表名拼写错误而导致的错误)

==== sqlca ====
sqlcode: -400
sqlerrm.sqlerrml: 49
sqlerrm.sqlerrmc: relation "pg_databasep" does not exist on line 38
sqlerrd: 0 0 0 0 0 0
sqlwarn: 0 0 0 0 0 0 0 0
sqlstate: 42P01
===============

34.8.3. SQLSTATESQLCODE #

字段 sqlca.sqlstatesqlca.sqlcode 是提供错误代码的两种不同的方案。两者均源自 SQL 标准,但 SQLCODE 已在 SQL-92 版本的标准中标记为已弃用,并在后续版本中删除。因此,强烈建议新应用程序使用 SQLSTATE

SQLSTATE 是一个由五个字符组成的数组。五个字符包含了数字或表示了各种错误和警告情况代码的大写字母。 SQLSTATE 有一个层次结构:前两个字符表示情况的总类别,后三个字符表示总情况的一个子类别。一个成功状况由代码 00000 表示。大部分 SQLSTATE 代码在 SQL 标准中定义。 PostgreSQL 服务器本机支持 SQLSTATE 错误代码;因此在所有应用程序中使用此错误代码方案可以实现高度的一致性。有关详细信息,请参阅 附录 A

SQLCODE,已弃用的错误代码方案,是一个简单的整数。0 值表示成功,正值表示有附加信息时的成功,负值表示错误。SQL 标准只定义了正值 +100,表示最后命令返回或影响了 0 行,没有特殊的负值。因此,此方案只能实现较差的可移植性,并且没有层次代码分配。从历史上看,PostgreSQL 的嵌入式 SQL 处理器已经为其使用分配了一些特定的 SQLCODE 值,这些值下面列出了它们的数值和符号名称。请记住,这些值不可移植到其他 SQL 实现。为了简化应用程序到 SQLSTATE 方案的移植,还列出了相应的 SQLSTATE。然而,这两种方案之间不存在一对一或一对多的映射(实际上是多对多),因此您应该在每种情况下参阅 附录 A 中的全局 SQLSTATE 列表。

以下是已分配的 SQLCODE

0 (ECPG_NO_ERROR) #

表示没有错误。(SQLSTATE 00000)

100 (ECPG_NOT_FOUND) #

这是一种无害的情况,表示最后命令检索或处理了 0 行,或者表示您已到达游标末尾。(SQLSTATE 02000)

在循环中处理游标时,可以使用此代码以检测何时中止循环,如下所示

while (1)
{
    EXEC SQL FETCH ... ;
    if (sqlca.sqlcode == ECPG_NOT_FOUND)
        break;
}

但是 WHENEVER NOT FOUND DO BREAK 在内部有效地执行此操作,因此明确地写出此代码通常没有好处。

-12 (ECPG_OUT_OF_MEMORY) #

表示虚拟内存已耗尽。数值被定义为 -ENOMEM。(SQLSTATE YE001)

-200 (ECPG_UNSUPPORTED) #

表示预处理器生成了库不知道的内容。也许运行的是预处理器和库的不兼容版本。(SQLSTATE YE002)

-201 (ECPG_TOO_MANY_ARGUMENTS) #

表示命令指定的宿主变量多于命令预期的。(SQLSTATE 07001 或 07002)

-202 (ECPG_TOO_FEW_ARGUMENTS) #

表示命令指定的宿主变量少于命令预期的。(SQLSTATE 07001 或 07002)

-203 (ECPG_TOO_MANY_MATCHES) #

表示查询返回了多行,但该语句仅准备存储一行结果(例如,因为指定的变量不是数组)。(SQLSTATE 21000)

-204 (ECPG_INT_FORMAT) #

宿主变量为 int 类型,而数据库中的数据为其他类型,并且包含不能解释为 int 的值。此转换中,库使用 strtol()。(SQLSTATE 42804)

-205 (ECPG_UINT_FORMAT) #

宿主变量为 unsigned int 类型,而数据库中的数据为其他类型,并且包含不能解释为 unsigned int 的值。此转换中,库使用 strtoul()。(SQLSTATE 42804)

-206 (ECPG_FLOAT_FORMAT) #

宿主变量为 float 类型,而数据库中的数据为另一种类型,并且包含不能解释为 float 的值。此转换中,库使用 strtod()。(SQLSTATE 42804)

-207 (ECPG_NUMERIC_FORMAT) #

宿主变量为 numeric 类型,而数据库中的数据为另一种类型,并且包含不能解释为 numeric 值的值。(SQLSTATE 42804)

-208 (ECPG_INTERVAL_FORMAT) #

宿主变量为 interval 类型,而数据库中的数据为另一种类型,并且包含不能解释为 interval 值的值。(SQLSTATE 42804)

-209 (ECPG_DATE_FORMAT) #

主机变量类型为 date,而数据库中的数据为另一种类型,并且包含无法解释为 date 值的值。(SQLSTATE 42804)

-210 (ECPG_TIMESTAMP_FORMAT) #

主机变量类型为 timestamp,而数据库中的数据为另一种类型,并且包含无法解释为 timestamp 值的值。(SQLSTATE 42804)

-211 (ECPG_CONVERT_BOOL) #

这意味着主机变量类型为 bool,而数据库中的数据既不是 't' 也不是 'f'。(SQLSTATE 42804)

-212 (ECPG_EMPTY) #

发送到 PostgreSQL 服务器的语句为空。(这通常不会在嵌入式 SQL 程序中发生,因此它可能指向内部错误。)(SQLSTATE YE002)

-213 (ECPG_MISSING_INDICATOR) #

返回了空值,但未提供空指示器变量。(SQLSTATE 22002)

-214 (ECPG_NO_ARRAY) #

普通变量用在需要数组的地方。(SQLSTATE 42804)

-215 (ECPG_DATA_NOT_ARRAY) #

数据库在需要数组值的地方返回了普通变量。(SQLSTATE 42804)

-216 (ECPG_ARRAY_INSERT) #

无法将值插入到数组中。(SQLSTATE 42804)

-220 (ECPG_NO_CONN) #

程序试图访问一个不存在的连接。(SQLSTATE 08003)

-221 (ECPG_NOT_CONN) #

程序试图访问一个确实存在但尚未打开的连接。(这是内部错误。)(SQLSTATE YE002)

-230 (ECPG_INVALID_STMT) #

你试图使用的语句尚未准备就绪。(SQLSTATE 26000)

-239 (ECPG_INFORMIX_DUPLICATE_KEY) #

重复键错误,违反唯一约束(Informix 兼容模式)。(SQLSTATE 23505)

-240 (ECPG_UNKNOWN_DESCRIPTOR) #

没有找到指定的描述符。你试图使用的语句尚未准备就绪。(SQLSTATE 33000)

-241 (ECPG_INVALID_DESCRIPTOR_INDEX) #

指定的描述符索引超出范围。(SQLSTATE 07009)

-242 (ECPG_UNKNOWN_DESCRIPTOR_ITEM) #

请求无效的描述符项。(这是一个内部错误。)(SQLSTATE YE002)

-243 (ECPG_VAR_NOT_NUMERIC) #

在执行动态语句期间,数据库返回数值,而主机变量非数值。(SQLSTATE 07006)

-244 (ECPG_VAR_NOT_CHAR) #

在执行动态语句期间,数据库返回非数值,而主机变量为数值。(SQLSTATE 07006)

-284 (ECPG_INFORMIX_SUBSELECT_NOT_ONE) #

子查询的结果不是单行(Informix 兼容模式)。(SQLSTATE 21000)

-400 (ECPG_PGSQL) #

PostgreSQL 服务器引发的一些错误。消息包含 PostgreSQL 服务器的错误消息。

-401 (ECPG_TRANS) #

PostgreSQL 服务器发出信号,表明无法启动、提交或回滚事务。(SQLSTATE 08007)

-402 (ECPG_CONNECT) #

连接数据库尝试未成功。(SQLSTATE 08001)

-403 (ECPG_DUPLICATE_KEY) #

重复键错误,违反唯一约束。(SQLSTATE 23505)

-404 (ECPG_SUBSELECT_NOT_ONE) #

子查询的结果不是单行。(SQLSTATE 21000)

-602 (ECPG_WARNING_UNKNOWN_PORTAL) #

指定的游标名称无效。(SQLSTATE 34000)

-603 (ECPG_WARNING_IN_TRANSACTION) #

事务正在进行中。(SQLSTATE 25001)

-604 (ECPG_WARNING_NO_TRANSACTION) #

不存在活动(正在进行)的事务。(SQLSTATE 25P01)

-605 (ECPG_WARNING_PORTAL_EXISTS) #

指定了现有的游标名称。(SQLSTATE 42P03)