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 是否为headerif( 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");                            // 给字符串数组加\nstrcpy(&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);  // 解析传入的数据获取tokeni += 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}
#endifbreak;}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 ){                                          // 删除对应的tablesqliteDeleteTable(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 FUNCTIONUMINUS 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;                                              // 保存对应的opcodep->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);                                  // 添加tracesqliteVdbeExec(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_DEBUGif( 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 NDEBUGif( p->trace ){fprintf(p->trace,"%4d %-12s %4d %4d %s\n",pc, zOpName[pOp->opcode], pOp->p1, pOp->p2,pOp->p3 ? pOp->p3 : "");}
#endifswitch( 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工具,大家需要自行查阅该文档,再结合代码来分析,概述的执行流程图如下:

由于本人才疏学浅,如有错误请批评指正。

sqlite-1.0.0源码执行的基本流程概述相关推荐

  1. Flask1.1.4 Werkzeug1.0.1 源码分析:启动流程

    基于QuickStart中的一个demo来分析 from flask import Flaskapp = Flask(__name__)@app.route("/") def he ...

  2. 从源码解析-Android系统启动流程概述 init进程zygote进程SystemServer进程启动流程

    Android系统启动流程 启动流程 Loader Kernel Native Framework Application init进程 启动 rc文件规则 Actions Commands Serv ...

  3. Jmeter-3.0的源码导入eclipse并执行

    最近在研究jmeter二次开发,所以需要先jmeter导入Eclipse,后续还需要学习jmeter源码以及二次开发的相关知识,到时候也会分享出来,下面是jmeter导入Eclipse的过程 首先去官 ...

  4. 【转】Ubuntu 14.04.3上配置并成功编译Android 6.0 r1源码

    http://www.linuxidc.com/Linux/2016-01/127292.htm 终于成功把Android 6.0 r1源码的源码编译.先上图,这是在Ubuntu中运行的Android ...

  5. 从路由原理出发,深入阅读理解react-router 4.0的源码

      react-router等前端路由的原理大致相同,可以实现无刷新的条件下切换显示不同的页面.路由的本质就是页面的URL发生改变时,页面的显示结果可以根据URL的变化而变化,但是页面不会刷新.通过前 ...

  6. android 系统源码调试 局部变量值_如何方便快速的整编Android 9.0系统源码?

    点击上方"刘望舒",选择"星标" 多点在看,就是真爱! 作者 :  刘望舒  |  来源 :刘望舒的博客地址:http://liuwangshu.cn/fram ...

  7. Ubuntu16.04编译android6.0.1源码记录

    目录 目录 一.安装环境 二.下载源码 1.下载repo 2.初始化repo 3.同步源代码 关于驱动 三.编译源码 四.导入源码到AS 五.刷入真机 六.修改源码 总结: 3.同步源代码 关于驱动 ...

  8. [转组第10天] | Android6.0.0_r1源码编译和POC程序的编译

    2018-05-23 Android6.0.0_r1源码编译 简要说明:android源码编译的四个流程:1.源码下载;2.构建编译环境;3.编译源码;4运行.下文也将按照该流程讲述. 主机环境 Ub ...

  9. Ubuntu16.04编译Android 6.0系统源码过程简要记录总结

    一,安装VMware Workstation,百度网盘下载(内含注册机) 链接: https://pan.baidu.com/s/1wz4hdNQBikTvyUMNokSVYg 提取码: yed7 V ...

最新文章

  1. linux shell 字符串比较相等、不相等
  2. centos7 python3 sqlserver_Centos7.3下SQLServer安装配置方法图文教程
  3. 【GDAL】GDAL栅格数据结构学习笔记(一): 关于Metadata
  4. HihoCoder - 1175 拓扑排序·二
  5. Linux 释放cpugpu内存、显存和硬盘
  6. 欢迎光临CAX软件二次开发开源社区!
  7. HDU - 6992 Lawn of the Dead 线段树 + 思维
  8. 吴恩达机器学习作业4(python实现)
  9. 用collectionview实现瀑布流-转(后面附demo,供参考)
  10. mysql 5.5 驱动jar包_MySQL驱动jar包下载
  11. 计算机更新bios,升级bios,详细教您主板怎么更新bios
  12. mysql c盘满了 怎么办_C盘太满清理技巧
  13. webp格式图片转化为常见的png格式图片
  14. mysql 数据库切分_mysql数据库切分
  15. useRoutes() may be used only in the context of a <Router> component.
  16. 多核 CPU 和多个 CPU 有何区别?与线程的关系?
  17. 两种方式建立Vsftpd虚拟用户
  18. 常见锐捷光模块型号大全
  19. 解决在高分辨率下运行Photoshop CS6,程序界面字体过小的问题
  20. mysql-5.7.11-winx64_mysql 5.7.11 winx64安装配置教程

热门文章

  1. 与AMD合并后,赛灵思与英特尔、英伟达在数据中心市场呈“三足鼎立”之势
  2. Python 图像处理 | 图像平滑之均值滤波、方框滤波、高斯滤波及中值滤波
  3. 只需4秒,这个算法就能鉴别你的LV是真是假
  4. 通俗易懂!使用Excel和TF实现Transformer
  5. 2019如何学Python?这里有你需要的答案
  6. 公开课 | 详解CNN-pFSMN模型以及在语音识别中的应用
  7. 重磅!Facebook公布PyTorch 1.0预览版!
  8. 推荐一款 Nginx 可视化配置神器
  9. 一文带你领略并发编程的内功心法
  10. Redis持久化讲解