본문 바로가기

프로그래밍(TA, AA)/C C++

[Pro*C] Pro*C 소개와 기본 특징 및 오류 진단

1. Pro*C 소개와 기본 특징


1-1. Pro*C 개요


SQL 문은 절차형 언어가 아닙니다. 그래서 오라클을 포함한 많은 데이터베이스는 PL/SQL이라는 절차형 언어를 제공합니다. PL/SQL은 오라클 내부에서 실행되는 프로그램으로서 오라클 내부라는 한정된 공간에서 실행되는 도구입니다.


DBMS의 버전이 높아지면서 이전관 다르게 PL/SQL에도 다양한 기능이 추가 되었습니다. 예를 들면, TCP/IP, HTTP, FILE I/O 등과 같은 기능이 추가되어 다양한 방식의 프로그래밍이 가능해졌습니다. 하지만 오라클 외부 프로그램과의 연동 등에 있어서는 많은 제약이 있습니다. 예를 들어, TCP/IP 통신을 하는 PL/SQL 문장을 작성하여 수행하는 중에 메모리 할당 등 예기치 않은 오류로 프로그램이 종료되는 상황이 발생할 수 있습니다. 프로그램이 오류로 인해 종료되는 부분은 프로그램을 수정하여 해결이 가능합니다. 하지만 이로 인해 오라클 엔진에 부하가 가해진다면 이는 돌이킬 수 없는 장애로 이어질 수 있습니다. 이렇듯 내부 PL/SQL을 통한 프로그램의 경우 오라클이라는 내부 울타리에서만 수행되는 특징이 큰 제약으로 작용합니다.


이런 고민을 해결하기 위해서 대부분의 DBMS 벤더는 외부 C 프로그램과 결합할 수 있는 선행 컴파일러를 제공하고 있으며 오라클에서는 이를 Pro*C라고 합니다. Pro*C는 PL/SQL과 같이 절차적 프로그래밍이 가능한 프로그램 도구로서 PL/SQL처럼 오라클 내부에서 수행되는 프로그램이 아니라 실행 가능한 외부 프로그램으로 작성이 되어 관련 작업을 수행할 수 있게 해 주는 도구입니다.


다만 일반 프로그램 언어들과 다른 점이 있다면, Pro*C만으로는 실행 파일을 만들어 낼 수 없다는 것입니다. 그래서 Pro*C를 Pre Compiler(설행 컴파일러)라고 합니다. Pro*C는 선행 컴파일러이기 때문에 선행 컴파일을 통해서 실행 파일이 아닌 C 컴파일러가 인식할 수 있는 출력 파일(xxx.c)을 생성합니다. 선행 컴파일러를 통해 생성된 C 프로그램은 C 프로그램의 통상적인 방법으로 컴파일되고, 오라클 라이브러리와 결합(링크)되어 실행 모듈이 만들어집니다.


Pro*C 프로그래밍을 위해서는 Pro*C 고유의 문법을 숙지하고 있어야 하지만 C 프로그래밍에 대한 기본 지식도 있어야 합니다. 특히 메모리와 관련된 부분이나 포인터 등과 같은 C의 고급 기능을 사용하기 위해서는 C 프로그래밍에 대한 지식을 갖추고 있어야 합니다. 그리고 C 프로그램은 기본적으로 OS의 특성에도 많은 영향을 받으므로 OS의 고유한 특성도 공부해야 합니다.


Pro*C를 처음 접하는 처음 접하는 분들에게 너무 많은 부담감을 드린 듯하지만 대부분의 개발자가 작성하는 일반적인 배치 프로그램이나 미들웨어 연동 프로그램의 경우에는 C에 대해 많이 알고 있지 않아도 정상적인 개발과 운영이 가능합니다. 물론 조금 더 심도 있는 프로그래밍을 위해서는 C의 고급 기능이나 OS의 고유한 특성을 자세히 알고 있어야 합니다.


그러나 이 책을 공부함에 있어서 C에 대해 많이 알고 있지 않아도 무방합니다. 기초 단계를 넘어 고급 단계로 가기 위해서 무엇이 필요한지 미리 알아본 것이므로 크게 부담감을 갖지 않아도 됩니다.



1-2. Pro*C 확장 범위


Pro*C는 오라클 데이터베이스와 연동할 수 있는 C 프로그램입니다. C 프로그램이기 때문에 C 프로그램으로 처리할 수 있는 다양한 기능을 Pro*C로도 처리할 수 있습니다. Pro*C를 어느 범위까지 확장해서 활용할 수 있는지 요약하면 다음과 같습니다.


  1. 갸간 혹은 주간 배치 프로그램
  2. 미들웨어 프로그램(Tuxedo, Tmax, Entera)
  3. OLTP 프로그램
     - TCP/IP 모듈과 결합한 자료 생성 및 전송 프로그래밍(EDI)
     - 온라인 프로그램으로 이루어진 다량의 자료 가공 프로그램을 Pro*C로 이관하여 성능 개선


Pro*C 프로그램의 소스 코드를 한번이라도 보았다면 C 코드 중간중간에 SQL 문장이 들어있는 것을 보았을 것입니다. 그래서 Pro*C 프로그램을 Embedded SQL 프로그램이라고도 합니다. 큰 틀에서 보면 C 프로그램 안에 SQL 코드가 들어가 있는 것이 Pro*C 프로그램입니다. 이 말은 C 프로그램으로 처리할 수 있는 모든 기능을 Pro*C로도 구현할 수 있다는 것입니다. 즉, Pro*C의 확장 범위는 우리가 생각하는 것보다 훨씬 더 넓습니다.


TCP/IP, UDP, HTTP 프로그램과 SQL 코드를 결합한 프로그램을 구현할 수 있으며, 미들웨어(TMAX, TUXEDO)와 결합한 프로그램도 Pro*C로 작성할 수 있습니다. 이처럼 단순한 배치 프로그램뿐 만이 아니라 다양한 업무와 분야에 Pro*C를 적용할 수 있씁니다. 윈도우용 실행 프로그램을 만들려고 할 때도 Pro*C와 Visual C++, Turbo-C를 결합하여 MFC, ATL,  Console-C 등과 같은 다양한 프로그래밍을 처리할 수 있습니다.


필자의 경우에도 미들웨어를 도입하기 전에 당사 시스템의 주요 부분에서 TCP/IP와 결합한 Pro*C 프로그램을 작성하여 온라인 서비스에서 운영하였으며, 이를 통해 OLTP 시스템의 성능을 크게 향상시킨 바 있습니다.


결론적으로, C로 구현할 수 있는 모든 기능을 Pro*C로도 구현할 수 있습니다. 이 부분이 가지는 장점에 대해서는 Pro*C 프로그래밍을 할수록 더 많이 느낄 수 있으므로 여기서 하나하나 열거하지 않겠습니다. 다만 초보 Pro*C 개발자 딱지를 떼는 순간부터, 위에서 기술한 확장 범위까지 능숙하게 처리할 수 있는 고급 개발자가 되기 위해 SQL 뿐만 아니라 C 프로그램에 대한 공부도 계속 병행해야 함을 명심하기 바랍니다.



1-3. 프로그램 작성 방식


4GL 프로그램으로 DB 핸들링 프로그램을 작성하기 위해서 가장 먼저 할 일은 DB 접속 방식을 선택하는 것입니다. ODBC 방식으로 할 것인지, OLE-DB 방식으로 진행할 것인지를 선택한 후에 그에 맞는 프로그램 방식을 결정해야 합니다.


Pro*C 역시 다르지 않습니다. GUI 방식의 4GL 프로그램처럼 마우스 클릭을 통해 설정할 수 있는 것이 아니라 코딩 시점에 Header 파일 선정, 프로그램 방식의 선정, 컴파일 옵션의 변경을 통해 작성 방식을 선택하여 사용할 수 있습니다. Pro*C에서 프로그램 작성 방식은 두 가지로 나누어지며, 하나는 내장 SQL 방식이고, 다른 하나는 OCI(Oracle Call Interface) 방식입니다.



(1) 내장 SQL 방식

내장 SQL 방식이란 C 프로그램 내부에서 'EXEC SQL'이라는 접두사 뒤에 SQL 문장을 직접 기술하는 방식입니다. 내장 SQL 방식은 통상적으로 가장 많이 사용됩니다. 우리에게 익숙한 윈도우 환경의 개발툴과 비교해 본다면, OLE나 ODBC를 통해 데이터베이스와 연결하여 작업하는 방식의 프로그램과 비슷하다고 할 수 있습니다. 


이 책에서도 가장 대중적인 방식인 내장 SQL 방식을 기준으로 실습 예제를 만들고 설명할 것입니다. 내장 SQL 문장에는 일반 SQL 문장뿐만 아니라 오라클을 이용한 다양한 형식의 내부 문장을 사용할 수 있으며, 미리 생성되어 있는 Stored Procedure, Package 또는 개발자가 임의로 작성한 PL/SQL도 사용할 수 있습니다. 즉, 오라클 데이터베이스에서 사용하는 모든 문장, 즉 DML, DDL, DCL, PL/SQL, 일반 SQL 문장을 내장 SQL 문장에 사용할 수 있습니다.


내장 SQL 문장에 사용할 수 있는 명려어는 아래 표(내장 SQL 문장 구문 방식의 프로그램 구문)와 같습니다.


 명령어

 설명

 ARRAYLEN

 PL/SQL에서 호스트 array를 사용한다.

 BEGIN DECLARE SECTION

 END DECLARE SECTION

 ANSI 모드에서 호스트 변수를 사용한다.

 DECLARE

 내부 오브젝트를 선언한다.

 INCLUDE

 외부 *.h를 참조한다.

 TYPE

 임의 데이터 타입을 설정한다.

 VAR

 변수의 동일화

 WHENEVER

 runtime 에러 핸들링 문장

 ALLOCATE

 CURSOR 변수에 영역을 할당한다

 ALTER

 오라클의 정의

 ANALYZE
 AUDIT
 COMMENT
 CONNECT
 CREATE
 DROP
 GRANT
 NOAUDIT
 RENAMERE
 VOKE
 TRUNCATE
 CLOSE

 오라클에 접속 제어 오브젝트 변경 문장

 DELETE
 FETCH
 INSERT
 LOCK TABLE
 OPEN
 SELECT
 UPDATE
 EXPLAIN PLAN

 데이터 조작 및 데이터 추출문

 COMMIT
 ROLLBACK
 SAVEPOINT
 SET TRANSACTION

 트랜잭션 제어 문

 DESCRIBE
 EXECUTEPREPARE

 Dynamic SQL 사용을 위한 문

 ALTER SESSION
 SET ROLE

 세션 제어 문



(2) OCI 방식

OCI(Oracle Call Interface) 방식이란 OCI 라이브러리를 통해서 오라클 SQL 문장을 직접 호출하여 사용하는 방식입니다. 내장 SQL 방식에 비해서 조금 더 하위 레벨에 해당하는 프로그래밍 방식으로서 OCI를 통해서 DB 핸들링 작업을 실행하기 때문에 데이터베이스 서버의 자원을 효율적으로 관리하고 SQL 문장 수행의 각 단계를 직접 제어할 수 있다는 장점이 있습니다.


그러나 프로그래밍 방법에 있어 내장 SQL 프로그램에 비해 복잡하기 때문에 개발자의 숙련도가 요구된다는 단점이 있습니다. Unix 환경의 C 프로그램을 대부분의 개발자들은 쉽게 생각하기보다는 어렵게 생각하고 있습니다. 이는 대부분의 제어를 개발자가 직접 기술해야 하기 때문입니다. 반면에 GUI 프로그램은 마우스 클릭 몇번만으로 대부분의 설정이 가능해집니다. 이렇듯 OCI 프로그램에서는 모든 부분의 직접 제어나 기술이 내장 SQL 프로그램에 비해서 어렵게 여겨지는 것입니다. 물론 대부분의 프로그램에는 특정한 패턴이 있으므로 그 패턴을 익히기만 하면 어떤 프로그램 방식이 더 쉽다, 혹은 더 어렵다고 단정지을 수는 없습니다.


대부분의 경우라고 할 수는 없지만 적지 않은 개발자들이 내장 SQL 문장 방식을 사용하고 있습니다. 이 책에서도 내장 SQL 문장 방식을 사용하며, OCI 방식 프로그램에 대해서는 다루지 않습니다. OCI 방식의 프로그램 방법을 더 자세히 알고 싶으면 오라클 사이트에 관련 예제를 어렵지 않게 구할 수 있으므로 이를 참고하면 됩니다.


 내장 SQL 방식

OCI 방식

 SQL 연산을 쉽고 명료하게 처리하기 위해 3GL 어플리케이션 개발

 데이터베이스를 최대한 제어하면서 3GL 어플리케이션 개발

 간결한 코드 작성

 길고 복잡한 코드 작성

 컴파일 전에 소스 코드 선행 컴파일

 선행 컴파일 단계 없이 코드 컴파일

 선행 컴파일러를 별도로 구매

 오라클 데이터베이스와 함께 OCI 라이브러리 획득

 ANSI 표준 준수(X3.168-1992)

 독점 비 표준 절차적 호출 인터페이스 사용

 다중 행 질의만을 위해 명시적 커서 선언

 모든 데이터베이스 연산을 처리하기 위한 명시적 커서 선언

 선행 컴파일 시에 SQL 구문 확인 

 실행 시간에 SQL 구문 확인




1-4. Pro*C의 데이터 형


Pro*C에서 사용하는 데이터 형은 C 프로그램에서 사용하는 일반적인 데이터 형과 Pro*C에서만 사용할 수 있는 고유한 형태의 데이터 형으로 구성되어 있습니다. 아래의 내용은 Pro*C 프로그램에서 사용하는 데이터 형을 설명한 것입니다.


  • 기본적인 데이터 형의 일차원 배열
  • CHAR 데이터 형과 VARCHAR 데이터 형의 이차원 배열
  • 출력 호스트 변수로 VARCHAR 변수를 사용할 때 오라클은 구조체의 멤버 길이를 설정하지만 배열을 NULL-terminate(\0)시키지는 않습니다. 그러므로 출력하기 전에 출력 호스트 변수를 NULL-terminate 시키십시오. CHAR은 자동 NULL-terminate입니다.
  • 기본적인 데이터 형에 대한 포인터
  • 사용자 정의의 typedef
  • 구조체
  • 배열의 구조체
  • 구조체에 대한 포인터
  • 구조체 배열


아래의 [표 1-3]에서는 C 프로그램에서 일반적으로 사용할 수 있는 데이터 형의 종류와 각 데이터 형의 세부 내용을 설명하였습니다. Pro*C 역시 C 프로그램이기 때문에 C의 일반적인 데이터 형을 사용할 수 있습니다.


 C의 데이터 형

 설명

 char

 단일 문자

 char[n]

 n 바이트의 문자 배열

 int

 정수

 short
 short int

 작은 정수 (지시자 변수에 대한 데이터 형)

 long
 long int

 긴 정수

 floatlong
 float

 부동 소수점, 단정도

 double

 부동 소수점, 배정도

 VARCHAR

 가변 길이 문자



오라클 데이터베이스의 데이터 형과 C 프로그램의 데이터 형은 호환됩니다. 그 내용을 아래 표와 같이 정리할 수 있습니다.


 오라클 DB의 데이터 형

 C의 데이터 형

 설명

 VARCHAR2(Y)
 (Y: 1~2000)

 char

 단일 문자

 CHAR(X)(X:1~255)

 char[n]

 VARCHAR[n]

 int

 short

 float

 n 바이트의 문자 배열
 n 바이트의 가변 문자 배열
 정수

 작은 정수

 부동 소수점

 NUMBER

 NUMBER(P, S)

 int

 short

 long

 float

 double
 char

 char[n]

 VARCHAR[n]

 정수

 작은 정수

 긴 정수

 부동 소수점

 배정도 부동 소수점

 단일 문자

 n 바이트의 문자 배열

 n 바이트의 가변 문자 배열

 DATE

 char[n]

 VARCHAR[n]

 n 바이트의 문자 배열
 n 바이트의 가변 문자 배열

 LONG

 char[n]

 VARCHAR[n]

 n 바이트의 문자 배열
 n 바이트의 가변 문자 배열

 RAW(X)

 unsigned char[n]
 VARCHAR[n]

 n 바이트의 문자 배열

 n 바이트의 가변 문자 배열

 LONG LAW

 unsigned char[n]
 VARCHAR[n]

 n 바이트의 문자 배열
 n 바이트의 가변 문자 배열

 ROWID

 unsigned char[n]
 VARCHAR[n]

 n 바이트의 문자 배열
 n 바이트의 가변 문자 배열

 MLSLABEL

 unsigned char[n]

 VARCHAR[n]

 n 바이트의 문자 배열
 n 바이트의 가변 문자 배열

 주의:  X : 1~255 사이의 값, 디폴트는 1

         Y : 1~2000 사이의 값

         P : 2~38, S: -84~127 사이의 값


오라클의 char을 C 프로그램의 char로 치환하고, number을 in, long, short로 치환해서 사용할 수 있습니다. 그러므로 오라클 데이터베이스와 연결해서 프로그래밍을 함에 있어서 큰 제약이 없습니다.


VARCHAR 형의 경우에는 데이터 형의 특성상 C 프로그램의 char 데이터 형과는 다른 특이한 형태를 가지고 있습니다. Pro*C 프로그램에서 VARCHAR uid[20];로 선언하면 실제로는 아래와 같은 형태로 생성됩니다.


struct {
	unsigned short int len;
	unsigned char arr[20];
} uid;


오라클 데이터베이스에서 VARCHAR2 데이터 형의 선언은 가변 길이 문자열로, 데이터형 선언시 정의된 데이터 길이 내에서 다양한 길이의 데이터를 가질 수 있다는 것입니다. 좀더 쉽게 설명하자면 'VARCHAR2 CODE[40]'로 선언하고 '1'이라는 데이터를 할당하면 전체 40바이트의 길이 중 1바이트만 사용이 되고 나머지 39바이트는 빈 공간으로 남게되어 공간의 효율적 활용이 가능해지며 이러한 것을 가변 길이 문자열이라고 하는 것입니다. 


이와 같이 Pro*C의 VARCHAR 형도 오라클 데이터베이스의 VARCHAR2와 동일하게 가변 길이로 할당이 되어 사용된다는 것입니다. 다만 Pro*C만의 특징이 있다면, Pro*C에서 VARCHAR로 선언하는 것은, 내부적으로는 가변적 데이터 할당이 가능한 문자형 구조체로 선언하는 것과 같습니다.


구조체로 선언하지 않더라도 구조체의 특성을 가지므로 변수에 값을 할당하는 방법도 일반 char 변수와 다르게 이루어집니다. 뿐만 아니라 명시적으로 포인터로 선언하지 않더라도 VARCHAR 형은 데이터를 포인터로만 전달할 수 있기 때문에 데이터 할당과 출력에 있어 일반 데이터 형과 같은 형태가 아닌 포인터의 처리 프로세스를 따릅니다. 


char 형으로 선언하고 할당하려면 아래와 같이 진행하면 됩니다. 이 형식은 C 프로그램과 동일하며, 사용법에 있어서도 전혀 차이가 없음을 알 수 있습니다.


char uid[20];
strcpy(uid, "userid");


char 형과 달리 VARCHAR형의 경우에는 다음과 같이 선언하고 할당 합니다.

VARCHAR uid[20];
strcpy((char*)uid.arr, "userid");
uid.len = (short)strlen((char *)uid.arr);

VARCHAR 형은 선언과 동시에 len과 arr이라는 멤버를 갖는 구조체로 선언됩니다. 구조체이지기 때문에 앞서 확인한 char 형의 변수 데이터 할당 방법과 동일하게 사용할 수 ㅇ벗습니다.


구조체 변수에 대한 데이터 할당을 위해서는 구조체를 이루고 있는 각 멤버에 데이터를 할당해야 합니다. 물론 경우에 따라서는 구조체에 대한 할당만으로도 각 멤버에 대한 데이터 할당이 가능하기도 합니다. 이는 내부적으로 볼 때 자동적으로 각 멤버에 대한 할당이 이루어지는 것입니다. 대표적으로 Pro*C에서 데이터 추출 시 사용하는 FETCH 구문이 이에 해당됩니다.


VARCHAR 형도 구조체입니다. 그렇기 때문에 데이터를 할당하기 위해서는 구조체를 이루고 있는 각 멤버에 데이터를 할당해야만 합니다. 뿐만 아니라 데이터 전달이 포인터로만 가능하기 때문에 데이터 형에 대한 포인터로의 형 변환(Type Cast)도 필요합니다.


Pro*C의 VARCHAR형과 char 형은 사용 방법에서만 차이가 있는 것이 아닙니다. 저장 공간에 있어서도 차이가 있습니다. char 형은 Fixed Data Type으로 데이터 형의 길이만큼 저장 공간을 차지하고, VARCHAR 형은 데이터의 길이와 상관없이 할당받은 데이터 길이만큼의 저장 공간을 갖습니다. 사용의 편이성만 놓고 보자면 구조체로의 형변환도 안되고, 포인터로의 형번화도 필요없는 char형이 더 좋아보이지만 저장 공간, 즉 메모리 공간의 효율적 사용이라는 측면에서 본다면 VARCHAR 형은 char 형과 견주어 절대 뒤지지 않는 훌륭한 데이터형입니다.


이러한 장점이 작은 규모의 프로그래멩서는 큰 차이를 갖지 않지만, 사용자의 요청이 많은 프로그램이나 핸들링 해야 할 데이터가 많은 프로그램의 경우에는 성능 차이가 점점 크게 벌어지므로 각자 처한 상황에 맞게 충분히 고려한 후에 적절한 데이터 형을 사용해야 합니다.


Pro*C 프로그램에서 저장 공간과 처리 방법에 있어서 차이를 보이는 데이터 형은 char 형과 VARCHAR 형 두 개 밖에 없습니다. 다른 데이터 형은 C 프로그램에서 사용되는 일반 데이터 형과 동일합니다. 그러므로 두 데이터 형을 사용할 때는 주의하기 바랍니다.



2. Pro*C 오류 진단과 처리


프로그램을 작성할 때 가장 중요한 것은 오류에 대한 정확한 핸들링과 그에 대한 처리입니다. 프로그램 오작동시 원인이 무엇인지와 오작동의 원인을 해결하기 위해 무엇이 필요한지를 정확히 짚어낼 수 있어야 합니다. Pro*C에는 이를 인지하고 처리하는 기능이 있습니다.


어떻게 보면 C 프로그램보다 에러 검출이 더 쉽다고 할 수 있습니다. C 프로그램의 경우에는 시스템 시그널에 대한 예외 처리를 해야 한다든지, core 파일 없이 프로그램이 비정상 종료되는 segmentation fault를 처리해야 하는 등 여러 가지 상황에 따라 조건을 다르게 적용해서 처리해야 하는 어려움이 있습니다. 이에 반해 Pro*C에서는 이러한 복잡한 조건이 붙지 않습니다.


내장 SQL이 포함된 응용 프로그램을 실행하면 다양한 형태의 에러와 상태에 대한 경고가 발생할 수 있습니다. 그러므로 응용 프로그램이 정상적으로 수행되어 명료하게 종료되기 위해서는 실행 시 에러와 경고를 미리 예상하고 처리해야 합니다. 설사 미리 예상하지 못하더라도 이러한 오류와 경고를 어딘가에 기록해서 후에라도 이를 해결할 수 있어야 합니다. 앞으로 발생할 모든 에러를 미리 예측할 수는 없지만 프로그램 흐름 상에서 기능적으로 발생하는 에러와 경고를 미리 예상하고 처리해야 합니다. 설사 미리 예상하지 못하더라도 이러한 오류와 경고를 어딘가에 기록해서 후에라도 이를 해결할 수 있어야 합니다. 앞으로 발생할 모든 에러를 미리 예측할 수는 없지만 프로그램 흐름 상에서 기능적으로 발생하는 에러와 포괄적 범위의 에러의 처리에 대해서는 필히 기술하여 처리해야 합니다.


에러나 경고를 감지하고 에러에 대해 더 많은 정보를 얻기 위해서는 별도 통신 영역의 정보를 가진 도구가 필요하며 Pro*C에서는 SQL 통신 영역(SQLCA)오라클 통신 영역(ORACA)이라는 두 개의 특수한 데이터 구조를 지원합니다. 이는 프로그램 안에서 사용하는 SQL 문장에 대한 경고나 오류의 상태를 기술하는 구조체로서, 개발에 사용되는 컴퓨터의 오라클 홈 디렉터리 밑의 precomp 및의 public 밑에 설치되며 해당 파일을 직접 열어 확인할 수 있습니다. 위치를 정식으로 표기하면 다음과 같습니다.


$ORACLE_HOME/precomp/public


SQLCA는 Pro*C 프로그램에 있어 필수 요소이고, ORACA는 더 많은 정보를 얻기 위한 선택 요소로 사용됩니다. Pro*C에서는 SQLCA를 사용하여 모든 SQL 문장의 상태를 감지한 후에 상태 코드와 같은 정보를 제공합니다. SQLCA보다 더 많은 확장된 정보를 얻고자 할 때, 가령 응용 프로그램이 커서 캐시와 같은 오라클 자원의 사용을 모니터링 하기 위해, 또는 오류가 발생한 시점에 수행된 SQL 문의 정보를 확인하기 위해 ORACA를 사용할 수 있습니다.


 Oracle Text File

 요구사항

 내용

 sqlca

 필수

 SQLCA를 위한 선언

 oraca

 선택

 오라클 통신 영역을 위한 선언


Pro*C에서의 오류 처리는 다른 프로그램이나 오라클의 PL/SQL과 동일하게 이루어집니다. 각각의 내장 SQL 문장 뒤에 호스트 언어 문장을 사용하여 오류를 명시적으로 처리하거나, 'WHENEVER' 문장을 사용하여 발생 가능한 모든 오류에 대해 동일한 처리 방식이 수행되도록 처리할 수 있습니다.


이번 장에서는 상기의 두 가지 오류 검출 도구에 대해 알아 보도록 하겠습니다.



2-1. SQLCA


모든 Pro*C 프로그램에서 프로그램 실행에 고나한 정보를 데이터베이스와 교환하기 위해서 SQLCA(SQL 통신 영역)를 필수로 사용합니다. SQLCA를 사용함에 있어서 별도의 필요 조건이나 방법은 필요치 않으며, C 프로그램에서 헤더 파일을 사용할 때와 같이 프로그램의 시작 부분에 헤더 파일을 첨부해서 사용할 수 있습니다.


SQLCA는 예외와 경고를 감지합니다. SQL 문 속에 변수의 수가 너무 적어서 요구된 데이터를 반환할 수 없는 경우나, 데이터 추출 시 추출하려는 데이터가 없는 경우(ORA-1403) 등 다양한 경우의 에러와 경고에 대해 SQLCA는 SQL 문장의 실행 시점에서 이를 감지하고 출력합니다.


내장 SQL 프로그램 방식을 선택하였다면, SQLCA는 Include 파일 선언 부를 통해 프로그램 내에서 필수로 선언해서 사용해야 하는 참조 파일입니다. SQLCA는 오라클 홈 디렉터리 밑의 precomp/public에 'sqlca.h' 파일로 존재합니다. sqlca.h 파일은 SQLCA를 기술한 파일로서 Pro*C 프로그램에서 SQLCA를 사용하기 위해서는 일반 Include 파일의 선언과 동일한 방법으로 선언해서 사용할 수 있으며, 헤더 파일 설정 구문은 다음과 같습니다.

#include <sqlca.h>

위와 같이 C 프로그램의 고유한 방식으로 선언하여 사용할 수 있으며, Pro*C의 고유한 문법인 'EXEC SQL INCLUDE SQLCA;'를 통해 선언하여 사용할 수도 있습니다. EXEC SQL INCLUDE 문법을 사용할 경우에는 해당 파일의 확장자를 별도로 지정할 필요가 없습니다. 아래와 같이 헤더 파일을 설정하면 됩니다.

EXEC SQL INCLUDE SQLCA;

SQLCA는 직전의 SQL 문장의 처리 결과에 대해 SQLCA 파일에 정의되어 있는 sqlca 구조체에 정보를 저장합니다. 그러므로 개발자는 각 SQL 문장마다 SQLCA 구조체 내용을 검색하여 에러와 경고 여부에 대한 정보를 확인할 수 있습니다.


SQLCA는 다음과 같은 정보를 가지고 있습니다.


  • 경고 플래그 처리 상황에 관한 정보
  • 에러 코드
  • 진단 정보


SQLCA 구조체의 개괄적인 구조는 아래와 같습니다.

struct {
	char sqlcaid[8];
	long sqlcabc;
	long sqlcode;
	struct {
		unsigned short sqlerrml;
		char sqlerrmc[70];
	} sqlerrm;
	char sqlerrp[8];
	long sqlerrd[6];
	char sqlwarn[8];
	char sqlext[8];
} sqlca;

SQLCA 구조체의 각 멤버를 하나씩 살펴보겠습니다.


 번호/멤버

 설명

 sqlca.sqlcaid

 문자열로, 글로벌 구조로 사용하는 경우에 한해서, 할당 시에 "SQLCA"로 초기화됩니다. 이는 C가 아니라 주로 FORTRAN에 관련된 것입니다.

 sqlca.sqlcabc

 4바이트 2진 정수로 SQLCA 구조 자신의 길이가 바이트로 설정됩니다. 이는 C가 아니라 주로 FORTRAN에 관련된 것입니다.

 sqlca.sqlcode

 가장 최근의 문장에서 에러 또는 시스템 장애가 발생한 경우에 오라클서버는 음수 에러 코드를, 가장 최근의 문장이 아무런 행을 반환하지 않는 SELECT나 FETCH 등의 경고인 경우는 +1403(MODE = ANSI인 경우는 +100)를 가지며, 나머지의 경우는 0으로 실행이 정상으로 종료한 것을 나타냅니다. 양수는 실행 시 에러 발생을 의미합니다.

 sqlca.sqlerrm.sqlerrml

 가장 최근에 발생한 오라클 서버 에러 메시지(sqlca.sqlerrm.sqlerrmc)의 길이를 나타내는 필드입니다. 

 sqlca.sqlerrm.sqlerrmc

 가변 길이의 문자열로, sqlca.sqlcode 안에 표시된 에러 번호에 대응하는 에러 메시지의 내용입니다. 가장 최근의 오라클 서버 에러 메시지 내용이 들어가는 필드입니다.

 sqlca.sqlerrp

 현재는 사용하지 않습니다.

 sqlca.sqlerrd

 4바이트 2진 정수 6개로 된 배열이고, 오라클 RDBMS의 내부 상황을 파악하기 위해 사용합니다.

 sqlca.sqlerrd[4]

 구문 분석 오류 오프셋을 가지는 필드입니다. SQL 문 실행 때 참조된 형재 행 위치를 리턴합니다.

 sqlca.sqlerrd[2]

 INSERT, UPDATE, DELETE의 DML 처리에 대해서 몇 개의 행이 처리되었는지, SELECT 시 몇 개의 행이 추출되었는지를 나타냅니다. 해당 문장이 처리한 행의 개수가 들어갑니다.

 sqlca.sqlwarn

 1문자의 요소 8개로 구성된 구조입니다. 이들의 요소는 프리 컴파일 중에 발생한 여러 가지 상화에 대한 경고를 나타냅니다. 예를 들면, 오라클이 평균치의 계산에서 널(null)을 무시한 경우 등에 경고가 설정됩니다. 이 경우는 대응하는 요소의 값이 "W"로 됩니다. 그렇지 않은 경우에 이 요소의 값은 널(null)입니다. 1개라도 경고가 설정되면 첫 번째 요소 [0]는 항상 "W"가 됩니다. 각 "W"는 치명적인 에러는 아니지만, 각 경고는 검사해야 할 문제나 상황이 있음을 프로그래머에게 보여줍니다.

 sqlca.sqlwarn[0]

 어떤 임의의 경고가 1개라도 설정된 경우에는 W, 그렇지 않은 경우에는 NULL 스트링을 가지는 필드입니다. 즉, sqlwarn에 대해서 indicator 역할을 하는 필드입니다.

 sqlca.sqlwarn[1]

 SELECT나 FETCH 문장이 호스트 변수의 폭이 충분하지 않기 때문에 리턴된 문자 필드가 1개 이상 절사 되었음을 나타냅니다. 이것은 문자 데이터에만 적용됩니다. 즉, 오라클은 수치 타입 데이터에 대해서는 경고를 설정하거나, 음의 sqlca.sqlcode를 리턴하지 않고 절사를 합니다. 어느 필드가 얼마만큼 절사되었는지를 알기 위해서는, 이것에 대응하는 지시자 별수를 참조해야 합니다. 지시자 변수가 양인 경우, 이 값은 절사 전의 데이터 길이입니다. 그러므로 호스트 변수의 폭을 이것에 따라서 증가시켜야 합니다.

 sqlca.sqlwarn[2]

 이는 반드시 에러라고 볼 수 없으며, 프로그래머에게 유용하게 사용될 수 있습니다. "W"가 설정되어 있는 경우, sqlca.sqlwarn[2]는 AVG, SUM, MIN, MAX 등의 함수 계산에서 1개 이상의 널이 무시되었음을 보여줍니다.

 sqlca.sqlwarn[3]

 SELECT나 FETCH 절에서 열의 개수가 INTO 절에서의 출력 호스트 변수의 개수와 다른 경우에는 W, 그렇지 않은 경우에는 NULL 스트링을 가지는 필드입니다. 이때 데이터는 리턴되지만, 리턴되는 항목의 수는 적은 쪽에 일치됩니다.

 sqlca.sqlwarn[4]

 UPDATE나 DELETE에서 WHERE 절이 없는 경우에는 W, 그렇지 않은 경우에는 NULL 스트링을 가지는 필드입니다. 이는 오라클이 조건없는 UPDATE 또는 DELETE 인지를 나타내서 확인시키거나, 롤백할 수 있도록 경고를 설정합니다.

 sqlca.sqlwarn[5]

 현재는 사용되지 않습니다.

 sqlca.sqlwarn[6]

 실행된 SQL 문에 의해 오라클이 논리적인 작업 단위의 롤백을 행한 경우에, 이 필드에는 "W"가 설정됩니다. 이 필드는 ROLLBACK WORK 문 뒤에 설정되는 것이 아니고, 묵시적인 ROLLBACK WORK에 대해서 설정되는 것에 주의하기 바랍니다. 예를 들어, Deadlock에 의해 트랜잭션이 롤백되는 경우에는 "W"기 설정됩니다.

 sqlca.sqlwarn[7]

 행의 현재 데이터가 쿼리와는 모순되는 데이터인 경우(혹인, 행의 현재 데이터가 이 쿼리의 시작 후에 삭제된 경우)에 이 필드에는 "W"가 설정됩니다. 쿼리에서 지정하고 다시 FOR UPDATE 구에서 지정한 테이블에 포함되는 열이, 이 쿼리를 시작한 시점에서 이 행이 추출 또는 잠금 된 시점 내에 갱신된 경우에 추출 처리가  리턴된 시점에서 이 경고가 설정됩니다. 이 경고가 설정되는 조건은 이 열의 쿼리의 SELECT 리스트에 있으면서 FOR UPDATE 구에서 지정한 테이블 속에도 있어야 합니다. 이 요소는 SQL/DS나 DB2와 호환성이 없습니다. SQL/DS나 DB2에는 이와 같은 기능이 없기 때문에 사용자가 이들 시스템과의 호환성을 확보하고 싶은 경우, 이 요소를 사용해서는 안됩니다.

 sqlca.sqlext

 현재는 사용되지 않습니다.



실제 프로그램에서 SQLCA를 사용하려면 아래의 예제와 같은 문법을 사용해야 합니다.

EXEC SQL UPDATE EMP
SET ENAME = 'MARK';
WHERE EMPNO = '1';

IF(sql.ca.sqlcode != 0 && sqlca.sqlcode != 1403) {
	printf("[ERROR] UPDATE SQLCODE : [%d] MSG : [%s].\n",
		sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
	exit(0);
}


위의 예제는 EMP 테이블의 데이터를 갱신하면서 발생할 수 있는 오류에 대한 처리를 기술한 것입니다. 위 예제 프로그램은 EMP 테이블의 EMPNO가 '1'인 데이터에 대해 ENAME 컬럼의 값을 갱신하는 프로그램입니다. 업데이트할 데이터가 존재하지 않아 발생하는 오라클 경고 코드인 ORA-1403(NO DATA FOUND)이 아니거나, 그 외의 SQL 오류가 발생하면 화면에 에러 발생임을 알리고 에러 코드와 에러 메시지를 화면에 출력합니다.


물론 1403의 경우만 검출하여 처리하고 싶다든지, SQLCODE가 0이 아닌 모든 경우에 대해서 오류 처리를 하는 것과 같이, 모든 오라클 코드 값에 대한 사용자 정의가 가능합니다. 실제 프로그램에서도 위와 같이 사용하며, SQLCA의 다른 요소들도 위와 동일하게 사용할 수 있습니다.



2-2. ORACA


SQLCA 만으로도 많은 정보를 획득하고 필요한 작업을 처리할 수 있습니다. 실제 현업에서도 SQLCA 만을 참조하여 프로그램을 개발하는 경우가 많이 있습니다. 하지만 SQLCA 영역에서 얻는 정보보다 더 많은 정보를 획득하여 사용하고 싶다면 ORACA의 사용을 고려해 볼 수 있습니다.


ORACA는 SQLCA에서 얻을 수 있는 것보다 조금 더 많은 정보를 얻을 수 있는 데이터 구조입니다. ORACA는 실행 시 발생하는 에러뿐만이 아니라, 성능 통계에 대한 보조 정보도 제공합니다. ORACA는 필수가 아닌 선택이며 SQLCA와 동일하게 Include 부분에서 참조하여 사용할 수 있습니다. SQLCA 파일과 동일한 위치인 오라클 홈 디렉터리 밑의 precomp/public에 'oraca.h' 파일이 있습니다.


ORACA 파일은 SQLCA 파일에서와 같이 다음의 두 가지 방식으로 선언 될 수 있습니다.

EXEC SQL INCLUDE ORACA;
#include <oraca.h>


ORACA는 다음과 같은 정보를 가지고 있습니다.

  • 현재 SQL 문의 텍스트(orastxt)
  • 에러가 있는 파일의 이름(orasfrm)
  • 에러가 있는 행의 번호(oraslnr)
  • SQL 문 보존 플래그(orastxtf): 이 플래그를 설정함으로써 어느 조건으로 문을 보존할 것인지를 선택할 수 있습니다.
      0. 디폴트 값. SQL 문을 보존하지 않습니다.
      1. SQLERROR가 있는 SQL 문만을 보존합니다.
      2. SQLERROR 및 SQLWARN이 있는 문을 보존합니다.
      3. SQL 문을 전부 보존합니다.
  • DEBUG 처리의 사용 허가 플래그: 이 플래그는 0이나 1을 설정할 수 있으며, 1을 설정한 경우에는 모든 DEBUG 처리를 사용할 수 있습니다.
  • 커서 캐시 검사(orahchf)


SQLCA는 프로그램의 시작 부분에 선언되므로 프로그램 안에서 전역적으로 사용될 수 있습니다. 하지만 ORACA는 선언만으로는 전역적 사용이 불가하며, 아래에 제시된 바와 같이 몇 단계의 예비 단계를 거쳐야 ORACA의 기능이 활성화되어 사용이 가능합니다.

  • 단계 1: EXEC SQL INCLUDE 또는 #include를 사용하여 ORACA 선언
  • 단계 2: ORACA = YES로 선행 컴파일러 옵션 설정
  • 단계 3: ORACA에서 필요한 플래그 설정


SQLCA와 달리 ORACA는 선택적 사용이 가능하게 되어 있습니다. 선택적 사용이라는 것은 선언만 하고 사용하지 않을 수도 있다는 것입니다. 이러한 특징 때문에 SQLCA 처럼 선언과 동시에 SQLCA 구조체 멤버가 활성화되어 사용되는 것이 아니라, ORACA를 구성하고 있는 각 구조체에 대한 값의 설정을 통해서만 기능의 활성화가 이루어지며 사용이 가능해집니다.


ORACA 구조체가 어떻게 구성되어 있는지 살펴보겠습니다. 아래의 표를 보면 ORACA 구조체를 개괄적으로 확인할 수 있습니다.


struct {
	char oracaid[8];
	long oracabc;
	long oracchf;
	long oradbgf;
	long orastxtf;
	struct {
		unsigned short orastxtl;
		char orastxtc[70];
	} orastxt;
	struct {
		unsigned short orasfnml;
		char orasfnmc[70]
	} orasfnm;
	long oraslnr;
	long orahoc;
	long oramoc;
	long oracoc;
	long oranor;
	long oranpr;
	long oranex;
} oraca;

각 구조체의 멤버가 어떤 정보를 갖는지는 아래 표를 통해 확인 가능합니다.


 번호/멤버

 설명

 oraca.oracaid

 예약된 필드입니다.

 oraca.oracabc

 예약된 필드입니다.

 oraca.oracchf

 성능 통계를 수집하기 위한 플래그 픨드로 사용자에 의해 설정될 수 있습니다. 만약 이 필드의 값이 0이라면 통계 정보를 수집하지 않음을 의미하며, 항상 수집을 하도록 설정하고자 한다면 1로 설정합니다. 이는 커서 캐시의 일관성을 체크와 관련된 필드입니다.

 oraca.oradbgf

 마스터 플래그로서 사용자에 의해 설정될 수 있습니다. 1이라면 다른 모든 ORACA 필드를 활성화하는 것이며, 그렇지 않은 경우에는 0이 설정됩니다.

 oraca.orastxtf

 가장 최근에 실행된 SQL 문장을 저장할 시점을 보여주기 위해 플래그가 설정합니다. 이 값이 0이라면 디폴트 값으로 SQL 문을 보존하지 않으며, 1인 경우에는 에러가 있는 SQL 문만을 보존하며, 2인 경우에는 에러 및 경고가 있는 SQL문을 보존하며, 3인 경우에는 항상 SQL 문 전부를 보존합니다.

 oraca.orastxt

 마지막 SQL 문장의 텍스트 정보를 가지는 필드로서 에러를 검출하는 경우에 특히 유효합니다. 사용자는 오라클이 해석한 문의 내용을 조사할 수 있습니다. 프리 컴파일러가 해석한 문(CONNECT, FETCH, COMMIT 등)이 ORACA 안에는 나타나지 않습니다. SQL 문의 처음부터 70문자까지 표시됩니다. SQL문의 처음부터 70문자까지 표시됩니다. SQL문의 포맷은 SQLCA의 에러 메시지 포맷과 동일합니다.

 oraca.orastxt.orastxtl

 저장된 SQL 문장의 길이를 나타냅니다.

 oraca.orastxt.orastxtc

 저장된 SQL 문장의 텍스트를 나타냅니다.

 oraca.orasfnm

 하나의 어플리케이션을 위해 복수의 파일을 컴파일하는 경우, 오라클은 에러가 발생한 파일을 나타냅니다. 즉, SQL 문장을 포함하는 파일의 이름을 가지는 필드 영역입니다.

 oraca.orasfnm.orasfnml

 저장된 SQL 문장을 포함하는 파일 이름의 길이가 들어갑니다.

 oraca.orasfnm.orasfnmc

 파일명에 대한 실제 텍스트가 들어갑니다(xxx.pc).

 oraca.oraslnr

 저장된 SQL 문장에 대한 파일 내에서 해당 라인 번호를 가집니다.

 oraca.orahoc

 요청된 최대 오픈 오라클 커서 정보가 들어갑니다.
 oraca.oramoc

 필요한 최대 오픈 오라클 커서 정보가 들어갑니다.

 oraca.oracoc 현재 사용 중인 오라클 커서의 수가 값으로 들어갑니다.
 oraca.oranor 커서 캐시 재할당의 횟수 정보가 들어갑니다.

 oraca.oranpr

 SQL 문의 "해석(parse)" 횟수 정보를 가집니다.

 oraca.nex

 SQL 문의 실행 횟수 정보를 값으로 가집니다.


실제 프로그램에서 ORACA를 사용하려면 앞서 기술한 바와 같이 선언만으로는 충분하지 않습니다. 구조체의 각 멤버에 활성화 및 설정 단계가 필요합니다. 아래의 예제와 같은 처리 과정을 거쳐야 합니다.


#include <stdio.h>
#include <sqlca.h>
#include <oraca.h>

EXEC SQL BEGIN DECLARE SECTION;
    VARCHAR uid[20];
    VARCHAR pwd[20];
EXEC SQL END DECLARE SECTION;

/*
 * ORACA = YES는 ORACA를 사용하기 위해 필히 기술해야 함.
 * */

EXEC ORACLE OPTION (oraca = yes);

void main() {
    /* 에러 및 경고가 발생한 SQL 문장을 저장하도록 설정 */
    oraca.oradbgf = 1;      /* 모든 필드 활성화 */
    oraca.orastxtf = 3;     /* 모든 SQL 문장 보존 */
    oraca.oracchf = 1;      /* 성능 정보 수집 */

    /* Log on oracle */
    strcpy((char *)uid.arr, "userid");
    uid.len = (short) strlen((char *)uid.arr);
    strcpy((char *)pwd.arr, "password");
    pwd.len = (short) strlen((char *)pwd.arr);

    EXEC SQL CONNECT :uid IDENTIFIED BY :pwd;
    if(sqlca.sqlcode != 0) {
        printf("[ERROR] Connect Error SQL_MSG : [%]d\n",
                sqlca.sqlerrm.sqlerrmc);
        exit(0);
    }

    EXEC SQL SELECT empno
            INTO :empno
            FROM scott.emp
            WHERE empno = 1;

    if(sqlca.sqlcode < 0) {
        printf("\nError occurred on SQL statement: %.*s",
                oraca.orastxt.orastxtl, oraca.orastxt.orastxtc);
        printf("\n Contained in file : %.*s",
                oraca.orasfnm.orasfnml, oraca.orasfnm.orasfnmc);
        printf("\n Near line number : %d", oraca.oraslnr);
        printf("\n ORACLE error number : %d", sqlca.sqlcode);
        printf("\n ORACLE error message : %.*s \n",
                sqlca.sqlerrm.sqlerrml, sqlca.sqlerrm.sqlerrmc);
        exit();
    }
    exit();
}

예제의 시작 부분에서 ORACA 기능을 활성화하고 데이터 수집 레벨을 정의하여 ORACA를 사용하는 프로그램입니다. 기능 정의 이후에 수행되는 SQL 문장들은 별도의 기능 재정의가 없는 한 최초에 정의된 기능을 유지합니다.


SQLCA에 비해 설정해야 할 부분이 더 많기는 하지만 조금 더 세부정보를 출력할 수 있기 때문에 더 많은 정보를 얻고자 할 때는 ORACA가 유용합니다. 다만 실제 업무에서는 에러가 발생한 부분이 별도로 기술되게 프로그램을 작성하므로, 굳이 실행 시의 SQL 문장과 같은 정보가 필요치 않은 경우가 많습니다. ORACA를 사용할 때 이러한 부분을 적절히 사용하시기 바랍니다.







2-3. 오류 검출 및 처리


Pro*C에서 오류를 검출하고 이를 처리함에 있어 SQLCA나 ORACA를 사용한다는 것을 알아보았습니다. 그렇다면 실제 프로그램에서 오류의 검출과 검출된 오류에 대해서 어떤 방식으로 처리하는지 살펴보겠습니다.


오류 검출과 처리 방법에는 두 가지 방식이 있습니다. 하나는 명시적 오류 처리 방식이고, 다른 하나는 묵시적 오류 처리 방식입니다.


명시적 오류 처리는 Pro*C 프로그램 안의 SQL 문장 또는 EXEC SQL 문장을 실행한 후에 매번 SQL 문장의 오류를 검사하고 이에 대한 처리를 기술하는 방식입니다. 즉, 발생 가능한 모든 부분을 명시적으로 기술하여 처리하는 방식으로서 가장 많이 사용됩니다. 개발자가 직접 SQL 문장 밑에 기술하게 되므로 원하는 처리 방식이 가능하다는 장점을 가지게 됩니다.


그러나 미처 기술하지 못한 부분에 대한 오류 검출이 불가하며, 개발자가 깜빡 잊고 기술하지 않고 넘어가는 것과 같은 개발자 실수에 무방비로 노출되어 있다고 볼 수 있기 때문에 개발자의 주의가 많이 필요하다고 볼 수 있습니다.


묵시적 오류 처리는 모든 SQL 문장과 EXEC SQL 문장 실행 후에 오류 처리를 기술하는 것이 아니라, 전역적 혹은 지역적으로 오류가 발생했을 때의 처리 방안을 기술하여 모든 오류에 동일한 처리 방식을 적용합니다.


모든 오류 또는 경고에 대해 동일한 처리가 가능하며, 명시적 처리와 같이 매번 SQL 문장마다 기술하는 방식이 아니기 때문에 개발자의 실수에 의한 누락이 발생하지는 않습니다.


두 가지 방식을 설명하다 보니 묵시적 오류 처리가 명시적 오류 처리보다 훨씬 더 좋은 방식처럼 설명되었지만 더 나은 방식은 있을 수 없으며 각자가 처한 환경이나 선호도에 따라 취사 선택하여 사용하면 됩니다. 일반적으로는 간단한 프로그램에서는 명시적 처리를 많이 선호하는 편이며, 코드 라인이 많은 프로그램이거나 팀 코딩을 해야 하는 경우에는 묵시적 처리를 선호하는 편입니다.


물론 이것도 주고나적인 견해이므로 각자의 상황에 맞게 선택해서 사용하면 됩니다. 명시적 오류 처리와 묵시적 오류 처리가 어떻게 다른지 그 차이점은 아래 표와 같습니다.


 묵시적 오류 처리

명시적 오류 처리 

 WHENEVER 문장 사용

 정규 호스트 언어 조건 문장 사용

 전역적으로 실행 시간 조건 검사

 문장 별로 실행 시간 조건 검사

 위치적으로 SQL 문장에 영향

 논리적으로 SQL 문장에 영향



(1) 명시적 오류 처리

명시적 오류 처리 방식에서는 묵시적 처리 방식처럼 전역적 혹은 지역적이라는 단어가 존재하지 않습니다. SQL 문장 마다 오류 처리 구문을 지정하여 오류를 처리합니다. 명시적으로 처리한다는 것은 SQL 문장 10개에 대해 10개의 오류 처리를 할 수도 있고 안 할 수도 있다는 것으로, 오류 처리를 하지 않았다고 하여 실행 상의 문제나 컴파일 상의 문제가 일어나지는 않습니다. 다만 발생 가능한 오류 처리가 누락되어서, 실제로 오류가 발생할 위험은 내재되어 있습니다.


명시적 오류 처리는 각 SQL 문장이 수행된 후에 SQLCA 구조체의 멤버인 'sqlcode'를 통해 확인 가능하며, 추출된 sqlcode 값에 따라 정상과 오류를 구분하여 처리합니다. 오류가 발생한 경우 오류 번호가 아닌 오류 메시지 내용을 확인하고 싶다면 SQLCA 구조체의 또 다른 멤버인 'sqlerrm.sqlerrmc'를 활요하면 됩니다. SQL 문장마다 사용할 수 있으며, 재사용 시 직전의 값은 초기화됩니다.

#include <stdio.h>
#include <sqlca.h>

EXEC SQL BEGIN DECLARE SECTION;
    VARCHAR uid[20];    /* DB USER ID */
    VARCHAR pwd[20];    /* DB PASSSWORD */
    Int empno;
EXEC SQL END DECLARE SECTION;

void main {
    /* Log on oracle */
    strcpy((char *)uid.arr, "userid");
    uid.len = (short)strlen((char *)uid.arr);
    strcpy((char *)pwd.arr, "password");
    pwd.len = (short)strlen((char *)pwd.arr);

    EXEC SQL CONNECT :uid IDENTIFIED BY :pwd;

    if(sqlca.sqlcode != 0) {
        printf("[ERROR] Connect Error SQL_MSG : [%d].\n",
                sqlca.sqlerrm.sqlerrmc);
        exit(0);
    }
    EXEC SQL SELECT empno
            INTO :empno
            FROM scott.emp
            WHERE empno = 1;

    if(sqlca.sqlcode == 1403) {
        printf("[WARNING] NO DATA FOUND.\n");
    }
    exit(0);
}


이 예제는 데이터베이스에 접속하여 scott.emp 테이블에서 사원 번호가 '1'인 데이터를 추출하는 프로그램으로서, 데이터베이스 접속과 데이터 검출 시의 에러 및 경고에 대해 명시적으로 기술하여 오류를 처리하고 있습니다.


이외 같이 처리하는 것을 명시적 오류 처리 방법이라고 합니다. 특별히 어려운 부분은 없지만 SQL 문장 마다 개발자가 직접 기술해야 하기 때문에 개발자의 실수에 의해 기술하지 않고 처리되는 경우가 발생할 수 있습니다. 이는 운영 중에 에러로 연결되어서 운영에 치명적인 해를 가할 수도 있습니다. 그러므로 명시적 오류 처리 방식을 이용할 경우에 항상 유념해야 합니다.



(2) 묵시적 오류 처리

명시적 오류 처리는 프로그램 내 모든 SQL 문장 마다 오류 처리를 해야 하며 개발자의 실수로 오류 처리를 하지 않은 경우에는 잠재적으로 오류 가능성을 내포한 프로그램이 되는 것입니다. 어떤 회사든지 이러한 명시적 오류를 누락한 프로그램은 어렵지 않게 찾을 수 있습니다. 이러한 코딩 누락에 의한 오류와 SQL 문장ㅁ 마다 기술해야 하는 번거러움을 해결하기 위해 묵시적 오류 처리를 이용할 수 있습니다.


묵시적 오류 처리에서는 전역적인 설정과 지역적인 설정이 모두 가능합니다. 전역적으로 처리한다는 것은 프로그램의 시작 전, 즉 호스트 변수 선언 후에 기술하는 것을 의미합니다. 지역적이라 함은 각 함수 별로 프로그램 시작 위치에 'EXEC SQL WHENEVER' 문장을 두는 것을 의미하며. 전역적 혹은 지역적 설정 두 방식 모두 실행 가능한 첫 번째 SQL 문장 이전에 선언하여 사용하게 됩니다.


묵시적으로 에러를 처리하기 위한 구문은 아래와 같습니다.

EXEC SQL WHENEVER condition action;


  • condition

 SQLERROR

 현재 문장이 에러를 발생시킬 때 (SQLCODE < 0)

 NOT FOUND

 현재 SELECT 또는 FETCH 문장 후 검색한 행이 없을 때 (SQLCODE = 1403)

 SQLARNING

 현재 문장이 경고를 발생시킬 때 (SQLWARN[1] = W)


  • action

 GOTO label

 label 문장으로 분기합니다.

 STOP

 즉시 종료합니다.

 CONTINUE

 정상적으로 수행합니다. 실행시간 조건은 무시됩니다.

 DO rountine_call

 routine을 호출합니다. Proc*C/C++ release 2.2 이후에서는 매개변수 전달을 허용합니다.



WHENEVER 문장 사용 시 condition은 정의되어 있으며, 각 condition에 대한 action은 다양한 방식으로 처리 가능합니다. 이번에는 예제를 통해 실제 처리 예를 알아 보겠습니다.


WHENEVER를 사용하여 모든 오류 상황에 특정한 라벨을 지정해서 처리하도록 한 전역적인 묵시적 오류 처리의 예제입니다. 명시적 처리에 비해서 간단하게 사용할 수 있는 이점이 있습니다. 다만 모든 오류에 대해 일괄적인 처리 방식을 적용하기 때문에 특정 오류에 대한 개별 오류 처리는 불가능 합니다.


#include <stdio.h>
#include <sqlca.h>

EXEC SQL BEGIN DECLARE SECTION;
    VARCHAR uid[20];
    VARCHAR pwd[20];
EXEC SQL END DECLARE SECTION;

EXEC SQL WHENEVER SQLERROR GOTO error_position;
/* 전역적으로 설정 */

void main() {
    /* Log on oracle */
    strcpy((char *)uid.arr, "userid");
    uid.len = (short) strlen((char *)uid.arr);
    strcpy((char *)pwd.arr, "password");
    pwd.len = (short) strlen((char *)pwd.arr);

    EXEC SQL CONNECT :uid IDENTIFIED BY :pwd;

    EXEC SQL INSERT INTO emp(empno, ename)
                VALUES(1, 'Kevin');
    EXEC SQL COMMIT WORK RELEASE;

    exit();
    error_postion;
    printf("[ERROR]SQL ERROR CODE : [%d], MSG: [%s].\n",
            sqlca.sqlcode,
            sqlca.sqlerrml.sqlerrmc);
    EXEC SQL ROLLBACK WORK RELEASE;
    exit();
}


아래 예제는 WHENEVER를 전역적이 아닌 지역적으로 사용하여 WHENEVER 구문에 의해 지정된 처리를 main 함수 내에서만 수행하게 하며, 다른 함수에서는 WHENEVER와 상관없이 개별적으로 명시적인 처리가 가능하도록 구성한 프로그램입니다.


#include <stdio.h>
#include <sqlca.h>

EXEC SQL BEGIN DECLARE SECTION;
    VARCHAR uid[20];
    VARCHAR pwd[20];
EXEC SQL END DECLARE SECTION;

void main() {
    EXEC SQL WHENEVER SQLERROR GOTO error_position;
    /* 지역적으로 설정 */
    /* Log on oracle */
    strcpy((char *)uid.arr, "userid");
    uid.len = (short)strlen((char *)uid.arr);
    strcpy((char *)pwd.arr, "password");
    pwd.len = (short)strlen((char *)pwd.arr);

    EXEC SQL CONNECT :uid IDENTIFIED BY :pwd;

    EXEC SQL INSERT INTO emp(empno, ename)
                    VALUES(1, 'Kevin');
    EXEC SQL COMMIT;

    Update_Emp(); /* 내부 함수 호출 */
    exit();
    error_position;
    printf("[ERROR] SQL ERROR CODE: [%d], MSG: [%s].\n",
            sqlca.sqlcode,
            sqlca.sqlerrml.sqlerrmc);
    EXEC SQL ROLLBACK WORK RELEASE;
}

void Update_Emp() {
    EXEC SQL UPDATE emp
        SET ename = 'Martin'
        WHERE empno = 1;
    if(sqlcq.sqlcode != 0 && sq;ca/sq;cpde != 1403) {
        printf("[ERROR] UPDATE CODE : [%d].\n", sqlca.sqlcode);
        EXEC SQL ROLLBACK WORK;
    }
}


명시적 처리든 묵시적 처리든 프로그램을 개발함에 있어 오류 처리 부분을 꼭 기술해야 합니다. 많은 개발자들이 오류 처리의 중요성을 간과하여 오류 처리 부분을 누락흔ㄴ 경우를 많이 접하게 도비니다. 개발의 필수 덕목이 오류 처리라는 것을 꼭 명심하기 바랍니다.