2017年6月28日水曜日

MySQLのauto_incrementは実質ULLONG_MAX - 2で打ち止まるはなし

TL;DR


とある事情があってInnoDBのAUTO_INCREMENT関連のコードを読んでいた時に、innobase_next_autoincの中でこんなコードになっていることに気が付いた。
 2497         if (block >= max_value
 2498             || offset > max_value
 2499             || current >= max_value
 2500             || max_value - offset <= offset) {
 2501
 2502                 next_value = max_value;
 2503         } else {
いわゆるフツーのオートインクリメント処理の前段に入るチェックで、現在のauto_incrementの値にauto_increment_incrementの値を足してもデータ型の限界を超えないかとかそんなチェックをしているんだけれど、どうやら 足した結果データ型の限界を超える場合はデータ型の限界値を返す らしい。
データ型の最大値が常に返るから、ぶち抜いた時のエラーは ERROR 1062 (23000): Duplicate entry '..' for key 'PRIMARY' になるのか。ふむふむ。
しかし流れで読んでいたhandler::update_auto_incrementの中にちょっと気になる記述を見つけた。
3589       if (nr == ULLONG_MAX)
3590         DBUG_RETURN(HA_ERR_AUTOINC_READ_FAILED);  // Mark failure
はて。
  • 「払い出されたauto_incrementの値がULLONG_MAXだったらHA_ERR_AUTOINC_READ_FAILED」
ということは
  • 「現在のauto_increment値がUULONG_MAX - 1の時にauto_incrementの払い出しをしようとすると、UULONG_MAXが払い出されるから(データ型上はまだ入るけど)エラー」
なのか?
mysql57 13> INSERT INTO t1 VALUES (18446744073709551614, 'BIGINT MAX - 1');
Query OK, 1 row affected (0.00 sec)

mysql57 13> SELECT * FROM t1;
+----------------------+----------------+
| num                  | val            |
+----------------------+----------------+
| 18446744073709551614 | BIGINT MAX - 1 |
+----------------------+----------------+
1 row in set (0.00 sec)

mysql57 13> INSERT INTO t1 VALUES (NULL, 'NEXT');
ERROR 1467 (HY000): Failed to read auto-increment value from storage engine

mysql57 13> SELECT * FROM t1;
+----------------------+----------------+
| num                  | val            |
+----------------------+----------------+
| 18446744073709551614 | BIGINT MAX - 1 |
+----------------------+----------------+
1 row in set (0.00 sec)
おおおホントだ入らない。 しかしデータ型としてはまだもう1つ行けるので、直接値を指定すれば入る。
mysql57 13> INSERT INTO t1 VALUES (18446744073709551615, 'BIGINT MAX');
Query OK, 1 row affected (0.01 sec)

mysql57 13> SELECT * FROM t1;
+----------------------+----------------+
| num                  | val            |
+----------------------+----------------+
| 18446744073709551614 | BIGINT MAX - 1 |
| 18446744073709551615 | BIGINT MAX     |
+----------------------+----------------+
2 rows in set (0.00 sec)
しかもこの時も、auto_incrementの値がBIGINT UNSIGNEDの最大値の18446744073709551615以上になるから18446744073709551615が返ってDuplicate Entryエラーになる前に、払い出しそのものに失敗した扱いでER_AUTOINC_READ_FAILEDが返るのね。。
18446744073709551614と18446744073709551615の差が生死を分けることはないと思いますが、みなさまご注意ください。

0 件のコメント :

コメントを投稿