런타임 에러 처리

tbESQL/C 프로그램을 실행할 때 발생할 수 있는 예외 상황을 처리하기 위한 방법인 런타임 에러 처리에 대해 설명합니다.

개요

tbESQL/C 프로그램에서 SQL 문장을 실행하는 중에 에러가 발생했을 때, 그에 대한 적절한 조치를 취하기 위해 다음의 3가지 인터페이스를 제공합니다.

  • 상태 변수

  • SQLCA

  • WHENEVER

tbESQL/C 프로그램 내에서 하나의 SQL 문장을 실행하면 항상 상태 변수 또는 SQLCA에 실행 결과 또는 에러 정보를 저장합니다. 애플리케이션 프로그램 개발자는 SQL 문장을 실행한 직후에 상태 변수 또는 SQLCA를 검토하여 에러가 발생했을 때 해결을 위해 코드를 삽입해 주어야 합니다.

상태 변수와 SQLCA의 변수는 SQL 문장이 실행될 때마다 자동으로 변경되고, 프로그램 내에서 그 내용을 검토해야 합니다. 이러한 작업을 자동적으로 수행하기 위하여 WHENEVER 문장을 사용합니다. WHENEVER 문장은 에러 또는 경고가 발생했을 때 그에 따른 처리를 위한 특정한 작업을 수행하도록 선언합니다.

일반적인 tbESQL/C 프로그램에서는 WHENEVER 문장을 이용하여 에러 또는 경고가 발생했을 때 특정한 에러 처리 함수를 호출하도록 선언합니다. 에러 처리 함수는 상태 변수 또는 SQLCA의 변수의 내용을 검토하여 적절히 조치합니다. 에러 상황에 따라 프로그램을 계속 진행하거나 정지시킬 수 있습니다.

상태 변수

상태 변수는 에러 또는 경고 코드를 가지며, 에러 또는 경고에 대한 상세한 정보가 저장됩니다.

특징

상태 변수에는 SQLSTATE와 SQLCODE가 있는데, 주요 특징은 다음과 같습니다.

  • 두 상태 변수는 서로 다른 값을 갖습니다. SQL 문장을 실행할 때마다 두 상태 변수의 값은 tbESQL/C 프로그램에 의해 자동적으로 갱신되는데, 같은 예외 상황이라고 해도 두 상태 변수는 서로 다른 값을 갖습니다. SQLSTATE에는 에러 및 경고 코드 가 저장되지만 SQLCODE에는 에러 코드만이 저장됩니다.

  • SQLSTATE의 길이는 5bytes입니다. SQLSTATE는 6bytes로 선언하지만 맨 마지막에 포함되는 NULL 문자를 제외하면, 실제 SQLSTATE의 길이는 5bytes입니다.

  • SQLSTATE는 다음의 두 가지 코드로 구성됩니다.

코드
설명

클래스 코드

에러 또는 경고에 대한 큰 분류이며, 크기는 2bytes

서브 클래스 코드

클래스 코드의 큰 분류 내부의 구체적인 내용을 포함하고 있으며, 크기는

3bytes

클래스 코드와 서브 클래스 코드를 예를 들어 설명하면, SQLSTATE의 값이 '22012'라면, 클래스 코드 '22'는 데이터 예외 상황(Data exception)을 의미하며 서브 클래스 코드 '012'는 0으로 나눔(Divide by zero)을 의미합니다.

  • SQLSTATE에는 '0' ~ '9', 'A' ~ 'Z'로 이루어진 문자열이 저장됩니다. SQL-92에서 정의된 클래스 코드와 서브 클래스 코드는 모두 '0' ~ '4'와 'A' ~ 'H'로 시작됩니다. 이외의 문자로 시작되는 클래스 코드와 서브 클래스 코드는 추가적인 에러 및 경고 코드를 정의하기 위한 영역입니다.

상태 변수 선언

프로그램 내에서 상태 변수를 사용하기 위해서는 먼저 상태 변수를 선언해야 합니다. 두 가지 상태 변수를 선언하는 방법은 다음과 같습니다.

  • SQLSTATE SQLSTATE는 반드시 크기 6char 배열 타입을 가져야 하며, DECLARE 영역 안에 대문자로 선언해야 합니다. 다음은 SQLSTATE를 선언하는 예입니다.

  • SQLCODE SQLCODE는 반드시 long 타입을 가져야 하며, DECLARE 영역 안에 대문자로 선언해야 합니다. 다음은 SQLCODE를 선언하는 예입니다.

상태 변수 종류

상태 변수에는 다음의 두 가지가 있는데, SQL 문장을 실행하는 도중에 발생한 에러 또는 경고를 서로 다른 값으로 표현합니다.

  • SQLSTATE SQLSTATE가 갖는 에러 또는 경고 코드는 SQL-92 표준에서 정의되어 있고, 추가적인 코드를 정의할 수 있도록 확장성을 지원하고 있습니다. 다음은 SQLSTATE의 클래스 코드를 정리한 것입니다. 여기에서는 SQL-92 표준에서 정의된 클래스 코드만을 나열하였습니다.

클래스 코드
설명

00

성공적인 완료

01

경고

circle-info

참고: SQLSTATE가 가질 수 있는 에러 및 경고 코드는 "Tibero tbCLI 안내서"를 참고합니다.

  • SQLCODE SQLCODE는 SQL-92에서는 사용하지 않습니다. SQLCODE에 저장되는 에러 코드는 정수 값을 가지며, 0인 경우와 양수인 경우, 음수인 경우로 나눌 수 있습니다. 각 SQLCODE의 값의 의미는 다음과 같습니다.

SQLCODE의 값
설명

0

SQLCODE 값이 0인 경우는 에러 없이 SQL 문장을 성공적으로 수행한 경우를 나타냄

양수

  • SQLCODE 값이 0보다 큰 경우는 SQL 문장의 수행을 마쳤으나 예외 상황이 발생했음을 의미

  • 예를 들어 SELECT 문장이 반환한 결과 로우가 하나도 없거나 UPDATE 문장을 실행한 결과 갱신된 로우가 하나도 없는 경우를 들 수 있음

음수

  • SQLCODE 값이 0보다 작은 경우는 에러 상황을 나타냄

  • 이 때에는 에러 때문에 SQL 문장의 수행을 끝마치지 못했음을 의미

circle-info

참고

음수 값을 갖는 SQLCODE 값은 Tibero에서 발생하는 일반적인 에러의 번호입니다. 이에 대해서는 "Tibero 에러 참조 안내서"를 참고합니다.

안내서에 기술된 에러 메시지 번호는 모두 양수이며, SQLCODE 값의 절댓값을 기준으로 찾습니다. SQLCODE는 “SQLCA”에서 설명할 SQLCA에 포함되며, WHENEVER 문장에서 검토하는 값도 SQLCODE 값입니다.

사용 예제

다음은 상태 변수를 사용하는 예제 프로그램입니다.

상태 변수를 검토하는 코드는 SQL 문장을 실행한 바로 다음에 와야 하며 모든 경우에 대비해야 합니다.

위의 예에서는 SQLCODE의 값에 따라 다음과 같이 처리하는 방법이 다릅니다.

① SQLCODE의 값이 0이면 정상적으로 tbESQL 문장을 실행한 경우입니다.

② SQLCODE의 값이 1403이면 결과 로우가 없는 경우입니다.

③ SQLCODE의 값이 0보다 작으면 에러가 발생한 경우입니다.

SQLCA

SQLCA는 SQL 통신 영역(SQL Communication Area)을 줄여서 부르는 말로, 임의의 SQL 문장이 실행된 결과가 저장되는 구조체 변수입니다.

SQLCA 구조체 변수

SQLCA는 헤더 파일 sqlca.h 내부에 sqlca 구조체 변수로 정의되어 있습니다. 구조체 변수 sqlca에 포함된 데이터는 바로 전에 실행된 SQL 문장의 정보입니다. 따라서 다른 SQL 문장을 실행하면 이전에 SQL 문장을 실행한 결과는 더는 sqlca에 저장되지 않습니다.

이 변수에 포함되는 정보는 다음과 같습니다.

포함 정보
설명

에러 코드

  • 에러 코드는 앞 절에서 설명한 SQLCODE와 같은 값을 가짐

  • 이 값은 WHENEVER 문장에서 참조하는 값

에러 메시지

에러 메시지는 최대 70bytes의 길이를 가짐

처리된 로우의 개수 및 파싱 에러의 위치

처리된 로우의 개수는 바로 전에 실행된 SQL 문장에 따라 의미가 달라짐

  • SELECT : 질의 결과 반환된 결과 로우의 누적 개수

  • INSERT : 삽입된 로우의 개수

  • UPDATE : 갱신된 로우의 개수

  • DELETE : 삭제된 로우의 개수

파싱 에러의 위치는 SQL 문장을 파싱하는 과정에서 에러가 발생한 경우에 SQL 문장 내의 에러 위치를 가리킴

경고 플래그

  • 경고 플래그는 경고 상황이 발생한 경우에 설정됨

  • 경고 상황으로는 출력 변수에 저장된 컬럼 값이 잘려진 값인 경우, SELECT 또는 FETCH 문장 내의 INTO 절에 포함된 출력 변수의 개수가 결과 로우의 개수에 비하여 적은 경우 등이 있음

다음은 sqlca 구조체 변수가 정의된 내용입니다.

다음은 sqlca 구조체 변수의 내부에 정의된 각 멤버 변수에 대한 설명입니다.

멤버 변수
설명

sqlcaid

SQLCA라는 것을 나타내기 위한 변수이며, 항상 "SQLCA" 문자열을 가짐

sqlabc

sqlca 구조체 변수의 Byte 길이가 저장됨

sqlcode

  • 결과가 숫자로 저장됨

  • 실행이 성공한 경우 0이며, 이외의 경우 적절한 에러 값 등을 가짐

  • 자세한 내용은 “7.2. 상태 변수”를 참고

sqlerrm

  • 에러 메시지의 Byte 길이가 저장되는 변수

  • 실제 메시지는 sqlerrm.sqlerrmc에 저장되어 있으며, 이 문자열은 마지막 NULL 문 자를 포함하지 않으므로 에러 메시지의 Byte 길이를 가지는 sqlerrm.sqlerrml 변수 를 반드시 참조해야 함

sqlerrp

SQL-99 표준에 있지만, 현재 Tibero에서는 사용하지 않음

sqlerrd

  • sqlerrd[2]와 sqlerrd[4]만 사용됨

  • sqlerrd[2]는 처리된 로우의 개수, sqlerrd[4]는 파싱 에러의 위치를 나타냄냄

sqlwarn

  • 경고 플래그

  • Byte 하나당 하나의 경고 상황이 발생했음을 나타냄

  • 문자 'W'를 저장함으로써 플래그를 설정

다음은 멤버 변수 sqlwarn에 저장되는 각 경고 플래그의 의미

  • sqlwarn[0] : sqlwarn[1], sqlwarn[2], sqlwarn[3] 중 하나가 설정되면 동시에 이 플래그도 'W'로 설정됨

  • sqlwarn[1] : 문자열 출력 변수에 잘린 값이 저장된 경우

  • sqlwarn[2] : SUM, AVG 등의 집단 함수를 계산할 때 NULL 값이 사용되지 않는 경우

  • sqlwarn[3] : SELECT 또는 FETCH 문장의 INTO 절에 있는 출력 변수의 개수가 컬럼의 개수와 일치하지 않는 경우

  • sqlwarn[4] ~ sqlwarn[7] : SQL-99 표준에 있지만, 현재 Tibero에서는 사용하지 않음

sqlext

SQL-99 표준에 있지만, 현재 Tibero에서는 사용하지 않음

사용 예제

SQLCA의 변수 sqlca는 전역 변수이며 헤더 파일 sqlca.h 내에 정의되어 있습니다. 따라서 이 변수를 사용하기 위해서는 다음의 코드를 tbESQL/C 프로그램 처음에 삽입해 주어야 하며, 따로 변수를 선언할 필요는 없습니다.

본 예제는 “사용 예제”에서 제시한 프로그램을 SQLCA 변수와 출력 배열 변수를 이용하도록 수정한 것입니다. 상태 변수 SQLCODE는 구조체 변수 sqlca 내에 포함되어 있으며, SELECT 실행 결과 로우의 개수와 에러 메시지도 sqlca에서 얻을 수 있습니다.

WHENEVER

WHENEVER 문장은 다른 tbESQL/C 문장처럼 어떠한 동작을 실행하기 위한 문장이 아니라, 특정 조건이 발생하면 특정 동작을 수행하라는 선언을 위한 문장입니다.

구성요소

WHENEVER 문장은 다음과 같이 조건 부분과 동작 부분으로 구성됩니다.

  • {조건} WHENEVER 문장의 조건 부분에 대한 설명은 다음과 같습니다.

항목
설명

SQLERROR

SQLCODE가 음수인 경우에 해당됨

SQLWARNING

  • SQLCODE가 0보다 큰 경우 또는 SQLCA 내의 경고 플래그가 설정된 경우

  • SQLCODE가 NOT FOUND 코드 값(+100)에 해당되는 경우는 제외됨

  • 경고 플래그가 설정된 경우는 sqlca.sqlwarn[0]에 'W' 값이 저장된 경우

NOT FOUND

  • SQLCODE가 NOT FOUND 코드 값인 경우에 해당됨

  • 질의의 결과 로우가 없거나 커서를 이용해 더 이상 액세스할 결과 로우가 없거나 INSERT, UPDATE, DELETE 문장에 의해 처리된 로우가 없을 때 NOT FOUND 경고가 발생

  • {동작} WHENEVER 문장의 동작 부분에 대한 설명은 다음과 같습니다.

항목
설명

CONTINUE

  • 프로그램의 다음 문장부터 계속하여 진행

  • tbESQL/C 프로그램에서 에러 또는 경고가 발생했을 때 디폴트로 CONTINUE를 실행

  • 이는 에러 또는 경고에 대한 처리가 없는 것과 동일

DO

  • 특정 함수를 호출

  • 호출된 함수는 대개 에러 처리 함수이며, 함수가 실행되고 나면 에러 또는 경고가 발생한 다음 문장부터 실행이 계속됨

  • 만약 치명적인 에 러가 발생했다면 함수 내에서 트랜잭션을 롤백시키고 프로그램을 종료할 수도 있음

DO BREAK

  • 루프 내에서 에러 또는 경고가 발생했을 때 BREAK를 실행

  • 만약 루프 내에서 사용되지 않았다면 컴파일 에러를 발생시킴

DO CONTINUE

  • 루프 내에서 에러 또는 경고가 발생했을 때 CONTINUE를 실행

  • 만약 루프 내 에서 사용되지 않았다면 컴파일 에러를 발생시킴

GOTO

  • GOTO를 실행하여 프로그램 내의 특정 위치로 분기

  • 분기할 위치는 레이블로 정의

STOP

  • 프로그램을 종료하며 현재 트랜잭션을 롤백

  • 에러 메시지의 반환 없이 바로 종료됨

다음은 WHENEVER 문장을 사용하는 예입니다.

위의 예에서 NOT FOUND가 조건, DO BREAK가 동작에 해당됩니다.

사용 예제

WHENEVER 문장이 선언되면 그 효과는 바로 다음 문장에서부터 시작됩니다. tbESQL/C 프로그램은 SQL 문장을 실행할 때마다 WHENEVER 문장에 포함된 조건이 발생하였는지 자동적으로 검토합니다. 만약 조건 이 발생하면 선언된 동작을 수행합니다.

WHENEVER 문장의 효과는 같은 조건에 대한 다음 WHENEVER 문장이 나타날 때까지 이어집니다. WHENEVER 문장이 나타나더라도 선언된 조건이 다르면 이전의 WHENEVER 문장의 효과는 지속됩니다. 즉, WHENEVER 문장의 효과는 블록 구조와는 상관이 없습니다.

WHENEVER 문장을 사용할 때는 다음과 같은 사항에 유의해야 합니다.

  • NOT FOUND가 SELECT, FETCH 문장에 의한 것인지 INSERT, UPDATE, DELETE 문장에 의한 것인지 구분해야 합니다. INSERT, UPDATE, DELETE 문장은 SELECT 또는 FETCH 문장과 마찬가지로 처리된 로우가 하나도 없으면 NOT FOUND를 발생합니다. 이때 프로그램 내에서는 NOT FOUND가 SELECT, FETCH 문장에 의한 것인지INSERT, UPDATE, DELETE 문장에 의한 것인지 구분해 주어야 합니다. 다음은 INSERT 문장을 잘못 사용하여 의도하던 바와 다르게 실행될 수 있는 경우의 예입니다.

[예 1] 잘못된 INSERT 문장

위의 코드를 작성한 의도는 모든 결과 로우를 액세스하여 더 이상의 결과 로우가 없을 때 FETCH 문장에서 NOT FOUND를 발생하면 루프를 빠져 나가는 것입니다. 하지만 INSERT 문장에서 삽입되는 로우가 없다면 마찬가지로 NOT FOUND를 발생하고 루프를 빠져나가게 됩니다. 따라서 모든 결과 로우를 액세스하지 못하게 됩니다.

다음은 [예 1]에서 발생한 문제를 해결한 예입니다.

위의 예에서는 WHENEVER 문장을 사용하지 않고 직접 SQLCODE를 검토하는 코드를 삽입하였습니다.

  • DO 또는 GOTO 동작을 선언했을 때 잘못하면 무한 루프에 빠질 수 있습니다. WHENEVER 문장에서 DO 또는 GOTO 동작을 선언했을 때 에러 또는 경고를 처리하기 위한 루틴에서 또다시 에러 또는 경고가 발생하면 무한 루프에 빠질 수 있습니다. 다음은 GOTO 동작을 선언한 경우에 무한 루프가 발생할 수 있는 예입니다.

[예 2] GOTO가 선언된 경우의 무한 루프

위의 예에서 SELECT 문장을 실행하는 중에 에러가 발생하면 error_handle 레이블로 분기하고, DELETE 문장을 실행합니다. 이때 DELETE 문장에서 에러가 발생합니다면 DELETE 문장은 무한 반복 상태가 됩니다.

다음은 [예 2]의 무한 루프 문제를 해결한 예입니다.

위의 예에서는 에러 처리 부분의 맨 앞에 WHENEVER 문장을 첨가하여 에러가 발생하더라도 계속 실행하도록 하여 무한 루프가 발생할 가능성을 없앴습니다.

다음과 같이 코드를 작성해도 [예 2]에서 발생하는 문제를 해결할 수 있습니다.

위의 예에서는 WHENEVER 문장에 DO 동작을 선언해 함수를 호출하였습니다.

함수의 맨 처음에 WHENEVER 문장을 삽입하고 맨 마지막에 에러 처리 함수를 호출하는 WHENEVER 문장을 다시 삽입하였습니다.

  • GOTO 동작을 선언하는 경우 분기할 레이블의 위치를 SQL 문장에서 액세스할 수 있는 범위에 두어야 합니다. 즉, SQL 문장에서 참조할 수 있는 레이블로 선언해야 합니다. WHENEVER 문장이 선언된 tbESQL/C 프로그램을 프리컴파일하면 모든 SQL 문장 뒤에 SQLCODE를 검토하고 선언된 동작을 수행하는 코드를 삽입합니다. 만약 GOTO 동작에서 분기할 레이블이 현재 삽입된 코드에서 참조할 수 없는 위치에 있다면 컴파일 에러가 발생합니다.

다음은 GOTO 레이블에 의하여 컴파일 에러가 발생하는 예입니다.

위의 예에서 메인 루틴 내에서는 error_handle 레이블을 참조할 수 있지만, 함수 f에서는 참조할 수 없습니다. 이러한 문제를 해결하기 위하여 함수 f 내에 새로운 WHENEVER 문장을 삽입하는 방법도 있습니다.

Last updated