RustでPostgreSQLデータベースに接続する方法

1. 同期接続の実装

まず、Cargo.tomlに以下のクレートを追加します:

postgres = "0.19.7"
use postgres::{Client, NoTls, error::Error};

fn main() -> Result<(), Error> {
    let mut db_connection = Client::connect("host=localhost port=5432 dbname=myappdb user=appuser password=mypassword", NoTls).unwrap();
    
    for record in db_connection.query("SELECT user_id,username,avatar_url,created_at::TEXT,is_active FROM users", &[])? {
        let user_id: i32 = record.get("user_id");
        let username: &str = record.get("username");
        let avatar_url: &str = record.get("avatar_url");
        let created_at: &str = record.get(3);
        let is_active: bool = record.get(4);
        println!("ユーザーID: {}, ユーザー名: {}, アバター: {}, 作成日時: {:?}, 有効状態: {}", user_id, username, avatar_url, created_at, is_active);
    }

    let result_set = db_connection.query("SELECT COUNT(*)::integer AS total FROM users WHERE user_id=$1", &&[&42])?;
    let count: i32 = result_set[0].get("total");
    println!("ユーザー数: {}", count);

    Ok(())
}

注意:PostgreSQLでは、rootユーザー名の使用は避けてください。エラーが発生します。

また、COUNT(*)の結果を明示的にintegerに変換している点に注意してください。これをしないと、PostgreSQLはデフォルトでi8型を返し、Rust側で型不一致エラーが発生します。

2. 非同期操作の実装

必要なクレートをCargo.tomlに追加します:

tokio = {version = "1.35.1", features = ["full"]}
tokio-postgres = "0.7.10"
chrono = "0.4.34"
use tokio_postgres::{NoTls, Error};
use chrono::prelude::{DateTime, Utc};
use std::time::SystemTime;

#[tokio::main]
async fn main() -> Result<(), Error> {
    println!("Tokio非同期版PostgreSQL操作テスト");
    
    let (db_client, conn) = tokio_postgres::connect("host=localhost port=5432 dbname=myappdb user=appuser password=mypassword", NoTls).await.unwrap();
    
    // データベース接続を監視するタスクを起動
    tokio::spawn(async move {
        if let Err(e) = conn.await {
            eprintln!("接続エラー: {}", e);
        }
    });
    
    let records = db_client.query("SELECT user_id,username,avatar_url,created_at,is_active FROM users", &[]).await?;
    for row in records {
        let user_id: i32 = row.get("user_id");
        let username: &str = row.get("username");
        let avatar_url: String = row.get("avatar_url");
        let created_at: SystemTime = row.get("created_at");
        let formatted_time: DateTime<Utc> = created_at.clone().into();
        println!("ID={}, ユーザー名={}, アバターURL={}, 作成日時={}", user_id, username, avatar_url, formatted_time.format("%Y-%m-%d %H:%M:%S"));
    }

    let query_result = db_client.query("SELECT MAX(user_id)::integer FROM users", &[]).await?;
    let max_id: i32 = query_result[0].get(0);
    println!("最大ID: {}", max_id);
    
    let new_id = max_id + 1;
    let new_username: String = format!("user-{}", new_id);
    let new_avatar: String = format!("avatar_{}.jpg", new_id);
    let inserted_rows = db_client.execute(
        "INSERT INTO users(user_id,username,avatar_url,created_at,is_active) VALUES($1,$2,$3,$4,$5)", 
        &&[&new_id, &new_username, &new_avatar, &SystemTime::now(), &true]
    ).await?;
    println!("挿入された行数: {}", inserted_rows);

    let updated_rows = db_client.execute(
        "UPDATE users SET avatar_url='default_avatar_' || user_id::TEXT WHERE avatar_url=''", 
        &[]
    ).await?;
    println!("更新された行数: {}", updated_rows);

    let deleted_rows = db_client.execute(
        "DELETE FROM users WHERE user_id=$1", 
        &&[&new_id]
    ).await?;
    println!("削除された行数: {}", deleted_rows);

    let transaction = db_client.transaction().await?;
    transaction.execute(
        "UPDATE users SET is_active=$1,notes=username || user_id::TEXT WHERE user_id=$2", 
        &&[&false, &1]
    ).await?;
    transaction.execute(
        "UPDATE users SET is_active=$1,notes=username || user_id::TEXT WHERE user_id=$2", 
        &&[&true, &2]
    ).await?;
    transaction.commit().await?;
    
    Ok(())
}

PostgreSQLのtimestamp型はRustのSystemTime型に直接変換できます。馴染みのある年月日時分秒形式に変換するには、chronoクレートを使用します。

上記コードには、挿入、削除、更新、検索、トランザクションの各操作例が含まれています。

3. 非同期処理と接続プール

必要なクレートをCargo.tomlに追加します:

tokio = {version = "1.35.1", features = ["full"]}
tokio-postgres = "0.7.10"
deadpool-postgres = {version = "0.9.0"}
chrono = "0.4.34"
// 接続プールの設定
let mut config = tokio_postgres::Config::new();
config.host("localhost");
config.port(5432);
config.dbname("myappdb");
config.user("appuser");
config.password("mypassword");

let pool_config = ManagerConfig {
    recycling_method: RecyclingMethod::Fast
};

let manager = Manager::from_config(config, NoTls, pool_config);
let connection_pool = Pool::new(manager, 16);
let pooled_client = connection_pool.get().await.unwrap();

let query_result = tokio::spawn(async move {
    pooled_client.query("SELECT user_id,username,avatar_url,created_at,is_active FROM users", &[])
        .await
        .expect("クエリ実行に失敗しました")
})
.await
.expect("タスク実行に失敗しました");

for row in query_result {
    let user_id: i32 = row.get("user_id");
    let username: String = row.get("username");
    println!("接続プールデモ出力: {}, {}", user_id, username);
}

let second_client = connection_pool.get().await.unwrap();
let calculation_result = second_client.query("SELECT 50 * 3::integer", &[]).await.unwrap();
let product: i32 = calculation_result[0].get(0);
println!("50 × 3 = {}", product);

タグ: rust PostgreSQL tokio deadpool-postgres データベース

7月14日 23:50 投稿