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 中的枚举定义完全匹配,包括值的顺序和名称。