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(); 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 ===============
字段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)