选择 打开 改范围 完整检索页

pgsql.cc 提供对 postgresql.org 官网内容的中文翻译,由 Pigsty 团队维护。

受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
测试与开发版本: 19 / devel
不受支持的版本: 13 / 12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0
历史版本PostgreSQL 9.3 已于 2018 年 11 月结束社区维护,本页译文保留供仍在使用旧版本的读者参考。新系统请看当前版本

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 标准只规定了以下动作:CONTINUEGOTO(以及 GO TO)。

这里有一个可能会用在简单程序中的例子。当一个警告发生时它打印一个简单消息,而发生一个错误时它会中止程序:

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

语句EXEC SQL WHENEVER是 SQL 预处理器的指令,而不是 C 语句。它设置的错误或警告动作适用于源代码中出现在处理器设置位置之后的所有嵌入式 SQL 语句,除非在第一个EXEC SQL WHENEVER与触发该条件的 SQL 语句之间,为同一条件设置了其他动作。这与 C 程序的控制流无关。因此,下面两个 C 程序片段都不会产生预期效果:

/*
 * 错误
 */
int main(int argc, char *argv[])
{
    ...
    if (verbose) {
        EXEC SQL WHENEVER SQLWARNING SQLPRINT;
    }
    ...
    EXEC SQL SELECT ...;
    ...
}
/*
 * 错误
 */
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表示一种无害的情况,例如上一个查询返回零行。sqlcodesqlstate是两种不同的错误编码方案,详见下文。

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

字段sqlcaidsqlabcsqlerrp,以及sqlerrdsqlwarn的其余元素,目前不包含有用信息。

SQL 标准中没有定义sqlca结构体,但是在一些其他的 SQL 数据库系统中都有实现。在核心上这些定义都相似,但是如果你想要编写可移植的应用,那么你应该仔细研究不同的实现。

这里有一个整合使用WHENEVERsqlca的例子,当一个错误发生时打印出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
===============

33.8.3. SQLSTATESQLCODE #

字段sqlca.sqlstatesqlca.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值:

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)

主变量是类型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)。

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

提交更正

译文有误、术语不当或页面显示问题,请到译文仓库 pgsty/pgdoc 报告译文问题。 英文原文本身的问题,请在当前版本的对应页面向上游反馈;上游不再修订已结束维护的版本。