sqlite使用总结3

前端之家收集整理的这篇文章主要介绍了sqlite使用总结3前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

(4) 事务处理

sqlite 支持事务处理的。如果你知道你要同步删除很多数据,不仿把它们做成一个统一的事务。@H_502_11@

通常一次 sqlite3_exec 就是一次事务,如果你要删除1万条数据,sqlite就做了1万次:开始新事务->删除一条数据->提交事务->开始新事务->… 的过程。这个操作是很慢的。因为时间都花在了开始事务、提交事务上。@H_502_11@

你可以把这些同类操作做成一个事务,这样如果操作错误,还能够回滚事务。@H_502_11@

事务的操作没有特别的接口函数,它就是一个普通的 sql 语句而已:@H_502_11@

分别如下:@H_502_11@

int result; @H_502_11@

result = sqlite3_exec( db,"begin transaction",&zErrorMsg ); //开始一个事务@H_502_11@

result = sqlite3_exec( db,"commit transaction",&zErrorMsg ); //提交事务@H_502_11@

result = sqlite3_exec( db,"rollback transaction",&zErrorMsg ); //回滚事务@H_502_11@

@H_502_11@

一、 给数据库加密

前面所说的内容网上已经有很多资料,虽然比较零散,但是花点时间也还是可以找到的。现在要说的这个——数据库加密,资料就很难找。也可能是我操作水平不够,找不到对应资料。但不管这样,我还是通过网上能找到的很有限的资料,探索出了给sqlite数据库加密的完整步骤。@H_502_11@

这里要提一下,虽然 sqlite 很好用,速度快、体积小巧。但是它保存的文件却是明文的。若不信可以用 NotePad 打开数据库文件瞧瞧,里面 insert 内容几乎一览无余。这样赤裸裸的展现自己,可不是我们的初衷。当然,如果你在嵌入式系统、智能手机上使用 sqlite,最好是不加密,因为这些系统运算能力有限,你做为一个新功能提供者,不能把用户有限的运算能力全部花掉。@H_502_11@

sqlite为了速度而诞生。因此sqlite本身不对数据库加密,要知道,如果你选择标准AES算法加密,那么一定有接近50%的时间消耗在加解密算法上,甚至更多(性能主要取决于你算法编写水平以及你是否能使用cpu提供的底层运算能力,比如MMXsse系列指令可以大幅度提升运算速度)。@H_502_11@

sqlite免费版本是不提供加密功能的,当然你也可以选择他们的收费版本,那你得支付2000块钱,而且是USD。我这里也不是说支付钱不好,如果只为了数据库加密就去支付2000块,我觉得划不来。因为下面我将要告诉你如何为免费的sqlite扩展出加密模块——自己动手扩展,这是sqlite允许,也是它提倡的。@H_502_11@

那么,就让我们一起开始为 sqlite3.c 文件扩展出加密模块。@H_502_11@

@H_502_11@

i.1 必要的宏@H_502_11@

通过阅读 sqlite 代码(当然没有全部阅读完,6万多行代码,没有一行是我习惯的风格,我可没那么多眼神去看),我搞清楚了两件事:@H_502_11@

sqlite支持加密扩展的;@H_502_11@

需要 #define 一个宏才能使用加密扩展。@H_502_11@

这个宏就是 sqlITE_HAS_CODEC@H_502_11@

你在代码最前面(也可以在 sqlite3.h 文件第一行)定义:@H_502_11@

#ifndef sqlITE_HAS_CODEC@H_502_11@

#define sqlITE_HAS_CODEC@H_502_11@

#endif@H_502_11@

@H_502_11@

如果你在代码里定义了此宏,但是还能够正常编译,那么应该是操作没有成功。因为你应该会被编译器提示有一些函数无法链接才对。如果你用的是 VC 2003,你可以在“解决方案”里右键点击你的工程,然后选“属性”,找到“C/C++”,再找到“命令行”,在里面手工添加/D "sqlITE_HAS_CODEC"”。@H_502_11@

定义了这个宏,一些被 sqlite 故意屏蔽掉的代码就被使用了。这些代码就是加解密的接口。@H_502_11@

尝试编译,vc提示你有一些函数无法链接,因为找不到他们的实现。@H_502_11@

如果你也用的是VC2003,那么会得到下面的提示@H_502_11@

error LNK2019: 无法解析的外部符号 _sqlite3CodecGetKey ,该符号在函数 _attachFunc 中被引用@H_502_11@

error LNK2019: 无法解析的外部符号 _sqlite3CodecAttach ,该符号在函数 _attachFunc 中被引用@H_502_11@

error LNK2019: 无法解析的外部符号 _sqlite3_activate_see ,该符号在函数 _sqlite3Pragma 中被引用@H_502_11@

error LNK2019: 无法解析的外部符号 _sqlite3_key ,该符号在函数 _sqlite3Pragma 中被引用@H_502_11@

fatal error LNK1120: 4 个无法解析的外部命令@H_502_11@

@H_502_11@

这是正常的,因为sqlite只留了接口而已,并没有给出实现。@H_502_11@

下面就让我来实现这些接口。@H_502_11@

@H_502_11@

i.2 自己实现加解密接口函数@H_502_11@

如果真要我从一份 www.sqlite.org 网上down下来的 sqlite3.c 文件,直接摸索出这些接口的实现,我认为我还没有这个能力。@H_502_11@

好在网上还有一些代码已经实现了这个功能。通过参照他们的代码以及不断编译中vc给出的错误提示,最终我把整个接口整理出来。@H_502_11@

实现这些预留接口不是那么容易,要重头说一次怎么回事很困难。我把代码都写好了,直接把他们按我下面的说明拷贝到 sqlite3.c 文件对应地方即可。我在下面也提供了sqlite3.c 文件,可以直接参考或取下来使用。@H_502_11@

@H_502_11@

这里要说一点的是,我另外新建了两个文件crypt.ccrypt.h@H_502_11@

其中crypt.h如此定义:@H_502_11@

#ifndef DCG_sqlITE_CRYPT_FUNC_@H_502_11@

#define DCG_sqlITE_CRYPT_FUNC_@H_502_11@

/***********@H_502_11@

董淳光写的 sqlITE 加密关键函数@H_502_11@

***********/@H_502_11@

@H_502_11@

/***********@H_502_11@

关键加密函数@H_502_11@

***********/@H_502_11@

int My_Encrypt_Func( unsigned char * pData,unsigned int data_len,const char * key,unsigned int len_of_key );@H_502_11@

@H_502_11@

/***********@H_502_11@

关键解密函数@H_502_11@

***********/@H_502_11@

int My_DeEncrypt_Func( unsigned char * pData,unsigned int len_of_key );@H_502_11@

@H_502_11@

#endif@H_502_11@

@H_502_11@

@H_502_11@

其中的 crypt.c 如此定义:@H_502_11@

#include "./crypt.h"@H_502_11@

#include "memory.h"@H_502_11@

/***********@H_502_11@

关键加密函数@H_502_11@

***********/@H_502_11@

int My_Encrypt_Func( unsigned char * pData,unsigned int len_of_key )@H_502_11@

{@H_502_11@

return 0;@H_502_11@

}@H_502_11@

@H_502_11@

/***********@H_502_11@

关键解密函数@H_502_11@

***********/@H_502_11@

int My_DeEncrypt_Func( unsigned char * pData,unsigned int len_of_key )@H_502_11@

{@H_502_11@

return 0;@H_502_11@

}@H_502_11@

@H_502_11@

这个文件很容易看,就两函数,一个加密一个解密。传进来的参数分别是待处理的数据、数据长度、密钥、密钥长度。@H_502_11@

处理时直接把结果作用于 pData 指针指向的内容@H_502_11@

你需要定义自己的加解密过程,就改动这两个函数,其它部分不用动。扩展起来很简单。@H_502_11@

这里有个特点,data_len 一般总是 1024 字节。正因为如此,你可以在你的算法里使用一些特定长度的加密算法,比如AES要求被加密数据一定是128位(16字节)长。这个1024不是碰巧,而是 sqlite 的页定义是1024字节,在sqlite3.c文件里有定义:@H_502_11@

# define sqlITE_DEFAULT_PAGE_SIZE 1024@H_502_11@

你可以改动这个值,不过还是建议没有必要不要去改它。@H_502_11@

@H_502_11@

上面写了两个扩展函数,如何把扩展函数 sqlite 挂接起来,这个过程说起来比较麻烦。我直接贴代码@H_502_11@

3个步骤。@H_502_11@

首先,在 sqlite3.c 文件顶部,添加下面内容@H_502_11@

@H_502_11@

#ifdef sqlITE_HAS_CODEC@H_502_11@

#include "./crypt.h"@H_502_11@

/***********@H_502_11@

用于在 sqlite3 最后关闭时释放一些内存@H_502_11@

***********/@H_502_11@

void sqlite3pager_free_codecarg(void *pArg);@H_502_11@

#endif@H_502_11@

这个函数之所以要在 sqlite3.c 开头声明,是因为下面在 sqlite3.c 里面某些函数里要插入这个函数调用。所以要提前声明。@H_502_11@

@H_502_11@

其次,在sqlite3.c文件搜索sqlite3PagerClose函数,要找到它的实现代码(而不是声明代码)。@H_502_11@

实现代码里一开始是:@H_502_11@

#ifdef sqlITE_ENABLE_MEMORY_MANAGEMENT@H_502_11@

/* A malloc() cannot fail in sqlite3ThreadData() as one or more calls to @H_502_11@

** malloc() must have already been made by this thread before it gets@H_502_11@

** to this point. This means the ThreadData must have been allocated already@H_502_11@

** so that ThreadData.nAlloc can be set.@H_502_11@

*/@H_502_11@

ThreadData *pTsd = sqlite3ThreadData();@H_502_11@

assert( pPager );@H_502_11@

assert( pTsd && pTsd->nAlloc );@H_502_11@

#endif@H_502_11@

@H_502_11@

需要在这部分后面紧接着插入:@H_502_11@

@H_502_11@

#ifdef sqlITE_HAS_CODEC@H_502_11@

sqlite3pager_free_codecarg(pPager->pCodecArg);@H_502_11@

#endif@H_502_11@

@H_502_11@

这里要注意,sqlite3PagerClose 函数大概也是 3.3.17版本左右才改名的,以前版本里是叫 “sqlite3pager_close”。因此你在老版本sqlite代码搜索sqlite3PagerClose”是搜不到的。@H_502_11@

类似的还有“sqlite3pager_get”、“sqlite3pager_unref”、“sqlite3pager_write”、“sqlite3pager_pagecount”等都是老版本函数,它们在 pager.h 文件里定义。新版本对应函数是在 sqlite3.h 里定义(因为都合并到 sqlite3.csqlite3.h文件了)。所以,如果你在使用老版本的sqlite,先看看 pager.h 文件,这些函数不是消失了,也不是新蹦出来的,而是老版本函数改名得到的。@H_502_11@

@H_502_11@

最后,往sqlite3.c 文件下找。找到最后一行:@H_502_11@

@H_502_11@

/************** End of main.c ************************************************/@H_502_11@

@H_502_11@

在这一行后面,接上本文最下面的代码段。@H_502_11@

这些代码很长,我不再解释,直接接上去就得了。@H_502_11@

唯一要提的是 DeriveKey 函数。这个函数是对密钥的扩展。比如,你要求密钥是128位,即是16字节,但是如果用户只输入 1个字节呢?2个字节呢?或输入50个字节呢?你得对密钥进行扩展,使之符合16字节的要求。@H_502_11@

DeriveKey 函数就是做这个扩展的。有人把接收到的密钥求md5,这也是一个办法,因为md5运算结果固定16字节,不论你有多少字符,最后就是16字节。这是md5算法的特点。但是我不想用md5,因为还得为它添加包含一些 md5 .c.cpp文件。我不想这么做。我自己写了一个算法来扩展密钥,很简单的算法。当然,你也可以使用你的扩展方法,也而可以使用 md5 算法。只要修改 DeriveKey 函数就可以了。@H_502_11@

DeriveKey 函数里,只管申请空间构造所需要的密钥,不需要释放,因为在另一个函数里有释放过程,而那个函数会在数据库关闭时被调用。参考我的 DeriveKey 函数来申请内存。@H_502_11@

@H_502_11@

这里我给出我已经修改好的 sqlite3.c sqlite3.h 文件@H_502_11@

如果太懒,就直接使用这两个文件,编译肯定能通过,运行也正常。当然,你必须按我前面提的,新建 crypt.h crypt.c 文件,而且函数要按我前面定义的要求来做。@H_502_11@

@H_502_11@

@H_502_11@

i.3 加密使用方法@H_502_11@

现在,你代码已经有了加密功能@H_502_11@

你要把加密功能给用上,除了改 sqlite3.c 文件、给你工程添加 sqlITE_HAS_CODEC 宏,还得修改你的数据库调用函数@H_502_11@

前面提到过,要开始一个数据库操作,必须先 sqlite3_open @H_502_11@

加解密过程就在 sqlite3_open 后面操作。@H_502_11@

假设你已经 sqlite3_open 成功了,紧接着写下面的代码@H_502_11@

int i;@H_502_11@

//添加、使用密码 @H_502_11@

i = sqlite3_key( db,"dcg",3 );@H_502_11@

//修改密码@H_502_11@

i = sqlite3_rekey( db,0 );@H_502_11@

@H_502_11@

sqlite3_key 函数来提交密码。@H_502_11@

1个参数是 sqlite3 * 类型变量,代表着用 sqlite3_open 打开的数据库(或新建数据库)。@H_502_11@

2个参数是密钥。@H_502_11@

3个参数是密钥长度。@H_502_11@

sqlite3_rekey 修改密码参数含义同 sqlite3_key@H_502_11@

@H_502_11@

实际上,你可以在sqlite3_open函数之后,到 sqlite3_close 函数之前任意位置调用 sqlite3_key 来设置密码。@H_502_11@

但是如果你没有设置密码,而数据库之前是有密码的,那么你做任何操作都会得到一个返回值:sqlITE_NOTADB,并且得到错误提示:“file is encrypted or is not a database”。@H_502_11@

只有当你用 sqlite3_key 设置了正确的密码,数据库才会正常工作。@H_502_11@

如果你要修改密码,前提是你必须先 sqlite3_open 打开数据库成功,然后 sqlite3_key 设置密钥成功,之后才能用 sqlite3_rekey 修改密码@H_502_11@

如果数据库有密码,但你没有用 sqlite3_key 设置密码,那么当你尝试用 sqlite3_rekey 修改密码时会得到 sqlITE_NOTADB 返回值。@H_502_11@

如果你需要清空密码,可以使用:@H_502_11@

//修改密码@H_502_11@

i = sqlite3_rekey( db,NULL,0 );@H_502_11@

来完成密码清空功能@H_502_11@

@H_502_11@

@H_502_11@

i.4 sqlite3.c 最后添加代码@H_502_11@

@H_502_11@

@H_502_11@

/***@H_502_11@

董淳光定义的加密函数@H_502_11@

***/@H_502_11@

#ifdef sqlITE_HAS_CODEC@H_502_11@

@H_502_11@

/***@H_502_11@

加密结构@H_502_11@

***/@H_502_11@

#define CRYPT_OFFSET 8@H_502_11@

typedef struct _CryptBlock@H_502_11@

{@H_502_11@

BYTE* ReadKey; // 数据库和写入事务的密钥@H_502_11@

BYTE* WriteKey; // 写入数据库的密钥@H_502_11@

int PageSize; // 页的大小@H_502_11@

BYTE* Data;@H_502_11@

} CryptBlock,*LPCryptBlock;@H_502_11@

@H_502_11@

#ifndef DB_KEY_LENGTH_BYTE /*密钥长度*/@H_502_11@

#define DB_KEY_LENGTH_BYTE 16 /*密钥长度*/@H_502_11@

#endif@H_502_11@

@H_502_11@

#ifndef DB_KEY_PADDING /*密钥位数不足时补充的字符*/@H_502_11@

#define DB_KEY_PADDING 0x33 /*密钥位数不足时补充的字符*/@H_502_11@

#endif@H_502_11@

猜你在找的Sqlite相关文章