Apache頂級專案ShardingSphere — SQL Parser的設計與實現
導語:SQL作為現代計算機行業的資料處理事實標準,是目前最重要的資料處理介面之一,從傳統的DBMS(如MySQL、Oracle),到主流的計算框架(如spark,flink)都提供了SQL的解析引擎,因此想對sql進行精細化的操作,一定離不開SQL Parser。Apache ShardingSphere 是一套開源的分散式資料庫中介軟體解決方案組成的生態圈,需要對SQL進行精細化的操作,如改寫,加密等,因此也實現了SQL Parser,並提供獨立的Parser引擎。
先來認識一下傳統資料庫中一條SQL處理流程是怎樣的,接受網路包-》資料庫協議解析網路包的到sql-》SQL語法解析為抽象語法樹-》把語法樹轉換成關係代數表示式樹(邏輯執行計劃)-》再轉換成物理運算元樹(物理執行計劃)-》遍歷物理運算元樹執行相應運算元的實現獲取資料並返回。
一、工作原理
SQL Parser 的功能是把一條SQL解析為抽象語法樹(AST),SQL Parser需要編譯原理相關的知識,簡單介紹一下。
SQL Parser 包含詞法解析(Lexer)和語法解析(Parser),詞法解析的作用是把一個sql 分割成一個一個不可分割的單元,例子:
原始sql: select id,name from table1 where name=“xxx”;
詞法解析器輸入是原始sql,並且暴露一個介面nextToken(),每次呼叫nextToken()都會返回一個Token(表示上面所說的不可分割的元素),虛擬碼如下:
String originSql = "select id,name from table1 where name='xxx'";
Lexer lexer = new Lexer(originSql);
while(!lexer._hitEOF){// 判斷是否結束
System.out.printLn(lexer.nextToken())
}
// 輸出如下:
select
id
,
name
from
table1
where
name
=
'xxx'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
語法分析器的作用是使用Lexer的輸出(呼叫nextToken()),構造出AST,以上面的sql為例,解析器得出的語法樹如下:
1、Lexer(詞法分析)原理
Lexer 也稱為分詞,從左向右掃描SQL,將其分割成一個個的toke(不可分割的,具有獨立意義的單元,類似英語中的單詞)。
Lexer的實現一般都是構造DFA(確定性有限狀態自動機)來實現的,以一個例子說明。
狀態轉移圖如下,這是一個能夠識別識別符號,數字和一般運算子的詞法解析器。
程式碼實現可以使用傳統的while case的模板實現,虛擬碼如下:
Class Token{...}
Enum State {
BEGIN,OPERATER,IDENTIFIER,NUMBER
}
Class Lexer{
String input = "...";
State state = BEGIN;
int index = 0;
Token nextToken() {
while(index < input.length) {
Char char = input.charAt(index);
switch (state) {
case BEGIN:
switch (char){
case a-zA-Z_ :
index++;
state = IDENTIFIER;
case +-*/ :
state = BEGIN
return Token(OPERATER)
case 0-9:
state = NUMBER;
index++;
default:
return null;
}
case IDENTIFIER:
switch (char){
case a-zA-Z_0-9 :
index++;
state = IDENTIFIER;
default :
state = BEGIN;
index--;
return Token(IDENTIFIER)
}
case NUMBER:
switch (char){
case 0-9 :
index++;
state = NUMBER;
default :
state = BEGIN;
index--;
return Token(NUMBER)
}
default:
return null;
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
2、Parser(語法解析) 原理
Parser階段有兩種型別方法來實現,一種是自頂向下分析法,另一種是自底向上分析法,簡單介紹一下兩種型別分析法的處理思路。
首先給出上下文無關語法和相關術語的定義:
- 終結符集合 T (terminal set)
一個有限集合,其元素稱為 終結符(terminal) - 非終結符集合 N (non-terminal set)
一個有限集合,與 T 無公共元素,其元素稱為 非終結符(non-terminal) - 符號集合 V (alphabet)
T 和 N 的並集,其元素稱為符號(symbol) 。因此終結符和非終結符都是符號。符號可用字母:A, B, C, X, Y, Z, a, b, c 等表示。 - 符號串(a string of symbols)
一串符號,如 X1 X2 … Xn 。只有終結符的符號串稱為 句子(sentence)。空串 (不含任何符號)也是一個符號串,用 ε 表示。符號串一般用小寫字母 u, v, w 表示。 - 產生式(production)
一個描述符號串如何轉換的規則。對於上下文字無關語法,其固定形式為:A -> u ,其中 A 為非終結符, u 為一個符號串。 - 產生式集合 P (production set)
一個由有限個產生式組成的集合 - 展開(expand)
一個動作:將一個產生式 A -> u 應用到一個含有 A 的符號串 vAw 上,用 u 代替該符號串中的 A ,得到一個新的符號串 vuw。 - 摺疊(reduce)
一個動作:將一個產生式 A -> u 應用到一個含有 u 的符號串 vuw 上,用 A 代替該符號串中的 u ,得到一個新的符號串 vAw。 - 起始符號 S (start symbol)
N 中的一個特定的元素 - 推導(derivate)
一個過程:從一個符號串 u 開始,應用一系列的產生式,展開到另一個的符號串 v。若 v 可以由 u 推導得到,則可寫成:u => v 。 - 上下文字無關語法 G (context-free grammar, CFG)
一個 4 元組:(T, N, P, S) ,其中 T 為終結符集合, N 為非終結符集合, P 為產生式集合, S 為起始符號。一個句子如果能從語法 G 的 S 推導得到,可以直接稱此句子由語法 G 推導得到,也可稱此句子符合這個語法,或者說此句子屬於 G 語言。G 語言( G language) 就是語法 G 推匯出來的所有句子的集合,有時也用 G 代表這個集合。 - 解析(parse)
也稱為分析,是一個過程:給定一個句子 s 和語法 G ,判斷 s 是否屬於 G ,如果是,則找出從起始符號推導得到 s 的全過程。推導過程中的任何符號串(包括起始符號和最終的句子)都稱為 中間句子(working string)。
2.1 自頂向下分析法 LL(1)
LL(1) 是自頂向下分析法的一種,第一個L代表從左向右掃描待解析文字,第二個L代表從左向右展開,(1)代表每次讀取一個Token。
以簡單表示式為例子:
// 語法規則, 整個是一個產生式,左邊expr為非終結符,NUM是一
//個Token,為終結符
expr: NUM | NUM expr
// 待解析文字
1 2 23 45
- 1
- 2
- 3
- 4
- 5
解析過程如下:
我們的目標是將起始符號expr展開成句子 1 2 23 45
- 對比expr和NUM(1),只能選擇expr -> NUM expr,才可以和NUM(1)匹配,展開後得NUM expr
- 忽略已匹配的NUM(1), 再次讀入NUM(2),只能選擇expr -> NUM expr,展開後得NUM expr
- 忽略已匹配的NUM(2),再次讀入NUM(23),只能選擇expr -> NUM expr,展開後得NUM expr
- 忽略已匹配的NUM(23),再次讀入NUM(45),只能選擇expr -> NUM,展開後得NUM
- 得到最終句子 NUM(1)NUM(2) NUM(23) NUM(45) 可接受,解析完成
注意點:為什麼1,2,3只能選擇expr -> NUM expr, Lexer中會有介面判斷是否得到末尾,ShardingSphere中介面是lexer._hitEOF。
2.1 自底向上分析法 LR(1)
自底向上分析的順序和自頂向下分析的順序相反,從給定的句子開始,不斷的挑選出合適的產生式,將中間句子中的子串摺疊為非終結符,最終摺疊到起始符號。
LR(1) 是自底向上分析法的一種,第一個L代表從左向右掃描待解析文字,第二個R代表從右向坐摺疊,(1)代表每次讀取一個Token。
以簡單表示式為例:
// 語法規則, 整個是一個產生式,左邊expr為非終結符,NUM是一
//個Token,為終結符
expr: NUM | expr NUM
// 待解析文字
1 2 23 45
- 1
- 2
- 3
- 4
- 5
解析過程是如下:
我們的目標是把 1 2 23 45 摺疊為expr
- 讀入NUM(1),發現只能選擇expr -> NUM, 摺疊得expr
- 讀入NUM(2),只能選擇expr -> expr NUM,摺疊得expr
- 讀入NUM(23),只能選擇expr -> expr NUM, 摺疊得expr
- 讀入NUM(45),只能選擇expr-> expr NUM, 摺疊得expr 可接受,解析完成
二、ShardingSphere Parser 實現
實現Parser的方式一般分為兩種,一種是寫程式碼實現狀態機來進行解析,另一種是通過解析器生成器根據定義的語法規則生成解析器,ShardingSphere使用第二種方式,這是由於衡量了效能,擴充套件性和容易維護因素最終決定的。
以ShardingSphere中的MySQL 解析引擎為例,模組shardingsphere-sql-parser-mysql,語法定義路徑src/main/antlr。
功能點:
- 提供獨立的SQL解析引擎
- 可以方便的對語法規則進行擴充和修改
- 提供SQL 變數引數化功能
- 提供SQL 格式化功能
- 支援多種方言
資料庫 | 支援狀態 |
---|---|
MySQL | 支援,完善 |
PostgreSQL | 支援,完善 |
SQLServer | 支援 |
Oracle | 支援 |
SQL92 | 支援 |
使用方法:
//maven 依賴
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-sql-parser-engine</artifactId>
<version>${project.version}</version>
</dependency>
// 根據需要引入指定方言的解析模組(以MySQL為例),可以新增所有支援的方言,也可以只新增使用到的
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-sql-parser-mysql</artifactId>
<version>${project.version}</version>
</dependency>
//獲取語法樹
/**
* databaseType type:String 可能值 MySQL,Oracle,PostgreSQL,SQL92,SQLServer
* sql type:String 解析的SQL
* useCache type:boolean 是否使用快取
* @return parse tree
*/
ParseTree tree = new SQLParserEngine(databaseType).parse(sql, useCache);
// 獲取SQLStatement
/**
* databaseType type:String 可能值 MySQL,Oracle,PostgreSQL,SQL92,SQLServer
* useCache type:boolean 是否使用快取
* @return SQLStatement
*/
ParseTree tree = new SQLParserEngine(databaseType).parse(sql, useCache);
SQLVisitorEngine sqlVisitorEngine = new SQLVisitorEngine(databaseType, "STATEMENT");
SQLStatement sqlStatement = sqlVisitorEngine.visit(tree);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
三、常見解析器
1、MySQL Parser
詞法解析通過手寫程式碼的方式實現,語法解析通過定義語法規則,使用bison生成語法解析程式碼。
2、PostgreSQL Parser
通過定義語法規則,使用flex/bison生成詞法,語法解析程式碼。
3、TIDB Parser(能夠單獨使用, golang)
詞法解析通過手寫程式碼的方式實現,語法解析通過定義語法規則,使用goyacc生成語法解析程式碼。
4、ShardingSphere Parser(能夠單獨使用, 多種目標語言c,c++,java, golang,python)
- 通過定義語法規則,使用ANTLR生成詞法,語法解析程式碼
- 可以方便自定義語法
- 提供快取機制
- SQL 格式化,SQL引數化
- 支援多種DB: Mysql, PostgreSQL, SQLServer, Oracle, SQL92
- 支援自定義visitor
5、alibaba druid(能夠單獨使用,java)
- 通過程式碼的方式實現詞法解析器和語法解析器
- 支援多種DB: Mysql, PostgreSQL, SQLServer, Oracle, odps, db2, hive, SQL92
- SQL 格式化,SQL引數化
- 支援自定義visitor
6、Jsqlparser(能夠單獨使用, java)
- 通過javacc定義語法解析規則實現
- 不侷限於某一個DB
注意:
- 基本都是通過定義語法來實現的,詞法解析都是通過定義正則語言,構造有限狀態自動機實現,區別是LL(自頂向下)語法和LR(自底向上)語法。
- antlr使用的是LL(自頂向下)的語法規則 改進的LL(*)演算法,Bison和goyacc使用LR(自底向上)的語法規則,LALR演算法,Jsqlparser是LL(k), druid目測類似LL(k)。
- LR相比LL表達能力更強,典型的例子是antlr不支援相互左遞迴,bison支援。
四、AST(語法樹)應用
- 轉化為邏輯執行計劃,邏輯執行計劃再轉換為物理執行計劃,物理執行計劃用於儲存引擎具體執行。
eg: select * from table1, table2 where table1.id=1 轉化為邏輯執行計劃為:
通過遍歷語法樹,對SQL進行格式化,引數化等(完)
http://weibo.com/p/23126100007598826952660993
http://www.tianya.cn/143521411/profile
http://www.tianya.cn/143521411
http://www.tianya.cn/143518697/profile
http://www.tianya.cn/143518697
http://www.tianya.cn/143521486/profile
http://www.tianya.cn/143521486
http://www.tianya.cn/143521503/profile
http://www.tianya.cn/143521503
http://www.tianya.cn/143521536/profile
http://www.tianya.cn/143521536
http://www.tianya.cn/143521568/profile
http://www.tianya.cn/143521568
http://www.tianya.cn/143521578/profile
http://www.tianya.cn/143521578
http://www.tianya.cn/143521620/profile
http://www.tianya.cn/143521620
http://www.tianya.cn/143521999/profile
http://www.tianya.cn/143522003/profile
http://www.tianya.cn/143522012/profile
http://www.tianya.cn/143522018/profile
http://www.tianya.cn/143522027/profile
http://www.tianya.cn/143522033/profile
http://www.tianya.cn/143522038/profile
http://www.tianya.cn/143522047/profile
http://www.tianya.cn/143522052/profile
http://www.tianya.cn/143522056/profile
http://www.tianya.cn/143522059/profile
http://www.tianya.cn/143522064/profile
http://www.tianya.cn/143522069/profile
http://www.tianya.cn/143522077/profile
http://www.tianya.cn/143522079/profile
http://www.tianya.cn/143522086/profile
http://www.tianya.cn/143522091/profile
http://www.tianya.cn/143522096/profile
http://www.tianya.cn/143522110/profile
http://www.tianya.cn/143522117/profile
http://www.tianya.cn/143522120/profile
http://www.tianya.cn/143522127/profile
http://www.tianya.cn/143522142/profile
http://www.tianya.cn/143522145/profile
http://www.tianya.cn/143522155/profile
http://www.tianya.cn/143522176/profile
http://www.tianya.cn/143522183/profile
http://www.tianya.cn/143522245/profile
http://www.tianya.cn/143522252/profile
http://www.tianya.cn/143522254/profile
http://www.tianya.cn/143522259/profile
http://www.tianya.cn/143522265/profile
http://www.tianya.cn/143522296/profile
http://www.tianya.cn/143522300/profile
http://www.tianya.cn/143522307/profile
http://www.tianya.cn/143522310/profile
http://www.tianya.cn/143522317/profile
http://www.tianya.cn/143522320/profile
http://www.tianya.cn/143522324/profile
http://www.tianya.cn/143522336/profile
http://www.tianya.cn/143522330/profile
http://www.tianya.cn/143521999
http://www.tianya.cn/143522003
http://www.tianya.cn/143522012
http://www.tianya.cn/143522018
http://www.tianya.cn/143522027
http://www.tianya.cn/143522033
http://www.tianya.cn/143522038
http://www.tianya.cn/143522047
http://www.tianya.cn/143522052
http://www.tianya.cn/143522056
http://www.tianya.cn/143522059
http://www.tianya.cn/143522064
http://www.tianya.cn/143522069
http://www.tianya.cn/143522077
http://www.tianya.cn/143522079
http://www.tianya.cn/143522086
http://www.tianya.cn/143522091
http://www.tianya.cn/143522096
http://www.tianya.cn/143522110
http://www.tianya.cn/143522117
http://www.tianya.cn/143522120
http://www.tianya.cn/143522127
http://www.tianya.cn/143522142
http://www.tianya.cn/143522145
http://www.tianya.cn/143522155
http://www.tianya.cn/143522176
http://www.tianya.cn/143522183
http://www.tianya.cn/143522245
http://www.tianya.cn/143522252
http://www.tianya.cn/143522254
http://www.tianya.cn/143522259
http://www.tianya.cn/143522265
http://www.tianya.cn/143522296
http://www.tianya.cn/143522300
http://www.tianya.cn/143522307
http://www.tianya.cn/143522310
http://www.tianya.cn/143522317
http://www.tianya.cn/143522320
http://www.tianya.cn/143522324
http://www.tianya.cn/143522336
http://www.tianya.cn/143522330