NimだけでMySQLにアクセスしたい

# 外部ライブラリに頼りたくない
NimでMySQLに接続する際は標準ライブラリの[`db_mysql`](https://nim-lang.org/docs/db_mysql.html)を使用するのが一般的である（と思う）。しかし、`db_mysql`（正確には依存先の[`mysql`](https://nim-lang.org/docs/mysql.html)）はMySQLクライアント用ライブラリを動的リンクするため、（ビルド環境ではなく）実行環境に下表のライブラリを用意する必要があるが、手間がかかるだけでなく外部ライブラリの変更に弱い。
そこで外部ライブラリに依存しない方法をとりたい。

|OS|必要ライブラリ|
|----|----|
|Linux|libmysqlclient.so|
|Windows|libmysql.dll[（参考）](https://qiita.com/6in/items/2067b71e3b1a10880bad)|
|MacOS|libmysqlclient.dylib|
備考：Nimでは外部ライブラリのインターフェースとデータ構造をnimファイル内で定義する必要があるのだが、現在のNimの`mysql`ライブラリとMySQLクライアントライブラリの~~インターフェース~~データ構造に差異がある。これはMySQLライブラリの~~インターフェース~~データ構造はバージョン毎に微妙に異なる~~のでNimのFFIの仕組みとは相性が悪いのではと感じる~~にも関わらずNim側でデータ構造を必要以上に細かく定義しているせいだと思われる。

# Golangはどうしてる？
Golangでは標準ライブラリとして各DB共通インターフェース[`database/sql`](https://golang.org/pkg/database/sql/)を用意し、実装は[各Driverライブラリ](https://github.com/golang/go/wiki/SQLDrivers)に任されているが、MySQL用Driverの中で最もスタンダードである(と思う)[go-sql-driver/mysql](https://github.com/go-sql-driver/mysql/)では全てGolangで実装されていて外部ライブラリを必要としない。

# じゃあNimでも書けばいいじゃん
ということで、NimでMySQL接続用のライブラリを書いてみようと思ったが、まずはどんなプロトコルでMySQLとやり取りするのかを調べないといけない。

そこで以下のページでプロトコルの概要を調べるとTCPを使いMySQLのClient/Server Protocolに則って通信すれば良いと分かった。

- [MySQLプロトコルのハンドシェイクパケットを眺めてみる](https://gihyo.jp/dev/serial/01/mysql-road-construction-news/0078)
- [MySQLプロトコルでハンドシェイクレスポンスを返してみる](https://gihyo.jp/dev/serial/01/mysql-road-construction-news/0081)
- [MySQLのプロトコル解説](https://www.slideshare.net/tmtm/mysql-protocol)

NimでTCP通信するには[`net`](https://nim-lang.org/docs/net.html)か[`asyncnet`](https://nim-lang.org/docs/asyncnet.html)を使えばいいらしいのだが今回は簡単のために`net`を使うことにした。

あとはMySQLの[公式リファレンス](https://dev.mysql.com/doc/dev/mysql-server/latest/PAGE_PROTOCOL.html)を見たり、良く分からないところは[go-sql-driver/mysql](https://github.com/go-sql-driver/mysql/)を参考にしたりしてコードを書いてみる。

# とりあえずはMySQLに接続できた
~~認証方法が`mysql_native_password`にしか対応できてない~~`caching_sha2_password`にも対応できたけどOpenSSLを使ったのでpureでなくなった（MySQL8.0ではデフォルトが`caching_sha2_password`になった）とか色々あるけど、LinuxとWindowsでMySQLに接続してSQLを実行することに成功した。（Macは持ってない）

## 接続用ライブラリ
<https://github.com/tauplus/mysql_connector>

### サンプル
```yaml:サンプル用docker-compose
version: '3.8'

services:

  db:
    image: mysql:8.0.23
    command: >
      --default-authentication-plugin=mysql_native_password
      --character-set-server=utf8mb4
      --collation-server=utf8mb4_unicode_ci
    restart: always
    ports:
      - "3306:3306"
    environment:
      MYSQL_RANDOM_ROOT_PASSWORD: 1
      MYSQL_DATABASE: test
      MYSQL_USER: nim
      MYSQL_PASSWORD: nim
```

```nim:サンプルプログラム
import mysql_connector

proc main()=
  var db = db_open("127.0.0.1:3306", "nim", "nim", "test")
  defer: db.db_close()

  let drop_sql = 
    sql"""DROP TABLE IF EXISTS `user`"""
  let create_sql = sql"""
    CREATE TABLE IF NOT EXISTS `user` (
    `id` int NOT NULL,
    `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '',
    PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"""

  db.exec(drop_sql)
  db.exec(create_sql)

  let insert_args = 
    @[
      @["1","Tom"],
      @["2","Jay"],
      @["3","Ann"]
    ]

  for insert_arg in insert_args:
    db.exec(sql"INSERT INTO user VALUES (?, ?)", insert_arg)
  let rows = db.get_all_rows(sql"SELECT * FROM user WHERE id >= ? ORDER BY id", 2)

  echo rows

main()
```

```bash:結果
@[@["2", "Jay"], @["3", "Ann"]]
```

# 道のりは遠い
正常系で動くものはできたが、実運用に使えるレベルにはないのでこれから勉強しながらエラーハンドリングやいろんな機能追加をできたらいいな。