LoginSignup
11
5

More than 3 years have passed since last update.

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

Last updated at Posted at 2021-03-05

外部ライブラリに頼りたくない

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

OS 必要ライブラリ
Linux libmysqlclient.so
Windows libmysql.dll(参考)
MacOS libmysqlclient.dylib

備考:Nimでは外部ライブラリのインターフェースとデータ構造をnimファイル内で定義する必要があるのだが、現在のNimのmysqlライブラリとMySQLクライアントライブラリのインターフェースデータ構造に差異がある。これはMySQLライブラリのインターフェースデータ構造はバージョン毎に微妙に異なるのでNimのFFIの仕組みとは相性が悪いのではと感じるにも関わらずNim側でデータ構造を必要以上に細かく定義しているせいだと思われる。

Golangはどうしてる?

Golangでは標準ライブラリとして各DB共通インターフェースdatabase/sqlを用意し、実装は各Driverライブラリに任されているが、MySQL用Driverの中で最もスタンダードである(と思う)go-sql-driver/mysqlでは全てGolangで実装されていて外部ライブラリを必要としない。

じゃあNimでも書けばいいじゃん

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

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

NimでTCP通信するにはnetasyncnetを使えばいいらしいのだが今回は簡単のためにnetを使うことにした。

あとはMySQLの公式リファレンスを見たり、良く分からないところはgo-sql-driver/mysqlを参考にしたりしてコードを書いてみる。

とりあえずはMySQLに接続できた

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

接続用ライブラリ

サンプル

サンプル用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
サンプルプログラム
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()
結果
@[@["2", "Jay"], @["3", "Ann"]]

道のりは遠い

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

11
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
5