사용자 도구

사이트 도구


kb:odbc

ODBC

윈도우즈계에서의 데이터베이스 접근을 위한 표준 인터페이스. SqlServer 같이 네이티브 API를 제공하지 않는 DBMS의 경우에는 ODBC를 사용해야한다. 그런데 API도 지저분하고, 설명도 난잡하고, 여러 모로 짜증 좀 나네…

Tips

각종 핸들 속성 정리

MSDN이 보기 어렵게 되어 있어, 따로 정리했다. 근데 뭔가 모르는 것들이 잔뜩 있네.

Environment

SQLSetEnvAttr

속성 타입 가능한 값 의미
SQL_ATTR_CONNECTION_POOLING UINT SQL_CP_OFF
SQL_CP_ONE_PER_DRIVER
SQL_CP_ONE_PER_HENV
커넥션 풀링 on/off
SQL_ATTR_CP_MATCH UINT SQL_CP_STRICT_MATCH
SQL_CP_RELAXED_MATCH
커넥션 풀링 설정
SQL_ATTR_ODBC_VERSION UINT SQL_OV_ODBC3
SQL_OV_ODBC2
드라이버 설정
SQL_ATTR_OUTPUT_NTS UINT SQL_TRUE
SQL_FALSE
문자열 취급 방법 설정

Connection

SQLSetConnectAttr

속성 타입 가능한 값 의미 설정 가능 시점
SQL_ATTR_ACCESS_MODE UINT SQL_MODE_READ_ONLY
SQL_MODE_READ_WRITE
읽기, 쓰기 Either
SQL_ATTR_ASYNC_ENABLE UINT SQL_ASYNC_ENABLE_OFF
SQL_ASYNC_ENABLE_ON
비동기 모드 Either
SQL_ATTR_AUTOCOMMIT UINT SQL_AUTOCOMMIT_OFF
SQL_AUTOCOMMIT_ON
자동 커밋 Either
SQL_ATTR_CONNECTION_DEAD UINT SQL_CD_TRUE
SQL_CD_FALSE
커넥션 ALIVE 여부 After
SQL_ATTR_CONNECTION_TIMEOUT UINT 쿼리 후 기다릴 시간, 0 == INFINITE Either
SQL_ATTR_CURRENT_CATALOG STRING 데이터베이스 이름 현재 작업 중인 데이터베이스 Either
SQL_ATTR_LOGIN_TIMEOUT UINT 로그인 성공/실패때까지 기다릴 시간, 0 == INFINITE Before
SQL_ATTR_METADATA_ID UINT SQL_TRUE
SQL_FALSE
대소문자 구별. 이외에도 뭔가 있다. Either
SQL_ATTR_ODBC_CURSORS UINT SQL_CUR_USE_IF_NEEDED
SQL_CUR_USE_ODBC
SQL_CUR_USE_DRIVER
커서 종류 설정 Before
SQL_ATTR_PACKET_SIZE UINT 바이트 크기 네트워크를 통해 오가는 패킷 크기 Before
SQL_ATTR_QUIET_MODE UINT 32비트 윈도우 핸들 NULL이면 대화창 표시하지 않는다. Either
SQL_ATTR_TRACE UINT SQL_OPT_TRACE_OFF
SQL_OPT_TRACE_ON
트레이스 여부 Either
SQL_ATTR_TRACEFILE STRING 파일 이름 트레이스 파일 이름 Either
SQL_ATTR_TRANSLATE_LIB STRING 파일 이름 아 몰라 After
SQL_ATTR_TRANSLATE_OPTION UINT 32비트 플래그 아 몰라 After
SQL_ATTR_TXN_ISOLATION UINT 32비트 비트 마스크. SQL_TXN_ISOLATION_OPTIONS 참고 트랜잭션 락 레벨 Either

Statement

SQLSetStmtAttr

속성 타입 가능한 값 의미
SQL_ATTR_APP_PARAM_DESC UINT APD에 대한 핸들, SQL_NULL_DESC APD가 뭐지?
SQL_ATTR_APP_ROW_DESC UINT ARD에 대한 핸들, SQL_NULL_DESC ARD가 뭐지?
SQL_ATTR_ASYNC_ENABLE UINT SQL_ASYNC_ENABLE_OFF
SQL_ASYNC_ENABLE_ON
비동기 처리 설정
SQL_ATTR_CONCURRENCY UINT SQL_CONCUR_READ_ONLY
SQL_CONCUR_LOCK
SQL_CONCUR_ROWVER
SQL_CONCUR_VALUES
락 설정
SQL_ATTR_CURSOR_SCROLLABLE UINT SQL_NONSCROLLABLE
SQL_SCROLLABLE
커서 스크롤 설정
SQL_ATTR_CURSOR_SENSITIVITY UINT SQL_UNSPECIFIED
SQL_INSENSITIVE
SQL_SENSITIVE
결과를 다른 커서와 공유하는가?
SQL_ATTR_CURSOR_TYPE UINT SQL_CURSOR_FORWARD_ONLY
SQL_CURSOR_STATIC
SQL_CURSOR_KEYSET_DRIVEN
SQL_CURSOR_DYNAMIC
커서 종류 설정
SQL_ATTR_ENABLE_AUTO_IPD UINT SQL_FALSE
SQL_TRUE
?
SQL_ATTR_FETCH_BOOKMARK_PTR UINT 포인터 이진 북마크에 대한 값이라는데, 이건 또 뭐지?
SQL_ATTR_IMP_PARAM_DESC UINT IPD에 대한 핸들 IPD가 뭐지?
SQL_ATTR_IMP_ROW_DESC UINT IRD에 대한 핸들 IRD가 뭐지?
SQL_ATTR_KEYSET_SIZE UINT 행의 수 키셋 기반 커서에서 다룰 행의 수
SQL_ATTR_MAX_LENGTH UINT 바이트 길이 결과셋 안의 문자열 혹은 바이너리 컬럼의 최대 길이
SQL_ATTR_MAX_ROWS UINT 행의 수 결과셋의 최대 행 수
SQL_ATTR_METADATA_ID UINT SQL_TRUE
SQL_FALSE
대소문자 구별 여부. 이외에도 뭔가 있다.
SQL_ATTR_NOSCAN UINT SQL_NOSCAN_OFF
SQL_NOSCAN_ON
이스케이프 문자 검색 여부
SQL_ATTR_PARAM_BIND_OFFSET_PTR UINT* 포인터 옵셋 변수에 대한 포인터 동적 파라미터 바인딩 시에 더하는 값이라는데 뭔지 모르겠다.
SQL_ATTR_PARAM_BIND_TYPE UINT SQL_PARAM_BIND_BY_COLUMN
or 구조체의 크기
음, 나중에 읽어보자
SQL_ATTR_PARAM_OPERATION_PTR USINT* SQL_PARAM_PROCEED, SQL_PARAM_IGNORE 값으로 이루어진 배열에 대한 포인터 배열을 한번에 바인딩하는 것과 관련된 속성인 듯?
SQL_ATTR_PARAM_STATUS_PTR USINT* 배열의 포인터 쿼리 실행 결과를 받아오기 위한 배열
SQL_ATTR_PARAMS_PROCESSED_PTR UINT* 배열의 포인터 리 실행 결과를 받아오기 위한 배열
SQL_ATTR_PARAMSET_SIZE UINT 한 파라미터에 들어있는 값의 숫자 배열을 바인딩하기 위해 설정하는 값
SQL_ATTR_QUERY_TIMEOUT UINT 쿼리를 수행하고 결과셋을 기다릴 시간, 0 == INFINITE
SQL_ATTR_RETRIEVE_DATA UINT SQL_RD_ON
SQL_RD_OFF
커서가 움직이면 데이터를 바로 가져오는지 여부
SQL_ATTR_ROW_ARRAY_SIZE UINT 행의 수 결과셋을 페치할 때마다 가져올 행의 수
SQL_ATTR_ROW_BIND_OFFSET_PTR UINT* ? ?
SQL_ATTR_ROW_BIND_TYPE UINT ? ?
SQL_ATTR_ROW_NUMBER UINT 행 번호 현재 행이 전체 결과셋에서 차지하는 번호
SQL_ATTR_ROW_OPERATION_PTR USINT* ? ?
SQL_ATTR_ROW_STATUS_PTR USINT*
SQL_ATTR_ROWS_FETCHED_PTR UINT* 행 갯수를 집어넣을 변수의 포인터 결과셋을 가져올 때마다 안의 행 숫자를 집어넣을 변수 설정
SQL_ATTR_SIMULATE_CURSOR UINT SQL_SC_NON_UNIQUE
SQL_SC_TRY_UNIQUE
Positioned update and delete statements?
SQL_ATTR_USE_BOOKMARKS UINT SQL_UB_OFF
SQL_UB_VARIABLE
북마크 사용 여부

BLOB/TEXT 컬럼 사용시 유의점

SQLGetData 함수를 이용해 BLOB/TEXT 컬럼을 읽어올 때에는, BLOB/TEXT 컬럼이 SELECT 구문의 마지막에 오도록 해야한다.

SELECT ID, BLOB_COLUMN, INT_COLUMN FROM SomeTable -- ERROR
SELECT ID, INT_COLUMN, BLOB_COLUMN FROM SomeTable -- OK

자세한 사항은 Getting Long Data 페이지를 참고.

There are a number of restrictions on using SQLGetData. In general, columns accessed with SQLGetData:

  • Must be accessed in order of increasing column number (because of the way the columns of a result set are read from the data source). For example, it is an error to call <code>SQLGetData</code> for column 5 and then call it for column 4.
  • Cannot be bound.
  • Must have a higher column number than the last bound column. For example, if the last bound column is column 3, it is an error to call SQLGetData for column 2. For this reason, applications should be careful to place long data columns at the end of the select list.
  • Cannot be used if <code>SQLFetch</code> or <code>SQLFetchScroll</code> was called to retrieve more than one row.''

Connection String

SQLDriverConnect에 사용하는 주소 문자열.

connection-string ::= empty-string[;] | attribute[;] | attribute; connection-string
empty-string ::=
attribute ::= attribute-keyword=attribute-value | DRIVER=[{]attribute-value[}]
attribute-keyword ::= DSN | UID | PWD
         | driver-defined-attribute-keyword
attribute-value ::= character-string
driver-defined-attribute-keyword ::= identifier

SQL Server

DRIVER=SQL Server;SERVER=myserver;DATABASE=testdb;Trusted_Connection=Yes
DRIVER=SQL Server;SERVER=myserver;DATABASE=testdb;UID=testuser;PWD=testpwd

File DSN

FILEDSN=C:\sample.dsn

sample.dsn

DRIVER = SQL Server
UID = Larry
DATABASE = MyDB

:!: File DSN에는 패스워드를 지정할 수 없다.

Scrollable Cursor

별다른 옵션을 주지 않을 경우, 기본적으로 생성되는 커서는 forward-only 커서다. 커서를 앞뒤로 움직이거나, 특정 옵셋으로 가기 위해서는 scrollable cursor 를 사용해야한다.

// Statement 핸들에다가 사용할 커서의 타입과 한번에 가져올 행의 갯수를 설정한다.
SQLSetStmtAttr(hStmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)1, 0);
SQLSetStmtAttr(hStmt, SQL_ATTR_CURSOR_SCROLLABLE, (SQLPOINTER)SQL_SCROLLABLE, 0);
...
// SQLFetchScroll 함수를 통해 결과셋을 가져온다.
SQLFetchScroll(hStmt, SQL_FETCH_NEXT, 0);
SQLFetchScroll(hStmt, SQL_FETCH_PRIOR, 0);

Connection Pooling

커넥션 풀링이 뭐하는 기능인지는 이름이 다 말해주고 있다. 서버와의 커넥션을 만드는 비용이 비싸기 때문에 애플리케이션 레벨에서 커넥션을 닫아도 실제로 닫지는 않고 있다가 해당 서버와의 연결 요청이 들어오면 기존의 커넥션을 재이용하는 기능이다. 기본적으로 커넥션 스트링을 이용해 커넥션을 구분한다. 같은 서버에 연결한다고 하더라도 커넥션 스트링이 한 바이트라도 틀리면 다른 커넥션으로 인식한다는 이야기다.

커넥션 풀링을 사용할 때 주의할 점은 다음과 같다.

  • 데이터베이스 변경 (USE XXX)
  • 세팅 변경 (SET NOCOUNT ON)
  • 임시 객체 생성 (커넥션이 '풀링'되므로 커넥션 자체가 사라지기 전까지 계속 존재하게 된다)

커넥션 풀링을 사용할 때 살펴볼 값 중의 하나가 CPTimeout 값과 RetryWait 값이다. CPTimeout 값은 사용하고 있지 않은 커넥션을 풀에 담아둘 시간이고, RetryWait 값은 서버에서 응답이 없을 경우, 다음 커넥션 재생성을 위해 기다릴 시간이다.

RetryWait 값은 제어판 → 관리도구 → 데이터원본 (ODBC) → 연결 풀링 탭에서 수정할 수 있다. CPTimeout 값은 해당 드라이버를 더블 클릭하면 뜨는 윈도우에서 수정할 수 있다. 레지스트리에서도 수정할 수 있다. HKLM\Software\Odbc\Odbcinst.ini\<drivername> 항목을 찾아보기 바란다. 값은 초단위다.

풀링은 ODBC API를 통해 설정할 수도 있다.

SQLSetEnvAttr(
    hEnv, 
    SQL_ATTR_CONNECTION_POOLING, 
    ULongToPtr(SQL_CP_ONE_PER_HENV or SQL_CP_ONE_PER_DRIVER), 
    SQL_IS_UINTEGER
    );
  • SQL_CP_ONE_PER_HENV - 같은 HENV를 공유하는 모든 커넥션 풀링
  • SQL_CP_ONE_PER_DRIVER - 드라이버별로 커넥션 풀링. 같은 드라이버만 사용한다면 HENV랑 별 차이가 없다.

내부 네트워크 부하에 의해, 커넥션이 이상하게 끊어지는 경우가 있다고 한다. 이 문제를 해결하기 위해서 쿼리를 실행할 때마다 커넥션 객체를 생성하되, 커넥션 풀링을 이용하라…라는 이야기가 있던데…

커넥션 풀링을 사용하는 애플리케이션을 빠르게 실행했다, 죽였다를 반복하면, SQLConnect 내부에서 Access Violation이 발생하는 경우가 있었다. 윈도우즈 2003 서버, SQL Server 2005 기준이다. 설정이 뭔가 잘못된 것인지, ODBC 내부 문제인지 모르겠네.

찾아보니 이런 글이 있었다. 이 문제가 맞나? .NET 이랑은 아무 관련 없는데… http://support.microsoft.com/?kbid=328476 요런 글도 있고…

링크

kb/odbc.txt · 마지막으로 수정됨: 2014/11/08 16:11 (바깥 편집)