SQLite Cooking
SQLite is an open source DBMS that runs embedded in a client application. It's included by default on Android, iOS, and macOS. You can easily install SQLite on Windows, Linux distros. As the name suggests, SQLite, unlike RDBMS(MySQL, MS SQL, Oracle ...), has no network support. Provides database functionality through file I / O in applications that include SQLite support.
If your OS already has SQLite, sqlite3 command will work fine and sqlite> prompt will appear.
If not installed, installl the SQLite, SQLite development packages.
If sqlite-devel package is installed correctly, you can see the result like this.
sqlite3_open( const char *dbname, sqlite3 **db) function opens database file. The dbname is just a file, so you can pass a full path name like "/usr/local/data/ivr.db".
Remember that if the dbname file does not exist, sqlite3 makes a dbname file automatically. Therefore, this function always returns 0(success code) unless the file cannot be created due to permission problems.
From now on, sqlite3_exec functions will be used frequently.
Pay close attention to the callback parameter and the void * parameter. Calling the sqlite3_exec function does not return a result, but passes it through a callback function.
Some people are concerned about time synchronization due to callback function calls. However, the callback function call is made in the sqlite3_exec function, so there is no time synchronization problem. In other words, when the sqlite3_exec function returns, the callback function is alreay called.
void * parameter is used when you want to pass a parameter to the callback function. It can be used to receive the function results synchronously. I'll talk more about this later.
Build and run the code.
Build and run the code.
Now let's check the table with sqlite3 command. As you can see, the 4 row are created successfully.
Build and run the code.
This time "insert_or_replace()" function does not insert the row bacause of duplicated ID calues, instead the row values will be updated.
In the "select_result()" function we passed "select_callback" callback when calling sqlite3_exec functions.
"select_callback" function is called for each row. In this case the callback function is called 4 times!
This time, I created a "query_channel" function that returns only one row value using a query statement using an ID value.
And I passed channel_info data when calling sqlite3_exec function. This value is passed as the argument of the callback function.
Be careful : If the select sql returns multiple rows, the callback function will be called several times, and only the final result will be returned.
Build and run the code.
You can get the fetched row values using void * parameter.
Install SQLite
If you want to use sqlite in your c/c++ application, first make sure that SQLite is installed in your operating system.[root@localhost ~]# sqlite3 SQLite version 3.6.20 Enter ".help" for instructions Enter SQL statements terminated with a ";" sqlite>
If your OS already has SQLite, sqlite3 command will work fine and sqlite> prompt will appear.
If not installed, installl the SQLite, SQLite development packages.
yum install sqlite sqlite-devel
Using SQLite in c/c++
If you want to use SQLite in your c/c++ code, just include sqlite3.h header file. Build with -lsqlite3 link optionscheck whether sqlite-devel is installed
Just make a cpp file that include sqlite3.h header, and compile the source#include <stdio.h> #include <string> #include <string.h> #include <stdlib.h> #include <sqlite3.h> int main(int argc, char *argv[]) { fprintf(stdout, "SQLite development environment check success\n"); return 0; }
If sqlite-devel package is installed correctly, you can see the result like this.
[root@localhost study]# g++ step1.cpp -lsqlite3 [root@localhost study]# ./a.out SQLite development environment check success
Open SQLite3 Database
#include <stdio.h> #include <string> #include <string.h> #include <stdlib.h> #include <sqlite3.h> using namespace std; sqlite3 *db; int init_sqlite3() { int rc; rc = sqlite3_open("ivr.db", &db); if( rc ) { fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db)); return(-1); } else { fprintf(stderr, "Opened database successfully\n"); } return 0; } int main(int argc, char *argv[]) { int ret; fprintf(stdout, "SQLite development environment check success\n"); ret = init_sqlite3(); if(ret) exit(0); sqlite3_close(db); return 0; }
sqlite3_open( const char *dbname, sqlite3 **db) function opens database file. The dbname is just a file, so you can pass a full path name like "/usr/local/data/ivr.db".
Remember that if the dbname file does not exist, sqlite3 makes a dbname file automatically. Therefore, this function always returns 0(success code) unless the file cannot be created due to permission problems.
Create table if not exists
If there is no desired table in the database, this function is used to automatically create it. "CREATE TABLE IF NOT EXISTS" SQL command will make a table if not exists.From now on, sqlite3_exec functions will be used frequently.
int sqlite3_exec( sqlite3*, /* An open database */ const char *sql, /* SQL to be evaluated */ int (*callback)(void*,int,char**,char**), /* Callback function */ void *, /* 1st argument to callback */ char **errmsg /* Error msg written here */ );
Pay close attention to the callback parameter and the void * parameter. Calling the sqlite3_exec function does not return a result, but passes it through a callback function.
Some people are concerned about time synchronization due to callback function calls. However, the callback function call is made in the sqlite3_exec function, so there is no time synchronization problem. In other words, when the sqlite3_exec function returns, the callback function is alreay called.
void * parameter is used when you want to pass a parameter to the callback function. It can be used to receive the function results synchronously. I'll talk more about this later.
#include <stdio.h> #include <string> #include <string.h> #include <stdlib.h> #include <sqlite3.h> using namespace std; sqlite3 *db; static int default_callback(void *NotUsed, int argc, char **argv, char **azColName) { int i; for(i = 0; i<argc; i++) { printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL"); } printf("\n"); return 0; } int init_sqlite3() { int rc; rc = sqlite3_open("ivr.db", &db); if( rc ) { fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db)); return(-1); } else { fprintf(stderr, "Opened database successfully\n"); } return 0; } int create_table() { string sql; int rc; char *zErrMsg = 0; /* Create SQL statement */ sql = "CREATE TABLE IF NOT EXISTS CHANNEL(" \ "ID INT PRIMARY KEY NOT NULL," \ "CALL_ID INT NOT NULL," \ "TIME REAL NOT NULL," \ "CALLER TEXT NOT NULL," \ "RESERVED INT );"; /* Execute SQL statement */ rc = sqlite3_exec(db, sql.c_str(), default_callback, 0, &zErrMsg); if( rc != SQLITE_OK ){ fprintf(stderr, "SQL error: %s\n", zErrMsg); sqlite3_free(zErrMsg); } else { fprintf(stdout, "Table created successfully\n"); } return 0; } int main(int argc, char *argv[]) { int ret; fprintf(stdout, "SQLite development environment check success\n"); ret = init_sqlite3(); if(ret) exit(0); create_table(); sqlite3_close(db); return 0; }
Build and run the code.
[root@localhost study]# g++ step2.cpp -lsqlite3 [root@localhost study]# ./a.out SQLite development environment check success Opened database successfully Table created successfully
insert or update SQL
Now let's add some data to the table we created earlier. In our table, the ID column is a key that does not allow duplicates. If the ID column value already exists, update the row. If not, execute the insert command. We will insert or update 4 rows. "REPLACE" command will do it.} #include <stdio.h> #include <string> #include <string.h> #include <stdlib.h> #include <sys/time.h> // for_gettimeofday #include <unistd.h> // for usleep() #include <sqlite3.h> using namespace std; sqlite3 *db; static int default_callback(void *NotUsed, int argc, char **argv, char **azColName) { int i; for(i = 0; i<argc; i++) { printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL"); } printf("\n"); return 0; } int init_sqlite3() { int rc; rc = sqlite3_open("ivr.db", &db); if( rc ) { fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db)); return(-1); } else { fprintf(stderr, "Opened database successfully\n"); } return 0; } int create_table() { string sql; int rc; char *zErrMsg = 0; /* Create SQL statement */ sql = "CREATE TABLE IF NOT EXISTS CHANNEL(" \ "ID INT PRIMARY KEY NOT NULL," \ "CALL_ID INT NOT NULL," \ "TIME REAL NOT NULL," \ "CALLER TEXT NOT NULL," \ "RESERVED INT );"; /* Execute SQL statement */ rc = sqlite3_exec(db, sql.c_str(), default_callback, 0, &zErrMsg); if( rc != SQLITE_OK ){ fprintf(stderr, "SQL error: %s\n", zErrMsg); sqlite3_free(zErrMsg); } else { fprintf(stdout, "Table created successfully\n"); } return 0; } int insert_or_replace() { char sql[1024]; int rc; char *zErrMsg = 0; struct timeval start; int channel = 1000; int callid = 10000; double secs_used; for(int x = 0; x < 4; x++){ gettimeofday(&start, NULL); secs_used= (double)(start.tv_sec * 1000000.0 + start.tv_usec) / 1000000.0 ; sprintf(sql, "REPLACE INTO CHANNEL (ID, CALL_ID, TIME, CALLER, RESERVED) VALUES (%d, %d, %f, 'Caller',0 );", channel + x, callid + x, secs_used); printf("%s\n", sql); rc = sqlite3_exec(db, sql, default_callback, 0, &zErrMsg); if( rc != SQLITE_OK ){ fprintf(stderr, "SQL error: %s\n", zErrMsg); sqlite3_free(zErrMsg); } else { fprintf(stdout, "Records created successfully\n"); } sleep(1); } return 0; } int main(int argc, char *argv[]) { int ret; fprintf(stdout, "SQLite development environment check success\n"); ret = init_sqlite3(); if(ret) exit(0); create_table(); insert_or_replace(); sqlite3_close(db); return 0; }
Build and run the code.
[root@localhost study]# g++ step3.cpp -lsqlite3 [root@localhost study]# ./a.out SQLite development environment check success Opened database successfully Table created successfully REPLACE INTO CHANNEL (ID, CALL_ID, TIME, CALLER, RESERVED) VALUES (1000, 10000, 1572012952.884573, 'Caller',0 ); Records created successfully REPLACE INTO CHANNEL (ID, CALL_ID, TIME, CALLER, RESERVED) VALUES (1001, 10001, 1572012953.914426, 'Caller',0 ); Records created successfully REPLACE INTO CHANNEL (ID, CALL_ID, TIME, CALLER, RESERVED) VALUES (1002, 10002, 1572012954.929729, 'Caller',0 ); Records created successfully REPLACE INTO CHANNEL (ID, CALL_ID, TIME, CALLER, RESERVED) VALUES (1003, 10003, 1572012955.945338, 'Caller',0 ); Records created successfully
Now let's check the table with sqlite3 command. As you can see, the 4 row are created successfully.
[root@localhost study]# sqlite3 ivr.db SQLite version 3.6.20 Enter ".help" for instructions Enter SQL statements terminated with a ";" sqlite> select * from channel; 1000|10000|1572012952.88457|Caller|0 1001|10001|1572012953.91443|Caller|0 1002|10002|1572012954.92973|Caller|0 1003|10003|1572012955.94534|Caller|0
select SQL
The select command also uses the sqlite3_exec function. This time the callback function is very important. This is because the query result is passed through the callback function.#include <stdio.h> #include <string> #include <string.h> #include <stdlib.h> #include <sys/time.h> // for clock_gettime() #include <unistd.h> // for usleep() #include <sqlite3.h> using namespace std; sqlite3 *db; static int default_callback(void *NotUsed, int argc, char **argv, char **azColName) { int i; for(i = 0; i<argc; i++) { printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL"); } printf("\n"); return 0; } static int select_callback(void *NotUsed, int argc, char **argv, char **azColName) { int i; struct timeval start; gettimeofday(&start, NULL); double secs_used= ((double)start.tv_sec * 1000000.0 + start.tv_usec) / 1000000.0 ; for(i = 0; i<argc; i++) { printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL"); if(strcasecmp( azColName[i], "TIME") == 0){ printf("secs elapsed : %10.3f\n", secs_used -atof(argv[i])); } } printf("\n"); return 0; } int init_sqlite3() { int rc; rc = sqlite3_open("ivr.db", &db); if( rc ) { fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db)); return(-1); } else { fprintf(stderr, "Opened database successfully\n"); } return 0; } int create_table() { string sql; int rc; char *zErrMsg = 0; /* Create SQL statement */ sql = "CREATE TABLE IF NOT EXISTS CHANNEL(" \ "ID INT PRIMARY KEY NOT NULL," \ "CALL_ID INT NOT NULL," \ "TIME REAL NOT NULL," \ "CALLER TEXT NOT NULL," \ "RESERVED INT );"; /* Execute SQL statement */ rc = sqlite3_exec(db, sql.c_str(), default_callback, 0, &zErrMsg); if( rc != SQLITE_OK ){ fprintf(stderr, "SQL error: %s\n", zErrMsg); sqlite3_free(zErrMsg); } else { fprintf(stdout, "Table created successfully\n"); } return 0; } int insert_or_replace() { char sql[1024]; int rc; char *zErrMsg = 0; struct timeval start; int channel = 1000; int callid = 10000; double secs_used; for(int x = 0; x < 4; x++){ gettimeofday(&start, NULL); secs_used= (double)(start.tv_sec * 1000000.0 + start.tv_usec) / 1000000.0 ; sprintf(sql, "REPLACE INTO CHANNEL (ID, CALL_ID, TIME, CALLER, RESERVED) VALUES (%d, %d, %f, 'Caller',0 );", channel + x, callid + x, secs_used); printf("%s\n", sql); rc = sqlite3_exec(db, sql, default_callback, 0, &zErrMsg); if( rc != SQLITE_OK ){ fprintf(stderr, "SQL error: %s\n", zErrMsg); sqlite3_free(zErrMsg); } else { fprintf(stdout, "Records created successfully\n"); } sleep(1); } return 0; } int select_result() { char sql[1024]; int rc; char *zErrMsg = 0; struct timeval start; const char* data = "Callback function called"; strcpy(sql,"SELECT * from CHANNEL"); rc = sqlite3_exec(db, sql, select_callback, (void*)data, &zErrMsg); if( rc != SQLITE_OK ) { fprintf(stderr, "SQL error: %s\n", zErrMsg); sqlite3_free(zErrMsg); } else { fprintf(stdout, "Operation done successfully\n"); } return 0; } int main(int argc, char *argv[]) { int ret; fprintf(stdout, "SQLite development environment check success\n"); ret = init_sqlite3(); if(ret) exit(0); create_table(); insert_or_replace(); select_result(); sqlite3_close(db); return 0; }
Build and run the code.
[root@localhost study]# g++ step4.cpp -lsqlite3 [root@localhost study]# ./a.out SQLite development environment check success Opened database successfully Table created successfully REPLACE INTO CHANNEL (ID, CALL_ID, TIME, CALLER, RESERVED) VALUES (1000, 10000, 1572013347.223009, 'Caller',0 ); Records created successfully REPLACE INTO CHANNEL (ID, CALL_ID, TIME, CALLER, RESERVED) VALUES (1001, 10001, 1572013348.267462, 'Caller',0 ); Records created successfully REPLACE INTO CHANNEL (ID, CALL_ID, TIME, CALLER, RESERVED) VALUES (1002, 10002, 1572013349.282904, 'Caller',0 ); Records created successfully REPLACE INTO CHANNEL (ID, CALL_ID, TIME, CALLER, RESERVED) VALUES (1003, 10003, 1572013350.298310, 'Caller',0 ); Records created successfully ID = 1000 CALL_ID = 10000 TIME = 1572013347.22301 secs elapsed : 4.091 CALLER = Caller RESERVED = 0 ID = 1001 CALL_ID = 10001 TIME = 1572013348.26746 secs elapsed : 3.047 CALLER = Caller RESERVED = 0 ID = 1002 CALL_ID = 10002 TIME = 1572013349.2829 secs elapsed : 2.032 CALLER = Caller RESERVED = 0 ID = 1003 CALL_ID = 10003 TIME = 1572013350.29831 secs elapsed : 1.016 CALLER = Caller RESERVED = 0
This time "insert_or_replace()" function does not insert the row bacause of duplicated ID calues, instead the row values will be updated.
In the "select_result()" function we passed "select_callback" callback when calling sqlite3_exec functions.
"select_callback" function is called for each row. In this case the callback function is called 4 times!
Get results using void * parameter
Finally, let's get the result using void * parameter in the callback.#include <stdio.h> #include <string> #include <string.h> #include <stdlib.h> #include <sys/time.h> // for clock_gettime() #include <unistd.h> // for usleep() #include <sqlite3.h> using namespace std; typedef struct _channel_info{ int channel; int call_id; char caller[64]; double elapsed; } channel_info; sqlite3 *db; static int default_callback(void *NotUsed, int argc, char **argv, char **azColName) { int i; for(i = 0; i<argc; i++) { printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL"); } printf("\n"); return 0; } static int query_channel_callback(void *info, int argc, char **argv, char **azColName) { int i; struct timeval start; channel_info *pinfo = (channel_info *)info ; gettimeofday(&start, NULL); double secs_used= ((double)start.tv_sec * 1000000.0 + start.tv_usec) / 1000000.0 ; memset(pinfo, 0x00, sizeof(channel_info)); for(i = 0; i<argc; i++) { if(strcasecmp( azColName[i], "TIME") == 0){ pinfo->elapsed = secs_used -atof(argv[i]); } else if(strcasecmp( azColName[i], "CALL_ID") == 0){ pinfo->call_id = atoi(argv[i]); } else if(strcasecmp( azColName[i], "CALLER") == 0){ strncpy(pinfo->caller, argv[i], 64); } else if(strcasecmp( azColName[i], "ID") == 0){ pinfo->channel = atoi(argv[i]); } } return 0; } int init_sqlite3() { int rc; rc = sqlite3_open("ivr.db", &db); if( rc ) { fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db)); return(-1); } else { fprintf(stderr, "Opened database successfully\n"); } return 0; } int create_table() { string sql; int rc; char *zErrMsg = 0; /* Create SQL statement */ sql = "CREATE TABLE IF NOT EXISTS CHANNEL(" \ "ID INT PRIMARY KEY NOT NULL," \ "CALL_ID INT NOT NULL," \ "TIME REAL NOT NULL," \ "CALLER TEXT NOT NULL," \ "RESERVED INT );"; /* Execute SQL statement */ rc = sqlite3_exec(db, sql.c_str(), default_callback, 0, &zErrMsg); if( rc != SQLITE_OK ){ fprintf(stderr, "SQL error: %s\n", zErrMsg); sqlite3_free(zErrMsg); } else { fprintf(stdout, "Table created successfully\n"); } return 0; } int insert_or_replace() { char sql[1024]; int rc; char *zErrMsg = 0; struct timeval start; int channel = 1000; int callid = 10000; double secs_used; for(int x = 0; x < 4; x++){ gettimeofday(&start, NULL); secs_used= (double)(start.tv_sec * 1000000.0 + start.tv_usec) / 1000000.0 ; sprintf(sql, "REPLACE INTO CHANNEL (ID, CALL_ID, TIME, CALLER, RESERVED) VALUES (%d, %d, %f, 'Caller',0 );", channel + x, callid + x, secs_used); printf("%s\n", sql); rc = sqlite3_exec(db, sql, default_callback, 0, &zErrMsg); if( rc != SQLITE_OK ){ fprintf(stderr, "SQL error: %s\n", zErrMsg); sqlite3_free(zErrMsg); } else { fprintf(stdout, "Records created successfully\n"); } sleep(1); } return 0; } int query_channel(int channel, int *call_id, char *caller, double *elapsed) { char sql[1024]; int rc; char *zErrMsg = 0; struct timeval start; float secs_used; channel_info data; memset(&data, 0x00, sizeof(channel_info)); sprintf(sql,"SELECT CALL_ID, TIME, CALLER from CHANNEL where ID = %d", channel); rc = sqlite3_exec(db, sql, query_channel_callback, (void*)&data, &zErrMsg); if( rc != SQLITE_OK ) { fprintf(stderr, "SQL error: %s\n", zErrMsg); sqlite3_free(zErrMsg); } else { fprintf(stdout, "Querychanneln done successfully\n"); fprintf(stdout, "caller:%s\n", data.caller); fprintf(stdout, "callid:%d\n", data.call_id); fprintf(stdout, "celapsed:%f\n", data.elapsed); *call_id = data.call_id; *elapsed = data.elapsed; strcpy(caller, data.caller); } return 0; } int main(int argc, char *argv[]) { int ret; fprintf(stdout, "SQLite development environment check success\n"); ret = init_sqlite3(); if(ret) exit(0); create_table(); insert_or_replace(); int call_id; double elapsed; char caller[64]; query_channel(1000,&call_id, caller, &elapsed); sqlite3_close(db); return 0; }
This time, I created a "query_channel" function that returns only one row value using a query statement using an ID value.
And I passed channel_info data when calling sqlite3_exec function. This value is passed as the argument of the callback function.
rc = sqlite3_exec(db, sql, query_channel_callback, (void*)&data, &zErrMsg);
static int query_channel_callback(void *info, int argc, char **argv, char **azColName) { int i; struct timeval start; channel_info *pinfo = (channel_info *)info ; ...............
Be careful : If the select sql returns multiple rows, the callback function will be called several times, and only the final result will be returned.
Build and run the code.
[root@localhost study]# g++ step5.cpp -lsqlite3 [root@localhost study]# ./a.out SQLite development environment check success Opened database successfully Table created successfully REPLACE INTO CHANNEL (ID, CALL_ID, TIME, CALLER, RESERVED) VALUES (1000, 10000, 1572014946.196102, 'Caller',0 ); Records created successfully REPLACE INTO CHANNEL (ID, CALL_ID, TIME, CALLER, RESERVED) VALUES (1001, 10001, 1572014947.225994, 'Caller',0 ); Records created successfully REPLACE INTO CHANNEL (ID, CALL_ID, TIME, CALLER, RESERVED) VALUES (1002, 10002, 1572014948.241377, 'Caller',0 ); Records created successfully REPLACE INTO CHANNEL (ID, CALL_ID, TIME, CALLER, RESERVED) VALUES (1003, 10003, 1572014949.256861, 'Caller',0 ); Records created successfully Querychanneln done successfully caller:Caller callid:10000 celapsed:4.076267
You can get the fetched row values using void * parameter.
댓글
댓글 쓰기