加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

sqlite之我见--C/C++ API接口示例

发布时间:2020-12-12 19:21:56 所属栏目:百科 来源:网络整理
导读:转载自:http://www.jb51.cc/article/p-yqwmkucf-og.html 在之前的两篇博文中,分别介绍了SQLITE的基础知识和操作,C/C++ 的一些常用API sqlite之我见--简单介绍与基本操作 sqlite之我见--C/C++ API接口介绍 本文中,我会给大家用几个小程序示例SQLITE C/C++

转载自:http://www.52php.cn/article/p-yqwmkucf-og.html

在之前的两篇博文中,分别介绍了SQLITE的基础知识和操作,C/C++ 的一些常用API

sqlite之我见--简单介绍与基本操作

sqlite之我见--C/C++ API接口介绍


本文中,我会给大家用几个小程序示例SQLITE C/C++ API的使用。

1.我们看下最简单的sqlite程序,通过sqlite3_open, sqlite3_exec, sqlite3_close来实现一个简单的数据库操作。

/* 
 * 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:%dn",notused,argc);
    for (i = 0; i < argc; i++)
    {
        printf("%s = %sn",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,"无法打开数据库:%sn",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: %sn",pErrMsg);
        sqlite3_free(pErrMsg); //这个要的哦,要不然会内存泄露的哦!!!
        sqlite3_close(db);
        return 1;
    }
    printf("数据库建表成功!!n");
    
    //执行插入数据
    ret = sqlite3_exec(db,sSQL2,"SQL insert error: %sn",pErrMsg);
        sqlite3_free(pErrMsg); //这个要的哦,要不然会内存泄露的哦!!!
        sqlite3_close(db);
        return 1;
    }
    printf("数据库插入数据成功!n");
    
    //执行查询操作
    ret = sqlite3_exec(db,sSQL3,"SQL error: %sn",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上是有事务来实现原子操作的的例子,

代码如下:

/* 
 * 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,"无法打开数据库: %sn",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: %sn",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: %sn",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: %sn",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_*等接口来实现对数据库的操作。

代码如下:里面的注释,有兴趣的可以试着打开试一下,但要记得注释掉相关的重复功能的语句哦。

/* 
 * 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,%dn",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: %sn",err_msg);
        sqlite3_free(err_msg);
        sqlite3_close(conn);
        return 1;
    }
//    printf("数据库语句对象编译成功!!!%dn",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: %dn",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("列数:%dt",col_count);
        const char *col_0_name = sqlite3_column_name(stmt,0); //获取列名
        printf("列名:%st",col_0_name);
        int id = sqlite3_column_int(stmt,0);
        printf("id值:%dt",id);
        int id_type = sqlite3_column_type(stmt,0); //获取列数据类型
        printf("id类型:%dt",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 %dn",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,经常会占用更多的空间。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读