2017/10/23開催のMyNA(日本MySQLユーザ会)会で話題になった**「MySQL 8.0で予約語が増えた」**問題。
壇上の@yoku0825さんからは「若手の方よろしく」とのことだったので、おじさんは少し変化球で行きます。
- MySQL最新情報セミナー 2017年10月 in 東京(togetterまとめ)
- MyNA(日本MySQLユーザ会)会 2017年10月(togetterまとめ)
1. MySQLのキーワード
SQLのコマンド・関数・演算子などとして使われる単語(または「_」で連結された複数の単語)です。
- 9.3 Keywords and Reserved Words(MySQL 8.0 Reference Manual)
- 9.3 Keywords and Reserved Words(MySQL 5.7 Reference Manual)
リンク先では「significance in SQL」と表現されています。
それぞれ、当該バージョンのキーワード表と前バージョンからの差分を見ることができます。
実は意識していなかったのですが、↑のリファレンスマニュアルにある通り、予約語と非予約語があります。
2. 予約語
先のリファレンスマニュアルのページの下方にキーワード表がありますが、「(R)」が付いているのが予約語です。
※注釈リンクの先に「added in 8.0.x (reserved)」などと記述されているものも含みます。
たとえば、MySQL 8.0で新たに予約語となったキーワードに「RANK」(関数)がありますが、このキーワードはDB名、テーブル名、カラム名などの識別子としてそのまま使うと怒られます。
$ mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 7
Server version: 8.0.3-rc-log MySQL Community Server (GPL)
Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> CREATE DATABASE hoge;
Query OK, 1 row affected (0.02 sec)
mysql> USE hoge;
Database changed
mysql> CREATE TABLE rank (id INT PRIMARY KEY, val INT);
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'rank (id INT PRIMARY KEY, val INT)' at line 1
バッククォートで囲めば大丈夫です。
mysql> CREATE TABLE `rank` (id INT PRIMARY KEY, val INT);
Query OK, 0 rows affected (0.02 sec)
また、「DB名.」の後ろのテーブル名などでもOKです。
mysql> DROP TABLE `rank`;
Query OK, 0 rows affected (0.01 sec)
mysql> CREATE TABLE hoge.rank (id INT PRIMARY KEY, val INT);
Query OK, 0 rows affected (0.01 sec)
最初からクォートなしで通すことはできませんが、この形ならDB名に使っても大丈夫です。
mysql> CREATE DATABASE `rank`;
Query OK, 1 row affected (0.01 sec)
mysql> CREATE TABLE rank.rank (id INT PRIMARY KEY, val INT);
Query OK, 0 rows affected (0.02 sec)
#3. 非予約語
ここを意識したことがなかったのですが、非予約語はテーブル名などの識別子として、そのまま使っても大丈夫です。
※先の表で「(R)」が付いておらず、かつ注釈リンク先に「(reserved)」などと記述されていないキーワードはこちらにあたります。
mysql> CREATE TABLE role (id INT PRIMARY KEY, val INT);
Query OK, 0 rows affected (0.02 sec)
実は、ユーザ会会で「死ぬ」とネタにされた、「ROLE」は通ります。
※注釈リンク先に「ROLE: became nonreserved in 8.0.1」と書かれているので(addedではなくてbecame)、おそらく8.0.0で予約語化され、8.0.1で非予約語のキーワードになったのでしょうね。
文法から、キーワードとして使っているのか識別子として使っているのが明確に判断ができるものは、こちらの扱いでも大丈夫ですね。
なんとなく「キーワードと被っていても識別子に使えるワードがある」ことには気づいていたのですが、マニュアルを隅から隅まで読んでいなかったので、このページの存在を教えてもらえて良かったです。
4. まとめ
「キーワードになっただけなら問題ないけど、予約語には注意してね」ということです。
「何をいまさら」「当たり前だろ」と思うかもしれませんが、バージョンアップで予約語が増えた場合、クォートで囲んでおらずドットで繋げてもいない識別子が予約語にバッティングすると、それまで使っていたアプリケーションや作業用のSQLが死にます。
おそらく、このような事態に遭遇すると、最初は何が起きたのかわからないことが多いと思います。
リファレンスマニュアルには、
At some point, you might upgrade to a higher version, so it is a good idea to have a look at future reserved words, too. You can find these in the manuals that cover higher versions of MySQL. Most of the reserved words in the table are forbidden by standard SQL as column or table names (for example, GROUP). A few are reserved because MySQL needs them and uses a yacc parser.
なんて書かれていますが、将来(次の次あたり)を見通すのはたぶん難しいと思います。
…ので、死を避けるために普段からクォートで囲むことを意識したほうが良いです(という話でした)。
※ということを、yoku0825さんに教えていただきました。
ただ、リファレンスマニュアルにある、
In addition, _FILENAME is reserved.
については、色々試してみましたが(「_FILENAME」というテーブルを作ってみたり、「○○_FILENAME」をやってみたり、ファイル名じゃないけど「_RANK」してみたり、はたまたデータディレクトリに存在するファイル名に置き換えてみたり)よくわかりませんでした。
5. おわりに
MySQLユーザ会会、面白かったです。
遠方からの参加ゆえ、途中退出せざるをえなかったのが残念ですが。
ぎりぎりまで居てもよかったのですが、案の定、帰る途中に山手線を品川駅で止めて遅らせた人が…。
もっとも、新幹線(終電)も東京駅での連絡の関係で同じだけ遅れて品川駅にやってきましたけど。
※2017/10/23 MyNA会ネタ第2弾です。
【おまけ】
MySQL 8.0、5.7関連投稿記事へのリンクを集めました。