ImageVerifierCode 换一换
格式:DOC , 页数:52 ,大小:605.50KB ,
资源ID:3534861      下载积分:20 文钱
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,省得不是一点点
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.wenke99.com/d-3534861.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: QQ登录   微博登录 

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(SQLite3源程序分析_v100.doc)为本站会员(sk****8)主动上传,文客久久仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知文客久久(发送邮件至hr@wenke99.com或直接QQ联系客服),我们立即给予删除!

SQLite3源程序分析_v100.doc

1、SQLite3 源程序分析作者:空转本文分析的 SQLite 版本为 3.6.18。现在已经变成 3.6.20 了,但本文中所涉及的内容变化不大。读者最好能下载一个源程序,然后将本文与源程序对照阅读。这样也有利于发现本文的错误,说实话吧,我写的时候是连分析带猜的,错误肯定很多。参考文献:1-The Definitive Guide to SQLite . Michael Owens:比较经典的 SQLite 著作。我边看边翻译了其中的部分内容,但翻得不好,大家还是看原文吧。2-SQLite 文件格式分析_v102 . 空转:我写的,写得特好。现在是 v102 版,跟前面的版本相比增加了不少背景

2、知识,对文件格式的介绍算是很全面了。看本文之前,应该先浏览一下此参考文献。1. SQLite3 程序分析1.1.主程序流程所谓“主程序”是指 SQLite 所提供的命令行处理程序(CLP ) 。通过对它的分析可以对SQLite 源程序建立整体概念,比一上来就直接分析单独 API 的处理过程要容易。CLP 的主要程序都在 shell.c 中。CLP 的执行流程很简单:循环接受用户输入的 SQL 命令,处理 SQL 命令。命令的执行都是调用 sqlite3_exec()函数完成,也就是采用的是 “执行封装的 Query”的形式 1。程序定义了一个功能比较强大的回叫函数来处理 SQL 命令执行的返回

3、结果:static int callback(void *pArg, int nArg, char *azArg, char *azCol);程序定义了 9 种回显的形式,通过一个 callback_data 结构来对回显参数进行配置。1.1.1. 程序主函数程序的 main()函数在 shell.c 的尾部,简化后的 main()函数的执行过程主要分为 5 步:1. 设置回显参数2. 取数据库文件名3. 打开数据库4. 循环处理 SQL 命令5. 关闭数据库如下:int main(int argc, char *argv)struct callback_data data; /回显参数int

4、 rc = 0;Argv0 = argv0;main_init( /设置默认的回显形式/取数据库文件名,如没有,默认为内存数据库data.zDbFilename = argv1;data.out = stdout;/* 如果数据库文件存在,则打开它。* 如果不存在,先不打开(现在什么都不做) ,* 可以防止用户因错误的输入而创建空文件。*/if( access(data.zDbFilename, 0)=0 ) open_db(printf(“SQLite version %sn“Enter “.help“ for instructionsn“Enter SQL statements termi

5、nated with a “;“n“,sqlite3_libversion();rc = process_input(if( db ) /关闭数据库if( sqlite3_close(db)!=SQLITE_OK )fprintf(stderr,“error closing database: %sn“, sqlite3_errmsg(db);return rc;说明:上述函数与源程序相比做了很大的简化,去掉的部分不是不重要的,而是“可以不解释”的。实用程序的流程一般都是复杂的,SQLite 也不例外。本文按照自己的主线进行介绍,只求能说明问题(自圆其说) ,主线之外的东西,不管重不重要,都尽

6、量忽略。后面的函数也存在这样情况,就不再说明了。回显参数的设置就不再介绍了,参考源程序的 callback()函数和 callback_data 结构,有关回叫函数的使用见参考文献一。下面介绍数据库的打开过程。1.1.2. 打开数据库数据库文件的打开过程在 SQLite 的权威文档中有介绍,过程如下图:图 1-1 数据库文件的打开过程在 CLP 中打开数据库,比上图又多了两层,其调用层次如下:1-main():位于 shell.c。从命令行参数中得到数据库名,如果数据库文件存在,则打开它。2-open_db():位于 shell.c。功能:确认数据库是否已经打开。如果已打开,则什么都不做。如果

7、没有,则打开它。如果打开失败,输出一个错误信息。3-sqlite3_open():位于 main.c。功能:打开一个数据库。该函数中只包含对 opendatabase()的调用,但调用的参数与 sqlite3_open_v2()所使用的参数不同。4-opendatabase():位于 main.c。功能:这个函数为 sqlite3_open()和 sqlite3_open16()工作,打开一个数据库。数据库文件名“zFilename“采用 UTF-8 编码。先生成各类标志什么的,然后生成默认的排序法。当需要生成数据库后台驱动时,调用sqlite3BtreeFactory()。在此函数中真正分配

8、 sqlite 结构的空间:db = sqlite3MallocZero( sizeof(sqlite3) )。在调用 sqlite3BtreeFactory()之前,需要对 db 的一些域进行设置。5-sqlite3BtreeFactory()位于 main.c。功能:本函数创建到数据库 BTree 驱动的连接。如果 zFilename 是文件名,则打开并使用它。如果 zFilename 是“:memory:“,则使用内存数据库(在连接断开时释放) 。如果 zFilename 为空且数据库是虚拟(virtual)的,则只是暂时使用,在连接断开时被删除。虚拟数据库可以是磁盘文件或就在内存中,由

9、 sqlite3TempInMemory()函数来决定是哪一种情况。6-sqlite3BtreeOpen():位于 btree.c。功能:打开一个数据库文件。由于在 sqlite3BtreeFactory()中已经调用过 sqlite3TempInMemory()函数,所以此处逻辑稍简单了一些。zFilename 是数据库文件名。如果 zFilename 为空,创建一个具有随机文件名的数据库,这个数据库会在调用 sqlite3BtreeClose()时被删除。如果 zFilename 是“:memory:“,创建内存数据库,并在关闭时被释放。如果此 Btree 是共享缓冲区的候选者,则尝试寻找

10、一个已存在的 BtShared 来共享。 (参本文后面关于内存数据结构的介绍)如果不是共享缓冲区的候选者或未找到已存在的 BtShared,则调用 sqlite3PagerOpen()函数打开文件。文件打开之后,调用 sqlite3PagerReadFileheader()来读文件头中的配置信息。7-sqlite3PagerOpen():位于 pager.c。功能:分配并初始化一个新 Pager 对象,将其指针放到*ppPager。该 pager 会在调用 sqlite3PagerClose()时被释放。zFilename 参数是要打开的数据库文件的路径。如果 zFilename 为空,创建随

11、机文件名的文件。如果 zFilename 为“:memory:“,所有信息都放到缓冲区中,不会被写入磁盘。这用来实现内存数据库。如果 pager 对象已分配且指定文件打开成功,返回 SQLITE_OK 并将*ppPager 指向新 pager对象。如果有错误发生,*ppPager 置空并返回错误代码。执行过程是:先申请空间,再调用 sqlite3OsOpen()打开文件(如果需要) ,再根据打开的文件设置内存。8-sqlite3OsOpen():位于 os.c。功能:打开一个文件,与具体的操作系统无关。是一种 VFS 封装。VFS 的意思是“virtual file system“,虚拟文件系

12、统。本函数只有几条语句,只有一条关键语句:rc = pVfs-xOpen(pVfs, zPath, pFile, flags 对于 Win32 操作系统,该语句实际调用的是 winOpen()函数。9-winOpen():位于 os_win.c。功能:打开一个 Windows 操作系统文件。先将文件名转换为操作系统所使用的编码。再设置一系列参数。最终调用操作系统函数 CreateFileA()打开文件。10-CreateFileA():位于 WINBASE.H。功能:打开文件名所指定的文件。如果文件不存在,则创建。1.1.3. 循环处理 SQL 命令SQL 命令的处理是由 process_in

13、put()函数完成的。该函数还完成”.” 命令的处理,这我们就不管了。简化后的 process_input()函数如下:static int process_input(struct callback_data *p, FILE *in)while( 1 )zLine = one_input_line(zSql, in);if( zLine continue;rc = sqlite3_exec(p-db, zSql, callback, p, if( rc | zErrMsg )处理出错信息;return errCnt;这么简化应该就不用解释了。1.2.SQL 命令编译与执行的过程1.2.1.

14、 sqlite3_exec()函数函数 sqlite3_exec()位于文件 legacy.c 的尾部,其函数头为:int sqlite3_exec(sqlite3 *db, /* 一个打开的数据库连接 */const char *zSql, /* 要执行的 SQL 语句 */sqlite3_callback xCallback, /* 回叫函数 */void *pArg, /* 传递给 xCallback()的第一个参数 */char *pzErrMsg /* 将错误信息写到*pzErrMsg 中 */)sqlite3_exec()函数一次可以执行多条 SQL 命令。执行完成后返回一个 SQ

15、LITE_ success/failure 代码,还会将错误信息写到*pzErrMsg 中。如果 SQL 是查询,查询结果中的每一行都会调用 xCallback()函数。pArg 为传递给 xCallback()的第一个参数。如果xCallback=NULL,即使对查询命令也没有回叫调用。sqlite3_exec()函数的实现体现了一个典型的、实用的 SQL 语句处理过程,我认为对应用程序的开发很有借鉴意义,所以就不过多简化了,去掉一些测试代码,增加一些注释,源程序基本如下:int sqlite3_exec(sqlite3 *db, /* 一个打开的数据库连接 */const char *zS

16、ql, /* 要执行的 SQL 语句 */sqlite3_callback xCallback, /* 回叫函数 */void *pArg, /* 传递给 xCallback()的第一个参数 */char *pzErrMsg /* 将错误信息写到*pzErrMsg 中 */)int rc = SQLITE_OK; /* 返回码 */const char *zLeftover; /* 未处理的 SQL 串尾部。zSql 中可能包含多个 SQL语句,一次处理一个,此变量为剩下的还未处理的语句。 */sqlite3_stmt *pStmt = 0; /* 当前 SQL 语句(对象) */char *

17、azCols = 0; /* 结果字段(s)的名称 */int nRetry = 0; /* 重试的次数 */int callbackIsInit; /* 如果初始化了回叫函数,为 true */if( zSql=0 ) zSql = “;sqlite3Error(db, SQLITE_OK, 0); /* 清除 db 中的错误信息 */while( (rc=SQLITE_OK | (rc=SQLITE_SCHEMA if( azCols=0 )goto exec_out;for(i=0; imallocFailed = 1;goto exec_out;/* 3-调用回叫函数对返回的记录进行处

18、理 */if( xCallback(pArg, nCol, azVals, azCols) )rc = SQLITE_ABORT;sqlite3VdbeFinalize(Vdbe *)pStmt);pStmt = 0;sqlite3Error(db, SQLITE_ABORT, 0);goto exec_out;/*如果返回的不是记录,有两种情况:一种是到达结果记录集的结尾,第二种是执行 create table 一类的不返回记录集的命令。无论哪种情况,此处都需要“定案” 。*/if( rc!=SQLITE_ROW )rc = sqlite3VdbeFinalize(Vdbe *)pStmt)

19、;pStmt = 0;if( rc!=SQLITE_SCHEMA )nRetry = 0;zSql = zLeftover;while( sqlite3Isspace(zSql0) ) zSql+;break;sqlite3DbFree(db, azCols);azCols = 0;exec_out:if( pStmt ) sqlite3VdbeFinalize(Vdbe *)pStmt);sqlite3DbFree(db, azCols);rc = sqlite3ApiExit(db, rc);/* 对出错信息进行处理 */if( rc!=SQLITE_OK *pzErrMsg = sqli

20、te3Malloc(nErrMsg);if( *pzErrMsg )memcpy(*pzErrMsg, sqlite3_errmsg(db), nErrMsg);elserc = SQLITE_NOMEM;sqlite3Error(db, SQLITE_NOMEM, 0);else if( pzErrMsg )*pzErrMsg = 0;return rc;1.2.2. SQL 语句编译的调用层次当调用 sqlite3_prepare()函数时,编译一条 SQL 语句。编译过程的调用层次如下:1- sqlite3_prepare()在 prepare.c 中。SQLite 现在提供两个版本的编

21、译 API 函数:遗留的和现在使用的。在遗留版本中,原始 SQL 文本没有保存在编译后的语句(sqlite3_stmt 结构)中,因此,如果 schema 发生改变,sqlite3_step()会返回 SQLITE_SCHEMA。在新版本中,编译后的语句中保存原始 SQL 文本,当遇到 schema 改变时自动重新编译。sqlite3_prepare()函数中其实只包含一条对 sqlite3LockAndPrepare()的调用语句:rc = sqlite3LockAndPrepare(db,zSql,nBytes,0,ppStmt,pzTail);其中第 4 个参数为 0,表示不将 SQL

22、文本复制到 ppStmt 中。空注:源程序中紧跟此函数的 sqlite3_prepare_v2()函数中在调用 sqlite3LockAndPrepare()时第 4 个参数为 1,不知与上述解释是否矛盾。2- sqlite3LockAndPrepare()在 prepare.c 中。结合注释,很简单,也很清晰。static int sqlite3LockAndPrepare(sqlite3 *db, /* 数据库句柄 */const char *zSql, /* UTF-8 编码的 SQL 语句 */int nBytes, /* zSql 的字节数 */int saveSqlFlag, /*

23、 如果为 True,将 SQL 文本复制到 sqlite3_stmt 中。 */sqlite3_stmt *ppStmt, /* OUT: 指向语句句柄 */const char *pzTail /* OUT: 未处理的 SQL 串 */)int rc;*ppStmt = 0;if( !sqlite3SafetyCheckOk(db) ) /* 确定 db 指针的合法性。 */return SQLITE_MISUSE;/* 将 UTF-8 编码的 SQL 语句 zSql 编译成。 */rc = sqlite3Prepare(db, zSql, nBytes, saveSqlFlag, ppSt

24、mt, pzTail);if( rc=SQLITE_SCHEMA ) /* 如果遇到 SCHEMA 改变,定案,再编译 */sqlite3_finalize(*ppStmt);rc = sqlite3Prepare(db, zSql, nBytes, saveSqlFlag, ppStmt, pzTail);return rc;3- sqlite3Prepare()在 prepare.c 中。很长的函数,在其中调用 sqlite3RunParser()函数,在给定的 SQL 字符串上执行分析器。函数中,先创建 Parse 结构、加锁什么的,到调用 sqlite3RunParser()函数时参数

25、反而很简单了:sqlite3RunParser(pParse, zSql, 此处 zSql 是一个完整的 SQL 语句串。调用返回后还要做一系列处理,略。4- sqlite3RunParser()在 tokenize.c 中。功能:在给定的 SQL 字符串上执行分析器。传入一个 parser 结构。返回一个 SQLITE_状态码。如果有错误发生,将错误信息写入*pzErrMsg。本函数内部是一个循环语句,每次循环处理一个词,根据词的类型做出不同的处理。如果是正经的词(不是空格什么的) ,都会调用 sqlite3Parser()函数对其进行分析。5- sqlite3Parser()在 parse

26、.c 中。本函数为分析器主程序。parse.c 中的程序好象都是自动生成的,我反正是看不懂,也就不想看了。摘一段与兄弟们共享:if( yypParser-yyidx 0 | yymajor=0 )yy_destructor(yypParser,(YYCODETYPE)yymajor,yy_parse_failed(yypParser);yymajor = YYNOCODE;else if( yymx!=YYERRORSYMBOL )YYMINORTYPE u2;u2.YYERRSYMDT = 0;yy_shift(yypParser,yyact,YYERRORSYMBOL,1.2.3. 查询的

27、执行过程前一小节介绍的编译调用层次看起来还是很清晰的,但实际执行时情况要复杂得多。比如 Oracle 一类的数据库,以服务器的形式供客户端访问。在服务器启动的过程中可以完成所有必要的初始化工作,在解析 SQL 语句时逻辑可能反而简单一些。 (Oracle 好像也有一些东西在第一次调用时才加载,比如 Java 虚拟机什么的) 。SQLite 这样的数据库主要是提供 API 供应用程序调用,这就要求在一次单独的调用中要完成所有相关工作。另外,SQLite 好像更倾向于将工作留到不得不做时再做(即使不一定非得这样) ,所以在 SQLite 中经常会看到“如果还没创建,则创建”或“如果还没打开,则打开”一类的代码。这样,程序的旁枝就会比较多,有时读起来会有一定困难。比如,在SQLite 启动后第 1 次执行 select 语句时,在编译该语句的过程中需要完成 schema 信息内存初始化的全部工作。

Copyright © 2018-2021 Wenke99.com All rights reserved

工信部备案号浙ICP备20026746号-2  

公安局备案号:浙公网安备33038302330469号

本站为C2C交文档易平台,即用户上传的文档直接卖给下载用户,本站只是网络服务中间平台,所有原创文档下载所得归上传人所有,若您发现上传作品侵犯了您的权利,请立刻联系网站客服并提供证据,平台将在3个工作日内予以改正。