一:sql准备过程
在前面的分析中我们知道,sqlite3_open()为我们打开了数据库并准备了所要的内存空间,锁,vfs等。
接下来就分析sql是如何被解析器一步一步解析的。
上图是准备sql语句的过程分析图。
1.1:sqlite3_prepare_v2函数:
该函数是准备的入口函数。
传入了5个参数:
sqlite3 *db : sqlite3_open()返回的数据库句柄
const char *zSql: 要准备的sql语句
int nBytes : sql语句的 长度(strlen(sql))
sqlite3_stmt **ppStmt: 准备的语句存储该结构体对象中,可以认为是预编译语句的句柄,执行时需要传入这个句柄
const char **pzTail: 指向被解析的字符串的末尾
SQLite 现在提供两个版本的编译 API 函数:遗留的和现在使用的。
在遗留版本中,原始 SQL 文本没有保存在编译后的语句(sqlite3_stmt 结构)中,因此,如
果 schema 发生改变,sqlite3_step()会返回 SQLITE_SCHEMA。在新版本中,编译后的语句
中保存原始 SQL 文本,当遇到schema 改变时自动重新编译。sqlite3_prepare()函数中其实只包含一条对 sqlite3LockAndPrepare()的调用语句:
|
1.2:sqlite3LockAndPrepare函数
该函数并没有做太多事情主要是多了两个参数,判断一下db是否合法。
调用sqlite3Prepare函数。
|
1.3: sqlite3Prepare函数
这个函数做了以下几个事:
1:加入解析器Parse
2:所需内存的初始化以及是否禁用后备内存
3:判断链接的数据库是否加锁,如果加锁需要解锁
4:zSqlCopy= sqlite3DbStrNDup(db, zSql, nBytes)语句复制了zsql。
5:调用sqlite3RunParser(&sParse,zSql, &zErrMsg);
还有的后面补上
|
1.4:sqlite3RunParser函数
sqlite3RunParser位于token.c文件中,它是进行SQL语句分析的入口,它调用sqlite3GetToken对SQL语句zSql进行分词,然后调用sqlite3Parser进行语法分析。而sqlite3Parser在语法规则发生规约时调用相应的opcode生成子例程,生成opcode。
在给定的SQL字符串上运行解析器.解析器结构传入。
功能:在给定的 SQL 字符串上执行分析器。传入一个 parser 结构。返回一个 SQLITE_状态
码。如果有错误发生,将错误信息写入*pzErrMsg。
本函数内部是一个循环语句,每次循环处理一个词,根据词的类型做出不同的处理。如果是
正经的词(不是空格什么的),都会调用 sqlite3Parser()函数对其进行分析。
SQLITE_PRIVATEint sqlite3RunParser(Parse *pParse, constchar *zSql, char **pzErrMsg){
int nErr = 0; /* Number of errors encountered(遇到的错误数) */ void *pEngine; /* The LEMON-generated LALR(1) parser(lemon算法解析器) */ int n = 0; /* Length of the next token token(下一个token的长度) */ int tokenType; /* type of the next token (下一个token的类型)*/ int lastTokenParsed = -1; /* type of the previous token (上一个token的类型)*/ sqlite3 *db = pParse->db; /* The database connection */ int mxSqlLen; /* Max length of an SQL string(sql字符串的最大长度) */ #ifdefsqlite3Parser_ENGINEALWAYSONSTACK yyParser sEngine; /* Space to hold the Lemon-generated Parser object(保存lemon生成的解析器对象的空间 ) */ #endif
assert( zSql!=0 ); mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH];// mxSqlLen 1000000000 int if( db->nVdbeActive==0 ){//虚拟机活动数量为0 db->u1.isInterrupted = 0;//sqlite3_interrupt不被执行(我猜是中断执行) } pParse->rc = SQLITE_OK; pParse->zTail = zSql; assert( pzErrMsg!=0 ); /* sqlite3ParserTrace(stdout, "parser: "); */ #ifdefsqlite3Parser_ENGINEALWAYSONSTACK pEngine = &sEngine; sqlite3ParserInit(pEngine);//初始化已分配的解析器(初始化解析器对象各个字段) #else pEngine = sqlite3ParserAlloc(sqlite3Malloc); if( pEngine==0 ){ sqlite3OomFault(db); return SQLITE_NOMEM_BKPT; } #endif assert( pParse->pNewTable==0 ); assert( pParse->pNewTrigger==0 ); assert( pParse->nVar==0 ); assert( pParse->pVList==0 ); while( 1 ){ if( zSql[0]!=0 ){// zSql[0]表示一个token 开头(比如select就为s) u8*)zSql, &tokenType); mxSqlLen -= n; if( mxSqlLen<0 ){ pParse->rc = SQLITE_TOOBIG; break; } }else{
if( lastTokenParsed==TK_SEMI ){ tokenType = 0; }elseif( lastTokenParsed==0 ){ break; }else{ tokenType = TK_SEMI; } zSql -= n; } if( tokenType>=TK_SPACE ){ assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL ); if( db->u1.isInterrupted ){ pParse->rc = SQLITE_INTERRUPT; break; } if( tokenType==TK_ILLEGAL ){ sqlite3ErrorMsg(pParse, "unrecognized token: \"%.*s\"", n, zSql); break; } zSql += n; }else{ pParse->sLastToken.z = zSql;//0x0070808f "id,name from stu" const char * pParse->sLastToken.n = n; pParse->sLastToken, pParse); lastTokenParsed = tokenType; zSql += n; if( pParse->rc!=SQLITE_OK || db->mallocFailed ) break; } } assert( nErr==0 ); pParse->zTail = zSql; #ifdef YYTRACKMAXSTACKDEPTH sqlite3_mutex_enter(sqlite3MallocMutex()); sqlite3StatusHighwater(SQLITE_STATUS_PARSER_STACK, sqlite3ParserStackPeak(pEngine) ); sqlite3_mutex_leave(sqlite3MallocMutex()); #endif/* YYDEBUG */ #ifdefsqlite3Parser_ENGINEALWAYSONSTACK sqlite3ParserFinalize(pEngine); #else sqlite3ParserFree(pEngine, sqlite3_free); #endif if( db->mallocFailed ){ pParse->rc = SQLITE_NOMEM_BKPT; } if( pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE && pParse->zErrMsg==0 ){ pParse->zErrMsg = sqlite3MPrintf(db, "%s", sqlite3ErrStr(pParse->rc)); } assert( pzErrMsg!=0 ); if( pParse->zErrMsg ){ *pzErrMsg = pParse->zErrMsg; sqlite3_log(pParse->rc, "%s", *pzErrMsg); pParse->zErrMsg = 0; nErr++; } if( pParse->pVdbe && pParse->nErr>0 && pParse->nested==0 ){ sqlite3VdbeDelete(pParse->pVdbe); pParse->pVdbe = 0; } #ifndef SQLITE_OMIT_SHARED_CACHE if( pParse->nested==0 ){ sqlite3DbFree(db, pParse->aTableLock); pParse->aTableLock = 0; pParse->nTableLock = 0; } #endif #ifndef SQLITE_OMIT_VIRTUALTABLE sqlite3_free(pParse->apVtabLock); #endif
if( !IN_DECLARE_VTAB ){
sqlite3DeleteTable(db, pParse->pNewTable); }
if( pParse->pWithToFree ) sqlite3WithDelete(db, pParse->pWithToFree); sqlite3DeleteTrigger(db, pParse->pNewTrigger); sqlite3DbFree(db, pParse->pVList); while( pParse->pAinc ){ AutoincInfo *p = pParse->pAinc; pParse->pAinc = p->pNext; sqlite3DbFreeNN(db, p); } while( pParse->pZombieTab ){ Table *p = pParse->pZombieTab; pParse->pZombieTab = p->pNextZombie; sqlite3DeleteTable(db, p); } assert( nErr==0 || pParse->rc!=SQLITE_OK ); return nErr; }
|
这个函数里面比较重要的三个函数我已经加粗了。
下面我说一下解析时是如何循环的。
n = sqlite3GetToken((u8*)zSql, &tokenType);
这句代码就是获取sql语句第一个词的长度,比如有一个sql语句select id name from stu;
通过while循环获取第一个词select的长度为6。然后依次循环直到结束。
第一次循环zSql[0]的数为s,及sql预语句的第一个关键字的第一个字符。
然后获取n为6及select的长度,mxSqlLen-= n;这条语句是让最大长度减当前n的值。
后面就将n的大小和sql语句传个解析对象和Token
然后执行sqlite3Parser(pEngine,tokenType, pParse->sLastToken, pParse);函数进行解析