Notice
Recent Posts
Recent Comments
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- 모든 경우수 돌기
- SQLite
- 알고리즘
- Ctrl z
- PID 제어
- 동적 프로그래밍
- gsoap
- 최소 신장 트리
- PID
- 네트워크 문제 해결
- 진보성
- 특허
- WebService
- 주성분 분석
- 전세
- 유클리드 거리
- 리본
- 직선의 방정식
- 플레인 스위핑
- 주식
- 리눅스
- 모터종류
- pca
- algorithm 함수
- vc mfc
- 동적프로그래밍
- 보정서
- Linux
- SW 검정
- SW검정
Archives
- Today
- Total
SQLite C API 소개 본문
원본 출처 : http://jeminency.egloos.com/3325961
꽤 간단한 내용이지만, SQLite는 한글 매뉴얼이 별로 없는 탓에 끄적여 봅니다.
그간 C API를 써 본 DB들을 보면, ODBC와 비슷한 형태를 갖고 있는 경우가 많습니다. 오라클은 C API보다는 Pro*C를 쓰니 예외로 하고요.. 오라클 C API가 어떻게 되어 있는 지도 모르겠네요 -_-;
대략 Open-Prepare-Bind-Execute-(Fetch)-Close의 과정을 거치는데 open-close는 당연히 DB를 열고 핸들러(DB 정보 구조체)를 초기화하는 것과 종료하는 것이니 어려울 것 없고요.
Prepare는 DB에게 자기가 실행할 SQL 문장을 던져주고 미리 실행 플랜을 준비하도록 합니다. binding 할 값이 없다면 prepare 하지 않고 곧바로 execute도 가능합니다만, 예를 들어 문장은 동일한데 값만 바뀌는 쿼리를 여러 번 실행할 경우에 다이렉트로 그 때마다 실행을 한다면, 매번 DB 엔진은 SQL 문장을 해석하고 어떻게 실행할 지를 결정하고, 값에 따라 실행하게 되겠죠. 값만 달라진다면, 문장을 해석하고 실행할 지 결정하는 부분은 사실 한 번만 해도 되는데 말이죠.
그리고 bind는 실행하기 전 쿼리에 값을 할당하는 것입니다. 바인딩할 부분은 보통 '?'으로 표현하는데 'SELECT * FROM test WHERE id = ?' 이런 식으로 쿼리 문장을 표현할 수 있습니다. 이 문장 그대로 prepare를 딱 한 번만 한 다음 실행할 때는 '?'위치에 integer 변수를 binding 하여 여러 가지로 id 값을 주어서 결과를 가져올 수 있습니다.
Execute는 말 그대로 문장을 실행하는 것인데 DB에서는 select 쿼리가 실행 되었을 경우 result set 공간에 결과값을 저장해 놓습니다.
Fetch는 그런 result set의 값을 가져오는 과정입니다. insert-update-delete를 실행했을 경우는 당연히 필요없는 과정이죠.
상당히 대충 설명했는데 -_- 직접 예를 보겠습니다.
testtbl의 스키마는 'CREATE TABLE testtbl(id int, value text);' 입니다.
예제 프로그램은 id를 1부터 3까지 돌리며 해당되는 레코드를 긁어오는 것입니다.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sqlite3.h>
#define EXIT_WITH_ERROR(func, dbh) \
do { \
if (func != SQLITE_OK) \
{ \
fprintf(stderr, "line %d: %s \n: %s\n", __LINE__ \
, #func ,sqlite3_errmsg(dbh)); \
exit(sqlite3_errcode(dbh)); \
} \
} while(0)
int main()
{
sqlite3 *dbh;
sqlite3_stmt *stmt;
int i, j;
const char *tail;
char *errmsg;
char *sql = "select * from testtbl where id=?";
/* "testdb"라는 DB 파일을 연다 */
EXIT_WITH_ERROR(sqlite3_open("testdb", &dbh), dbh);
/* sql 문장에 대해 질의 플랜을 준비한다 */
EXIT_WITH_ERROR(sqlite3_prepare(dbh, sql, strlen(sql), &stmt, NULL) , dbh);
for (i=1; i<=3; i++)
{
/* '?' 위치에 i 값을 바인딩한다 */
EXIT_WITH_ERROR(sqlite3_bind_int(stmt, 1, i) , dbh);
/* sql 쿼리 실행 */
while (sqlite3_step(stmt) != SQLITE_DONE)
{
/* 실행된 결과값을 받아와 출력 */
printf("%d : %s\n" , sqlite3_column_int(stmt, 0)
, sqlite3_column_text(stmt, 1));
}
sqlite3_reset(stmt);
}
sqlite3_finalize(stmt);
}
EXIT_WITH_ERROR는 에러 체크를 위해 제가 작성한 매크로입니다. 실제 sqlite3_ 로 시작하는 함수들만 보시면 됩니다.
처음에 DB 오픈을 하고 sql 문장에 대해 prepare를 수행하죠?
prepare의 마지막 NULL 인자는 원래 char *가 들어가는 곳입니다. sql 문장이 하나가 아니라 여러 개일 경우는 첫번째 것만 prepare하고 마지막 인자의 포인터 변수에 두번째 문장이 시작하는 위치를 가리키도록 되어 있습니다.
순차적으로 수행되어야 할 sql 문이라면 변수를 따로 안 두고 실행 단위별로 sql 문을 선언하여 쓸 수 있다는 장점이 있습니다(자세한 설명은 sqlite3_prepare()의 매뉴얼 페이지를..).
그 다음에는 for 문을 돌며 바인딩-실행-결과 페치를 반복합니다.
바인딩은 for 문의 변수인 i를 그대로 이용합니다. 바인딩 함수의 두번째 인자는 binding variable의 순서입니다. 첫번째니까 '1'이죠. 여러 개 있으면 앞에서부터 1,2,3,..이 됩니다. 숫자이므로 sqlite3_bind_int를 썼고, 그 외에 _null, _text 등 다양한 바인딩 함수가 있습니다.
그리고 sqlite3_step()을 써서 실행을 하는데 특이한 점은 이 함수가 실행과 페치를 동시에 수행한다는 점입니다.
예를 들어 MySQL이라면, mysql_query()로 실행하고, mysql_store_result()로 결과 셋을 가져 온 후, mysql_fetch_row()을 반복 실행하여 한 건씩 긁어오는 과정을 거칩니다.
반면 SQLite는 sqlite3_step을 실행하면 실행하고 결과 셋의 첫번째 레코드를 가져오는 과정까지 거칩니다.
그 다음은 각 컬럼을 sqlite3_column_* 함수들을 써서 가져오면 됩니다(컬럼 번호는 0부터 시작합니다).
그리고, 두번째 이후로 sqlite3_step()을 실행하면 sql을 실행하지는 않고 결과 셋의 다음 레코드들을 가져오게 되는 것이죠. 만약 가져올 결과 레코드가 없으면 SQLITE_DONE을 리턴합니다.
즉, 만약 결과가 실행할 때마다 한 건씩만 있다면 굳이 while 문을 돌릴 필요는 없습니다. 실행 결과는 다음과 같습니다.
1 : aaa
1 : ddd
2 : bbb
3 : ccc
만약 while 문을 쓰지 않았다면 결과에는 1:ddd가 나오지 않을 것입니다. id 컬럼이 unique 하다면 while문을 쓰지 않아도 되겠지만, 방어적인 프로그래밍을 위해서는 while을 사용하는 스타일이 나을 듯 합니다 ^^
그리고 결과셋을 다 긁어왔다면 sqlite3_reset을 실행해서 바인딩 된 변수들을 초기화 해 주어야 합니다.
그렇지 않을 경우 두번째 bind를 할 때에 에러가 납니다.
이상으로 간단히 SQLite의 C API에 대해 살펴 보았습니다, 간단하죠? ^^
개인적으로는 다른 DB에 비해 비교적 API 사용법이 쉽다는 느낌이 듭니다. DB 데몬이 없기 때문에 API를 더욱 간략하게 만들 수 있었는 지도 모르겠습니다.
하지만 C를 쓸 경우는 아무래도 다른 언어보다는 조금 번거롭죠.
pysqlite 같은 걸 쓸 경우는 훨씬 편합니다. 이후에는 C언어 이외의 SQLite API에 대해 한 번 보도록 하겠습니다.