본문 바로가기

프로그래밍/리눅스

[ORACLE] Pro*C 간단 매뉴얼





출처 : http://thx4alice.tistory.com/396

Pro-C 간단 매뉴얼


---------------
  2000. 5. 23
---------------

+++++++++++
  ProC...
+++++++++++

-. 오라클 데이타베이스 내의 테이블에 존재하는 레코드들을 조회,수정 등의 데이타 처리를 하고, C프로그램으로 전환하기 위해 사용함.

-. C와의 차이점
        -. 외형상의 차이 : 확장자가 .pc로 끝난다.
        -. 기본적으로 C문법을 따르며, C프로그램코드에 DB에 접속하여 레코드들을 처리하는 과정이 추가되어 작성된다.
           즉, C로 작성된 프로그램코드에 오라클의 SQL의 DML or DDL명령문이 'EXEC'구문과 결합된 형태가 추가된 것임을 알 수 있다.
        -. 변수선언에서 C언어에서는 사용하지 않는, .arr과 .len이라는 변수가 컴파일된 C source코드에 생성되어 있는 것을 확인할 수 있다.

-. 컴파일 과정
        -. filename.pc -> filename.c -> filename.o -> filename*(생성된 오브젝트 파일을 링크해서 실행파일을 만드는 과정이다.)
       
-. 실제 컴파일
        -. makefile 위치
        $ORACLE_HOME에 proc/에 보면 proc.mk파일이 있다.
       
        이중에서
        1) USERID = uinfo/mation
         -. 오라클 DB에 접속할때 사용할 사용자 아이디와 비밀번호 지정
        
        2) DEST = /home/elfsun/simple/bin
        -. 컴파일후 실행파일을 복사할 디렉토리
       
        3) COMM_OBJ = /home/elfsun/simple/lib/lib1.o\
                      /home/elfsun/simple/lib/lib2.o\
                      /home/elfsun/simple/lib/lib3.o\
                      /home/elfsun/simple/lib/lib4.o
        -. 컴파일시 참조로할 오브젝트 파일들.
        \(백슬레시)로 구분함.
       
        4) all : $(PROGRAM)
        -. 프로그램의 이름을 지정할 수 있다. 이것은 프로그램의 소스 이름이 되며, 컴파일후 생성될 실행 파일의 이름이 되기도 한다.
       
        5) install :
        -. 컴파일후 생성된 실행 파일을 복사해주는 작업을 수행
       
        6) clean :
        -. 컴파일 과정에서 생성되는 C파일과 오브젝트 파일들을 삭제할때 수행
       
        이들중 수행할 각 명령들은 '@'바로 뒤에 작성해 줄 수 있다.
       
        [실제 컴파일 수행 - makefile이름이 proc.mk인 경우]
       
        1) 실제 컴파일을 하기 위해
                $ make -f proc.mk
       
        2) 컴파일한 실행 파일을 ~/bin/ 디렉토리에 복사하기 위해
                $ make -f proc.mk install
       
        3) 컴파일 후 생성되는 오브젝트 파일과 c파일을 삭제하기 위해
                $ make -f proc.mk clean
               
-. SQLCA관련 변수들...
        -. sqlca.sqlerr[0] : fetch를 사용하여 쿼리된 레코드의 건수를 리컨시켜 주는 역할을 하는 변수이다.
        -. sqlca.sqlwarn : 경고에 대한 정보들을 지니고 있는 변수이다.
        -. sqlca.sqlerrm.sqlerrmc : sql 명령수행중 에러가 발생했을때, 발생한 에러에 대한 내용과 오류 코드번호에 관한 정보를 출력시켜 준다.
           ---------------------------------------
        -. sqlca.sqlcode값        내 용
           ---------------------------------------
           0                sql문이 정상적으로 수행
          
           1403                유일하게 양수값을 갖는 에러코드임.
                           조건에 맞는 데이타가 하나도 없을때.
          
           -1405        조회된 값중 null값이 있을때
           ---------------------------------------


-. host변수 선언
       
        -. 호스트변수는 데이타베이스에서 데이타 처리 결과를 저장하기 위해 사용되며, 응용 프로그램내에서는
        변수로서의 역할을 한다. 따라서 호스트 변수를 선언할 때에는 데이타베이스 내에서 사용되는 칼럼의
        데이타 타입과 그 크기를 고려하여 선언되어야 하며, 프리컴파일 과정을 통해 응용프로그램내에서도
        사용가능한 형태이어야 한다.

        ex)
        EXEC SQL BEGIN DECLARE SECTION;
                varchar username[16], userarea[10];
                int usrno, usrage;
        EXEC SQL END DECLARE SECTION;          
       
        [주의]        
        1) 호스트 변수 선언시에 오라클 예약어를 사용하지 말것.
        2) 호스트 변수에서는 null처리를 해주지 않기 때문에 응용 프로그램에서 반드시 null처리를 해주어야 한다.
           단, 호스트 변수가 숫자형인 경우에는 예외이다.
          

-. DB접속

        방법1>
        EXEC SQL CONNECT elfsun
                INDENTIFIED BY 1234;
               
        방법2>
        EXEC SQL BEGIN DECLARE SECTION;
                varchar userid[8], passwd[8];
        EXEC SQL END DECALRE SECTION;          
       
        strncpy(userid.arr,"elfsun",6);
        strncpy(passwd.arr,"1234",4);
       
        EXEC SQL CONNECT :userid;
                IDENTIFIED BY :passwd;
       
        -. 일반적으로 방법2를 더 선호함.
        -. 방법2를 쓸때에 변수명 앞에는 반드시 :를 붙여서 사용하고 있는 변수가 호스트 변수임을 명시해야 한다.
       

-. CURSOR이용 : 쿼리문으로 조회되는 레코드가 한 건 이상일때 사용함.

        형식>        
        1) EXEC SQL PREPARE statement FROM variable;  (쿼리하고자 하는 sql문장 정의)
        2) EXEC SQL DECLARE cursor_name CURSOR FOR statement; ( 사용할 커서 선언 )
        3) EXEC SQL OPEN cursor_name;   ( 선언한 커서 열기 )        
        4) EXEC SQL FETCH cursor_name INTO :host_variable1, :host_variable2, :host_variable3, ....;  ( 열어둔 커서 실행 )
        5) EXEC SQL CLOSE cursor_name;  ( 열었던 커서 닫음 )
       

        1) EXEC SQL PREPARE statement FROM variable;  (쿼리하고자 하는 sql문장 정의)
                ex)
                char SqlCmd[100];    (변수 SqlCmd를 선언)
                memset(SqlCmd,0x00,sizeof(SqlCmd));  ( memset함수를 이용하여 초기화 작업함)
                sprintf(SqlCmd,"SELECT usrno, usrname, usrarea FROM forumlist");  (쿼리문을 SqlCmd에 대입함)
                EXEC SQL PREPARE st_1 from :SqlCmd; (EXEC..문장을 이용하여 쿼리할 구조를 정의함)

        2) EXEC SQL DECLARE cursor_name CURSOR FOR statement; ( 사용할 커서 선언 )
                형식1>
                EXEC SQL DECLARE cursor_name CURSOR FOR statement;
                형식2>
                EXEC SQL DECLARE cursor_name CURSOR FOR select 문장;
               
                -. cursor_name : 사용할 커서명
                ex)
                EXEC SQL DECLARE CUR_1 CURSOR FOR ST_1; (ST_1은 PREPARE에서 정의한것과 동일해야 함)
                EXEC SQL DECLARE CUR_1 CURSOR FOR select usrno, username, userarea From forumlist;

        3) EXEC SQL OPEN cursor_name;   ( 선언한 커서 열기 )
                ex)
                EXEC SQL OPEN CUR_1;
               
        4) EXEC SQL FETCH cursor_name INTO :host_variable1, :host_variable2, :host_variable3, ....;  ( 열어둔 커서 실행 )
                ex)
                EXEC SQL BEGIN DECLARE SECTION;
                        varchar usrname[16], usrarea[10];
                        int usrno;
                EXEC SQL END DECLARE SECTION;
                        ......
                       
                EXEC SQL FETCH CUR_1 INTO :usrno, :usrname, :userarea;
               
                -. 여기서 사용하는 조회된 칼럼들과 1대1로 대응되어야 한다.
                또한 사용될 호스트 변수들은 이미 이전에 선언이 되어 있어야만 사용이 가능하다. 칼럼의 데이타 타입과 크기가
                일치해야함에도 주의.

        5) EXEC SQL CLOSE cursor_name;  ( 열었던 커서 닫음 )
                ex)
                EXEC SQL CLOSE CUR_1;


-. 오류 처리

        1) whenever sqlerror 구문
                형식)
                ① EXEC SQL WHENEVER SQLERROR GOTO stop_st;
               
                ② EXEC SQL WHENEVER SQLERROR GOTO STOP;
               
                ③ EXEC SQL WHENEVER SQLERROR GOTO CONTINUE;
                       
                -. 위 문장들은 SQL문장의 수행 결과 리턴되는 sqlca.sqlcode의 값이 음수로서, 에러를 발생시키는 경우, 더이상
                프로그램을 진행시키지 않도록 stop_st루틴(①번에 해당)으로 분기하여 프로그램의 에러처리를 하도록 하는 문장임.
                stop_st대신 STOP나 CONTINUE를 쓰기도 한다.
               
                -. 또한 실행시켰던 SQL문장들이 레코드의 값 자체의 변경과 관련이 있는 문장들, 즉 INSERT, DELETE, UPDATE와 같은
                SQL문장들이었다면, 'EXEC SQL ROLLBACK WORK'명령을 이용하여 그 트렌잭션을 취소시키는 과정을 추가해 주어야 한다.
               
        2) whenever not found 구문
                형식)
                EXEC SQL WHENEVER NOT FOUND GOTO stop_st;
               
                EXEC SQL WHENEVER NOT FOUND STOP;
               
                EXEC SQL WHENEVER NOT FOUND CONTINUE;
               
                -. 위 문장들은 SQL문장 실행 결과 sqlca.sqlcode값이 1403으로 조건에 맞는 레코드 건수가 하나도 없는 경우이다.
               

-. ProC내에서의 select 문장
        ex1)
        -------------------------------------------------------------
        EXEC SQL BEGIN DECALRE SECTION;
                varchar usrname[16], usrarea[10];
                int usrno;
        EXEC SQL END DECLARE SECTION;
       
        EXEC SQL SELECT usrno, usrname, usrarea
                INTO : usro, :usrname, :usrarea
                FROM forumlist
                WHERE usrno=5;
       
        EXEC SQL WHENEVER SQLERROR GOTO stop_st;
       
        EXEC SQL COMMIT WORK;
       
        printf("\n 회원의 고유번호        =%d",usrno);
        printf("\n 회원의 이름                =%d",usrname.arr);
        printf("\n 회원의 이름(크기)        =%d BYTE", usrname.len);
        printf("\n 회원의 거주지역        =%s", usrarea.arr);
        printf("\n 회원의 거주지역(크기) =%d BYTE", usrarea.len);
       
        stop_st :
        printf("\n 에러메시지 =%s", sqlca.sqlerrm.sqlerrmc);
        -------------------------------------------------------------
        -. into 구문은 쿼리 결과에 만족하는 데이타를 검색해 와서 into구문 뒤의 변수에 각각 할당한다.
        -. 호스트 변수명 앞에 (:)을 붙여 줌으로써 조회할 테이블이 칼럼명과 구분을 지어 준다.
       
        ex2)
        -------------------------------------------------------------
        EXEC SQL BEGIN DECALRE SECTION;
                varchar usrname[16], usrarea[10];
                int usrno;
                char SqlCmdp100];
        EXEC SQL END DECLARE SECTION;
       
        memset(SqlCmd,0x00,sizeof(SqlCmd));
        sprintf(Sqlcmd,"select usrno, usrname, usrarea from forumlist");
        EXEC SQL PREPARE ST_1 FROM :SqlCmd;
        EXEC SQL DECLARE CUR_1 FOR ST_1;
        EXEC SQL OPEN CUR_1;
        EXEC SQL FETCH SUR_1 INTO :usrno, :usrname, :usrarea;

        if (sqlca.sqlcode < && sqlca.sqlcode != -1405)
        {
                printf("\n 에러메시지 = %s", sqlca.sqlerrm.sqlerrmc);
                printf("\n 레코드 조회시 에러 발생!");
                EXEC SQL CLOSE CUR_1;
                EXEC SQL ROLLBACK WORK;
                exit();
        } /* error */
        else if(sqlca.sqlcode == 1403)
        {
                printf("\n 에러메시지 ="%s", sqlca.sqlerrm.sqlerrmc);
                printf('\n 조건에 맞는 데이타가 없슴다!");
                EXEC SQL CLOSE CUR_1;
                EXEC SQL ROLLBACK WORK;
                exit();
        } /* not exist */
        while(sqlca.sqlcode ==0)
        {
                printf("\n 회원의 고유번호        =%d",usrno);
                printf("\n 회원의 이름                =%d",usrname.arr);
                printf("\n 회원의 이름(크기)        =%d BYTE",usrname.len);
                printf("\n 회원의 거주지역        =%s", usrarea.arr);
                printf("\n 회원의 거주지역(크기) =%d BYTE", usrarea.len);
                EXEC SQL FETCH CUR_1 INTO :usrno, :usrname, :usrarea;
        } /* while */
        printf("\n 오라클 메시지 = %s",sqlca.sqlerrm.sqlerrmc);
        printf("\n 레코드가 성공적으로 조회되었습니다.");
        EXEC SQL CLOSE CUR_1;
        EXEC SQL COMMIT WORK;
        -------------------------------------------------------------
       
-. PROC속의 UPDATE문장
        -----------------------------------------------------------------------------
        ex1)
        EXEC SQL BEGIN DECLARE SECTION;
                varchar username[16], usrarea[10];
                int usrno;
        EXEC SQL END DECLARE SECTION;        
       
        EXEC SQL SELECT usrno, username, usrarea
                into :usrno, :usrname, :usrarea
                from forumlist
                where usrno=3;
        EXEC SQL WHENEVER SQLERROR GOTO stop_st;
       
        printf("\n 변경전 회원[%d]번의 거주지역 =[%s]",usrno,usrarea.arr);
        EXEC SQL COMMIT WORK;
        strcpy(userarea.arr,"SEOUL");
        usrarea.len =5;
       
        EXEC SQL UPDATE FORUMLIST
                SET usrarea =:usrarea
                WHERE usrno =3;
       
        EXEC SQL WHENEVER SQLERROR GOTO stop_st;
       
        EXEC SQL COMMIT WORK;
        printf("\n 오라클 메시지 =%s",sqlca.sqlerrm.sqlerrmc);
       
        EXEC SQL SELECT usrno,usrarea
                INTO :usrno, :usrarea
                FROM forumlist
                WHERE usrno=3;
       
        EXEC SQL WHENEVER SQLERROR GOTO stop_st;
       
        printf("\n 변경후의 회원 [%d]번의 거주지역 =[%s]", usrno.usrarea.arr);
        EXEC SQL COMMIT WORK;
        exit();
       
        stop_st:
        EXEC SQL ROLLBACK WORK;
        printf("\n 에러발생 =%s",sqlca.sqlerrm.sqlerrmc);
        -----------------------------------------------------------------------------
       
        -----------------------------------------------------------------------------
        ex2)
        EXEC SQL BEGIN DECLARE SECTION;
                varchar usrname[16], usrarea[10];
                int usrno;
        EXEC SQL END DECLARE SECTION;
        char SqlCmd[100];
       
        EXEC SQL select usrno,username,usrarea
                into :usrno, :usrname, :usrarea
                from forumlist
                where usrno=3;
        EXEC SQL WHENEVER SQLERROR GOTO stop_st;
       
        printf("\n 변경전의 [%d]번째 회원의 거주지역 =[%s]", usrno, usrarea.arr);
        EXEC SQL COMMIT WORK;
       
        memset(SqlCmd,0x00,sizeof(SqlCmd));
        sprintf(SqlCmd,"UPDATE FORUMLIST set usrarea=%s \ (한 문장이 끝나지 않았기 때문에 \(역슬래시)를 사용함)
                                where usrno=3","seoul");
        EXEC SQL EXCUTE IMMEDIATE :SqlCmd;
        EXEC SQL WHENEVER SQLERROR GOTO stop_st;
        EXEC SQL COMMIT WORK;
       
        EXEC SQL select usrno, usrarea
                into :usrno, :usrarea
                from forumlist
                where usrno=3;
        EXEC SQL WHENEVER SQLERROR GOTO stop_st;
       
        printf("\n 변경후의 [%d]번째 회원의 거주지역 =[%s]", usrno,usrarea.arr);
        EXEC SQL COMMIT WORK;
        exit();
       
        stop_st:
        EXEC SQL ROLLBACK WORK;
        printf("\n 에러 메시지 =%s", sqlca.sqlerrm.sqlerrmc);
        -----------------------------------------------------------------------------
        -. 위의 update문을 약간 수정하면
                sprintf(SqlCmd,"update forumlist set userarea=%s where usrno=%d","seoul",3);로 해도 됨.


-. ProC속의 Insert문장
        -----------------------------------------------------------------------------
        ex1)
        EXEC SQL BEGIN DECLARE SECTION;
                varchar usrname[16], usrarea[10], usrkindcode[4];
                int usrno ;
        EXEC SQL END DECLARE SECTION;
        int tot;
       
        tot =0;
        usrno=10;
        strncpy(usrname.arr,"LEE-Y-S",Strlen("LEE-Y-S"));
        strncpy(usrarea.arr,"seoul",strlen("seoul"));
        strncpy(usrkindcode,"1004",strlen("1004"));
       
        EXEC SQL INSERT INTO FORUMLIST(usrno, usrname, usrarea, usrkindcode)
                values(:usrno, :usrname, :usrarea, :usrkindcode);
        EXEC SQL WHENEVER SQLERROR GOTO stop_st;
        printf("\n insert success!!");
        printf("\n 오라클 메시지 = %s", sqlca.sqlerrm.sqlerrmc);
       
        EXEC SQL SELECT COUNT(*)
                INTO :TOT
                FROM FORUMLIST
        EXEC SQL WHENEVER SQLERROR GOTO stop_st;
       
        printf("\n forumlist 테이블에 존재하는 총 회원 수 =%d", tot);
        EXEC SQL COMMIT WORK;
       
        EXEC SQL SELECT usrno, usrarea
                INTO :usrno, :usrarea
                FROM FORUMLIST
                WHERE usrno=10;
        EXEC SQL WHENEVER SQLERROR GOTO stop_st;
       
        printf("\n 추가된 회원 [%d]번의 거주지역 =[%s]", usrno, usrarea.arr);
        EXEC SQL COMMIT WORK;
       
        stop_st;
        EXEC SQL ROLLBACK WORK;
        printf("\n 오라클 에러=%d",sqlca.sqlerrm.sqlerrmc);
        -----------------------------------------------------------------------------
        ex1은 insert문장에 함께 기술해 줄 호스트 변수들을 선언해 주었지만...
        ex2에서는 선언하지 않았다.
        ex2의 첨에 있는 usrarea는 그 다음에 수행할 select명령에 필요한 선언 문장이다.
        -----------------------------------------------------------------------------
        ex2)
        EXEC SQL BEGIN DECLEARE SECTION;
                varchar usrarea[10];
                int usrno;
        EXEC SQL END DECLARE SECTION;
        char SqlCmd[100];
        int tot;
       
        tot=0;
        memset(SqlCmd,0x00,sizeof(SqlCmd));
        sprintf(SqlCmd,"insert into forumlist(usrno, usrname, usrarea, \
                        usrkindcode) values(%d, %s, %s, %s)", \
                        10,"lee-k-y","seoul","1004");
        EXEC SQL EXECUTE IMMEDIATE :SqlCmd;
        EXEC SQL WHENEVER SQLERROR GOTO stop_st;
       
        printf("\n Insert Success!");
       
        EXEC SQL SELECT count(*)
                into :tot;
                from forumlist
        EXEC SQL WHENEVER SQLERROR GOTO stop_st;
       
        printf("\n forumlist 테이블에 존재하는 총 회원 수 = %d",tot);
        EXEC SQL COMMIT WORK;
       
        EXEC SQL SELECT usrno, usrarea
                into :usrno, :usrarea
                from forumlist
                where usrno=10;
        EXEC SQL WHENEVER SQLERROR GOTO stop_st;
        printf("\ 추가된 회원 [%d]번의 거주지역 =[%s]",usrarea.arr);
        EXEC SQL COMMIT WORK;
       
        stop_st:
        EXEC SQL ROLLBACK WORK;
        printf("\n 오라클 에러 = %d", sqlca.sqlerrm,sqlerrmc);
        -----------------------------------------------------------------------------

-. Proc내에서의 delete문장.

        -----------------------------------------------------------------------------
        ex1)
        EXEC SQL BEGIGN DECLRAE SECTION;
                varchar usrarea[10];
                int usrno;
        EXEC SQL END DECLARE SECTION;
        int tot;
       
        tot=0;
        EXEC SQL DELETE FORUMLIST WHERE USRNO=10;
       
        EXEC SQL WHENEVER SQLERROR GOTO stop_st;
        EXEC SQL COMMIT WORK;
       
        printf("\n Delete 문장 성공!!");
       
        EXEC SQL SELECT count(*)
                into :tot;
                from forumlist
        EXEC SQL WHENEVER SQLERROR GOTO stop_st;
       
        printf("\n forumlist 테이블에 존재하는 총 회원 수 =%d",tot);
        EXEC SQL COMMIT WORK;
       
        stop_st:
        EXEC SQL ROLLBACK WORK;
        printf("\n Oracle Error =%d",sqlca.sqlerrm.sqlerrmc);
        -----------------------------------------------------------------------------
        -. 어떠한 호스트 변수도 사용하지 않음.
       
        -----------------------------------------------------------------------------
        ex2)
        EXEC SQL BEGIN DECLARE SECTION;
                varchar usrarea[10];
                int usrno;
        EXEC SQL END DECLARE SECTION;
        char SqlCmd[100];
        int tot;
        tot=0;
        memset(SqlCmd,0x00,sizeof(SqlCmd));
        sprintf(SqlCmd,"Delete forumlist where usrno=%d",10);
        EXEC SQL EXCUTE IMMEDIATE :SqlCmd;
       
        EXEC SQL WHENEVER SQLERROR GOTO stop_st;
        EXEC SQL COMMIT WORK;
       
        prinf("\n Delete Success!!");
       
        EXEC SQL SELECT COUNT(*)
                INTO :TOT;
                FROM FORUMLIST
        EXEC SQL WHENEVER SQLERROR GOTO stop_st;
       
        printf("\n forumlist 테이블에 존재하는 총 회원 수=%d",tot);
        EXEC SQL COMMIT WORK;
       
        stop_st;
        EXEC SQL ROLLBACK WORK;
        printf("\n Oracle Error : =%d",sqlca.sqlerrm.sqlerrmc);
        -----------------------------------------------------------------------------
        -. where절에서 회원의 고유번호값을 지정해주었다.는 점이 ex1과 다른점이다.
        -. delete 부분을 다음과 같이 수정해도 된다.
                usrnum=10;
                sqlprintf(SqlCmd,"delete forumlist where usrno=%d",usrnum);
                이처럼 변수를 먼저 선언해준 후 값을 대입해서 삭제 하는 방법도 있다.


-. 트랜잭션의 제어
       
        -. EXEC SQL COMMIT WORK;
            : DML 명령인 INSERT, DELETE ,UPDATE 문장을 수행한 경우에 유용하다.
            : DDL 명령은 자동 COMMIT 되므로 사용안해도 됨.
       
        -. EXEC SQL ROLLBACK WORK;