| PostgreSQL 9.4.4 中文手册 | |||
|---|---|---|---|
| 上一页 | 上一级 | 章 33. ECPG - 在C中嵌入 SQL | 下一页 |
本节描述了如何处理异常情况以及嵌入SQL程序的警告。有两个非排他性功能可以解决。 配置回调用来处理警告以及使用 WHENEVER 命令处理错误条件。 关于错误或者警告的详细信息可以从 sqlca 变量中获得。
当产生特定条件时,捕获错误和警告的一个简单方法是设置一个要执行的具体操作。通常:
EXEC SQL WHENEVER condition action;
condition 可以是下列之一:
当在SQL语句执行期间发生错误时,调用指定操作。
当在SQL语句执行期间发生警告时,调用指定操作。
当SQL语句检索或者影响零行,则调用指定操作。(这个条件不是错误, 但是你可能对特意处理它感兴趣。)
action 可以是下列之一:
这实际上意味着该条件被忽略。这是缺省的。
跳转到指定标签(使用C goto 语句)。
输出消息到标准错误。这对于简单程度或者原型期间非常有用。 不能配置该消息的详细信息。
调用 exit(1) ,这将终止程序。
执行C语句 break 。这只有在循环中或者 switch 语句中使用。
调用具有指定参数的指定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;
为了更强大的错误处理, 嵌入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
===============
字段 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 值:
表明没有错误。(SQLSTATE 00000)
这是无害条件表明检索最后一条命令或者处理零行,或者你在游标结尾。(SQLSTATE 02000)
当在循环中处理游标时,你可以使用该代码作为检测什么时候终止循环的方式,像这样:
while (1)
EXEC SQL FETCH ... ;
if (sqlca.sqlcode == ECPG_NOT_FOUND)
break;
但是WHENEVER NOT FOUND DO BREAK有效的内部执行这个,因此
在明确写这个时通常没有优势。
表明耗尽了你的虚拟内存。作为 -ENOMEM 定义该数值。 (SQLSTATE YE001)
表明预处理器产生了该库不知道的一些东西。可能你正在该预处理器和该库不兼容版本上运行。(SQLSTATE YE002)
这意味着指定命令比期望命令宿主变量更多。(SQLSTATE 07001或者07002)
这意味着指定命令比期望命令宿主变量更少。(SQLSTATE 07001或者07002)
这意味着查询返还多行但是语句只准备存储一个结果行(比如, 因为指定变量不是数组)。(SQLSTATE 21000)
宿主变量是类型
int
,并且数据库中数据是不同类型,而且
包含不能解释为
int
类型的值。
为这种转换该库使用
strtol()
。(SQLSTATE 42804)
宿主变量是类型
无符号int
,并且数据库中数据是不同类型,而且
包含不能解释为
无符号int
类型的值。
为这种转换该库使用
strtoul()
。(SQLSTATE 42804)
宿主变量是类型
float
,并且数据库中数据是另一种类型,而且
包含不能解释为
float
类型的值。
为这种转换该库使用
strtod()
。(SQLSTATE 42804)
宿主变量是类型 numeric ,并且数据库中数据是另一种类型,而且 包含不能解释为 numeric 类型的值。(SQLSTATE 42804)
宿主变量是类型 interval ,并且数据库中数据是另一种类型,而且 包含不能解释为 interval 类型的值。(SQLSTATE 42804)
宿主变量是类型 date ,并且数据库中数据是另一种类型,而且 包含不能解释为 date 类型的值。(SQLSTATE 42804)
宿主变量是类型 timestamp ,并且数据库中数据是另一种类型,而且 包含不能解释为 timestamp 类型的值。(SQLSTATE 42804)
这意味着宿主变量是类型 bool , 并且数据库中数据既不是 't' 也不是 'f' 。(SQLSTATE 42804)
发送到 PostgreSQL 服务器的语句是空的。 (这通常不会发生在嵌入SQL程序中,因此它可能指向一个内部错误。) (SQLSTATE YE002)
返回一个空值,而且没有提供空指示符变量。(SQLSTATE 22002)
一个普通变量被用于需要数组的地方。(SQLSTATE 42804)
数据库返回需要数组值位置的普通变量。(SQLSTATE 42804)
该程序试图访问一个不存在的连接。(SQLSTATE 08003)
该程序试图访问一个存在但无法打开的连接。(这是一个内部错误。)(SQLSTATE YE002)
你正尝试使用的语句未准备好。(SQLSTATE 26000)
重复键错误,违反唯一约束(Informix兼容模式)。(SQLSTATE 23505)
未找到指定描述符。你尝试使用的语句未准备好。(SQLSTATE 33000)
指定的描述符索引超出了范围。(SQLSTATE 07009)
请求无效描述符项。(这是个内部错误。) (SQLSTATE YE002)
在动态语句执行的过程中,数据库返回一个数值,但宿主变量不是数字的。 (SQLSTATE 07006)
在动态语句执行的过程中,数据库返回一个非数值, 但宿主变量是数字的。 (SQLSTATE 07006)
子查询结果不是单行(Informix兼容模式)。(SQLSTATE 21000)
PostgreSQL 服务器产生一些错误。 包含的错误消息来自 PostgreSQL 服务器。
PostgreSQL 发出信号我们不能启动,提交,或者回滚事务。 (SQLSTATE 08007)
尝试与数据库的连接没有成功。(SQLSTATE 08001)
重复键错误,违反唯一约束。(SQLSTATE 23505)
子查询结果不是单行。(SQLSTATE 21000)
指定一个无效游标名。(SQLSTATE 34000)
事务正在进行中。(SQLSTATE 25001)
这是一个非活跃(进行中)事务。(SQLSTATE 25P01)
指定一个已经存在游标名。(SQLSTATE 42P03)