Rustでmysqlを操作する.

概要

Rust言語は最近書籍が発売されましたね.

mysql の設定など

関連packageのインストール

sudo apt-get install -y build-essential
sudo apt-get install -y libssl-dev
sudo apt-get install -y cmake
mysql と言ってけど嘘つきました.mariadbをインストールします.
sudo apt-get install mariadb-server mariadb-client
mysql の起動
sudo systemctl start mysql.service

無限にいいユーザーを作成

GRANT ALL PRIVILEGES ON *.* TO 'nwiizo'@'localhost' IDENTIFIED BY 'password';

良しなにDBを作成します.

MariaDB [(none)]> CREATE DATABASE dbtest;
Query OK, 1 row affected (0.00 sec)
MariaDB [dbtest]> use dbtest;
Database changed
MariaDB [dbtest]> CREATE TABLE `test` (
    ->   `id` bigint(20) NOT NULL AUTO_INCREMENT,
    ->   `name` varchar(255) DEFAULT NULL,
    ->   KEY `id` (`id`)
    -> ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.03 sec)
MariaDB [dbtest]> show databases;
+--------------------+
| Database           |
+--------------------+
| dbtest             |
| information_schema |
| mysql              |
| performance_schema |
+--------------------+
4 rows in set (0.00 sec)

MariaDB [dbtest]> show tables;
+------------------+
| Tables_in_dbtest |
+------------------+
| test             |
+------------------+

Rustを良しなに変更して以下います.

Cargo.toml

[dependencies]
mysql = "*"

src/main.rs

#[macro_use]
extern crate mysql;
// ...

use mysql as my;

#[derive(Debug, PartialEq, Eq)]
struct Payment {
    customer_id: i32,
    amount: i32,
    account_name: Option<String>,
}

fn main() {
    let pool = my::Pool::new("mysql://nwiizo:password@localhost:3307/mysql").unwrap();

    // Let's create payment table.
    // Unwrap just to make sure no error happened.
    pool.prep_exec(r"CREATE TEMPORARY TABLE payment (
                         customer_id int not null,
                         amount int not null,
                         account_name text
                     )", ()).unwrap();

    let payments = vec![
        Payment { customer_id: 1, amount: 2, account_name: None },
        Payment { customer_id: 3, amount: 4, account_name: Some("foo".into()) },
        Payment { customer_id: 5, amount: 6, account_name: None },
        Payment { customer_id: 7, amount: 8, account_name: None },
        Payment { customer_id: 9, amount: 10, account_name: Some("bar".into()) },
    ];

    // Let's insert payments to the database
    // We will use into_iter() because we do not need to map Stmt to anything else.
    // Also we assume that no error happened in `prepare`.
    for mut stmt in pool.prepare(r"INSERT INTO payment
                                       (customer_id, amount, account_name)
                                   VALUES
                                       (:customer_id, :amount, :account_name)").into_iter() {
        for p in payments.iter() {
            // `execute` takes ownership of `params` so we pass account name by reference.
            // Unwrap each result just to make sure no errors happened.
            stmt.execute(params!{
                "customer_id" => p.customer_id,
                "amount" => p.amount,
                "account_name" => &p.account_name,
            }).unwrap();
        }
    }

    // Let's select payments from database
    let selected_payments: Vec<Payment> =
    pool.prep_exec("SELECT customer_id, amount, account_name from payment", ())
    .map(|result| { // In this closure we will map `QueryResult` to `Vec<Payment>`
        // `QueryResult` is iterator over `MyResult<row, err>` so first call to `map`
        // will map each `MyResult` to contained `row` (no proper error handling)
        // and second call to `map` will map each `row` to `Payment`
        result.map(|x| x.unwrap()).map(|row| {
            // ⚠️ Note that from_row will panic if you don't follow your schema
            let (customer_id, amount, account_name) = my::from_row(row);
            Payment {
                customer_id: customer_id,
                amount: amount,
                account_name: account_name,
            }
        }).collect() // Collect payments so now `QueryResult` is mapped to `Vec<Payment>`
    }).unwrap(); // Unwrap `Vec<Payment>`

    // Now make sure that `payments` equals to `selected_payments`.
    // Mysql gives no guaranties on order of returned rows without `ORDER BY`
    // so assume we are lukky.
    assert_eq!(payments, selected_payments);
    println!("Yay!");
}

2018.08.21 mysqlのversion を* にするとregex1.0.2 が入り彼はbuildが失敗するみたいで.クソウケる. 任意のversionにすることをお勧めします