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);