sqlite3原始碼解析之sql解析(一)
一: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()的呼叫語句:
SQLITE_APIint sqlite3_prepare_v2( sqlite3 *db, /* Database handle. 成功開啟的資料庫控制代碼*/ constchar *zSql, /* UTF-8 encoded SQL statement. UTF8編碼的 SQL 語句 */ intnBytes, /* Length of zSql in bytes. 引數 sql 的位元組數, 包含 '\0' */ sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement 輸出:預編譯語句控制代碼 */ constchar **pzTail/* OUT: End of parsed string 輸出:指向 sql 語句中未使用的部分*/ ){ int rc; rc = sqlite3LockAndPrepare(db,zSql,nBytes,SQLITE_PREPARE_SAVESQL,0, ppStmt,pzTail); assert( rc==SQLITE_OK || ppStmt==0 || *ppStmt==0 ); return rc; } |
1.2:sqlite3LockAndPrepare函式
該函式並沒有做太多事情主要是多了兩個引數,判斷一下db是否合法。
呼叫sqlite3Prepare函式。
staticint sqlite3LockAndPrepare( sqlite3 *db, /* Database handle. */ constchar *zSql, /* UTF-8 encoded SQL statement.(UTF-8 編碼的 SQL 語句) */ intnBytes, /* Length of zSql in bytes. (zSql 的位元組數)*/ u32prepFlags, /* Zero or more SQLITE_PREPARE_* flags(儲存的sql文字) */ Vdbe *pOld, /* VM being reprepared */ sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement(指向已準備好的語句的指標 ) */ constchar **pzTail/* OUT: End of parsed string(未處理的 SQL 串) */ ){ int rc; #ifdef SQLITE_ENABLE_API_ARMOR if( ppStmt==0 ) return SQLITE_MISUSE_BKPT; #endif *ppStmt = 0; if( !sqlite3SafetyCheckOk(db)||zSql==0 ){//確定 db 指標的合法性 returnSQLITE_MISUSE_BKPT; } sqlite3_mutex_enter(db->mutex); sqlite3BtreeEnterAll(db); rc = sqlite3Prepare(db, zSql, nBytes, prepFlags, pOld, ppStmt, pzTail);//將 UTF-8 編碼的 SQL 語句 zSql 編譯成 if( rc==SQLITE_SCHEMA ){ // 如果遇到 SCHEMA 改變,定案,再編譯 sqlite3ResetOneSchema(db, -1); sqlite3_finalize(*ppStmt); rc = sqlite3Prepare(db, zSql, nBytes, prepFlags, pOld, ppStmt, pzTail); } sqlite3BtreeLeaveAll(db); sqlite3_mutex_leave(db->mutex); assert( rc==SQLITE_OK || *ppStmt==0 ); return rc; } |
1.3: sqlite3Prepare函式
這個函式做了以下幾個事:
1:加入解析器Parse
2:所需記憶體的初始化以及是否禁用後備記憶體
3:判斷連結的資料庫是否加鎖,如果加鎖需要解鎖
4:zSqlCopy= sqlite3DbStrNDup(db, zSql, nBytes)語句複製了zsql。
5:呼叫sqlite3RunParser(&sParse,zSql, &zErrMsg);
還有的後面補上
staticint sqlite3Prepare( sqlite3 *db, /* Database handle. */ constchar *zSql, /* UTF-8 encoded SQL statement. */ intnBytes, /* Length of zSql in bytes. */ u32prepFlags, /* Zero or more SQLITE_PREPARE_* flags */ Vdbe *pReprepare, /* VM being reprepared */ sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement (輸出經過處理的準備語句)*/ constchar **pzTail/* OUT: End of parsed string */ ){ char *zErrMsg = 0; /* Error message */ int rc = SQLITE_OK; /* Result code */ int i; /* Loop counter */ Parse sParse; /* Parsing context(解析語境) */ memset(&sParse, 0, PARSE_HDR_SZ); memset(PARSE_TAIL(&sParse), 0, PARSE_TAIL_SZ);//初始化記憶體空間 sParse.pReprepare = pReprepare; assert( ppStmt && *ppStmt==0 ); /* assert( !db->mallocFailed ); // not true with SQLITE_USE_ALLOCA */ assert( sqlite3_mutex_held(db->mutex) ); /* For a long-term use prepared statement avoid the use of ** lookaside memory. (長期使用事先準備好的宣告避免後備儲存器的使用。) */ if( prepFlags & SQLITE_PREPARE_PERSISTENT ){ sParse.disableLookaside++;//禁用後備記憶體 來使需要重複使用的準備語句可以多次使用 db->lookaside.bDisable++;//同時禁止db中後備記憶體分配的引數 } /* **(檢查以驗證是否可以在所有資料庫模式上獲得讀鎖 .無法獲得讀鎖,表明某些其他資料庫連線持有寫鎖。 這反過來意味著另一個連線對模式做出了未提交的更改。 ) **(我們是否要繼續和準備對未提交的模式更改的宣告,如果這些模式更改隨後回滾,並在其位置進行不同的更改 然後,當準備好的語句執行時,模式cookie將無法檢測模式更改。災難會隨之發生。 */ for(i=0; i<db->nDb; i++) {//依此遍歷連線的資料庫 Btree *pBt = db->aDb[i].pBt; if( pBt ){ assert( sqlite3BtreeHoldsMutex(pBt) ); rc = sqlite3BtreeSchemaLocked(pBt);//判斷這個btree是否被鎖 if( rc ){ //如果是就提示該資料庫已被加鎖 constchar *zDb = db->aDb[i].zDbSName; sqlite3ErrorWithMsg(db, rc, "database schema is locked: %s", zDb); testcase( db->flags & SQLITE_ReadUncommit ); goto end_prepare; } } } sqlite3VtabUnlockList(db);//然後解鎖 sParse.db = db; if( nBytes>=0 && (nBytes==0 || zSql[nBytes-1]!=0) ){ char *zSqlCopy; int mxLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH]; //準備語句最大長度 testcase( nBytes==mxLen ); testcase( nBytes==mxLen+1 ); if( nBytes>mxLen ){//如果超過了這個長度就提示 sqlite3ErrorWithMsg(db, SQLITE_TOOBIG, "statement too long"); rc = sqlite3ApiExit(db, SQLITE_TOOBIG); goto end_prepare;//結束準備階段 } zSqlCopy = sqlite3DbStrNDup(db, zSql, nBytes);//複製zSql字串 if( zSqlCopy ){ sqlite3RunParser(&sParse, zSqlCopy, &zErrMsg); sParse.zTail = &zSql[sParse.zTail-zSqlCopy]; sqlite3DbFree(db, zSqlCopy); }else{ sParse.zTail = &zSql[nBytes]; } }else{ sqlite3RunParser(&sParse, zSql, &zErrMsg); } assert( 0==sParse.nQueryLoop ); if( sParse.rc==SQLITE_DONE ) sParse.rc = SQLITE_OK; if( sParse.checkSchema ){ schemaIsValid(&sParse); } if( db->mallocFailed ){ sParse.rc = SQLITE_NOMEM_BKPT; } if( pzTail ){ *pzTail = sParse.zTail; } rc = sParse.rc; #ifndef SQLITE_OMIT_EXPLAIN if( rc==SQLITE_OK && sParse.pVdbe && sParse.explain ){ staticconstchar * const azColName[] = { "addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment", "selectid", "order", "from", "detail" }; int iFirst, mx; if( sParse.explain==2 ){ sqlite3VdbeSetNumCols(sParse.pVdbe, 4); iFirst = 8; mx = 12; }else{ sqlite3VdbeSetNumCols(sParse.pVdbe, 8); iFirst = 0; mx = 8; } for(i=iFirst; i<mx; i++){ sqlite3VdbeSetColName(sParse.pVdbe, i-iFirst, COLNAME_NAME, azColName[i], SQLITE_STATIC); } } #endif if( db->init.busy==0 ){ sqlite3VdbeSetSql(sParse.pVdbe, zSql, (int)(sParse.zTail-zSql), prepFlags); } if( sParse.pVdbe && (rc!=SQLITE_OK || db->mallocFailed) ){ sqlite3VdbeFinalize(sParse.pVdbe); assert(!(*ppStmt)); }else{ *ppStmt = (sqlite3_stmt*)sParse.pVdbe; } if( zErrMsg ){ sqlite3ErrorWithMsg(db, rc, "%s", zErrMsg); sqlite3DbFree(db, zErrMsg); }else{ sqlite3Error(db, rc); } /* Delete any TriggerPrg structures allocated while parsing this statement. */ while( sParse.pTriggerPrg ){ TriggerPrg *pT = sParse.pTriggerPrg; sParse.pTriggerPrg = pT->pNext; sqlite3DbFree(db, pT); } end_prepare: sqlite3ParserReset(&sParse); rc = sqlite3ApiExit(db, rc); assert( (rc&db->errMask)==rc ); return rc; } |
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) n = sqlite3GetToken((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; sqlite3Parser(pEngine, tokenType, 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
概要
上一篇,我們主要搭建了一個簡單的環境,這邊我們主要來分析下mybatis是如何來載入它的配置檔案Configuration.xml的。
分析
1 public class App {
2 public static void main(String[] args) {
3 學習優秀框架的原始碼,是提升個人技術水平必不可少的一個環節。如果只是停留在知道怎麼用,但是不懂其中的來龍去脈,在技術的道路上註定走不長遠。最近,學習了一段時間的spring原始碼,現在整理出來,以便日後溫故知新。
IOC容器是spring最核心的模組之一,是整個spring體系的基石,spring其
結構化查詢語言(Structured Query Language)簡稱SQL
結構化查詢語言是高階的非過程化程式語言,允許使用者在高層資料結構上工作。它不要求使用者指定對資料的存放方法,也不需要使用者瞭解具體的資料存放方式,所以具有完全不同底層結構的不同資料庫
已經寫了幾篇關於Android原始碼的,原始碼程式碼量太大,所以如果想分析某個模組可能不知如何下手,說一下思路
1,分析原始碼英文閱讀能力要夠,想要分析某個模組一般找模組對應的英文,就是模組
2,找到之後首先檢視清單配置檔案Androidmani.fest,找到程式主介面activity
3,通過檢視配置檔 AQS其實就是java.util.concurrent.locks.AbstractQueuedSynchronizer這個類。 閱讀Java的併發包原始碼你會發現這個類是整個java.util.concurrent的核心之一,也可以說是閱讀整個併發包原始碼的一個突破口。
比如讀ReentrantLock的
1. 使用
spark給出的example中涉及到LBFGS有兩個,分別是LBFGSExample.scala和LogisticRegressionWithLBFGSExample.scala,第一個是直接使用LBFGS直接訓練,需要指定一系列優化引數,優
一:sql準備過程在前面的分析中我們知道,sqlite3_open()為我們打開了資料庫並準備了所要的記憶體空間,鎖,vfs等。接下來就分析sql是如何被解析器一步一步解析的。上圖是準備sql語句的過程分析圖。 1.1:sqlite3_prepare_v2函式:該函數是準備 # 一、簡介
- Springboot原始碼解析是一件大工程,逐行逐句的去研究程式碼,會很枯燥,也不容易堅持下去。
- 我們不追求大而全,而是試著每次去研究一個小知識點,最終聚沙成塔,這就是我們的springboot原始碼管中窺豹系列。
![ 簡介 ](https://zhangbin1989.gitee. 概述
上一篇我們講了configuation.xml中幾個標籤的解析,例如<properties>,<typeAlises>,<settings>等,今天我們來介紹剩下的兩個比較重要的標籤之一,<environments>,這個標籤主要用於我們訪問資料庫的配置 在上一篇《併發程式設計(十一)—— Java 執行緒池 實現原理與原始碼深度解析(一)》中提到了執行緒池ThreadPoolExecutor的原理以及它的execute方法。這篇文章是接著上一篇文章寫的,如果你沒有閱讀上一篇文章,建議你去讀讀。本文解析ThreadPoolExecutor#submit。
一 、概述 SOFABoot是螞蟻金服開源的基於 Spring Boot 的研發框架,它在Spring Boot 的基礎上,提供了諸如 Readiness Check,類隔離,日誌空間隔離等等能力。在增強了 Spring Boot 的同時,SOFABoot 提供
在網路程式設計時,知道域名是不能直接訪問一個主機的,需要轉換成相應的IP地址。有時在程式中需要將一個IP地址轉換成一個域名。本節將講解C程式中的IP地址與域名的轉換問題。
提示:在TCP/IP網路中,通訊雙方的主機必須知道彼此的IP地址方可進行正常的通訊,如果給出的主機的域 1. private voidprocessImports(ConfigurationClass configClass, SourceClass currentSourceClass,2. Collection<SourceClass>importCandidates, b
您可能聽說過View ,ViewManager,Window,PhoneWindow,WindowManager,WindowManagerService,可是你知道這幾個類是什麼關係,幹嘛用的。概括的來說,View是放在Window中的,Window是一個抽象
一、什麼是HystrixCircuitBreaker?
HystrixCircuitBreaker可以防止應用程式重複的嘗試呼叫容易失敗的依賴服務。HystrixCircuitBreaker的目的和Retry模式的目的是不同的。Retry模式令應用程式不斷的去重試呼叫依賴服 上一篇跟蹤了IOC容器對配置檔案的定位,現在我們繼續跟蹤程式碼,看看IOC容器是怎麼載入和註冊配置檔案中的資訊的。開始之前,首先我們先來了解一下IOC容器所使用的資料結構-------BeanDefinition,它是一個上層介面,有很多實現類,分別對應不同的資料載體。我們平時開發的時候,也會定義很多po 上一篇主要是跟蹤了IOC容器對bean標籤進行解析之後存入Map中的過程,這些bean只是以BeanDefinition為載體單純的儲存起來了,並沒有轉換成一個個的物件,今天繼續進行跟蹤,看一看IOC容器是怎樣例項化物件的。
我們都使用過以下程式碼:
1 FileSystemXmlApplicati 方法注入
在spring容器中,大部分bean的作用域(scope)是單例(singleton)的,少部分bean的作用域是原型(prototype),如果一個bean的作用域是原型,我們A bean的作用域是原型,B bean中以@Autowired的方式注入A,那麼B在A中依舊是單例。我們可以讓B類實現A 組合Java配置
在XML中,我們可以使用<import/>標籤,在一個XML檔案中引入另一個XML檔案,在Java類中,我們同樣可以在一個配置類中用@Import引入另一個配置類,被引入的配置類中的@Bean也會載入到spring容器。程式碼如下:
@Configuration
public 名詞解釋
SQL: Structured Query Language,結構化查詢語言,是一種在關係型資料庫中用於管理資料的標準語言。SQL是一種宣告式程式語言,即只需表明需要什麼而無需關注實現細節(C#中的LINQ也是如此)。
SQL方言:在SQL基礎上延伸的其它語言,如SQL Server中 |