转载自:http://www.jb51.cc/article/p-yqwmkucf-og.html
在之前的两篇博文中,分别介绍了sqlITE的基础知识和操作,C/C++ 的一些常用API
本文中,我会给大家用几个小程序示例sqlITE C/C++ API的使用。
1.我们看下最简单的sqlite程序,通过sqlite3_open, sqlite3_exec, sqlite3_close来实现一个简单的数据库操作。
@H_301_27@/* * File: sqlite_test.cpp * Author: Carl * * Created on September 20,2012,3:28 PM */ #include <cstdlib> #include <cstdio> #include <sqlite3.h> static int _sql_callback(void *notused,int argc,char **argv,char **szColName) { int i = 0; printf("notused:0x%x,argc:%d\n",notused,argc); for (i = 0; i < argc; i++) { printf("%s = %s\n",szColName[i],argv[i] == 0 ? "NULL" : argv[i]); } printf("\n"); return 0; } /* * */ int main(int argc,char** argv) { const char *ssql1 = "create table users(userid varchar(20) PRIMARY KEY,age int,birthday datetime);"; const char *ssql2 = "insert into users values('wang',20,'1989-5-4');"; const char *ssql3 = "select * from users;"; sqlite3 *db = 0; char *pErrMsg = 0; int ret = 0; //连接数据库 ret = sqlite3_open("./test.db",&db); if (ret != sqlITE_OK) { fprintf(stderr,"无法打开数据库:%s\n",sqlite3_errmsg(db)); sqlite3_close(db); return 1; } printf("数据库连接成功\n"); //执行建表sql ret = sqlite3_exec(db,ssql1,_sql_callback,&pErrMsg); if (ret != sqlITE_OK) { fprintf(stderr,"sql create error: %s\n",pErrMsg); sqlite3_free(pErrMsg); //这个要的哦,要不然会内存泄露的哦!!! sqlite3_close(db); return 1; } printf("数据库建表成功!!\n"); //执行插入数据 ret = sqlite3_exec(db,ssql2,"sql insert error: %s\n",pErrMsg); sqlite3_free(pErrMsg); //这个要的哦,要不然会内存泄露的哦!!! sqlite3_close(db); return 1; } printf("数据库插入数据成功!\n"); //执行查询操作 ret = sqlite3_exec(db,ssql3,"sql error: %s\n",pErrMsg); sqlite3_free(pErrMsg); sqlite3_close(db); return 1; } printf("数据库查询成功!!\n"); //关闭数据库 sqlite3_close(db); db = 0; return 0; }
运行结果如下结果:
[carl@Fedora sqlite]$ g++ sqlite_test.cpp -lsqlite3 [carl@Fedora sqlite]$ ./a.out 数据库连接成功 数据库建表成功!! 数据库插入数据成功! notused:0x0,argc:3 userid = wang age = 20 birthday = 1989-5-4 数据库查询成功!! [carl@Fedora sqlite]$
2. 我们再看一个在sqlite上是有事务来实现原子操作的的例子,
代码如下:
@H_301_27@/* * File: sqlite_test.cpp * Author: Carl * * Created on 2012年9月22日,上午7:50 */ #include <cstdlib> #include <cstdio> #include <sqlite3.h> static int _sql_callback(void *notused,argv[i] == 0 ? "NULL" : argv[i]); } printf("\n"); return 0; } /* * */ int main(int argc,char** argv) { const char *ssql1 = "create table test_for_cpp (id int,name varchar(10),age int);"; char sql[100] = {0}; sqlite3 *db = NULL; char *pErrMsg = NULL; int ret = 0; bool is_success = true; const char *ssql3 = "select * from test_for_cpp;"; ret = sqlite3_open("./test.db",&db); if (sqlITE_OK != ret) { fprintf(stderr,"无法打开数据库: %s\n",sqlite3_errmsg(db)); sqlite3_close(db); return 1; } printf("数据库连接成功\n"); ret = sqlite3_exec(db,NULL,&pErrMsg); if (sqlITE_OK != ret) { fprintf(stderr,pErrMsg); sqlite3_free(pErrMsg); sqlite3_close(db); return 1; } printf("数据库建表成功!!\n"); sqlite3_exec(db,"begin;",&pErrMsg); //开启事务 if (sqlITE_OK != ret) { fprintf(stderr,"sql begin error: %s\n",pErrMsg); sqlite3_free(pErrMsg); sqlite3_close(db); return 1; } printf("数据库开启事务成功!!\n"); for (int i = 0; i < 10; i++) { sprintf(sql,"insert into test_for_cpp(id,name,age) values(%d,\"%s\",%d);",i,"Carl",i); ret = sqlite3_exec(db,sql,&pErrMsg); if (sqlITE_OK != ret) { is_success = false; fprintf(stderr,"for %d time error: %s\n",pErrMsg); sqlite3_free(pErrMsg); break; } } if (is_success) { sqlite3_exec(db,"commit;",0); printf("数据库插入数据成功!\n"); } else { sqlite3_exec(db,"rollback;",0); printf("数据库插入数据失败!\n"); } ret = sqlite3_exec(db,"sql ERROR: %s\n",pErrMsg); sqlite3_free(pErrMsg); sqlite3_close(db); return 1; } printf("数据库查询成功!!\n"); sqlite3_close(db); db = 0; return 0; }
运行结果:
[carl@Fedora sqlite]$ g++ sqlite_test.cpp -lsqlite3 [carl@Fedora sqlite]$ ./a.out 数据库连接成功 数据库建表成功!! 数据库开启事务成功!! 数据库插入数据成功! notused:0x0,argc:3 id = 0 name = Carl age = 0 notused:0x0,argc:3 id = 1 name = Carl age = 1 notused:0x0,argc:3 id = 2 name = Carl age = 2 notused:0x0,argc:3 id = 3 name = Carl age = 3 notused:0x0,argc:3 id = 4 name = Carl age = 4 notused:0x0,argc:3 id = 5 name = Carl age = 5 notused:0x0,argc:3 id = 6 name = Carl age = 6 notused:0x0,argc:3 id = 7 name = Carl age = 7 notused:0x0,argc:3 id = 8 name = Carl age = 8 notused:0x0,argc:3 id = 9 name = Carl age = 9 数据库查询成功!! [carl@Fedora sqlite]$
3. 我们接着看一下如何能够更好的使用语句参数来操作sqlite数据库,用sqlite3_prepare_v2,sqlite3_bind_*,sqlite3_step,sqlite3_column_*等接口来实现对数据库的操作。
代码如下:里面的注释,有兴趣的可以试着打开试一下,但要记得注释掉相关的重复功能的语句哦。
@H_301_27@/* * File: sqlite_test2.cpp * Author: Carl * * Created on September 21,3:12 PM */ #include <cstdlib> #include <cstdio> #include <cstring> #include <sqlite3.h> static int _sql_callback(void *notused,char** argv) { sqlite3 *conn = NULL; sqlite3_stmt *stmt = NULL; char *err_msg = NULL; int ret = 0; char col_types[][10] = {"","Interger","Float","Text","Blob","NULL"}; ret = sqlite3_open("./test.db",&conn); if (sqlITE_OK != ret) { fprintf(stderr,"sqlite open err,%d\n",ret); return 1; } printf("打开数据库成功!!!\n"); // ret = sqlite3_prepare_v2(conn,"SELECT * FROM [test_for_cpp] WHERE [name]==:name",-1,&stmt,(const char **)&err_msg); ret = sqlite3_prepare_v2(conn,"SELECT * FROM [test_for_cpp] WHERE [name]==?2",(const char **)&err_msg); if (sqlITE_OK != ret) { fprintf(stderr,"sqlite prepare error: %s\n",err_msg); sqlite3_free(err_msg); sqlite3_close(conn); return 1; } // printf("数据库语句对象编译成功!!!%d\n",sqlite3_bind_parameter_index(stmt,":name")); printf("数据库语句对象编译成功!!!\n"); ret = sqlite3_bind_text(stmt,2,4,sqlITE_STATIC); if (sqlITE_OK != ret) { fprintf(stderr,"sqlite bind error: %d\n",ret); sqlite3_close(conn); return 1; } printf("数据库语句对象bind成功!!!\n"); while (ret = sqlite3_step(stmt),ret == sqlITE_ROW) { int col_count = sqlite3_column_count(stmt); //结果集中列的数量 printf("列数:%d\t",col_count); const char *col_0_name = sqlite3_column_name(stmt,0); //获取列名 printf("列名:%s\t",col_0_name); int id = sqlite3_column_int(stmt,0); printf("id值:%d\t",id); int id_type = sqlite3_column_type(stmt,0); //获取列数据类型 printf("id类型:%d\t",id_type); const char *col_2_name = sqlite3_column_name(stmt,2); int age = sqlite3_column_int(stmt,2); int age_type = sqlite3_column_type(stmt,2); const char *col_1_name = sqlite3_column_name(stmt,1); char name[80]; strncpy(name,(const char *)sqlite3_column_text(stmt,1),80); int name_type = sqlite3_column_type(stmt,1); //打印结果 printf("col_count: %d,%s = %d(%s),%s = %s(%s),%s = %d(%s)\n",col_count,col_0_name,id,col_types[id_type],col_1_name,col_types[name_type],col_2_name,age,col_types[age_type]); } fprintf(stderr,"sqlite step exit with %d\n",ret); sqlite3_finalize(stmt); sqlite3_close(conn); return 0; }
运行结果如下:
[carl@Fedora sqlite]$ g++ sqlite_test2.cpp -lsqlite3 [carl@Fedora sqlite]$ ./a.out 打开数据库成功!!! 数据库语句对象编译成功!!! 数据库语句对象bind成功!!! 列数:3 列名:id id值:0 id类型:1 col_count: 3,id = 0(Interger),name = Carl(Text),age = 0(Interger) 列数:3 列名:id id值:1 id类型:1 col_count: 3,id = 1(Interger),age = 1(Interger) 列数:3 列名:id id值:2 id类型:1 col_count: 3,id = 2(Interger),age = 2(Interger) 列数:3 列名:id id值:3 id类型:1 col_count: 3,id = 3(Interger),age = 3(Interger) 列数:3 列名:id id值:4 id类型:1 col_count: 3,id = 4(Interger),age = 4(Interger) 列数:3 列名:id id值:5 id类型:1 col_count: 3,id = 5(Interger),age = 5(Interger) 列数:3 列名:id id值:6 id类型:1 col_count: 3,id = 6(Interger),age = 6(Interger) 列数:3 列名:id id值:7 id类型:1 col_count: 3,id = 7(Interger),age = 7(Interger) 列数:3 列名:id id值:8 id类型:1 col_count: 3,id = 8(Interger),age = 8(Interger) 列数:3 列名:id id值:9 id类型:1 col_count: 3,id = 9(Interger),age = 9(Interger) sqlite step exit with 101 //101意思为sqlITE_DONE [carl@Fedora sqlite]$
下面解释下为什么要学会这种能够细致控制sqlite的方法(即使用语句参数),主要有以下几个优点:
(1) 使用“语句参数”方式,具有更高的安全性,可以有效防止“sql注入攻击”。 “sql注入攻击”要想达到目的,就必须让attack value随着sql命令字符串一起传送进sql解析器。黑客如果在一条sql命令字符串被送入到sqlite3_prepare函数之前,利用c字符串处理函数等途径将attack value注入其中,而在sqlite3_prepare函数之中进行解析(parse),就可以达到攻击目的。而使用“语句参数”方式,被传送到sqlite3_prepare函数的只是sql命令字符串中的参数符号(如:“?”),而不是具体的值。在sqlite3_prepare函数执行之后,才会使用bind函数给参数符号绑定具体的值,这就可以避免attack value随着sql命令字符串一起在sqlite3_prepare函数中被解析,从而有效躲避“sql注入攻击”。
(2)使用“语句参数”方式,可以更快的完成值替换。
(3)使用“语句参数”方式,更节省内存。原因是使用如snprintf函数,需要一个sql命令模板,一块足够大的输出缓存,而且字符串处理函数需要工作内存(working memory),除此之外对于整形,浮点型,特别是BLOBs,经常会占用更多的空间。
原文链接:https://www.f2er.com/sqlite/198413.html