Rumah pangkalan data tutorial mysql Mysql源码学习――词法分析MYSQLlex_MySQL

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

Jun 01, 2016 pm 01:44 PM
pelanggan pelayan rancangan

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
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

Video Face Swap

Video Face Swap

Tukar muka dalam mana-mana video dengan mudah menggunakan alat tukar muka AI percuma kami!

Alat panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Pelanggan VMware Horizon tidak boleh dibuka [Betulkan] Pelanggan VMware Horizon tidak boleh dibuka [Betulkan] Feb 19, 2024 pm 11:21 PM

VMware Horizon Client membantu anda mengakses desktop maya dengan mudah. Walau bagaimanapun, kadangkala infrastruktur desktop maya mungkin mengalami masalah permulaan. Artikel ini membincangkan penyelesaian yang boleh anda ambil apabila klien VMware Horizon gagal dimulakan dengan jayanya. Mengapa klien VMware Horizon saya tidak boleh dibuka? Apabila mengkonfigurasi VDI, ralat mungkin berlaku jika klien VMWareHorizon tidak dibuka. Sila sahkan bahawa pentadbir IT anda telah memberikan URL dan bukti kelayakan yang betul. Jika semuanya baik-baik saja, ikuti penyelesaian yang dinyatakan dalam panduan ini untuk menyelesaikan isu tersebut. Betulkan Klien VMWareHorizon Tidak Dibuka Jika VMW tidak dibuka pada komputer Windows anda

Pelanggan VMware Horizon membeku atau terhenti semasa menyambung [Betulkan] Pelanggan VMware Horizon membeku atau terhenti semasa menyambung [Betulkan] Mar 03, 2024 am 09:37 AM

Apabila menyambung ke VDI menggunakan klien VMWareHorizon, kami mungkin menghadapi situasi di mana aplikasi membeku semasa pengesahan atau blok sambungan. Artikel ini akan meneroka isu ini dan menyediakan cara untuk menyelesaikan situasi ini. Apabila klien VMWareHorizon mengalami masalah pembekuan atau sambungan, terdapat beberapa perkara yang boleh anda lakukan untuk menyelesaikan isu tersebut. Betulkan klien VMWareHorizon membeku atau tersekat semasa menyambung Jika klien VMWareHorizon membeku atau gagal menyambung pada Windows 11/10, ikuti penyelesaian yang dinyatakan di bawah: Semak sambungan rangkaian Mulakan semula klien Horizon Semak status pelayan Horizon Kosongkan cache klien Betulkan Ho

Bagaimana untuk menyelesaikan masalah bahawa carian eMule tidak dapat menyambung ke pelayan Bagaimana untuk menyelesaikan masalah bahawa carian eMule tidak dapat menyambung ke pelayan Jan 25, 2024 pm 02:45 PM

Penyelesaian: 1. Semak tetapan eMule untuk memastikan anda telah memasukkan alamat pelayan dan nombor port yang betul 2. Semak sambungan rangkaian, pastikan komputer disambungkan ke Internet, dan tetapkan semula penghala; adalah dalam talian. Jika tetapan anda adalah Jika tiada masalah dengan sambungan rangkaian, anda perlu menyemak sama ada pelayan dalam talian 4. Kemas kini versi eMule, lawati tapak web rasmi eMule, dan muat turun versi terkini perisian eMule; 5. Dapatkan bantuan.

Penyelesaian kepada ketidakupayaan untuk menyambung ke pelayan RPC dan ketidakupayaan untuk memasuki desktop Penyelesaian kepada ketidakupayaan untuk menyambung ke pelayan RPC dan ketidakupayaan untuk memasuki desktop Feb 18, 2024 am 10:34 AM

Apakah yang perlu saya lakukan jika pelayan RPC tidak tersedia dan tidak boleh diakses pada desktop Dalam beberapa tahun kebelakangan ini, komputer dan Internet telah menembusi setiap sudut kehidupan kita. Sebagai teknologi untuk pengkomputeran berpusat dan perkongsian sumber, Panggilan Prosedur Jauh (RPC) memainkan peranan penting dalam komunikasi rangkaian. Walau bagaimanapun, kadangkala kita mungkin menghadapi situasi di mana pelayan RPC tidak tersedia, mengakibatkan ketidakupayaan untuk memasuki desktop. Artikel ini akan menerangkan beberapa kemungkinan punca masalah ini dan memberikan penyelesaian. Pertama, kita perlu memahami mengapa pelayan RPC tidak tersedia. Pelayan RPC ialah a

Penjelasan terperinci mengenai fius pemasangan CentOS dan pelayan pemasangan CentOS Penjelasan terperinci mengenai fius pemasangan CentOS dan pelayan pemasangan CentOS Feb 13, 2024 pm 08:40 PM

Sebagai pengguna LINUX, kami selalunya perlu memasang pelbagai perisian dan pelayan pada CentOS Artikel ini akan memperkenalkan secara terperinci cara memasang fius dan menyediakan pelayan pada CentOS untuk membantu anda menyelesaikan operasi yang berkaitan dengan lancar. CentOS installation fuseFuse ialah rangka kerja sistem fail ruang pengguna yang membenarkan pengguna yang tidak mempunyai hak untuk mengakses dan mengendalikan sistem fail melalui sistem fail tersuai Memasang fius pada CentOS adalah sangat mudah, cuma ikuti langkah berikut: 1. Buka terminal dan Log masuk sebagai pengguna akar. 2. Gunakan arahan berikut untuk memasang pakej fius: ```yuminstallfuse3. Sahkan gesaan semasa proses pemasangan dan masukkan `y` untuk meneruskan. 4. Pemasangan selesai

Bagaimana untuk mengkonfigurasi Dnsmasq sebagai pelayan geganti DHCP Bagaimana untuk mengkonfigurasi Dnsmasq sebagai pelayan geganti DHCP Mar 21, 2024 am 08:50 AM

Peranan geganti DHCP adalah untuk memajukan paket DHCP yang diterima ke pelayan DHCP lain pada rangkaian, walaupun kedua-dua pelayan berada pada subnet yang berbeza. Dengan menggunakan geganti DHCP, anda boleh menggunakan pelayan DHCP berpusat di pusat rangkaian dan menggunakannya untuk menetapkan alamat IP secara dinamik kepada semua subnet/VLAN rangkaian. Dnsmasq ialah pelayan protokol DNS dan DHCP yang biasa digunakan yang boleh dikonfigurasikan sebagai pelayan geganti DHCP untuk membantu mengurus konfigurasi hos dinamik dalam rangkaian. Dalam artikel ini, kami akan menunjukkan kepada anda cara mengkonfigurasi dnsmasq sebagai pelayan geganti DHCP. Topik Kandungan: Topologi Rangkaian Mengkonfigurasi Alamat IP Statik pada Geganti DHCP D pada Pelayan DHCP Berpusat

Panduan Amalan Terbaik untuk Membina Pelayan Proksi IP dengan PHP Panduan Amalan Terbaik untuk Membina Pelayan Proksi IP dengan PHP Mar 11, 2024 am 08:36 AM

Dalam penghantaran data rangkaian, pelayan proksi IP memainkan peranan penting, membantu pengguna menyembunyikan alamat IP sebenar mereka, melindungi privasi dan meningkatkan kelajuan akses. Dalam artikel ini, kami akan memperkenalkan panduan amalan terbaik tentang cara membina pelayan proksi IP dengan PHP dan menyediakan contoh kod khusus. Apakah pelayan proksi IP? Pelayan proksi IP ialah pelayan perantaraan yang terletak di antara pengguna dan pelayan sasaran Ia bertindak sebagai stesen pemindahan antara pengguna dan pelayan sasaran, memajukan permintaan dan respons pengguna. Dengan menggunakan pelayan proksi IP

Panduan Pembangunan Klien PHP MQTT Panduan Pembangunan Klien PHP MQTT Mar 27, 2024 am 09:21 AM

MQTT (MessageQueuingTelemetryTransport) ialah protokol penghantaran mesej ringan yang biasa digunakan untuk komunikasi antara peranti IoT. PHP ialah bahasa pengaturcaraan sisi pelayan yang biasa digunakan yang boleh digunakan untuk membangunkan klien MQTT. Artikel ini akan memperkenalkan cara menggunakan PHP untuk membangunkan klien MQTT dan memasukkan kandungan berikut: Konsep asas protokol MQTT Pemilihan dan contoh penggunaan perpustakaan klien PHPMQTT: Menggunakan klien PHPMQTT untuk menerbitkan dan

See all articles