相关文章推荐

33.8. 错误处理

本节描述了如何处理异常情况以及嵌入SQL程序的警告。有两个非排他性功能可以解决。 配置回调用来处理警告以及使用 WHENEVER 命令处理错误条件。 关于错误或者警告的详细信息可以从 sqlca 变量中获得。

33.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 语句中使用。

CALL name ( args )
DO name ( args )

调用具有指定参数的指定C函数。

SQL标准仅仅提供 CONTINUE GOTO (和 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;

33.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 表示无害条件,比如最后查询返回零行。 sqlcode sqlstate 是两个 不同的错误编码方案;详情如下。

如果最后一个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

字段 sqlcaid , sqlcabc , sqlerrp ,以及 sqlerrd sqlwarn 的剩余元素 目前没有任何有用信息。

在SQL标准中没有定义结构 sqlca , 但是在其他几个SQL数据库系统中实现了。定义核心是相似的,但是如果你想要 编写可移植应用程序,那么你应该仔细调查不同的实现。 这是一个结合 WHENEVER sqlca 的使用的例子, 当发生错误时,输出 sqlca 的内容。 在安装更多 "user-friendly" 错误处理程序之前, 这可能用于调试或者原型应用。

EXEC SQL WHENEVER SQLERROR CALL print_sqlca();
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
===============

33.8.3. SQLSTATE vs. SQLCODE

字段 sqlca.sqlstate sqlca.sqlcode 是提供错误码的两个不同模式。 两者来自SQL标准,但是 SQLCODE 在标准SQL-92 版本中已经过时,并且在后期版本中已经废除。因此, 强烈建议新应用使用 SQLSTATE

SQLSTATE 是五字符数组。 五字符包含数字或者表示不同错误和警告条件代码的大写字母。 SQLSTATE 有一个分层模式: 前两个字符表示条件的一般类,最后三个字符表示一般条件的子类。 通过代码 00000 表示成功状态。 SQLSTATE 代码是SQL标准中定义最多部分。 PostgreSQL 服务器本地支持 SQLSTATE 错误代码;因此通过在所有应用程序中 使用该错误代码方案实现高度一致性。 更多信息参阅 附录 A

SQLCODE ,已废弃的错误编码方案,是一个简单的integer。 0值表示成功,正值表示附带额外信息的成功,负值表示错误。 SQL标准仅仅定义正值+100,这表示返回最后命令或者影响零行,并且 没有明确负值。因此,该方案实现差的移植性,而且没有分层编码安排。 从历史角度, PostgreSQL 嵌入的SQL预处理器 为它的使用分配了一些指定 SQLCODE 。 使用数值和符号名称将它列在下面。记住这些是不能移植到其他SQL实现的。 为了简化应用程序移植到 SQLSTATE 方案,相应的 SQLSTATE 也被列出来。然而, 在两个方案(实际上是多对多)之间没有一对一或者一对多映射, 因此在每种情况下你应该咨询列在 附录 A 中的全球 SQLSTATE

这些是已分配的 SQLCODE 值:

0 ( ECPG_NO_ERROR )

表明没有错误。(SQLSTATE 00000)

100 ( ECPG_NOT_FOUND )

这是无害条件表明检索最后一条命令或者处理零行,或者你在游标结尾。(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 )

宿主变量是类型 无符号int ,并且数据库中数据是不同类型,而且 包含不能解释为 无符号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)

-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)

 
推荐文章