PostgreSQL 中文参考文档
🌐 英文原版 官方 ↗

34.8. 错误处理 #

34.8.1. Setting Callbacks
34.8.2. sqlca
34.8.3. SQLSTATE vs. SQLCODE

本节描述如何在嵌入式 SQL 程序中处理异常情况和警告。 有两种非互斥的工具可用于此目的。

34.8.1. Setting Callbacks #

捕获错误和警告的一种简单方法是设置一个特定的动作, 使得每当出现某个特定条件时就执行它。一般形式为:

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 语法中 CALL 和 DO 的含义不同。)

SQL 标准只规定了 CONTINUE 和 GOTO(以及 GO TO)这两种动作。

下面是一个你可能想在简单程序中使用的例子。 它在发生警告时打印一条简单的消息, 而在发生错误时中止程序:

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

语句 EXEC SQL WHENEVER 是 SQL 预处理器的 一条指令,而不是一条 C 语句。它设置的错误或警告动作 适用于出现在处理程序设置点之下、且位于同一个源文件中的 所有嵌入式 SQL 语句, 而与 SQL 语句在 C 程序的控制流中的位置无关。 因此,如果 EXEC SQL WHENEVER 指定的动作 不适合某个特定语句,你需要在它之前设置不同的动作, 以防两个动作同时生效。

34.8.2. sqlca #

为了实现更强大的错误处理,嵌入式 SQL 接口提供了一个名为 sqlca(SQL 通信区)的全局变量, 它具有如下结构:

struct
{
    char sqlcaid[8];
} sqlca;

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

如果最后一条 SQL 语句没有发生错误, 那么 sqlca.sqlcode 将为 0, 而 sqlca.sqlstate 将为 "00000"。如果发生了警告或错误, 那么 sqlca.sqlerrm.sqlerrmc 中将保存 描述该错误的字符串。

如果最后一条 SQL 语句执行成功,那么 sqlca.sqlerrd[1] 包含被处理行的 OID (如果适用),而 sqlca.sqlerrd[2] 包含被处理或返回的行数 (如果适用)。

在发生错误或警告的情况下, sqlca.sqlerrm.sqlerrmc 将包含一个 描述该错误的字符串。字段 sqlca.sqlerrm.sqlerrml 包含存储在 sqlca.sqlerrm.sqlerrmc 中的错误消息的长度 (strlen() 的结果,对 C 程序员来说 这并不重要)。

在发生警告的情况下,sqlca.sqlwarn[2] 会被设置为 W。 (在其他所有情况下,它都会被设置为与 W 不同的值。) 如果 sqlca.sqlwarn[1] 被设置为 W,那么在把一个值存入宿主变量时 发生了截断。 (如果发生了截断,sqlca.sqlwarn[0] 也会被设置为 W。) 在发生其他警告时,sqlca.sqlwarn[0] 和 sqlca.sqlwarn[2] 会被设置为 W。

字段 sqlcaid、 sqlabc、 sqlerrp,以及 sqlerrd 和 sqlwarn 的其余元素 目前不包含任何有用的信息。

结构 sqlca 并未在 SQL 标准中定义, 但在其他几个 SQL 数据库系统中都有实现。 它们的定义在核心上是相似的, 但如果你想编写可移植的应用, 那么应当研究不同实现并选择使用哪些。

下面是一个把 WHENEVER 和 sqlca 结合起来使用的例子, 它在发生错误时打印出 sqlca 的内容。 这也许对调试或原型开发应用很有用。

34.8.3. SQLSTATE vs. SQLCODE #

字段 sqlca.sqlstate 和 sqlca.sqlcode 是提供错误代码的 两种不同的方案。两者都源自 SQL 标准, 但 SQLCODE 在标准的 SQL:1999 版本中 已被标记为弃用,并在后续版本中被移除。 因此,我们强烈建议新应用使用 SQLSTATE。

SQLSTATE 是一个五字符的数组。 这五个字符包含数字或大写字母, 代表各种错误和警告情况的代码。 SQLSTATE 具有一种层次化的方案: 前两个字符表示情况的一般类别, 后三个字符表示一般情况的子类。 这种方案的成功状态由 00000 代码表示。 这些代码大多可以在附录 Appendix A 中找到。

SQLCODE 是已弃用的错误代码方案, 它是一个简单的整数。值为 0 表示成功, 正值表示成功并带有附加信息, 负值表示错误。SQL 标准 只规定了非零值加负号的含义, 并声明哪些值表示哪些错误是特定于实现的。 如果你实现的 ECPG 应用依赖于错误的具体代码值, 那么你应当熟悉 PostgreSQL 服务器赋予这些代码的值。

这些是被赋予的 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;
}

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