返回文章列表

Rust與PostgreSQL資料函式庫連線與操作

本文示範如何使用 Rust 的 sqlx crate 連線 PostgreSQL 資料函式庫,建立資料函式庫、使用者,並執行 SQL 查詢。同時,文章也涵蓋瞭如何在 Rust 專案中定義資料函式庫模型、建立資料表,以及使用 Actix Web 框架構建資料函式庫驅動的 Web 服務,包含設定路由、處理 HTTP

Web 開發 資料函式庫

Rust 的 sqlx crate 提供了型別安全且非同步的資料函式庫操作方式,本文將逐步示範如何使用 sqlx 連線 PostgreSQL 資料函式庫,並進行資料函式庫的建立、使用者設定、資料表設計以及資料查詢等操作。同時,我們也將探討如何結合 Actix Web 框架,開發一個以資料函式庫為核心的 Web 服務,包含路由設定、請求處理以及單元測試等環節,以確保程式碼的健壯性和可靠性。最後,我們將深入研究如何定義資料模型,並將 HTTP 請求的資料轉換為 Rust 資料結構,以便於後續的資料函式庫操作。

在 PostgreSQL 中建立資料函式庫與使用者並連線 Rust 程式

STEP 3: 建立 PostgreSQL 資料函式庫與使用者

首先,我們需要進入 PostgreSQL 命令列介面。登入 PostgreSQL 使用者帳戶後,執行以下指令:

psql

此時,你應該會看到 postgres=# 的提示符號。接著,建立一個名為 ezytutors 的資料函式庫:

postgres=# create database ezytutors;

建立一個新的使用者 truuser 並設定密碼:

postgres=# create user truuser with password 'mypassword';

將新建立的使用者加入至 ezytutors 資料函式庫的存取許可權:

postgres=# grant all privileges on database ezytutors to truuser;

完成後,離開 PostgreSQL 命令列介面:

postgres=# \q

離開 PostgreSQL 使用者帳戶:

exit

設定環境變數 DATABASE_USER 為剛才建立的使用者名稱:

export DATABASE_USER=truuser

使用以下指令登入 ezytutors 資料函式庫:

psql -U $DATABASE_USER -d ezytutors --password

輸入密碼後,你應該會看到 ezytutors=> 的提示符號。執行 \list 指令確認資料函式庫是否建立成功。

PostgreSQL 資料函式庫建立流程

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title Rust與PostgreSQL資料函式庫連線與操作

package "機器學習流程" {
    package "資料處理" {
        component [資料收集] as collect
        component [資料清洗] as clean
        component [特徵工程] as feature
    }

    package "模型訓練" {
        component [模型選擇] as select
        component [超參數調優] as tune
        component [交叉驗證] as cv
    }

    package "評估部署" {
        component [模型評估] as eval
        component [模型部署] as deploy
        component [監控維護] as monitor
    }
}

collect --> clean : 原始資料
clean --> feature : 乾淨資料
feature --> select : 特徵向量
select --> tune : 基礎模型
tune --> cv : 最佳參數
cv --> eval : 訓練模型
eval --> deploy : 驗證模型
deploy --> monitor : 生產模型

note right of feature
  特徵工程包含:
  - 特徵選擇
  - 特徵轉換
  - 降維處理
end note

note right of eval
  評估指標:
  - 準確率/召回率
  - F1 Score
  - AUC-ROC
end note

@enduml

圖表翻譯: 此圖示呈現了建立 PostgreSQL 資料函式庫與使用者的步驟順序,從登入 PostgreSQL 開始,建立資料函式庫、使用者、授權,最後登入並確認資料函式庫列表。

STEP 4: 定義 Rust 資料函式庫模型並建立資料表

在 Rust 專案中定義資料函式庫模型並建立資料表。首先,建立一個 database.sql 檔案於專案根目錄下的 src 資料夾中,並加入以下 SQL 指令碼:

/* Drop table if it already exists*/
drop table if exists ezy_course_c4;

/* Create a table. */
create table ezy_course_c4
(
    course_id serial primary key,
    tutor_id INT not null,
    course_name varchar(140) not null,
    posted_time TIMESTAMP default now()
);

/* Load seed data for testing */
insert into ezy_course_c4 (course_id, tutor_id, course_name, posted_time)
values (1, 1, 'First course', '2020-12-17 05:40:00');

insert into ezy_course_c4 (course_id, tutor_id, course_name, posted_time)
values (2, 1, 'Second course', '2020-12-18 05:45:00');

執行以下指令以套用 SQL 指令碼至 ezytutors 資料函式庫:

psql -U $DATABASE_USER -d ezytutors < $PROJECT_ROOT/src/database.sql

確認資料是否成功寫入 ezy_course_c4 資料表:

psql -U $DATABASE_USER -d ezytutors --password
select * from ezy_course_c4;

你應該會看到類別似以下的結果:

 course_id | tutor_id | course_name  |      posted_time       
---
-
---
-
---
+
---
-
---
---
+
---
-
---
-
---
---
+
---
-
---
-
---
-
---
-
---
--
         1 |        1 | First course | 2020-12-17 05:40:00
         2 |        1 | Second course | 2020-12-18 05:45:00
(2 rows)

STEP 5: 編寫 Rust 程式碼連線資料函式庫並查詢資料表

src/bin/iter1.rs 中加入以下 Rust 程式碼:

use dotenv::dotenv;
use std::env;
use std::io;
use sqlx::postgres::PgPool;
use chrono::NaiveDateTime;

#[derive(Debug)]
pub struct Course {
    pub course_id: i32,
    pub tutor_id: i32,
    pub course_name: String,
    pub posted_time: Option<NaiveDateTime>,
}

#[actix_rt::main]
async fn main() -> io::Result<()> {
    dotenv().ok();
    let database_url = env::var("DATABASE_URL").expect("DATABASE_URL is not set in .env file");
    let db_pool = PgPool::connect(&database_url).await.unwrap();
    
    let course_rows = sqlx::query!(
        r#"select course_id, tutor_id, course_name, posted_time from ezy_course_c4 where course_id = $1"#,
        1
    )
    .fetch_all(&db_pool)
    .await
    .unwrap();

    let mut courses_list = vec![];
    for course_row in course_rows {
        courses_list.push(Course {
            course_id: course_row.course_id,
            tutor_id: course_row.tutor_id,
            course_name: course_row.course_name,
            posted_time: Some(chrono::NaiveDateTime::from(course_row.posted_time.unwrap())),
        })
    }

    println!("Courses = {:?}", courses_list);
    Ok(())
}

程式碼解密:

  1. 載入 .env 檔案中的環境變數。
  2. 從環境變數中讀取 DATABASE_URL
  3. 使用 sqlx 連線到 PostgreSQL 資料函式庫。
  4. ezy_course_c4 資料表執行 SQL 查詢,篩選出 course_id = 1 的資料。
  5. 將查詢結果轉換為 Course 結構體並存入向量中。
  6. 列印查詢結果。

此程式碼展示瞭如何使用 Rust 連線到 PostgreSQL 資料函式庫並執行查詢操作。透過使用 sqlxdotenv 等 crate,我們能夠簡化資料函式庫操作和環境變數的管理。

使用 Rust 和 sqlx 連線 Postgres 資料函式庫

在前面的章節中,我們已經成功地使用 sqlx 連線到 Postgres 資料函式庫。在本章節中,我們將進一步探討如何建立一個資料函式庫驅動的 Web 服務。

設定資料函式庫連線

首先,我們需要在專案根目錄中建立一個 .env 檔案,並新增以下內容:

DATABASE_URL=postgres://<my-user>:<mypassword>@127.0.0.1:5432/ezytutors

請將 <my-user><mypassword> 替換為您在設定資料函式庫時使用的使用者名稱和密碼。5432 是 Postgres 伺服器的預設連線埠,ezytutors 是我們要連線的資料函式庫名稱。

建立資料函式庫連線池

接下來,我們將使用 sqlx 建立一個資料函式庫連線池。這有助於在 Actix Web 框架產生的多個執行緒中有效地管理資料函式庫連線。

use sqlx::postgres::PgPool;

pub struct AppState {
    pub health_check_response: String,
    pub visit_count: Mutex<u32>,
    pub db: PgPool,
}

AppState 結構體中,我們保留了前一章節中需要的兩個欄位,並增加了一個新的欄位 db,代表 sqlx Postgres 連線池。

定義資料模型

現在,讓我們在 src/iter2/models.rs 檔案中定義資料模型。在這裡,我們將定義一個資料結構來表示課程,並編寫一個實用方法,將 HTTP POST 請求中傳送的 JSON 資料負載轉換為 Rust Course 資料結構。

use actix_web::web;
use chrono::NaiveDateTime;
use serde::{Deserialize, Serialize};

#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct Course {
    pub course_id: i32,
    pub tutor_id: i32,
    pub course_name: String,
    pub posted_time: Option<NaiveDateTime>,
}

impl From<web::Json<Course>> for Course {
    fn from(course: web::Json<Course>) -> Self {
        Course {
            course_id: course.course_id,
            tutor_id: course.tutor_id,
            course_name: course.course_name.clone(),
            posted_time: course.posted_time,
        }
    }
}

資料模型解密:

  1. Course 資料結構包含課程 ID、導師 ID、課程名稱和發布時間等欄位。其中,posted_time 欄位是 Optional<T> 型別,因為對於新的課程發布,該欄位將由導師 Web 服務自動填充,使用者無需提供此資訊。
  2. From 特性將提取 HTTP POST 請求中傳送的資料負載,並將其轉換為 Rust Course 資料結構。

設定路由

src/iter2/routes.rs 檔案中,我們將定義路由。

use super::handlers::*;
use actix_web::web;

pub fn general_routes(cfg: &mut web::ServiceConfig) {
    cfg.route("/health", web::get().to(health_check_handler));
}

pub fn course_routes(cfg: &mut web::ServiceConfig) {
    cfg.service(
        web::scope("/courses")
            .route("/", web::post().to(post_new_course))
            .route("/{tutor_id}", web::get().to(get_courses_for_tutor))
            .route("/{tutor_id}/{course_id}", web::get().to(get_course_details)),
    );
}

路由解密:

  1. general_routes 函式定義了 /health 路由,用於檢查服務的健康狀態。
  2. course_routes 函式定義了 /courses 路由,包括三個子路由:
    • POST /courses:用於建立新的課程。
    • GET /courses/{tutor_id}:用於檢索特定導師的所有課程。
    • GET /courses/{tutor_id}/{course_id}:用於檢索特定課程的詳細資訊。

編寫單元測試

在本章節中,我們還將編寫單元測試,以確保 Web 服務的正確性。