MySQLのリファレンスマニュアルにあるEBNFは全てを表現していないので注意が必要

タイトルの通りです。

前提

MySQLのリファレンスマニュアルの構文のページにはEBNFが書いてあります。

例としてCREATE TABLE文のページを用意しました。ページの上のほうにEBNFによって定義された文法が書かれています。 dev.mysql.com

EBNFに定義されていない書きかた

前のセクションでCREATE TABLE文のリファレンスマニュアルを参照しました。このセクションではこのCREATE TABLE文の文法の話をします。ですのでこれ以降はリファレンスマニュアルのEBNFを用意いただき一緒に読んでもらうと楽しめると思います。

次のSQLはMySQL8.0.33で正常にパースできます。

CREATE TABLE user (
  id BIGINT
) DEFAULT CHARSET = utf8mb4;

なので文法としては正しいということが分かります。しかしEBNFだけを見るとDEFAULT CHARSET = utf8mb4は当てはまる文が存在しません。

デフォルト文字セットを定義できるのは[DEFAULT] CHARACTER SET [=] charset_nameの形式に合う文のみです。SQLとEBNFを見比べるとCHARSETの部分が異なることが分かります。

ということで例にあげたSQLは何故かパースできるけどリファレンスマニュアルにあるEBNF上では正しくない文ということになります。

Yaccでの定義を見てみる

前セクションでEBNFで定義されていないけどなぜかパースできるSQLを紹介しました。

では実際にMySQLはどのような定義のもとで構文解析しているのか深堀りしてみましょう。

今回注目するのはリファレンスマニュアルのEBNFには存在しない定義であるDEFAULT CHARSET = utf8mb4の部分にします。

MySQLはパーサジェネレータとしてBisonを用いています。なのでyaccファイルを見ると構文の定義が分かりそうです。読んでみましょう。

CREATE TABLE文はこの場所から定義が始まっています。 https://github.com/mysql/mysql-server/blob/8.0/sql/sql_yacc.yy#L3395

今回注目しているのはリファレンスマニュアルにtable_optionsとして定義されている場所なのでそれらしい文字を更に追いかけます。

そんな調子で深堀りをしていくと今回注目しているDEFAULT CHARSETを指定する箇所は以下の場所であることが分かります。

default_charset:
          opt_default character_set opt_equal charset_name { $$ = $4; }
        ;

コードは以下URLから引用。

https://github.com/mysql/mysql-server/blob/8.0/sql/sql_yacc.yy#L6903

どうやらcharacter_setに何故かパースできる部分の答えがありそうです。

character_set:
          CHAR_SYM SET_SYM
        | CHARSET
        ;

コードは以下URLから引用。

https://github.com/mysql/mysql-server/blob/8.0/sql/sql_yacc.yy#L7600C1-L7603C10

このコードを見るとcharacter_setCHARACTER SETCHARSETと書くことができると定義されています。

ということで CHARSETCHARACTER SETと同じように定義されているので正常にパースされたということです。別名になっているとも言えるでしょう。

リファレンスマニュアルのEBNFには定義されていないけど。

実は定義されている

ということで前セクションでEBNFとして定義されていないけど何故かパースできる箇所についてパーサの定義を調べることで結論を探しました。

結論としてはEBNFとしては定義されていないCHARSETCHARACTER SETと同じように振る舞うということが分かりました。

ここでもう一度リファレンスマニュアルを読むと以下のような記載が見つかります。

CHARSET is a synonym for CHARACTER SET.

https://dev.mysql.com/doc/refman/8.0/en/create-table.html#create-table-options

はい。今回yaccの定義を追い掛けて見つけることができた結論が書いてあります。

ここでタイトル回収です。MySQLのリファレンスマニュアルにあるEBNFは全てを表現していません。

なのでリファレンスマニュアルをしっかり最後まで読み込みましょう。EBNFに定義されていない文法もあります。

まとめ

MySQLのリファレンスマニュアルのEBNFは許容する全ての文法を表現していない可能性があります。

なので参照するときはリファレンスマニュアルの下のほうを最後まで読みましょう。

全てを表現していないEBNFを冒頭に載せるの、リファレンスマニュアルとしてどうなの?とはちょっと思う。