maybe daily dev notes

私の開発日誌

MariaDBコントリビューション録その3

前回のあらすじ

3つ目のIssueは強敵だったが、新しく覚えた「perrorを手がかりに調べる」技でどうにか解決した。__builtin_expect も仲間に加わり、tmokmssの冒険はまだまだ続く。

tmokmss.hatenablog.com

今回のIssue

[MDEV-24582] INSERT silently truncates too long value for a virtual column without warnings or errors - Jira

Generated columnの文字列長が長すぎる場合、エラーも警告もなくTruncateされてしまうというバグ。

まずは再現

一旦DockerでMariaDBを立て、同クエリを打ってみる。

version: '3.8'
services:
  mysql:
    image: "mariadb:5.5"
    ports:
      - "3306:3306"
    volumes:
      - "mysql_data:/var/lib/mysql"
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_USER: admin
      MYSQL_PASSWORD: password
volumes:
  mysql_data:
CREATE TABLE t1 (a VARCHAR(3), v VARCHAR(3) AS (CONCAT('x-',a)) VIRTUAL);
INSERT INTO t1 (a) VALUES ('foo');

vx-foo になるので VARCHAR(3)の文字数を超過するが、たしかにエラーもなく INSERT に成功した。x-f と勝手にTruncateされる模様。

ちなみに同じクエリをMySQL 5.7に対して打つと、以下のエラーが帰ってくる。

Data too long for column 'v' at row 1

とりあえず、Issueに書いてあることは再現できた。

二分探査でバグ発生コミットを特定

と、いうのが定石なのだが、今回はうまくいかなかった。以下に顛末を記す。

二分探査するには、まずバグがないコミットとあるコミットを見つける必要がある。あるコミットは先程再現して見つかっているので、バグがないコミットの方を探そう。このため、適当にさかのぼってクエリを実行してみる。

5.5はダメなので、5.4以下を探していこう。ところで実はMariaDB 5.4というのは存在しない。経緯は謎だが、欠番となっている。このため、5.3から試していく。

調べるとこの人が5.3と5.2のイメージを作ってくれているので、利用しよう。 ちなみにさっきからDockerを使っているのは、MariaDBの昔のGitコミットではいつものビルドコマンドが使えず、必要なビルド方法を調べるのが面倒だったため。粗い検証方法ではあるが、それなりの参考にはなるはず。

結果的には、5.3でも5.2でも同様にバグが再現した。5.1はイメージが見つからず試せていないが、あとから知ったがそもそも virtual column は5.2から実装された機能だったらしい。

ここで疑うのは、そもそもこのバグ最初からあったんではということ。こうなるとバグの可能性があるコード片は簡単に絞り込めないため、難易度が跳ね上がる。一旦二分探査は諦めて、方針を改めよう。

MySQLのコードを参考にする

次に予想したのは、MySQLではバグが再現しないことから、MySQLでは同バグを修正したのではということ。つまり、MySQL側の修正コミットを特定できれば、それを参考にMariaDBのバグも修正できるかもしれない。

これも2分探査で見つけられるはずなので、まずはMySQLでバグが再現するコミットを見つけることを試みる。冒頭では5.7で再現しなかったので、MySQL 5.6にクエリを打つ。

ここで意外な事実が発覚。なんとvirtual columnはMySQLでは5.7から実装された機能だった。5.6では CREATE TABLE クエリに対して無情な構文エラーが返ってくる。

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 'AS (CONCAT('x-',a)) VIRTUAL)' at line 1

WikipediaによればMySQL 5.7は2015年にリリースされたものである。一方MariaDB 5.2は2010年なので、MariaDBMySQLとでは全く別の時期にリリースされたことが分かる。

つまり、2つは似たような機能ではあるものの、実態は全く別のコードで動いている可能性が高い。これだとMySQLのコードを参考にMariaDBのバグを直す方針も難しいだろう。どうする?

助けを求める

膨大なコードから手がかりを見つけることが困難なため、独力では詰みに近い。 師に指示を仰いだところ、見るべきコードが啓示された。

たしかに、今回の処理と関係してそうである。次回は、この辺りの処理をデバッガで追うことにしよう。

次回へ続く。