pub mod sqlite;
#[cfg(feature = "remote-db")]
pub mod postgres;
#[cfg(feature = "remote-db")]
pub mod mysql;
#[cfg(feature = "duckdb")]
pub mod duckdb;
use anyhow::Result;
use async_trait::async_trait;
pub const MAX_ROWS: usize = 10_000;
#[derive(Debug)]
pub struct QueryResult {
pub columns: Vec<String>,
pub rows: Vec<Vec<String>>,
}
impl QueryResult {
pub fn to_text(&self) -> String {
if self.columns.is_empty() {
return "(no rows)".to_string();
}
let mut out = self.columns.join(" | ");
out.push('\n');
for row in &self.rows {
out.push_str(&row.join(" | "));
out.push('\n');
}
out
}
pub fn print_table(&self) {
if self.columns.is_empty() {
println!("(statement ran; no rows returned)");
return;
}
let header = self.columns.join(" | ");
println!("{header}");
println!("{}", "-".repeat(header.len()));
for row in &self.rows {
println!("{}", row.join(" | "));
}
println!("\n({} row(s))", self.rows.len());
}
}
#[async_trait]
pub trait SqlRunner: Send + Sync {
async fn run_sql(&self, sql: &str) -> Result<QueryResult>;
async fn introspect_schema(&self) -> Result<Vec<String>> {
Err(anyhow::anyhow!("schema introspection not supported for this runner"))
}
async fn categorical_hints(&self) -> Result<Vec<String>> {
Ok(Vec::new())
}
}