【Rust】SQLxで非同期にMySQLにクエリを送ってみた話。

# はじめに
RustでDBを扱うとなると[DIESEL](https://github.com/diesel-rs/diesel)が定番らしいですが、こちらはどうやら非同期処理を扱っていない様子。
ということで、このところ非同期処理で人気になってきたらしい[SQLx](https://github.com/launchbadge/sqlx)を使ってみたので、その過程を記事にしてみました。
チュートリアルみたいな感覚で見て頂けると嬉しい限りです。

# 事前知識
事前知識として、以下のようなものがあると理解がしやすいかなと思います。
もし本記事で分からないところがあれば、参考文献として見てみてください。
~~ちなみに自分は一昨日くらいにMySQLを触り始めた者です。~~

<ul>
    <li>
        <dl>
            <dt>Rust</dt>
            <dd><a href="https://doc.rust-jp.rs/book-ja/title-page.html" target="_blank">The Rust Programming Language</a><br>
                9. エラー処理 くらいまで</dd>
        </dl>
    </li>
    <li>
        <dl>
            <dt>MySQL</dt>
            <dd><a href="https://26gram.com/mysql" target="_blank">【初心者でもわかるMySQL入門】MySQLの使い方を基礎からマスター</a><br>
                MySQLでデータを追加する（insert into の使い方） くらいまで</dd>
        </dl>
    </li>
</ul>

# 環境
* OS: Windows 10
* Rust: 1.57.0
  * 各クレートの詳細は後述のCargo.tomlを参照されたし。

# SQLxとは

開発を始める前に、まずはSQLxがどのようなクレートなのかを紹介します。
といっても、GitHubのREADMEに書かれていることの丸パクリなので大した情報量はないです。

SQLxとは以下のような特徴をもつ純Rust製のSQLフレームワークです。

* 非同期処理による同時実行性を提供
* PostgreSQL、MySQL、SQLite、及びMSSQLをサポート
* マクロにより、SQL構文をそのまま埋め込める

やはり非同期処理があるといいですね。
The☆Rustって感じがします~~(適当~~

# CLIインストール

ではSQLxを使うために、ぼちぼち環境構築をしていきましょう。
とはいっても、SQLxのCLIをインストールするだけです。

```powershell
cargo install --version="0.5.10" sqlx-cli
```

`version`のところは適宜変更してください。
後述の`Cargo.toml`のsqlxのバージョンと合わせるといいでしょう。

# 開発

ではいよいよSQLxを使っていきます。
今回はSQLxがどんなものかを試したいだけなので、

```
+------+--------------------------+
| id   | name                     |
+------+--------------------------+
|    1 | yukarisann kawaii yatta- |
+------+--------------------------+
```

こんな感じのテーブルを作ることを目標にします。
~~ﾕｶﾘｻﾝｶﾜｲｲﾔｯﾀｰ~~

## プロジェクト作成

まずはお馴染みの`cargo`でRustのプロジェクトを作成します。

```powershell
cargo new sqlx_mysql
```

## 依存関係

次に`Cargo.toml`に依存関係を記述します。

```toml:Cargo.toml
[package]
name = "sqlx_mysql"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
async-std = { version = "1.8.0", features = [ "attributes" ] }
dotenv = "0.15.0"
sqlx = { version = "0.5.10", features = [ "mysql", "runtime-async-std-native-tls" ] }
```

各クレートの役割は以下のような感じです。

|async-std|dotenv|sqlx|
|:-:|:-:|:-:|
|main()のasync化|.envの読み込み|MySQLへのアクセス|

## .env作成

環境変数を記述する`.env`をプロジェクトルートに作成しましょう。

```env:.env
DATABASE_URL=mysql://[user]:[password]@[host]:[port]/sqlx_test
```

諸々の個人情報は、ご自分の環境に合わせて適宜変更してください。
自分はだいたい以下のようになりました。

```env:.env
DATABASE_URL=mysql://root:[password]@localhost:3306/sqlx_test
```

## DB作成

開発に使うDBを作成します。
先程の`.env`に記述した`sqlx_test`というDBを以下のコマンドで新規作成しましょう。

```powershell
sqlx db create
```

## マイグレーションファイル作成

以下のコマンドでマイグレーションファイルを作成します。

```powershell
sqlx migrate add test
```

すると`migrations/[timestamp]_test.sql`が作成されているはずなので、そこに以下のSQL構文を記述しましょう。

```SQL:[timestamp]_test.sql
CREATE TABLE IF NOT EXISTS hoge(
    id int,
    name varchar(255)
);
```

そうしたら、以下のコマンドでマイグレーションを反映させます。

```powershell
sqlx migrate run
```

## main.rs

ようやっと`main.rs`を書きます。

```Rust:src/main.rs
use std::{env,process};
use dotenv::dotenv;
use sqlx::mysql::MySqlPool;

#[async_std::main]
async fn main(){
    dotenv().ok();    // .envの読み込み
    let database_url=match env::var("DATABASE_URL"){
        Ok(ok)=>ok,
        Err(err)=>{
            eprint!("Error: std::env said, {}\n",err);
            process::exit(1);
        }
    };

    let pool=match MySqlPool::connect(&database_url).await{
        Ok(ok)=>ok,
        Err(err)=>{
            eprint!("Error: sqlx said, {}\n",err);
            process::exit(1);
        }
    };

    if let Err(err)=sqlx::query!(r#"
        INSERT INTO hoge(
            id,
            name
        )
        VALUES(
            1,
            "yukarisann kawaii yatta-"
        );
    "#)
    .execute(&pool)
    .await{
        eprint!("Error: sqlx said {}\n",err);
        process::exit(1);
    }
}
```

かなりのウンコードですが、ゆかりさんの可愛さが全てを解決してくれます。
やったね！

真面目にポイントを解説すると、`query!`内の文字列は`r##`でraw文字列にした方がいいと思います。
raw文字列はエスケープシーケンスを必要としないので、`""`の中に`""`を素のまま記述できるんですね。
~~そんなことしなくたって、中に記述するのを`''`に変えればいいだけの話じゃん。~~

# 実行

とりあえず動くものはできたので、実際に動かしてみましょう。

```powershell
cargo run
```

めっさ長いコンパイルを乗り越えたら多分正常に実行されているはずです。
コンパイルエラーが起きていなければ、以下のSQL構文でテーブルを確認してみましょう。

```SQL
USE sqlx_test;
SELECT * FROM hoge;
```

上手くいけば、目標としていた以下のような出力が得られるはずです。

```
+------+--------------------------+
| id   | name                     |
+------+--------------------------+
|    1 | yukarisann kawaii yatta- |
+------+--------------------------+
```

# ディレクトリ構成

最終的に、以下のようなディレクトリ構成になります。

```
📦sqlx_mysql
 ┣ 📂migrations
 ┃ ┗ 📜[timestamp]_test.sql
 ┣ 📂src
 ┃ ┗ 📜main.rs
 ┣ 📜.env
 ┣ 📜.gitignore
 ┣ 📜Cargo.lock
 ┗ 📜Cargo.toml
```

# おわりに

今回SQLxのほんのさきっちょを触ってみて、やはりSQL構文を直書きできるのは素直で書きやすいなと思いました。
ただやはり開発が活発ということもあり、公式のexampleが少し不足気味な気がします。
あと、ドキュメントをもう少し整備してくれるとありがたいですね。
そこら辺の問題はクレートが安定化するにつれて解決していくはずなので、ありがたやと手を合わせながら待ちましょう。

またね。