Heim Datenbank MySQL-Tutorial 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
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

Video Face Swap

Video Face Swap

Tauschen Sie Gesichter in jedem Video mühelos mit unserem völlig kostenlosen KI-Gesichtstausch-Tool aus!

Heiße Werkzeuge

Notepad++7.3.1

Notepad++7.3.1

Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version

SublimeText3 chinesische Version

Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1

Senden Sie Studio 13.0.1

Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6

Dreamweaver CS6

Visuelle Webentwicklungstools

SublimeText3 Mac-Version

SublimeText3 Mac-Version

Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

VMware Horizon Client kann nicht geöffnet werden [Fix] VMware Horizon Client kann nicht geöffnet werden [Fix] Feb 19, 2024 pm 11:21 PM

Mit VMware Horizon Client können Sie bequem auf virtuelle Desktops zugreifen. Manchmal kann es jedoch bei der virtuellen Desktop-Infrastruktur zu Startproblemen kommen. In diesem Artikel werden die Lösungen erläutert, die Sie ergreifen können, wenn der VMware Horizon-Client nicht erfolgreich gestartet werden kann. Warum wird mein VMware Horizon-Client nicht geöffnet? Wenn beim Konfigurieren von VDI der VMWareHorizon-Client nicht geöffnet ist, kann ein Fehler auftreten. Bitte bestätigen Sie, dass Ihr IT-Administrator die richtige URL und die richtigen Anmeldeinformationen angegeben hat. Wenn alles in Ordnung ist, befolgen Sie die in dieser Anleitung aufgeführten Lösungen, um das Problem zu beheben. Beheben Sie, dass sich der VMWareHorizon-Client nicht öffnet, wenn VMW auf Ihrem Windows-Computer nicht geöffnet wird

Der VMware Horizon-Client friert beim Herstellen einer Verbindung ein oder bleibt stehen [Fix] Der VMware Horizon-Client friert beim Herstellen einer Verbindung ein oder bleibt stehen [Fix] Mar 03, 2024 am 09:37 AM

Wenn wir über den VMWareHorizon-Client eine Verbindung zu einem VDI herstellen, kann es vorkommen, dass die Anwendung während des Authentifizierungsprozesses einfriert oder die Verbindung blockiert wird. In diesem Artikel wird dieses Problem untersucht und Möglichkeiten zur Lösung dieser Situation aufgezeigt. Wenn der VMWareHorizon-Client einfriert oder Verbindungsprobleme auftreten, können Sie einige Maßnahmen ergreifen, um das Problem zu beheben. Beheben Sie, dass der VMWareHorizon-Client beim Herstellen einer Verbindung einfriert oder hängen bleibt. Wenn der VMWareHorizon-Client unter Windows 11/10 einfriert oder keine Verbindung herstellen kann, führen Sie die unten aufgeführten Lösungen aus: Überprüfen Sie die Netzwerkverbindung. Starten Sie den Horizon-Client neu. Überprüfen Sie den Status des Horizon-Servers. Löschen Sie den Client-Cache. Beheben Sie Ho

So lösen Sie das Problem, dass die eMule-Suche keine Verbindung zum Server herstellen kann So lösen Sie das Problem, dass die eMule-Suche keine Verbindung zum Server herstellen kann Jan 25, 2024 pm 02:45 PM

Lösung: 1. Überprüfen Sie die eMule-Einstellungen, um sicherzustellen, dass Sie die richtige Serveradresse und Portnummer eingegeben haben. 2. Überprüfen Sie die Netzwerkverbindung, stellen Sie sicher, dass der Computer mit dem Internet verbunden ist, und setzen Sie den Router zurück Wenn Ihre Einstellungen kein Problem mit der Netzwerkverbindung sind, müssen Sie überprüfen, ob der Server online ist. 4. Aktualisieren Sie die eMule-Version, besuchen Sie die offizielle Website von eMule und laden Sie die neueste Version der eMule-Software herunter. 5. Suchen Sie Hilfe.

Lösung für die Unfähigkeit, eine Verbindung zum RPC-Server herzustellen und den Desktop nicht aufzurufen Lösung für die Unfähigkeit, eine Verbindung zum RPC-Server herzustellen und den Desktop nicht aufzurufen Feb 18, 2024 am 10:34 AM

Was soll ich tun, wenn der RPC-Server nicht verfügbar ist und auf dem Desktop nicht darauf zugegriffen werden kann? In den letzten Jahren sind Computer und das Internet in jeden Winkel unseres Lebens vorgedrungen. Als Technologie für zentralisierte Datenverarbeitung und gemeinsame Nutzung von Ressourcen spielt Remote Procedure Call (RPC) eine entscheidende Rolle in der Netzwerkkommunikation. Manchmal kann es jedoch vorkommen, dass der RPC-Server nicht verfügbar ist und der Desktop nicht aufgerufen werden kann. In diesem Artikel werden einige mögliche Ursachen für dieses Problem beschrieben und Lösungen bereitgestellt. Zuerst müssen wir verstehen, warum der RPC-Server nicht verfügbar ist. RPC-Server ist ein

Detaillierte Erläuterung der CentOS-Installationssicherung und des CentOS-Installationsservers Detaillierte Erläuterung der CentOS-Installationssicherung und des CentOS-Installationsservers Feb 13, 2024 pm 08:40 PM

Als LINUX-Benutzer müssen wir häufig verschiedene Software und Server unter CentOS installieren. In diesem Artikel wird detailliert beschrieben, wie Sie Fuse installieren und einen Server unter CentOS einrichten, damit Sie die entsprechenden Vorgänge reibungslos abschließen können. Die Installation von FuseFuse unter CentOS ist ein User-Space-Dateisystem-Framework, mit dem unprivilegierte Benutzer über ein benutzerdefiniertes Dateisystem auf das Dateisystem zugreifen und es bedienen können. Die Installation von Fuse unter CentOS ist sehr einfach. Befolgen Sie einfach die folgenden Schritte: 1. Öffnen Sie das Terminal und melden Sie sich an Root-Benutzer. 2. Verwenden Sie den folgenden Befehl, um das Sicherungspaket zu installieren: „yuminstallfuse3“. Bestätigen Sie die Eingabeaufforderungen während des Installationsvorgangs und geben Sie „y“ ein, um fortzufahren. 4. Installation abgeschlossen

So konfigurieren Sie Dnsmasq als DHCP-Relay-Server So konfigurieren Sie Dnsmasq als DHCP-Relay-Server Mar 21, 2024 am 08:50 AM

Die Rolle eines DHCP-Relays besteht darin, empfangene DHCP-Pakete an einen anderen DHCP-Server im Netzwerk weiterzuleiten, auch wenn sich die beiden Server in unterschiedlichen Subnetzen befinden. Durch die Verwendung eines DHCP-Relays können Sie einen zentralen DHCP-Server im Netzwerkzentrum bereitstellen und ihn verwenden, um allen Netzwerksubnetzen/VLANs dynamisch IP-Adressen zuzuweisen. Dnsmasq ist ein häufig verwendeter DNS- und DHCP-Protokollserver, der als DHCP-Relay-Server konfiguriert werden kann, um die Verwaltung dynamischer Hostkonfigurationen im Netzwerk zu unterstützen. In diesem Artikel zeigen wir Ihnen, wie Sie dnsmasq als DHCP-Relay-Server konfigurieren. Inhaltsthemen: Netzwerktopologie Konfigurieren statischer IP-Adressen auf einem DHCP-Relay D auf einem zentralen DHCP-Server

Best-Practice-Leitfaden zum Erstellen von IP-Proxyservern mit PHP Best-Practice-Leitfaden zum Erstellen von IP-Proxyservern mit PHP Mar 11, 2024 am 08:36 AM

Bei der Netzwerkdatenübertragung spielen IP-Proxyserver eine wichtige Rolle. Sie helfen Benutzern dabei, ihre echten IP-Adressen zu verbergen, ihre Privatsphäre zu schützen und die Zugriffsgeschwindigkeit zu verbessern. In diesem Artikel stellen wir den Best-Practice-Leitfaden zum Aufbau eines IP-Proxyservers mit PHP vor und stellen spezifische Codebeispiele bereit. Was ist ein IP-Proxyserver? Ein IP-Proxyserver ist ein Zwischenserver zwischen dem Benutzer und dem Zielserver. Er fungiert als Übertragungsstation zwischen dem Benutzer und dem Zielserver und leitet die Anforderungen und Antworten des Benutzers weiter. Durch die Verwendung eines IP-Proxyservers

PHP MQTT-Client-Entwicklungshandbuch PHP MQTT-Client-Entwicklungshandbuch Mar 27, 2024 am 09:21 AM

MQTT (MessageQueuingTelemetryTransport) ist ein leichtes Nachrichtenübertragungsprotokoll, das häufig für die Kommunikation zwischen IoT-Geräten verwendet wird. PHP ist eine häufig verwendete serverseitige Programmiersprache, die zur Entwicklung von MQTT-Clients verwendet werden kann. In diesem Artikel wird die Verwendung von PHP zum Entwickeln eines MQTT-Clients vorgestellt und der folgende Inhalt umfasst: Grundkonzepte des MQTT-Protokolls Auswahl und Verwendungsbeispiele der PHPMQTT-Clientbibliothek: Verwendung des PHPMQTT-Clients zum Veröffentlichen und

See all articles