SQLite - 윈디하나의 솔라나라
|
sqlite3.c
, shell.c
, sqlite3.h
, sqlite3ext.h
만으로 되어있음을 알 수 있다. 만약 사전 빌드 없이 전체기능을 응용에 임베딩 하려면 sqlite3.c
파일만 추가하면 된다. 소스를 열어보면 소스가 잘 정돈되어있으며 주석(영문)도 잘 되어있다고 생각한다. 각각의 파일은 아래와 같은 기능을 한다.
SQLite 를 빌드하려면 아래와 같이 할 수 있다. SQLite 의 Autoconf 배포 버전을 사용했다.
windy@wl ~/src $ wget https://www.sqlite.org/2024/sqlite-autoconf-3460100.tar.gz
windy@wl ~/src $ tar xvfz sqlite-autoconf-3460100.tar.gz
windy@wl ~/src $ cd sqlite-autoconf-3460100
windy@wl ~/src/sqlite-autoconf-3460100 $ ./configure \
--enable-session \
--enable-editline \
--enable-static \
CFLAGS="-m64 -DSQLITE_DIRECT_OVERFLOW_READ -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_USE_ALLOCA -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_ENABLE_COLUMN_METADATA -DSQLITE_ENABLE_RBU -I/usr/local/include" \
LDFLAGS="-m64"
1)
windy@wl ~/src/sqlite-autoconf-3460100 $ make
windy@wl ~/src/sqlite-autoconf-3460100 $ sudo make install
1)
FTS5,
JSON1,
R*Tree,
DBSTAT,
RBU,
Session 확장을 활성화 한다.
컴파일시 넣을 확장에 대해서는 Compile-time Options를 참조하자.이 문서에서는 일반적인 기능을 모두 넣었지만, 반대로 실행시 필요 없는 기능을 빼서 사용할 수도 있다. 주로 임베디드 기기에서 이렇게 사용한다. 예를 들어 CREATE TABLE
구문은 개발할때만 필요하고 DB 파일을 생성할 때만 필요한 기능이므로, SQLite 임베딩시 이 기능은 삭제하고 빌드하곤 한다. 삭제할 수 있는 옵션에 대해서는 Compile-time Options - 9. Options To Omit Features를 참조하자.
SQLite 의 TCL 버전은 아래와 같이 빌드할 수 있다. 위의 SQLite 빌드를 마친후 진행하면 된다.
windy@wl ~/src/sqlite-autoconf-3460100 $ cd tea windy@wl ~/src/sqlite-autoconf-3460100/tea $ cp /usr/lib/amd64/tclConfig.sh . windy@wl ~/src/sqlite-autoconf-3460100/tea $ which cc /opt/developerstudio12.6/bin/cc windy@wl ~/src/sqlite-autoconf-3460100/tea $ vi tclConfig.sh TCL_CC='/opt/developerstudio12.6/bin/cc' windy@wl ~/src/sqlite-autoconf-3460100/tea $ ./configure --with-tcl=./ --enable-64bit windy@wl ~/src/sqlite-autoconf-3460100/tea $ make windy@wl ~/src/sqlite-autoconf-3460100/tea $ sudo make install
sqlite3 [options] [databasefile] [SQL]
sqlite3(1)의 옵션은 아래와 같다.
-A ARGS...
".archive ARGS" 실행후 종료-append
파일의 끝에 데이터베이스 추가-ascii
출력 모드를 아스키로 설정-bail
오류 발생시 정지-batch
강제 배치형 I/O 사용-column
쿼리 결과를 form 형식의 공백문자를 사용해 나란히 만든 표로 출력-cmd COMMAND
stdin 에서 명령을 받아들이기 전에 실행할 명령-csv
출력 모드를 CSV(comma separated values)으로 설정-echo
실행전 명령을 출력-init FILENAME
FILENAME 에서 명령(SQL문 및 메타구문)을 읽고 살행-[no]header
헤더 ON/OFF-help
도움말 출력후 종료-html
출력 모드를 단순한 HTML으로 설정-interactive
강제 대화형 I/O 사용-line
쿼리 결과를 한개의 라인에 한개의 값을 출력. 로우는 빈 라인으로 출력. 스크립트나 프로그램에서 쉽게 파싱하도록 고안됨-list
쿼리 결과를 매 줄마다 구분자(|
이 기본값)로 구분해 출력. 기본 출력값임.-lookaside SIZE N
Lookaside 메모리를 위한 SIZE 바이트의 N 엔트리 사용-memtrace
메모리 할당/해제 트레이스-mmap N
기본 mmap 크기를 N으로 설정-newline SEP
출력 로우 구분자를 설정. 기본값 '\n'-nullvalue TEXT
널 값으로 사용할 문자를 설정. 기본값은 '' (빈 문자열)-pagecache SIZE N
페이지 캐시 메모리에서 SIZE바이트당 N개의 슬롯 사용-quote
출력 모드를 쿼트(Quote)모드로 설정-readonly
데이터베이스를 읽기전용으로 오픈-separator SEP
출력 필드 구분자를 separator 으로 설정. 기본값은 |
-stats
매 종료시 메모리 상태 출력-version
SQLite 버전 보기-vfs NAME
기본 VFS으로 name 을 사용-zip
지정한 파일을 ZIP 파일로 간주.으로 시작하는 커맨드를 메타 커맨드라고 부른다. 아래와 같이
.help
를 쳐서 확인해볼 수 있다.
windy@wl ~/sqlite $ sqlite3 test.sqlite SQLite version 3.28.0 2019-04-16 19:49:53 Enter ".help" for usage hints. sqlite> .help .archive ... Manage SQL archives .auth ON|OFF Show authorizer callbacks .backup ?DB? FILE Backup DB (default "main") to FILE .bail on|off Stop after hitting an error. Default OFF .binary on|off Turn binary output on or off. Default OFF .cd DIRECTORY Change the working directory to DIRECTORY .changes on|off Show number of rows changed by SQL .check GLOB Fail if output since .testcase does not match .clone NEWDB Clone data into NEWDB from the existing database .databases List names and files of attached databases .dbconfig ?op? ?val? List or change sqlite3_db_config() options .dbinfo ?DB? Show status information about the database .dump ?TABLE? ... Render all database content as SQL .echo on|off Turn command echo on or off .eqp on|off|full|... Enable or disable automatic EXPLAIN QUERY PLAN .excel Display the output of next command in a spreadsheet .exit ?CODE? Exit this program with return-code CODE .expert EXPERIMENTAL. Suggest indexes for specified queries .fullschema ?--indent? Show schema and the content of sqlite_stat tables .headers on|off Turn display of headers on or off .help ?-all? ?PATTERN? Show help text for PATTERN .import FILE TABLE Import data from FILE into TABLE .imposter INDEX TABLE Create imposter table TABLE on index INDEX .indexes ?TABLE? Show names of indexes .limit ?LIMIT? ?VAL? Display or change the value of an SQLITE_LIMIT .lint OPTIONS Report potential schema issues. .load FILE ?ENTRY? Load an extension library .log FILE|off Turn logging on or off. FILE can be stderr/stdout .mode MODE ?TABLE? Set output mode .nullvalue STRING Use STRING in place of NULL values .once (-e|-x|FILE) Output for the next SQL command only to FILE .open ?OPTIONS? ?FILE? Close existing database and reopen FILE .output ?FILE? Send output to FILE or stdout if FILE is omitted .parameter CMD ... Manage SQL parameter bindings .print STRING... Print literal STRING .progress N Invoke progress handler after every N opcodes .prompt MAIN CONTINUE Replace the standard prompts .quit Exit this program .read FILE Read input from FILE .restore ?DB? FILE Restore content of DB (default "main") from FILE .save FILE Write in-memory database into FILE .scanstats on|off Turn sqlite3_stmt_scanstatus() metrics on or off .schema ?PATTERN? Show the CREATE statements matching PATTERN .selftest ?OPTIONS? Run tests defined in the SELFTEST table .separator COL ?ROW? Change the column and row separators .session ?NAME? CMD ... Create or control sessions .sha3sum ... Compute a SHA3 hash of database content .shell CMD ARGS... Run CMD ARGS... in a system shell .show Show the current values for various settings .stats ?on|off? Show stats or turn stats on or off .system CMD ARGS... Run CMD ARGS... in a system shell .tables ?TABLE? List names of tables matching LIKE pattern TABLE .testcase NAME Begin redirecting output to 'testcase-out.txt' .timeout MS Try opening locked tables for MS milliseconds .timer on|off Turn SQL timer on or off .trace ?OPTIONS? Output each SQL statement as it is run .vfsinfo ?AUX? Information about the top-level VFS .vfslist List all available VFSes .vfsname ?AUX? Print the name of the VFS stack .width NUM1 NUM2 ... Set column widths for "column" mode sqlite> .quit windy@wl ~/sqlite $
알아두어야 한다고 생각되는 명령어는 별도로 표시했다.
SQLite 에는 몇가지 의사테이블이 있다. 모두 sqlite_
접두어를 가진다.
sqlite_master
, sqlite_temp_master
: 루트 페이지를 저장하고 있는 테이블sqlite_autoindex_TABLE_N
: 자동으로 생성된 인덱스(UNIQUE 또는 PRIMARY KEY 지시어를 사용한 경우) 정보를 저장하고 있는 테이블sqlite_sequence
: 자동 증가값을 저장하고 있는 테이블(AUTOINCREMENT 지시어를 사용한 경우)sqlite_stat1
: SQLite 의 상태를 보여주는 테이블sqlite_statN
: SQLite 의 상태를 보여주는 테이블. N 은 현재 2 ~ 4 까지 있다. 이 상태를 보려면 SQLITE_ENABLE_STAT2, SQLITE_ENABLE_STAT3, SQLITE_ENABLE_STAT4 를 정의해서 빌드 해야 한다.sqlite_init.sql | (2,613 바이트) |
-- Initialize Query for SQLite -- WindyHanas Solanara:SQLite - http://www.solanara.net/solanara/sqlite select 'SQLite ' || sqlite_version(); .print "" .print "CREATE TABLE, VIEW" drop table if exists authinfo; create table if not exists authinfo ( seq integer constraint authinfo_seq_pk primary key autoincrement, userid varchar(255) constraint authinfo_userid_nn not null constraint authinfo_userid_uq unique, pswd char(32) constraint authinfo_pswd_nn not null, salt char(4) constraint authinfo_salt_nn not null, ldate datetime default current_timestamp constraint authinfo_ldate_nn not null, sdate date default current_timestamp constraint authinfo_sdate_nn not null, pdate datetime default current_timestamp constraint authinfo_pdate_nn not null ); drop table if exists userinfo; create table if not exists userinfo ( seq integer constraint userinfo_seq_pk primary key constraint userinfo_seq_fk references authinfo (seq), username varchar(255) constraint userinfo_username_nn not null, tel1 varchar(255), tel2 varchar(255), grade real default 1.0, desc text, photo blob, rdate datetime default current_timestamp constraint userinfo_rdate_nn not null ); create index if not exists authinfo_sdate_ix on authinfo (sdate); drop view if exists userlist; create view if not exists userlist as select userid, username, tel1, grade, datetime(rdate, 'localtime') rdate from authinfo a inner join userinfo u using (seq); drop view if exists userjoinstat; create view if not exists userjoinstat as select datetime(sdate, 'localtime') sdate, count(a.seq) cnt from authinfo a indexed by authinfo_sdate_ix group by sdate; begin transaction; insert into authinfo (userid, pswd, salt) values ('windy', '1111', 'SQLT'); insert into userinfo (seq, username, tel1) values (1, '윈디하나', '010-000-0000'); insert into authinfo (userid, pswd, salt) values ('root', '1111', 'SQLT'); insert into userinfo (seq, username, tel1) values (2, '루트', '010-111-1111'); commit; .print "" .print "ANALYZE" analyze authinfo; analyze userinfo; .print "" explain query plan select * from userlist where userid = 'windy'; .print "" .print "SELECT USERLIST" select * from userlist; .print "" .print "SELECT USERJOINSTAT" select * from userjoinstat; .print "" .print "SELECT DBSTAT" select * from dbstat;아래와 같이 실행할 수 있으며, 이와 유사한 화면이 나올 것이다.
windy@wl ~/sqlite $ sqlite3 userdb.sqlite ".read sqlite_init.sql" SQLite 3.29.0 CREATE TABLE, VIEW ANALYZE QUERY PLAN |--SEARCH TABLE authinfo AS a USING COVERING INDEX sqlite_autoindex_authinfo_1 (userid=?) `--SEARCH TABLE userinfo AS u USING INTEGER PRIMARY KEY (rowid=?) SELECT USERLIST root|루트|010-111-1111|1.0|2019-09-16 00:16:43 windy|윈디하나|010-000-0000|1.0|2019-09-16 00:16:43 SELECT USERJOINSTAT 2019-09-16 00:16:43|2 SELECT DBSTAT authinfo|/|2|leaf|2|155|3925|78|4096|4096 authinfo_sdate_ix|/|6|leaf|2|45|4037|23|20480|4096 sqlite_autoindex_authinfo_1|/|3|leaf|2|16|4066|8|8192|4096 sqlite_master|/|1|leaf|8|1922|2030|742|0|4096 sqlite_sequence|/|4|leaf|1|12|4072|12|12288|4096 sqlite_stat1|/|7|leaf|3|87|3989|42|24576|4096 userinfo|/|5|leaf|2|98|3982|52|16384|4096명령을 실행하면
userdb.sqlite
파일이 생성된다. 이 파일은 하기 예제에서 사용된다.
35% Faster Than The Filesystem에 의하면 SQLite 는 파일 시스템보다 더 빠른 BLOB 성능을 보일 수 있다고 한다. BLOB 성능이 그만큼 좋다는 의미다. kvtest
라는 프로그램을 제공하는데, 아래와 같이 테스트해볼 수 있다.
windy@wl ~/src/sqlite-autoconf-3280000 $ wget -O kvtest.c "https://www.sqlite.org/src/raw/test/kvtest.c?name=94da54bb66aae7a54e47cf7e4ea4acecc0f217560f79ad3abfcc0361d6d557ba"
windy@wl ~/src/sqlite-autoconf-3280000 $ cc -DSQLITE_DIRECT_OVERFLOW_READ -lm -m64 -o kvtest kvtest.c sqlite3.c
windy@wl ~/src/sqlite-autoconf-3280000 $ cp kvtest ~/sqlite/
windy@wl ~/src/sqlite-autoconf-3280000 $ cd ~/sqlite/
windy@wl ~/sqlite $ ./kvtest init kvtest.db --count 1k --size 10k --variance 2k 1)
windy@wl ~/sqlite $ ./kvtest stat kvtest.db
Number of entries: 1000
Average value size: 9960
Minimum value size: 8009
Maximum value size: 11999
Page-size: 4096
Page-count: 2607
Freelist-count: 0
Integrity-check: ok
windy@wl ~/sqlite $ ./kvtest export kvtest.db kvtest.dir
001000
windy@wl ~/sqlite $ ls -al kvtest.dir
...
-rw-r--r-- 1 windy staff 10865 0월 0일 00:00 001000
windy@wl ~/sqlite $ ./kvtest export kvtest.db kvtest.tree --tree
00/10/00
windy@wl ~/sqlite $ ls -al kvtest.tree
drwxr-xr-x 13 windy staff 13 0월 0일 00:00 001000
1) 사이트에서 제시한 명령은 ./kvtest init test1.db --count 100k --size 10k --variance 2k
이다. 이렇게 하면 1GB 정도되는 데이터베이스를 가지고 성능을 테스트하는데, 테스트 시간이 오래 걸려 임의로 1/100 으로 줄여 테스트했다. 따라서 여기 나와있는 실행결과는 큰 의미가 없다.이제 성능을 측정해보자.
windy@wl ~/sqlite $ ./kvtest run kvtest.db --count 10k --blob-api SQLite version: 3.28.0 --count 10000 --max-id 1000 --asc --cache-size 1000 --jmode delete --mmap 0 --blob-api Database page size: 4096 Total elapsed time: 0.266 Microseconds per BLOB read: 26.600 Content read rate: 375.0 MB/s windy@wl ~/sqlite $ ./kvtest run kvtest.dir --count 10k --blob-api --count 10000 --max-id 1000 --asc Total elapsed time: 0.363 Microseconds per BLOB read: 36.300 Content read rate: 274.8 MB/s windy@wl ~/sqlite $ ./kvtest run kvtest.tree --count 10k --blob-api --count 10000 --max-id 1000 --asc Total elapsed time: 0.369 Microseconds per BLOB read: 36.900 Content read rate: 270.3 MB/s업데이트 메소드를 사용해 성능을 측정해보자.
windy@wl ~/sqlite $ ./kvtest run kvtest.db --count 10k --update SQLite version: 3.28.0 --count 10000 --max-id 1000 --asc --cache-size 1000 --jmode delete --mmap 0 Database page size: 4096 Total elapsed time: 3.789 Microseconds per BLOB write: 378.900 Content write rate: 26.3 MB/s windy@wl ~/sqlite $ ./kvtest run kvtest.dir --count 10k --update --count 10000 --max-id 1000 --asc Total elapsed time: 2.757 Microseconds per BLOB write: 275.700 Content write rate: 36.2 MB/s windy@wl ~/sqlite $ ./kvtest run kvtest.tree --count 10k --update --count 10000 --max-id 1000 --asc Total elapsed time: 2.634 Microseconds per BLOB write: 263.400 Content write rate: 37.9 MB/s
필자의 시스템에서는 성능이 위와 같이 나왔다. (적은 샘플을 가지고 테스트했다는 것을 감안해서 보자) 단순하게 비교하는건 어렵기 때문에, 테스트하고자 하는 시스템에서 성능을 확인해보자. 다른건 몰라도 읽기 성능은 꽤 마음에 들었다.
md5()
를 생성해 sqlite3_create_function()
을 이용해 추가하고, 사용 예제를 보였다. MD5 계산은 솔라리스에 번들된 libmd5
라이브러리를 사용했다.
sqlite.c | (5,506 바이트) |
/* SQLite Sample for Solaris WindyHana's Solanara: SQLite - http://www.solanara.net/solanara/sqlite dynamic: cc -lsqlite3 -lmd5 -I/usr/local/include -L/usr/local/lib -o sqlite sqlite.c static: cc -lm -lmd5 -I/usr/local/include -o sqlite sqlite.c /usr/local/lib/libsqlite3.a */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <md5.h> #include "sqlite3.h" int exec_callbackcnt; static int exec_callback(void * userarg, int argc, char** argv, char** azColName){ if (exec_callbackcnt == 0) { printf("SQL: %s\n\t", userarg); for (int i = 0; i < argc; i++) { printf("%s\t", azColName[i]); } printf("\n"); } exec_callbackcnt++; printf("%d\t", exec_callbackcnt); for (int i = 0; i < argc; i++) { printf("%s\t", argv[i] ? argv[i]: "NULL"); } printf("\n"); return 0; } char* bin2hex(const unsigned char *bin, size_t len) { // original source: https://nachtimwald.com/2017/09/24/hex-encode-and-decode-in-c/ char * hx = "0123456789abcdef"; char * rlt; size_t i; if (bin == NULL || len == 0) { return NULL; } rlt = malloc(len*2+1); for (i = 0; i < len; i++) { rlt[i*2] = hx[bin[i] >> 4]; rlt[i*2+1] = hx[bin[i] & 0x0F]; } rlt[len*2] = '\0'; return rlt; } /** * Solaris의 MD5 라이브러리를 사용한 MD5 계산 함수 */ char* hash_md5_alloc(char* data, int data_len) { unsigned char* auth_buffer = malloc(16); md5_calc(auth_buffer, data, data_len); // MD5_CTX md5_context; // MD5Init(&md5_context); // MD5Update(&md5_context, data, data_len); // MD5Final(auth_buffer, &md5_context); char * rlt = bin2hex(auth_buffer, 16); free(auth_buffer); return rlt; } /** * SQLite의 MD5 커스텀 함수을 생성 */ static void func_md5(sqlite3_context* pContext, int argc, sqlite3_value **argv) { void* pUserData = pUserData = sqlite3_user_data(pContext); char* rlt = NULL; for (int i = 0; i < argc; i++) { // 1개만 있음 if (sqlite3_value_type(argv[i]) == SQLITE_TEXT) { // SQLITE_TEXT 만 넘김 char* val = (char *) sqlite3_value_text(argv[i]); int len = strlen(val); rlt = hash_md5_alloc(val, len); } } sqlite3_result_text(pContext, rlt, -1, SQLITE_TRANSIENT); free(rlt); } typedef struct _func_first_struct { char * rlt; } func_first_struct; static void func_first_step(sqlite3_context* pContext, int argc, sqlite3_value **argv) { func_first_struct* p = (func_first_struct*) sqlite3_aggregate_context(pContext, sizeof(func_first_struct)); for (int i = 0; i < argc; i++) { // 1개만 있음 if (sqlite3_value_type(argv[i]) == SQLITE_TEXT) { // SQLITE_TEXT 만 넘김 if (p->rlt == NULL) { p->rlt = (char *) sqlite3_value_text(argv[i]); } } } } static void func_first_final(sqlite3_context* pContext) { func_first_struct* p = (func_first_struct *) sqlite3_aggregate_context(pContext, sizeof(func_first_struct)); sqlite3_result_text(pContext, p->rlt, -1, SQLITE_TRANSIENT); } int execSQL(sqlite3* db, char * SQL) { char* zErrMsg = NULL; int rlt; exec_callbackcnt = 0; rlt = sqlite3_exec(db, SQL, exec_callback, (void *) SQL, &zErrMsg); if (rlt != SQLITE_OK ) { fprintf(stderr, "SQL오류: %s\n", zErrMsg); } return rlt; } int progress_handler(void* udp) { printf("."); return 0; } void print_limit(sqlite3* db) { printf("\n"); printf("%s = %d\n", "SQLITE_LIMIT_LENGTH", sqlite3_limit(db, SQLITE_LIMIT_LENGTH, -1)); printf("%s = %d\n", "SQLITE_LIMIT_COLUMN", sqlite3_limit(db, SQLITE_LIMIT_COLUMN, -1)); printf("%s = %d\n", "SQLITE_LIMIT_EXPR_DEPTH", sqlite3_limit(db, SQLITE_LIMIT_EXPR_DEPTH, -1)); printf("%s = %d\n", "SQLITE_LIMIT_COMPOUND_SELECT", sqlite3_limit(db, SQLITE_LIMIT_COMPOUND_SELECT, -1)); printf("%s = %d\n", "SQLITE_LIMIT_VDBE_OP", sqlite3_limit(db, SQLITE_LIMIT_VDBE_OP, -1)); printf("%s = %d\n", "SQLITE_LIMIT_FUNCTION_ARG", sqlite3_limit(db, SQLITE_LIMIT_FUNCTION_ARG, -1)); printf("%s = %d\n", "SQLITE_LIMIT_ATTACHED", sqlite3_limit(db, SQLITE_LIMIT_ATTACHED, -1)); printf("%s = %d\n", "SQLITE_LIMIT_LIKE_PATTERN_LENGTH", sqlite3_limit(db, SQLITE_LIMIT_LIKE_PATTERN_LENGTH, -1)); printf("%s = %d\n", "SQLITE_LIMIT_VARIABLE_NUMBER", sqlite3_limit(db, SQLITE_LIMIT_VARIABLE_NUMBER, -1)); printf("%s = %d\n", "SQLITE_LIMIT_TRIGGER_DEPTH", sqlite3_limit(db, SQLITE_LIMIT_TRIGGER_DEPTH, -1)); printf("\n"); } int main(int argc, char *argv[]) { sqlite3* db; int rlt; char * md5usercontext = "MD5USERDATA"; char * firstusercontext = "FIRSTUSERDATA"; printf("SQLite %s\nSQLite Lib %s\n", SQLITE_VERSION, sqlite3_libversion()); if (sqlite3_compileoption_used("SQLITE_ENABLE_FTS5")) { printf("-- with FTS5\n"); } rlt = sqlite3_open("userdb.sqlite", &db); if (rlt != SQLITE_OK ) { fprintf(stderr, "데이터베이스를 열 수 없음: %s\n", sqlite3_errmsg(db)); exit(1); } print_limit(db); #ifndef SQLITE_OMIT_PROGRESS_CALLBACK // 매 10개의 내부 명령셋 호출시 progress_handler 호출됨 // SQLite 는 내부적으로 Bytecode Engine 이 있어 SQL을 Bytecode 으로 변환시켜 실행한다. sqlite3_progress_handler(db, 10, progress_handler, NULL); #endif rlt = sqlite3_create_function(db, "md5", 1, SQLITE_TEXT, md5usercontext, &func_md5, NULL, NULL); rlt = sqlite3_create_function(db, "first", 1, SQLITE_TEXT, firstusercontext, NULL, &func_first_step, &func_first_final); rlt = execSQL(db, "select md5('한글') \"한글(UTF-8)MD5해시\""); rlt = execSQL(db, "select first(username) fun from userinfo"); rlt = execSQL(db, "select * from userlist"); sqlite3_close(db); return rlt != SQLITE_OK; }
컴파일 하기
# 동적 컴파일
windy@wl ~/sqlite $ cc -I/usr/local/include -L/usr/local/lib -R/usr/local/lib -lsqlite3 -lmd5 -m64 -o sqlite sqlite.c
# 정적 컴파일
windy@wl ~/sqlite $ cc -I/usr/local/include -lm -lmd5 -m64 -o sqlite sqlite.c /usr/local/lib/libsqlite3.a
# BerkeyelDB 컴파일
windy@wl ~/sqlite $ cc -I/usr/local/db/include -lmd5 -o sqlite sqlite.c /usr/local/db/lib/libdb_sql-6.1.a -lrt -lsocket -lnsl 1)
1) 이 커맨드는 윈디하나의 솔라나라: BerkeleyDB - SQLite절을 참조하자. 이 커맨드를 사용하기 위해서는 BerkeyelDB가 필요하다.
실행은 아래와 같이 할 수 있다. 실행하기 전에 userdb.sqlite
파일을 생성해놓자.
windy@wl ~/sqlite $ ./sqlite SQLite 3.28.0 SQLite Lib 3.28.0 -- with FTS5 SQLITE_LIMIT_LENGTH = 1000000000 SQLITE_LIMIT_COLUMN = 2000 SQLITE_LIMIT_EXPR_DEPTH = 1000 SQLITE_LIMIT_COMPOUND_SELECT = 500 SQLITE_LIMIT_VDBE_OP = 250000000 SQLITE_LIMIT_FUNCTION_ARG = 127 SQLITE_LIMIT_ATTACHED = 10 SQLITE_LIMIT_LIKE_PATTERN_LENGTH = 50000 SQLITE_LIMIT_VARIABLE_NUMBER = 999 SQLITE_LIMIT_TRIGGER_DEPTH = 1000 SQL: select md5('한글') "한글(UTF-8)MD5해시" 한글(UTF-8)MD5해시 1 52b8c54ab4ea672ee6cdfdfef0a31db4 SQL: select first(username) fun from userinfo fun 1 루트 SQL: select * from userlist userid username tel1 grade rdate 1 root 루트 010-111-1111 1.0 2019-05-07 02:13:37 2 windy 윈디하나 010-000-0000 1.0 2019-05-07 02:13:37
windy@wl ~/sqlite $ cat sqlite.tcl package require sqlite3 sqlite3 userdb "userdb.sqlite"; set tableNames [userdb eval {SELECT name FROM sqlite_master WHERE type IN ('table', 'view')}] puts $tableNames userdb close windy@wl ~/sqlite $ tclsh sqlite.tcl authinfo sqlite_sequence userinfo userlist userjoinstat sqlite_stat1
RSS ATOM XHTML 5 CSS3 |
Copyright © 2004-2025 Jo HoSeok. All rights reserved. |