secra-database 0.1.0

基于 SeaORM 的 Rust 数据库连接和管理库
Documentation
//! 数据库表管理服务
//!
//! 提供数据库表的查询、信息获取等功能

use sea_orm::{ConnectionTrait, DatabaseBackend, DatabaseConnection};
use serde::{Deserialize, Serialize};
use thiserror::Error;
use tracing::error;

/// 表管理错误
#[derive(Error, Debug)]
pub enum TableError {
    #[error("数据库查询失败: {0}")]
    QueryFailed(String),
    #[error("表不存在: {0}")]
    TableNotFound(String),
}

/// 表信息
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TableInfo {
    /// 表名
    pub table_name: String,
    /// Schema 名称
    pub schema_name: String,
    /// 表类型(BASE TABLE, VIEW 等)
    pub table_type: String,
    /// 表注释
    pub table_comment: Option<String>,
    /// 行数(估算)
    pub row_count: Option<u64>,
}

/// 表列信息
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TableColumnInfo {
    /// 列名
    pub column_name: String,
    /// 数据类型
    pub data_type: String,
    /// 是否可为空
    pub is_nullable: bool,
    /// 默认值
    pub default_value: Option<String>,
    /// 列注释
    pub column_comment: Option<String>,
    /// 字符最大长度
    pub character_maximum_length: Option<u32>,
    /// 数值精度
    pub numeric_precision: Option<u32>,
    /// 数值小数位数
    pub numeric_scale: Option<u32>,
    /// 是否为主键
    pub is_primary_key: bool,
}

/// 数据库表服务
pub struct TableService;

impl TableService {
    /// 获取所有表信息(PostgreSQL)
    ///
    /// # 注意
    /// 此方法需要 PostgreSQL 数据库,并且需要能够执行查询操作。
    /// 由于 SeaORM 的 DatabaseConnection 不直接支持复杂查询结果解析,
    /// 建议使用 sqlx 连接池或通过其他方式获取查询结果。
    ///
    /// # 参数
    /// * `db` - 数据库连接
    /// * `schema` - Schema 名称(可选,默认为 "public")
    ///
    /// # 替代方案
    /// 如果需要获取表列表,可以:
    /// 1. 使用 sqlx 连接池直接查询
    /// 2. 使用 SeaORM Entity 查询(如果已定义表 Entity)
    /// 3. 通过数据库管理工具查询
    pub async fn list_tables(
        db: &DatabaseConnection,
        schema: Option<&str>,
    ) -> Result<Vec<TableInfo>, TableError> {
        let schema_name = schema.unwrap_or("public");
        
        // 验证 schema_name 不为空
        if schema_name.is_empty() {
            return Err(TableError::QueryFailed("Schema 名称不能为空".to_string()));
        }

        // 检查数据库类型
        let backend = db.get_database_backend();
        if backend != DatabaseBackend::Postgres {
            return Err(TableError::QueryFailed(
                format!("当前仅支持 PostgreSQL 数据库,当前数据库类型: {:?}", backend)
            ));
        }

        // 测试连接是否可用
        let _ = db.execute_unprepared("SELECT 1").await.map_err(|e| {
            TableError::QueryFailed(format!("数据库连接不可用: {}", e))
        })?;

        // 注意:SeaORM 的 DatabaseConnection 不直接支持查询结果解析
        // 要获取表列表,需要使用 sqlx 连接池或通过其他方式
        Err(TableError::QueryFailed(
            format!(
                "表列表查询功能需要 sqlx 连接池支持。\
                建议:1) 使用 sqlx 连接池直接查询,或 2) 通过数据库管理工具查询。\
                要查询的 Schema: {}",
                schema_name
            )
        ))
    }

    /// 获取指定表的信息
    ///
    /// # 注意
    /// 此方法需要 PostgreSQL 数据库,并且需要能够执行查询操作。
    /// 由于 SeaORM 的 DatabaseConnection 不直接支持复杂查询结果解析,
    /// 建议使用 sqlx 连接池或通过其他方式获取查询结果。
    ///
    /// # 参数
    /// * `db` - 数据库连接
    /// * `schema` - Schema 名称(可选,默认为 "public")
    /// * `table_name` - 表名称
    pub async fn get_table_info(
        db: &DatabaseConnection,
        schema: Option<&str>,
        table_name: &str,
    ) -> Result<TableInfo, TableError> {
        let schema_name = schema.unwrap_or("public");
        
        // 验证输入
        if schema_name.is_empty() {
            return Err(TableError::QueryFailed("Schema 名称不能为空".to_string()));
        }
        if table_name.is_empty() {
            return Err(TableError::QueryFailed("表名称不能为空".to_string()));
        }

        // 检查数据库类型
        let backend = db.get_database_backend();
        if backend != DatabaseBackend::Postgres {
            return Err(TableError::QueryFailed(
                format!("当前仅支持 PostgreSQL 数据库,当前数据库类型: {:?}", backend)
            ));
        }

        // 测试连接是否可用
        let _ = db.execute_unprepared("SELECT 1").await.map_err(|e| {
            TableError::QueryFailed(format!("数据库连接不可用: {}", e))
        })?;

        // 注意:SeaORM 的 DatabaseConnection 不直接支持查询结果解析
        // 要获取表信息,需要使用 sqlx 连接池或通过其他方式
        Err(TableError::QueryFailed(
            format!(
                "表信息查询功能需要 sqlx 连接池支持。\
                建议:1) 使用 sqlx 连接池直接查询,或 2) 通过数据库管理工具查询。\
                要查询的表: {}.{}",
                schema_name, table_name
            )
        ))
    }

    /// 获取表的所有列信息(PostgreSQL)
    ///
    /// # 注意
    /// 此方法需要 PostgreSQL 数据库,并且需要能够执行查询操作。
    /// 由于 SeaORM 的 DatabaseConnection 不直接支持复杂查询结果解析,
    /// 建议使用 sqlx 连接池或通过其他方式获取查询结果。
    ///
    /// # 参数
    /// * `db` - 数据库连接
    /// * `schema` - Schema 名称(可选,默认为 "public")
    /// * `table_name` - 表名称
    pub async fn get_table_columns(
        db: &DatabaseConnection,
        schema: Option<&str>,
        table_name: &str,
    ) -> Result<Vec<TableColumnInfo>, TableError> {
        let schema_name = schema.unwrap_or("public");
        
        // 验证输入
        if schema_name.is_empty() {
            return Err(TableError::QueryFailed("Schema 名称不能为空".to_string()));
        }
        if table_name.is_empty() {
            return Err(TableError::QueryFailed("表名称不能为空".to_string()));
        }

        // 检查数据库类型
        let backend = db.get_database_backend();
        if backend != DatabaseBackend::Postgres {
            return Err(TableError::QueryFailed(
                format!("当前仅支持 PostgreSQL 数据库,当前数据库类型: {:?}", backend)
            ));
        }

        // 测试连接是否可用
        let _ = db.execute_unprepared("SELECT 1").await.map_err(|e| {
            TableError::QueryFailed(format!("数据库连接不可用: {}", e))
        })?;

        // 注意:SeaORM 的 DatabaseConnection 不直接支持查询结果解析
        // 要获取表列信息,需要使用 sqlx 连接池或通过其他方式
        Err(TableError::QueryFailed(
            format!(
                "表列信息查询功能需要 sqlx 连接池支持。\
                建议:1) 使用 sqlx 连接池直接查询,或 2) 通过数据库管理工具查询。\
                要查询的表: {}.{}",
                schema_name, table_name
            )
        ))
    }

    /// 检查表是否存在
    pub async fn table_exists(
        db: &DatabaseConnection,
        schema: Option<&str>,
        table_name: &str,
    ) -> Result<bool, TableError> {
        match Self::get_table_info(db, schema, table_name).await {
            Ok(_) => Ok(true),
            Err(TableError::TableNotFound(_)) => Ok(false),
            Err(e) => Err(e),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_table_info_serialization() {
        let info = TableInfo {
            table_name: "test_table".to_string(),
            schema_name: "public".to_string(),
            table_type: "BASE TABLE".to_string(),
            table_comment: Some("测试表".to_string()),
            row_count: Some(100),
        };

        let json = serde_json::to_string(&info).unwrap();
        assert!(json.contains("test_table"));
    }
}