[번역] 런타임 에러 처리 (Handling Runtime Errors)
Language/Pro*C 2015. 5. 20. 17:23목 차
▣ The Need for Error Handling
▣ Error Handling Alternatives
▣ SQLSTATE 상태변수( The SQLSTATE Status Variable )
▣ SQLCODE선언( Declaring SQLCODE )
▣ Key Components of Error Reporting Using the SQLCA
▣ Using the SQL Communications Area (SQLCA)
▣ Getting the Full Text of Error Messages
▣ Using the WHENEVER Directive
▣ Obtaining the Text of SQL Statements
▣ Using the Oracle Communications Area (ORACA)
: 여러가지 상황에서 에러가 발생할 수 있기 때문에 에러 처리는 필요하며, 프로그램에
중요한 부분을 차지하게 된다.
2) Error Handling Alternatives
: Manual 참조
: Manual 참조
4) SQLCODE선언( Declaring SQLCODE )
: Manual 참조
5) Key Components of Error Reporting Using the SQLCA
: sqlcode -> 0 success, sqlcode -> - error, sqlcode-> + exception
sqlca.sqlerrd[4]-> SQL문을 파싱하다가 에러가 발생하면 옵셋값을 설정.
파싱하다 에러가 난 지점의 문자위치를 옵셋에 설정(zero-based position)
에러가 발생하지 않으면,sqlca.sqlerrd[4]는 0이다. 첫번째문자에서 에러가 발생해도
sqlca.sqlerrd[4]에는 0이 설정된다. 그래서 sqlca.sqlerrd[4]는 sqlca.sqlcode가
음수로 리턴될때만 체크한다.
에러코드와 메세지는 SQLERRMC인 SQLCA변수에 저장되며 길이는 70문자가 저장된다.
70문자 이상의 TEXT를 보기위해서는 sqlglm()함수를 사용한다.
6) Using the SQL Communications Area (SQLCA)
: MODE=ORACLE인 경우, SQLCA선언이 필요하며,선언하기 위해서는 아래와 같이 하면된다.
EXEC SQL INCLUDE SQLCA;
혹은
#include <sqlca.h>
Declare Section을 사용하면, SQLCA는 Declare Section 밖에 선언되어야만 한다.
SQLCA를 선언하지 않으면 컴파일시 에러가 난다.
MODE=ANSI인경우, SQLCA선언은 선택적이면, SQLCODE혹은 SQLSTATE 상태변수는 반드시 선언
해야만 한다. SQLCODE는 int형이다. SQLCA 대신에 SQLCODE혹은 SQLSTATE를 선언하고 컴파일
할 경우, 전처리기는 내부적으로 SQLCA를 할당한다. Pro*C/C++프로그램은 내부적인 SQLCA를
접근할 수 없다.
6-1) SQLCA Contents
: SQLCA는 다음과 같은 내용을 포함하고 있다.
# 오라클 에러코드
# 경고 플래그들 (Warning flags)
# 이벤트 정보
# 처리된 행의 수
# 분석 (Diagnostics)
sqlca.h 헤더 파일에 대해서 살펴보자.
/*
이름 : SQLCA = SQL Communications Area.
기능 : 코드 내용은 없다. 오라클은 sql문이 실행되는동안 sqlca에
상태정보를 채운다.
NOTES
**************************************************************
*** ***
*** This file is SOSD. Porters must change the data types ***
*** appropriately on their platform. See notes/pcport.doc ***
*** for more information. ***
*** ***
**************************************************************
If the symbol SQLCA_STORAGE_CLASS is defined, then the SQLCA
will be defined to have this storage class. For example:
#define SQLCA_STORAGE_CLASS extern
will define the SQLCA as an extern.
If the symbol SQLCA_INIT is defined, then the SQLCA will be
statically initialized. Although this is not necessary in order
to use the SQLCA, it is a good programing practice not to have
unitialized variables. However, some C compilers/operating systems
don't allow automatic variables to be initialized in this manner.
Therefore, if you are INCLUDE'ing the SQLCA in a place where it
would be an automatic AND your C compiler/operating system doesn't
allow this style of initialization, then SQLCA_INIT should be left
undefined -- all others can define SQLCA_INIT if they wish.
If the symbol SQLCA_NONE is defined, then the SQLCA
variable will not be defined at all. The symbol SQLCA_NONE
should not be defined in source modules that have embedded SQL.
However, source modules that have no embedded SQL, but need to
manipulate a sqlca struct passed in as a parameter, can set the
SQLCA_NONE symbol to avoid creation of an extraneous sqlca variable.
*/
#ifndef SQLCA
#define SQLCA 1
struct sqlca
{
/* ub1 */ char sqlcaid[8]; //SQLCA로 초기화된다.
/* b4 */ long sqlabc; //SQLCA구조체의 길이(바이트)
/* b4 */ long sqlcode; //최근실행된 상태코드 [ 0 : 성공, >0 : exception
MODE=ANSI인경우, +100은 INSERT후에 입력된 행이 없음
<0 은 어떤 ERROR때문에 SQL문을 실행할 수 없을때 발생.
이경우 모든 트랜잭션은 롤백된다.
struct
{
/* ub2 */ unsigned short sqlerrml; // sqlerrmc의 메세지 길이
/* ub1 */ char sqlerrmc[70]; // 에러코드에 상응하는 메세지(not null terminate)
} sqlerrm; // 70문자까지 저장될 수 있음
// 70문자 이상의 경우 sqlglm()함수 사용
/* ub1 */ char sqlerrp[8]; // 미래를 위해서 예약된 변수
/* b4 */ long sqlerrd[6]; // sqlerrd[0] -> 미래를 위해 예약된 변수
// sqlerrd[1] -> 미래를 위해 예약된 변수
// sqlerrd[2] -> 최근에 실행된 SQL문의 처리 행수
SQL문이 실패하면, 정의되지 않음
처리행수는 OPEN문후에 0이되고, FETCH
후에 증가한다.
// EXCUTE,INSERT,UPDATE,DELETE,SELECT문에서 성공적으로
처리된 행수를 반영,UPDATE,DELETE CASCADE에 의해서
처리된 행수는 포함하지 않음.
// sqlerrd[3] -> 미래를 위해 예약된 변수
// sqlerrd[4] -> 최근에 실행된 SQL문의 파싱에러가 난
문자위치의 OFFSET저장.
// sqlerrd[5] -> 미래를 위해 예약된 변수
/* ub1 */ char sqlwarn[8];
// sqlwarn[0] -> 다른 warning flag가 설정되면, 이 요소가 설정
// sqlwarn[1] -> 잘려진 컬럼값이 호스트변수에 할당되었을때 설정
// sqlwarn[2] -> NULL컬럼이 sql 그룹함수에서 사용되지 않는경우 설정
// sqlwarn[3] -> 쿼리의 컬럼수와 select나 fetch문의 into절에 호스트변수의 수가 틀릴
경우 설정
// sqlwarn[4] -> 사용되지 않음
// sqlwarn[5] -> EXEC SQL CREATE {PROCEDURE |FUNCTION | PACKAGE | PACKAGE BODY}문
이 pl/sql 컴파일 에러 때문에 실패했을때 설정.
// sqlwarn[6] -> 사용되지 않음
// sqlwarn[7] -> 사용되지 않음
};
#ifndef SQLCA_NONE
#ifdef SQLCA_STORAGE_CLASS
SQLCA_STORAGE_CLASS struct sqlca sqlca
#else
struct sqlca sqlca
#endif
#ifdef SQLCA_INIT
= {
{'S', 'Q', 'L', 'C', 'A', ' ', ' ', ' '},
sizeof(struct sqlca),
0,
{ 0, {0}},
{'N', 'O', 'T', ' ', 'S', 'E', 'T', ' '},
{0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0}
}
#endif
;
#endif
#endif
6-2) PL/SQL 고려사항
: 내부에 포함된 PL/SQL블록을 실행할때 SQLCA의 요소는 설정되지 않는다.
만약 블록이 여러행을 FETCH한다면, sqlerrd[2]는 단지 1로 설정되어 있을 것이다.
PL/SQL블록 실행후 SQLCA의 요소는 sqlcode와 sqlerrm에 의존해야만 한다.
7) Getting the Full Text of Error Messages
: 70문자 이상의 에러메세지를 취득해야 할 경우 아래의 함수를 사용한다.
size_t *buffer_size,
size_t *message_length);
==============================================================================
문법 설명
------------------------------------------------------------------------------
message_buffer 에러메세지를 저장하기위한 버퍼
buffer_size 버퍼의 최대값을 지정하는 변수(바이트단위)
message_length 에러메세지의 실제길이를 저장
==============================================================================
아래의 예제는 200바이트 문자까지 에러메세지를 얻기위해서 sqlglm()함수를 사용하는
예제를 보여주고 있다.
EXEC SQL WHENEVER SQLERROR DO sql_error();
...
/* other statements */
...
sql_error()
{
char msg[200];
size_t buf_len, msg_len;
buf_len = sizeof (msg);
sqlglm(msg, &buf_len, &msg_len); /* note use of pointers */
printf("%.*snn", msg_len, msg);
exit(1);
}
위의 함수를 사용하기전에 SQLCODE가 0이 아닌경우를 먼저체크 하고 사용해야한다.
만약에 SQLCODE가 0인경우 sqlglm()함수를 호출하면 이전 SQL문과 관련된 메세지를 얻게된다.
8) Using the WHENEVER Directive
: 상황체크나 에러처리를 자동으로 하기위해서는 WHENEVER 명령(지시자)가 필요한다.
아래와 같은 문법으로 사용한다.
EXEC SQL WHENEVER <condition> <action>;
<condition>
SQLWARNING : sqlwarn[0]은 오라클이 경고를 리턴했을때 혹은 SQLCODE가 1043이 아닌 다른
양의 정수값을 가질때 설정된다. 예를 들어 오라클은 호스트 변수에 잘려진
컬럼값을 할당할때 설정한다.
SQLERROR : SQLCODE는 오라클이 에러를 리턴하기때문에 음수값을 가진다.
NOT FOUND : SQLCODE는 1043값을 가진다. MODE=ANSI인경우 100을 리턴한다.
<action>
CONTINUE : 다음 문장을 계속해서 실행할때 사용한다.
DO : 에러처리 함수로 제어를 넘긴다.
DO BREAK : LOOP와 같은 행위를 할때, LOOP를 빠져나가는 행위를 한다.
GOTO label_name : label_name명으로 넘어간다. label_name명은 31문자를 기본으로 한다.
STOP : 작동을 멈추고, commit되지 않은 작업은 Roll Back된다.
8-1) WHENEVER 예제
: 아래에는 WHENEVER 예제를 보여주고 있다.
EXEC SQL WHENEVER SQLWARNING CONTINUE;
EXEC SQL WHENEVER SQLERROR GOTO error_handler;
==========================================================================
'no data found'상태가 일어나면, close_cursor로 간다.
warning이 발생하면, 다음 문장을 계속한다.
error가 발생하면, error_handler로 간다.
--------------------------------------------------------------------------
...
EXEC SQL WHENEVER SQLERROR DO handle_insert_error("INSERT error");
EXEC SQL INSERT INTO emp (empno, ename, deptno)
VALUES (:emp_number, :emp_name, :dept_number);
EXEC SQL WHENEVER SQLERROR DO handle_delete_error("DELETE error");
EXEC SQL DELETE FROM dept WHERE deptno = :dept_number;
...
handle_insert_error(char *stmt)
{
switch(sqlca.sqlcode)
{
case -1:
/* duplicate key value */
...
break;
case -1401:
/* value too large */
...
break;
default:
/* do something here too */
...
break;
}
}
handle_delete_error(char *stmt)
{
printf("%snn", stmt);
if (sqlca.sqlerrd[2] == 0)
{
/* no rows deleted */
...
}
else
{
...
}
...
}
8-2) DO BREAK와 DO CONTINUE 의 사용
: 아래의 예제는 사원 이름, 봉급, 임무를 어떻게 표시하는지 보여주고 있다.
#include <stdio.h>
main()
{
char *uid = "scott/tiger";
struct { char ename[12]; float sal; float comm; } emp;
/* 연결시 에러가 발생하면 whoops로 이동 */
EXEC SQL WHENEVER SQLERROR GOTO whoops;
EXEC SQL CONNECT :uid;
EXEC SQL DECLARE c CURSOR FOR
SELECT ename, sal, comm FROM EMP ORDER BY ENAME ASC;
EXEC SQL OPEN c;
/* 데이터가 없으면, 'BREAK'처리 */
EXEC SQL WHENEVER NOT FOUND DO BREAK;
/* 에러가 발생하면, 다음 처리를 위해 loop의 처음으로 이동한다. */
EXEC SQL WHENEVER SQLERROR DO CONTINUE;
while (1)
{
EXEC SQL FETCH c INTO :emp;
/* ORA-1405는 'continue'되고. 값이 있는 commission인경우만 표시 */
printf("%s %7.2f %9.2fn", emp.ename, emp.sal, emp.comm);
}
/* This 'CONTINUE' shuts off the 'DO CONTINUE' allowing the program to
proceed if any further errors do occur, specifically, with the CLOSE */
EXEC SQL WHENEVER SQLERROR CONTINUE;
EXEC SQL CLOSE c;
exit(EXIT_SUCCESS);
whoops:
printf("%.*sn", sqlca.sqlerrm.sqlerrml, sqlca.sqlerrm.sqlerrmc);
exit(EXIT_FAILURE);
}
8-3) WHENEVER의 범위
: WHENEVER의 범위는 위치에 따라서 틀려진다. 아래의 예제를 보자
EXEC SQL WHENEVER SQLERROR STOP;
EXEC SQL CONNECT :username IDENTIFIED BY :password;
...
goto step3;
step2:
EXEC SQL WHENEVER SQLERROR CONTINUE;
EXEC SQL UPDATE emp SET sal = sal * 1.10;
...
step3:
EXEC SQL DROP INDEX emp_index;
위의 예제는 WHENEVER SQLERROR 지시자는 두번째에 의해서 대체된다. 그래서 단지 CONNECT
문장에서만 적용된다. 두번째 WHENEVER SQLERROR 지시자는 STEP1에서 STEP3로 제어가 넘어
가더라도 UPDATE와 DROP문 두개다 적용된다.
8-4) WHENEVER 가이드라인
: 아래의 가이드 라인은 일반적인 함정을 피할 수 있도록 도울 것이다.
문장위치
: 처음 SQL문 전에 WHENEVER지시자를 적으면, 파일의 끝까지 영향을 미친다.
End-of-Data Condition처리
: 아래의 예제 처럼 처리한다.
EXEC SQL WHENEVER NOT FOUND DO break;
for (;;)
{
EXEC SQL FETCH...
}
EXEC SQL CLOSE my_cursor;
...
위의 경우는 Fetch해서 데이터가 없는경우 프로그램을 멈추는 처리이다.
EXEC SQL WHENEVER NOT FOUND DO break;
for(;;)
{
EXEC SQL FETCH ...
EXEC SQL WHENEVER NOT FOUND CONTINUE;
EXEC SQL INSERT INTO ...
}
EXEC SQL CLOSE my_cursor;
...
위의 경우는 insert될 데이터가 없다고 하더라고 계속해서 작업을 진행하는 처리이다.
무한반복 피하기
: 무한 루프를 피하기 위한 방법을 아래의 예제에서 보여주고 있다.
EXEC SQL WHENEVER SQLERROR GOTO sql_error;
...
sql_error:
EXEC SQL WHENEVER SQLERROR CONTINUE;
EXEC SQL ROLLBACK WORK RELEASE;
...
에러처리전에 EXEC SQL WHENEVER SQLERROR CONTINUE;문을 사용하여 무한 루프를 방지한다.
/* 적당하지 않은 WHENEVER의 사용 */
...
EXEC SQL WHENEVER NOT FOUND GOTO no_more;
for (;;)
{
EXEC SQL FETCH emp_cursor INTO :emp_name, :salary;
...
}
no_more:
// Not Found인 경우 무한루프를 돌기때문에 아래의 처리를 반드시 해야한다.
EXEC SQL WHENEVER NOT FOUND GOTO no_match;
EXEC SQL DELETE FROM emp WHERE empno = :emp_number;
...
no_match:
...
주소 유지
: 아래 예제의 경우 에러가 발생한다. 원인은 func2함수내의 insert문의
범위내에 func1에 있는 labelA가 없기때문이다.
{
EXEC SQL WHENEVER SQLERROR GOTO labelA;
EXEC SQL DELETE FROM emp WHERE deptno = :dept_number;
...
labelA:
...
}
func2()
{
EXEC SQL INSERT INTO emp (job) VALUES (:job_title);
...
}
WHENEVER GOTO문으로 분기되는 label은 전처리 파일에서 선언되어야만 한다.
에러후의 리턴
: 에러처리후에 리턴되어야 한다면, DO routine_call을 사용한다.
선택적으로, 아래의 예처럼 sqlcode값을 비교할 수 있다.
...
EXEC SQL UPDATE emp SET sal = sal * 1.10;
if (sqlca.sqlcode < 0)
{
/* handle error */
EXEC SQL DROP INDEX emp_index;
9) Obtaining the Text of SQL Statements
: SQLStmtGetText() 함수 (예전이름 : sqlgls() 함수)는 아래의 정보를 리턴한다.
# 최근에 파싱된 SQL문의 TEXT
# 문장의 유효한 길이
# SQL명령에 대한 함수 코드
SQLStmtGetText()의 prototype은 아래와 같다.
void SQLStmtGetText(dvoid *context, char *sqlstm, size_t *stmlen, size_t *sqlfc);
<context>
런타임 context이다. "CONTEXT변수" 참조.
<sqlstm>
sql문장을 리턴하는 문자버퍼.
<stmlen>
sqlstm버퍼의 실제 크기(바이트) 설정.
<sqlfc>
SQL명령의 함수 코드.
함수코드에 대한 내용은 [Table 9-3]을 참조한다.
에러가 발생하면 stmlen은 0을 리턴한다. 에러가 발생할 수 있는 상태는 아래와 같다.
# SQL문이 파싱되지 않은경우
# 유효하지 않은 파라미터를 넘겼을경우
# SQLLIB에서 내부적인 예외가 발생했을 경우
9-1) 제한사항
: 아래의 명령이 포함된 문장은 SQLStmtGetText()함수에 리턴되지 않는다.
# CONNECT
# COMMIT
# ROLLBACK
# FETCH
위의 명령에 대해서는 SQL 함수코드가 존재하지 않는다.
10) Using the Oracle Communications Area (ORACA)
: SQLCA에서 제공하는것보다 많은 런타임 에러나 상태변경 정보를 원한다면 ORACA를 사용한다.
ORACA는 오라클 리소스(RESOURCE)의 사용을 모니터링 한다.
10-1) ORACA 선언
: ORACA를 선언하기 위해서는 아래와 같이 선언한다.
EXEC SQL INCLUDE ORACA;
or
#include <oraca.h>
프로그램에서 Declare Section을 사용한다면 , ORACA는 그것 밖에 선언 되어야만 한다.
10-2) ORACA 내용
: ORACA는 옵션설정,시스템통계를 포함하며
# SQL문장
# 에러가 발생한 파일이름
# 파일에서 에러의 위치
# 커서 캐쉬와 통계
의 확장 분석을 포함한다.
아래는 oraca.h의 부분 내용이다.
======================================================================================
/*
NAME
ORACA : Oracle Communications Area.
If the symbol ORACA_NONE is defined, then there will be no ORACA
*variable*, although there will still be a struct defined. This
macro should not normally be defined in application code.
If the symbol ORACA_INIT is defined, then the ORACA will be
statically initialized. Although this is not necessary in order
to use the ORACA, it is a good pgming practice not to have
unitialized variables. However, some C compilers/operating systems
don't allow automatic variables to be init'd in this manner. Therefore,
if you are INCLUDE'ing the ORACA in a place where it would be
an automatic AND your C compiler/operating system doesn't allow this style
of initialization, then ORACA_INIT should be left undefined --
all others can define ORACA_INIT if they wish.
*/
#ifndef ORACA
#define ORACA 1
struct oraca
{
char oracaid[8]; /* 예약변수 */
long oracabc; /* 예약변수 */
/* 사용자에의해서 설정될 수 있는 Flags */
long oracchf; /* <> 0 if "check cur cache consistncy"*/
long oradbgf; /* <> 0 if "do DEBUG mode checking" */
long orahchf; /* <> 0 if "do Heap consistency check" */
long orastxtf; /* SQL stmt text flag */
#define ORASTFNON 0 /* = don't save text of SQL stmt */
#define ORASTFERR 1 /* = only save on SQLERROR */
#define ORASTFWRN 2 /* = only save on SQLWARNING/SQLERROR */
#define ORASTFANY 3 /* = always save */
struct
{
unsigned short orastxtl;
char orastxtc[70];
} orastxt; /* text of last SQL stmt */
struct
{
unsigned short orasfnml;
char orasfnmc[70];
} orasfnm; /* name of file containing SQL stmt */
long oraslnr; /* line nr-within-file of SQL stmt */
long orahoc; /* highest max open OraCurs requested */
long oramoc; /* max open OraCursors required */
long oracoc; /* current OraCursors open */
long oranor; /* nr of OraCursor re-assignments */
long oranpr; /* nr of parses */
long oranex; /* nr of executes */
};
#ifndef ORACA_NONE
#ifdef ORACA_STORAGE_CLASS
ORACA_STORAGE_CLASS struct oraca oraca
#else
struct oraca oraca
#endif
#ifdef ORACA_INIT
=
{
{'O','R','A','C','A',' ',' ',' '},
sizeof(struct oraca),
0,0,0,0,
{0,{0}},
{0,{0}},
0,
0,0,0,0,0,0
}
#endif
;
#endif
#endif
/* end oraca.h */
출처 - http://younbok.egloos.com/9342715
'Language > Pro*C' 카테고리의 다른 글
[번역] 호스트배열 (Host Arrays) - 03 (0) | 2015.05.20 |
---|---|
[번역] 호스트배열 (Host Arrays) - 02 (0) | 2015.05.20 |
[번역] 호스트배열 (Host Arrays) - 01 (0) | 2015.05.20 |
[번역] Embedded PL/SQL - 04 (0) | 2015.05.20 |
[번역] Embedded PL/SQL - 03 (0) | 2015.05.20 |