MySQL核心原始碼解讀-SQL解析之解析器淺析

京東發表於2018-12-12

MYSQL伺服器接收SQL格式的查詢,首先要對sql進行解析,內部將文字格式轉換為二進位制結構,這個轉換就是解析器,解析的目的是為了讓優化器更好的處理指令,以便以最優的路徑,最少的耗時返回我們想要的結果。

sql解析器的構成:

  1. 詞法分析(Lexical scanner):作用是將整個查詢分解為多個元素。

  2. 語法規則(Grammar rule module):尋找sql語法規則組合,產生一個序列,執行這些規則相關的程式碼。

1 and 2 產生一棵解析樹,提供給優化器使用。

mysql解析器的特殊性在於它直接轉換為程式記憶體中的內部解析的C/C++結構,而一般的解析器是將文字表示式轉換為位元組程式碼。

1. 原始碼解讀解析器

MySQL語法解析封裝在函式MYSQLparser中,包含兩個模組:詞法分析(Lexical scanner)和語法規則(Grammar rule module)。詞法分析將整個SQL語句打碎成一個個單詞(Token),而語法規則模組則根據MySQL定義的語法規則生成對應的資料結構,並儲存在物件THD->LEX結構當中。最後優化器,根據這裡的資料,生成執行計劃,再呼叫儲存引擎介面執行。詞法分析和語法規則模組有兩個較成熟的開源工具Flex和Bison分別用來解決這兩個問題。MySQL出於效能和靈活考慮,選擇了自行完成詞法解析部分,語法規則部分使用Bison。詞法解析和Bison溝通的核心函式是由詞法解析器提供的函式介面yylex(),在Bison中,必要的時候呼叫yylex()獲得詞法解析的資料,完成自己的語法解析。Bison的入口為yyparse(),在MySQL中定義為MYSQLParse。

  • 1.1.     解析示意圖

MySQL核心原始碼解讀-SQL解析之解析器淺析

MySQL核心原始碼解讀-SQL解析之解析器淺析

Bison在做語法解析後,會將解析結果(解析樹/AST)儲存在THD::LEX中,通過儲存WHERE的資料結構來檢視語法解析的結果。

  • 1.2.     解析樹的ITEM物件

在MYSQL中,有以下ITEM大型別:

FIELD_ITEM, FUNC_ITEM,

SUM_FUNC_ITEM,

STRING_ITEM,

INT_ITEM,

REAL_ITEM,

NULL_ITEM,

VARBIN_ITEM,

COPY_STR_ITEM,

FIELD_AVG_ITEM,

DEFAULT_VALUE_ITEM,

PROC_ITEM,COND_ITEM,

REF_ITEM,

FIELD_STD_ITEM,

FIELD_VARIANCE_ITEM,

INSERT_VALUE_ITEM,

SUBSELECT_ITEM,

ROW_ITEM,

CACHE_ITEM,

TYPE_HOLDER,

PARAM_ITEM

其中許多ITEM還有小類,如Item_func有如下小型別:

UNKNOWN_FUNC,

EQ_FUNC,

EQUAL_FUNC,

NE_FUNC,

LT_FUNC,

LE_FUNC,

GE_FUNC,

GT_FUNC,FT_FUNC,

LIKE_FUNC,

NOTLIKE_FUNC,

ISNULL_FUNC,

ISNOTNULL_FUNC,

COND_AND_FUNC,

COND_OR_FUNC,

COND_XOR_FUNC,

BETWEEN, IN_FUNC,

INTERVAL_FUNC,

ISNOTNULLTEST_FUNC,

SP_EQUALS_FUNC,

SP_DISJOINT_FUNC,

SP_INTERSECTS_FUNC,

SP_TOUCHES_FUNC,

SP_CROSSES_FUNC,

SP_WITHIN_FUNC,

SP_CONTAINS_FUNC,

SP_OVERLAPS_FUNC,

SP_STARTPOINT,

SP_ENDPOINT,

SP_EXTERIORRING,

SP_POINTN,

SP_GEOMETRYN,

SP_INTERIORRINGN,

NOT_FUNC,

NOT_ALL_FUNC,

NOW_FUNC,

VAR_VALUE_FUNC

  • 1.3.     ITEM語法樹

MySQL核心原始碼解讀-SQL解析之解析器淺析

  • 1.4.     FIELD 型別

enum enum_field_types {

MYSQL_TYPE_DECIMAL, MYSQL_TYPE_TINY,

 MYSQL_TYPE_SHORT,MYSQL_TYPE_LONG,

 MYSQL_TYPE_FLOAT,MYSQL_TYPE_DOUBLE,

 MYSQL_TYPE_NULL,MYSQL_TYPE_TIMESTAMP,

 MYSQL_TYPE_LONGLONG,MYSQL_TYPE_INT24,

 MYSQL_TYPE_DATE, MYSQL_TYPE_TIME,

 MYSQL_TYPE_DATETIME,MYSQL_TYPE_YEAR,

 MYSQL_TYPE_NEWDATE,

 MYSQL_TYPE_ENUM=247,

 MYSQL_TYPE_SET=248,

 MYSQL_TYPE_TINY_BLOB=249,

 MYSQL_TYPE_MEDIUM_BLOB=250,

 MYSQL_TYPE_LONG_BLOB=251,

 MYSQL_TYPE_BLOB=252,

 MYSQL_TYPE_VAR_STRING=253,

 MYSQL_TYPE_STRING=254,

 MYSQL_TYPE_GEOMETRY=255

};

  • 1.5.     FIELD和ITEM的關係

通過Item類中的tmp_table_field_from_field_type函式將一個Item類轉化為一個Filed類返回,例如

Item_int ->Field_longlong

Item_real->Field_double

Item_string->Field_string

  • 1.6.     Bison語法中的WHERE

select_from ==>

       |

       |---->from

 join_table_list

 where_clause

       |

       |---->expr or expr

       |---->expr amd expr

       |---->……………………

       |---->simple_expr comp_op simple_expr

              group_clause having_clause

              opt_order_clause

              opt_limit_clause

where 要點:

where_clause:

        /* empty */ {}

      | WHERE expr

      {

        THD->lex->current_select->where = $2

      }


expr:

      ...

      | expr and expr

       {

         $$ = new (YYTHD->mem_root) Item_cond_and($1, $3)

       }

      |ident comp_op NUM   /*簡化版*/

      {

         $$ = new Item_func_ge(a, b); /*簡化版*/

      }        

通過解析就能生成where的語法樹。

Where解析樹的分支:如圖

MySQL核心原始碼解讀-SQL解析之解析器淺析

  • 1.7.     總結

解析器的最終執行結果就是解析樹,sql語法的複雜性要求具有同樣複雜程度的結構,通過這種結構有效儲存用於執行每個可能使用到的sql語句所需的資訊。

解析樹中重要的兩個物件【enum_sql_command和select_lex】, sql_command顯示sql型別,execute_command則指導呼叫相關函式。通過核心級別的呼叫,最終生成解析樹,提供給優化器,最終完成我們的操作指令。

2.    參考資料

[1]  OReilly Understanding MySQL Internals

[2]  MySQL核心:InnoDB儲存引擎

[3]  OReilly

[4]《lex與yacc》(第二版)

[5]《flex與bison》(第二版)

[6]  Bison操作手冊:     http://www.gnu.org/software/bison/manual/bison.html

相關文章