Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Note

하드를 정리하다가 예전에 프로젝트했던 문서에서 OTL 관련 내용을 발견했다. C++ 을 써 본지도 오래 됐고 앞으로 쓰게 될지도 모르겠지만 당시에 고민했던 내용을 해결하도록 도와준 고마운 라이브러리라 개인적인 기록 차원에서 정리해 본다.

OTL을 처음 써 본게 2000년이고 마지막으로 쓴게 2007년이고 예전 자료와 기억을 토대로 기술하므로 현재의 상황과는 많이 다를수 있다.

C 언어로 오라클에서 데이타를 가져오고 넣고 했던 업무를 처음했던때가 1999년 이었던 것 같다. 지금이야 Java와 JDBC 가 있고 더 나아가 Mybatis나 Hibernate 같은 ORM 을 사용하지만 당시에 DB를 다루는 작업은 녹녹치가 않았다.

 

DBMS 를 오라클을 사용한다면 Pro*C 는 어쩔수 없이 선택해야 하는 방안이었다. 당시의 Pro*c 는 아주 형편없는 물건이라 .pc 파일을 전처리할때 C++ 형식의 주석인 // 가 있으면 아무 에러 메시지없이 segmentation fault 를 내뱉고 죽어버릴 정도였다.

또 전처리 방식이므로 .pc 를 변환해서 .c 를 만들고 이것을 컴파일하는 복잡한 과정을 거쳐야 했고 디버깅도 번거로웠다.

Expand
titlePro*C 사용예제

당시에는 저런 삽질을 해야 했었다..

Code Block
EXEC SQL BEGIN DECLARE SECTION;
sql_context ctx1[CONTEXT_MAX_COUNT];            /* declare global context ctx1     */
EXEC SQL END DECLARE SECTION;
 
EXEC SQL  TYPE my_long IS LONG VARCHAR(1000000) REFERENCE;


int oracleConnect(char* szAlias, char* szUser, char* szPass, int nContextCount)
{
    EXEC SQL BEGIN DECLARE SECTION;
        VARCHAR  username[128];
        VARCHAR  password[32];
		VARCHAR  alias[128];
		int nCount = 0;
	EXEC SQL END DECLARE SECTION;
		memset(username.arr, 0x00, sizeof(username.arr));
		username.len = 0;
		memset(password.arr, 0x00, sizeof(password.arr));
		password.len = 0;
		memset(alias.arr, 0x00, sizeof(alias.arr));
		alias.len = 0;
	strcpy((char*)username.arr, szUser);
    username.len = (unsigned short)strlen(szUser);
	strcpy((char*)password.arr, szPass);
    password.len = (unsigned short)strlen(szPass);

	EXEC SQL WHENEVER SQLERROR GOTO connect_error;
	if(strlen(szAlias) > 0)
	{
		strcpy((char*)alias.arr, szAlias);
		alias.len = (unsigned short)strlen(szAlias);
	
		EXEC SQL CONTEXT USE :ctx1[nContextCount];
		EXEC SQL CONNECT :username IDENTIFIED BY :password USING :alias;	
	}
	else
	{
		EXEC SQL CONTEXT USE :ctx1[nContextCount];
		EXEC SQL CONNECT :username IDENTIFIED BY :password ; 

	}   
		
	return STAT_OK;
connect_error:

	return(sqlca.sqlcode);
}
 
int oracleClose()
{
    EXEC SQL WHENEVER SQLERROR CONTINUE; 
	int nCount = 0;
	for (nCount=0; nCount<g_nContextCount; nCount++)
	{
		EXEC SQL CONTEXT USE :ctx1[nCount];
		EXEC SQL COMMIT WORK RELEASE;
	}

	return STAT_OK;
}
 
int oracleSelectPolicyList(char* sqlText, PolicyList **head, int nContextNumber)
{
	EXEC SQL BEGIN DECLARE SECTION; 
		int	 result; 
		int	 general = 0;
        VARCHAR	PolicyID[50]; 
        VARCHAR	PolicyCode[10]; 
        VARCHAR	Type[10]; 
        VARCHAR	UsageType[10]; 
    EXEC SQL END DECLARE SECTION;  
	PolicyList *first, *second;
		memset(PolicyID.arr, 0x00, sizeof(PolicyID.arr));
		PolicyID.len = 0;
		memset(PolicyCode.arr, 0x00, sizeof(PolicyCode.arr));
		PolicyCode.len = 0;
		memset(Type.arr, 0x00, sizeof(Type.arr));
		Type.len = 0;
		memset(UsageType.arr, 0x00, sizeof(UsageType.arr));
		UsageType.len = 0;
    EXEC SQL WHENEVER SQLERROR CONTINUE;
#ifdef _USER_THREAD
	EXEC SQL CONTEXT USE :ctx1[nContextNumber];
#endif
	EXEC SQL PREPARE ORA_STMT FROM :sqlText; 
    EXEC SQL DECLARE ORA_CUR_SELECT CURSOR  FOR ORA_STMT; 
    EXEC SQL OPEN ORA_CUR_SELECT ; 
	while(1) 
	{
		EXEC SQL FETCH ORA_CUR_SELECT INTO :PolicyID, :PolicyCode, :Type, :UsageType; 
		result = sqlca.sqlcode;
        if(result == 1403) 
		{
            break;
        } 
		else if((result < 0) && (result != ORACLE_STAT_NULL_RETURN)) 
		{
            EXEC SQL CLOSE ORA_CUR_SELECT;
            return result;
        }
		
		if(general == 0) 
		{
			*head = (PolicyList *)calloc(1, sizeof(PolicyList));
			strncpy((*head)->PolicyID,   (char *)PolicyID.arr,   PolicyID.len);
			strncpy((*head)->PolicyCode, (char *)PolicyCode.arr, PolicyCode.len);
			strncpy((*head)->Type,       (char *)Type.arr,       Type.len);
			strncpy((*head)->UsageType,  (char *)UsageType.arr,       UsageType.len);
			(*head)->next = NULL;
			second        = *head;
			general++;
			continue;
		}  
		first = (PolicyList *)calloc(1, sizeof(recordPolicyificateList));
		strncpy(first->PolicyID,   (char *)PolicyID.arr,   PolicyID.len);
		strncpy(first->PolicyCode, (char *)PolicyCode.arr, PolicyCode.len);
		strncpy(first->Type,       (char *)Type.arr,       Type.len);
		strncpy(first->UsageType,  (char *)UsageType.arr,  UsageType.len);
		first->next  = NULL;
		second->next = first;
		second       = second->next;
		first        = NULL;
		general++;
	}
    EXEC SQL CLOSE ORA_CUR_SELECT; 
	if(general > 0)
		return(0);
	return(result);
}

 

 

C 언어를 써야 했다면 ODBC 를 쓰면 되지 않냐는 궁금증도 있을 것이다. 당시는 99년이었고 윈도 NT의 위상은 형편없어서 대부분의 서버는 Solaris, AIX, HP-UX 등의 상용 유닉스를 쓰던 시절이었다.

ODBC 는 MS 가 제정한 규격이었고 오라클같은 DBMS 제조사에서는 윈도외에는 ODBC 용 드라이버를 제공하지 않았다. 

그래서 유닉스용 ODBC 를 제공하는 외국의 솔루션 업체가 있었는데 가격이 매우 비쌌고 UnixODBC 같은 오픈소스 프로젝트가 있었지만 안정성이 검증되지 않고 프로젝트에 도입할 수가 없었다.

 

Pro*C 를 사용해서 프로젝트를 몇 개 하고 슬슬 대안을 찾던 중에 OCI(Oracle Call Interface) 라는 걸 알게 되었다.

이건 전처리를 통과하지 않아도 되는 순수 C 로 된 라이브러리였고 막강한 기능을 제공했었다.

Ref

...