RubyのMysql2の各種情報（バージョン情報やオプション）を取得する方法

# 背景

Ruby on Rails 等で MySQLを利用している時、低レイヤーの根深い問題や、よくわからないエラーに遭遇したことはありますでしょうか？

RubyでMySQLを利用する際によく使われるライブラリである `Mysql2` では、C拡張を利用して `libmysqlclient` とリンクし、データベースサーバ上の `mysqld` に対してアクセスしている都合上、複数の概念に対してのバージョン情報やオプションを扱う必要があります。

これらのバージョン情報は、問題の原因究明に重要であるため、取得方法を知っておくことで、効率的な作業が可能になります。自分が調べる時は勿論、他の人に手伝ってもらう時にも重要なヒントになります。

# TL; DR

以下のようなデータを記録しておくと、調査が捗ります。

```ruby
# Railsの場合の Mysql2::Client のオブジェクトの取得方法の例
client = ActiveRecord::Base.connection.raw_connection
# 自前の場合の Mysql2::Client のオブジェクトの作成方法の例
client = Mysql2::Client.new(host: 'localhost', username: 'root')

puts JSON.pretty_generate(
  'RUBY_DESCRIPTION' =>  RUBY_DESCRIPTION,            # Rubyの詳細
  'Mysql2::VERSION' =>  Mysql2::VERSION,              # gemのバージョン
  'Mysql2::Client#info' => client.info,               # libmysqlclient のバージョン
  'Mysql2::Client#server_info' => client.server_info, # 接続先の MySQL Server のバージョン
)

# 接続に関するオプション (おまけ)
puts JSON.pretty_generate(client.query_options.reject { |k, _v| k == :password })  # 他にも重要な情報はあるが、最低限、パスワードは取り除く
```

出力例

```json
{
  "RUBY_DESCRIPTION": "ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-darwin18]",
  "Mysql2::VERSION": "0.4.10",
  "Mysql2::Client#info": {
    "id": 80016,
    "version": "8.0.16",
    "header_version": "8.0.16"
  },
  "Mysql2::Client#server_info": {
    "id": 50637,
    "version": "5.6.37"
  }
}
```

# 各種バージョン

## Rubyの詳細情報

`ruby -v` の詳細が記述されます。

https://docs.ruby-lang.org/ja/latest/method/Object/c/RUBY_DESCRIPTION.html

## gem のバージョン

gem のバージョンは、以下の Mysql2 自体のバージョン `Mysql2::VERSION` になります。

https://github.com/brianmario/mysql2

コードは以下のような形になっています。

https://github.com/brianmario/mysql2/blob/982dbdb1d521c668547cfd4ab927558c689b8bfd/lib/mysql2/version.rb#L2

```ruby
module Mysql2
  VERSION = "0.5.3".freeze
end
```

## libmysqlclient のバージョン

Mysql2 は、独自に MySQLサーバ との通信プロトコルを実装しているわけではなく、 MySQLの提供している、Cで書かれたクライアントライブラリを利用してアクセスしています。
この時のライブラリのバージョンによって、微妙な挙動の差異が発生する場合があるので、この情報も重要です。

Mysql2 では、以下のようにC拡張で `Mysql2::Client#info` が実装されています。

https://github.com/brianmario/mysql2/blob/f8560c551bf1999baf7df43290e8f89471e77af4/ext/mysql2/client.c#L948-L968

```c
  version = rb_str_new2(mysql_get_client_info());
```

これが呼び出している `mysql_get_client_info()` が何を返すかは、MySQLのドキュメントに記述されています。

https://dev.mysql.com/doc/refman/5.6/en/mysql-get-client-info.html

> Returns a string that represents the MySQL client library version (for example, "5.6.48").

ということで、クライアントライブラリ `libmysqlclient` のバージョンが返却されます。

## 接続先の MySQL Server のバージョン

問題が発生した時は、クライアントバージョンは勿論ですが、相手側のサーババージョンも気になるところです。

Mysql2 では、以下のようにC拡張で `Mysql2::Client#server_info` が実装されています。

https://github.com/brianmario/mysql2/blob/f8560c551bf1999baf7df43290e8f89471e77af4/ext/mysql2/client.c#L970-L994

```c
  server_info = rb_str_new2(mysql_get_server_info(wrapper->client));
```

これが呼び出している `mysql_get_server_info()` が何を返すかは、MySQLのドキュメントに記述されています。

https://dev.mysql.com/doc/refman/5.6/en/mysql-get-server-info.html

> Returns a string that represents the MySQL server version (for example, "5.6.48").

ということで、接続先の MySQL サーバのバージョンが返却されます。

## 接続に関するオプション

MySQLのサーバ側で設定されているオプションも勿論大切ですが、各クライアント（コネクション）毎に維持されているオプションも、細かい挙動を知る上では重要になってきます。

Mysql2 では、以下のようにインスタンス変数へのアクセッサーが生やされて、 `Mysql2::Client#query_options` があるので、それを利用する形が良いと思われます。

**※ このアクセッサーにはパスワードを含む情報が格納されているので、情報共有の際には、それをしっかり取り除くことを留意しておいてください。**

https://github.com/brianmario/mysql2/blob/98304cbbc0c9a964c833474a06f55c433ec26432/lib/mysql2/client.rb#L3

# 参考

https://github.com/brianmario/mysql2
https://dev.mysql.com/doc/refman/5.6/en/c-api-server-client-versions.html