相关文章推荐

应用程序必须预测运行时错误并尝试从中恢复。本章提供了有关错误报告和恢复的深入讨论。您将学习如何使用状态变量SQLCODE,SQLSTATE,SQLCA(SQL通信区域)和 WHENEVER 语句来处理警告和错误。您还将学习如何使用状态变量ORACA(Oracle通信区域)诊断问题。讨论了以下主题:

错误处理的必要性

错误处理替代方案

关于MODE = {ANSI | ANSI14}时使用状态变量

关于使用SQL通信区域

关于使用Oracle通信区域

8.2 错误处理替代方案

Oracle预编译器提供了四个状态变量,用作错误处理机制:

SQLCODE(Pro * FORTRAN中的SQLCOD)

SQLSTATE(Pro * FORTRAN中的SQLSTA)

SQLCA(使用WHENEVER声明)

ORACA

MODE选项(描述)控制ANSI / ISO合规性。SQLCODE,SQLSTATE和SQLCA变量的可用性取决于MODE设置。无论MODE设置如何,您都可以声明和使用ORACA变量。有关更多信息,请参阅 关于使用Oracle通信区域

MODE={ORACLE|ANSI13} ,您必须声明SQLCA状态变量。接受SQLCODE和SQLSTATE声明(不推荐),但不会将其识别为状态变量。有关更多信息,请参阅 关于使用SQL通信区域

MODE={ANSI|ANSI14} ,您可以使用SQLCODE,SQLSTATE和SQLCA变量中的任何一个,两个或全部三个。要确定哪种变量(或变量组合)最适合您的应用,请参阅 关于在MODE = {ANSI | ANSI14}时使用状态变量 “。

8.3.6 声明SQLCODE

SQLCODE(Pro * FORTRAN中的SQLCOD)必须在Declare Section 内部 外部 声明为4字节整数变量,如 表8-1 所示。

如果在Declare Section之外声明,仅当ASSUME_SQLCODE = YES时,SQLCODE才被识别为状态变量。当 MODE={ORACLE|ANSI13} 时,将忽略SQLCODE声明。

在Pro * COBOL源文件中,如果声明了SQLCA, 不要 声明SQLCODE。同样,如果声明了SQLCODE, 不要 声明SQLCA。SQLCA结构声明的状态变量也称为SQLCODE,因此如果使用两种错误报告机制,则会发生错误。

通过使用允许本地和全局声明的主机语言,您可以声明多个SQLCODE变量。访问本地SQLCODE受其程序范围的限制。在每次SQL操作之后,Oracle都会将状态代码返回到当前范围内的SQLCODE。因此,您的程序可以通过显式检查SQLCODE或使用 WHENEVER 语句隐式检查来了解最新SQL操作的结果。

在特定编译单元中声明SQLCODE而不是SQLCA时,预编译器会为该单元分配内部SQLCA。您的主程序无法访问内部SQLCA。如果声明SQLCA SQLCODE(Pro * COBOL中不支持),Oracle会在每次SQL操作后向两者返回相同的状态代码。

在每次SQL操作之后,Oracle都会将状态代码返回到当前范围内的SQLCODE变量。状态代码(表示SQL操作的结果)可以是以下任何数字:

Oracle在不检测错误或异常的情况下执行了SQL语句。

Oracle执行了该语句,但检测到异常。当Oracle无法找到满足 WHERE 子句中的条件的行或 SELECT INTO FETCH 返回任何行时,就会发生这种情况。

MODE={ANSI|ANSI14|ANSI13}, +100 在没有行的 INSERT 之后 MODE={ANSI|ANSI14|ANSI13}, +100 返回到SQLCODE。当子查询不返回要处理的行时,可能会发生这种情况。

由于数据库,系统,网络或应用程序错误,Oracle未执行该语句。这些错误是无法恢复的。当它们发生时,在大多数情况下,当前事务应该回滚。负返回码对应于 Oracle数据库错误消息中 列出的错误代码。

您可以通过使用您自己的代码显式检查SQLCODE或使用 WHENEVER 语句隐式检查来了解最新SQL操作的结果。

在特定预编译单元中声明SQLCODE而不是SQLCA时,预编译器会为该单元分配内部SQLCA。您的主程序无法访问内部SQLCA。如果声明SQLCA SQLCODE(仅限Pro * FORTRAN),Oracle会在每次SQL操作后向两者返回相同的状态代码。

MODE={ORACLE|ANSI13}, 将忽略SQLCODE的声明。

表8-5 显示了SQL92预定义的类。

表8-5预定义的SQL92类

表8-6 显示了Oracle错误如何映射到SQLSTATE状态代码。在某些情况下,几个Oracle错误会映射到状态代码。在其他情况下,没有Oracle错误映射到状态代码(因此最后一列为空)。60000 ... 99999范围内的状态代码是实现定义的。

表8-6 Oracle错误映射到SQLSTATE状态

Oracle错误

8.4 关于使用SQL通信区域

SQL通讯区域( SQLCA)是一种类似记录的数据结构。其字段包含Oracle在SQL时更新的错误,警告和状态信息 声明被执行。因此,SQLCA始终反映最近SQL操作的结果。要确定结果,可以在SQLCA中检查变量。

在允许本地和全局声明的主机语言中,您的程序可以有多个SQLCA。例如,它可能有一个全局SQLCA和几个本地SQLCA。访问本地SQLCA受其在程序范围内的限制。Oracle仅将信息返回给“活动”SQLCA。

另请注意,当您的应用程序使用时 SQL * Net同时访问本地和远程数据库的组合,所有数据库都写入一个SQLCA。每个数据库 没有 不同的SQLCA。有关更多信息,请参阅“ 并发登录 ”。

MODE={ORACLE|ANSI13} ,需要SQLCA;如果未声明SQLCA,则会发生编译时错误。当 MODE={ANSI|ANSI14} ,SQLCA是可选的,但是如果不声明SQLCA,则不能使用 WHENEVER SQLWARNING语句。因此,如果要使用 WHENEVER SQLWARNING语句,则必须声明SQLCA。

如果在特定编译单元中声明SQLCODE而不是SQLCA,则预编译器会为该单元分配内部SQLCA。您的主程序无法访问内部SQLCA。如果声明SQLCA SQLCODE(仅限Pro * FORTRAN),Oracle会在每次SQL操作后向两者返回相同的状态代码。

MODE={ANSI|ANSI14} ,必须声明SQLSTATE(请参阅 SQLCODE和SQLSTATE SQLSTATE状态变量支持SQL92标准指定的SQLSTATE状态变量。您可以在有或没有SQLCODE的情况下使用SQLSTATE状态变量。有关更多信息,请 参见表8-3 表8-4

8.4.4 SQLCA中有什么?

SQLCA包含有关SQL语句结果的以下运行时信息:

Oracle错误代码

行处理计数

图8-2 显示了SQLCA中的所有变量。要查看特定主机语言的SQLCA结构和变量名称,请参阅本指南的补充内容。

图8-2 SQLCA变量

下面是图8-2的描述
“图8-2 SQLCA变量”的描述

8.4.11 SQLCA结构

本节介绍SQLCA的结构,其字段以及它们可以存储的值。

Oracle在未检测到错误或异常的情况下执行了该语句。

Oracle执行了该语句,但检测到异常。当Oracle找不到符合 WHERE -clause搜索条件的行或 SELECT INTO FETCH 没有返回任何行时,就会发生这种情况。

MODE={ANSI|ANSI14|ANSI13}, +100 之后返回到SQLCODE INSERT没有行。当子查询不返回要处理的行时,可能会发生这种情况。

由于数据库,系统,网络或应用程序错误,Oracle未执行该语句。这些错误是无法恢复的。当它们发生时,在大多数情况下,当前事务应该回滚。

负返回码对应于 Oracle数据库错误消息中 列出的错误代码。

此整数字段保存存储在SQLERRMC中的消息文本的长度。

则sqlerrmc

此字符串字段包含存储在SQLCODE中的错误代码的消息文本,最多可存储70个字符。对于超过70个字符的消息的全文,请使用SQLGLM函数。

验证SQLCODE是否定的

您引用SQLERRMC 之前 。如果在SQLCODE为零时引用SQLERRMC,则会获得与先前SQL语句关联的消息文本。

该字段留作将来使用。

SQLERRD(3)

该字段包含最近执行的SQL语句处理的行数。但是,如果SQL语句失败,则SQLERRD(3)的值未定义,但有一个例外。如果在数组操作期间发生错误,则处理在导致错误的行处停止,因此SQLERRD(3)给出成功处理的行数。

行处理计数在 OPEN 语句后归零,在 FETCH 语句后递增。对于 EXECUTE INSERT UPDATE DELETE SELECT INTO 语句,计数反映了成功处理的行数。伯爵的确如此 包括更新或处理的行 删除级联。例如,如果因为符合WHERE子句条件而删除了20行,并且删除了5行,因为它们现在(在主删除之后)违反了列约束,则计数为20而不是25。

SQLERRD(4)

该字段留作将来使用。

SQLERRD(5)

此字段包含一个偏移量,指定在最近执行的SQL语句中解析错误开始的字符位置。第一个角色占据零位。

SQLERRD(6)

该字段留作将来使用。

8.4.18 SQLWARN

这个单个字符数组有八个元素。它们用作警告标志。Oracle通过为其分配“W”(用于警告)字符值来设置标志。旗帜警告特殊情况。

例如,当Oracle将截断的列值分配给输出主机变量时,会设置警告标志。

另请注意,虽然 图8-2将 SQLWARN说明为数组,但它在Pro * COBOL中实现为一个组项,其中包含名为SQLWARN0到SQLWARN7的基本PIC X项。Pro * FORTRAN实现由LOGICAL变量SQLWN0到SQLWN7组成。

中国领域的描述 SQLWARN如下:

SQLWARN(0)

如果设置了另一个警告标志,则设置此标志。

SQLWARN(1)

如果将截断的列值分配给输出主机变量,则会设置此标志。这仅适用于字符数据。Oracle在不设置警告或返回负SQLCODE值的情况下截断某些数字数据。

要查明列值是否被截断以及删除了多少,请检查与输出主机变量关联的指示符变量。指标变量返回的(正)整数是列值的原始长度。您可以相应地增加主机变量的长度。

SQLWARN(2)

如果在评估SQL组函数(如AVG,COUNT或MAX)时忽略了一个或多个空值,则会设置此标志。此行为是预期的,因为除COUNT(*)外,所有组函数都忽略空值。如有必要,可以使用SQL函数NVL临时为空列条目分配值(例如,零)。

SQLWARN(3)

如果查询选择列表中的列数不等于 SELECT FETCH 语句的 INTO 子句中的主机变量数,则设置此标志。返回的项目数量是两者中较小的一个。

SQLWARN(4)

如果表中的每一行都由没有WHERE子句的 UPDATE DELETE 语句处理,则设置此标志。如果没有搜索条件限制处理的行数,则更新或删除称为 无条件 。此类更新和删除是不常见的,因此Oracle会设置此警告标志。这样,您可以在必要时回滚事务

SQLWARN(5)

当EXEC SQL CREATE {PROCEDURE | FUNCTION | PACKAGE | PACKAGE BODY}语句由于PL / SQL编译错误而失败时,将设置此标志。

SQLWARN(6)

此标志已不再使用。

SQLWARN(7)

此标志已不再使用。

8.4.21 获取错误消息的全文

SQLCA可以容纳长达70个字符的错误消息。要获取更长(或嵌套)错误消息的全文,您需要 SQLGLM函数。如果连接到Oracle,则可以调用 SQLGLM使用语法

SQLGLM(message_buffer, buffer_size, message_length);
                        

message_buffer

是您希望Oracle在其中存储错误消息的文本缓冲区(Oracle空白填充到此缓冲区的末尾)。

缓冲区大小

是一个整数变量,指定缓冲区的最大大小(以字节为单位)。

message_length

是一个整数变量,Oracle在其中存储错误消息的实际长度。

Oracle错误消息的最大长度为512个字符,包括错误代码,嵌套消息和表和列名称等消息插入。SQLGLM返回的错误消息的最大长度取决于您为buffer_size指定的值。

在以下示例中,您调用SQLGLM以获取最多100个字符的错误消息:

-- declare variables for function call
msg_buffer CHARACTER(100);
buf_size INTEGER;
msg_length INTEGER;
set buf_size = 100;
EXEC SQL WHENEVER SQLERROR DO sql_error;
-- other statements
ROUTINE sql_error
BEGIN
 -- get full text of error message
 SQLGLM(msg_buffer, buf_size, msg_length);
 display contents of msg_buffer;
 exit program with an error
END sql_error;

请注意,只有在发生SQL错误时才会调用SQLGLM。调用SQLGLM 之前,始终确保SQLCODE为负数。如果在SQLCODE为零时调用SQLGLM,则会获得与先前SQL语句关联的消息文本。

默认情况下,预编译程序会忽略Oracle错误和警告条件,并在可能的情况下继续处理。执行自动条件检查和错误处理,使用WHENEVER语句。

使用WHENEVER语句,您可以指定Oracle检测到错误,警告条件或“未找到”条件时要采取的操作。这些操作包括继续下一个语句,调用例程,分支到带标签的语句或停止。

您使用以下语法编写WHENEVER语句:

EXEC SQL WHENEVER <condition> <action>;

您可以让Oracle自动检查SQLCA是否满足以下任何条件。

设置SQLWARN(0)是因为Oracle返回了警告(警告标志之一,SQLWARN(1)到SQLWARN(7),也设置了)或SQLCODE具有+1403以外的正值。例如,当Oracle将截断的列值分配给输出主机变量时,会设置SQLWARN(1)。

当MODE = {ANSI | ANSI14}时,声明SQLCA是可选的。但是,要使用WHENEVER SQLWARNING, 必须声明SQLCA。

8.4.32 指南

以下指南将帮助您避免一些常见的陷阱。

发表声明。 通常,在程序中的第一个可执行SQL语句之前编写 WHENEVER 语句。这可以确保所有后续错误都被捕获,因为WHENEVER语句对文件末尾保持有效。

处理数据结束条件。 在使用游标获取行时,您的程序应准备好处理数据结束条件。如果 FETCH 没有返回数据,程序应该分支到发出 CLOSE 命令的标记代码段,如下所示:

SQL WHENEVER NOT FOUND GOTO no_more;
no_more:
 EXEC SQL CLOSE my_cursor;

避免无限循环。 如果 WHENEVER SQLERROR GOTO 语句分支到包含可执行SQL语句的错误处理例程,则如果SQL语句因错误而失败,则程序可能会进入无限循环。您可以通过在SQL语句之前编写 WHENEVER SQLERROR CONTINUE 来避免这种情况,如以下示例所示:

EXEC SQL WHENEVER SQLERROR GOTO sql_error;
sql_error:
 EXEC SQL WHENEVER SQLERROR CONTINUE;
 EXEC SQL ROLLBACK WORK RELEASE;

如果没有 WHENEVER SQLERROR CONTINUE 语句, ROLLBACK 错误将再次调用例程,启动无限循环。

不小心使用 WHENEVER 会导致问题。例如,如果 DELETE 语句设置 NOT FOUND ,则以下代码进入无限循环,因为没有行符合搜索条件:

-- improper use of WHENEVER
EXEC SQL WHENEVER NOT FOUND GOTO no_more;
 EXEC SQL FETCH emp_cursor INTO :emp_name, :salary;
ENDLOOP;
no_more:
 EXEC SQL DELETE FROM EMP WHERE EMPNO = :emp_number;

在下一个示例中,您可以通过重置 GOTO 目标来正确处理 NOT FOUND 条件:

-- proper use of WHENEVER
EXEC SQL WHENEVER NOT FOUND GOTO no_more;
 EXEC SQL FETCH emp_cursor INTO :emp_name, :salary;
ENDLOOP;
no_more:
 EXEC SQL WHENEVER NOT FOUND GOTO no_match;
 EXEC SQL DELETE FROM EMP WHERE EMPNO = :emp_number;
no_match:

保持可寻址性。 使用允许本地和全局标识符的宿主语言,确保由 WHENEVER GOTO 语句控制的所有SQL语句都可以分支到 GOTO 标签。下面的代码将导致编译时错误,因为在 labelA FUNC1不FUNC2 INSERT语句的范围之内:

FUNC1
 BEGIN
 EXEC SQL WHENEVER SQLERROR GOTO labelA;
 EXEC SQL DELETE FROM EMP WHERE DEPTNO = :dept_number;
 labelA:
FUNC2
 BEGIN
 EXEC SQL INSERT INTO EMP (JOB) VALUES (:job_title); 

WHENEVER GOTO 语句分支的标签必须与语句位于同一预编译文件中。

错误后返回。 如果程序在处理错误后必须返回,请使用 DO routine_call 操作。或者,您可以测试SQLCODE的值,如以下示例所示:

EXEC SQL UPDATE EMP SET SAL = SAL * 1.10;
IF sqlca.sqlcode < 0 THEN
 -- handle error
EXEC SQL DROP INDEX EMP_INDEX;

只需确保没有 WHENEVER GOTO WHENEVER STOP 语句处于活动状态。

8.4.33 获取SQL语句的文本

在许多预编译器应用程序中,可以方便地知道正在处理的语句的文本,长度以及它包含的SQL命令(例如 INSERT SELECT )。对于使用动态SQL的应用程序尤其如此。

例程SQLGLS是SQLLIB运行时库的一部分,它返回以下信息:

您可以在发出静态SQL语句后调用SQLGLS。使用动态SQL方法1,可以在执行SQL语句后调用SQLGLS。使用动态SQL方法2,3或4,可以在准备语句后调用SQLGLS。

要调用SQLGLS,请使用以下语法:

SQLGLS(SQLSTM, STMLEN, SQLFC)

表8-7 显示了SQLGLS参数列表中可用于参数的主机语言数据类型。

表8-7 SQLGLS参数数据类型

所有参数必须通过引用传递。这通常是默认参数传递约定;你不需要采取特别的行动。

参数SQLSTM是一个空白填充(非空终止)字符缓冲区,用于保存SQL语句的返回文本。您的程序必须静态声明缓冲区或为其动态分配内存。

长度参数 STMLEN是一个四字节整数。在调用SQLGLS之前,请将此参数设置为SQLSTM缓冲区的实际大小(以字节为单位)。当SQLGLS返回时,SQLSTM缓冲区包含填充到缓冲区长度的SQL语句文本空白。STMLEN返回返回语句文本中的实际字节数,不计算空白填充。但是,如果发生错误,STMLEN将返回零。

一些可能的错误如下:

没有解析SQL语句。

您传递了无效参数(例如,负长度值)。

SQLLIB中发生内部异常。

参数 SQLFC是一个四字节整数,它在语句中返回SQL命令的SQL函数代码。 表8-8 显示了每个SQL命令的功能代码。

SQLGLS不返回包含以下命令的语句:

CONNECT

ROLLBACK

这些语句没有SQL函数代码。

表8-8 SQL命令功能代码

SQL函数 SQL函数

8.5.1 声明ORACA

要声明ORACA,只需在主机语言源文件中包含它(使用EXEC SQL INCLUDE 语句),如下所示:

* Include the Oracle Communications Area (ORACA).
 EXEC SQL INCLUDE ORACA

ORACA必须在声明部分 之外 声明。

预编译程序时, INCLUDE ORACA语句将被多个程序变量声明替换。这些声明允许Oracle与您的程序进行通信。

如果设置了主DEBUG标志(ORADBGF),则此标志允许您在每个游标操作之前检查游标缓存的一致性。

Oracle运行时库执行一致性检查,并可能发出错误消息,这些消息列在 Oracle数据库错误消息中 。它们像Oracle错误消息一样返回到SQLCA。

此标志具有以下设置:

0 - 禁用缓存一致性检查(默认值)。

1 - 启用缓存一致性检查。

如果设置了主DEBUG标志(ORADBGF),则此标志告诉Oracle运行时库每次预编译器动态分配或释放内存时都要检查堆的一致性。这对于检测扰乱内存的程序错误很有用。

必须在发出 CONNECT 命令之前设置此标志,并且一旦设置,则无法清除;后续更改请求将被忽略。它具有以下设置:

0 - 禁用堆一致性检查(默认值)。

1 - 启用堆一致性检查。

此子记录标识包含当前SQL语句的文件,因此可帮助您在为一个应用程序预编译多个文件时查找错误。它包含以下两个字段:

ORASFNML

此整数字段保存ORASFNMC中存储的文件名的长度。

ORASFNMC

此字符串字段包含文件名。最多存储前70个字符。

8.5.23 一个例子

以下程序提示输入部门编号,将该部门中每个员工的姓名和工资插入两个表中的一个,然后显示ORACA的诊断信息:

EXEC SQL BEGIN DECLARE SECTION; 
 username CHARACTER(20);
 password CHARACTER(20);
 emp_name INTEGER;
 dept_number INTEGER;
 salary REAL;
EXEC SQL END DECLARE SECTION; 
EXEC SQL INCLUDE SQLCA;
EXEC SQL INCLUDE ORACA;
display 'Username? ';
read username;
display 'Password? ';
read password;
EXEC SQL WHENEVER SQLERROR DO sql_error;
EXEC SQL CONNECT :username IDENTIFIED BY :password;
display 'Connected to Oracle';
EXEC ORACLE OPTION (ORACA=YES);
-- set flags in the ORACA
set oraca.oradbgf = 1; -- enable debug operations
set oraca.oracchf = 1; -- enable cursor cache consistency check
set oraca.orastxtf = 3; -- always save the SQL statement
display 'Department number? ';
read dept_number;
EXEC SQL DECLARE emp_cursor CURSOR FOR
 SELECT ENAME, SAL + NVL(COMM,0)
 FROM EMP
 WHERE DEPTNO = :dept_number;
EXEC SQL OPEN emp_cursor;
EXEC SQL WHENEVER NOT FOUND DO no_more; 
rLOOP
 EXEC SQL FETCH emp_cursor INTO :emp_name, :salary;
 IF salary < 2500 THEN
 EXEC SQL INSERT INTO PAY1 VALUES (:emp_name, :salary);
 EXEC SQL INSERT INTO PAY2 VALUES (:emp_name, :salary);
 ENDIF;
ENDLOOP;
ROUTINE no_more
BEGIN
 EXEC SQL CLOSE emp_cursor;
 EXEC SQL WHENEVER SQLERROR CONTINUE;
 EXEC SQL COMMIT WORK RELEASE;
 display 'Last SQL statement: ', oraca.orastxt.orastxtc;
 display '... at or near line number: ', oraca.oraslnr;
 display
 display ' Cursor Cache Statistics';
 display '-------------------------------------------';
 display 'Maximum value of MAXOPENCURSORS ', oraca.orahoc;
 display 'Maximum open cursors required: ', oraca.oramoc;
 display 'Current number of open cursors: ', oraca.oracoc;
 display 'Number of cache reassignments: ', oraca.oranor;
 display 'Number of SQL statement parses: ', oraca.oranpr;
 display 'Number of SQL statement executions: ', oraca.oranex;
 exit program;
END no_more;
ROUTINE sql_error
BEGIN
 EXEC SQL WHENEVER SQLERROR CONTINUE;
 EXEC SQL ROLLBACK WORK RELEASE;
 display 'Last SQL statement: ', oraca.orastxt.orastxtc;
 display '... at or near line number: ', oraca.oraslnr;
 display
 display ' Cursor Cache Statistics';
 display '-------------------------------------------';
 display 'Maximum value of MAXOPENCURSORS ', oraca.orahoc;
 display 'Maximum open cursors required: ', oraca.oramoc;
 display 'Current number of open cursors: ', oraca.oracoc;
 display 'Number of cache reassignments: ', oraca.oranor;
 display 'Number of SQL statement parses: ', oraca.oranpr;
 display 'Number of SQL statement executions: ', oraca.oranex;
 exit program with an error;
END sql_error;
 
推荐文章