'리눅스'에 해당되는 글 6

  1. 2008.10.23 Pro* C 예제
IT_Expert/Pro* C (ProC)(Pro C) | Posted by 낫기법필 2008. 10. 23. 10:48

Pro* C 예제


Unix 상에서 Compile을 하는 방법은 보통 Make 명령을 사용해서 수행한다.  <?XML:NAMESPACE PREFIX = O />

Make 명령은 Makefile이란 이름의 파일을 수행한다. 일종의 배치 파일형식이라고 생각하면 된다.  Makefile말고도 makefile이나 각자가 원하는 파일명의 파일을 만들어서 수행할 있다. Make명령 뒤에 원하는 파일명을 특별히 기술하지 않는다면 우선 순위는 Makefile , makefile , make 파일명 순으로 처리 된다.  통상적으로 Makefile 사용한다. 

 

Makefile 작성방법

Unix-C Compile 경우에는 compile 명령인 cc 사용한다.

간단한 작성법을 보자면

(Unix-C에서는 ‘#’ 설명부이다.)

#####################################################

CC = cc                                            # 매크로의 선언이다.

CFLAGS= -c      

OFLAGS= -O -s

 

PGMS=    test1     

 

Test1:                    test1.c  

                  @Echo "[$@] processing ..."

                  $(CC) $(CFLAGS) $(OFLAGS) $@.c          # 컴파일을 수행한다.

                  $(CC) $(OFLAGS) -o test1  $@.o $(LIBS)     # 실행파일을 생성한다.

###################################################### 

 

위의 Makefile내용은 test1.c라는 프로그램을 compile하고 실행파일까지 생성하는 방법을 기술한 것이다.  Makefile 매크로 기법을 사용하여 기술할 있는데 자세한 기법은 정확하게 기술된 서적이 없어서 자세히는 모르지만 알고 있는 범위에서 설명을 하겠다.  좀더 자세한 사항을 원한다면 Unix상에서 man(외부명령의 도움말을 있는 명령)명령으로 make 관한 설명을 수도 있다.

 

매크로의 사용

매크로의 사용은 변수를 선언하고 초기치를 주는 형식과 비슷하다.  위의 예제에서 대문자의 형식으로 표현된 것이 매크로 명이고 표현은

매크로 = 내용

으로 사용하면 된다.

매크로의 적용은 $(매크로 )으로 사용한다.

$@ 사용은 선언명과 대치 된다.  선언명은

  선언명:

으로 선언한다.

프로그램에 대한 자세한 설명을 하자면 CC라는 매크로 명에 Unix-C compiler cc 선언하고cc 옵션처리를 위해 DFALGS, OFALGS 매크로 명을 선언한 옵션을 주었다. (cc 옵션은 나중에 설명하기로 하겠다.)

프로그램 여러 개를 한꺼번에 처리하기 위해서는 PGMS라는 선언으로 사용할 있다. 위의 여제는 하나의 프로그램을 처리하기 위한 방법이지만 아래의 경우는 여러 개의 프로그램을 처리하는 부분이다.

           OBJS = a.o b.o

 

           pgm: $(OBJS)

               cc $(OBJS) -o pgm

           a.o: incl.h a.c

               cc -c a.c

           b.o: incl.h b.c

               cc -c b.c

      일단 두개의 프로그램의 object파일명을 OBJS라는 매크로로 선언한 프로그램은

      cc컴파일러를 이용해서 만드는데 object파일들은 OBJS라는 매크로로 선언된 파일들

      이고 자기 자신에게 선언된 파일들을 가지고 만든다.

      각각의 object들은 선언부의 형식으로 만들어서 사용한다.

밖에도 사용하는 방법은 많이 있지만 어떻게 컴파일 것인가에 따라 조금씩 기술하는 방법이 틀리므로 각자 필요할 자주 만들어 보는 것이 도움이 같다.

 

  PRO-C makefile 앞에서 설명한 부분에 ORACLE 경로, PROC 경로와 PRO-C 컴파일 경우의 가지 옵션처리를 추가해 주면 된다.

######################################################

#                                                    #

# Environ Variable for KookMin HP and Oracle 7.3.x          #

#                                                    #

######################################################

# ORACLE 경로와 옵션 처리부

ORAINCDIR=      $(ORACLE_HOME)/precomp/public

ORALIBS=        -L$(ORACLE_HOME)/lib -lsql -lsqlnet -lncr -lsqlnet \

        $(ORACLE_HOME)/lib/libclient.a -lcommon -lgeneric \

        -lsqlnet -lncr -lsqlnet $(ORACLE_HOME)/lib/libclient.a \

        -lcommon -lgeneric -lepc -lnlsrtl3 -lc3v6 -lcore3 \

        -lnlsrtl3 -lcore3 -lnlsrtl3 -lcl -lm –lm

# PRO-C 옵션 처리부

# sqlcheck에는 3가지가 있는데

# syntax => 문법만을 검사한다. (기본값)

# semantics => 문법과 sql문을 검사한다. ( 반드시 userid옵션이 포함되야 한다.)

# full => 모든 검사를 시행한다. (userid옵션 포함)

# SQL문까지 검사하고 싶으면 반드시 semantics full 옵션을 사용해야 한다.

PROFLAGS=ireclen=132 oreclen=132 sqlcheck=semantics userid=tips5/tips5      

 

INCS=      $(ORAINCDIR)

LIBS=       $(ORALIBS)

 

DEFINES=

CFLAGS= -c

OFLAGS= -O -s

 

PGMS=    test2     

all:             test2     

test2:    test2.pc  

                  @echo "[$@] processing ..."

     # iname 옵션은 pro-c에서 precompile 소스화일을 기술하기 위한 argument이다.

        $(ORACLE_HOME)/bin/proc  $(PROFLAGS)  iname=$@.pc

     # pc 작성된 프로그램을 Unix-C 인식할 있도록 먼저 C 프로그램으로 변환한다.

                  $(CC) $(INCS) $(DFLAGS) $(CFLAGS) $(OFLAGS) $@.c

     # 만들어진 C프로그램을 가지고 실행파일을 생성한다.

                  $(CC) $(DFLAGS) $(OFLAGS) -o test2  $@.o $(LIBS)

     # 만들어진 C프로그램을 삭제한다. –F 옵션은 강제 삭제.

                  /bin/rm -f $@.c

###############################################################

 

precompiler ORACLE 옵션

옵션을 프로그램의 코딩에 기술할 경우에는

 EXEC ORACLE OPTION( 옵션 = )

으로 선언해서 사용할 있다.

. AUTO_CONNECT=YES/NO : 자동으로 CONNECT 것인지를 선택한다. (default NO)

                          YES 경우는 OPS$<USERNAME> 설정하여 자동 CONNECT

                          되게 한다. COMMAND LINE이나 CONFIGURATION FILE안에

                          기술할 있다.

. CODE=ANSI_C/KR_C/CPP : compiler COMPILE방법을 결정한다. (default KR_C)

                          COMMAND LINE 안에만 기술할 있다.

. COMP_CHARSET=MULTY_BYTE/SINGLE_BYTE : ?????  (default MULTY_BYTE)

                          COMMAND LINE 안에만 기술할 있다.

. CONFIG=<filename> : configuration file 기술한다.

. CPP_SUFFIX=<filename extension> : CODE=CPP 경우 설정한다.  용도는 모르겠고 예로

                          CPP_SUFFIX=cc OR CPP_SUFFIX=C등이 있다. COMMAND LINE

                          이나 CONFIGURATION FILE안에 기술할 있다.

. DEF_SQLCODE=NO/YES : default NO , 만약 YES 경우는 SQLCODE 기술 하기 위해

                         #define  SQLCODE sqlca.sqlcode라고 기술해야 한다.

                         NO 경우는 EXEC SQL INCLUDE SQLCA; 라고 기술.

. ERRORS=YES/NO : default YES , YES 경우는 터미널로 즉시 ERROR 보여주고 LIST

                   파일도 가진다. NO 경우는 그냥 LIST화일만을 가진다.

. DBMS=NATIVE/V6/V7/V6_CHAR : DBMS 엔진을 설정한다. (default NATIVE)

. HOLD_CURSOR=YES/NO :                  CURSOR 사용하는데 CACHE 사용하는지 인지를

                   결정한다. DEFAULT NO, 프로그램의 performance 영향을 많이

                   준다. 반복적인 SQL문이 재사용되는 것을 막아 준다(YES 경우)

                   재사용되는 것을 막아주기 때문에 옵티마이져는 SQL문을 해석할

                   필요가 없는 것이다.  대체적으로 이중CURSOR 사용할 경우 COLSE

                   두번 되는 것을 막기위해 사용된다. OPEN하고 재해석하는 시간이

                   줄어든다.

. INAME=<PATH 파일명> : INPUT FILE 이름을 기술한다.

. ONAME=,path 파일명 : OUTPUT FILE 이름을 기술한다.

. INCLUDE= pathname : INCLUDE 경로를 설정한다.

. MAXOPENCURSORS=5 ~ 255 : 최대로 있는 cursor 수를 말한다. (default 10)

. MODE=ANSI/ISO/ORACLE : 문법을 맞게 검사하는 MODE 설정한다. DEFAULT

                    ORACLE이다.

. ORACA= YES/NO : ORACLE communication area 설정한다. DEFAULT NO

. RELEASE_CURSOR= YES/NO : CURSOR 연계해서 사용할 경우에 MEMORY CACHE

                   사용을 설정한다. YES 경우 문의 실행시마다 재해석한다. 일반적으로

                   실행횟수가 적은 문에서는 YES 사용하고 빈번하게 실행되는 문에선

                   NO 설정한다.

. SELECT_ERROR= YES/NO : default YES이다.  NO 경우에는 SELECT INTO절에서

                  INTO절에 받는 변수의 크기보다 데이터가 들어왔을 에러를

                  발생하지 않는다.

. SQLCHECK=SEMANTICS/FULL/SYNTAX/LIMITED/NONE : compile compile 옵션체크

                   방법을 설정한다. DEFAULT SYNTAX

             SEMANTICS 반드시 USEID 함께 사용한다. 옵션은 C 문법과

               SQL문의 구분까지 체크한다.

             FULL 모든 검색을 실시한다. USERID 기술한다.

             SYNTAX 문법만을 검색한다.

. USERID= username/password : 오라클의 SQL문을 검색하기 위해선 오라클과 접속을 해야

                   한다. 오라클의 사용자 ID 암호를 기술한다.

 

 

 

 

 

 

 

/*  모듈별 설명  */

우리가 작성한 모듈은 상당히 간단한 편에 속한다.  가장 까다로운 것이 99형식의 모듈이므로 00형식과 99형식에 대해서만 기술하고 다음에 나오는 예제를 보면 PRO-C 대한 이해는 SQL문을 알고 있는 분에게는 별로 어려움이 없으리라고 본다.

 

/***************************************************************************

   is030100.pc => 모든 필드의 값들을 tips5쪽에서 structure 받아 이것을 가지고

   데이터를 insert 하는 구문이다.  

***************************************************************************/

#include <stdio.h>

/* 구조체의 정보가 들어 있는 H 파일 */

#include "Extract.h"

/* sql구문을 사용하기 위해선 반드시 선언한다. */

EXEC SQL INCLUDE sqlca;

char    pg_id[9];

 

/*-------  trace file 관련 define ==> 이행시 막을것

         - tropen.h : trace file open header

         - tr9999.h : character, integer type display header

         - tr9998.h : doubel type display header ----------*/

 

#define TRACEPATH           "/home/tips5/kmc210/trc/"

FILE    *trace_file ;

#include "tr_extern.h"

/*------------------------------------------------------------*/

 

struct stk_03010000  s0301;

int is030100( stkmsg, len, errmsg )

char *stkmsg;

int  len;

char *errmsg;

{

 

   strcpy(pg_id,"is030100");

   Ex01_03010000( stkmsg, s0301, len );

 

 

/*-----  trace file 변수값 display => 이행시 막을것 -------*/

                                    /* trace file open */

   tropen();

   tr9999("*------------------\n");

 

   tr9999("s0301.gogb_idno     = (%s)\n",    s0301.gogb_idno        );

   tr9999("s0301.gogb_gubun    = (%s)\n",    s0301.gogb_gubun       );

   tr9999("s0301.gogb_nameko   = (%s)\n",    s0301.gogb_nameko      );

   tr9999("s0301.gogb_pstnhm   = (%s)\n",    s0301.gogb_pstnhm      );

   tr9999("s0301.gogb_addrhm   = (%s)\n",    s0301.gogb_addrhm      );

   tr9999("s0301.gogb_telnhm   = (%s)\n",    s0301.gogb_telnhm      );

   tr9999("s0301.gogb_pstnwk   = (%s)\n",    s0301.gogb_pstnwk      );

   tr9999("s0301.gogb_addrwk   = (%s)\n",    s0301.gogb_addrwk      );

   tr9999("s0301.gogb_telnwk   = (%s)\n",    s0301.gogb_telnwk      );

   tr9999("s0301.gogb_namewk   = (%s)\n",    s0301.gogb_namewk      );

   tr9999("s0301.gogb_jobcod   = (%s)\n",    s0301.gogb_jobcod      );

   tr9999("s0301.gogb_positn   = (%s)\n",    s0301.gogb_positn      );

   tr9999("s0301.gogb_faxnwk   = (%s)\n",    s0301.gogb_faxnwk      );

   tr9999("s0301.gogb_family   = (%s)\n",    s0301.gogb_family      );

   tr9999("s0301.gogb_brthdt   = (%s)\n",    s0301.gogb_brthdt      );

   tr9999("s0301.gogb_brthcd   = (%s)\n",    s0301.gogb_brthcd      );

   tr9999("s0301.gogb_marrdt   = (%s)\n",    s0301.gogb_marrdt      );

   tr9999("s0301.gogb_movedt   = (%s)\n",    s0301.gogb_movedt      );

   tr9999("s0301.gogb_resist   = (%s)\n",    s0301.gogb_resist      );

   tr9999("s0301.gogb_sryear   = (%d)\n",    s0301.gogb_sryear      );

   tr9999("s0301.gogb_hobbcd   = (%s)\n",    s0301.gogb_hobbcd      );

   tr9999("s0301.gogb_busnum   = (%s)\n",    s0301.gogb_busnum      );

   tr9999("s0301.gogb_cornum   = (%s)\n",    s0301.gogb_cornum      );

   tr9999("s0301.gogb_prstid   = (%s)\n",    s0301.gogb_prstid      );

   tr9999("s0301.gogb_iyamt    = (%d)\n",    s0301.gogb_iyamt       );

   tr9999("s0301.gogb_iysale   = (%d)\n",    s0301.gogb_iysale      );

   tr9999("s0301.gogb_prmbnk   = (%s)\n",    s0301.gogb_prmbnk      );

   tr9999("s0301.gogb_prmbus   = (%s)\n",    s0301.gogb_prmbus      );

   tr9999("s0301.gogb_prmusr   = (%s)\n",    s0301.gogb_prmusr      );

   tr9999("s0301.gogb_prmdat   = (%s)\n",    s0301.gogb_prmdat      );

   tr9999("s0301.gogb_prmbrc   = (%s)\n",    s0301.gogb_prmbrc      );

   tr9999("s0301.gogb_idnobf   = (%s)\n",    s0301.gogb_idnobf      );

   tr9999("s0301.gogb_idnoaf   = (%s)\n",    s0301.gogb_idnoaf      );

   tr9999("s0301.gogb_inflag   = (%s)\n",    s0301.gogb_inflag      );

   tr9999("s0301.gogb_lstdat   = (%s)\n",    s0301.gogb_lstdat      );

   tr9999("s0301.gogb_rtpscn   = (%d)\n",    s0301.gogb_rtpscn      );

   tr9999("s0301.gogb_rtpsdt   = (%s)\n",    s0301.gogb_rtpsdt      );

 

/*------------------------------------------------------------*/

   EXEC SQL INSERT INTO 고객기본정보

                    VALUES  (:s0301) ;

 

   if( sqlca.sqlcode != 0 ) {

      /* 중복된 데이터가 있는 경우 sqlca.sqlcode –1 된다 */

      if( sqlca.sqlcode == -1 ) {

 

      EXEC SQL DELETE FROM 고객기본정보

                     WHERE 주민사업자번호     =   :s0301.gogb_idno ;

          if( sqlca.sqlcode != 0 ) {

              sprintf( errmsg, "%5d %s_rtn01 고객기본정보 table delete error!", sqlca.sqlcode, pg_id);

              return( -9 );

          }

          else  {

              EXEC SQL INSERT INTO 고객기본정보

                            VALUES ( :s0301 ) ;

 

              if ( sqlca.sqlcode != 0 ) {

                   sprintf( errmsg, "%5d %s_rtn02 고객기본정보 table delete->insert error!",

                         sqlca.sqlcode, pg_id );

                   return( -9 );

              }

              else

 

                   tr9999("SUCCESS \n");

                   return ( 0 ) ;

         }

      }

      else {

            sprintf( errmsg, "%5d %s_rtn03 고객기본정보 table insert error!", sqlca.sqlcode, pg_id );

            return( -9 );

      }

   }

 

   tr9999("SUCCESS \n");

   return ( 0 ) ;

}

 

 

/***************************************************************************

   is030199.pc =>  세가지 종류의 처리가 존재한다.  기존의 정보를 새로운 정보로

                  변경하는 것은 같지만 그것의 처리가 복수개의 처리인지 또는

                  기준키의 변경인지 아닌지에 따라 로직은 변경이 있다.

                  Type 만을 보기 위해 중복되는 부분은 삭제함.

***************************************************************************/

#include <stdio.h>

#include <stdlib.h>

#include "Extract.h"

EXEC SQL INCLUDE sqlca;

char    pg_id[9];

 

struct stk_03010099  s030199;/*고객기본정보의 변경전, 변경후의 주민사업자번호*/

struct stk_03020000  s0302;  /*고객기타정보                                  */

struct stk_03030000  s0303;  /*고객정보변경내역                              */

struct stk_03040000  *s0304; /*보증인정보내역                            */

 

int is030199( stkmsg, len, errmsg )

char *stkmsg;

int  len;

char *errmsg;

{

 

   strcpy(pg_id,"is030199");

   Ex01_03010099( stkmsg, s030199, len );

 

   /**********************************/

    고객기타정보를 UPDATE하는 부분

    type 1..

   /**********************************/

   /* 변경전의 데이터를 모두 structure 받아 둔다. */

   EXEC SQL SELECT *

              INTO :s0302

              FROM 고객기타정보

             WHERE 주민사업자번호 = :s030199.gogb_idnobf ;

 

   if( sqlca.sqlcode == 0 ){

 

       strcpy(s0302.gogt_idno, s030199.gogb_idnoaf) ; /*변경된 주민사업자번호를 정보에 저장 */

 

       /* 기존의 데이터를 삭제 */

       EXEC SQL DELETE FROM 고객기타정보

                      WHERE 주민사업자번호  = :s030199.gogb_idnobf ;

       if( sqlca.sqlcode != 0 ) {

           sprintf( errmsg, "%5d %s_rtn01 고객기타정보 table delete error!", sqlca.sqlcode, pg_id);

           return( -9 );

       }

       else  {

           /* 변경된 주민사업자번호와 기존의 저장된데이터를 다시 insert 한다. */

           EXEC SQL INSERT INTO 고객기타정보

                         VALUES ( :s0302 ) ;

           if ( sqlca.sqlcode != 0 ) {

                sprintf( errmsg, "%5d %s_rtn02 고객기타정보 table delete->insert error!", sqlca.sqlcode,

                       pg_id );

                return( -9 );

           }

       }

   }

   else if( sqlca.sqlcode != 1403 ) {

 

      sprintf( errmsg, "%5d %s_rtn01 고객기타정보 table select error!", sqlca.sqlcode, pg_id);

      return( -9 );

   }

 

   /***************************************************************************/

    회원기본정보를       UPDATE하는 부분                                  

    (복수건이 가능하지만 primary key 아니므로 UPDATE 처리)             

     type 2..

   /***************************************************************************/

    EXEC SQL UPDATE 회원기본정보

                SET 주민사업자번호 = :s030199.gogb_idnoaf

              WHERE 주민사업자번호 = :s030199.gogb_idnobf ;

    if ( sqlca.sqlcode != 0 ) {

         sprintf( errmsg, "%5d %s_rtn01 회원기본정보  table UPDATE error!", sqlca.sqlcode, pg_id);

         return( -9 );

    }

 

 

 

 

   /***************************************************************************/

    보증인정보등록내역을 UPDATE하는 부분                                   

    (복수건이 가능하기 때문에 cursor 처리)                               

     type 3..

   /***************************************************************************/

    /* struct array memory 할당과 초기화 */

    if ((s0304 =

        (struct stk_03040000 *) malloc(sizeof(struct stk_03040000))) == 0)

    {

        printf(errmsg, "s0304 Memory allocation error.\n");

        return( -9 );

    }

    /* 복수건의 처리일 수도 있으므로 CURSOR 이용한다 */

    EXEC SQL DECLARE cur_s0304 CURSOR FOR

        SELECT *

          FROM 보증인정보등록내역

         WHERE 주민사업자번호 = :s030199.gogb_idnobf;

 

    EXEC SQL OPEN cur_s0304;

    if(sqlca.sqlcode != 0 ){

       sprintf( errmsg, "%5d %s_rtn01 보증인정보등록내역에 심각한 에러가 발생했습니다.",

              sqlca.sqlcode, pg_id);

       return( -9 );

    }

   

    EXEC SQL WHENEVER NOT FOUND DO break ;    

   

    for (;;)

    {

        EXEC SQL FETCH cur_s0304

                  INTO :s0304 ;

 

       strcpy(s0304->bjin_wridno, s030199.gogb_idnoaf) ; /*변경된 주민사업자번호를 정보에 저장 */

       EXEC SQL DELETE FROM 보증인정보등록내역

                      WHERE 주민사업자번호  = :s030199.gogb_idnobf

                        AND 카드계좌번호    = :s0304->bjin_wrac ;

 

       if( sqlca.sqlcode != 0 ) {

           sprintf( errmsg, "%5d %s_rtn01 보증인정보등록내역 table delete error!", sqlca.sqlcode,

                 pg_id);

           EXEC SQL CLOSE cur_s0304;

           return( -9 );

       }

       else  {

           EXEC SQL INSERT INTO 보증인정보등록내역

                         VALUES ( :s0304 ) ;

           if ( sqlca.sqlcode != 0 ) {

                sprintf( errmsg, "%5d %s_rtn02 보증인정보등록내역 table delete->insert error!",

                       sqlca.sqlcode, pg_id );

                EXEC SQL CLOSE cur_s0304;

                return( -9 );

           }

       }

 

    }

 

    EXEC SQL CLOSE cur_s0304;

 

tr9999("SUCCESS \n");

return( 0 );

 

} /*main loop */

 

 

/****************************************************************************************

  예제를 이용하여 보다 쉬게 PRO-C 접근하여 보자.  아래의 예제는 가장 기본적인 사항에 대하여 간단한 형식으로 처리한 것인데 사원번호로 사원의 이름과 봉급, 보너스를 받아서 화면에 출력하는 프로그램이다.

****************************************************************************************/

/* 기본적인 헤더화일을 연다. (특정한 헤더화일을 제외하고는 우리가 보통 사용하던 헤더화일을 생각하면 된다.) */

#include <stdio.h>

#include <string.h>

 

/* 사용자이름과 암호를 표현하기 위하여 자릿수를 define하고 있다. */

#define     UNAME_LEN      20

#define     PWD_LEN        40

 

/* 사용자이름과 암호가 들어갈 배열을 선언한다. */

VARCHAR     username[UNAME_LEN];

VARCHAR     password[PWD_LEN];

 

/* select 발행 시에 저장하게 변수를 structure 형식으로 선언한다. */

struct

{

    VARCHAR   emp_name[UNAME_LEN];

    float     salary;

    float     commission;

} emprec;

 

/* PRO-C에서는 표지변수(indicator)라는 것이 있다.  이것은 반드시 short형식으로 선언하며 Null값을 체크하기 위해서 많이 사용한다.  여기서도 표지변수를 사용하기 위해 선언하고 있다.  */

struct

{

    short     emp_name_ind;

    short     sal_ind;

    short     comm_ind;

} emprec_ind;

 

/* 사용자에게 입력받게 host 변수의 선언 */

int         emp_number;

int         total_queried;

/*  SQLCA(SQL Communications Area) 선언한다.  SQL문을 기술하기 위해서 반드시 사용하여야 한다. */

EXEC SQL INCLUDE SQLCA;

 

/* 에러가 발생 했을 발생하는 함수 선언 */

void  sql_error();

 

main()

{

    char temp_char[32];

 

    /* VARCHAR 형식은 CHAR형식과는 다르게 길이를 가지고 있다.  그러므로 우리가

      VARCHAR 형식으로 선언 하였을 경우에 변수 어떤 값을 입력하기 위해서는 반드

      값의 내용과 값의 길이를 같이 넣어 주어야 한다.  VARCHAR 형식은 변수

      .arr 값이 들어가는 부분과 변수명.len이라는 길이가 들어가는 부분으로 구분되

      있다. */

    /* tips5 라는 사용자명과 암호를 변수에 저장한다. */

    strncpy((char *) username.arr, "tips5", UNAME_LEN);

    username.len = strlen((char *) username.arr);

 

    strncpy((char *) password.arr, "tips5", PWD_LEN);

    password.len = strlen((char *) password.arr);

 

    /* 에러가 발생했을 경우 sql_error 실행하라는 구문 */

    EXEC SQL WHENEVER SQLERROR DO sql_error("ORACLE error--\n");

 

    /* ORACLE 접속하기 위한 구문 */

    EXEC SQL CONNECT :username IDENTIFIED BY :password;

 

    printf("\nConnected to ORACLE as user: %s\n", username.arr);

 

    /* 몇번의 query 발행되었는지 체크하는 변수 */

    total_queried = 0;

    for (;;)

    {

        emp_number = 0;

        printf("\nEnter employee number (0 to quit): ");

        /* 사원번호를 입력받는다. */

        gets(temp_char);

        emp_number = atoi(temp_char);

        /* 사원번호가 0 입력되면 loop 종료 */

        if (emp_number == 0)

            break;

 

        /* SQLCA.SQLCODE 1403이면 NOT FOUND 된다. NOT FOUND 일때의 분기

          하는 구문 */

        EXEC SQL WHENEVER NOT FOUND GOTO notfound;

 

        EXEC SQL SELECT ename, sal, comm

            INTO :emprec INDICATOR :emprec_ind  /*  INTO :emprec :emprec_ind 같다. */

            FROM EMP

            WHERE EMPNO = :emp_number;

 

        printf("\n\nEmployee\tSalary\t\tCommission\n");

        printf("--------\t------\t\t----------\n");

 

        /* printf 함수는 \0 값이 나올 때까지 출력하므로 사원명의 끝배열에 \0 넣고

          출력한다. */

        emprec.emp_name.arr[emprec.emp_name.len] = '\0';

        printf("%-8s\t%6.2f\t\t",

            emprec.emp_name.arr, emprec.salary);

 

        /* 표지변수에 들어온 값이 –1 경우는 NULL값이므로 ‘NULL’ 출력한다. */

        if (emprec_ind.comm_ind == -1)

            printf("NULL\n");

        else

            printf("%6.2f\n", emprec.commission);

 

        total_queried++;

        continue;

 

/* NOT FOUND 경우의 분기 */

notfound:

        printf("\nNot a valid employee number - try again.\n");

 

    } /* end for(;;) */

    /* 발행된 query 수를 출력한다. */

    printf("\n\nTotal rows returned was %d.\n", total_queried);

    printf("\nG'day.\n\n\n");

 

    /* Disconnect from ORACLE. */

    EXEC SQL COMMIT WORK RELEASE;

    /*프로그램 종료 */

    exit(0);

}

 

 

void

sql_error(msg)

char *msg;

{

    char err_msg[128];

    size_t buf_len, msg_len;

    EXEC SQL WHENEVER SQLERROR CONTINUE;

    printf("\n%s\n", msg);

    buf_len = sizeof (err_msg);

    /* sql 에러 메시지를 구하는 구문, 많이 사용된다. */

    sqlglm(err_msg, &buf_len, &msg_len);

    printf("%.*s\n", msg_len, err_msg);

 

    EXEC SQL ROLLBACK RELEASE;

    exit(1);

}

 

 

 

/****************************************************************************************

  우리가 SQL문을 사용하다가 보면 cursor문을 사용할 경우가 많다.  특히 PRO-C batch job 형식의 처리를 하기 위해서 많이 사용하는데 이런 처리를 위해서는 cursor문을 사용해야 경우가 많이 있다.  이번 예제는 단순한 cursor문이지만 반드시 알아 두어야 사항들이고 다음 예제에서 cursor 배열로 잡아서 데이터를 한꺼번에 처리하는 cursor 구문을 생각해 보기로 하자. 

****************************************************************************************/

#include <stdio.h>

#include <string.h>

EXEC SQL INCLUDE SQLCA ;

 

#define UNAME_LEN      20

#define PWD_LEN        40

#define USERNAME1 "scott/tiger@PMS"

 

/* asciiz라는 변수의 type 선언한다. */

typedef char asciiz[PWD_LEN];

/* */

EXEC SQL TYPE asciiz IS STRING(40) REFERENCE;

asciiz     username;

asciiz     password;

 

struct emp_info

{

    asciiz     emp_name;

    float      salary;

    float      commission;

};

 

/* errors 함수. */

void sql_error();

 

main()

{

    /* struct 포인터로 재선언한다. */

    struct emp_info *emp_rec_ptr;

    /* 위에서 emp_rec_ptr 아직 메모리에 할당되어 있지 않은 상태이다. 메모리를

       할당한다. */

 

    if ((emp_rec_ptr =

        (struct emp_info *) malloc(sizeof(struct emp_info))) == 0)

    {

        fprintf(stderr, "Memory allocation error.\n");

        exit(1);

    }

 

    /* Connect to ORACLE. */

    strcpy(username, "SCOTT");

    strcpy(password, "TIGER");

    EXEC SQL WHENEVER SQLERROR DO sql_error("ORACLE error--");

    /* connect  */

    EXEC SQL CONNECT :USERNAME1;

    printf("\nConnected to ORACLE as user: %s\n", username);

 

    /* EMP TABLE에서 SALES 시작하는 데이터를 cursor 잡는다. */

    EXEC SQL DECLARE salespeople CURSOR FOR

        SELECT ENAME, SAL, COMM

            FROM EMP

            WHERE JOB LIKE 'SALES%';

 

    /* Open the cursor. */

    EXEC SQL OPEN salespeople;

 

    /* Get ready to print results. */

    printf("\n\nThe company's salespeople are--\n\n");

    printf("Salesperson   Salary   Commission\n");

    printf("-----------   ------   ----------\n");

 

    /* NOT FOUND 이면 LOOP 종료한다. */

    EXEC SQL WHENEVER NOT FOUND DO break;

    for (;;)

    {

        /* FETCH 데이터를 화면에 출력한다. */

        EXEC SQL FETCH salespeople INTO :emp_rec_ptr;

        printf("%-11s%9.2f%13.2f\n", emp_rec_ptr->emp_name,

                emp_rec_ptr->salary, emp_rec_ptr->commission);

    }

    /* Close the cursor. */

    EXEC SQL CLOSE salespeople;

    printf("\nArrivederci.\n\n");

 

    EXEC SQL COMMIT WORK RELEASE;

    exit(0);

}

 

 

void

sql_error(msg)

char *msg;

{

    char err_msg[512];

    size_t buf_len, msg_len;

 

    EXEC SQL WHENEVER SQLERROR CONTINUE;

 

    printf("\n%s\n", msg);

 

    buf_len = sizeof (err_msg);

    /* sql 에러를 출력한다. */

    sqlglm(err_msg, &buf_len, &msg_len);

    printf("%.*s\n", msg_len, err_msg);

 

    EXEC SQL ROLLBACK RELEASE;

    exit(1);

}

 

 

 

/***************************************************************************

 배열로 처리하는 cursor문의 예제이다.

***************************************************************************/

#include <stdio.h>

#include <string.h>

#include <sqlca.h>

 

#define NAME_LENGTH   20

#define ARRAY_LENGTH   5

 

/* connectgkrl 위한 사용자명과 암호를 변수에 저장한다. */

char *username = "SCOTT/TIGER@PMS";

char *password = "TIGER";

 

/* host structure 내용을 배열로 선언한다. 크기는 ARRAY_LENGTH 만큼 */

struct

{

    int    emp_number[ARRAY_LENGTH];

    char   emp_name[ARRAY_LENGTH][NAME_LENGTH];

    float   salary[ARRAY_LENGTH];

} emp_rec;

 

 

/* 함수의 선언*/

void print_rows();  /* 데이터의 출력을 위한 함수 */

void sql_error();

 

main()

{

    /* 처리된 데이터의 수를 출력하기 위하여 선언 */

    int  num_ret;

 

    /* Connect to ORACLE. */

    EXEC SQL WHENEVER SQLERROR DO sql_error("Connect error:");

    EXEC SQL CONNECT :username;

    printf("\nConnected to ORACLE as user: %s\n", username);

 

    /* sql 문이 에러일 경우 */

    EXEC SQL WHENEVER SQLERROR DO sql_error("Oracle error:");

    /* Declare a cursor for the FETCH. */

    EXEC SQL DECLARE c1 CURSOR FOR

        SELECT empno, ename, sal FROM emp;

 

    EXEC SQL OPEN c1;

    /* 처리 데이터의 개수를 위해 선언한 변수의 초기화 */

    num_ret = 0;

 

    /* NOT FOUND 이면 LOOP 종료 */

    EXEC SQL WHENEVER NOT FOUND DO break;

 

    for (;;)

    {

        EXEC SQL FETCH c1 INTO :emp_rec;

        /* sqlca.sqlerrd 여러 개의 배열을 가진다. 중에 sqlca.sqlerrd[2] 자주 사용된다.

          여기에는 FETCH 데이터의 개수가 들어간다.  물론 UPDATE 문이나 INSERT

          문이 발행된 후에도 처리된 개수의 데이터가 저장된다.  여기서는 structure

          배열이 5개이므로 5개를 처리하고 다시 루프를 도는 형식으로 이루어져 있다.

          5개마다 데이터들은 화면에 출력될 것이다.  Sqlca.sqlerrd[2]에는 계속해서 처리된

          데이터가 더해져서 들어가게 된다.  아래 로직을 이해해야 한다. 많이 사용되

          므로 */

        print_rows(sqlca.sqlerrd[2] - num_ret);

        num_ret = sqlca.sqlerrd[2];        /* Reset the number. */

    }

 

     /* 마지막 루프까지 돌고나서 빠져 나왔을 남아 있는 데이터를 처리하기 위한

        로직이다.  */

     if ((sqlca.sqlerrd[2] - num_ret) > 0)

        print_rows(sqlca.sqlerrd[2] - num_ret);

 

    EXEC SQL CLOSE c1;

    printf("\nAu revoir.\n\n\n");

 

/* Disconnect from the database. */

    EXEC SQL COMMIT WORK RELEASE;

    exit(0);

}

 

void

print_rows(n)

int n;

{

    int i;

 

    printf("\nNumber   Employee         Salary");

    printf("\n------   --------         ------\n");

 

    for (i = 0; i < n; i++)

        printf("%-9d%-15.15s%9.2f\n", emp_rec.emp_number[i],

               emp_rec.emp_name[i], emp_rec.salary[i]);

 

}

 

 

void

sql_error(msg)

char *msg;

{

    EXEC SQL WHENEVER SQLERROR CONTINUE;

 

    printf("\n%s", msg);

    printf("\n% .70s \n", sqlca.sqlerrm.sqlerrmc);

 

    EXEC SQL ROLLBACK WORK RELEASE;

    exit(1);

}

 

 

 

/**************************************************************************************

     Dynamic SQL Method 1 대한 기술 방법을 정리한다.  여기서는 테이블을 생성하고

    데이터를 저장하고 다시 테이블을 삭제하는 방법으로 Dynamic SQL Method 1 방법을

    정리한다.

***************************************************************************************/

 

#include <stdio.h>

#include <string.h>

 

EXEC SQL INCLUDE SQLCA ;

/* ORACLE Communications Are 포함한다. 여기에는 ORACLE 자체의 에러나 실행상태, 구문등을 */

EXEC SQL INCLUDE ORACA ;

 

/* ORACA. 사용하기 위해서는 반드시 기술해야 한다. */

EXEC ORACLE OPTION (ORACA=YES);

 

/* 아래 프로그램에서 한번 open cursor 가지고 두번 사용한 것을 있다.

   이렇게 연계되는 cursor 사용할 경우에 사용하는 옵션이다. */

EXEC ORACLE OPTION(RELEASE_CURSOR=YES);

 

void sql_error();

/*type 선언*/

typedef char asciz[80];

EXEC SQL TYPE asciz IS STRING(80) REFERENCE;

 

main()

{

    /* Declare the program host variables. */

    char    *username = "SCOTT";

    char    *password = "TIGER";

    asciz    dynstmt1;

    char     dynstmt2[10];

    VARCHAR  dynstmt3[80];

    EXEC SQL WHENEVER SQLERROR DO sql_error("Oracle error:");

 

    /* ORACLE 에러인 경우 oraca.orastxtf oracle에러 메시지를 저장한다. */

    oraca.orastxtf = ORASTFERR;

 

    /* Connect to Oracle. */

    EXEC SQL CONNECT :username IDENTIFIED BY :password;

    puts("\nConnected to ORACLE.\n");

 

    puts("CREATE TABLE dyn1 (col1 VARCHAR2(4))");

    /* 테이블을 생성한다. Dynamic SQL Method 1 구문은 변수를 필요로 하지 않는

       SQL문일 경우에 해당된다. */

    EXEC SQL EXECUTE IMMEDIATE

         "CREATE TABLE dyn1 (col1 VARCHAR2(4))";

 

    /* dynstmt1 포인터 변수에 insert 문장을 넣고 변수를 Dynamic SQL 구문으로 실행

      수는 있다. SQL문안에 변수처리는 하지 못한다. */

    strcpy((char *)dynstmt1, "INSERT INTO DYN1 values ('TEST')");

    puts(dynstmt1);

    /* insert 문장 실행 */

    EXEC SQL EXECUTE IMMEDIATE :dynstmt1;

 

    /* commit문도 변수를 사용하지는 않으므로 방법을 사용할 있다. */

     strncpy(dynstmt2, "COMMIT    ", 10);

     printf("%.10s\n", dynstmt2);

     EXEC SQL EXECUTE IMMEDIATE :dynstmt2;

 

     /* drop형식도 형식으로 표현한다.. */

    strcpy((char *)dynstmt3.arr, "DROP TABLE DYN1");

    dynstmt3.len = strlen((char *)dynstmt3.arr);

    puts((char *) dynstmt3.arr);

 

    EXEC SQL EXECUTE IMMEDIATE :dynstmt3;

 

    /* Commit any outstanding changes and disconnect from Oracle. */

    EXEC SQL COMMIT RELEASE;

 

    puts("\nHave a good day!\n");

    return 0;

}

 

 

void

sql_error(msg)

char *msg;

{

    printf("\n%.*s\n",

       sqlca.sqlerrm.sqlerrml, sqlca.sqlerrm.sqlerrmc);

    printf("in \"%.*s...\"\n",

        oraca.orastxt.orastxtl, oraca.orastxt.orastxtc);

    printf("on line %d of %.*s.\n\n",

        oraca.oraslnr, oraca.orasfnm.orasfnml,

        oraca.orasfnm.orasfnmc);

 

    /* 에러발생시에도 계속 */

    EXEC SQL WHENEVER SQLERROR CONTINUE;

    /* rollback */

    EXEC SQL ROLLBACK RELEASE;

    Exit(1);

}

 

 

 

/****************************************************************************************

    sample7.pc: Dynamic SQL Method 2

    dynamic SQL Method 2 입력변수가 1개일 경우 사용하는 방법이다. 

****************************************************************************************/

#include <stdio.h>

#include <string.h>

 

#define USERNAME "SCOTT"

#define PASSWORD "TIGER"

#define ORASTFERR   2

 

EXEC SQL INCLUDE SQLCA ;

 

EXEC SQL INCLUDE ORACA;

EXEC ORACLE OPTION (ORACA=YES);

 

char    *username = USERNAME;

char    *password = PASSWORD;

VARCHAR  dynstmt[80];

int          empno  = 1234;

int          deptno1 = 10;

int          deptno2 = 20;

void sql_error();

 

main()

{

    EXEC SQL WHENEVER SQLERROR DO sql_error("Oracle error");

    /* 에러가 발생하면 현재의 SQL statement 저장하라는 flag */

    oraca.orastxtf = ORASTFERR;

 

    /* Connect to Oracle. */

    EXEC SQL CONNECT :username IDENTIFIED BY :password;

    puts("\nConnected to Oracle.\n");

 

    /* insert 문인데 v1, v2라는 변수를 가지고 있다. 변수선언은 하지 않는다. */

    strcpy(dynstmt.arr,

        "INSERT INTO EMP (EMPNO, DEPTNO) VALUES (:v1, :v2)");

    dynstmt.len = strlen(dynstmt.arr);

    /* 변수의 값을 출력한다. */

    puts((char *) dynstmt.arr);

    printf("   v1 = %d,  v2 = %d\n", empno, deptno1);

 

    /* dynamic SQL method 2 구문 */

    /* PREPARE 먼저 실행될 sql문을 기술한다. */

    EXEC SQL PREPARE S FROM :dynstmt;

    /* 기술된 sql문에서 사용된 변수를 아래의 변수로 대체하여 실행하겠다는 구문 */

    EXEC SQL EXECUTE S USING :empno, :deptno1;

 

    /* 입력될 값의 변경 */

    empno++;

    printf("   v1 = %d,  v2 = %d\n", empno, deptno2);

 

    /* 이미 선언하였으므로 다시 SQL문을 선언하지는 않는다 */

    EXEC SQL EXECUTE S USING :empno, :deptno2;

 

    /* 새로운 SQL 문의 선언 */

    strcpy(dynstmt.arr,

        "DELETE FROM EMP WHERE DEPTNO = :v1 OR DEPTNO = :v2");

    dynstmt.len = strlen(dynstmt.arr);

 

    puts((char *) dynstmt.arr);

    printf("   v1 = %d,    v2 = %d\n", deptno1, deptno2);

 

    EXEC SQL PREPARE S FROM :dynstmt;

    EXEC SQL EXECUTE S USING :deptno1, :deptno2;

 

    EXEC SQL COMMIT RELEASE;

    puts("\nHave a good day!\n");

    exit(0);

}

 

 

void

sql_error(msg)

char *msg;

{

    printf("\n%s", msg);

    printf("\n%.*s\n",

        sqlca.sqlerrm.sqlerrml, sqlca.sqlerrm.sqlerrmc);

    printf("in \"%.*s...\"\n",

        oraca.orastxt.orastxtl, oraca.orastxt.orastxtc);

    printf("on line %d of %.*s.\n\n",

        oraca.oraslnr, oraca.orasfnm.orasfnml,

        oraca.orasfnm.orasfnmc);

 

/ * Disable ORACLE error checking to avoid an infinite loop

 * should another error occur within this routine.

 */

    EXEC SQL WHENEVER SQLERROR CONTINUE;

 

    EXEC SQL ROLLBACK RELEASE;

    exit(1);

}

 

 

 

 

 

 

 

 

 

 

/ ****************************************************************************************

  sample8.pc:  Dynamic SQL Method 3 관한 예제이다.

*************************************************************************************** */

 

#include <stdio.h>

#include <string.h>

 

#define USERNAME "SCOTT"

#define PASSWORD "TIGER"

#define ORASTFERR   2

EXEC SQL INCLUDE SQLCA ;

EXEC SQL INCLUDE ORACA ;

EXEC ORACLE OPTION (ORACA=YES);

 

char    *username = USERNAME;

char    *password = PASSWORD;

VARCHAR  dynstmt[80];

VARCHAR  ename[10];

int      deptno = 10;

 

void sql_error();

 

main()

{

    /*  SQL statement 에러를 발생했을 경우 subroutin 수행 */

    EXEC SQL WHENEVER SQLERROR DO sql_error("Oracle error");

    /* 에러 발생시 ORACA 영역에 있는 SQL Statement text 저장하라는 flag */

    oraca.orastxtf = ORASTFERR;

 

    /* Connect to Oracle. */

    EXEC SQL CONNECT :username IDENTIFIED BY :password;

    puts("\nConnected to Oracle.\n");

 

    /* SQL문을 담는 변수를 VARCHAR 선언하였으므로 값과 길이까지 넣어야 한다. v1

      선언할 필요는 없다. 나중에 OPEN 에서 실제의 구문으로 대체된다. */

    strcpy(dynstmt.arr,

        "SELECT ename FROM emp WHERE deptno = :v1");

    dynstmt.len = strlen(dynstmt.arr);

    puts((char *) dynstmt.arr);

    printf("   v1 = %d\n", deptno);

    printf("\nEmployee\n");

    printf("--------\n");

   

    /* S라는 이름으로 dynstmt 내용을 가지고 dynamic구문을 사용할 것임을 선언하고

      parsing 한다 */

    EXEC SQL PREPARE S FROM :dynstmt;

    /*CURSOR S 구문을 선언한다. */

    EXEC SQL DECLARE C CURSOR FOR S;

    /*CURSOR 선언한 C 대칭변수에 deptno라는 변수로 대입하고 CURSOR 연다. */

    EXEC SQL OPEN C USING :deptno;

    /* CURSOR 내용이 없으면 (FETCH된것이 없으면) LOOP 빠져 나온다. */

    EXEC SQL WHENEVER NOT FOUND DO break;

 

    for (;;)

    {

      /*OPEN CURSOR에서 데이터를 FETCH하는데 ename이라는 변수에 넣는다. */

        EXEC SQL FETCH C INTO :ename;

      /*들어온 변수의 배열 끝에 다른 데이터와 구분을 하기 위해서 null 집어 넣는다.*/

        ename.arr[ename.len] = '\0';

        puts((char *) ename.arr);

        }

 

    /*FETCH ROW 수와 SQL문을 출력한다. */

    printf("\nQuery returned %d row%s.\n\n", sqlca.sqlerrd[2], (sqlca.sqlerrd[2] == 1) ? "" : "s");

 

    /* CLOSE  cursor. */

    EXEC SQL CLOSE C;

 

    EXEC SQL COMMIT RELEASE;

 

    puts("Sayonara.\n");

    exit(0);

}

 

 

void

sql_error(msg)

char *msg;

{

    printf("\n%s", msg);

    /* printf 출력하기 위해 마지막 배열에 null값을 넣는다. */

    sqlca.sqlerrm.sqlerrmc[sqlca.sqlerrm.sqlerrml] = '\0';

    oraca.orastxt.orastxtc[oraca.orastxt.orastxtl] = '\0';

    oraca.orasfnm.orasfnmc[oraca.orasfnm.orasfnml] = '\0';

    printf("\n%s\n", sqlca.sqlerrm.sqlerrmc);

    printf("in \"%s...\"\n", oraca.orastxt.orastxtc);

    printf("on line %d of %s.\n\n", oraca.oraslnr,

       oraca.orasfnm.orasfnmc);

    /* 에러를 출력한 다시 loop 돈다. */

    EXEC SQL WHENEVER SQLERROR CONTINUE;

    /* ERROR 발생해서 CURSOR 돌다가 빠져나오면 CURSOR CLOSE 실행되지

      않았으므로 에러처리 구문에서 반드시 CLOSE 해야 한다. */

    EXEC SQL CLOSE C;

    EXEC SQL ROLLBACK RELEASE;

    exit(1);

}

 

 

 

  여기서 잠깐 예제에 관한 내용을 접어두고 sql문에서 사용하는 SQLCA, SQLDA, ORACA 대해서 알아보자.

 

. SQLCA(SQL Communication Area: SQL 사용자 영역) : sql문을 기술할 경우에 우리는 SQL Communication Area 무시할 없다. 영역은 여러 개의요소들을 가지고 있는데 중에서 우리가 가장 알고 있는 부분은 SQLCODE이다.  차례차례 SQLCA 가지고 있는 영역에 대하여 알아보자. 여기서는 PRO-C에서의 SQLCA 중점으로 애기한다.

 

SQLCA structure 구조로 되어 있다.  우리는 보통 프로그램 코드 상에서 EXEC SQL INCLUDE SQLCA; 라고 하면 구조체를 사용할 수가 있다. 또한 구조체를 직접 프로그램의 코딩에 기술할 수도 있다. 직접 기술하면 구조체의 임의의 이름을 주어서 기술할 수도 있다.  SQLCA 구조는 다음과 같다.

Struct sqlca {

     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] ;

} ;

struct  sqlca  sqlca ;

SQLCA 프로그램 코딩에서 whenever문과 같이 자주 사용하는데 구문은

EXEC SQL WHENEVER anyerror CONTINUE;

같이 사용된다.  구문은 에러가 발생되어도 계속해서 실행하기를 원할 경우에 사용되는 구문이다.  밖에  GOTO문이나 DO문을 사용해서 분기시킬 수도 있다.

 

1.  sqlcaid sqlcabc => PL/SQL문이나 C에서는 사용하지 않는다. 이것은 FORTRAN 위한

                    필드이다.

2.  sqlca.sqlcode => 에러의 코드가 저장되는 필드로 크게 3가지로 구분된다. 0 음수 , 양수

                0 일경우는 정상종료임을 뜻한다.

                양수일 경우는 1403 한가지 뿐인데 데이터를 찾지 못했을 경우에 나타난다.

                              PL/SQL에서는 100으로 나타난다.

                음수일 경우는 상당히 많은 코드를 가지고 있다.  무조건 에러이다.

                              -1 중복해서 데이터가 있을 경우에 발생한다.

3.  sqlerrm => 필드는 sqlerrml sqlerrmc 나누어져 있다.  sqlerrml sqlerrmc 길이를

            저장한다.  sqlerrmc 에러의 text구문을 저장한다. 우리가 일반적인 에러구문

            사용자에게 보여주기 위해선 필드를 이용하면 된다.

            PL/SQL에서는 함수처럼 사용할 수도 있다. , SQLERRM(100)이라고 하면

            MESSAGE ORA-01403: no data found 된다.

4.  sqlerrp =>  지금은 사용하지 않는 필드이다.

5.  Sqlerrd =>  필드는 6개의 배열을 가진다. 그러나 3번째 배열만을 사용하고 나머지는

             지금은 사용하지 않는다.

             Sqlerrd[2] cursor fetch row 수나 update, delete 경우 처리된 데이터

              개수들을 기억한다.  매우 자주 사용한다.

             PL/SQL에서는 SQL%ROWCOUNT 체크할 있고 파워빌더에SQLNROWS

             체크할 있다.

6.  sqlwarn =>  8개의 배열을 가지고 있다.  프리컴파일 도중에 경고구문에 대해 저장을

             한다. 각각의 경고를 수는 없지만 경고가 발생 되었을 경우에는 언제나

[0] 번째에 “W” 값이 저장된다.

7.  sqlext =>  현재는 사용하지 않는다.

 

 

. SQLDA( SQL Descriptor Area : SQL 기술자 영역)  : 우리가 dynamic SQL 4 사용하기 위해선 SQLDA 알아야 한다.  dynamic SQL 4에서는 반드시 SQLDA 선언해 주어야 하기 때문이다.  SQLDA DESCRIBE문에 의해 할당된 영역을 말한다. SQLDA문은 가지의 목적으로 사용된다.  첫째는 SELECT 문에 의해 반환된 항목을 보존하기 위해 사용하는 경우와 바인드 변수( dynamic SQL 입력변수) 대한 정보를 보존하기 위해서 사용된다.

SQLDA 구조는 다음과 같다.

Struct  sqlda{

       Int       N;

       Char   **V;

       Int      *L;    

       Short    *T;

       Short    *I;

       Int       F;

       Char   **S;

       Short   *M;

       Short    *C;

       Char   **X;

       Short   *Y;

       Short   *Z;

};

typedef  struct  sqlda  sqlda;

 

 

 

. ORACA(ORACLE Communication Area) : SQLCA에서 얻지 못하는 정보를 얻기 위해서 사용된다. 

      EXEC SQL INCLUDE ORACA;  라고 선언하고 

      EXEC ORACLE OPTION( ORACA = YES ) 라고 기술해 주어야 한다.

 

ORACA 포함되어 있는 항목들에 대해서 정리해보자.

 

1.  orastxt => ORACLE  RDBMS 해석한 현재 SQL문의 text 저장하고 있다. 에러가 발생

          했을 경우 에러가 있는 SQL문을  검색하고자 경우에 자주 사용된다.  프리

          컴파일러가 해석하는 CONNECT, COMMIT FETCH등의 문장은 저장되지 않는다.

          orastxtc orastxtl 구성되어 있다.

2.  orasfnm => 여러 개의 파일을 컴파일하는 경우에 ORACLE 에러가 발생한 파일을

            드에 저장한다.  orasfnmc orasfnml 으로 구성되어 있다.

3.  oraslnr => 에러가 발생한 행의 번호를 넘겨준다.

4.  Orastxtf => 에러가 발생했을 경우 orastxt 에러가 발생한 구문을 저장하는데 이때 어떻

            저장할 것인지를 결정한다.

0         :  default SQL문을 보존하지 않는다.

1         :  SQLERROR 있는 SQL문만을 보존

2         :  SQLERROR SQLWARN 있는 SQL문만을 보존

3         :  SQL 전부를 보존한다.

5.  커서 캐쉬에 대한 통계를 위한 필드들

    orahoc  : 지정된 최대 오픈 커서

    oramoc  : 필요한 최대 커서

    oracoc  : guswo 사용중인 커서의

    oranor  : 커서 캐쉬 재할당의 회수

    oranpr  : SQL문의 해석한 횟수

    oranex  : SQL문의 실행횟수

 

 

 

 

/*************************************************************

Sample Program 9:  Calling a stored procedure

 ORACLE SP CALL 있는 예제이다.

*************************************************************/

#include <stdio.h>

#include <string.h>

EXEC SQL INCLUDE SQLCA;

 

typedef char asciz[20];

typedef char vc2_arr[11];

EXEC SQL BEGIN DECLARE SECTION;

      EXEC SQL TYPE asciz  IS STRING(20) REFERENCE;

      EXEC SQL TYPE vc2_arr IS VARCHAR2(11) REFERENCE;

      asciz     username;

      asciz     password;

      int       dept_no;                  /* which department to query? */

      vc2_arr   emp_name[10];            /* array of returned names */

      vc2_arr   job[10];

      float     salary[10];

      int       done_flag;

      int       array_size;

      int       num_ret;                 /* number of rows returned */

EXEC SQL END DECLARE SECTION;

long      SQLCODE;

 

void print_rows();            /* produces program output      */

void sql_error();             /* handles unrecoverable errors */

 

main()

{

    int   i;

    char  temp_buf[32];

 

    EXEC SQL WHENEVER SQLERROR DO sql_error();

    strcpy(username, "scott");

    strcpy(password, "tiger");

    EXEC SQL CONNECT :username IDENTIFIED BY :password;

    printf("\nConnected to ORACLE as user: %s\n\n", username);

    printf("Enter department number: ");

    /*사원번호를 입력받는다. */

    gets(temp_buf);

    dept_no = atoi(temp_buf);

 

    /*출력될 양식의header 출력한다. */

    printf("\n\n");

    printf("%-10.10s%-10.10s%s\n", "Employee", "Job", "Salary");

    printf("%-10.10s%-10.10s%s\n", "--------", "---", "------");

 

    /* 배열에 쓰일 변수의 초기화 */

    array_size = 10;

    done_flag = 0;

    num_ret = 0;

 

    for (;;)

    {

        EXEC SQL EXECUTE

            /* calldemo라는 package안의 get_employees라는 procedure 호출한다.

              괄호안의 변수를은 procedure 입출력변수와 대칭된다.  Done_flag이후의

              변수들은 procedure OUT변수들이다.*/

            BEGIN calldemo.get_employees

                (:dept_no, :array_size, :num_ret, :done_flag,

                 :emp_name, :job, :salary);

            END;

        END-EXEC;

 

        print_rows(num_ret);

        /* procedure done_flag값이 true이면 종료*/

        if (done_flag)

            break;

    }

    EXEC SQL COMMIT WORK RELEASE;

    exit(0);

}

 

void

print_rows(n)

int n;

{

    int i;

    if (n == 0)

    {

        printf("No rows retrieved.\n");

        return;

    }

    /*처리된 순서대로 procedure에서 넘어온 값들을 출력한다. */

    for (i = 0; i < n; i++)

        printf("%10.10s%10.10s%6.2f\n",

               emp_name[i], job[i], salary[i]);

}

 

 

void

sql_error()

{

    char msg[512];

    size_t buf_len, msg_len;

 

    EXEC SQL WHENEVER SQLERROR CONTINUE;

 

    buf_len = sizeof(msg);

    /* sql 에러 메시지를 구하는 구문 */

    sqlglm(msg, &buf_len, &msg_len);

    printf("\nORACLE error detected:");

    printf("\n%.*s \n", msg_len, msg);

 

    EXEC SQL ROLLBACK WORK RELEASE;

    exit(1);

}

 

 

/ ****************************************************************************************

 사원데이블을 fetch하는 프로그램이다.  처리하는 로직은 stored procedure 만들어져

 있으며 EMP_DEMO_PKG 라는 이름의 package안에 open_cur라는 이름으로 되어 있다. 여기서

  procedure 안에 있는 로직을 cursor형의 임의의 변수로 잡아서 변수를 allocate라는 명령

 으로 PL/SQL block 안에서 참조할 있도록 할당해서 사용하고 있다.

***************************************************************************************/

 

#include <stdio.h>

EXEC SQL INCLUDE SQLCA ;

 

void sql_error();

 

main()

{

    char temp[32];

     EXEC SQL BEGIN DECLARE SECTION;

        char *uid = "scott/tiger";

        /*  CURSOR형으로 변수를 선언한다. ALLOCATE 반드시 같이 사용해야 한다.*/

        SQL_CURSOR  emp_cursor;

        int dept_num;

        /* Fetch 값을 저장하기 위한 STRUCTURE */

        struct

        {

            int   emp_num;

            char  emp_name[11];

            char  job[10];

            int   manager;

            char  hire_date[10];

            float salary;

            float commission;

            int   dept_num;

        } emp_info;

 

        /* Fetch 데이터의 표지변수 선언을 위한 STRUCTURE */

        struct

        {

            short emp_num_ind;

            short emp_name_ind;

            short job_ind;

            short manager_ind;

            short hire_date_ind;

            short salary_ind;

            short commission_ind;

            short dept_num_ind;

        } emp_info_ind;

    EXEC SQL END DECLARE SECTION;

 

    EXEC SQL WHENEVER SQLERROR do sql_error("Oracle error");

   

    /* Connect to Oracle. */

    EXEC SQL CONNECT :uid;

 

    /* cursor 변수를 pl/sql block 할당한다. */

    EXEC SQL ALLOCATE :emp_cursor;

 

    /* 데이터가 존재하지 않을 loop 빠져 나온다. */

    EXEC SQL WHENEVER NOT FOUND DO break;

    for (;;)

    {

        printf("\nEnter department number  (0 to exit): ");

        /* 거래처를 입력받는다 0 이면 exit */

        gets(temp);

        dept_num = atoi(temp);

        if (dept_num <= 0)

            break;

        /* procedure 실행 */

        EXEC SQL EXECUTE

            begin

                emp_demo_pkg.open_cur(:emp_cursor, :dept_num);

            end;

        END-EXEC;

 

        printf("\nFor department %d--\n", dept_num);

        printf("ENAME\t             SAL\t            COMM\n");

        printf("-----\t             ---\t            ----\n");

 

        for (;;)

        {

             EXEC SQL FETCH :emp_cursor

                 INTO :emp_info INDICATOR :emp_info_ind;

             printf("%s\t", emp_info.emp_name);

             printf("%8.2f\t\t", emp_info.salary);

             if (emp_info_ind.commission_ind != 0)

                 printf("    NULL\n");

             else

                 printf("%8.2f\n", emp_info.commission);

        }

    }

/* Close the cursor. */

    EXEC SQL CLOSE :emp_cursor;

    exit(0);

}

 

 

void

sql_error(msg)

char *msg;

{

    long clen, fc;

    char cbuf[128];

 

    clen = (long) sizeof (cbuf);

    sqlgls(cbuf, &clen, &fc);

    printf("\n%s\n", msg);

    printf("Statement is--\n%s\n", cbuf);

    printf("Function code is %ld\n\n", fc);

 

    sqlglm(cbuf, (int *) &clen, (int *) &clen);

    printf ("\n%.*s\n", clen, cbuf);

    EXEC SQL WHENEVER SQLERROR CONTINUE;

    EXEC SQL ROLLBACK WORK;

    exit(-1);

}

 

 

 

 

 

 

 

/*******************************************************************

Sample Program 11:  Dynamic SQL Method 4

You can enter multi-line statements.  The limit is 1023 characters.

This sample program only processes up to MAX_ITEMS bind variables and

MAX_ITEMS select-list items.  MAX_ITEMS is #defined to be 40.

*******************************************************************/

#include <stdio.h>

#include <string.h>

/* setjmp 함수를 쓰기 위해 선언되었다. */

#include <setjmp.h>

 

#define MAX_ITEMS         40

#define MAX_VNAME_LEN     30

#define MAX_INAME_LEN     30

 

#ifndef NULL

#define NULL  0

#endif

 

char *dml_commands[] = {"SELECT", "select", "INSERT", "insert",

                        "UPDATE", "update", "DELETE", "delete"};

 

EXEC SQL BEGIN DECLARE SECTION;

    char    dyn_statement[1024];

    EXEC SQL VAR dyn_statement IS STRING(1024);

EXEC SQL END DECLARE SECTION;

 

EXEC SQL INCLUDE sqlca;

/* SQL Descriptor AREA선언 */

EXEC SQL INCLUDE sqlda;

 

/* SQLDA TYPE 변수선언 */

SQLDA *bind_dp;

SQLDA *select_dp;

 

/* EXTERN 함수 선언 */

extern SQLDA *sqlald();

extern void sqlnul();

 

/* Define a buffer to hold longjmp state info. */

jmp_buf jmp_continue;

 

/* 에러를 검색하기 위한 전역변수. */

int parse_flag = 0;

 

main()

{

    int oracle_connect();

    int alloc_descriptors();

    int get_dyn_statement();

    int set_bind_variables();

    int process_select_list();

    int i;

 

    /* Connect to the database. */

    if (oracle_connect() != 0)

        exit(1);

 

    /* Allocate memory for the select and bind descriptors. */

    if (alloc_descriptors(MAX_ITEMS, MAX_VNAME_LEN, MAX_INAME_LEN) != 0)

        exit(1);

 

    /* Process SQL statements. */

    for (;;)

    {

        /* 프로그램의 현재상태를 저장하는 함수이다. */

        i = setjmp(jmp_continue);

 

        /* Get the statement.  Break on "exit". */

        if (get_dyn_statement() != 0)

          break;

 

        EXEC SQL WHENEVER SQLERROR DO sql_error();

 

        parse_flag = 1;     /* Set a flag for sql_error(). */

        EXEC SQL PREPARE S FROM :dyn_statement;

        parse_flag = 0;     /* Unset the flag. */

        EXEC SQL DECLARE C CURSOR FOR S;

        /* Set the bind variables for any placeholders in the SQL statement. */

        set_bind_variables();

        EXEC SQL OPEN C USING DESCRIPTOR bind_dp;

 

        process_select_list();

 

        /* Tell user how many rows processed. */

        for (i = 0; i < 8; i++)

        {

           if (strncmp(dyn_statement, dml_commands[i], 6) == 0)

           {

               printf("\n\n%d row%c processed.\n", sqlca.sqlerrd[2],

                       sqlca.sqlerrd[2] == 1 ? '\0' : 's');

               break;

           }

        }

    }       /* end of for(;;) statement-processing loop */

    /* When done, free the memory allocated for pointers in the bind and select descriptors. */

    for (i = 0; i < MAX_ITEMS; i++)

    {   

        if (bind_dp->V[i] != (char *) 0)

            free(bind_dp->V[i]);

        free(bind_dp->I[i]);   /* MAX_ITEMS were allocated. */

        if (select_dp->V[i] != (char *) 0)

            free(select_dp->V[i]);

        free(select_dp->I[i]); /* MAX_ITEMS were allocated. */

    }

 

    /* Free space used by the descriptors themselves. */

    sqlclu(bind_dp);

    sqlclu(select_dp);

 

    EXEC SQL WHENEVER SQLERROR CONTINUE;

    /* Close the cursor. */

    EXEC SQL CLOSE C;

 

    EXEC SQL COMMIT WORK RELEASE;

    puts("\nHave a good day!\n");

 

    EXEC SQL WHENEVER SQLERROR DO sql_error();

    return;

}

/* 접속을 위한 함수 */

oracle_connect()

{

    EXEC SQL BEGIN DECLARE SECTION;

        VARCHAR  username[128];

        VARCHAR  password[32];

    EXEC SQL END DECLARE SECTION;

 

    printf("\nusername: ");

    fgets((char *) username.arr, sizeof username.arr, stdin);

    fflush(stdin);

    username.arr[strlen((char *) username.arr)-1] = '\0';

    username.len = strlen((char *) username.arr);

 

    printf("password: ");

    fgets((char *) password.arr, sizeof password.arr, stdin);

    fflush(stdin);

    password.arr[strlen((char *) password.arr) - 1] = '\0';

    password.len = strlen((char *) password.arr);

 

    EXEC SQL WHENEVER SQLERROR GOTO connect_error;

    EXEC SQL CONNECT :username IDENTIFIED BY :password;

 

    printf("\nConnected to ORACLE as user %s.\n", username.arr);

 

    return 0;

 

connect_error:

    fprintf(stderr, "Cannot connect to ORACLE as user %s\n", username.arr);

    return -1;

}

 

/*

 *  Allocate the BIND and SELECT descriptors using sqlald().

 *  Also allocate the pointers to indicator variables

 *  in each descriptor.  The pointers to the actual bind

 *  variables and the select-list items are realloc'ed in

 *  the set_bind_variables() or process_select_list()

 *  routines.  This routine allocates 1 byte for select_dp->V[i]

 *  and bind_dp->V[i], so the realloc will work correctly.

 */

 

alloc_descriptors(size, max_vname_len, max_iname_len)

int size;

int max_vname_len;

int max_iname_len;

{

    int i;

 

    /*

     * The first sqlald parameter determines the maximum number of

     * array elements in each variable in the descriptor. In

     * other words, it determines the maximum number of bind

     * variables or select-list items in the SQL statement.

     *

     * The second parameter determines the maximum length of

     * strings used to hold the names of select-list items

     * or placeholders.  The maximum length of column

     * names in ORACLE is 30, but you can allocate more or less

     * as needed.

     *

     * The third parameter determines the maximum length of

     * strings used to hold the names of any indicator

     * variables.  To follow ORACLE standards, the maximum

     * length of these should be 30.  But, you can allocate

     * more or less as needed.

     */

 

    if ((bind_dp =

            sqlald(size, max_vname_len, max_iname_len)) == (SQLDA *) 0)

    {

        fprintf(stderr,

            "Cannot allocate memory for bind descriptor.");

        return -1;  /* Have to exit in this case. */

    }

 

    if ((select_dp =

        sqlald (size, max_vname_len, max_iname_len)) == (SQLDA *) 0)

    {

        fprintf(stderr,

            "Cannot allocate memory for select descriptor.");

        return -1;

    }

    select_dp->N = MAX_ITEMS;

 

    /* Allocate the pointers to the indicator variables, and the

       actual data. */

    for (i = 0; i < MAX_ITEMS; i++) {

        bind_dp->I[i] = (short *) malloc(sizeof (short));

        select_dp->I[i] = (short *) malloc(sizeof(short));

        bind_dp->V[i] = (char *) malloc(1);

        select_dp->V[i] = (char *) malloc(1);

    }

      

    return 0;

}

 

 

get_dyn_statement()

{

    char *cp, linebuf[256];

    int iter, plsql;

    int help();

 

    for (plsql = 0, iter = 1; ;)

    {

        if (iter == 1)

        {

            printf("\nSQL> ");

            dyn_statement[0] = '\0';

        }

       

        fgets(linebuf, sizeof linebuf, stdin);

        fflush(stdin);

 

        cp = strrchr(linebuf, '\n');

        if (cp && cp != linebuf)

            *cp = ' ';

        else if (cp == linebuf)

            continue;

 

        if ((strncmp(linebuf, "EXIT", 4) == 0) ||

            (strncmp(linebuf, "exit", 4) == 0))

        {

            return -1;

        }

 

        else if (linebuf[0] == '?' ||

            (strncmp(linebuf, "HELP", 4) == 0) ||

            (strncmp(linebuf, "help", 4) == 0))

        {

            help();

            iter = 1;

            continue;

        }

 

        if (strstr(linebuf, "BEGIN") ||

            (strstr(linebuf, "begin")))

        {

            plsql = 1;

        }

 

        strcat(dyn_statement, linebuf);

 

        if ((plsql && (cp = strrchr(dyn_statement, '/'))) ||

            (!plsql && (cp = strrchr(dyn_statement, ';'))))

        {

            *cp = '\0';

            break;

        }

        else

        {

            iter++;

            printf("%3d  ", iter);

        }

    }

    return 0;

}

 

 

set_bind_variables()

{

    int i, n;

    char bind_var[64];

 

    /* Describe any bind variables (input host variables) */

    EXEC SQL WHENEVER SQLERROR DO sql_error();

 

    bind_dp->N = MAX_ITEMS;  /* Initialize count of array elements. */

    EXEC SQL DESCRIBE BIND VARIABLES FOR S INTO bind_dp;

 

    /* If F is negative, there were more bind variables

       than originally allocated by sqlald(). */

    if (bind_dp->F < 0)

    {

        printf ("\nToo many bind variables (%d), maximum is %d\n.",

                    -bind_dp->F, MAX_ITEMS);

        return;

    }

 

    /* Set the maximum number of array elements in the

       descriptor to the number found. */

    bind_dp->N = bind_dp->F;

 

    /* Get the value of each bind variable as a

     * character string.

     *  

     * C[i] contains the length of the bind variable

     *      name used in the SQL statement.

     * S[i] contains the actual name of the bind variable

     *      used in the SQL statement.

     *

     * L[i] will contain the length of the data value

     *      entered.

     *

     * V[i] will contain the address of the data value

     *      entered.

     *

     * T[i] is always set to 1 because in this sample program

     *      data values for all bind variables are entered

     *      as character strings.

     *      ORACLE converts to the table value from CHAR.

     *

     * I[i] will point to the indicator value, which is

     *      set to -1 when the bind variable value is "null".

     */

    for (i = 0; i < bind_dp->F; i++)

    {

        printf ("\nEnter value for bind variable %.*s:  ",

               (int)bind_dp->C[i], bind_dp->S[i]);

        fgets(bind_var, sizeof bind_var, stdin);

 

        /* Get length and remove the new line character. */

        n = strlen(bind_var) - 1;

 

        /* Set it in the descriptor. */

        bind_dp->L[i] = n;

 

        /* (re-)allocate the buffer for the value.

           sqlald() reserves a pointer location for

           V[i] but does not allocate the full space for

           the pointer. */

 

         bind_dp->V[i] = (char *) realloc(bind_dp->V[i],

                         (bind_dp->L[i] + 1));           

 

        /* And copy it in. */

        strncpy(bind_dp->V[i], bind_var, n);

 

        /* Set the indicator variable's value. */

        if ((strncmp(bind_dp->V[i], "NULL", 4) == 0) ||

                (strncmp(bind_dp->V[i], "null", 4) == 0))

            *bind_dp->I[i] = -1;

        else

            *bind_dp->I[i] = 0;

   

        /* Set the bind datatype to 1 for CHAR. */

        bind_dp->T[i] = 1;

    }

}

 

 

 

process_select_list()

{

    int i, null_ok, precision, scale;

 

    if ((strncmp(dyn_statement, "SELECT", 6) != 0) &&

        (strncmp(dyn_statement, "select", 6) != 0))

    {

        select_dp->F = 0;

        return;

    }

 

    /* If the SQL statement is a SELECT, describe the

        select-list items.  The DESCRIBE function returns

        their names, datatypes, lengths (including precision

        and scale), and NULL/NOT NULL statuses. */

 

    select_dp->N = MAX_ITEMS;

   

    EXEC SQL DESCRIBE SELECT LIST FOR S INTO select_dp;

 

    /* If F is negative, there were more select-list

       items than originally allocated by sqlald(). */

    if (select_dp->F < 0)

    {

        printf ("\nToo many select-list items (%d), maximum is %d\n",

                -(select_dp->F), MAX_ITEMS);

        return;

    }

 

    /* Set the maximum number of array elements in the

       descriptor to the number found. */

    select_dp->N = select_dp->F;

 

    /* Allocate storage for each select-list item.

 

       sqlprc() is used to extract precision and scale

       from the length (select_dp->L[i]).

 

       sqlnul() is used to reset the high-order bit of

       the datatype and to check whether the column

       is NOT NULL.

 

       CHAR    datatypes have length, but zero precision and

               scale.  The length is defined at CREATE time.

 

       NUMBER  datatypes have precision and scale only if

               defined at CREATE time.  If the column

               definition was just NUMBER, the precision

               and scale are zero, and you must allocate

               the required maximum length.

 

       DATE    datatypes return a length of 7 if the default

               format is used.  This should be increased to

               9 to store the actual date character string.

               If you use the TO_CHAR function, the maximum

               length could be 75, but will probably be less

               (you can see the effects of this in SQL*Plus).

 

       ROWID   datatype always returns a fixed length of 18 if

               coerced to CHAR.

 

       LONG and

       LONG RAW datatypes return a length of 0 (zero),

               so you need to set a maximum.  In this example,

               it is 240 characters.

 

       */

   

    printf ("\n");

    for (i = 0; i < select_dp->F; i++)

    {

        /* Turn off high-order bit of datatype (in this example,

           it does not matter if the column is NOT NULL). */

        sqlnul (&(select_dp->T[i]), &(select_dp->T[i]), &null_ok);

 

        switch (select_dp->T[i])

        {

            case  1 : /* CHAR datatype: no change in length

                         needed, except possibly for TO_CHAR

                         conversions (not handled here). */

                break;

            case  2 : /* NUMBER datatype: use sqlprc() to

                         extract precision and scale. */

                sqlprc (&(select_dp->L[i]), &precision, &scale);

                      /* Allow for maximum size of NUMBER. */

                if (precision == 0) precision = 40;

                      /* Also allow for decimal point and

                         possible sign. */

                /* convert NUMBER datatype to FLOAT if scale > 0,

                   INT otherwise. */

                if (scale > 0)

                    select_dp->L[i] = sizeof(float);

                else

                    select_dp->L[i] = sizeof(int);

                break;

 

            case  8 : /* LONG datatype */

                select_dp->L[i] = 240;

                break;

 

            case 11 : /* ROWID datatype */

                select_dp->L[i] = 18;

                break;

 

            case 12 : /* DATE datatype */

                select_dp->L[i] = 9;

                break;

 

            case 23 : /* RAW datatype */

                break;

 

            case 24 : /* LONG RAW datatype */

                select_dp->L[i] = 240;

                break;

        }

        /* Allocate space for the select-list data values.

           sqlald() reserves a pointer location for

           V[i] but does not allocate the full space for

           the pointer.  */

 

         if (select_dp->T[i] != 2)

           select_dp->V[i] = (char *) realloc(select_dp->V[i],

                                    select_dp->L[i] + 1); 

         else

           select_dp->V[i] = (char *) realloc(select_dp->V[i],

                                    select_dp->L[i]); 

 

        /* Print column headings, right-justifying number

            column headings. */

        if (select_dp->T[i] == 2)

           if (scale > 0)

             printf ("%.*s ", select_dp->L[i]+3, select_dp->S[i]);

           else

             printf ("%.*s ", select_dp->L[i], select_dp->S[i]);

        else

            printf ("%-.*s ", select_dp->L[i], select_dp->S[i]);

 

        /* Coerce ALL datatypes except for LONG RAW and NUMBER to

           character. */

        if (select_dp->T[i] != 24 && select_dp->T[i] != 2)

            select_dp->T[i] = 1;

 

        /* Coerce the datatypes of NUMBERs to float or int depending on

           the scale. */

        if (select_dp->T[i] == 2)

          if (scale > 0)

             select_dp->T[i] = 4;  /* float */

          else

             select_dp->T[i] = 3;  /* int */

    }

    printf ("\n\n");

 

    /* FETCH each row selected and print the column values. */

    EXEC SQL WHENEVER NOT FOUND GOTO end_select_loop;

 

    for (;;)

    {

        EXEC SQL FETCH C USING DESCRIPTOR select_dp;

 

        /* Since each variable returned has been coerced to a

           character string, int, or float very little processing

           is required here.  This routine just prints out the

           values on the terminal. */

        for (i = 0; i < select_dp->F; i++)

        {

            if (*select_dp->I[i] < 0)

                if (select_dp->T[i] == 4)

                  printf ("%-*c ",(int)select_dp->L[i]+3, ' ');

                else

                  printf ("%-*c ",(int)select_dp->L[i], ' ');

            else

                if (select_dp->T[i] == 3)     /* int datatype */

                  printf ("%*d ", (int)select_dp->L[i],

                                 *(int *)select_dp->V[i]);

                else if (select_dp->T[i] == 4)     /* float datatype */

                  printf ("%*.2f ", (int)select_dp->L[i],

                                 *(float *)select_dp->V[i]);

                else                          /* character string */

                  printf ("%-*.*s ", (int)select_dp->L[i],

                                                         (int)select_dp->L[i], select_dp->V[i]);

        }

        printf ("\n");

    }

end_select_loop:

    return;

}

 

 

 

help()

{

    puts("\n\nEnter a SQL statement or a PL/SQL block at the SQL> prompt.");

    puts("Statements can be continued over several lines, except");

    puts("within string literals.");

    puts("Terminate a SQL statement with a semicolon.");

    puts("Terminate a PL/SQL block (which can contain embedded semicolons)");

    puts("with a slash (/).");

    puts("Typing \"exit\" (no semicolon needed) exits the program.");

    puts("You typed \"?\" or \"help\" to get this message.\n\n");

}

 

 

sql_error()

{

    int i;

 

    /* ORACLE error handler */

    printf ("\n\n%.70s\n",sqlca.sqlerrm.sqlerrmc);

    if (parse_flag)

        printf

        ("Parse error at character offset %d in SQL statement.\n",

           sqlca.sqlerrd[4]);

 

    EXEC SQL WHENEVER SQLERROR CONTINUE;

    EXEC SQL ROLLBACK WORK;

    longjmp(jmp_continue, 1);

}