本节描述了如何在嵌入式 SQL 程序中处理异常情况和警告。为此提供了两种非互斥的机制。
WHENEVER
命令配置回调函数来处理警告和错误情况。sqlca
变量获取有关错误或警告的详细信息。捕获错误和警告的一种简单方法是在发生特定条件时设置要执行的特定操作。通常
EXEC SQL WHENEVERcondition
action
;
condition
可以是以下之一:
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 语法中的 CALL
和 DO
的含义。)
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
、sqlabc
、sqlerrp
以及 sqlerrd
和 sqlwarn
的其余元素目前不包含有用的信息。
结构 sqlca
未在 SQL 标准中定义,但在其他几个 SQL 数据库系统中实现。核心定义相似,但如果您想编写可移植的应用程序,则应仔细研究不同的实现。
这是一个结合使用 WHENEVER
和 sqlca
的示例,在发生错误时打印 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 ===============
SQLSTATE
与 SQLCODE
#字段 sqlca.sqlstate
和 sqlca.sqlcode
是提供错误代码的两种不同方案。两者都源自 SQL 标准,但 SQLCODE
在 SQL-92 标准版中已被标记为不推荐使用,并在后续版本中被删除。因此,强烈鼓励新应用程序使用 SQLSTATE
。
SQLSTATE
是一个五字符数组。这五个字符包含数字或大写字母,代表各种错误和警告条件的编码。 SQLSTATE
具有分层方案:前两个字符表示条件的通用类别,后三个字符表示通用条件的子类别。成功状态由代码 00000
表示。 SQLSTATE
代码在很大程度上由 SQL 标准定义。 PostgreSQL 服务器原生支持 SQLSTATE
错误代码;因此,通过在所有应用程序中使用此错误代码方案,可以实现高度的一致性。有关更多信息,请参见 附录 A。
SQLCODE
是已弃用的错误代码方案,它是一个简单的整数。值为 0 表示成功,正值表示成功并带有附加信息,负值表示错误。SQL 标准仅定义了正值 +100,它表示最后一个命令返回或影响了零行,没有特定的负值。因此,该方案只能实现较差的可移植性,并且没有分层代码分配。历史上,PostgreSQL 的嵌入式 SQL 处理器为其使用分配了一些特定的 SQLCODE
值,这些值与它们的数值和符号名称一起列出如下。请记住,这些不能移植到其他 SQL 实现。为了简化应用程序向 SQLSTATE
方案的移植,也列出了相应的 SQLSTATE
。但是,这两种方案之间没有一对一或一对多的映射(事实上是多对多),因此您应该在每种情况下查阅 附录 A 中的全局 SQLSTATE
列表。
这些是分配的 SQLCODE
值:
ECPG_NO_ERROR
) #表示没有错误。(SQLSTATE 00000)
ECPG_NOT_FOUND
) #这是一个无害的条件,表示最后一个命令检索或处理了零行,或者您已到达游标的末尾。(SQLSTATE 02000)
在循环中处理游标时,您可以使用此代码来检测何时中止循环,如下所示:
while (1) { EXEC SQL FETCH ... ; if (sqlca.sqlcode == ECPG_NOT_FOUND) break; }
但是 WHENEVER NOT FOUND DO BREAK
在内部有效地完成了此操作,因此通常没有好处会显式写出此代码。
ECPG_OUT_OF_MEMORY
) #表示您的虚拟内存已耗尽。数值定义为 -ENOMEM
。(SQLSTATE YE001)
ECPG_UNSUPPORTED
) #表示预处理器生成了库不知道的内容。您可能正在运行不兼容版本的预处理器和库。(SQLSTATE YE002)
ECPG_TOO_MANY_ARGUMENTS
) #这意味着命令指定的宿主变量多于命令期望的。(SQLSTATE 07001 或 07002)
ECPG_TOO_FEW_ARGUMENTS
) #这意味着命令指定的宿主变量少于命令期望的。(SQLSTATE 07001 或 07002)
ECPG_TOO_MANY_MATCHES
) #这意味着查询返回了多行,但语句仅准备好存储一行结果(例如,因为指定的变量不是数组)。(SQLSTATE 21000)
ECPG_INT_FORMAT
) #宿主变量的类型为 int
,数据库中的数据类型不同,并且包含的值无法解释为 int
。库使用 strtol()
进行转换。(SQLSTATE 42804)
ECPG_UINT_FORMAT
) #宿主变量的类型为 unsigned int
,数据库中的数据类型不同,并且包含的值无法解释为 unsigned int
。库使用 strtoul()
进行转换。(SQLSTATE 42804)
ECPG_FLOAT_FORMAT
) #宿主变量的类型为 float
,数据库中的数据类型不同,并且包含的值无法解释为 float
。库使用 strtod()
进行转换。(SQLSTATE 42804)
ECPG_NUMERIC_FORMAT
) #宿主变量的类型为 numeric
,数据库中的数据类型不同,并且包含的值无法解释为 numeric
值。(SQLSTATE 42804)
ECPG_INTERVAL_FORMAT
) #宿主变量的类型为 interval
,数据库中的数据类型不同,并且包含的值无法解释为 interval
值。(SQLSTATE 42804)
ECPG_DATE_FORMAT
) #宿主变量的类型为 date
,数据库中的数据类型不同,并且包含的值无法解释为 date
值。(SQLSTATE 42804)
ECPG_TIMESTAMP_FORMAT
) #宿主变量的类型为 timestamp
,数据库中的数据类型不同,并且包含的值无法解释为 timestamp
值。(SQLSTATE 42804)
ECPG_CONVERT_BOOL
) #这意味着宿主变量的类型为 bool
,数据库中的数据既不是 't'
也不是 'f'
。(SQLSTATE 42804)
ECPG_EMPTY
) #发送到 PostgreSQL 服务器的语句是空的。(这通常不会在嵌入式 SQL 程序中发生,所以可能指向内部错误。)(SQLSTATE YE002)
ECPG_MISSING_INDICATOR
) #返回了一个 NULL 值,并且没有提供 NULL 指示符变量。(SQLSTATE 22002)
ECPG_NO_ARRAY
) #在一个需要数组的地方使用了普通变量。(SQLSTATE 42804)
ECPG_DATA_NOT_ARRAY
) #数据库在需要数组值的地方返回了一个普通变量。(SQLSTATE 42804)
ECPG_ARRAY_INSERT
) #值无法插入到数组中。(SQLSTATE 42804)
ECPG_NO_CONN
) #程序试图访问一个不存在的连接。(SQLSTATE 08003)
ECPG_NOT_CONN
) #程序试图访问一个存在的但未打开的连接。(这是一个内部错误。)(SQLSTATE YE002)
ECPG_INVALID_STMT
) #您试图使用的语句尚未准备好。(SQLSTATE 26000)
ECPG_INFORMIX_DUPLICATE_KEY
) #重复键错误,违反唯一约束(Informix 兼容模式)。(SQLSTATE 23505)
ECPG_UNKNOWN_DESCRIPTOR
) #找不到指定的描述符。您试图使用的语句尚未准备好。(SQLSTATE 33000)
ECPG_INVALID_DESCRIPTOR_INDEX
) #指定的描述符索引超出范围。(SQLSTATE 07009)
ECPG_UNKNOWN_DESCRIPTOR_ITEM
) #请求了无效的描述符项。(这是一个内部错误。)(SQLSTATE YE002)
ECPG_VAR_NOT_NUMERIC
) #在执行动态语句期间,数据库返回了一个数值,而宿主变量不是数值。(SQLSTATE 07006)
ECPG_VAR_NOT_CHAR
) #在执行动态语句期间,数据库返回了一个非数值,而宿主变量是数值。(SQLSTATE 07006)
ECPG_INFORMIX_SUBSELECT_NOT_ONE
) #子查询的结果不是单行(Informix 兼容模式)。(SQLSTATE 21000)
ECPG_PGSQL
) #由 PostgreSQL 服务器引起的一些错误。消息包含来自 PostgreSQL 服务器的错误消息。
ECPG_TRANS
) #PostgreSQL 服务器发出信号,表示我们无法开始、提交或回滚事务。(SQLSTATE 08007)
ECPG_CONNECT
) #连接到数据库的尝试未成功。(SQLSTATE 08001)
ECPG_DUPLICATE_KEY
) #重复键错误,违反唯一约束。(SQLSTATE 23505)
ECPG_SUBSELECT_NOT_ONE
) #子查询的结果不是单行。(SQLSTATE 21000)
ECPG_WARNING_UNKNOWN_PORTAL
) #指定了无效的光标名称。(SQLSTATE 34000)
ECPG_WARNING_IN_TRANSACTION
) #事务正在进行中。(SQLSTATE 25001)
ECPG_WARNING_NO_TRANSACTION
) #没有活动的(正在进行的)事务。(SQLSTATE 25P01)
ECPG_WARNING_PORTAL_EXISTS
) #指定了已有的光标名称。(SQLSTATE 42P03)
如果您在文档中发现任何不正确之处,与您对特定功能的体验不符,或者需要进一步说明,请使用 此表格 报告文档问题。