PostgreSQL 枚举类型与 Rust sqlx 的集成指南
1. 在 PostgreSQL 中创建枚举类型
首先,你需要在 PostgreSQL 中创建自定义枚举类型,这是存储枚举数据的前提:
1 2 3 4 5 6 7 8 9 10 11
| CREATE TYPE user_role AS ENUM ('admin', 'moderator', 'user');
CREATE TABLE users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), username TEXT NOT NULL UNIQUE, email TEXT NOT NULL UNIQUE, role user_role NOT NULL DEFAULT 'user', created_at TIMESTAMPTZ DEFAULT NOW() );
|
2. 在 Rust 中定义匹配的枚举类型
在 Rust 代码中,你需要定义一个与数据库枚举匹配的枚举类型,并派生相应的 trait:
1 2 3 4 5 6 7 8 9 10
| use serde::{Serialize, Deserialize}; use sqlx::Type;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Type)] #[sqlx(type_name = "user_role", rename_all = "lowercase")] pub enum UserRole { Admin, Moderator, User, }
|
关键点:
- #[sqlx(type_name = “user_role”)] 指定了 PostgreSQL 中的枚举类型名称
- rename_all = “lowercase” 告诉 sqlx 如何将 Rust 的 PascalCase 枚举变体映射到 PostgreSQL 的小写枚举值
3. 在数据模型中使用枚举
在你的用户结构体中,可以直接使用这个枚举类型:
1 2 3 4 5 6 7 8 9 10 11
| use sqlx::FromRow; use chrono::{DateTime, Utc};
#[derive(Debug, FromRow)] pub struct User { pub id: uuid::Uuid, pub username: String, pub email: String, pub role: UserRole, pub created_at: DateTime<Utc>, }
|
4. 查询处理与类型映射
这是最关键的部分,在使用 query_as! 宏时,需要显式处理枚举类型的映射
查询数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| let user = sqlx::query_as!( User, r#" SELECT id, username, email, role as "role: UserRole", -- 显式类型转换 created_at FROM users WHERE email = $1 "#, email ) .fetch_one(&pool) .await?;
|
插入数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| let new_user = sqlx::query_as!( User, r#" INSERT INTO users (username, email, role) VALUES ($1, $2, $3) RETURNING id, username, email, role as "role: UserRole", created_at "#, username, email, UserRole::Admin as UserRole ) .fetch_one(&pool) .await?;
|
更新数据
对于更新操作,需要使用类型转换语法
1 2 3 4 5 6 7 8 9 10 11
| sqlx::query!( r#" UPDATE users SET role = ($1::text)::user_role, updated_at = NOW() WHERE id = $2 "#, role.to_string(), user_id ) .execute(&pool) .await?;
|
或者更优雅的方式:
1 2 3 4 5 6 7 8 9 10 11
| sqlx::query!( r#" UPDATE users SET role = $1, updated_at = NOW() WHERE id = $2 "#, role as UserRole, user_id ) .execute(&pool) .await?;
|
5. 补充:枚举值与字符串的映射
如果你需要自定义枚举值在数据库中的表示方式(如中文),可以使用 #[sqlx(rename = “…”)] 属性:
1 2 3 4 5 6 7 8
| #[derive(Debug, Clone, sqlx::Type, Serialize, Deserialize)] #[sqlx(type_name = "operator_type")] pub enum OperatorType { #[sqlx(rename = "个人")] Person, #[sqlx(rename = "组织")] Organization, }
|
6. 常见问题与解决方案
1. “unsupported type role of column #N” 错误
这是因为 sqlx 的查询宏无法自动推断自定义类型。解决方案是在查询中显式指定类型:
1
| SELECT role as "role: UserRole" FROM users
|
2. 类型转换错误
1 2 3 4 5
| -- 在查询中使用类型转换 VALUES ($1, $2, $3::user_role)
-- 或者使用类型化的参数 .bind(role as UserRole)
|
3. 枚举定义不一致
确保 PostgreSQL 中的枚举定义与 Rust 中的枚举定义完全匹配,包括值的顺序和名称。