GA

2018/10/25

MySQL 8.0.13でカラム定義のDEFAULTに関数が指定できるようになった

みんなだいすき DEFAULT がついに関数を指定できるようになった。
8.0.12とそれ以前はリテラルのみが指定可能、例外として TIMESTAMP, DATETIME 型の CURRENT_TIMESTAMP のみだった。
記法は .. DEFAULT ( expression ) で、 DEFAULT のあとに括弧を入れてから関数なり表現なりを書く。
The MySQL 8.0.13 Maintenance Release is Generally Available | MySQL Server Blog に書いてある↓をそのまま試そうとしても、括弧が抜けているので通らない。。
mysql80 40> CREATE TABLE t2 (a BINARY(16) DEFAULT uuid_to_bin(uuid()));
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 'uuid_to_bin(uuid()))' at line 1

mysql80 40> CREATE TABLE t2 (a BINARY(16) DEFAULT (uuid_to_bin(uuid())));
Query OK, 0 rows affected (0.11 sec)
( ´-`).oO(MySQL Server Teamのブログではまれにだがよくあること
mysql80 40> CREATE TABLE t1 (num serial, val varchar(32), val_len int NOT NULL DEFAULT (CHARACTER_LENGTH(val)));
Query OK, 0 rows affected (0.10 sec)

mysql80 40> INSERT INTO t1 (num, val) VALUES (1, 'one');
Query OK, 1 row affected (0.07 sec)

mysql80 40> SELECT * FROM t1;
+-----+------+---------+
| num | val  | val_len |
+-----+------+---------+
|   1 | one  |       3 |
+-----+------+---------+
1 row in set (0.00 sec)
しかしこれ迂闊にNOT NULLなカラムのデフォルトにNULLアンセーフな関数とNULLABLEなカラムの値を組み合わせるとおかしなことになった。
mysql80 40> INSERT INTO t1 (num, val) VALUES (2, NULL);
Query OK, 1 row affected, 1 warning (0.06 sec)

mysql80 40> SHOW WARNINGS;
+-------+------+---------------------------------+
| Level | Code | Message                         |
+-------+------+---------------------------------+
| Error | 1048 | Column 'val_len' cannot be null |
+-------+------+---------------------------------+
1 row in set (0.00 sec)

mysql80 40> SELECT * FROM t1;
+-----+------+---------+
| num | val  | val_len |
+-----+------+---------+
|   1 | one  |       3 |
|   2 | NULL |       0 |
+-----+------+---------+
2 rows in set (0.00 sec)
CHARACTER_LENGTH(NULL)NULL なので、NOT NULLな val_len のデフォルト値が NULL になるというなんか地獄のように矛盾した結果、INT型のフォールバック先である0に落ち着いた様子。
ただこれ STRICT_TRANS_TABLES の状態でこの動作になっちゃうのでちょっとあんまり嬉しくない(同じことをデフォルト値を使わずにやるとちゃんとエラーになるのに…)
mysql80 40> SELECT @@sql_mode;
+-----------------------------------------------------------------------------------------------------------------------+
| @@sql_mode                                                                                                            |
+-----------------------------------------------------------------------------------------------------------------------+
| ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION |
+-----------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql80 40> INSERT INTO t1 (num, val, val_len) VALUES (3, NULL, CHARACTER_LENGTH(NULL));
ERROR 1048 (23000): Column 'val_len' cannot be null
使い道があんまり思いつかない(generated columnでもいいケースがほとんど?)けれど、ブログのサンプルにもあった uuid_to_bin(uuid()) を使って時間経過順に並ぶ形式にしたUUIDをサロゲートキーにする、とかとても上手く使えそう。

0 件のコメント :

コメントを投稿