1use crate::db::mysql::MysqlBackend;
9use crate::db::postgres::PostgresBackend;
10use crate::db::sqlite::SqliteBackend;
11use crate::db::validation::validate_read_only_with_dialect;
12use crate::error::AppError;
13use serde_json::Value;
14use sqlparser::dialect::Dialect;
15use tracing::{error, info};
16
17#[allow(async_fn_in_trait)]
19pub trait DatabaseBackend {
20 async fn list_databases(&self) -> Result<Vec<String>, AppError>;
22
23 async fn list_tables(&self, database: &str) -> Result<Vec<String>, AppError>;
25
26 async fn get_table_schema(&self, database: &str, table: &str) -> Result<Value, AppError>;
28
29 async fn get_table_schema_with_relations(&self, database: &str, table: &str) -> Result<Value, AppError>;
31
32 async fn execute_query(&self, sql: &str, database: Option<&str>) -> Result<Value, AppError>;
34
35 async fn create_database(&self, name: &str) -> Result<Value, AppError>;
37
38 fn dialect(&self) -> Box<dyn Dialect>;
40
41 fn read_only(&self) -> bool;
43}
44
45#[derive(Debug, Clone)]
50#[allow(clippy::large_enum_variant)]
51pub enum Backend {
52 Mysql(MysqlBackend),
54 Postgres(PostgresBackend),
56 Sqlite(SqliteBackend),
58}
59
60impl DatabaseBackend for Backend {
61 async fn list_databases(&self) -> Result<Vec<String>, AppError> {
62 match self {
63 Self::Mysql(b) => b.list_databases().await,
64 Self::Postgres(b) => b.list_databases().await,
65 Self::Sqlite(b) => b.list_databases().await,
66 }
67 }
68
69 async fn list_tables(&self, database: &str) -> Result<Vec<String>, AppError> {
70 match self {
71 Self::Mysql(b) => b.list_tables(database).await,
72 Self::Postgres(b) => b.list_tables(database).await,
73 Self::Sqlite(b) => b.list_tables(database).await,
74 }
75 }
76
77 async fn get_table_schema(&self, database: &str, table: &str) -> Result<Value, AppError> {
78 match self {
79 Self::Mysql(b) => b.get_table_schema(database, table).await,
80 Self::Postgres(b) => b.get_table_schema(database, table).await,
81 Self::Sqlite(b) => b.get_table_schema(database, table).await,
82 }
83 }
84
85 async fn get_table_schema_with_relations(&self, database: &str, table: &str) -> Result<Value, AppError> {
86 match self {
87 Self::Mysql(b) => b.get_table_schema_with_relations(database, table).await,
88 Self::Postgres(b) => b.get_table_schema_with_relations(database, table).await,
89 Self::Sqlite(b) => b.get_table_schema_with_relations(database, table).await,
90 }
91 }
92
93 async fn execute_query(&self, sql: &str, database: Option<&str>) -> Result<Value, AppError> {
94 match self {
95 Self::Mysql(b) => b.execute_query(sql, database).await,
96 Self::Postgres(b) => b.execute_query(sql, database).await,
97 Self::Sqlite(b) => b.execute_query(sql, database).await,
98 }
99 }
100
101 async fn create_database(&self, name: &str) -> Result<Value, AppError> {
102 match self {
103 Self::Mysql(b) => b.create_database(name).await,
104 Self::Postgres(b) => b.create_database(name).await,
105 Self::Sqlite(b) => b.create_database(name).await,
106 }
107 }
108
109 fn dialect(&self) -> Box<dyn Dialect> {
110 match self {
111 Self::Mysql(b) => b.dialect(),
112 Self::Postgres(b) => b.dialect(),
113 Self::Sqlite(b) => b.dialect(),
114 }
115 }
116
117 fn read_only(&self) -> bool {
118 match self {
119 Self::Mysql(b) => b.read_only(),
120 Self::Postgres(b) => b.read_only(),
121 Self::Sqlite(b) => b.read_only(),
122 }
123 }
124}
125
126impl Backend {
127 pub async fn tool_list_databases(&self) -> Result<String, AppError> {
133 info!("TOOL: list_databases called");
134 let db_list = self.list_databases().await?;
135 info!("TOOL: list_databases completed. Databases found: {}", db_list.len());
136 Ok(serde_json::to_string_pretty(&db_list).unwrap_or_else(|_| "[]".into()))
137 }
138
139 pub async fn tool_list_tables(&self, database_name: &str) -> Result<String, AppError> {
145 info!("TOOL: list_tables called. database_name={database_name}");
146 let table_list = match self.list_tables(database_name).await {
147 Ok(t) => t,
148 Err(e) => {
149 error!("TOOL ERROR: list_tables failed for database_name={database_name}: {e}");
150 return Err(e);
151 }
152 };
153 info!("TOOL: list_tables completed. Tables found: {}", table_list.len());
154 Ok(serde_json::to_string_pretty(&table_list).unwrap_or_else(|_| "[]".into()))
155 }
156
157 pub async fn tool_get_table_schema(&self, database_name: &str, table_name: &str) -> Result<String, AppError> {
163 info!("TOOL: get_table_schema called. database_name={database_name}, table_name={table_name}");
164 let schema = self.get_table_schema(database_name, table_name).await?;
165 info!("TOOL: get_table_schema completed");
166 Ok(serde_json::to_string_pretty(&schema).unwrap_or_else(|_| "{}".into()))
167 }
168
169 pub async fn tool_get_table_schema_with_relations(
175 &self,
176 database_name: &str,
177 table_name: &str,
178 ) -> Result<String, AppError> {
179 info!("TOOL: get_table_schema_with_relations called. database_name={database_name}, table_name={table_name}");
180 let result = self.get_table_schema_with_relations(database_name, table_name).await?;
181 info!("TOOL: get_table_schema_with_relations completed");
182 Ok(serde_json::to_string_pretty(&result).unwrap_or_else(|_| "{}".into()))
183 }
184
185 pub async fn tool_execute_sql(&self, sql_query: &str, database_name: &str) -> Result<String, AppError> {
196 info!(
197 "TOOL: execute_sql called. database_name={database_name}, sql_query={}",
198 &sql_query[..sql_query.len().min(100)]
199 );
200
201 if self.read_only() {
202 let dialect = self.dialect();
203 validate_read_only_with_dialect(sql_query, dialect.as_ref())?;
204 }
205
206 let db = if database_name.is_empty() {
207 None
208 } else {
209 Some(database_name)
210 };
211
212 let results = self.execute_query(sql_query, db).await?;
213 let row_count = results.as_array().map_or(0, Vec::len);
214 info!("TOOL: execute_sql completed. Rows returned: {row_count}");
215 Ok(serde_json::to_string_pretty(&results).unwrap_or_else(|_| "[]".into()))
216 }
217
218 pub async fn tool_create_database(&self, database_name: &str) -> Result<String, AppError> {
225 info!("TOOL: create_database called for database: '{database_name}'");
226 let result = self.create_database(database_name).await?;
227 info!("TOOL: create_database completed");
228 Ok(serde_json::to_string_pretty(&result).unwrap_or_else(|_| "{}".into()))
229 }
230}