代码改变世界

SQLite虚拟机工作原理(二)

2009-03-04 17:18  竹 石  阅读(946)  评论(0编辑  收藏  举报

接着上一节的插入原理,现在来讲一下查询的执行原理: 

二、简单的查询
到现在为止,已经知道VDBE是如何将数据写入数据库中的了,现在下来看看查询是如何工作的,下面是我们用到的例子:SELECT * FROM examp;
下面就是对执行这条SQL语句产生的指令:

在看这个问题之前我们还是先看一下SQLite的查询是如何工作的,这样我们才知道我们需要完成什么工作,对于查询结果的每一行,SQLite都会调用一个Callback函数:
int Callback(void *pUserData, int nColumn, char *azData[], char *azColumnName[]);
SQLite库会给VDBE提供一个指向回调函数的指针和一个pUserData指针(不管是回调函数指针还是pUserData指针,它都是由API函数sqlite_exec()传进来的)VDBE的工作就是为nColumn、azData[]、azColumnName[]取值,nColumn就是查询结果的列数,azColumnName[]数组中的每个字符串就是查询结果的每一列名,azData[]放的是实际的数据。


0 ColumnName 0 0 one
1 ColumnName 1 0 two
VDBE的查询程序的前两条指令是为azColumn设置值的,ColumnName指令是告诉VDBE应该给azColumnName数组设置什么值的,每次查询都是以ColumnName指令开始的,对查询结果的每个列都会有这样的指令。并且在后来的查询中对于每一列都会有相应的列指令。

2 Integer 0 0
3 OpenRead 0 3 examp
4 VerifyCookie 0 81
指令2和3打开一个要访问的数据库表的游标,这人工作和在插入数据时候用到的OpenWrite指令是差不多的,除了这回打开是用来读的而那个是写的,指令4象在插入的例子中一样是用来验证数据库模式的

5 Rewind 0 10
指令Rewind是初始化一个对要查询表的循环的迭代器,它把游标定位到表的第一个数据上,这是Column指令和下一条指令所需要的,下一条指令将会用这个游标迭代整个表的,如果这个表是空的则跳转到P2(10),如果表不是空的,则就跳到下一条指令,从现在开始就到了循环体部分了。

6 Column 0 0
7 Column 0 1
8 Callback 2 0
上面的指令是整个循环体,它对表中的每一条记录都是只执行一次,上面6、7指令都是将P2操作数对应的各自的列的数据放到栈中,在这个例子中,第6条指令是将one列中的数据放到栈中,而第7条指令是将two列中的数据放到栈中,第8条指令是引发对回调函数的执行,这时它的P1操作数将会变成查询结果的列数,这条指令会将P1条记录出栈并放到azData[]数组中。

9 Next 0 6
这条指令是在执行一个循环的分叉部分,从第5条指令到现在是构成了整个循环的逻辑结构,这是个应该值得注意的关键概念,这个指令是将游标前进指向下一条记录,如果前进成功,那么立即跳转到这个循环开始,如果这个前进不成功,则继续执行下面的结束循环的指令,而不跳回。

10 Close 0 0
11 Halt 0 0
Close指令是在程序的结尾将指向要查询表的游标关闭,其实没必要在这里执行这条指令,因为在程序结束后,VDBE都会把所有的游标自动的关闭,Halt指令是用来关闭VDBE程序的。
注意到查询记录时没有用到Transaction和Commit指令,而在插入的时候用到了,因为SELECT是个读操作,而不会改变数据库,所以它不需要事务。