드디어 오라클 마지막 질문 ㅜ.ㅜ

cjy1126의 이미지

답변해주신분들 정말 감사합니다.

sample 파일과 책... 가장 중요한 답변 덕에 스터디 프로젝트 임무인 select를 해냈습니다. ^^

그런데 또 문제가 생겼습니다.

제가 전해줄 파일은 db.pc proc로 컴파일한 파일인 db.c db.h 2개이고

테스트용으로 main.c 파일을 만들었습니다.

이건 db.h 입니다.

#include <stdio.h>
#include <string.h>
#include <sqlca.h>
#include <stdlib.h>
#include <sqlda.h>
#include <sqlcpr.h>

void oracle_connect();
void cursor_define();
void select_addr(char *addr[]);
void oracle_close();

이건 proc로 컴파일한 db.c 입니다.

#include "db.h"

char *username="mini";
char *password="mini";
char saddr[16];

void oracle_connect()
{
    EXEC SQL CONNECT :username IDENTIFIED BY :password;
    printf("\nConnected to ORACLE as user: %s\n", username);
}

void cursor_define()
{
    EXEC SQL DECLARE select CURSOR FOR SELECT SADDR FROM SERVER;
}

void select_addr(char *addr[])
{
    int i;

    for(i=0; i<10; i++)
        memset(addr[i], '\0', (sizeof(char)*16));

    EXEC SQL OPEN select;

    EXEC SQL WHENEVER NOT FOUND DO break;

    i=0;

    for( ; ; )
    {
        EXEC SQL FETCH select INTO :saddr;
        strcpy(addr[i], saddr);
        i++;
    }

    EXEC SQL CLOSE select;
}

void oracle_close()
{
    EXEC SQL COMMIT WORK RELEASE;
    exit(0);
}

test용 main.c 입니다.

#include "db.h"

main()
{
    char *addr[10];
    int i;

    for(i=0; i<10; i++)
        addr[i] = (char *)malloc(sizeof(char)*16);

    oracle_connect();
    cursor_define();
    select_addr(addr);

    for(i=0; addr[i]!=NULL; i++)
        printf("%s\n", addr[i]);

    oracle_close();
}

출력이 이렇게 되네요.

[root@CP36 mini]# ./db

Connected to ORACLE as user: mini
192.168.2.37
211.218.217.1
192.168.3.73
200.123.245.0






]惶홼
?
U?R?
?

횾?>
Segmentation fault
[root@CP36 mini]#

포인터 배열 10개를 미리 만들어서 select를 받았는데, 실제 데이타는 4개가 있었습니다.

오라클과 C가 NULL 개념이 달라서 그런거 같은데, 해결 방법을 모르겠습니다.

그렇다고 i<10까지 찍으면 빈줄이 6줄 생기고... 난감하네요 ㅜ.ㅜ

도움 부탁드립니다.

choissi의 이미지

패치한 후에... sqlca.sqlcode 값을 확인해서
제대로 패치 했는지 확인해봐야 겠죠

아래는 예입니다.,

/**********************************************************************/
/* 주식 계좌복구                                                      */
/**********************************************************************/
int re_geja_func_01()
{
	char geja1[12], geja2[12], id[21], start_ymd[9];
	double seed_money;
	int rtn = 0;
		
	EXEC SQL DECLARE cur_func_01 CURSOR FOR
		SELECT A.geja, NVL(B.geja, 'NULL'), A.id, A.start_ymd, A.seed_money
		FROM moredb.moresgeja a, moredb.moresyesu b
		WHERE a.geja_gb = '01' 
			AND a.geja = b.geja (+)
			AND a.end_ymd >= :Base_ymd
		order by a.geja;
			
	EXEC SQL OPEN cur_func_01;
	if (sqlca.sqlcode != 0)	{
		OUT("[%s] re_geja_func_01 : OPEN CURSOR Error [%d][%s]\n",
			pname, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);
		return -1;
	}
	
	for (;;){
	
		memset(geja1,     0x00, sizeof(geja1));
		memset(geja2,     0x00, sizeof(geja2));
		memset(id,        0x00, sizeof(id));
		memset(start_ymd, 0x00, sizeof(start_ymd));
		seed_money = 0.0;
		
		EXEC SQL FETCH cur_func_01 INTO :geja1, :geja2, :id, :start_ymd, :seed_money;
		if (sqlca.sqlcode == 1403) break;
		if (sqlca.sqlcode != 0)	{
			OUT("[%s] re_geja_func_01 : FETCH CURSOR Error [%d][%s]\n",
				pname, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc);

			EXEC SQL CLOSE cur_func_01;
			return -1;
		}	

		OUT("[%s] FETCH geja[%s]<->[%s] id[%.8s] ymd[%s] seed_money[%12.0f]\n",
			pname, geja1, geja2, id, start_ymd, seed_money);

		/* 계좌정보가 삭제되었다.. 복구 작업 시작. */
		if(memcmp(geja1, geja2, 11) != 0) {

			printf("[%s] FETCH geja[%s]<->[%s] id[%.8s] ymd[%s] seed_money[%12.0f]\n",
				pname, geja1, geja2, id, start_ymd, seed_money);

			rtn = StockGejaRegistry(start_ymd, geja1);
			if(rtn < 0) {
				OUT("[%s] re_geja_func_01 : StockGejaRegistry Error rtn[%d] ymd[%s] geja[%s] \n",
					pname, rtn, start_ymd, geja1);
				
				EXEC SQL CLOSE cur_func_01;
				return -2;				
			}
	
			rtn = StockGejaCashInOut(start_ymd, geja1, seed_money);
			if(rtn < 0) {
				OUT("[%s] re_geja_func_01 : StockGejaCashInOut Error rtn[%d] ymd[%s] geja[%s] seed[%.0f]",
					pname, rtn, start_ymd, geja1, seed_money);

				EXEC SQL CLOSE cur_func_01;
				return -3;				
			}	

		}
		
	}
	EXEC SQL CLOSE cur_func_01;
	return 1;	
}

울랄라~ 호기심 천국~!!
http://www.ezdoum.com

cjy1126의 이미지

답변 감사합니다.
그런데... 이해가 안돼요 :cry:

choissi의 이미지

      EXEC SQL FETCH cur_func_01 INTO :geja1, :geja2, :id, :start_ymd, :seed_money; 
      if (sqlca.sqlcode == 1403) break; 
      if (sqlca.sqlcode != 0)   { 
         OUT("[%s] re_geja_func_01 : FETCH CURSOR Error [%d][%s]\n", 
            pname, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc); 

         EXEC SQL CLOSE cur_func_01; 
         return -1; 
      }    

exec로 sql문을 실행하면
sqlca.sqlcode에 상태코드가 들어가게 됩니다.
그래서 select해서 아무것도 없거나 fetch가 안되거나 하면
알아 챌수가 있는거죠.

1403이 record not found 입니다.
pro*c 책 중에 이부분을 안다루는 게 없을텐데.. ^^
책 색인에서 한번 찾아보세요...

그리고 pro*c로 돌리고 나면 c파일이 생기는데,
pro*c가 만들어낸 c파일을 한번 열어서 분석해보세요
그럼 오라클이 뭘 어떻게 원하는지 pc파일을 어케 만들어야 하는지
계념을 잡는데 도움이 됩니다.

울랄라~ 호기심 천국~!!
http://www.ezdoum.com

cjy1126의 이미지

지금 oracle bible 영진책을 보고 있어서요.

pro*c가 엄청 조금 나왔네요.

설명도 거의 없고 -_-;

설명대로 sqlca.sqlcode == 1403 해도 여전히 쓰레기가 나오네요.

지금 sqlca 와 sqlcode로 맹서치중입니다 ^^

답변 감사합니다.

오늘 너무 많은걸 배워서 기쁘네요 ^^

sunyzero의 이미지

sqlcode가 -1403 이면 더이상 데이터가 없는것으로 간주합니다. 그런데 일단 0이 아니면 에러니까 루프를 벗어나야 합니다.

그리고 fetch 할때 한개씩 할때(실제로는 보통 몇십개씩 fetch하는게 더 좋고, 더 자세하게 할려면 descriptor를 사용하는게 좋죠)는 배열이 아니라 한개의 구조체를 계속 재사용합니다.

========================================
* The truth will set you free.

최종호의 이미지

보통 오라클에서 커서를 사용해서 하나의 row씩 처리하는 문의 구조는 다음과 같습니다.

declare cursor_name cursor ......;
open cursor_name ..;
while (1) {
    fetch  .. into ....;
    if (SQLCODE == 1403)
         break;
    else if (SQLCODE != 0) {
         오류처리;
         break;
    }
    받아온 데이터 처리;
}
close cursor_name;

1403은 DB 모드에 따라서 틀려질 수 있는 값이니까 define 문을 사용하셔도 되겠구요. SQLCODE는 sqlca.sqlcode를 의미하고요.

아마 님의 경우에는 WHENEVER NOT FOUND DO break;
문을 주셨으니까 SQLCODE를 검사 안해도 오라클에서 알아서 빠져나오도록
했을꺼 같습니다. 근데 다른 오류는 검사를 하지 않으셨네요. 실제로 쓰실 때에는 다른 오류도 검사하도록 하셔야 할꺼구요.

문제는 데이터를 가져온 후 출력할 때 출력조건 (NULL) 때문에 그렇습니다.
프로그램 초기에 10개의 포인터 배열을 만들고, 그 다음 문장에서 배열의 포인터들에 malloc()을 통해서 값을 할당해 주잖아요.
그러면 addr[i] 값을은 NULL이 아니라 할당된 주소를 가리키고 있습니다.

select_addr에 들어가서 해 주는 일은 각 배열의 포인터가 가리키고 있는 주소공간을 '\0' 들로 채워주고요.

for 루프를 돌면서 fetch를 통해서 DB에 들어있는 데이터를 addr[0]부터 쭈욱 채워주고요.
그리고는 select_addr()을 빠져나와서 main()에서 for loop를 이용해서 받아온 데이터를 출력하죠.
만일 4개의 데이터가 있었으면, 님께서는 addr[4] 가 NULL 이 되기를 기대하셔서 addr[4] != NULL 조건을 만족하지 않아서 for loop가 끝나기를 바랄텐데요,,

addr[4] 는 분명 '\0' 이 16개 들어있는 것은 맞지만 addr[4] 자체는 NULL 이 아닙니다. addr[4] 는 '\0'이 16개 들어있는 공간을 가리키는 주소값이 들어있습니다.

for 문을 다음과 같이 수정하시면 될 것입니다.

    for(i=0; addr[i][0]!='\0'; i++) 
        printf("%s\n", addr[i]); 

아니면 select_addr에서 몇개의 데이터를 fetch했는지 counter를 명시적으로 파라메터로 주고 받든지요.

다른 얘기 하나.
cursor 이름으로 select를 사용한 건 혼란을 일으킬만하니까 다른 것으로 바꾸시는 게 좋을 듯 합니다. ^^

xfmulder의 이미지

select 한 건수는 sqlca.sqlerrd[2] 입니다.

즉, sqlca 구조체의 멤버중에 sqlerrd 라는 이름의 int 형 배열이 있는데 이들중 3번째 ([2] 니까) 값에 실행한 횟수가 들어있습니다.
또 sqlca.sqlerrd[2]는 fetch뿐 아니라 insert, update. delete 건수도 들어간다고 하더군요.

(그럼 sqlca.sqlerrd[0], sqlca.sqlerrd[1] , sqlca.sqlerrd[3] 등은 뭐죠?)

내 자식들도 나처럼 !!

익명 사용자의 이미지

malloc.... 초기화 안되는....... NULL 이 아니라서...

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.