ProC에서 Error 처리하기(SQLSTAT,SQLCODE,SQLCA)

디자인상의 문제, 코딩상의 문제, 하드웨어 문제, 비정상적인 값의 입력 등등....
프로그램을 수행하다 보면 다양한 에러들을 접하게 된다. 프로그램을 정상적으로

수행하기 위해서는 이들 에러에 대한 적절한 대책이 필요 한데, 특히 SQL문의

수행과 관련된 에러의 처리는 데이타의 무결성을 유지하고, 정확한 트랜젝션

수행의 기본이 된다. PRO*C에서는 SQL문과 관련된 에러를 처리하기 위한 다양한

방법을 제공하고 있는데, 크게 상태 변수(SQLSTATE, SQLCODE)를 사용하는 방법과 SQLCA를 활용하는 방법으로 나눠볼 수 있다.

1)
  MODE OPTION값에 따른 ERROR 처리


  * MODE = ANSI 일때

    SQLSTATE SQLCODE중에 하나는 반드시 사용해야 한다.
    SQLCA는 선택적이다
.
 
* MODE = ORACLE
    SQLCA를 사용해야 한다
.
    SQLSTATE SQLCODE는 선언해도 사용되지 않는다
.

2)
 
SQLCODE

  SQLCODE SQL문을 수행한 STATUS CODE 값을 가지고 있다. 

  이 변수는 LONG으로 선언해서 사용해야 한다
.

  < 예제
>
   
DECLARE SQL BEGIN DECLARE SECTION;
        int   
emp_number, dept_number;
   
EXEC SQL END DECLARE SECTION;
    long 
SQLCODE;

3)
  SQLSTATE 상태 변수
(status variable)

  SQLCODE와 달리 SQLSTATE는 에러와 warning을 모두 저장하고 있고
, character
  5자리의 값을 가지고 있다. SQLSTATE를 사용하기 위해서는 반드시  CHAR SQLSTATE[6]; 으로 선언해야 한다. 

  SQLSTATE 상태 코드는 예외의 유형을 구분해주는 CLASS 두 자리와 구체적인

  예외를 말해주는 3자리의 SUBCLASS로 구성된다. 이들 CODE값과 그 의미는    PROGRAMMER*S GIDE TO THE ORACLE PRO*C/C++ PRECOMPILER
Handling
  runtime error편에 자세히 나와 있다
.

4)
  SQLCA(COMMUNICATION AREA)의 사용


  SQLCA ORACLE COMMUNICATION을 다루는 C STRUCT를 말한다. 이는 상태 코드,
  경고 FLAG, 처리된 ROW , PARSING ERROR,  기타 에러 메세지에 관한 정보를

  담고 있다.


* SQLCA
의 선언

  EXEC SQL INCLUDE SQLCA;    또는  # include <sqlca.h>

* SQLCA STRUCTURE
와 의미

  - sqlcaid : identifier
  - sqlcabc : SQLCA의 총
size
  - sqlcode : 최근에 실행된 SQL문의 상태코드

                      0  에러 없이 정상 수행   
                    >0  SQL문이 수행되기는 했지만 exception이 발생

                    <0  SQL문 수행 안됨. sqlca.sqlerr[4]로 내용 확인 가능
  - sqlerrm :  에러 메세지 길이(sqlerrml)와 메시지 내용(sqlerrmc)
                    메세지는 총 70자리 까지 밖에 저장되지 않음. 이 이상

                    의 메세지를 보려면 sqlglm()을 이용해야 한다.(sqlcpr.h
                    에 정의
)
                    만약 sqlcode 0일때 sqlerrmc의 내용을 보면 이전에

                    수행된 에러 메시지가 남아 있으므로 sqlcode < 0일때만
                    사용하는 것이  바람직하다.

  - sqlerrd :  SQL문 수행과 관련된 각종 INTEGER정보들

          sqlerrd[0], sqlerrd[1], sqlerrd[3], sqlerrd[5] 
                    : 현재 사용하고 있지 않음

          sqlerrd[2] : 처리된 row.  cascade를 처리된 row수는 제외됨.
                      Array processing을 하는 중에 에러가 발생했다면,

                      변수에는 정상적으로 처리된 row의 수를 return하게

                      된다.
          sqlerrd[4] : SQL문에서 parse error가 발생한 지점. ( 0부터 시작
)

  - sqlwarn : warning flag. 해당 warning 발생시 *W* set한다
.
        sqlwarn[0] : warning 발생 여부
set
        sqlwarn[1] : character data의 경우truncated 여부
set
                          indicator변수를 사용하면 원래의 size 확인 가능
.
        sqlwarn[2] : NULL 값이 group함수에 사용되었는지를
set
        sqlwarn[3] :  select column bind column의 갯수가 다른

                          경우 set
        sqlwarn[4] : where절이 없는update, delete문 수행 시
set.
                          where절이 없이 수행된다는 것은 비정상적인 상황

                          이기 때문에 warning을 한다.
       
sqlwarn[5]: EXEC SQL CREATE {PROCEDURE/FUNCTION |
                            PACKAGE|PACKAGE BODY]문 수행시 컴파일 에러

                            발생시 set

5)
  WHENEVER문 사용법


  디폴트로 컴파일된 프로그램은 오라클 에러나, warning을 무시하고, 가능하다면
  프로그램 수행을 계속한다. 비정상적인 동작에 적절한 조치를 취하기 위해 자동

  으로 에러나 warning의 발생을 감지하려면 WHENEVER문을 사용하면 된다. 
  WHENEVER문의 scope rule은 위치에 의해 결정되는 것이지, logical하게

  결정되는 것이 아니라는 점을 주의해야 한다. 그러므로 수행될 SQL 문 이전에

  WHENEVER절을 위치하도록 해야 한다. 그리고, 다음 WHENEVER절이 나타나기
  이전까지만 효과가 있다.

  WHENEVER SQLWARING문을 사용하려면 반드시 SQLCA를 선언해야 한다
.

 
* SYNTAX
      EXEC  SQL   
WHENEVER <condition> <action>;
  * CONDITION유형 

      SQLWARNING /  SQLERROR  / 
NOT FOUND
  * ACTION 유형

    - CONTINUE :WHENEVER문을 사용하지 않은 것과 동일한 결과
    -  DO
    - GOTO 
<label>
    - STOP : COMMIT되지 않은 WORK은 모두 ROLLBACK 시킴
.

  condition action을 적절히 혼합하여 사용하면 되는데, 조심해야 할 사항은

  WHENEVER SQLERROR GOTO <lable>을 사용할때 무한 loop에 걸리지 않도록 주의

  해야 한다. (대신에 WHENEVER SQLERROR CONTINUE를 사용하는 것이 좋다.)
 

  다음은 WHENEVER  ...... DO  구문을 사용한 간단한 예제이다
.

 
......
  EXEC SQL WHENEVER SQLERROR DO   
handle_insert_error(*Insert Error*);
 
EXEC SQL INSERT INTO emp (empno, ename,deptno)
           
VALUES (:v_empno, :v_ename, :v_deptno);
  EXEC SQL WHENEVER SQLERROR DO 
handle_delete_error(*delete Error*);

 
......
 
handle_insert_error(char *stmt)
 
{
     
switch(sqlca.sqlcode)
      { case        -1 : 
/*duplicate l\key value */
                               
......
                               
break;
        case    -1401 :  /*value too large*/   

                                 
......
                                 
break;
        default        : 
/*do somthing here too*/
                                 
........
                                 
break;
       
}
 
}

 
handle_delete_error(char *stmt)
 
{
     
printf("%s\n\n", stmt);
      if 
( sqlca.sqlerrd[2] == 0 )
      {   
/* no rows deleted */
          ........   

     
}
     
else
     
{
       
......
     
}
  }


* WHENEVER절을 사용할때 주의사항 *

precompiler를 사용할 때 EXEC SQL WHENEVER절로  error를
처리하게 된다.
whenever절은 다음에 만나는 다른 whenever절에 의해서
효력을 상실하게 된다. 그러므로 다음과 같이 두개의 whenever절이
있을경우:
예>
EXEC SQL WHENEVER SQLERROR DO aaa();
EXEC SQL WHENEVER SQLERROR DO bbb();

에러가 발생시에 aaa()대한 처리를 수행하지 않고 bbb()에 대한
처리를 수행하게 된다.

그리고 영향을 주는 범위가 위치에 의해 결정이 되기때문에 program
을 작성시에 착각을 일으키기가 쉽다.
먼저 쉽게 오류를 일으킬 경우로 조건문에서  두개의 whenever절이
사용되었을 경우를 알아보자.
program중에 다음과 같은 경우의 문장이 있다고 가정을 하자.

int a=1;

if (a==1)
        EXEC SQL WHENEVER SQLERROR DO aaa();
else
        EXEC SQL WHENEVER SQLERROR DO bbb();

EXEC SQL SELECT .....
         INTO   .....
         FROM  ...;

이 경우에 ERROR가 발생하게 되면 error 처리로서 수행되는 함수는
비록 논리적으로는 aaa()인것 같지만 실제로는 bbb()이다.
다음의 경우도 살펴보자.

main()
{
EXEC SQL WHENEVER SQLERROR DO aaa();

sub_funtion()

EXEC SQL SELECT .....
         INTO   .....
         FROM   xxx;

}
sub_funtion()
{
EXEC SQL WHENEVER SQLERROR DO bbb();
EXEC SQL SELECT .....
         INTO   .....
         FROM   yyy;
}
위와 같은 경우에 yyy table에 대한 error가 발생할 경우에는
당연히 bbb()함수로 error 처리를 하게되고 xxx table에 대한
error가 발생할 경우에는 비록 sub_funtion을 수행 했지만
aaa()함수를 수행하게 된다.

이상과 같이 whenever절은 논리적으로 프로그램이 어떻게 되어
있는지가 아니라 프로그램안에서의 whenever절이 어느 위치에
있는지에 의해서 기능이 결정된다.

by 티티알 2007. 5. 14. 14:08
| 1 2 3 4 5 ··· 14 |