01
27

ODBC 관리자

ODBC 관리자

ODBC 관리자에 해당 사용할 DBMS 드라이버가 있다면

ODBC로 데이터베이스를 불러온 준비가 되었다.

 

 

본인은 MS사의 Access를 사용할 것이므로 MS 드라이버를 다운로드하였다.

설치 방법은 바로 이전 포스팅 참고하면 된다.

 

ODBC 핸들 종류 및 할당 및 해제

ODBC핸들에는 종류가 4가지가 있다.

 

핸들 종류

 

환경 핸들, 연결(접속) 핸들, 명령 핸들, 설명 핸들이 그것이다.

 

할당 함수

1
2
3
4
SQLRETURN SQLAllocHandle(  
      SQLSMALLINT   HandleType,//할당하고자 핸들 타입
      SQLHANDLE     InputHandle,//생성할 부모 핸들 지정
      SQLHANDLE *   OutputHandlePtr);// 생성할 핸들의 주소
cs

 

파라미터에 부모 핸들 지정이 있는데, 

환경 핸들을 만들고 환경 핸들 갖고 접속 핸들을 만들고

접속핸들을 가지고 접속에 성공하면 명령 핸들을 상속한다.

상속관계를 가지고 있다.

 

반환 값이 SQLRETURN 인데, 성공하면 SQL_SUCCESS를 반환한다. 

SQL_ERROR 또는 SQL_SUCCESS_WITH_INFO 플래그가 뜬다면

에러 처리를 하기 위한. SQLError() 함수가 있다.

 

해제 함수

1
2
3
SQLRETURN SQLFreeHandle(  
     SQLSMALLINT   HandleType, //해제할 핸들 ㅏ입
     SQLHANDLE     Handle);  // 해제 
cs

 


환경 핸들, 연결(접속) 핸들, 명령 핸들을 사용해서

MS Access 파일인 User_DB.accdb 파일을 불러와보자.

파일

1. ODBC 헤더 파일, 라이브러리를 추가한다.

ODBC Core functions을 하는 헤더와 라이브러리 추가

#include <sql.h>, #include <sqlext.h>

링크 : obdc32.lib

라이브러리는 기본으로 프로젝트에 링크가 되어있다.

 

2. 환경 핸들 할당

핸들 선언

우선 환경 핸들을 SQLAllocHandle()함수로 할당한다.

 

 

그런 후에 환경 버전 처리를 하는 SQLSetEnvAttr()함수를 호출한다.

 

 

3. 연결 핸들 할당 및 연결 시도 

환경 핸들을 부모로 두어 접속 핸들 할당한다.

 

 

그런 다음 접속 시도인 SQLDriverConnect()함수를 호출한다.

SQLDriverConnect()를 하기 위해서 드라이버 경로를 입력해줘야 한다.

 

하드코딩

 

위 방법처럼 직접 드라이버를 입력하는 건 좋지 않다.

.dsn 파일의 규격의 텍스트 파일을 읽어서 연결해야 한다. 

 

아래는 해당 프로젝트 안에 있는. dsn 파일을 읽어온 코드이다.

 

4. 명령 핸들 할당 및 명령

드라이버의 연결이 완료가 되었다면 명령 핸들을 할당해서 명령을 한다.

연결이 되었다면 SQLAllocHandle 함수로 명령 핸들 할당한다. 

 

 

명령문(쿼리문)부터 보자

 

위 사진은 유저 테이블 안에 모든 값을 조회해달라는 쿼리문이다.

SQLExecDirect 바로 명령어 처리하는 함수이다.

1
2
3
4
SQLRETURN SQLExecDirect(  
     SQLHSTMT     StatementHandle, // 명령 핸들
     SQLCHAR *    StatementText,  // 실행할 SQL문
     SQLINTEGER   TextLength);  // SQL문의 문자열 
cs

 

Select문, Update문, Insert문, Delete문 모두 가능하다.

다루지는 않겠다. 

Select 문으로 그 값을 개발자가 조회해야 하는데,

조회된 값을 얻어오기 위해서는

c언어 변수와 테이블 변수와 연동을 해야 한다.  (Bind)

 

SQLBindCol - 변수 Binding

1
2
3
4
5
6
7
SQLRETURN SQLBindCol(  
      SQLHSTMT       StatementHandle, //명령 핸들 
      SQLUSMALLINT   ColumnNumber,  //바인딩될 컬럼의 번호
      SQLSMALLINT    TargetType,  //바인딩 되는 변수의 데이터 타입
      SQLPOINTER     TargetValuePtr,  // 결과값 저장할 버퍼
      SQLLEN         BufferLength,  //버퍼의 길이
      SQLLEN *       StrLen_or_IndPtr);  //컬럼의 길이나 상태를 리턴
cs

 

반복문으로 결과 집합에서 다음 데이터 행 집합을 가져오고

바인딩된 모든 열에 대한 데이터를 반환하는 함수 호출.

SQLFetch() 함수 - 바인딩된 모든 열 데이터 반환

1
2
SQLRETURN SQLFetch(  
     SQLHSTMT     StatementHandle);  
cs

 

데이터 SQL_NO_DATA 데이터가 없을 때까지 커서를 통해서 조회를 한다.

시작 하기 전의 커서 상태 블록 커서는 결과 집합의 시작 부분 앞에 배치 됩니다. 새 행 집합의 첫 번째 행이 결과 집합의 시작 앞에 있으면 Sqlfetch 는 SQL_NO_DATA을 반환 합니다.
종료 후의 커서 상태 블록 커서는 결과 집합의 끝 뒤에 배치 됩니다. 새 행 집합의 첫 번째 행이 결과 집합의 끝 뒤에 있으면 Sqlfetch 는 SQL_NO_DATA을 반환 합니다.

SQL_NO_DATA는 데이터가 커서의 위치에서 데이터가 없을때 뜬다.

while (SQLFetch(handle_HSTMT) != SQL_NO_DATA)로 

커서 위치에 데이터가 없을 때까지 출력하도록 한다.

만약 커서를 다 사용했다면 SQLCloseCursor(명령핸들); 로 커서를 초기화한다.

 

 

 

결과 

성공적으로 DB에 있는 데이터를 Select로 전체 조회를 했다. 

 

전체 코드

더보기
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <tchar.h>
#include <string>
#include <sql.h>
#include <sqlext.h>
#include <iostream>
//SQLHANDLE = void* 임 이름만 다름
SQLHENV handle_HENV; // 환경핸들
SQLHDBC handle_HDBC; // 접속핸들
SQLHSTMT handle_HSTMT; // 명령핸들
void Check();
void main()
{
    setlocale(LC_ALL, "");
    if (SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &handle_HENV) != SQL_SUCCESS)
    {
        return;
    }
    //환경 설정 환경 버젼 선택
    if (SQLSetEnvAttr(handle_HENV, SQL_ATTR_ODBC_VERSION,
         (SQLPOINTER)SQL_OV_ODBC3_80, SQL_IS_INTEGER) != SQL_SUCCESS)//버젼처리
    {
        return;
    }
    //환경 핸들 갖고 접속핸들 만듬 (환경핸들이 부모)
    if (SQLAllocHandle(SQL_HANDLE_DBC, handle_HENV, &handle_HDBC) != SQL_SUCCESS)
    {
        return;
    }
 
    //접속을 해야함 유니코드 버젼으로 하겠다. 버젼선택 mdb, accdb 파일을 읽어온다.
    //파일 경로 스트링
    SQLWCHAR dir[MAX_PATH] = { 0, };
    GetCurrentDirectory(MAX_PATH, dir);
    std::wstring dbpath = dir;
    dbpath += L"\\User_DB.dsn";
 
    TCHAR inCon[256= { 0, };
    TCHAR outCon[256= { 0, };
 
    //연결 Connect. _countof 문자열의 길이
    //원래는 .dsn 파일의 규격의 텍스트 파일을 읽어서 연결해야한다. 
    _stprintf(inCon, _T("FileDsn=%s"), dbpath.c_str());
    
    SQLSMALLINT cbOut_Len;
    SQLRETURN ret = SQLDriverConnect(handle_HDBC, NULL, inCon,_countof(inCon),
                    outCon, _countof(outCon), &cbOut_Len, SQL_DRIVER_NOPROMPT);
 
    //접속이 성공하면 success가 뜨는데 완벽한 성공, 두번째는 부족한대 성공 
    if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO)
    {
        Check();
        return;
    }
 
    if (SQLAllocHandle(SQL_HANDLE_STMT, handle_HDBC, &handle_HSTMT) != SQL_SUCCESS)
    {
        return;
    }
 
    SQLLEN lid = SQL_NTS;
    SQLLEN lname = SQL_NTS;
    SQLLEN llevel = SQL_NTS;
    int user_id = 0;
    int user_level= 0;
    TCHAR user_name[20= { 0, };
 
    //결과를 저장 바인드
    //첫번째 필드로 바인드 각각의 레코드를 반환해준다. 그중에 1번 필드
    //명령 핸들에 사용자 c++ 변수를 바인딩해줌 메모리 연결
    ret = SQLBindCol(handle_HSTMT, 1, SQL_C_ULONG, &user_id, 0&lid);
    ret = SQLBindCol(handle_HSTMT, 2, SQL_UNICODE, user_name, sizeof(user_name), &lname);
    ret = SQLBindCol(handle_HSTMT, 3, SQL_C_ULONG, &user_level, 0&llevel);
 
    //쿼리문 SQL문 demogame 테이블에서 모든 값을 출력
    TCHAR sql[MAX_PATH] = L"select * from user_table";
    ret = SQLExecDirect(handle_HSTMT, (SQLTCHAR*)&sql, SQL_NTS);
 
    // 행을 반환할 때 바인딩된 각 열에 대한 데이터를 
    // 해당 열에 바인딩된 버퍼에 넣는다
    while (SQLFetch(handle_HSTMT) != SQL_NO_DATA)
    {
        std::wcout << L"번호 : " << user_id << L"\t이름 : " <<
            user_name << L"\t레벨 : " << user_level<< std::endl;
    }
    SQLCloseCursor(handle_HSTMT);
 
    SQLFreeHandle(SQL_HANDLE_STMT, handle_HSTMT);
    SQLDisconnect(handle_HDBC);
    SQLFreeHandle(SQL_HANDLE_DBC, handle_HDBC);
    SQLFreeHandle(SQL_HANDLE_ENV, handle_HENV);
}
void Check()
{
    SQLTCHAR szSQLState[SQL_SQLSTATE_SIZE + 1];
    SQLTCHAR errorBuffer[SQL_MAX_MESSAGE_LENGTH + 1];
    SQLINTEGER iSQLCode;
    SQLSMALLINT length;
    SQLError(handle_HENV, handle_HDBC,
        handle_HSTMT,
        szSQLState,
        &iSQLCode,
        errorBuffer,
        sizeof(errorBuffer),
        &length);
    MessageBox(NULL, errorBuffer, szSQLState, MB_OK);
}
cs

 

COMMENT