本文基於MySQL5.7.22進行分析。
1. SQL總體執行流程圖
透過上面圖,可以從全域性上了解SQL語句執行流程以及與其他模組互動。
1.1 SQL查詢執行流程
2. 語法解析
2.1 程式語言知識回顧
在介紹具體的MySQL資料庫解析SQL之前,先來回歸一下程式語言的知識點
1. 形式語言(Formal language)
形式語言是用精確的數學或機器可處理的公式定義的語言,個人理解形式語言就是符號化的語言,比如程式語言(C C++ JAVA PYTHON),都是定義一組符號來描述對映人的思維邏輯的,符號化的語言的好處就是能夠準確表達所要表達的,不會產生二義性.
2. 文法(grammar)
當我們要描述一種語言時,需要給出這種語言的所有句子,當句子的數目是有限可數時,就要都列出來;當句子是一個無窮集,也就是無限不可數時,就要給出可以表示它們的結構的描述方法或者說,句子的組成規則。這種規則就是文法。即從形式上用於描述和規定結構的稱為文法(或者說語法), 也可以理解為指怎麼由一堆符號組成一個有含義的句子的規則和協議.
3. 上下文無關文法(context-free grammar)(數學描述)
一個四元陣列G=(VN,VT,S,P):
VN:非空有限的非終結符集合VT:非空有限的終結符集
S:開始符號P:產生式集合
其中,VN∩VT=∅,S∈VN
P中產生式一般形式為A→α|β,其中A∈VN,α,β∈(VN∪VT)*
大寫字母表示非終結符,小寫字母表示終結符,α、β、γ等代表由 終結符和非終結符號的並集的閉包 中的元素 組成的符號串
上下文無關文法取名為“上下文無關”的原因就是因為字元A總可以被字串α或β自由替換,而無需考慮字元A出現的上下文
4. 終結符(terminal symbol )
終結符是一個形式語言的基本符號。就是說,它們能在一個形式語法的推導規則的輸入或輸出字串存在,而且它們不能被分解成更小的單位。可以理解為產生式推導到什麼時候停止呢,推導到終止符為止.
5. 非終結符(nonterminal symbol)
非終結符是可以被取代的符號。一個形式文法中必須有一個起始符號;這個起始符號屬於非終結符的集合。在上下文無關文法中,每個推導規則的左邊只能有一個非終結符而不能有兩個以上的非終結符或終結符。
6. 巴科斯正規化(BNF: Backus-Naur Form)
以美國人巴科斯(Backus)和丹麥人諾爾(Naur)的名字命名的一種形式化的語法表示方法,用來描述語法的一種形式體系,是一種典型的元語言。又稱巴科斯-諾爾形式(Backus-Naur form)。它不僅能嚴格地表示語法規則,而且所描述的語法是與上下文無關的。它具有語法簡單,表示明確,便於語法分析和編譯的特點。
程式語言的文法除了數學化的描述,還需要在在實際生產中易於描述的符號化語言,BNF就是用來描述上下文無關文法的符號化的語言.
2.2 概念與bison
2.1章節說明的概念跟bison又是一種什麼關係呢?
bison是屬於 GNU 專案的一個語法分析器生成器。
bison能夠將上下文無文法解釋語法分析表,由於相容yacc,而yacc是BNF進行描述文法規則的, 所以可以理解為bison能夠解析以BNF描述上下文無關文法的語法分析器生成器.
2.3 MySQL與bison
MySQL使用bison作為其解析SQL語句的語法分析器.
2.4 SQL解析相關檔案及關聯
1. 相關檔案
SQL詞法解析檔案:
sql/sql_lex.h 、sql/lex_token.h 、sql/lex.h、 sql/lex_symbol.h、
sql/sql_lex.cc 、sql/ gen_lex_token.cc
SQL語法解析檔案:
sql/sql_yacc.yy 、sql/sql_yacc.cc、 sql/sql_yacc.h
SQL語句的hint語法解析檔案:
sql/sql_hints.yy 、sql/sql_hints.yy.cc
2. 語法解析
3. sql/sql_yacc.cc
3.1 sql_yacc.cc描述
sql_yacc.cc規定了SQL語句語法規則,定義了SQL語句的關鍵字.
3.2 sql_yacc.cc檔案結構
%{ Prologue %} Bison declarations %% Grammar rules %% Epilogue |
1. Prologue部分包括宏定義和在語法規則動作中使用的函式和變數的宣告. 這些將複製到分析器檔案的開頭以便先於yyparse的定義. 你可以使用`#include'來從標頭檔案獲取宣告. 如果你不需要任何的C宣告, 可以省略這個部分的括號分隔符`%{'和`%}', 這部分被BISON原封不動地複製到輸出的.C檔案中
2. Bison declatations部分包含了定義終結符和非終結符的宣告,優先順序等等
3. Grammar Rules部分包含了一個或多個Bison語法規則, 在這裡至少應該有一個語法規則,並且第一個%%, 絕對不能省略,解釋它在檔案的最開頭.
4. 就像Prologue部分被複制到開頭一樣,Epilogue部分被逐字地複製到分析器檔案的結尾. 如果你想放一些程式碼卻沒必要放在yyparse的定義之前,這裡是最方便的地方. 如果最後一部分為空,你可以省略分隔它的分隔符%%.
3.3 sql_yacc.cc檔案解析
3.3.1 Prologue部分
該部分包含了C語言的標頭檔案,宏定義,該部分主要宣告和定義了2個關鍵函式,如下:
int yylex(void *yylval, void *yythd);詞法解析函式的宣告
void MYSQLerror(YYLTYPE *, THD *thd, const char *s);語法分析錯誤函式的定義。
3.3.2 Bison declatations部分
本部分與prologue部分使用 %% 進行分隔
3.3.3 Grammar Rules部分
本部分與Bison declatations部分,使用 %% 進行分隔
例子分析:
Bison產生式: result: components…;
下面的例子就是一個產生式
query是產生式的左端, 冒號後面是產生式的右端, | 代表或的意思, {}當query產生式推出右端情況的時候所執行的動作,一個產生式結束要是 ;
其中, query verb_clause 都是非終止符, END_OF_INPUT 是終止符, 也就是說產生式推導到終止符就停止推導.
即query->END_OF_INPUT | verb_clause | verb_clause END_OF_INPUT
query: END_OF_INPUT { THD *thd= YYTHD; if (!thd->bootstrap &&!thd->m_parser_state->has_comment()) { my_message(ER_EMPTY_QUERY, ER(ER_EMPTY_QUERY), MYF(0)); MYSQL_YYABORT; } thd->lex->sql_command= SQLCOM_EMPTY_QUERY; YYLIP->found_semicolon= NULL; } |verb_clause { Lex_input_stream *lip = YYLIP; if (YYTHD->get_protocol()->has_client_capability(CLIENT_MULTI_QUERIES) && lip->multi_statements && !lip->eof()) { lip->next_state= MY_LEX_END; lip->found_semicolon= lip->get_ptr(); } else { lip->found_semicolon= NULL; } } ';' opt_end_of_input |verb_clause END_OF_INPUT { YYLIP->found_semicolon= NULL; } ; |
4. 參考資料
1. 《lex與yacc》(第二版)
2. 《flex與bison》(第二版)
3. Bison操作手冊:http://www.gnu.org/software/bison/manual/bison.html