use async_trait::async_trait;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::typed_id::SessionId;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DatabaseInfo {
pub name: String,
pub size_bytes: i64,
pub page_count: i32,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SqlQueryResult {
pub columns: Vec<String>,
pub rows: Vec<Vec<serde_json::Value>>,
pub row_count: usize,
#[serde(skip_serializing_if = "std::ops::Not::not")]
pub truncated: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SqlExecuteResult {
pub rows_affected: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TableSchema {
pub name: String,
pub columns: Vec<ColumnSchema>,
pub row_count: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ColumnSchema {
pub name: String,
#[serde(rename = "type")]
pub column_type: String,
pub notnull: bool,
pub pk: bool,
pub default_value: Option<String>,
}
#[derive(Debug, thiserror::Error)]
pub enum SessionSqlDbError {
#[error("database not found: {0}")]
DatabaseNotFound(String),
#[error("database already exists: {0}")]
DatabaseAlreadyExists(String),
#[error("invalid database name: {0}")]
InvalidDatabaseName(String),
#[error("database limit exceeded: {0}")]
LimitExceeded(String),
#[error("query error: {0}")]
QueryError(String),
#[error("query timeout after {0} seconds")]
QueryTimeout(u64),
#[error("result too large: {0}")]
ResultTooLarge(String),
#[error("operation blocked by authorizer: {0}")]
AuthorizerBlocked(String),
#[error("internal error: {0}")]
Internal(String),
}
impl SessionSqlDbError {
pub fn is_tool_error(&self) -> bool {
matches!(
self,
Self::DatabaseNotFound(_)
| Self::DatabaseAlreadyExists(_)
| Self::InvalidDatabaseName(_)
| Self::LimitExceeded(_)
| Self::QueryError(_)
| Self::QueryTimeout(_)
| Self::ResultTooLarge(_)
| Self::AuthorizerBlocked(_)
)
}
}
#[async_trait]
pub trait SessionSqlDbStore: Send + Sync {
async fn create_database(
&self,
session_id: SessionId,
name: &str,
) -> Result<DatabaseInfo, SessionSqlDbError>;
async fn list_databases(
&self,
session_id: SessionId,
) -> Result<Vec<DatabaseInfo>, SessionSqlDbError>;
async fn get_database(
&self,
session_id: SessionId,
name: &str,
) -> Result<Option<DatabaseInfo>, SessionSqlDbError>;
async fn delete_database(
&self,
session_id: SessionId,
name: &str,
) -> Result<bool, SessionSqlDbError>;
async fn sql_execute(
&self,
session_id: SessionId,
db_name: &str,
sql: &str,
) -> Result<SqlExecuteResult, SessionSqlDbError>;
async fn sql_query(
&self,
session_id: SessionId,
db_name: &str,
sql: &str,
) -> Result<SqlQueryResult, SessionSqlDbError>;
async fn sql_schema(
&self,
session_id: SessionId,
db_name: &str,
table: Option<&str>,
) -> Result<Vec<TableSchema>, SessionSqlDbError>;
}