Pro*C 사용방법안내 (Ver 0.9) 이문서는오라클의 Pro*C/C++ Precompiler Programmer s Guide.pdf 문서의일부분을정리한것입니다. 보다자세한사용법은위문서를참고하시길바랍니다. 1
I. 개요..3 II. 환경설정.4 III. Pro*C/C++ 프로그램가이드 5 3.1 용어설명.5 3.2 삽입 SQL 문장의사용..5 3.3 변수선언.5 3.4 선언부 (declare section) 6 3.5 SQL 문의사용법..6 3.5.1 SELECT 문 6 3.5.2 DML 문 7 3.5.3 커서의조작 8 3.5.4 VARCHAR 변수..9 3.5.5 호스트구조체 (host structure)..11 3.5.6 호스트배열 (host array)..12 3.5.7 지시변수 (indicator variable) 14 3.5.8 INCLUDE 문..15 3.6 에러처리..16 3.6.1 WHENEVER문..16 3.6.2 sqlca의사용 19 3.6.3 oraca를이용하는방법..22 3.7 오라클접속및해제 24 3.7.1 접속.24 3.7.2 원격에서접속하는경우 25 3.7.3 접속종료.25 3.8 컴파일및실행 26 IV. 예제설명.27 4.1 Pro*C 예제 27 2
I. 개요 오라클의예비컴파일러 (precompiler) 는응용프로그램안에직접 SQL문을사용할수있게해주는도구이다. 예비컴파일러는소스프로그램을받아들여그속에삽읻 (embed) 된 SQL 문장을표준 Oracle runtime library 호출로바꾸어수정된프로그램소스코드를생성한다. 사용자는이소스코드를일반적인방법으로컴파일하고, 링크하고, 수행한다. 오라클은 C와 C++ 을위해 Pro*C/C++ 이라는예비컴파일러를제공하며일반적으로삽입 SQL문이포함된소스파일의확장자는.pc 이다. 이 pc 파일을예비컴파일러를통하여컴파일하면 C파일이생성된다. Pro*C를이용한응용프로그램의개발절차는아래그림과같다. 시스템에디터 소스프로그램 *.pc file( 삽입 SQL 문포함 ) Pro*C/C++ 예비컴파일러 수정된소스프로그램 *.c file( 삽입 SQL 문을라이브러리호출로변환 ) 컴파일러 (gcc 등 ) 오브젝트프로그램 링커 라이브러리호출해결 Oracle Runtime Library (SQLLIB) 실행프로그램 3
II. 환경설정 Redhat 리눅스를기반으로설명한다. pcscfg.cfg 파일은 ProC를사용하기위한시스템설정파일이다. $ORACLE_HOME/ precomp/admin 에 pcscfg.cfg 파일이있다. 이파일에다음과같은내용을추가한다. sys_include=($oracle_home/precomp/public, /usr/include, /usr/lib/gcc-lib/i386- redhat-linux/egcs-2.91.66/include) include=($oracle_home/precomp/public) include=($oracle_home/rdbms/demo) include=($oracle_home/network/public) include=($oracle_home/plsql/public) ltype=short sys_include 부분이두행에걸쳐있는데, 실제사용할때는한라인으로작성해야한다. /usr/lib/gcc-lib/i386-redhat-linx/egcs-2.91.66/include 부분은각자의리눅스환경에맞도록변경해야한다. /usr/lib/gcc-lib/i386-redhat-linux 디렉토리는대부분존재할것이다. 그다음하위디렉토리를여러분의환경에맞도록수정한다. 4
III. Pro*C/C++ 프로그램가이드 3.1 용어설명 - 호스트프로그램 : 삽입 SQL 문이포함된프로그램 - 호스트언어 : 삽입 SQL을포함한 C, C++, JAVA, COBOL 등을지칭 - 호스트변수 : 삽인 SQL문에서조건에사용되는값을전달하거나검색한값들을프로그램에전달하는변수 3.2 삽입 SQL 문장의사용 응용프로그램상에서, 완전한 SQL문장과완전한 C 문장을혼합해서사용할수있고, C의변수와구조체 (structure) 를 SQL 문에서사용할수있다. 호스트프로그램에서 SQL 문장을사용하려면, EXEC SQL이라는키워드를사용하여시작하고세미콜론으로종료한다. C의변수들을 SQL 문에서사용하려면다른 SQL 필드이름과구별하기위해콜론 (:) 을앞에붙여사용한다. EXEC SQL SELECT empno, ename, job INTO :empno, :ename, :job FROM emp WHERE deptno = :deptno ; 위의 SQL 문에서 SELECT 다음의 empno, ename, job 은 emp 테이블의필드이름을나타내며, INTO 다음의 empno, ename, job은호스트변수를의미한다. 즉위의문장은 emp 테이블에서주어진부서번호 (deptno) 에속하는직원들의번호 (empno), 이름 (ename), 직업 (job) 을가져와서호스트변수 empno, ename, job에저장하는것이다. 3.3 변수선언 호스트변수는아래의표 1 과같은타입으로선언할수있다. 데이타타입 설명 5
Char 한문자 Char[n] N개의문자배열 ( 문자열 ) int 정수 short 작은정수 long 큰정수 float 부동소수점수 ( 단정도형 ) double 부동소수점수 ( 배정도형 ) VARCHAR[n] 가변길이문자열 3.4 선언부 (declare section) 호스트변수를 C 언어의규칙에따라선언부에서선언해야한다. 예비컴파일옵션중 MODE=ORACLE 이면, 특별한선언부에선언할필요가없다. CODE=CPP 옵션이면 (C++ 을사용한다면 ), 반드시선언부가있어야한다. EXEC SQL BEGIN DECLARE SECTION; /* 모든호스트변수를선언 */ char *uid = "scott/tiger"; EXEC SQL END DECLARE SECTION; 선언부에는아래와같은것들을포함할수있다. - 호스트변수 - 호스트변수가아닌 C/C++ 변수 - EXEC SQL INCLUDE 문 - EXEC SQL ORACLE 문 - C/C++ 주석 3.5 SQL 문의사용법 3.5.1 SELECT 문 SELECT 문을실행할떄는 SELECT 문이반환하는데이타행의수를다루어야한다. SELECT 문장은아래와같이구분할수있다. 6
- 어떤행도반환하지않는질의 - 한행만반환하는질의 - 한행이상을반환하는질의 한행이상을반환하는질의는명시적으로선언된커서 (cursor) 나호스트배열의사용을필요로한다. 여기서는우선하나의행만을반환하는 SELECT 문에대해서언급한다. 커서의사용법은아래에서구체적으로다룬다. SELECT 문의예는아래와같다. EXEC SQL SELECT ename, job, sal + 2000 INTO :emp_name, :job_title, :salary FROM emp WHERE empno = :emp_number; 오라클은 INTO 절에있는호스트변수들에반환 SELECT 문의반환값을저장한다. 주의할것은 SELECT 절에있는컬럼의수와 INTO 절에있는호스트변수의수가같아야한다. 3.5.2 DML 문 INSERT, UPDATE, DELETE 문을사용하는방법은아래와같다. INSERT의경우 VALUE 절에적절한호스트변수를전달한다. UPDATE는 WHERE 절과 SET절에호스트변수를명세하여사용할수있고, DELETE는 WHERE 절에호스트변수를명세하면된다. EXEC SQL INSERT INTO emp (empno, ename, sal, deptno) VALUES (:emp_number, :emp_name, :salary, :dept_number); EXEC SQL UPDATE emp SET sal = :salary, comm = :commission WHERE empno = :emp_number; EXEC SQL DELETE FROM emp WHERE deptno = :dept_number ; 7
3.5.3 커서의조작 커서를다루기위해서는아래의명령이필요하다. 삽입 SQL 문 DECLARE OPEN FETCH CLOSE 설명커서의이름을명명하고, SELECT 문과연관시킴질의를수행하고, 결과집합을명시결과집합에서하나의행을가져오고, 커서를이동시킴커서를닫음 아래의예와같이 DECLARE CURSOR문을이용하여 emp_cursor라는이름의커서를정의한다. 커서와연관된 SELECT 문에 INTO 절을포함할수없다. 커서의경우 FETCH 명령에서 INTO 절을사용한다. EXEC SQL DECLARE emp_cursor CURSOR FOR SELECT ename, empno, sal FROM emp WHERE deptno = :dept_number; 커서의선언은다른커서조작명령 (OPEN, FETCH, CLOSE) 보다앞에위치해야한다. 커서조작명령은하나의단위로컴파일되어야한다. 예를들어파일 A에서 DECLARE를하고파일 B에서 OPEN할수없다. 아래의예와같이 emp_cursor 를오픈할수있다. EXEC SQL OPEN emp_cursor; OPEN 명령은결과집합의첫번째행의바로앞을지정하고있는상태이다. FETCH 명령을이용하여결과집합에서행들을가져오고, 호스트변수의결과들을저장하도록한다. FETCH 명령은아래와같이사용할수있다. EXEC SQL FETCH emp_cursor INTO :emp_name, :emp_number, :salary; 여러결과를다가져오기위해서, 보통 FETCH 명령은아래처럼루프속에서사용된다. EXEC SQL WHENEVER NOT FOUND GOTO for (;;) 8
} EXEC SQL FETCH emp_cursor INTO :emp_name, :emp_number, :salary; printf( Name : %s, emp_name); /* 이름출력 */ 결과집합이비었거나더이상의행을가지고있지않다면, FETCH 명령은 no data found 에러를발생한다. 일반적으로위의예처럼무한루프를빠져나오기위해 WHENEVER NOT FOUND를사용하게된다. WHENEVER 문은에러처리에서자세히설명한다. 결과들을다가져왔으면, 자원을해제하기위해커서를종료한다. EXEC SQL CLOSE emp_cursor; int emp_number; char temp[20]; VARCHAR emp_name[20]; /* 호스트변수에값지정 */ printf("employee number? "); gets(temp); emp_number = atoi(temp); printf("employee name? "); gets(emp_name.arr); emp_name.len = strlen(emp_name.arr); EXEC SQL INSERT INTO EMP (EMPNO, ENAME) VALUES (:emp_number, :emp_name); 3.5.4 VARCHAR 변수 가변길이문자열을선언하기위해 VARCHAR 슈도타입을사용한다. VARCHAR2 컬럼의값을처리할때, C의표준문자열보다 VARCHAR를사용하는것이더편리하다. VARCHAR를구조체로생각하면된다. 예를들어아래와같이 VARCHAR로선언하면예비 9
컴파일러는 arr 과 len 을멤버로갖는구조체로확장한다. VARCHAR username[20]; struct unsigned short len; unsigned char arr[20]; } username; VARCHAR를이용하면, SELECT나 FETCH 이후에명시적으로 VARCHAR 구조체의 len 필드를사용할수있다. 따라서아래와같은코드를사용하여 arr 필드에널 (null) 문자를추가하거나, 길이필드를이용하여 strncpy나 printf 함수를사용한다. username.arr[username.len] = 0 ; printf("username is %.*s n", username.len, username.arr); SQL 문에서는, 구조체이름에콜론 (:) 을붙여서 VARCHAR 변수를참조한다. int part_number; VARCHAR part_desc[40]; main() EXEC SQL SELECT pdesc INTO :part_desc FROM parts WHERE pnum = :part_number; 질의가실행된이후에, part_desc.len은 part_desc.arr에저장된문자열의실제길이를저장한다. C 문장에서는, 구조체의접근방식으로각각의필드들을이용한다. printf(" n nenter part description: "); 10
gets(part_desc.arr); /* INSERT나 UPDATE에서 VARCHAR를사용하기전에길이를설정해야한다. */ part_desc.len = strlen(part_desc.arr); 3.5.5 호스트구조체 (host structure) 호스트변수들을저장하기위해 C의구조체를사용할수있다. SELECT나 FETCH 문의 INTO절, INSERT 문의 VALUE 절에서호스트변수를저장하고있는구조체를참조할수있다. 구조체가호스트변수로사용될때, SQL 문에서는구조체의이름만사용되지만각각의필드들은 ORACLE에데이타를보내거나 ORACLE로부터데이타를받게된다. 아래는 emp 테이블에호스트구조체를사용하여데이타를추가하는예제이다. typedef struct char emp_name[11]; /* 컬럼길이보다하나크게선언 */ int emp_number; int dept_number; float salary; } emp_record; /* emp_record 타입의변수선언 */ emp_record new_employee; strcpy(new_employee.emp_name, "CHEN"); new_employee.emp_number = 9876; new_employee.dept_number = 20; new_employee.salary = 4250.00; EXEC SQL INSERT INTO emp (ename, empno, deptno, sal) VALUES (:new_employee ); 구조체에선언된필드들의순서와 SQL 문에서관련된컬럼들의순서가일치해야된다. 아래의예는순서가맞지않아서에러를발생하는예이다. struct int empno; float salary; 11
char emp_name[10]; } emp_record; /* 구조체필드의순서와대응되는컬럼순서의불일치 */ SELECT empno, ename, sal INTO :emp_record FROM emp; 3.5.6 호스트배열 (host array) 배열을사용하면, 하나의 SQL 문으로배열의전체원소를조작할수있다. 따라서특히네트워크환경에서, ORACLE 통신오버헤드가현저하게줄어든다. 예를들어, 300명의직원에관한정보를 EMP 테이블에추가하길원한다고하자. 배열이없다면 300개의 INSERT 문을실행해야한다. 배열을사용한다면, 하나의 INSERT 문으로가능하다. 배열은다음과같이선언한다. char emp_name[50][10]; int emp_number[50]; float salary[50]; VARCHAR v_array[10][30]; 문자배열 ( 스트링 ) 을제외하고는, SQL 문에서참조할수있는배열은 1차원으로제한되어 있이다.. int hi_lo_scores[25][25]; /* 허용되지않음 */ VARCHAR name[2][20]; /* 허용됨 */ char phone[2][14]; /* 허용됨 */ SELECT 문의 INTO 절에호스트배열을사용할수있다. SELECT 가반환하는행의최대수를안다면, 최대수만큼의호스트배열을선언하면된다. 아래의예와같이사용할수있다. char emp_name[50][20]; int emp_number[50]; float salary[50]; EXEC SQL SELECT ENAME, EMPNO, SAL INTO :emp_name, :emp_number, :salary FROM EMP 12
WHERE SAL > 1000; 위의예에서, SELECT 문은최대 50개의행만반환한다. 만약 50개의행이상을반환한다면, 위의방법으로모든행을가져올수없다. 더큰배열을사용하거나커서를선언한후 FETCH 문을사용해야한다. 호스트배열을 INSERT 문에서사용하는예는아래와같다. char emp_name[50][20]; int emp_number[50]; float salary[50]; /* 호스트배열에데이타지정 */ EXEC SQL INSERT INTO EMP (ENAME, EMPNO, SAL) VALUES (:emp_name, :emp_number, :salary); UPDATE 문에도호스트배열을사용할수있다. int emp_number[50]; float salary[50]; /* 호스트배열에데이타지정 */ EXEC SQL UPDATE emp SET sal = :salary WHERE EMPNO = :emp_number; 호스트배열을호스트구조체의필드로사용할수있다. struct char emp_name[3][10]; int emp_number[3]; int dept_number[3]; } emp_rec; strcpy(emp_rec.emp_name[0], "ANQUETIL"); strcpy(emp_rec.emp_name[1], "MERCKX"); strcpy(emp_rec.emp_name[2], "HINAULT"); emp_rec.emp_number[0] = 1964; emp_rec.dept_number[0] = 5; emp_rec.emp_number[1] = 1974; emp_rec.dept_number[1] = 5; emp_rec.emp_number[2] = 1985; emp_rec.dept_number[2] = 5; 13
EXEC SQL INSERT INTO emp (ename, empno, deptno) VALUES ( :emp_rec); 3.5.7 지시변수 (indicator variable) 모든호스트변수에부가적인지시변수를관련시킬수있다. 일반적으로지시변수는입력으로사용되는호스트변수에 NULL을할당하고, 출력으로사용되는호스트변수에 NULL 이오거나잘려진값이저장되는지검사하기위해사용된다. 모든지시변수는 2 바이트숫자형 (short) 로선언되어야한다. SQL에서사용법은아래와같다. :host_variable INDICATOR :indicator_variable 또는 :host_variable:indicator_variable 지시변수값의의미는아래표와같다. 값의미 0 연산이성공적으로수행됨 -1 NULL이반환되거나, 삽입되거나, 변경됨 -2 호스트변수에잘려진값이저장되고, 컬럼값의원래길이도알수없음 > 0 호스트변수에잘려진값이저장되고, 반환된값은컬럼값의원래길이를의미 아래의예와같이지시변수 SELECT 문에서사용할수있다. EXEC SQL BEGIN DECLARE SECTION; int emp_number; float salary, commission; short comm_ind; /* 지시변수 */ EXEC SQL END DECLARE SECTION; char temp[16]; float pay; /* SQL 문에서사용되지않는변수들 */ printf("employee number? "); gets(temp); emp_number = atof(temp); EXEC SQL SELECT SAL, COMM INTO :salary, :commission:ind_comm 14
FROM EMP WHERE EMPNO = :emp_number; if(ind_comm == -1) /* commission 이 NULL인지확인 */ pay = salary; else pay = salary + commission; INSERT에 NULL을주기위해서아래와같이사용할수있다. 아래의예는사용자의입력에따라 emp_number의값을유연하게입력하는것을보여준다. printf("enter employee number or 0 if not available: "); scanf("%d", &emp_number); if (emp_number == 0) ind_empnum = -1; else ind_empnum = 0; EXEC SQL INSERT INTO emp (empno, sal) VALUES ( :emp_number:ind_empnum, :salary); 3.5.8 INCLUDE 문 삽입 SQL을이용하여응용프로그램을작성하기위해서는 sqlca.h나 oraca.h 파일을포함해야한다. 아래와같은방법으로포함할수있다. #include <sqlca.h> #include <oraca.h> 또는 EXEC SQL INCLUDE sqlca; EXEC SQL INCLUDE oraca; 여기서 sqlca는 SQL Communication Area를뜻하며내부적으로는 sqlca라는구조체가선언된화일이다. 데이타베이스는 SQL문의실행이끝날때마다 sqlca 구조체에알맞은값을지정하여호스트프로그램에서참조할수있게해, 데이타베이스와호스트프로그램사이의통신역할을하게한다. sqlca에담겨지는정보는경고메시지, 에러발생여부및에러코드등이다. 자세한내용은다음절에서설명하고있다. oraca(oracle Communication Area) 는 sqlca라는 SQL 표준에덧붙여서오라클에서확장한구조체로서 sqlca에서제공되는정보외에추가로필요한정보를호스트프로그램에게제공하기위한구조체이다. 이 oraca를사용하기위해서는다음과같은문장을통해 ORACA=YES라는값을지정하여 15
oraca 를활성화시켜야한다. EXEC ORACLE OPTION (ORACA=YES); 그러나, ORACA를사용하면프로그램실행시추가작업으로인한부담이늘어나므로보통의경우에는위의문장을생략해서기본값인 ORACA=NO로지정하여 oraca를활성화시키지않는다. 3.6 에러처리 Pro*C/C++ 을이용한응용프로그램작성에서 SQL문의실행에따른에러처리및진단의방법은크게세가지로나눌수있다. - WHENEVER문을이용하는방법 - sqlca를이용하는방법 - oraca를이용하는방법 3.6.1 WHENEVER 문 WHENEVER 문의구문은다음과같다. EXEC SQL WHENEVER <condition> <action>; <condition> 에는아래의표와같은것을사용할수있다. 조건의미 SQLWARNING 경고메시지가발생한경우 SQLERROR 에러가발생한경우 NOT FOUND WHERE절의조건을만족하는행을발견할수없거나, SELECT INTO나 FETCH가어떤행도반환하지않은경우 <action> 에는아래의표와같은것을지정할수있다. 조치 CONTINUE DO DO BREAK 의미프로그램은다음문장을수행함. 디폴트동작으로 WHENEVER를쓰지않은경우와같음에러처리함수를수행. 에러처리함수수행후실패한 SQL문의다음문장으로제어가넘어감실제적인 break 문장. 루프에서사용 16
DO CONTINUE 실제적인 continue 문장. 루프에서사용 GOTO label_name 레이블로제어이동. STOP 프로그램수행을종료하고, 트랜잭션은롤백됨 WHENEVER문이정의되면그이후의 SQL문을실행할때마다 < 조건 > 에해당되는지를검사하고해당되면사건이일어날때마다 < 조치 > 에정의된동작을취하게된다. 이 WHENEVER문은다른조건이나조치로구성된 WHENEVER문을만날때까지계속유효하게동작한다. WHENEVER문의사용예를보자. main().. EXEC SQL DECLARE emp_cursor CURSOR FOR /* 커서설정 */ SELECT empno, ename FROM emp; EXEC SQL OPEN emp_cursor; /* 커서를연다 */ } EXEC SQL WHENEVER NOT FOUND DO break; while(1) EXEC SQL FETCH emp_cursor INTO :emp_number, :emp_name;. } EXEC SQL CLOSE emp_cursor; /* 커서를닫는다 */. 위예의 while루프에서 FETCH문이실행될때더이상레코드가발견되지않으면 WHENEVER문의조치에의해루프를빠져나오게된다. main() EXEC SQL WHENEVER SQLERROR DO sql_error(" 에러메시지 "); EXEC SQL SELECT. 17
} EXEC SQL UPDATE.. sql_error(char * errmasage) printf(" 에러 %s n", errmasage); exit(-1); } 위예에서는 WHENEVER문이정의된이후에실행되는 SQL문에서에러가발생하면 sql_error() 라는함수를실행하게된다. 즉 SELECT가실패해도 sql_error() 가호출되고, UPDATE가실패해도 sql_error() 가호출된다. 또다음과같은예를보자. /* 무한루프에빠지는예 */ EXEC SQL WHENEVER NOT FOUND GOTO no_more; while(1) EXEC SQL FETCH emp_cursor INTO :emp_number, :emp_name; } no_more: EXEC SQL DELETE FROM emp WHERE empno=:emp_number;. } 위의예를보면 FETCH에서더이상해당되는행이없는경우 no_more로넘어가게되고 no_more 다음의 DELETE문에서다시 NOT FOUND조건에해당되는경우다시 no_more 로넘어가서무한루프에빠지게되는것을알수있다. 아래의예와같이 GOTO 를다시설정하여무한루프를방지한다. EXEC SQL WHENEVER NOT FOUND GOTO no_more; 18
for (;;) EXEC SQL FETCH emp_cursor INTO :emp_name, :salary; } no_more: EXEC SQL WHENEVER NOT FOUND GOTO no_match; EXEC SQL DELETE FROM emp WHERE empno = :emp_number; no_match: 3.6.2 sqlca 의사용 SQLCA는구조체이다. SQLCA의필드는에러, 경고, SQL이수행될때마다오라클이갱신하는상태정보를담고있다. 따라서 SQLCA는가장최근의 SQL 연산의결과를반영한다. SQLCA를사용하기위해서는아래와같은문장을포함해야한다. EXEC SQL INCLUDE SQLCA; 또는 #include <sqlca.h> sqlca 구조체의구조는다음과같다. struct sqlca char sqlcaid[8]; /* "SQLCA" 문자스트링 */ long sqlabc; /* slqca구조체의길이 */ long sqlcode; /* 에러코드 */ struct unsigned short sqlerrml; /* 에러메시지길이 */ char sqlerrmc[70]; /* 에러메시지내용 */ } sqlerrm; char sqlerrp[8]; /* reserved */ long sqlerrd[6]; /* sqlerrp[0] : reserved sqlerrp[1] : reserved 19
sqlerrd[2] : 수행된행의개수 sqlerrd[3] : reserver sqlerrd[4] : 파싱에러옵셋 sqlerrd[5] : reserved */ char sqlwarn[8]; /* sqlwarn[0] : 경고플래그 sqlwarn[1] : 문자스트링이절단된경우 sqlwarn[2] : 안쓰임. sqlwarn[3] : SELECT문에서필드개수와 INTO문의호스트변수개수가일치하지않음 sqlwarn[4] : DELETE 또는 UPDATE문에서 where절이없음. sqlwarn[5] : reserved sqlwarn[6] : 안쓰임. sqlwarn[7] : 안쓰임. */ char sqlext[8]; /* reserved */ }; sqlca.sqlcode는 SQL문실행시발생한 에러코드이며그값에따라다음과같은뜻이있 다. Sqlcode 값 의미 0 성공 양수 SQL문의실행에는성공했지만예외상황이발생한경우이다. SELECT INTO 또는 FETCH문에서 WHERE절의조건에만족하는행이없는경 우에양수의 sqlcode가반환된다. 음수 SQL문의실행에실패한경우이다. 따라서대부분의경우트랜잭션을복 귀 (rollback) 하여야한다. sqlca.sqlcode를통해알아낸에러코드의의미와원인및조치를알고싶으면 prompt상에서 oerr라는명령을사용한다. oerr 명령은두개의인수를사용한다. 첫번째는에러메시지를발생시킨요소의코드 ( 예를들어 ora는오라클서버 ) 이며두번째인수는에러코드번호이다. prompt> oerr ora 에러코드의절대값 20
여기서에러코드는절대값을입력하여야한다. 즉, 다음과같이 -942 에러가발생하였다면 942를입력하여야한다. prompt> oerr ora 942 sqlwarn[0] 부터 sqlwarn[7] 까지는경고에관한요소이며이들을참조함으로써보다상세한제어를할수있다. sqlca.sqlerrd[2] 는 SQL문의영향을받은행의개수를담고있다. 즉, SELECT, DELETE, UPDATE문등의실행결과가반영된행의개수를말하며 sqlca.sqlcode와더불어가장많이참조되는요소이다. sqlca.sqlerrm.sqlerrmc는에러메시지를담고있으며이를참조하여다음과같이에러메시지를출력할수있다. sqlca.sqlerrm.sqlerrmc[sqlca.sqlerrm.sqlerrml]=' 0'; printf("%s", sqlca.sqlerrm.sqlerrmc); sqlerrmc에는최대 70자까지의에러메시지만저장되므로, 에러메시지전체를알고싶으면 sqlglm() 을사용할수있다. sqlglm() 의구문은다음과같다. sqlglm(message_buffer, &buffer_size, &message_length); 여기서각매개변수의뜻은다음과같다. - message_buffer : 에러메시지가담겨질문자배열. - buffer_size : message_buffer의크기. - message_length : message_buffer에담겨진메시지의크기. 예를보자. main() char msg_buffer[100]; int buf_size = 100; int msg_length; 21
} if (sqlca.sqlcode!=0) sqlglm( msg_buffer, &buf_size, &msg_length); msg_buffer[msg_length] = ' 0'; printf("%s", msg_buf); } 3.6.3 oraca 를이용하는방법 SQLCA가제공하는런타임에러와상태변화와같은정보를더자세히알고싶을때, ORACA를사용한다. ORACA는확장된진단툴을제공한다. 그러나 ORACA는런타임오버헤드를추가하므로, ORACA를선택적으로사용한다. ORACA를선언하기위해서는아래와같은문장을프로그램에추가해야한다. EXEC SQL INCLUDE ORACA; 또는 #include <oraca.h> ORACA 를활성화하려면, ORACA 옵션을명세해야한다. EXEC ORACLE OPTION (ORACA=YES); ORACA 구조체의정보는다음과같다. struct oraca char oracaid[8]; /* "ORACA" 문자스트링 */ long oracabc; /* oraca의크기 */ long oracchf; /* 커서캐쉬일관성플래그 */ long oradbgf; /* 마스터디버그플래그 */ long orahchf; /* 히프일관성플래그 */ long orastxtf; /* Save-SQL-statement 플래그 */ struct unsigned short orastxtl; /* 현재 SQL문의길이 */ char orastxtc[70]; /* 현재 SQL문의내용 */ 22
} orastxt; struct char orasfnmc[70]; /* 현재 SQL문을담고있는화일이름 */ unsigned short orasfnml; /* 화일이름의길이 */ } orasfnm; long oraslnr; /* 현재 SQL문이위치한행번호 */ long orahoc; /* 요청한 MAXOPENCURSORS 값 */ long oramoc; /* 최대로 open할수있는커서개수 */ long oracoc; /* 현재사용되고있는커서개수 */ long oranor; /* 재할당된커서캐쉬의개수 */ long oranpr; /* 파싱된 SQL문의개수 */ long oranex; /* 실행된 SQL문의개수 */ }; ORACA를사용한예는아래와같다. #include <sqlca.h> #include <oraca.h> main() char SQLSTATE[6]; EXEC SQL WHENEVER SQLERROR DO sql_error("oracle error"); EXEC SQL CONNECT :userid; EXEC ORACLE OPTION (ORACA=YES); oraca.oradbgf = 1; /* debug 연산활성화 */ oraca.oracchf = 1; /* 커서캐시통계정보수집 */ oraca.orastxtf = 3; /* 항상 SQL문저장 */ printf("enter department number: "); ); void sql_error(char *errmsg) char buf[6]; 23
} strcpy(buf, SQLSTATE); EXEC SQL WHENEVER SQLERROR CONTINUE; EXEC SQL COMMIT WORK RELEASE; if (strncmp(errmsg, "Oracle error", 12) == 0) printf(" n%s, sqlstate is %s n n", errmsg, buf); else printf(" n%s n n", errmsg); printf("last SQL statement: %.*s n", oraca.orastxt.orastxtl, oraca.orastxt.orastxtc); printf(" nat or near line number %d n", oraca.oraslnr); printf(" ncursor Cache Statistics n------------------------ n"); printf("maximum value of MAXOPENCURSORS: %d n", oraca.orahoc); printf("maximum open cursors required: %d n", oraca.oramoc); printf("current number of open cursors: %d n", oraca.oracoc); printf("number of cache reassignments: %d n", oraca.oranor); printf("number of SQL statement parses: %d n", oraca.oranpr); printf("number of SQL statement executions: %d n", oraca.oranex); exit(1); 3.7 오라클접속및해제 3.7.1 접속 오라클에질의를하거나데이타를조작하기전에데이타베이스에접속을해야한다. 접속을하기위해 CONNECT 문을사용한다. 아래는간단하게접속을하는방법을보여준다. 처음예에서 username이나 password는 char 또는 varchar 호스트변수이다. 밑의예에서 usr_pwd는사용자명과암호를 scott/tiger 처럼슬래쉬 (/) 로구분해서포함해야한다. EXEC SQL CONNECT :username IDENTIFIED BY :password ; 또는 EXEC SQL CONNECT :usr_pwd ; 아래의예는오라클의사용자 scott( 암호는 tiger) 로접속하는코드이다. 24
char *username = "SCOTT"; char *password = "TIGER"; EXEC SQL WHENEVER SQLERROR EXEC SQL CONNECT :username IDENTIFIED BY :password; 여기서 CONNECT 문에문자열을직접주면에러가발생한다. EXEC SQL CONNECT scott IDENTIFIED BY tiger ; /* 에러발생!!! */ 3.7.2 원격에서접속하는경우원격에서오라클서버를접속하려면 ( 예를들어 Pro*C/C++ 프로그램은 db.snu.ac.kr에있고, 오라클서버는 dbpub.snu.ac.kr에있는경우 ), 먼저 tnsname.ora 파일을편집한다. tnsname.ora 는 $ORACLE_HOME/network/admin ( 리눅스에서설치한경우 ) 밑에위치한다. dbpub = ( DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = dbpub.snu.ac.kr)(port = 1521)) (CONNECT_DATA = (SERVICE_NAME = ora9i)) ) % 주의 : 이설정은서버마다다르기때문에, 관리자가알려준사항을따라야한다. 원격에서접속할때 CONNECT 문에옵션을하나추가한다. /* 호스트변수설정 */ char username[10] = "scott"; char password[10] = "tiger"; char db_string[20] = "dbpub"; /* 원격 DB에접속 */ EXEC SQL CONNECT :username IDENTIFIED BY :password USING :db_string; 3.7.3 접속종료 오라클과의접속을해제할경우에는다음과같이두가지문장을사용할수있다. 25
1 EXEC SQL COMMIT 1 WORK RELEASE; 2 EXEC SQL ROLLBACK WORK RELEASE; 첫번째문장은프로그램내의 SQL문을 commit하고접속을해제하는경우이고두번째문장은 rollback한뒤접속을해제하는경우이다. RELEASE는자원을해제하고 log off하라는의미이다. 일반적으로프로그램이정상적으로수행된경우에는첫번째문장을이용하여접속을끊고, 에러발생시프로그램이종료되는경우에는두번째문장으로접속을끊는다. 3.8 컴파일및실행 sample1.pc 파일을 proc 를이용하여예비컴파일한다. $proc sample1.pc 예비컴파일하면 sample1.c 파일이생성된다. sample1.c 파일을 gcc를이용하여컴파일한다. 이때오라클관련라이브러리들을포함한다. $gcc -o sample1 -I$ORACLE_HOME/precomp/public -L$ORACLE_HOME/lib -lclntsh sample1.c -I( 대문자 i) 는 gcc의옵션으로헤더파일의디렉토리경로를지정한다. 여기서는 sqlca.h와 oraca.h 등의오라클관련헤더파일들의위치한 $ORACLE_HOME/precomp/public을포함하도록하고있다. -L은 gcc의옵션으로라이브러리가위치한디렉토리경로를지정한다. 여기서는 $ORACLE_HOME/lib를포함하고있다. -l( 소문자 L) 은 gcc의옵션으로라이브러리를지정한다. 여기서는 clntsh 라이브러리를가리킨다. $ORACLE_HOME/lib 밑에있는 libclntsh.so 라는공유라이브러리 (shared library) 를포함하라는의미이다. 컴파일이끝나면, sample1 이라는실행파일이생성된다. 1 COMMIT 과 ROLLBACK 은트랜잭션에서나오는용어이다. 트랜잭션의의미는 DB 책들을참고한다. COMMIT 과 ROLLBACK 에대한자세한사용법은 ProC.pdf 의 3 장을참고한다. 26
IV. 예제설명 4.1 Pro*C 예제 오라클에서제공하는 sample3.pc 파일을설명한다. 이예제는커서의사용법을보여주고있다. #include <stdio.h> #include <string.h> #include <stdlib.h> #include <sqlca.h> #define NAME_LENGTH 8 #define ARRAY_LENGTH 5 /* 사용자명 */ char *username = "SCOTT"; char *password = "TIGER"; /* 호스트구조체의선언 */ struct int emp_number[array_length]; char emp_name[array_length][name_length]; float salary[array_length]; } emp_rec; void print_rows(int n) int i; printf(" nnumber Employee Salary"); printf(" n------ -------- ------- n"); 27
for (i = 0; i < n; i++) printf("%d %s %8.2f n", emp_rec.emp_number[i], emp_rec.emp_name[i], emp_rec.salary[i]); } void sql_error(char* msg) EXEC SQL WHENEVER SQLERROR CONTINUE; printf(" n%s", msg); printf(" n%.70s n", sqlca.sqlerrm.sqlerrmc); } EXEC SQL ROLLBACK WORK RELEASE; exit(exit_failure); void main() int num_ret; /* 반환된행의수 */ /* 오라클에접속 */ EXEC SQL WHENEVER SQLERROR DO sql_error("connect error:"); EXEC SQL CONNECT :username IDENTIFIED BY :password; printf(" nconnected to ORACLE as user: %s n", username); EXEC SQL WHENEVER SQLERROR DO sql_error("oracle error:"); /* 커서선언 */ EXEC SQL DECLARE c1 CURSOR FOR SELECT empno, ename, sal FROM emp; EXEC SQL OPEN c1; num_ret = 0; /* 초기하 */ 28
/* fetch 루프 */ EXEC SQL WHENEVER NOT FOUND DO break; for (;;) EXEC SQL FETCH c1 INTO :emp_rec; } print_rows(sqlca.sqlerrd[2] - num_ret); num_ret = sqlca.sqlerrd[2]; /* num_ret 재설정. */ /* 마지막 ftech후남아있는행이있다면, 남아있는행들을출력 */ if ((sqlca.sqlerrd[2] - num_ret) > 0) print_rows(sqlca.sqlerrd[2] - num_ret); EXEC SQL CLOSE c1; printf(" nau revoir. n n n"); } /* 접속해제 */ EXEC SQL COMMIT WORK RELEASE; exit(exit_success); 29