7. 실행 시간 에러 처리#
개요#
응용 프로그램 안에서 실행 시간 에러 (run-time error)에 대한 처리가 가능해야 한다. 내장 SQL문은 SQLCODE, SQLSTATE 등의 변수와 WHENEVER문 등의 지원으로 실행 시간 에러 처리에 대한 방법을 프로그래머에게 제공한다.
실행결과#
내장 SQL문 수행 후 실행결과는 sqlca.sqlcode에 저장되며, 그 값은 다음과 같다.
-
SQL_SUCCESS
내장 SQL문을 성공적으로 수행한 경우 -
SQL_SUCCESS_WITH_INFO
내장 SQL문을 수행하였으나 warning이 발견된 경우 -
SQL_NO_DATA
SELECT문이나 FETCH문 수행 후 반환되는 레코드가 없는 경우 -
SQL_ERROR
내장 SQL문 수행시 에러가 발생한 경우
sqlca#
sqlca는 전처리 시 선언된, ulpSqlca 구조체의 인스턴스이다. ulpSqlca 는 내장 SQL문 수행 결과를 저장하기 위한 구조체로 ulpLibInterface.h 파일에 정의되어 있다. 프로그램 개발자는 응용 프로그램 안에서 sqlca 변수를 이용해 내장 SQL문의 수행 결과를 참조할 수 있다.
자료 구조#
typedef struct ulpSqlca
{
char sqlcaid[8]; /* not used */
int sqlcabc; /* not used */
int sqlcode;
struct
{
short sqlerrml;
char sqlerrmc[2048];
}sqlerrm;
char sqlerrp[8]; /* not used */
int sqlerrd[6];
char sqlwarn[8]; /* not used */
char sqlext[8]; /* not used */
} ulpSqlca;
구성 요소#
ulpSqlca 구조체는 여러 가지 구성 요소를 포함한다. 이 구성 요소들 중에는 현재 사용하지 않고 미래를 위해 예약된 구성 요소도 있는데, 그 구성 요소에 대한 설명은 생략한다.
각 구성 요소의 의미는 다음과 같다.
sqlcode#
내장 SQL문의 실행결과가 저장된다. 내장 SQL문의 실행결과는 다음값들 중 하나일 것이다.
-
SQL_SUCCESS
-
SQL_SUCCESS_WITH_INFO
-
SQL_NO_DATA
-
SQL_ERROR
sqlerrm.sqlerrmc#
에러메시지가 저장된다. 저장할 수 있는 에러메시지의 최대길이는 2048 bytes 이다.
sqlerrm.sqlerrml#
반환된 에러메시지 길이가 저장된다.
sqlerrd[2]#
INSERT, UPDATE, DELETE 연산 시 영향 받은 레코드 개수가 저장된다.
SELECT문이나 FETCH문 수행 시 출력 호스트 변수가 배열일 경우, 반환되는 레코드 개수가 저장된다. 이 경우 반환되는 레코드 개수는 누적되지 않고 현재 FETCH된 레코드 개수이다. 따라서 이 값은 호스트 변수의 배열 크기보다 클 수 없다.
sqlerrd[3]#
배열 타입의 입력 호스트 변수 사용시 내장 SQL문 수행 후 이 값을 참조할 수 있다. 이 변수에는 성공적으로 처리된 횟수가 저장된다. 따라서 이 값은 배열 크기보다 클 수 없다. 예를 들어 배열 크기 3의 입력 호스트 변수를 사용하여 UPDATE를 수행하였다고 할 때, 배열요소의 0번째는 성공, 1번째는 실패, 2번째는 성공한 경우 이 변수에는 2가 저장된다. 반면에, sqlca.sqlerrd[2]에는 UPDATE된 레코드 개수가 저장되기 때문에 2보다 큰 값이 저장될 수도 있다.
주의 사항#
모든 내장 SQL문 수행 후 반드시 sqlca.sqlcode 값을 체크하여 정확한 에러처리를 해주어야 한다.
SELECT문에서 문자형 타입의 칼럼의 값을 fetch할 경우, 출력 호스트 변수의 크기가 대응되는 칼럼 크기 + 1 보다 작을 경우, 호스트 변수에는 호스트 변수 크기만큼 데이터가 잘려서 저장될 수도 있는데, 이 때의 sqlca.sqlcode값은 SQL_SUCCESS_WITH_INFO이다.
UPDATE, DELETE 연산 시 영향받은 레코드 개수가 0개이면 이 때의 sqlca.sqlcode값은 SQL_NO_DATA이다. UPDATE, DELETE 연산 시 영향 받은 레코드 개수 확인 방법은 sqlca.sqlerrd[2]값을 참조하면 되고, 이 경우 이 값은 0이다.
SQLCODE#
SQLCODE에는 내장 SQL문 수행 결과가 SQL_ERROR일 경우 에러코드가 저장된다.
자료 구조#
int SQLCODE;
설명#
- 0
내장 SQL문을 성공적으로 수행한 경우. 즉, sqlca.sqlcode 값이 SQL_SUCCESS 인 경우 - 1
내장 SQL문을 수행하였으나 warning이 발견된 경우. 즉, sqlca.sqlcode 값이 SQL_SUCCESS_WITH_INFO 인 경우 - 100
SELECT문이나 FETCH문 수행 후 반환되는 레코드가 없는 경우. 즉, sqlca.sqlcode 값이 SQL_NO_DATA인 경우 - -1
내장 SQL문 수행 시 에러가 발생하였지만 해당 에러코드가 없는 경우. 이 때의 sqlca.sqlcode 값은 SQL_ERROR 이다. - -그 외 음수값
내장 SQL문 수행시 에러가 발생한 경우. 이 경우, 이 값은 실제 에러 코드이다.
에러 코드#
에러 코드는 에러가 발생한 위치에 따라 데이터베이스 서버에서 발생한 에러와 내장 SQL문에서 발생한 에러로 구분된다.
내장 SQL문 에러#
실행 시간에 내장SQL문에서 발생한 에러로 C/C++ 전처리기(APRE)가 에러코드를 반환한다. 아래 설명 외의 에러코드는 Error Message Reference를 참조하기 바란다.
-
-589826 : 같은 연결 이름으로 이미 연결되어 있는 경우
-
-589841 : 연결 이름의 길이가 50 bytes를 초과하는 경우
-
-589857 : 선언되지 않은 커서 이름으로 다른 커서 처리 SQL문을 수행하는 경우
-
-589858 : PREPARE되지 않은 SQL문 식별자로 다른 동적 SQL문을 수행하는 경우
데이터베이스 서버 에러#
실행 시간에 데이터베이스 서버에서 발생한 에러로 데이터베이스 서버에서 에러코드를 반환한다. 각 에러코드에 대한 자세한 설명은 Error Message Reference를 참조하기 바란다.
주의 사항#
SQLCODE에는 에러코드가 10진수 음수값으로 저장되어 있는 반면 Error Message Reference 에는 에러코드가 16진수 양수값과 10진수 양수값으로 설명되어 있다. 따라서 Error Message Reference 참조 시 SQLCODE의 절대값 또는 절대값을 16진수로 변환하여 참조하여야 한다.
SQLSTATE#
SQLSTATE에는 상태코드가 저장된다. 이 상태코드를 통해 어떤 에러가 발생했는지, 또는 어떤 warning이 발견되었는지 알 수 있다. SQLSTATE는 내장 SQL문의 결과가 SQL_ERROR 또는 SQL_SUCCESS_WITH_INFO 인 경우 참조할 수 있다.
자료구조#
char SQLSTATE[6];
상태코드#
-
00000 – 내장 SQL문을 성공적으로 수행한 경우
-
01004 – 호스트 변수가 타입일 때, 호스트 변수의 크기를 대응되는 칼럼의 크기 + 1 보다 작게 지정한 경우. 이 때 반환되는 데이터는 호스트 변수의 크기만큼 잘려서 저장된다.
-
07006 – 호스트 변수 타입이 대응되는 칼럼 타입과 타입 호환되지 않는 타입을 사용한 경우
-
07009 – 칼럼 개수와 대응되는 호스트 변수 개수가 일치하지 않은 경우
-
08001 – 데이터베이스 서버가 startup 되어 있지 않은 경우
-
08S01 – 데이터베이스 서버와의 연결이 끊어진 경우
-
21S01 – INSERT구문의 컬럼 개수와 입력 호스트 변수의 개수가 일치하지 않은 경우
-
22002 – 지시자 변수를 지정하지 않은 상태에서 NULL 데이터가 반환된 경우
-
HY000 – 일반적인 에러
-
HY001 – 메모리 할당 시 에러가 발생한 경우
-
HY009 – 호스트 변수와 지시자 변수가 널 포인터인 경우
-
HY010 – OPEN 되지 않은 커서를 FETCH 하는 경우
-
HY090 – 지시자 변수의 값이 유효하지 않은 경우
GET DIAGNOSTICS#
- GET DIAGNOSTICS 문은 GET DIAGNOSTICS 문을 제외한 바로 전에 수행된 SQL문에 대한 진단 정보를 제공한다.
- 이전에 수행된 SQL문이 여러 개의 에러를 발생한 경우에 sqlca는 첫 번째 에러에 대한 진단 정보만 제공하는 반면, GET DIAGNOSTICS 문은 모든 에러에 대한 진단 정보를 제공한다.
구문#
EXEC SQL GET [ CURRENT ] DIAGNOSTICS
{
statement_information
| condition_information
}
statement_information:
statement_information_item [, statement_information_item] ...
statement_information_item:
<:host_var1> = statement_information_item_name
statement_information_item_name: {
NUMBER
| ROW_COUNT
}
condittion_information:
CONDITION condition_number condition_information_item [, condition_information_item] ...
condition_number
<:host_var2 | integer>
condition_information_item:
<:host_var3> = condition_information_item_name
condition_information_item_name: {
RETURNED_SQLCODE
| RETURNED_SQLSTATE
| MESSAGE_TEXT
| ROW_NUMBER
| COLUMN_NUMBER
}
인자#
-
<:host_var1>
- 지정된 statement_information_item의 값을 받아오는 호스트 변수이다.
- 호스트 변수의 데이터 타입은 아래 설명의 "진단 항목별 호스트 변수의 데이터 타입" 표에 지정된 타입이어야 한다.
- 만약 진단 정보가 없다면 GET DIAGNOTICS 수행 후 호스트 변수는 디폴트로 셋팅된다. 숫자형일 경우 0, 문자형일 경우 empty string이다.
-
statement_information_item_name
- NUMBER : 앞서 수행된 SQL문이 반환한 에러의 갯수
- ROW_COUNT : 앞서 수행된 SQL문이 INSERT, UPDATE, DELETE인 경우 영향의 받은 로우의 갯수
-
condition_number
- 이 값은 진단 정보를 식별하기 위해 사용된다. 이 값이 1이면 첫 번째 진단 정보, 2이면 두 번째 진단 정보를 가리킨다. literal 또는 32 bit 정수 타입의 호스트 변수를 사용해야 하며 그렇지 않은 경우 에러가 발생한다.
- 0 이하의 값 또는 진단 정보 갯수보다 큰 값을 지정하면 에러가 발생한다.
- 호스트 변수를 쓸 때 indicator 변수는 허용되지 않는다.
-
<:host_var3>
- condition_number로 지정한 진단 레코드에서 지정된 condition_information_item의 값을 받아오는 호스트 변수이다.
- 호스트 변수의 데이터 타입은 아래 설명의 "진단 항목별 호스트 변수의 데이터 타입" 표에 지정된 타입이어야 한다. 그렇지 않을 경우 application 런타임 에러가 발생할 수 있다.
- indicator 변수가 제공되면 값의 길이가 indicator 변수에 반환된다.
- 만약 진단 정보가 없다면 GET DIAGNOTICS 수행 후 호스트 변수는 디폴트로 셋팅된다. 숫자형일 경우 0, 문자형일 경우 empty string이다.
-
condition_information_item_name
- RETURNED_SQLCODE: 진단 정보와 연관된 SQLCODE
- RETURNED_SQLSTATE: 진단 정보와 연관된 SQLSTATE
- MESSAGE_TEXT: 진단 정보와 연관된 에러 메시지
- ROW_NUMBER: 진단 정보와 연관된 로우의 번호. array binding을 사용해서 SQL문을 수행한 경우 사용될 수 있다.
- COLUMN_NUMBER: 진단 정보와 연관된 컬럼 또는 파라미터 번호.
설명#
- 진단 정보는 두 가지 영역 - 명령문 정보(statement information), 조건 정보(condition information) 로 제공된다.
- SQL 문 실행 후 명령문 실행에 대한 정보는 명령문 정보로 제공되며, SQL문 실행이 성공하지 않은 경우 적어도 하나의 조건 정보가 제공된다. 명령문 정보에서 NUMBER 항목으로 가져온 값이 조건 정보의 갯수이다.
- sqlca, SQLCODE, SQLSTATE의 사용에는 아무런 영향을 미치지 않는다.
진단 항목별 호스트 변수의 데이터 타입#
진단 정보를 받아오는 호스트 변수의 데이터 타입은 요청된 진단 항목의 데이터 타입과 호환되어야 한다.
- Statement Information
- NUMBER: INTEGER
- ROW_COUNT: INTEGER
- Condition Information
- RETURNED_SQLCODE: INTEGER
- RETURNED_SQLSTATE: CHAR(5)
- MESSAGE_TEXT: VARCHAR(2048)
- ROW_NUMBER: INTEGER
- COLUMN_NUMBER: INTEGER
예제#
여러 개의 에러가 발생한 경우 GET DIAGNOSTICS 문을 사용하는 예제이다.
int numErrors;
int retSqlcode;
char retSqlstate[6];
char message[2048];
EXEC SQL INSERT ...
EXEC SQL GET DIAGNOSTICS :numErrors = NUMBER;
for ( i=1;i <= numErrors;i++)
{
EXEC SQL GET DIAGNOSTICS CONDITION :i
:retSqlcode = RETURNED_SQLCODE,
:retSqlstate = RETURNED_SQLSTATE,
:message = MESSAGE_TEXT;
}
WHENEVER문#
APRE는 실행시간 에러 처리를 위한 방법으로 WHENEVER문을 지원한다.
구문#
EXEC SQL WHENEVER <condition> <action>;
인자#
-
<condition> : 내장 SQL문 실행결과
-
<action> : 내장 SQL문 실행결과에 따른 처리 방법
조건#
WHENEVER문에서 지정할 수 있는 조건은 다음과 같다.
SQLERROR#
내장 SQL문 수행 시 에러가 발생한 경우. 즉, sqlca.sqlcode 값이 SQL_ERROR일 때를 의미한다.
NOT FOUND#
SELECT문이나 FETCH문 수행 후 반환되는 레코드가 없는 경우. 즉, sqlca.sqlcode 값이 SQL_NO_DATA일 때를 의미한다.
처리 방법#
내장 SQL문 수행 결과가 WHENEVER문에서 지정한 조건과 일치하면 지정한 처리 방법이 실행된다.
WHENEVER문에서 지정할 수 있는 처리 방법은 다음과 같다.
CONTINUE#
계속 진행한다.
DO BREAK#
현재의 반복문을 빠져나가서 계속 진행한다. 이것은 반복문 안에서 'break;' 명령을 사용한 것과 동일한 효과를 가진다. 'DO BREAK'는 반복문 안에서만 지정 가능하며, 반복문이 끝나면 이 WHENEVER문은 효력을 잃는다.
DO CONTINUE#
현재의 반복문 처음으로 이동하여 계속 진행한다. 이것은 반복문 안에서 'continue;' 명령을 사용한 것과 동일한 효과를 가진다. 'DO CONTINUE'는 반복문 안에서만 지정 가능하며, 반복문이 끝나면 이 WHENEVER문은 효력을 잃게 된다.
DO function_name#
function_name으로 지정한 함수를 호출한다.
GOTO label_name#
label_name의 위치로 이동하여 계속 진행한다.
STOP#
데이터베이스 서버와의 연결을 해제하고 현재 프로그램을 종료한다.
설명#
WHENEVER문의 적용범위는 프로그램의 흐름과는 다르며, 현재 파일 내에서만 유효하다.
WHENEVER문은 적용하고자 하는 내장 SQL문 이전에 선언하여야 한다.
WHENEVER문이 선언되면 현재범위(current scope)와 현재의 하위범위(lower scope)에서 수행되는 모든 내장 SQL문의 수행 결과가 영향을 받는다. 즉, 내장 SQL문 수행 결과가 WHENEVER문에서 지정한 조건과 일치하면 지정한 action을 수행하게 된다.
WHENEVER문은 2가지 조건 즉, 'SQLERROR'와 'NOT FOUND'에 대해 독립적이다.
WHENEVER문을 선언한 범위를 빠져나오면 그 WHENEVER문은 효력을 상실하며, 그 범위 바깥의 내장 SQL문은 빠져 나온 현재범위(current scope) 또는 현재의 상위범위(upper scope)에 선언된 WHENEVER문의 영향을 받는다.
WHENEVER문이 선언된 범위(scope)에서 같은 조건으로 다른 WHENEVER문이 선언된다면 이전 WHENEVER문은 효력을 상실하고 새로 선언된 WHENEVER문이 적용된다.
같은 조건의 WHENEVER문이 중첩되어 선언된 경우, 가장 가까운 위치의 WHENEVER문의 영향을 받는다.
WHENEVER문은 connection에 독립적이다. 즉, connection이 하나 이상인 파일에서 WHENEVER문을 선언하면 connection이 다르더라도 해당 범위의 모든 내장 SQL문이 영향을 받는다.
전역에 WHENEVER문을 선언하였다면 현재 파일의 모든 내장 SQL문의 수행 결과가 영향을 받는다.
예제 프로그램#
runtime_error_check.sc#
$ALTIBASE_HOME/sample/APRE/runtime_error_check.sc
참고
실행결과#
$ is -f schema/schema.sql
$ make runtime_error_check
$ ./runtime_error_check
<RUNTIME ERROR CHECK>
------------------------------------------------------
[SQL_SUCCESS]
------------------------------------------------------
sqlca.sqlcode = 0
-----------------------------------------------------------
[SQL_SUCCESS_WITH_INFO With SQLSTATE=01004]
-----------------------------------------------------------
sqlca.sqlcode = 1
sqlca.sqlerrm.sqlerrmc = String data right truncated.
SQLSTATE = 01004
SQLCODE = 1
-----------------------------------------------------------
[SQL_ERROR With SQLSTATE=22002]
-----------------------------------------------------------
sqlca.sqlcode = -1
sqlca.sqlerrm.sqlerrmc = Indicator variable required but not supplied.
SQLSTATE = 22002
SQLCODE = -331841
-----------------------------------------------------------
[SQL_NO_DATA With SELECT]
-----------------------------------------------------------
sqlca.sqlcode = 100
sqlca.sqlerrm.sqlerrmc = Not found data
SQLSTATE = 02000
SQLCODE = 100
-----------------------------------------------------------
[SQL_NO_DATA With FETCH]
-----------------------------------------------------------
sqlca.sqlcode = 100
sqlca.sqlerrm.sqlerrmc = Not found data
SQLSTATE = 02000
SQLCODE = 100
2 rows fetched
-----------------------------------------------------------
[SQL_ERROR]
-----------------------------------------------------------
sqlca.sqlcode = -1
sqlca.sqlerrm.sqlerrmc = The row already exists in a unique index.
SQLSTATE = 23000
SQLCODE = -69720
-----------------------------------------------------------
[SQL_ERROR With SQLSTATE=HY010]
-----------------------------------------------------------
sqlca.sqlcode = -1
sqlca.sqlerrm.sqlerrmc = Function sequence error.
SQLSTATE = HY010
SQLCODE = -331796
-----------------------------------------------------------
[sqlca.sqlerrd[2]]
-----------------------------------------------------------
sqlca.sqlcode = 0
sqlca.sqlerrd[2] = 12
-----------------------------------------------------------
sqlca.sqlerrd[3] With Array In-Binding]
-----------------------------------------------------------
sqlca.sqlcode = 0
sqlca.sqlerrd[2] = 12
sqlca.sqlerrd[3] = 3
whenever1.sc#
$ALTIBASE_HOME/sample/APRE/whenever1.sc
참고
whenever2.sc#
$ALTIBASE_HOME/sample/APRE/whenever2.sc
참고
실행결과#
$ is -f schema/schema.sql
$ make whenever
$ ./whenever
<WHENEVER>
Success connection
------------------------------------------------------------------
DNO DNAME DEP_LOCATION MGR_NO
------------------------------------------------------------------
1001 PAPER TEAM New York 16
1002 RESEARCH DEVELOPMENT DEPT 2 Sydney 13
1003 SOLUTION DEVELOPMENT DEPT Japan 14
2001 QUALITY ASSURANCE DEPT Seoul 17
3001 CUSTOMER SUPPORT DEPT London 4
3002 PRESALES DEPT Peking 5
4001 MARKETING DEPT Seoul 8
4002 BUSINESS DEPT LA 7