8

How can I find the position of an error in a Dynamic SQL statement in PL/SQL or SQL?

From SQL*Plus I see the position of an error in, for example, an invalid SQL DML statement:

SYS@orcl> SELECT
       2    X
       3  FROM
       4    TABLEX
       5  /
  TABLEX
  *
ERROR at line 4:
ORA-00942: table or view does not exist

SQL*Plus shows the error with the line number, and prints and marks that line with an asterisk where the error is found.

Converting to Dynamic SQL, I can get the error code (SQLCODE) and error message (SQLERRM):

SYS@orcl> SET SERVEROUTPUT ON
SYS@orcl> BEGIN
       2    EXECUTE IMMEDIATE 'SELECT X FROM TABLEX';
       3  EXCEPTION
       4    WHEN OTHERS THEN
       5      DBMS_OUTPUT.PUT_LINE('SQLCODE:' || SQLCODE);
       6      DBMS_OUTPUT.PUT_LINE('SQLERRM:' || SQLERRM);
       7  END;
       8  /
SQLCODE:-942
SQLERRM:ORA-00942: table or view does not exist

But how do I get the position of the error in the Dynamic SQL string?

I see that Oracle provides a SQL Communications Area (SQLCA) that contains interesting information about an error. In particular:

  • the SQLCODE and SQLERRM fields (that might be the source of the data retrieved with the respective PL/SQL functions),
  • the SQLERRD field where the SQLERRD(5) element that gives the 'parse error offset'.

Is it possible to access SQLERRD from PL/SQL or SQL? If so, how? If not, what other technique can give the location of the error from PL/SQL or SQL?

(Here http://docs.oracle.com/cd/B28359_01/appdev.111/b31231/chapter8.htm#BABIGBFF the SQLCA is documented and accessed with Pro*C.)

(The answer here how to declare SQLCA.SQLERRD? seems to indicate that SQLERRD is not defined in PL/SQL and therefore not accessible.)

(The discussion here Why doesn't Oracle tell you WHICH table or view does not exist? gives some suggestions to show bad SQL using trace files and to show the location of errors in some development tools.)

4

2 回答 2

3

通过动态 PL/SQL 运行语句会将相关的行号存储在错误堆栈中。

例如,此语句在第 4 行有错误:

declare
    v_count number;
    v_bad_sql varchar2(32767) := 
        'SELECT
            X
          FROM
            TABLEX';
begin
    execute immediate v_bad_sql into v_count;
exception when others then
    begin
        execute immediate
            'begin for i in ( '||v_bad_sql||') loop null; end loop; end;';
    exception when others then
        dbms_output.put_line(sqlerrm);
    end;
end;
/

ORA-06550: line 4, column 4:
PL/SQL: ORA-00942: table or view does not exist
ORA-00942: table or view does not exist
ORA-06550: line 1, column 18:
PL/SQL: SQL Statement ignored
ORA-00942: table or view does not exist

这种方法有一些缺点:

  1. 它需要一些额外的、丑陋的代码来捕获异常并重试 SQL。
  2. 该示例仅适用于选择。您需要针对插入、更新、删除、合并、动态 PL/SQL 等进行调整。通常您应该知道它是哪种 SQL 语句。如果运气不好,您将需要解析语句,这可能非常困难。
  3. 如果整个 PL/SQL 语句在一行上,则列号错误。
于 2013-04-24T06:32:54.417 回答
3

你有一个用于在dbms_utility中提取错误消息的包

begin 
    .. generate error
exception when others then 
    dbms_output.put_line(
        dbms_utility.format_call_stack()      || chr(10) || 
        dbms_utility.format_error_backtrace() || chr(10) || 
        dbms_utility.format_error_stack())
end;
于 2013-04-24T09:02:17.310 回答