Query

データベースから情報を取る方法です。

環境は Postgres のバージョン 11 を使います。またその環境は Docker を用いて以下のコマンドで建てたサーバーを使います。

docker run \
  --name postgres \
  --detach \
  --publish 5432:5432 \
  --env POSTGRES_HOST_AUTH_METHOD=trust \
  postgres:11

準備

動作を見るためにusersというテーブルを作り、その中に適当なデータを入れておきたいと思います。

まずはdiesel_cliでマイグレーションファイルを作ります。名前はcreate_usersとし、以下のコマンドを実行します。実行後migrationsディレクトリ以下に雛形が作られます。

# セットアップがまだの場合
diesel migration setup \
  --database-url postgres://postgres:@loccalhost:5432
# diesel.toml が作られる

diesel migration generate create_users \
  --database-url postgres://postgres:@loccalhost:5432
# migrations/20xx..._create_users/{up,down}.sql
# が作られた

migrations/20xx..._create_users/up.sqlを以下のように編集します。

-- Your SQL goes here

CREATE TABLE IF NOT EXISTS users (
    id SERIAL PRIMARY KEY,
    name VARCHAR(30) NOT NULL,
    nickname VARCHAR(30),
    age INTEGER NOT NULL,
    active BOOLEAN DEFAULT 't' NOT NULL,
    created_at TIMESTAMP WITHOUT TIME ZONE NOT NULL
);

INSERT INTO users (name, nickname, age, created_at) VALUES
    ('foo', 'nickfoo', 20, '2020-04-29T01:17:16.644130000+00:00'),
    ('bar', null, 20, '2020-04-29T01:18:16.644130000+00:00');

usersテーブルの作成と、そのレコードの挿入をしてます。

一応戻せるようにdown.sqlも書いておきます。

-- This file should undo anything in `up.sql`

DROP TABLE IF EXISTS users;

できたら以下のコマンドで実行します。

diesel migration run

ちゃんとデータが入っているか以下で確認します。(jqコマンドを使用します)

psql postgres://postgres:@localhost:5432 \
  --tuples-only \
  --command \
    'select json_agg(users) from users' \
  | tr -d '\n+' \
  | jq .

以下のような出力があれば準備完了です。

[
  {
    "id": 1,
    "name": "foo",
    "nickname": "nickfoo",
    "age": 20,
    "active": true,
    "created_at": "2020-04-29T01:17:16.64413"
  },
  {
    "id": 2,
    "name": "bar",
    "nickname": null,
    "age": 20,
    "active": true,
    "created_at": "2020-04-29T01:18:16.64413"
  }
]

スキーマの確認

実は上記のdiesel migration run ...した際にスキーマモジュールが自動的に作成されているハズです。
例えば今回はこんな感じで作成されてました。

table! {
    users (id) {
        id -> Int4,
        name -> Varchar,
        nickname -> Nullable<Varchar>,
        age -> Int4,
        active -> Bool,
        created_at -> Timestamp,
    }
}

このファイルはdiesel migration setup ...した際に作られるprint_schema.fileに指定されたパスで作られます。

# デフォルト
[print_schema]
file = "src/schema.rs"

この自動作成のモジュールを使うためにsrc/lib.rsを作り以下のようにして、main.rsなどで読み込めるうようにしておきます。またその際table!マクロが解決できるようにdieselmacro_useしておきます。

#[macro_use]
extern crate diesel;

pub mod schema;

このtable!マクロでschema::users::dsl::{users,id,name,...}のようにインポートできるコードが生成されます。

アナログでスキーマモジュールを作る

diesel migrationコマンドを使わないプロジェクトなどで使いたい場合は、自分で手書きて用意しても問題なく動かすことができます。ただしそのパスはdiesel.tomlprint_schema.fileパス先でなければなりません。

スキーマで使用できる型は以下の通りです。

  • BigInt
  • Binary
  • Bool
  • Date
  • Double
  • Float
  • Integer
  • Interval
  • Nullable
  • Numeric
  • SmallInt
  • Text
  • Time
  • Timestamp
  • Tinyint

取得レコード用の入れ物(構造体)の作成

SQLのデータを入れれるような構造体を作ります。守る必要のあるルールで、その構造体はQueryableトレイトを継承してなければなりません。このトレイトはderiveで継承できます。

今回の場合構造体はこのようになります。

#[derive(Queryable, Debug)]
pub struct User {
  pub id: i32,
  pub name: String,
  pub nickname: Option<String>,
  pub age: i32,
  pub active: bool,
  pub created_at: chrono::NaiveDateTime
}

これをsrc/lib.rspub mod schema;の後に追記しておきます。

ここでTimestamp型の扱いに関してですが、chronoというライブラリを使います。これによりCargo.tomlの依存関係を以下のように修正します。
dieselfeaturesにもchronoの追加が必要です。

[dependencies]
diesel = { version = "1.4.4", features = [ "postgres", "chrono" ] }
chrono = "0.4.11"

データを取得する

src/main.rsを作り、最初のマイグレーションで挿入したデータを取得してみます。

use diesel::pg::PgConnection;
use diesel::prelude::*;
use <Cargo.toml の name (- は _ に変換される)>::{schema, User};

const DATABASE_URL: &'static str = "postgres://postgres:@localhost:5432";

fn main() {
  let connection =
    PgConnection::establish(DATABASE_URL).expect(&format!("Error connecting to {}", DATABASE_URL));

  let users = schema::users::dsl::users
    .load::<User>(&connection)
    .expect("Error loading users");

  println!("{:#?}", users);
}

cargo runで実行して以下のような結果が出力されれば完了です!

[
    User {
        id: 1,
        name: "foo",
        nickname: Some(
            "nickfoo",
        ),
        age: 20,
        active: true,
        created_at: 2020-04-29T01:17:16.644130,
    },
    User {
        id: 2,
        name: "bar",
        nickname: None,
        age: 20,
        active: true,
        created_at: 2020-04-29T01:18:16.644130,
    },
]

サンプルコード

動作確認できるコードは nju33-com/get-started-diesel-query に置かれています。