SQLite 버전 3용 C/C++ 인터페이스
SQLite 버전 3용 C/C++ 인터페이스
SQLite 버전 3.0은 SQLite 2.8.13 코드 기반에서 파생된 새 버전이지만, 파일 형식과 API가 호환되지 않아요. 이 문서는 SQLite 2에서 버전 3으로 넘어가는 프로그래머를 위한 역사적인 가이드예요. 핵심 API 구조와 동작 방식을 이해하는 데 도움이 됩니다.
출처: 문서
본문
참고: 이 문서는 2004년에 프로그래머들이 SQLite 버전 2에서 버전 3으로 옮겨가는 것을 돕기 위한 가이드로 작성됐어요. 담긴 정보는 여전히 대체로 정확하지만, 수년간 많은 변경과 개선이 있었어요. 다음 문서들을 대신 사용하는 걸 권장해요:
- An Introduction To The SQLite C/C++ Interface
- SQLite C/C++ Reference Guide
1.0 개요
SQLite 버전 3.0은 SQLite 2.8.13 코드 기반에서 파생된 새 버전이지만, 파일 형식과 API가 호환되지 않아요. SQLite 버전 3.0은 다음 기능에 대한 요구에 응답하기 위해 만들어졌어요:
- UTF-16 지원.
- 사용자 정의 가능한 텍스트 collating sequence.
- 색인된 컬럼에 BLOB를 저장하는 기능.
이 기능들을 구현하려면 버전 3.0으로 전환해야 했어요. 각 기능이 데이터베이스 파일 형식에 호환되지 않는 변경을 요구하기 때문이에요. API 정리 같은 다른 호환 불가 변경들도, 호환되지 않는 변경은 한꺼번에 처리하는 게 가장 좋다는 이론 아래 동시에 도입됐어요.
버전 3.0의 API는 버전 2.X API와 비슷하지만 중요한 차이점이 몇 가지 있어요. 가장 눈에 띄는 것은 모든 API 함수와 데이터 구조의 시작 부분에 있던 "sqlite_" 접두사가 "sqlite3_"로 바뀐 거예요. 이렇게 하면 두 API 사이의 혼동을 피할 수 있고 SQLite 2.X와 SQLite 3.0을 동시에 링크할 수 있어요.
UTF-16 문자열에 대한 C 데이터타입이 무엇이어야 하는지에 대한 합의는 없어요. 그래서 SQLite는 UTF-16 문자열을 가리키는 데 일반 형식인 void를 사용해요. 클라이언트 소프트웨어는 void를 시스템에 적합한 어떤 데이터타입으로든 캐스팅할 수 있어요.
2.0 C/C++ 인터페이스
SQLite 3.0의 API는 여러 데이터 구조와 #define에 더해 83개의 별도 함수를 포함해요. (완전한 API 참조는 별도 문서로 제공돼요.) 다행히 인터페이스는 그 크기가 암시하는 것만큼 복잡하지 않아요. 간단한 프로그램은 여전히 3개의 함수만으로 충분해요: sqlite3_open(), sqlite3_exec(), sqlite3_close(). 데이터베이스 엔진 실행에 대한 더 많은 제어는 sqlite3_prepare_v2()로 SQLite 문을 바이트 코드로 컴파일하고 sqlite3_step()으로 그 바이트 코드를 실행해서 제공돼요. sqlite3_column_으로 시작하는 이름의 루틴 계열은 쿼리 결과 집합에 대한 정보를 추출하는 데 사용돼요. 많은 인터페이스 함수는 UTF-8 버전과 UTF-16 버전이 쌍으로 제공돼요. 그리고 사용자 정의 SQL 함수와 사용자 정의 텍스트 collating sequence를 구현하는 데 사용되는 루틴 모음이 있어요.
2.1 데이터베이스 열기와 닫기
typedef struct sqlite3 sqlite3;
int sqlite3_open(const char*, sqlite3**);
int sqlite3_open16(const void*, sqlite3**);
int sqlite3_close(sqlite3*);
const char *sqlite3_errmsg(sqlite3*);
const void *sqlite3_errmsg16(sqlite3*);
int sqlite3_errcode(sqlite3*);
sqlite3_open() 루틴은 버전 2 인터페이스처럼 sqlite3 구조에 대한 포인터 대신 정수 오류 코드를 반환해요. sqlite3_open()과 sqlite3_open16()의 차이는 sqlite3_open16()이 데이터베이스 파일 이름으로 UTF-16(호스트 네이티브 바이트 순서)을 받는다는 거예요. 새 데이터베이스 파일을 만들어야 한다면, sqlite3_open16()은 내부 텍스트 표현을 UTF-16으로 설정하고 sqlite3_open()은 텍스트 표현을 UTF-8로 설정해요.
데이터베이스 파일의 열기 및/또는 생성은 파일이 실제로 필요해질 때까지 지연돼요. 이렇게 하면 네이티브 텍스트 표현이나 기본 페이지 크기 같은 옵션과 파라미터를 PRAGMA 문으로 설정할 수 있어요.
sqlite3_errcode() 루틴은 가장 최근의 주요 API 호출에 대한 결과 코드를 반환해요. sqlite3_errmsg()은 가장 최근 오류에 대한 영어 텍스트 오류 메시지를 반환해요. 오류 메시지는 UTF-8로 표현되며 일시적이에요. 어떤 SQLite API 함수에 대한 다음 호출에서 사라질 수 있어요. sqlite3_errmsg16()은 오류 메시지를 호스트 네이티브 바이트 순서의 UTF-16으로 반환한다는 점만 빼고 sqlite3_errmsg()처럼 동작해요.
SQLite 버전 3의 오류 코드는 버전 2에서 변경되지 않았어요. 다음과 같아요:
#define SQLITE_OK 0 /* Successful result */
#define SQLITE_ERROR 1 /* SQL error or missing database */
#define SQLITE_INTERNAL 2 /* An internal logic error in SQLite */
#define SQLITE_PERM 3 /* Access permission denied */
#define SQLITE_ABORT 4 /* Callback routine requested an abort */
#define SQLITE_BUSY 5 /* The database file is locked */
#define SQLITE_LOCKED 6 /* A table in the database is locked */
#define SQLITE_NOMEM 7 /* A malloc() failed */
#define SQLITE_READONLY 8 /* Attempt to write a readonly database */
#define SQLITE_INTERRUPT 9 /* Operation terminated by sqlite_interrupt() */
#define SQLITE_IOERR 10 /* Some kind of disk I/O error occurred */
#define SQLITE_CORRUPT 11 /* The database disk image is malformed */
#define SQLITE_NOTFOUND 12 /* (Internal Only) Table or record not found */
#define SQLITE_FULL 13 /* Insertion failed because database is full */
#define SQLITE_CANTOPEN 14 /* Unable to open the database file */
#define SQLITE_PROTOCOL 15 /* Database lock protocol error */
#define SQLITE_EMPTY 16 /* (Internal Only) Database table is empty */
#define SQLITE_SCHEMA 17 /* The database schema changed */
#define SQLITE_TOOBIG 18 /* Too much data for one row of a table */
#define SQLITE_CONSTRAINT 19 /* Abort due to contraint violation */
#define SQLITE_MISMATCH 20 /* Data type mismatch */
#define SQLITE_MISUSE 21 /* Library used incorrectly */
#define SQLITE_NOLFS 22 /* Uses OS features not supported on host */
#define SQLITE_AUTH 23 /* Authorization denied */
#define SQLITE_ROW 100 /* sqlite_step() has another row ready */
#define SQLITE_DONE 101 /* sqlite_step() has finished executing */
2.2 SQL 문 실행
typedef int (*sqlite_callback)(void*,int,char**, char**);
int sqlite3_exec(sqlite3*, const char *sql, sqlite_callback, void*, char**);
sqlite3_exec() 함수는 SQLite 버전 2에서와 거의 같은 방식으로 동작해요. 두 번째 파라미터에 지정된 0개 이상의 SQL 문이 컴파일되고 실행돼요. 쿼리 결과는 콜백 루틴에 반환돼요.
SQLite 버전 3에서 sqlite3_exec 루틴은 prepared statement 인터페이스 호출 주변의 래퍼일 뿐이에요.
typedef struct sqlite3_stmt sqlite3_stmt;
int sqlite3_prepare(sqlite3*, const char*, int, sqlite3_stmt**, const char**);
int sqlite3_prepare16(sqlite3*, const void*, int, sqlite3_stmt**, const void**);
int sqlite3_finalize(sqlite3_stmt*);
int sqlite3_reset(sqlite3_stmt*);
sqlite3_prepare 인터페이스는 단일 SQL 문을 나중에 실행할 바이트 코드로 컴파일해요. 이 인터페이스는 이제 데이터베이스에 접근하는 선호되는 방식이에요.
SQL 문은 sqlite3_prepare()의 경우 UTF-8 문자열이에요. sqlite3_prepare16()은 SQL 입력으로 UTF-16 문자열을 기대한다는 점만 빼고 같은 방식으로 동작해요. 입력 문자열의 첫 번째 SQL 문만 컴파일돼요. 다섯 번째 파라미터는 (있다면) 입력 문자열의 다음(미컴파일된) SQLite 문에 대한 포인터로 채워져요. sqlite3_finalize() 루틴은 prepared SQL 문을 할당 해제해요. 데이터베이스를 닫으려면 모든 prepared statement가 finalize되어야 해요. sqlite3_reset() 루틴은 prepared SQL 문을 리셋해서 다시 실행할 수 있게 해요.
SQL 문은 "?" 또는 "?nnn" 또는 ":aaa" 형태의 토큰을 포함할 수 있어요. 여기서 "nnn"은 정수이고 "aaa"는 식별자예요. 이런 토큰은 나중에 sqlite3_bind 인터페이스로 채워질 미지정 리터럴 값(또는 "wildcard")을 나타내요. 각 wildcard는 문에서의 순서나, "?nnn" 형태의 경우 "nnn"에 해당하는 연관 번호를 가져요. 같은 wildcard가 같은 SQL 문에서 두 번 이상 나타날 수 있는데, 이 경우 그 wildcard의 모든 인스턴스에는 같은 값이 채워져요. 바인딩되지 않은 wildcard의 값은 NULL이에요.
int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));
int sqlite3_bind_double(sqlite3_stmt*, int, double);
int sqlite3_bind_int(sqlite3_stmt*, int, int);
int sqlite3_bind_int64(sqlite3_stmt*, int, long long int);
int sqlite3_bind_null(sqlite3_stmt*, int);
int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*));
int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int n, void(*)(void*));
int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*);
prepared SQL 문의 wildcard에 값을 할당하는 데 사용되는 여러 sqlite3_bind 루틴이 있어요. 바인딩되지 않은 wildcard는 NULL로 해석돼요. 바인딩은 sqlite3_reset()으로 리셋되지 않아요. 하지만 wildcard는 sqlite3_reset() 후에 새 값으로 다시 바인딩될 수 있어요.
SQL 문이 준비되고 (선택적으로 바인딩된) 후에는 다음으로 실행돼요:
int sqlite3_step(sqlite3_stmt*);
sqlite3_step() 루틴은 결과 집합의 단일 행을 반환하면 SQLITE_ROW를, 정상적으로든 오류로든 실행이 완료되었으면 SQLITE_DONE을 반환해요. 데이터베이스 파일을 열 수 없으면 SQLITE_BUSY를 반환할 수도 있어요. 반환 값이 SQLITE_ROW이면 다음 루틴들로 결과 집합의 그 행에 대한 정보를 추출할 수 있어요:
const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);
int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
int sqlite3_column_bytes16(sqlite3_stmt*, int iCol);
int sqlite3_column_count(sqlite3_stmt*);
const char *sqlite3_column_decltype(sqlite3_stmt *, int iCol);
const void *sqlite3_column_decltype16(sqlite3_stmt *, int iCol);
double sqlite3_column_double(sqlite3_stmt*, int iCol);
int sqlite3_column_int(sqlite3_stmt*, int iCol);
long long int sqlite3_column_int64(sqlite3_stmt*, int iCol);
const char *sqlite3_column_name(sqlite3_stmt*, int iCol);
const void *sqlite3_column_name16(sqlite3_stmt*, int iCol);
const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
const void *sqlite3_column_text16(sqlite3_stmt*, int iCol);
int sqlite3_column_type(sqlite3_stmt*, int iCol);
sqlite3_column_count() 함수는 결과 집합의 컬럼 수를 반환해요. sqlite3_column_count()는 sqlite3_prepare_v2() 이후 언제든 호출할 수 있어요. sqlite3_data_count()는 sqlite3_step() 이후에만 동작한다는 점만 빼고 sqlite3_column_count()와 비슷하게 동작해요. 이전 sqlite3_step() 호출이 SQLITE_DONE이나 오류 코드를 반환했다면 sqlite3_data_count()는 0을 반환하는 반면, sqlite3_column_count()는 계속 결과 집합의 컬럼 수를 반환해요.
반환된 데이터는 다른 sqlite3_column_***() 함수들로 검사하는데, 모두 두 번째 파라미터로 컬럼 번호를 받아요. 컬럼은 왼쪽에서 오른쪽으로 0부터 세요. 이는 1부터 세는 파라미터와 다르다는 점에 주의해요.
sqlite3_column_type() 함수는 N번째 컬럼의 값에 대한 데이터타입을 반환해요. 반환 값은 다음 중 하나예요:
#define SQLITE_INTEGER 1
#define SQLITE_FLOAT 2
#define SQLITE_TEXT 3
#define SQLITE_BLOB 4
#define SQLITE_NULL 5
sqlite3_column_decltype() 루틴은 CREATE TABLE 문에서 컬럼의 선언된 타입인 텍스트를 반환해요. 표현식의 경우 반환 타입은 빈 문자열이에요. sqlite3_column_name()은 N번째 컬럼의 이름을 반환해요. sqlite3_column_bytes()는 BLOB 타입 컬럼의 바이트 수, 또는 UTF-8 인코딩 TEXT 문자열의 바이트 수를 반환해요. sqlite3_column_bytes16()은 BLOB에 대해서는 같은 값을 반환하지만 TEXT 문자열에 대해서는 UTF-16 인코딩의 바이트 수를 반환해요.
sqlite3_column_blob()은 BLOB 데이터를 반환해요.sqlite3_column_text()는 TEXT 데이터를 UTF-8로 반환해요.sqlite3_column_text16()은 TEXT 데이터를 UTF-16으로 반환해요.sqlite3_column_int()는 호스트 머신의 네이티브 정수 형식으로 INTEGER 데이터를 반환해요.sqlite3_column_int64()는 64비트 INTEGER 데이터를 반환해요.- 마지막으로
sqlite3_column_double()은 부동소수점 데이터를 반환해요.
데이터를 sqlite3_column_type()이 지정한 형식으로 검색할 필요는 없어요. 다른 형식을 요청하면 데이터가 자동으로 변환돼요.
데이터 형식 변환은 이전 sqlite3_column_blob(), sqlite3_column_text(), 그리고/또는 sqlite3_column_text16() 호출이 반환한 포인터를 무효화할 수 있어요. 포인터는 다음 경우에 무효화될 수 있어요:
- 초기 콘텐츠가 BLOB이고
sqlite3_column_text()또는sqlite3_column_text16()이 호출된 경우. 문자열에 0 종료자를 추가해야 할 수도 있어요. - 초기 콘텐츠가 UTF-8 텍스트이고
sqlite3_column_bytes16()또는sqlite3_column_text16()이 호출된 경우. 콘텐츠를 UTF-16으로 변환해야 해요. - 초기 콘텐츠가 UTF-16 텍스트이고
sqlite3_column_bytes()또는sqlite3_column_text()이 호출된 경우. 콘텐츠를 UTF-8로 변환해야 해요.
UTF-16be와 UTF-16le 사이의 변환은 항상 제자리에서 수행되고 이전 포인터를 무효화하지 않는다는 점에 주의해요. 물론 이전 포인터가 가리키는 버퍼의 콘텐츠는 수정되어 있겠지만요. 다른 종류의 변환은 가능할 때 제자리에서 수행되지만, 때로는 불가능해서 그런 경우 이전 포인터가 무효화돼요.
가장 안전하고 기억하기 쉬운 정책은 다음과 같아요: sqlite3_column_blob(), sqlite3_column_text(), 또는 sqlite3_column_text16()의 모든 결과가 이후의 sqlite3_column_bytes(), sqlite3_column_bytes16(), sqlite3_column_text(), 또는 sqlite3_column_text16() 호출에 의해 무효화된다고 가정해요. 즉, sqlite3_column_blob(), sqlite3_column_text(), sqlite3_column_text16()을 호출하기 전에 항상 sqlite3_column_bytes()나 sqlite3_column_bytes16()을 먼저 호출해야 해요.
2.3 사용자 정의 함수
사용자 정의 함수는 다음 루틴으로 만들 수 있어요:
typedef struct sqlite3_value sqlite3_value;
int sqlite3_create_function(
sqlite3 *,
const char *zFunctionName,
int nArg,
int eTextRep,
void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*)
);
int sqlite3_create_function16(
sqlite3*,
const void *zFunctionName,
int nArg,
int eTextRep,
void*,
void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*)
);
#define SQLITE_UTF8 1
#define SQLITE_UTF16 2
#define SQLITE_UTF16BE 3
#define SQLITE_UTF16LE 4
#define SQLITE_ANY 5
nArg 파라미터는 함수에 대한 인자 수를 지정해요. 0 값은 임의의 수의 인자가 허용됨을 나타내요. eTextRep 파라미터는 이 함수의 인자에 대해 텍스트 값이 어떤 표현으로 있을 것으로 기대되는지 지정해요. 이 파라미터의 값은 위에 정의된 파라미터 중 하나여야 해요. SQLite 버전 3은 서로 다른 텍스트 표현을 사용하는 같은 함수의 여러 구현을 허용해요. 데이터베이스 엔진은 필요한 텍스트 변환 수를 최소화하는 함수를 선택해요.
일반 함수는 xFunc만 지정하고 xStep과 xFinal은 NULL로 둬요. 집계 함수는 xStep과 xFinal을 지정하고 xFunc는 NULL로 둬요. 별도의 sqlite3_create_aggregate() API는 없어요.
함수 이름은 UTF-8로 지정돼요. 별도의 sqlite3_create_function16() API는 함수 이름이 UTF-16 호스트 바이트 순서로 지정된다는 점만 빼고 sqlite_create_function()과 동일하게 동작해요.
이제 함수의 파라미터는 SQLite 버전 2.X에서처럼 문자열에 대한 포인터가 아니라 sqlite3_value 구조에 대한 포인터라는 점에 주의해요. 다음 루틴들은 이 "값들"에서 유용한 정보를 추출하는 데 사용돼요:
const void *sqlite3_value_blob(sqlite3_value*);
int sqlite3_value_bytes(sqlite3_value*);
int sqlite3_value_bytes16(sqlite3_value*);
double sqlite3_value_double(sqlite3_value*);
int sqlite3_value_int(sqlite3_value*);
long long int sqlite3_value_int64(sqlite3_value*);
const unsigned char *sqlite3_value_text(sqlite3_value*);
const void *sqlite3_value_text16(sqlite3_value*);
int sqlite3_value_type(sqlite3_value*);
함수 구현은 다음 API를 사용해 컨텍스트를 얻고 결과를 보고해요:
void *sqlite3_aggregate_context(sqlite3_context*, int nbyte);
void *sqlite3_user_data(sqlite3_context*);
void sqlite3_result_blob(sqlite3_context*, const void*, int n, void(*)(void*));
void sqlite3_result_double(sqlite3_context*, double);
void sqlite3_result_error(sqlite3_context*, const char*, int);
void sqlite3_result_error16(sqlite3_context*, const void*, int);
void sqlite3_result_int(sqlite3_context*, int);
void sqlite3_result_int64(sqlite3_context*, long long int);
void sqlite3_result_null(sqlite3_context*);
void sqlite3_result_text(sqlite3_context*, const char*, int n, void(*)(void*));
void sqlite3_result_text16(sqlite3_context*, const void*, int n, void(*)(void*));
void sqlite3_result_value(sqlite3_context*, sqlite3_value*);
void *sqlite3_get_auxdata(sqlite3_context*, int);
void sqlite3_set_auxdata(sqlite3_context*, int, void*, void (*)(void*));
2.4 사용자 정의 collating sequence
다음 루틴들은 사용자 정의 collating sequence를 구현하는 데 사용돼요:
sqlite3_create_collation(sqlite3*, const char *zName, int eTextRep, void*,
int(*xCompare)(void*,int,const void*,int,const void*));
sqlite3_create_collation16(sqlite3*, const void *zName, int eTextRep, void*,
int(*xCompare)(void*,int,const void*,int,const void*));
sqlite3_collation_needed(sqlite3*, void*,
void(*)(void*,sqlite3*,int eTextRep,const char*));
sqlite3_collation_needed16(sqlite3*, void*,
void(*)(void*,sqlite3*,int eTextRep,const void*));
sqlite3_create_collation() 함수는 collating sequence 이름과 그 collating sequence를 구현할 비교 함수를 지정해요. 비교 함수는 텍스트 값 비교에만 사용돼요. eTextRep 파라미터는 SQLITE_UTF8, SQLITE_UTF16LE, SQLITE_UTF16BE, 또는 SQLITE_ANY 중 하나로, 비교 함수가 동작하는 텍스트 표현을 지정해요. 같은 collating sequence에 대해 UTF-8, UTF-16LE, UTF-16BE 텍스트 표현 각각에 대한 별도 비교 함수가 존재할 수 있어요. sqlite3_create_collation16()은 collation 이름이 UTF-8이 아니라 UTF-16 호스트 바이트 순서로 지정된다는 점만 빼고 sqlite3_create_collation()처럼 동작해요.
sqlite3_collation_needed() 루틴은 데이터베이스 엔진이 알 수 없는 collating sequence를 만나면 호출할 콜백을 등록해요. 콜백은 적절한 비교 함수를 찾아 필요에 따라 sqlite_3_create_collation()을 호출할 수 있어요. 콜백의 네 번째 파라미터는 UTF-8로 된 collating sequence의 이름이에요. sqlite3_collation_need16()은 콜백이 collating sequence 이름을 UTF-16 호스트 바이트 순서로 보내요.