首頁 資料庫 mysql教程 Mysql源码学习――词法分析MYSQLlex_MySQL

Mysql源码学习――词法分析MYSQLlex_MySQL

Jun 01, 2016 pm 01:44 PM
客戶端 伺服器 計劃

bitsCN.com

 

词法分析MYSQLlex

 

       客户端向服务器发送过来SQL语句后,服务器首先要进行词法分析,而后进行语法分析,语义分析,构造执行树,生成执行计划。词法分析是第一阶段,虽然在理解Mysql实现上意义不是很大,但作为基础还是学习下比较好。

 

词法分析即将输入的语句进行分词(token),解析出每个token的意义。分词的本质便是正则表达式的匹配过程,比较流行的分词工具应该是lex,通过简单的规则制定,来实现分词。Lex一般和yacc结合使用。关于lex和yacc的基础知识请参考Yacc 与Lex 快速入门- IBM。如果想深入学习的话,可以看下《LEX与YACC》。

 

然而Mysql并没有使用lex来实现词法分析,但是语法分析却用了yacc,而yacc需要词法分析函数yylex,故在sql_yacc.cc文件最前面我们可以看到如下的宏定义:

 

/* Substitute the variable and function names.  */

#define yyparse         MYSQLparse

#define yylex           MYSQLlex

 

  这里的MYSQLlex也就是本文的重点,即MYSQL自己的词法分析程序。源码版本5.1.48。源码太长,贴不上来,算啦..在sql_lex.cc里面。

 

  我们第一次进入词法分析,state默认值为MY_LEX_START,就是开始状态了,其实state的宏的意义可以从名称上猜个差不多,再比如MY_LEX_IDEN便是标识符。对START状态的处理伪代码如下:

 

case MY_LEX_START:

{

Skip空格

获取第一个有效字符c

state = state_map[c];

Break;

}

 

  我困惑了,这尼玛肿么出来个state_map?找到了在函数开始出有个赋值的地方:

 

uchar *state_map= cs->state_map;

  cs?!不会是反恐精英吧!!快速监视下cs为my_charset_latin1,哥了然了,原来cs是latin字符集,character set的缩写吧。那么为神马state_map可以直接决定状态?找到其赋值的地方,在init_state_maps函数中,代码如下所示:

 

/* Fill state_map with states to get a faster parser */

  for (i=0; i

  {

    if (my_isalpha(cs,i))

      state_map[i]=(uchar) MY_LEX_IDENT;

    else if (my_isdigit(cs,i))

      state_map[i]=(uchar) MY_LEX_NUMBER_IDENT;

#if defined(USE_MB) && defined(USE_MB_IDENT)

    else if (my_mbcharlen(cs, i)>1)

      state_map[i]=(uchar) MY_LEX_IDENT;

#endif

    else if (my_isspace(cs,i))

      state_map[i]=(uchar) MY_LEX_SKIP;

    else

      state_map[i]=(uchar) MY_LEX_CHAR;

  }

  state_map[(uchar)'_']=state_map[(uchar)'$']=(uchar) MY_LEX_IDENT;

  state_map[(uchar)'/'']=(uchar) MY_LEX_STRING;

  state_map[(uchar)'.']=(uchar) MY_LEX_REAL_OR_POINT;

  state_map[(uchar)'>']=state_map[(uchar)'=']=state_map[(uchar)'!']= (uchar) MY_LEX_CMP_OP;

  state_map[(uchar)'

  state_map[(uchar)'&']=state_map[(uchar)'|']=(uchar) MY_LEX_BOOL;

  state_map[(uchar)'#']=(uchar) MY_LEX_COMMENT;

  state_map[(uchar)';']=(uchar) MY_LEX_SEMICOLON;

  state_map[(uchar)':']=(uchar) MY_LEX_SET_VAR;

  state_map[0]=(uchar) MY_LEX_EOL;

  state_map[(uchar)'//']= (uchar) MY_LEX_ESCAPE;

  state_map[(uchar)'/']= (uchar) MY_LEX_LONG_COMMENT;

  state_map[(uchar)'*']= (uchar) MY_LEX_END_LONG_COMMENT;

  state_map[(uchar)'@']= (uchar) MY_LEX_USER_END;

  state_map[(uchar) '`']= (uchar) MY_LEX_USER_VARIABLE_DELIMITER;

  state_map[(uchar)'"']= (uchar) MY_LEX_STRING_OR_DELIMITER;

 

  先来看这个for循环,256应该是256个字符了,每个字符的处理应该如下规则:如果是字母,则state = MY_LEX_IDENT;如果是数字,则state = MY_LEX_NUMBER_IDENT,如果是空格,则state = MY_LEX_SKIP,剩下的全为MY_LEX_CHAR。 

       for循环之后,又对一些特殊字符进行了处理,由于我们的语句“select @@version_comment limit 1”中有个特殊字符@,这个字符的state进行了特殊处理,为MY_LEX_USER_END。

对于my_isalpha等这几个函数是如何进行判断一个字符属于什么范畴的呢?跟进去看下,发现是宏定义:

#define    my_isalpha(s, c)  (((s)->ctype+1)[(uchar) (c)] & (_MY_U | _MY_L))

Wtf,肿么又来了个ctype,c作为ctype的下标,_MY_U | _MY_L如下所示,

#define    _MY_U   01    /* Upper case */

#define    _MY_L   02    /* Lower case */

 

  ctype里面到底存放了什么?在ctype-latin1.c源文件里面,我们找到了my_charset_latin1字符集的初始值:

 

CHARSET_INFO my_charset_latin1=

{

    8,0,0,                           /* number    */

    MY_CS_COMPILED | MY_CS_PRIMARY, /* state     */

    "latin1",                        /* cs name    */

    "latin1_swedish_ci",              /* name      */

    "",                                /* comment   */

    NULL,                         /* tailoring */

    ctype_latin1,

    to_lower_latin1,

    to_upper_latin1,

    sort_order_latin1,

    NULL,           /* contractions */

    NULL,           /* sort_order_big*/

    cs_to_uni,             /* tab_to_uni   */

    NULL,           /* tab_from_uni */

    my_unicase_default, /* caseinfo     */

    NULL,           /* state_map    */

    NULL,           /* ident_map    */

    1,                  /* strxfrm_multiply */

    1,                  /* caseup_multiply  */

    1,                  /* casedn_multiply  */

    1,                  /* mbminlen   */

    1,                  /* mbmaxlen  */

    0,                  /* min_sort_char */

    255,        /* max_sort_char */

    ' ',                /* pad char      */

    0,                  /* escape_with_backslash_is_dangerous */

    &my_charset_handler,

    &my_collation_8bit_simple_ci_handler

};

 

  可以看出ctype = ctype_latin1;而ctype_latin1值为:

 

static uchar ctype_latin1[] = {

    0,

   32, 32, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 32, 32,

   32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,

   72, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,

  132,132,132,132,132,132,132,132,132,132, 16, 16, 16, 16, 16, 16,

   16,129,129,129,129,129,129,  1,  1,  1,  1,  1,  1,  1,  1,  1,

    1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1, 16, 16, 16, 16, 16,

   16,130,130,130,130,130,130,  2,  2,  2,  2,  2,  2,  2,  2,  2,

    2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2, 16, 16, 16, 16, 32,

   16,  0, 16,  2, 16, 16, 16, 16, 16, 16,  1, 16,  1,  0,  1,  0,

    0, 16, 16, 16, 16, 16, 16, 16, 16, 16,  2, 16,  2,  0,  2,  1,

   72, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,

   16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,

    1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,

    1,  1,  1,  1,  1,  1,  1, 16,  1,  1,  1,  1,  1,  1,  1,  2,

    2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,

    2,  2,  2,  2,  2,  2,  2, 16,  2,  2,  2,  2,  2,  2,  2,  2

};

 

  看到这里哥再一次了然了,这些值都是经过预计算的,第一个0是无效的,这也是为什么my_isalpha(s, c)定义里面ctype要先+1的原因。通过_MY_U和_MY_L的定义,可以知道,这些值肯定是按照相应的ASCII码的具体意义进行置位的。比如字符'A',其ASCII码为65,其实大写字母,故必然具有_MY_U,即第0位必然为1,找到ctype里面第66个(略过第一个无意义的0)元素,为129 = 10000001,显然第0位为1(右边起),说明为大写字母。写代码的人确实比较牛X,如此运用位,哥估计这辈子也想不到了,小小佩服下。State的问题点到为止了。

 

继续进行词法分析,第一个字母为s,其state = MY_LEX_IDENT(IDENTIFIER:标识符的意思),break出来,继续循环,case进入MY_LEX_IDENT分支:

 

Case MY_LEX_IDENT:

{

由s开始读,直到空格为止

If(读入的单词为关键字)

{

nextstate = MY_LEX_START;

Return tokval;        //关键字的唯一标识

}

Else

{

return IDENT_QUOTED 或者IDENT;表示为一般标识符

}

}

 

  这里SELECT肯定为关键字,至于为什么呢?下节的语法分析会讲。

 

解析完SELECT后,需要解析@@version_comment,第一个字符为@,进入START分支,state = MY_LEX_USER_END;

 

进入MY_LEX_USER_END分支,如下:

 

case MY_LEX_USER_END:        // end '@' of user@hostname

      switch (state_map[lip->yyPeek()]) {

      case MY_LEX_STRING:

      case MY_LEX_USER_VARIABLE_DELIMITER:

      case MY_LEX_STRING_OR_DELIMITER:

    break;

      case MY_LEX_USER_END:

    lip->next_state=MY_LEX_SYSTEM_VAR;

    break;

      default:

    lip->next_state=MY_LEX_HOSTNAME;

    break;

 

  哥会心的笑了,两个@符号就是系统变量吧~~,下面进入MY_LEX_SYSTEM_VAR分支

 

case MY_LEX_SYSTEM_VAR:

      yylval->lex_str.str=(char*) lip->get_ptr();

      yylval->lex_str.length=1;

      lip->yySkip();                                    // Skip '@'

      lip->next_state= (state_map[lip->yyPeek()] ==

            MY_LEX_USER_VARIABLE_DELIMITER ?

            MY_LEX_OPERATOR_OR_IDENT :

            MY_LEX_IDENT_OR_KEYWORD);

      return((int) '@');

 

  所作的操作是略过@,next_state设置为MY_LEX_IDENT_OR_KEYWORD,再之后便是解析MY_LEX_IDENT_OR_KEYWORD了,也就是version_comment了,此解析应该和SELECT解析路径一致,但不是KEYWORD。剩下的留给有心的读者了(想起了歌手经常说的一句话:大家一起来,哈哈)。

 

Mysql的词法解析的状态还是比较多的,如果细究还是需要点时间的,但这不是Mysql的重点,我就浅尝辄止了。下节会针对上面的SQL语句讲解下语法分析。

 

PS: 一直想好好学习下Mysql,总是被这样或那样的事耽误,当然都是自己的原因,希望这次能走的远点.....

 

PS again:本文只代表本人的学习感悟,如有异议,欢迎指正。

 



摘自 心中无码 bitsCN.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

VMware Horizo​​​​n Client無法開啟[已修復] VMware Horizo​​​​n Client無法開啟[已修復] Feb 19, 2024 pm 11:21 PM

VMwareHorizo​​n用戶端可協助您便利地存取虛擬桌面。然而,有時虛擬桌面基礎架構可能會遇到啟動問題。本文將討論當VMwareHorizo​​n客戶端未能成功啟動時,您可以採取的解決方法。為什麼我的VMwareHorizo​​n客戶端無法開啟?在設定VDI時,如果未開啟VMWareHorizo​​n客戶端,可能會發生錯誤。請確認您的IT管理員提供了正確的URL和憑證。如果一切正常,請按照本指南中提到的解決方案解決問題。修復未開啟的VMWareHorizo​​n用戶端如果您的Windows電腦上未開啟VMW

VMware Horizo​​​​n客戶端在連線時凍結或停滯[修復] VMware Horizo​​​​n客戶端在連線時凍結或停滯[修復] Mar 03, 2024 am 09:37 AM

在使用VMWareHorizo​​n用戶端連線到VDI時,我們可能會遇到應用程式在驗證過程中凍結或連線阻塞的情況。本文將探討這個問題,並提供解決這種情況的方法。當VMWareHorizo​​n用戶端出現凍結或連線問題時,您可以採取一些措施來解決這個問題。修復VMWareHorizo​​n用戶端在連接時凍結或卡住如果VMWareHorizo​​n客戶端在Windows11/10上凍結或無法連接,請執行下面提到的解決方案:檢查網路連接重新啟動Horizo​​n用戶端檢查Horizo​​​​n伺服器狀態清除客戶端緩存修復Ho

電驢搜尋連不上伺服器如何解決 電驢搜尋連不上伺服器如何解決 Jan 25, 2024 pm 02:45 PM

解決方法:1、檢查電驢設置,確保已輸入正確的伺服器位址和連接埠號碼;2、檢查網路連接,確保電腦已連接到互聯網,並重置路由器;3、檢查伺服器是否在線,如果您的設定和網路連線都沒有問題,則需要檢查伺服器是否在線上;4、更新電驢版本,造訪電驢官方網站,下載最新版本的電驢軟體;5、尋求協助。

無法連接到RPC伺服器導致無法進入桌面的解決方法 無法連接到RPC伺服器導致無法進入桌面的解決方法 Feb 18, 2024 am 10:34 AM

RPC伺服器不可用進不了桌面怎麼辦近年來,電腦和網路已經深入到我們的生活中的各個角落。作為一種集中運算和資源共享的技術,遠端過程呼叫(RPC)在網路通訊中起著至關重要的作用。然而,有時我們可能會遇到RPC伺服器無法使用的情況,導致無法進入桌面。本文將介紹一些可能導致此問題的原因,並提供解決方案。首先,我們需要了解RPC伺服器不可用的原因。 RPC伺服器是一種

CentOS安裝fuse及CentOS安裝伺服器詳解 CentOS安裝fuse及CentOS安裝伺服器詳解 Feb 13, 2024 pm 08:40 PM

身為LINUX用戶,我們經常需要在CentOS上安裝各種軟體和伺服器,本文將詳細介紹如何在CentOS上安裝fuse和建置伺服器的過程,幫助您順利完成相關操作。 CentOS安裝fuseFuse是一個使用者空間檔案系統框架,允許非特權使用者透過自訂檔案系統實現對檔案系統的存取和操作,在CentOS上安裝fuse非常簡單,只需按照以下步驟操作:1.開啟終端,以root用戶登入。 2.使用下列指令安裝fuse軟體包:```yuminstallfuse3.確認安裝過程中的提示,輸入`y`繼續。 4.安裝完

如何將Dnsmasq設定為DHCP中繼伺服器 如何將Dnsmasq設定為DHCP中繼伺服器 Mar 21, 2024 am 08:50 AM

DHCP中繼的作用是將接收到的DHCP封包轉送到網路上的另一個DHCP伺服器,即使這兩台伺服器位於不同的子網路中。透過使用DHCP中繼,您可以實現在網路中心部署集中式的DHCP伺服器,並利用它為所有網路子網路/VLAN動態分配IP位址。 Dnsmasq是一種常用的DNS和DHCP協定伺服器,可設定為DHCP中繼伺服器,以協助管理網路中的動態主機設定。在本文中,我們將向您展示如何將dnsmasq配置為DHCP中繼伺服器。內容主題:網路拓樸在DHCP中繼上設定靜態IP位址集中式DHCP伺服器上的D

用PHP建構IP代理伺服器的最佳實務指南 用PHP建構IP代理伺服器的最佳實務指南 Mar 11, 2024 am 08:36 AM

在網路資料傳輸中,IP代理伺服器扮演著重要的角色,能夠幫助使用者隱藏真實IP位址,保護隱私、提升存取速度等。在本篇文章中,將介紹如何用PHP建立IP代理伺服器的最佳實務指南,並提供具體的程式碼範例。什麼是IP代理伺服器? IP代理伺服器是位於使用者與目標伺服器之間的中間伺服器,它可作為使用者與目標伺服器之間的中轉站,將使用者的請求和回應轉發。透過使用IP代理伺服器

PHP MQTT客戶端開髮指南 PHP MQTT客戶端開髮指南 Mar 27, 2024 am 09:21 AM

MQTT(MessageQueuingTelemetryTransport)是一種輕量級的訊息傳輸協議,通常用於物聯網設備之間的通訊。 PHP是一種常用的伺服器端程式語言,可以用來開發MQTT客戶端。本文將介紹如何使用PHP開發MQTT客戶端,並包含以下內容:MQTT協定的基本概念PHPMQTT客戶端程式庫的選取和使用實例:使用PHPMQTT客戶端發布和

See all articles