sqlite-1.0.0原理概述
sqlite是一款嵌入式的轻量级的数据库,首个版本诞生于2000年,该数据库遵守ACID的关系数据库管理系统,SQLite不是一个cs架构的数据库引擎,而是被集成在用户程序中,SQLite实现了大部分的SQL标准。本文主要概述sqlite-1.0.0版本的启动的简单流程。
sqlite-1.0.0的源码
sqlite源码目录
主要包括了源码目录src,vdbe.c中主要包括了sqlite虚拟机的执行过程,where.c解析where语法的过程,shell.c主要是终端交互的过程中来获取输入并解析执行sql语句,insert.c主要就是插入数据的解析;在tool中的lemon的分析工具等。
sqlite的基本解析过程
一条sql的执行的主要过程如下:
在sqlite-1.0.0的实现过程中,使用了gdbm来做底层的数据存储与管理,sqlite的整个的实现流程,先是输入的语法词法解析,然后通过生成的语法生成相应的sql的虚拟机的执行指令,最后再执行该虚拟机指令,最终虚拟机指令通过gdbm来操作数据。
sqlite-1.0.0的源码的执行过程
通过在终端中输入sqlite执行,执行的入口函数为shell.c中的main函数;
int main(int argc, char **argv){
sqlite *db;
char *zErrMsg = 0;
char *argv0 = argv[0];
struct callback_data data;
memset(&data, 0, sizeof(data));
data.mode = MODE_List;
strcpy(data.separator,"|");
data.showHeader = 0;
while( argc>=2 && argv[1][0]=='-' ){ // 处理传入参数的处理 是否是html 是否为header
if( strcmp(argv[1],"-html")==0 ){
data.mode = MODE_Html;
argc--;
argv++;
}else if( strcmp(argv[1],"-list")==0 ){
data.mode = MODE_List;
argc--;
argv++;
}else if( strcmp(argv[1],"-line")==0 ){
data.mode = MODE_Line;
argc--;
argv++;
}else if( argc>=3 && strcmp(argv[0],"-separator")==0 ){
sprintf(data.separator,"%.*s",(int)sizeof(data.separator)-1,argv[2]);
argc -= 2;
argv += 2;
}else if( strcmp(argv[1],"-header")==0 ){
data.showHeader = 1;
argc--;
argv++;
}else if( strcmp(argv[1],"-noheader")==0 ){
data.showHeader = 0;
argc--;
argv++;
}else{
fprintf(stderr,"%s: unknown option: %s\n", argv0, argv[1]);
return 1;
}
}
if( argc!=2 && argc!=3 ){
fprintf(stderr,"Usage: %s ?OPTIONS? FILENAME ?SQL?\n", argv0);
exit(1);
}
data.db = db = sqlite_open(argv[1], 0666, &zErrMsg); // 打开文件
if( db==0 ){
data.db = db = sqlite_open(argv[1], 0444, &zErrMsg); // 如果打不开就换个模式打开
if( db==0 ){
if( zErrMsg ){ // 出错信息
fprintf(stderr,"Unable to open database \"%s\": %s\n", argv[1],zErrMsg);
}else{
fprintf(stderr,"Unable to open database %s\n", argv[1]);
}
exit(1);
}else{
printf("Database \"%s\" opened READ ONLY!\n", argv[1]);
}
}
data.out = stdout; // 输出信息
if( argc==3 ){ // 三个输入参数 第三个参数为sql语句
if( sqlite_exec(db, argv[2], callback, &data, &zErrMsg)!=0 && zErrMsg!=0 ){
fprintf(stderr,"SQL error: %s\n", zErrMsg);
exit(1);
}
}else{ // 打开sql的交互界面
char *zLine;
char *zSql = 0;
int nSql = 0;
int istty = isatty(0);
if( istty ){
printf(
"Enter \".help\" for instructions\n"
);
}
while( (zLine = one_input_line(zSql, istty))!=0 ){ // 读取终端命令行的输入数据
if( zLine && zLine[0]=='.' ){
do_meta_command(zLine, db, &data); // 展示元数据信息
free(zLine);
continue;
}
if( zSql==0 ){
nSql = strlen(zLine);
zSql = malloc( nSql+1 );
strcpy(zSql, zLine);
}else{
int len = strlen(zLine); // 获取输入的长度
zSql = realloc( zSql, nSql + len + 2 ); // 重新增加内存
if( zSql==0 ){
fprintf(stderr,"%s: out of memory!\n", argv0); // 如果申请内存失败则报错
exit(1);
}
strcpy(&zSql[nSql++], "\n"); // 给字符串数组加\n
strcpy(&zSql[nSql], zLine); // 拷贝获取的字符串数据
nSql += len;
}
free(zLine);
if( sqlite_complete(zSql) ){ // 检查是否是完成的字符串数据
data.cnt = 0;
if( sqlite_exec(db, zSql, callback, &data, &zErrMsg)!=0 // 执行该sql语句并设置回调显示函数callback
&& zErrMsg!=0 ){
printf("SQL error: %s\n", zErrMsg); // 如果出错则报错
free(zErrMsg);
zErrMsg = 0;
}
free(zSql); // 释放内存
zSql = 0;
nSql = 0;
}
} // 等待下一条数据输入
}
sqlite_close(db); // 关闭该数据库
return 0;
}
该main函数主要就是处理输入的命令行参数,并循环处理传入的终端的命令行的sql语句。接着我们就继续分析sqlit_exec函数的执行过程;
/*
** Execute SQL code. Return one of the SQLITE_ success/failure
** codes. Also write an error message into memory obtained from
** malloc() and make *pzErrMsg point to that message.
**
** If the SQL is a query, then for each row in the query result
** the xCallback() function is called. pArg becomes the first
** argument to xCallback(). If xCallback=NULL then no callback
** is invoked, even for queries.
*/
int sqlite_exec(
sqlite *db, /* The database on which the SQL executes */
char *zSql, /* The SQL to be executed */
sqlite_callback xCallback, /* Invoke this callback routine */
void *pArg, /* First argument to xCallback() */
char **pzErrMsg /* Write error messages here */
){
Parse sParse; // 解析的保存的数据结构
int rc;
if( pzErrMsg ) *pzErrMsg = 0;
if( (db->flags & SQLITE_Initialized)==0 ){
int rc = sqliteInit(db, pzErrMsg); // 检查是否数据库初始化完成如果未初始化完成则报错返回
if( rc!=SQLITE_OK ) return rc;
}
memset(&sParse, 0, sizeof(sParse)); // 变量的内容指向的地址设置为空
sParse.db = db; // 设置数据库
sParse.xCallback = xCallback; // 设置回调函数
sParse.pArg = pArg; // 设置传入参数
rc = sqliteRunParser(&sParse, zSql, pzErrMsg); // 开始解析并执行
sqliteStrRealloc(pzErrMsg);
return rc;
}
主要就是进行了检查并设置了相关的参数进行配置,接着就执行了sqliteRunParser函数来执行;
int sqliteRunParser(Parse *pParse, char *zSql, char **pzErrMsg){
int nErr = 0;
int i;
void *pEngine;
int once = 1;
static FILE *trace = 0;
extern void *sqliteParserAlloc(void*(*)(int)); // 该函数就是通过lemon来生成的函数
extern void sqliteParserFree(void*, void(*)(void*));
extern int sqliteParser(void*, int, ...); // 调用lemon定义的语法解析的函数,传入token来解析
extern void sqliteParserTrace(FILE*, char *);
i = 0;
sqliteParseInfoReset(pParse);
pEngine = sqliteParserAlloc((void*(*)(int))malloc);
if( pEngine==0 ){
sqliteSetString(pzErrMsg, "out of memory", 0);
return 1;
}
sqliteParserTrace(trace, "parser: "); // 设置追踪
while( nErr==0 && i>=0 && zSql[i]!=0 ){ // 循环遍历数据
int tokenType;
pParse->sLastToken.z = &zSql[i];
pParse->sLastToken.n = sqliteGetToken(&zSql[i], &tokenType); // 解析传入的数据获取token
i += pParse->sLastToken.n;
if( once ){
pParse->sFirstToken = pParse->sLastToken;
once = 0;
}
switch( tokenType ){ // 判断解析获得的数据
case TK_SPACE:
break;
case TK_COMMENT: {
/* Various debugging modes can be turned on and off using
** special SQL comments. Check for the special comments
** here and take approriate action if found.
*/
#ifndef NDEBUG // 是否打日志
char *z = pParse->sLastToken.z;
if( sqliteStrNICmp(z,"--parser-trace-on--",19)==0 ){
trace = stderr;
sqliteParserTrace(trace, "parser: ");
}else if( sqliteStrNICmp(z,"--parser-trace-off--", 20)==0 ){
trace = 0;
sqliteParserTrace(trace, "parser: ");
}else if( sqliteStrNICmp(z,"--vdbe-trace-on--",17)==0 ){
pParse->db->flags |= SQLITE_VdbeTrace;
}else if( sqliteStrNICmp(z,"--vdbe-trace-off--", 18)==0 ){
pParse->db->flags &= ~SQLITE_VdbeTrace;
#ifdef MEMORY_DEBUG
}else if( sqliteStrNICmp(z,"--malloc-fail=",14)==0 ){
sqlite_iMallocFail = atoi(&z[14]);
}else if( sqliteStrNICmp(z,"--malloc-stats--", 16)==0 ){
if( pParse->xCallback ){
static char *azName[4] = {"malloc", "free", "to_fail", 0 };
char *azArg[4];
char zVal[3][30];
sprintf(zVal[0],"%d", sqlite_nMalloc);
sprintf(zVal[1],"%d", sqlite_nFree);
sprintf(zVal[2],"%d", sqlite_iMallocFail);
azArg[0] = zVal[0];
azArg[1] = zVal[1];
azArg[2] = zVal[2];
azArg[3] = 0;
pParse->xCallback(pParse->pArg, 3, azArg, azName); // 通过回调函数打印对应的日志
}
#endif
}
#endif
break;
}
case TK_ILLEGAL: // 解析的token不合法
sqliteSetNString(pzErrMsg, "unrecognized token: \"", -1,
pParse->sLastToken.z, pParse->sLastToken.n, "\"", 1, 0);
nErr++;
break;
default:
sqliteParser(pEngine, tokenType, pParse->sLastToken, pParse); // 传入sqliteParser函数保存该词法
if( pParse->zErrMsg && pParse->sErrToken.z ){ // 检查是否出现错误
sqliteSetNString(pzErrMsg, "near \"", -1,
pParse->sErrToken.z, pParse->sErrToken.n,
"\": ", -1,
pParse->zErrMsg, -1,
0);
nErr++;
sqliteFree(pParse->zErrMsg);
pParse->zErrMsg = 0;
}
break;
}
}
if( nErr==0 ){ // 如果没有错误
sqliteParser(pEngine, 0, pParse->sLastToken, pParse); // 开始执行词法与语法解析
if( pParse->zErrMsg && pParse->sErrToken.z ){
sqliteSetNString(pzErrMsg, "near \"", -1,
pParse->sErrToken.z, pParse->sErrToken.n,
"\": ", -1,
pParse->zErrMsg, -1,
0);
nErr++;
sqliteFree(pParse->zErrMsg);
pParse->zErrMsg = 0;
}
}
sqliteParserFree(pEngine, free); // 执行完成后释放内存
if( pParse->zErrMsg ){
if( pzErrMsg ){
sqliteFree(*pzErrMsg);
*pzErrMsg = pParse->zErrMsg;
}else{
sqliteFree(pParse->zErrMsg);
}
if( !nErr ) nErr++;
}
if( pParse->pVdbe ){ // 释放虚拟机
sqliteVdbeDelete(pParse->pVdbe);
pParse->pVdbe = 0;
}
if( pParse->pNewTable ){ // 删除对应的table
sqliteDeleteTable(pParse->db, pParse->pNewTable);
pParse->pNewTable = 0;
}
sqliteParseInfoReset(pParse); // 重置pParse并继续解析执行
return nErr;
}
此时,由于使用的lemon的解析工具,对应的词法分析过程需要自行解析,所以,查看sqliteGetToken函数;
int sqliteGetToken(const char *z, int *tokenType){
int i;
switch( *z ){
case ' ': case '\t': case '\n': case '\f': case '\r': {
for(i=1; z[i] && isspace(z[i]); i++){}
*tokenType = TK_SPACE;
return i;
}
case '-': {
if( z[1]==0 ) return -1;
if( z[1]=='-' ){
for(i=2; z[i] && z[i]!='\n'; i++){}
*tokenType = TK_COMMENT;
return i;
}
*tokenType = TK_MINUS;
return 1;
}
case '(': {
*tokenType = TK_LP;
return 1;
}
case ')': {
*tokenType = TK_RP;
return 1;
}
case ';': {
*tokenType = TK_SEMI;
return 1;
}
case '+': {
*tokenType = TK_PLUS;
return 1;
}
case '*': {
*tokenType = TK_STAR;
return 1;
}
case '/': {
*tokenType = TK_SLASH;
return 1;
}
case '=': {
*tokenType = TK_EQ;
return 1 + (z[1]=='=');
}
case '<': {
if( z[1]=='=' ){
*tokenType = TK_LE;
return 2;
}else if( z[1]=='>' ){
*tokenType = TK_NE;
return 2;
}else{
*tokenType = TK_LT;
return 1;
}
}
case '>': {
if( z[1]=='=' ){
*tokenType = TK_GE;
return 2;
}else{
*tokenType = TK_GT;
return 1;
}
}
case '!': {
if( z[1]!='=' ){
*tokenType = TK_ILLEGAL;
return 2;
}else{
*tokenType = TK_NE;
return 2;
}
}
case '|': {
if( z[1]!='|' ){
*tokenType = TK_ILLEGAL;
return 1;
}else{
*tokenType = TK_CONCAT;
return 2;
}
}
case ',': {
*tokenType = TK_COMMA;
return 1;
}
case '\'': case '"': {
int delim = z[0];
for(i=1; z[i]; i++){
if( z[i]==delim ){
if( z[i+1]==delim ){
i++;
}else{
break;
}
}
}
if( z[i] ) i++;
*tokenType = TK_STRING;
return i;
}
case '.': {
if( !isdigit(z[1]) ){
*tokenType = TK_DOT;
return 1;
}
/* Fall thru into the next case */
}
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9': {
*tokenType = TK_INTEGER;
for(i=1; z[i] && isdigit(z[i]); i++){}
if( z[i]=='.' ){
i++;
while( z[i] && isdigit(z[i]) ){ i++; }
*tokenType = TK_FLOAT;
}
if( (z[i]=='e' || z[i]=='E') &&
( isdigit(z[i+1])
|| ((z[i+1]=='+' || z[i+1]=='-') && isdigit(z[i+2]))
)
){
i += 2;
while( z[i] && isdigit(z[i]) ){ i++; }
*tokenType = TK_FLOAT;
}else if( z[0]=='.' ){
*tokenType = TK_FLOAT;
}
return i;
}
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
case 's': case 't': case 'u': case 'v': case 'w': case 'x':
case 'y': case 'z': case '_':
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
case 'Y': case 'Z': {
for(i=1; z[i] && (isalnum(z[i]) || z[i]=='_'); i++){}
*tokenType = sqliteKeywordCode(z, i);
return i;
}
default: {
break;
}
}
*tokenType = TK_ILLEGAL;
return 1;
}
该函数就是解析传入的内容,讲其转为对应的Token值返回,再传入词法之后,通过sqliteParser(pEngine, 0, pParse->sLastToken, pParse)开始执行对应的词法与语法过程,此时我们查看对应的parse.y文件;
%token_prefix TK_
%token_type {Token} // token类型定义
%extra_argument {Parse *pParse} // 添加额外的传入值
%syntax_error {
sqliteSetString(&pParse->zErrMsg,"syntax error",0);
pParse->sErrToken = TOKEN;
}
%name sqliteParser // 重命名解析函数
%include {
#include "sqliteInt.h"
#include "parse.h"
}
// Input is zero or more commands.
input ::= cmdlist. // 输入多条cmd或者一条cmd
// These are extra tokens used by the lexer but never seen by the
// parser. We put them in a rule so that the parser generator will
// add them to the parse.h output file.
//
input ::= END_OF_FILE ILLEGAL SPACE UNCLOSED_STRING COMMENT FUNCTION
UMINUS COLUMN AGG_FUNCTION.
// A list of commands is zero or more commands
//
cmdlist ::= ecmd.
cmdlist ::= cmdlist SEMI ecmd.
ecmd ::= explain cmd. {sqliteExec(pParse);} // 最终会执行sqliteExec(pParse)函数
ecmd ::= cmd. {sqliteExec(pParse);}
ecmd ::= .
explain ::= EXPLAIN. {pParse->explain = 1;} // 是否是分析该命令行
// The first form of a command is a CREATE TABLE statement.
//
cmd ::= create_table create_table_args. // 创建表
create_table ::= CREATE(X) TABLE id(Y). {sqliteStartTable(pParse,&X,&Y);} // 调用开始table
create_table_args ::= LP columnlist conslist_opt RP(X).
{sqliteEndTable(pParse,&X);}
columnlist ::= columnlist COMMA column.
columnlist ::= column.
// About the only information used for a column is the name of the
// column. The type is always just "text". But the code will accept
// an elaborate typename. Perhaps someday we'll do something with it.
//
column ::= columnid type carglist.
columnid ::= id(X). {sqliteAddColumn(pParse,&X);} // 添加列
%type id {Token}
id(A) ::= ID(X). {A = X;}
id(A) ::= STRING(X). {A = X;}
type ::= typename.
type ::= typename LP signed RP.
type ::= typename LP signed COMMA signed RP.
typename ::= id.
typename ::= typename id.
signed ::= INTEGER.
signed ::= PLUS INTEGER.
signed ::= MINUS INTEGER.
carglist ::= carglist carg.
carglist ::= .
carg ::= CONSTRAINT id ccons.
carg ::= ccons.
carg ::= DEFAULT STRING(X). {sqliteAddDefaultValue(pParse,&X,0);} // 设置默认值
carg ::= DEFAULT ID(X). {sqliteAddDefaultValue(pParse,&X,0);}
carg ::= DEFAULT INTEGER(X). {sqliteAddDefaultValue(pParse,&X,0);}
carg ::= DEFAULT PLUS INTEGER(X). {sqliteAddDefaultValue(pParse,&X,0);}
carg ::= DEFAULT MINUS INTEGER(X). {sqliteAddDefaultValue(pParse,&X,1);}
carg ::= DEFAULT FLOAT(X). {sqliteAddDefaultValue(pParse,&X,0);}
carg ::= DEFAULT PLUS FLOAT(X). {sqliteAddDefaultValue(pParse,&X,0);}
carg ::= DEFAULT MINUS FLOAT(X). {sqliteAddDefaultValue(pParse,&X,1);}
carg ::= DEFAULT NULL.
// In addition to the type name, we also care about the primary key.
//
ccons ::= NOT NULL.
ccons ::= PRIMARY KEY sortorder. {sqliteCreateIndex(pParse,0,0,0,0,0);} // 创建索引
ccons ::= UNIQUE.
ccons ::= CHECK LP expr RP.
以上代码只是部分的parser.y文件,大家可以了解一下lemon的使用方法,此时先最终都会执行到sqliteExec函数;此时我们可以先分析一下sqliteInsert插入函数的执行的过程可知,
void sqliteInsert(
Parse *pParse, /* Parser context */
Token *pTableName, /* Name of table into which we are inserting */
ExprList *pList, /* List of values to be inserted */
Select *pSelect, /* A SELECT statement to use as the data source */
IdList *pColumn /* Column names corresponding to IDLIST. */
){
Table *pTab; /* The table to insert into */
char *zTab; /* Name of the table into which we are inserting */
int i, j, idx; /* Loop counters */
Vdbe *v; /* Generate code into this virtual machine */
Index *pIdx; /* For looping over indices of the table */
int srcTab; /* Date comes from this temporary cursor if >=0 */
int nColumn; /* Number of columns in the data */
int base; /* First available cursor */
int iCont, iBreak; /* Beginning and end of the loop over srcTab */
/* Locate the table into which we will be inserting new information.
*/
zTab = sqliteTableNameFromToken(pTableName);
pTab = sqliteFindTable(pParse->db, zTab);
sqliteFree(zTab);
if( pTab==0 ){
sqliteSetNString(&pParse->zErrMsg, "no such table: ", 0,
pTableName->z, pTableName->n, 0);
pParse->nErr++;
goto insert_cleanup;
}
if( pTab->readOnly ){
sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName,
" may not be modified", 0);
pParse->nErr++;
goto insert_cleanup;
}
/* Allocate a VDBE
*/
v = sqliteGetVdbe(pParse);
if( v==0 ) goto insert_cleanup;
/* Figure out how many columns of data are supplied. If the data
** is comming from a SELECT statement, then this step has to generate
** all the code to implement the SELECT statement and leave the data
** in a temporary table. If data is coming from an expression list,
** then we just have to count the number of expressions.
*/
if( pSelect ){
int rc;
srcTab = pParse->nTab++;
sqliteVdbeAddOp(v, OP_Open, srcTab, 1, 0, 0);
rc = sqliteSelect(pParse, pSelect, SRT_Table, srcTab);
if( rc ) goto insert_cleanup;
assert( pSelect->pEList );
nColumn = pSelect->pEList->nExpr;
}else{
srcTab = -1;
assert( pList );
nColumn = pList->nExpr;
}
/* Make sure the number of columns in the source data matches the number
** of columns to be inserted into the table.
*/
if( pColumn==0 && nColumn!=pTab->nCol ){
char zNum1[30];
char zNum2[30];
sprintf(zNum1,"%d", nColumn);
sprintf(zNum2,"%d", pTab->nCol);
sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName,
" has ", zNum2, " columns but ",
zNum1, " values were supplied", 0);
pParse->nErr++;
goto insert_cleanup;
}
if( pColumn!=0 && nColumn!=pColumn->nId ){
char zNum1[30];
char zNum2[30];
sprintf(zNum1,"%d", nColumn);
sprintf(zNum2,"%d", pColumn->nId);
sqliteSetString(&pParse->zErrMsg, zNum1, " values for ",
zNum2, " columns", 0);
pParse->nErr++;
goto insert_cleanup;
}
/* If the INSERT statement included an IDLIST term, then make sure
** all elements of the IDLIST really are columns of the table and
** remember the column indices.
*/
if( pColumn ){
for(i=0; i<pColumn->nId; i++){
pColumn->a[i].idx = -1;
}
for(i=0; i<pColumn->nId; i++){
for(j=0; j<pTab->nCol; j++){
if( sqliteStrICmp(pColumn->a[i].zName, pTab->aCol[j].zName)==0 ){
pColumn->a[i].idx = j;
break;
}
}
if( j>=pTab->nCol ){
sqliteSetString(&pParse->zErrMsg, "table ", pTab->zName,
" has no column named ", pColumn->a[i].zName, 0);
pParse->nErr++;
goto insert_cleanup;
}
}
}
/* Open cursors into the table that is received the new data and
** all indices of that table.
*/
base = pParse->nTab;
sqliteVdbeAddOp(v, OP_Open, base, 1, pTab->zName, 0);
for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
sqliteVdbeAddOp(v, OP_Open, idx+base, 1, pIdx->zName, 0);
}
/* If the data source is a SELECT statement, then we have to create
** a loop because there might be multiple rows of data. If the data
** source is an expression list, then exactly one row will be inserted
** and the loop is not used.
*/
if( srcTab>=0 ){
sqliteVdbeAddOp(v, OP_Rewind, srcTab, 0, 0, 0);
iBreak = sqliteVdbeMakeLabel(v);
iCont = sqliteVdbeAddOp(v, OP_Next, srcTab, iBreak, 0, 0);
}
/* Create a new entry in the table and fill it with data.
*/
sqliteVdbeAddOp(v, OP_New, 0, 0, 0, 0);
if( pTab->pIndex ){
sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
}
for(i=0; i<pTab->nCol; i++){
if( pColumn==0 ){
j = i;
}else{
for(j=0; j<pColumn->nId; j++){
if( pColumn->a[j].idx==i ) break;
}
}
if( pColumn && j>=pColumn->nId ){
char *zDflt = pTab->aCol[i].zDflt;
if( zDflt==0 ){
sqliteVdbeAddOp(v, OP_Null, 0, 0, 0, 0);
}else{
sqliteVdbeAddOp(v, OP_String, 0, 0, zDflt, 0);
}
}else if( srcTab>=0 ){
sqliteVdbeAddOp(v, OP_Field, srcTab, i, 0, 0);
}else{
sqliteExprCode(pParse, pList->a[j].pExpr);
}
}
sqliteVdbeAddOp(v, OP_MakeRecord, pTab->nCol, 0, 0, 0);
sqliteVdbeAddOp(v, OP_Put, base, 0, 0, 0);
/* Create appropriate entries for the new data row in all indices
** of the table.
*/
for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
if( pIdx->pNext ){
sqliteVdbeAddOp(v, OP_Dup, 0, 0, 0, 0);
}
for(i=0; i<pIdx->nColumn; i++){
int idx = pIdx->aiColumn[i];
if( pColumn==0 ){
j = idx;
}else{
for(j=0; j<pColumn->nId; j++){
if( pColumn->a[j].idx==idx ) break;
}
}
if( pColumn && j>=pColumn->nId ){
char *zDflt = pTab->aCol[idx].zDflt;
if( zDflt==0 ){
sqliteVdbeAddOp(v, OP_Null, 0, 0, 0, 0);
}else{
sqliteVdbeAddOp(v, OP_String, 0, 0, zDflt, 0);
}
}else if( srcTab>=0 ){
sqliteVdbeAddOp(v, OP_Field, srcTab, idx, 0, 0);
}else{
sqliteExprCode(pParse, pList->a[j].pExpr);
}
}
sqliteVdbeAddOp(v, OP_MakeKey, pIdx->nColumn, 0, 0, 0);
sqliteVdbeAddOp(v, OP_PutIdx, idx+base, 0, 0, 0);
}
/* The bottom of the loop, if the data source is a SELECT statement
*/
if( srcTab>=0 ){
sqliteVdbeAddOp(v, OP_Goto, 0, iCont, 0, 0);
sqliteVdbeAddOp(v, OP_Noop, 0, 0, 0, iBreak);
}
insert_cleanup:
if( pList ) sqliteExprListDelete(pList);
if( pSelect ) sqliteSelectDelete(pSelect);
sqliteIdListDelete(pColumn);
}
该函数首先获取一个虚拟机sqliteGetVdbe,然后讲获取的虚拟机通过sqliteVdbeAddOp函数讲需要执行的操作全部压入虚拟机栈中,
int sqliteVdbeAddOp(Vdbe *p, int op, int p1, int p2, const char *p3, int lbl){
int i, j;
i = p->nOp;
p->nOp++;
if( i>=p->nOpAlloc ){
int oldSize = p->nOpAlloc;
p->nOpAlloc = p->nOpAlloc*2 + 10;
p->aOp = sqliteRealloc(p->aOp, p->nOpAlloc*sizeof(Op)); // 重新扩展内存
if( p->aOp==0 ){
p->nOp = 0;
p->nOpAlloc = 0;
return 0; // 如果扩展失败就返回
}
memset(&p->aOp[oldSize], 0, (p->nOpAlloc-oldSize)*sizeof(Op)); // 讲申请的内存置空
}
p->aOp[i].opcode = op; // 保存对应的opcode
p->aOp[i].p1 = p1;
if( p2<0 && (-1-p2)<p->nLabel && p->aLabel[-1-p2]>=0 ){
p2 = p->aLabel[-1-p2];
}
p->aOp[i].p2 = p2;
if( p3 && p3[0] ){
p->aOp[i].p3 = sqliteStrDup(p3);
}else{
p->aOp[i].p3 = 0;
}
if( lbl<0 && (-lbl)<=p->nLabel ){
p->aLabel[-1-lbl] = i;
for(j=0; j<i; j++){
if( p->aOp[j].p2==lbl ) p->aOp[j].p2 = i;
}
}
return i; // 返回
}
当该插入过程执行完成之后,此时就会执行sqliteExec函数;
void sqliteExec(Parse *pParse){
if( pParse->pVdbe ){
if( pParse->explain ){ // 判断是否是需要explain
sqliteVdbeList(pParse->pVdbe, pParse->xCallback, pParse->pArg,
&pParse->zErrMsg); // 列表执行
}else{
FILE *trace = (pParse->db->flags & SQLITE_VdbeTrace)!=0 ? stderr : 0;
sqliteVdbeTrace(pParse->pVdbe, trace); // 添加trace
sqliteVdbeExec(pParse->pVdbe, pParse->xCallback, pParse->pArg,
&pParse->zErrMsg, pParse->db->pBusyArg,
pParse->db->xBusyCallback); // 执行添加的虚拟机字节码
}
sqliteVdbeDelete(pParse->pVdbe); // 删除该虚拟机内容
pParse->pVdbe = 0;
pParse->colNamesSet = 0;
}
}
此时正常情况下调用sqliteVdbeExec函数;
/*
** Execute the program in the VDBE.
**
** If an error occurs, an error message is written to memory obtained
** from sqliteMalloc() and *pzErrMsg is made to point to that memory.
** The return parameter is the number of errors.
**
** If the callback every returns non-zero, then the program exits
** immediately. No error message but the function does return SQLITE_ABORT.
**
** A memory allocation error causes this routine to return SQLITE_NOMEM
** and abandon furture processing.
**
** Other fatal errors return SQLITE_ERROR.
**
** If a database file could not be opened because it is locked by
** another database instance, then the xBusy() callback is invoked
** with pBusyArg as its first argument, the name of the table as the
** second argument, and the number of times the open has been attempted
** as the third argument. The xBusy() callback will typically wait
** for the database file to be openable, then return. If xBusy()
** returns non-zero, another attempt is made to open the file. If
** xBusy() returns zero, or if xBusy is NULL, then execution halts
** and this routine returns SQLITE_BUSY.
*/
int sqliteVdbeExec(
Vdbe *p, /* The VDBE */
sqlite_callback xCallback, /* The callback */
void *pArg, /* 1st argument to callback */
char **pzErrMsg, /* Error msg written here */
void *pBusyArg, /* 1st argument to the busy callback */
int (*xBusy)(void*,const char*,int) /* Called when a file is busy */
){
int pc; /* The program counter */
Op *pOp; /* Current operation */
int rc; /* Value to return */
char zBuf[100]; /* Space to sprintf() and integer */
p->tos = -1;
rc = SQLITE_OK;
#ifdef MEMORY_DEBUG
if( access("vdbe_trace",0)==0 ){
p->trace = stderr;
}
#endif
/* if( pzErrMsg ){ *pzErrMsg = 0; } */
for(pc=0; rc==SQLITE_OK && pc<p->nOp && pc>=0; pc++){
pOp = &p->aOp[pc];
/* Only allow tracing if NDEBUG is not defined.
*/
#ifndef NDEBUG
if( p->trace ){
fprintf(p->trace,"%4d %-12s %4d %4d %s\n",
pc, zOpName[pOp->opcode], pOp->p1, pOp->p2,
pOp->p3 ? pOp->p3 : "");
}
#endif
switch( pOp->opcode ){
/* Opcode: Goto P2 * *
**
** An unconditional jump to address P2.
** The next instruction executed will be
** the one at index P2 from the beginning of
** the program.
*/
case OP_Goto: {
pc = pOp->p2 - 1;
break;
}
/* Opcode: Halt * * *
**
** Exit immediately. All open DBs, Lists, Sorts, etc are closed
** automatically.
*/
case OP_Halt: {
pc = p->nOp-1;
break;
}
/* Opcode: Integer P1 * *
**
** The integer value P1 is pushed onto the stack.
*/
case OP_Integer: {
int i = ++p->tos;
if( NeedStack(p, p->tos) ) goto no_mem;
p->aStack[i].i = pOp->p1;
p->aStack[i].flags = STK_Int;
break;
}
/* Opcode: String * * P3
**
** The string value P3 is pushed onto the stack.
*/
case OP_String: {
int i = ++p->tos;
char *z;
if( NeedStack(p, p->tos) ) goto no_mem;
z = pOp->p3;
if( z==0 ) z = "";
p->zStack[i] = z;
p->aStack[i].n = strlen(z) + 1;
p->aStack[i].flags = STK_Str;
break;
}
...
}
从该函数的执行流程可知,该函数就是遍历对应的vdb的栈的列表,根据pOp的操作码来进行相关的操作,例如在OP_PutIdx字节码的操作中通过sqliteDbbePut来调用gdbm_store来操作数据的存储。此时基本的执行流程就概述完成。
总结
本文只是很粗略的描述了sqlite-1.0.0的基本的一个执行流程,其中忽视了大量的细节内容,大家有兴趣可自行查看,该sqlite中由于用到了lemon工具,大家需要自行查阅该文档,再结合代码来分析,概述的执行流程图如下:
由于本人才疏学浅,如有错误请批评指正。