Pro*C 프로그램 만들기
예전에 이 프로그램을 만들기 위해서 한 번 시작 해 본적이 있다. 그 때는 별 생각없이 시작한 것이, 이왕 시작 했으면 뭐라도 한 번 만들어 봐야 할 것 아닌가 하는 생각까지 발전했고, 발전 한 김에 이렇게 펼쳐 보기 시작한 결과 물이다. msys2 + MinGW 를 사용하여 윈도우에서 구성 할 수 있는 Pro*C 프로그램을 작성하는 것이 목표이었고, 어느 정도는 할 수 있을 것이란 생각이 들기 시작 하였다.
그러면 각각의 단계 별로 기억을 더듬어 보기로 한다.
InstantClient 다운 받기
우선 오라클을 사용하기 위해서는 이 클라이언트1를 받아야 한다. Basic 하고 precompile 버전을 다 받아서 둘이 합쳐야 될 듯하다. 예전 문서2에서는 precompile 버전만다운 받으라고 되어있는 데, 이 basic 버전을 받아서 합쳐주고,
그 다음빌드에 필요한 라이브러리들을 다운 받아 MinGW에 맞도록 라이브러리를 변환해서 사용하는 것이 해본 결과 맞는방법 일 것으로 보인다.
이참에 오라클 사이트에 회원으로 가입해 보는 것도 좋은 방법이다. 자바도 있고 ...
Precompiler 다운받기3
Instant Client Basic 다운 받기4
예전에 썼던 내용 참고하기5
모두 다 다운 받아서 한 디렉토리에 다 복사해 놓는다. 충돌나는 파일들이없기 때문에 이렇게 해도 무방한 듯 보인다.
MinGW 환경에 맞는 라이브러리 만들기
이렇게 다운 받은 파일들은 dll 이라서 컴파일 하기 위한 gcc 환경에서 맞는 정보가 들어 있지 않다. 따라서 내 입맞에 맞도록 라이브러리를 추출해야만 하는 작업을 한다. 예전에 썼던 문서6도 뒤적거려 보면 좋을 듯 하다.
pexports -o orasql12.dll > orasql12.def
dlltool --def orasql12.def --dllname orasql12.dll --output-lib liborasql12.a.
pexports -o oci.dll > oci.def
dlltool --def oci.def --dllname oci.dll --output-lib liboci12.a
위의 pexports 라는 명령어가 없으면 다음 사이트7에서 다운 받는 걸로 한다.
이렇게 해서 준비가 끝났다. 그럼 오라클에서 제공하는 샘플을 돌려보기로한다.
환경 설정은 다음과 같이 잡아 준다.
export PATH=$PATH:/C/DEV/COMP/OracleClient32:/C/DEV/COMP/OracleClient32/sdk
여기서 오라클 홈은 /C/DEV/COMP/OracleClient32 라고가정한다.
샘플 코드 사용하기
그러고 나면 이제 잘 돌아 가는 지 여부를 확인해 보아야 한다. 맞는 것을 찾아서 돌려보니 아래 sample.pc 가 젤 맞는 것같아서 약간 커스터마이징 해서 써보기로 한다.
클라이언트 다운받으면 부록으로 코드가 따라온다.
Sample.pc
#include <stdio.h>
#include <string.h>
#include <sqlda.h>
#include <sqlcpr.h>
#define USERID " test"
#define PASSWD " test"
#define USING "DSN=127.0.0.1;CONNTYPE=1;PORT_NO=1521"
EXEC SQL BEGIN DECLARE SECTION;
VARCHAR uid[30];
VARCHAR pwd[30];
VARCHAR conn_opt[100];
EXEC SQL END DECLARE SECTION;
EXEC SQL INCLUDE SQLCA.H;
void main()
{
strcpy(uid.arr,USERID);
uid.len =strlen(uid.arr);
strcpy(pwd.arr,PASSWD);
pwd.len = strlen(pwd.arr);
strcpy(conn_opt.arr, USING);
conn_opt.len = strlen(USING);
EXEC SQL WHENEVER SQLERROR GOTO errexit;
EXEC SQL CONNECT :uid IDENTIFIED BY :pwd USING:conn_opt;
printf("Connected to Oracle11g using %s/%s\n", uid.arr, pwd.arr);
EXEC SQL COMMIT WORK RELEASE;
return;
errexit:
printf("Connection failed");
return;
} /* end of main */
빌드 하기
자 그럼 이 Pro*C 빌드는 두가지 과정을 거치는 데, 우선 일반적으로 확장자가 pc 인 Pro*C 파일을 만들고 나서 이를 원하는 언어로 - 이 경우에는 C - 만들어 주는 역할을 하는 것이 바로 proc 라는 실행 파일이다. 아래는 그 proc 에 의해서 소스 파일이 만들어지고, 만들어진 소스 파일을 컴파일 하고 빌드 하는 일련의 과정을 보였다.
proc iname=sample.pc include=/C/DEV/COMP/OracleClient32/precomp include=/C/DEV/COMP/OracleClient32/sdk/include include=/mingw/i686-w64-mingw32/include PARSE=PARTIAL RELEASE_CURSOR=YES MODE=ANSI CODE=ANSI_C
gcc -D_DEBUG -D_REENTRANT -c -g -r -w -O -c sample.c -o sample.o
gcc -D_CRON -D_DEBUG -o conn_demo sample.o -L/C/DEV/COMP/OracleClient32 -loci12 -lorasql12
rm sample.c
그런데, 이렇게 테스트 해보면 또 안될 경우가 있었다. 내 경우에는 접속이 잘 안되는 현상이 발생 하였는 데, 뭐가 잘못되도 잘 못 된 것이나, 이 문제를 해결하려고 여태 들어 봤지만,
한가지 결론에 도달한 것이 있다. 공부 잘하는 사람들이 잘 알고 있다는 그 비법.
생각하지 말자는 것이다. 나중에 경험이 쌓이면 자연스럽게 알게 되거나, 아니면 영원히 모를 수 있다는 것에 개의치 말자는 것이다.
그래서 접속 하는 데 문제가 생긴 듯 한데 다음과 같이 tnsnames.ora 파일을만들 듯이 연결 문자열을 작성해서 넣어 보도록 하자
#include <stdio.h>
#include <string.h>
#include <sqlda.h>
#include <sqlcpr.h>
#define SERVICE "ECS"
#define USERID "Asks_test"
#define PASSWD "Asks_test"
#define
USING "xxx/xxx@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=XXX)))"
static void sqlerror_hard() ;
EXEC SQL BEGIN DECLARE SECTION;
VARCHAR conn_opt[100];
EXEC SQL END DECLARE SECTION;
void main()
{
size_t buf_len, msg_len;
char err_msg[512];
strcpy(conn_opt.arr, USING);
conn_opt.len = strlen(USING);
EXEC SQL WHENEVER SQLERROR DO sqlerror_hard();
EXEC SQL CONNECT :conn_opt;
printf("Connected to Oracle11g using %s/%s\n", uid.arr, pwd.arr);
EXEC SQL COMMIT WORK RELEASE;
return 0;
} /* end of main */
static void sqlerror_hard()
{
EXEC SQL WHENEVER SQLERROR CONTINUE;
printf("\nCannot connect as user %s::%s\n\n", uid.arr, conn_opt.arr);
printf("\nORACLE error detected:");
printf("\n%s \n", sqlca.sqlerrm.sqlerrmc);
EXEC SQL ROLLBACK WORK RELEASE;
exit(1);
}
아래 내용은 오라클 톰 아저씨8에게 누군가 물은 내용 중에 하나다. 위의 내용 처럼 tns 문자열로 접속하니 오히려 잘 된다.
sqlplus 'scott/tiger@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost.localdomain)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=ora9ir2.kyte.com)))'
실전에 쓰일만한 라이브러리 만들기
그리고 난 다음, 이제 실제 샘플을 돌려 볼 시간 인 듯… 실전에 쓰이기는 할 수 있을 까 하는 생각이 들고 있지만, 그리고 라이브러리 자체도 현재 진행 형이다.
다음 사이트9에서 찾았음. 읽어 보아야 하나, 읽어 보고 싶지 않아서 코드만 사용 하기로 한다. 사실 내가 집중하는 분야도 아니고, 호기심 반 재미 반으로 하고 있다.
test.c 파일만 간단히 연결 문자열만 다음과 같이 바꾸어서 시작해 보는 것으로 한다. 류명환 유명한 사람인가 보다...
여하튼 Select를 데이터베이스에서 해보는 것으로 시작하려 한다.
/* --------------------------------------------------------------------------------
파일 이름 : test.c
개발 일자 : 2002-10-28
작성자 : 류명환
-------------------------------------------------------------------------------- */
….주석 생략
… 선언문 생략
int main (void)
{
코드 생략
ret =
ora_connect_tns
("test/ test@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=XXX)))");
if(ret == SUCCESS)
{
printf("success!!!\n");
}
ret = ora_setstmt (stmt);
ret = ora_select_open (&ncols);
for (i = 1; i <= ncols; i ++)
{
ret = ora_select_fetch_name (data, i);
printf ("%-10s ", data);
}
printf ("\n-------------------------------------------------------\n");
for (i = 1; i <= ncols; i ++)
{
ret = ora_select_set_column (i, VARCHAR2, MAX_DATA_LEN);
}
while (ora_select_fetch_row () == SUCCESS)
{
if(ora_sqlcode () == FETCH_DONE) break;
for (i = 1; i <= ncols; i ++)
{
ret = ora_select_fetch_data (data, i);
printf ("%-10s", data);
}
printf ("\n");
}
ret = ora_disconnect (ROLLBACK);
exit (EXIT_SUCCESS);
}
오류가 만나자고 할 경우
소스 코드 중에 select 시에
Error : -1002 -ORA-01002: fetch out of sequence
를 만나는 데 이는 ORA-00100 코드를 만났을 때다.
Oracle Error: ORA-00100
Error Description: No data found
Error Cause: An application made reference to unknown or inaccessible data.
Action: Handle this condition within the application or make appropriate modifications
to the application code. NOTE: If the application uses Oracle-mode SQL
instead of ANSI-mode SQL, ORA-01403 will be generated instead of ORA-00100.
그리고 중요한 점은 윈도우에서는 PATH에 현재 ORACLE_HOME 즉 instant client 경로가 잡혀져있어야 한다는 것이다. 나의 경우에는
Set PATH=C:\DEV\COMP\OracleClient32;%PATH%
이 정도 될 듯하다.
Insert 하기
그리고 마지막으로 Pro*C에서 코딩 후에 insert 하려고 할 때 한글이 깨질 때면 다음 사이트10를 참고 하면 된다.
일단 내 목적 데이터베이스의 문자 셑을 확인 해 보면,
select parameter, value from nls_database_parameters where parameter = 'NLS_CHARACTERSET';
다음과 같다.
그래서 필요한 변수가 NLS_LANG 이라는 변수 이고 이 변수에 다음과 같이 할당한다.
export NLS_LANG=AMERICAN_AMERICA.KO16KSC5601
내가 경험 해본 결과, iconv를 사용해서 아무리 삽질을 해도 한글이깨져서 간단하게 구글링으로 문제를 해결 하였다.
결론은 광주광역시를 한번 보기가 참으로 힘들 구나 하는 것 이였다.
끝으로
이로써, Pro*C를 사용해서 오라클에 데이터를 밀어 넣을 준비가 끝났다....라고 생각했는 데, 뜻밖에 위의 실제 라이브러리 예제를 왠만히 고쳐선 안되는 것으로 생각이 든다. 물론 내용을 안읽고 소스만 그냥 커스터마이징 하고 있어서 더 그런 생각이 들기도 한다.
도로명 주소를 특정 오라클 데이터베이스에 밀어넣는 것이 가능한 것인가에 대한 부분에 대해서 테스트 중에 있는 데 약간의 삽질이 필요 한듯하다.
github11에 올린 소스들도 아직 미완성인 채로 있다. 완성이 될 수 있을 려면 일단 크기가 아니 볼륨이 좀 있는 데이터를 얼마나 빨리 집어 넣을 수 있을 것인가에 대한 문제가 걸려서 현재 진행 중에 있다.
부록1. Select 하기
int test_arg_auto_select (const char *tnsstr)
{
int i;
int ret;
int ncols;
char stmt [MAX_SQL_LEN];
char data [MAX_DATA_LEN];
strcpy (stmt, "SELECT * FROM TB_ADDR_MAP_TEMP_REF_JIBUN WHERE BJD_CD = :bjd_cd;");
ret = ora_connect_tns(tnsstr);
if(ret == SUCCESS)
{
printf("success!!!\n");
}
ret = ora_setstmt (stmt);
ret = ora_bind_params (&ncols, "%s", "2911012200");
printf ("\n-------------------ncols %d------------------------------------\n", ncols);
for (i = 1; i <= ncols; i ++)
{
ret = ora_select_fetch_name (data, i);
printf ("%-10s ", data);
}
printf ("\n-------------------------------------------------------\n");
for (i = 1; i <= ncols; i ++)
{
ret = ora_select_set_column (i, VARCHAR2, MAX_DATA_LEN);
}
while (ora_select_fetch_row () == SUCCESS)
{
if(ora_sqlcode () == FETCH_DONE) break;
for (i = 1; i <= ncols; i ++)
{
ret = ora_select_fetch_data (data, i);
printf ("%-10s", data);
}
printf ("\n");
}
ret = ora_disconnect (ROLLBACK);
exit (EXIT_SUCCESS);
}
부록2. Insert 하기
int test_arg_auto_insert (const char *tnsstr)
{
int i;
int ret;
int ncols;
int idx;
char stmt [MAX_SQL_LEN];
char data [MAX_DATA_LEN];
char sql_qry [MAX_DATA_LEN];
idx = sprintf(sql_qry, "%s", "INSERT INTO TB_ADDR_MAP_TEMP_REF_JIBUN ");
idx += sprintf(sql_qry+idx, "%s", "( ");
idx += sprintf(sql_qry+idx, "%s", " BJD_CD, ");
idx += sprintf(sql_qry+idx, "%s", " SIDO_NM, ");
idx += sprintf(sql_qry+idx, "%s", " SIGG_NM, ");
idx += sprintf(sql_qry+idx, "%s", " EMD_NM, ");
idx += sprintf(sql_qry+idx, "%s", " RI_NM, ");
idx += sprintf(sql_qry+idx, "%s", " SAN_CK, ");
idx += sprintf(sql_qry+idx, "%s", " JIBUN_MAIN, ");
idx += sprintf(sql_qry+idx, "%s", " JIBUN_SUB, ");
idx += sprintf(sql_qry+idx, "%s", " RD_CD, ");
idx += sprintf(sql_qry+idx, "%s", " GRND_CK, ");
idx += sprintf(sql_qry+idx, "%s", " BLDNO_MAIN, ");
idx += sprintf(sql_qry+idx, "%s", " BLDNO_SUB, ");
idx += sprintf(sql_qry+idx, "%s", " EMD_SERIAL, ");
idx += sprintf(sql_qry+idx, "%s", " MOVE_CODE ");
idx += sprintf(sql_qry+idx, "%s", ") VALUES ( ");
idx += sprintf(sql_qry+idx, "%s", " :BJD_CD, ");
idx += sprintf(sql_qry+idx, "%s", " :SIDO_NM, ");
idx += sprintf(sql_qry+idx, "%s", " :SIGG_NM, ");
idx += sprintf(sql_qry+idx, "%s", " :EMD_NM, ");
idx += sprintf(sql_qry+idx, "%s", " :RI_NM, ");
idx += sprintf(sql_qry+idx, "%s", " :SAN_CK, ");
idx += sprintf(sql_qry+idx, "%s", " :JIBUN_MAIN, ");
idx += sprintf(sql_qry+idx, "%s", " :JIBUN_SUB, ");
idx += sprintf(sql_qry+idx, "%s", " :RD_CD, ");
idx += sprintf(sql_qry+idx, "%s", " :GRND_CK, ");
idx += sprintf(sql_qry+idx, "%s", " :BLDNO_MAIN, ");
idx += sprintf(sql_qry+idx, "%s", " :BLDNO_SUB, ");
idx += sprintf(sql_qry+idx, "%s", " :EMD_SERIAL, ");
idx += sprintf(sql_qry+idx, "%s", " :MOVE_CODE ");
idx += sprintf(sql_qry+idx, "%s", "); ");
strcpy (stmt, sql_qry);
ret = ora_connect_tns(tnsstr);
if(ret == SUCCESS)
{
printf("success!!!\n");
}
ret = ora_setstmt (stmt);
if(ret == SUCCESS)
{
printf("success!!!\n");
}
ret = ora_bind_u_params (
"%s %s %s %s %s %s %s %s %s %s %s %s %s %s",
"2911012200","광주광역시","동구","학동","",
"0","81","0","291102009001","0","15","0","2967", "");
ret = ora_execute (&ncols);
if(ret == SUCCESS)
{
printf("insert success!!![%d]\n", ncols);
}
ret = ora_disconnect (ROLLBACK);
exit (EXIT_SUCCESS);
}
참고로 나는 학동이 어딘지 모른다...
이상.
1. http://www.oracle.com/technetwork/topics/winx64soft-089540.html
2. http://blog.naver.com/tommybee/50193025599
3. http://www.oracle.com/technetwork/topics/precomp-112010-084940.html
4. http://www.oracle.com/technetwork/topics/winsoft-085727.html
5. http://blog.naver.com/tommybee/50193025599
6. http://blog.naver.com/tommybee/50192960394
7. https://sourceforge.net/projects/mingw/files/MinGW/Extension/pexports/pexports-0.47/
8. https://asktom.oracle.com/pls/asktom/f?p=100:11:0::NO::P11_QUESTION_ID:45033135081903
9. http://www.joinc.co.kr/w/Site/Database/Book/ProcPrograming/8.Dynamic_SQL
'프로그래밍' 카테고리의 다른 글
SpringBoot를 사용해서 20분만에 만드는 유연한 CRUD 애플리케이션 빌드하기 (0) | 2023.03.20 |
---|---|
MinGW과 Pro*C (0) | 2023.03.19 |
[Win32]현재 디렉토리 내 모든 파일 리스트 만들기 (1) | 2023.03.16 |
sqlite + kyotocabinet 사용 (0) | 2023.03.15 |
Maraa 와 wiringPi 라이브러리로 라즈베리 파이에서 센서 제어해 보기 (0) | 2023.03.13 |