はじめに
RustでDBを扱うとなるとDIESELが定番らしいですが、こちらはどうやら非同期処理を扱っていない様子。
ということで、このところ非同期処理で人気になってきたらしいSQLxを使ってみたので、その過程を記事にしてみました。
チュートリアルみたいな感覚で見て頂けると嬉しい限りです。
事前知識
事前知識として、以下のようなものがあると理解がしやすいかなと思います。
もし本記事で分からないところがあれば、参考文献として見てみてください。
ちなみに自分は一昨日くらいにMySQLを触り始めた者です。
-
- Rust
-
The Rust Programming Language
9. エラー処理 くらいまで
-
- MySQL
-
【初心者でもわかるMySQL入門】MySQLの使い方を基礎からマスター
MySQLでデータを追加する(insert into の使い方) くらいまで
環境
- 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をインストールするだけです。
cargo install --version="0.5.10" sqlx-cli
version
のところは適宜変更してください。
後述のCargo.toml
のsqlxのバージョンと合わせるといいでしょう。
開発
ではいよいよSQLxを使っていきます。
今回はSQLxがどんなものかを試したいだけなので、
+------+--------------------------+
| id | name |
+------+--------------------------+
| 1 | yukarisann kawaii yatta- |
+------+--------------------------+
こんな感じのテーブルを作ることを目標にします。
ユカリサンカワイイヤッター
プロジェクト作成
まずはお馴染みのcargo
でRustのプロジェクトを作成します。
cargo new sqlx_mysql
依存関係
次に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
をプロジェクトルートに作成しましょう。
DATABASE_URL=mysql://[user]:[password]@[host]:[port]/sqlx_test
諸々の個人情報は、ご自分の環境に合わせて適宜変更してください。
自分はだいたい以下のようになりました。
DATABASE_URL=mysql://root:[password]@localhost:3306/sqlx_test
DB作成
開発に使うDBを作成します。
先程の.env
に記述したsqlx_test
というDBを以下のコマンドで新規作成しましょう。
sqlx db create
マイグレーションファイル作成
以下のコマンドでマイグレーションファイルを作成します。
sqlx migrate add test
するとmigrations/[timestamp]_test.sql
が作成されているはずなので、そこに以下のSQL構文を記述しましょう。
CREATE TABLE IF NOT EXISTS hoge(
id int,
name varchar(255)
);
そうしたら、以下のコマンドでマイグレーションを反映させます。
sqlx migrate run
main.rs
ようやっと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文字列はエスケープシーケンスを必要としないので、""
の中に""
を素のまま記述できるんですね。
そんなことしなくたって、中に記述するのを''
に変えればいいだけの話じゃん。
実行
とりあえず動くものはできたので、実際に動かしてみましょう。
cargo run
めっさ長いコンパイルを乗り越えたら多分正常に実行されているはずです。
コンパイルエラーが起きていなければ、以下の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が少し不足気味な気がします。
あと、ドキュメントをもう少し整備してくれるとありがたいですね。
そこら辺の問題はクレートが安定化するにつれて解決していくはずなので、ありがたやと手を合わせながら待ちましょう。
またね。