use std::collections::HashSet;
use anyhow::Result;
use crate::capacity::DEFAULT_TABLE_CAPACITY;
use crate::config::AppConfig;
use crate::database::Database;
use crate::error::{SqawkError, SqawkResult};
use crate::file_handler::FileHandler;
use crate::table::DataType;
pub struct SqlExecutor<'a> {
database: &'a mut Database,
file_handler: &'a mut FileHandler,
modified_tables: HashSet<String>,
config: AppConfig,
affected_rows: usize,
dml_executed: bool,
}
impl<'a> SqlExecutor<'a> {
pub fn new(
database: &'a mut Database,
file_handler: &'a mut FileHandler,
config: &AppConfig,
) -> Self {
SqlExecutor {
database,
file_handler,
modified_tables: HashSet::with_capacity(DEFAULT_TABLE_CAPACITY),
config: config.clone(),
affected_rows: 0,
dml_executed: false,
}
}
pub fn execute(&mut self, sql: &str) -> SqawkResult<Vec<crate::table::Table>> {
if self.config.verbose() {
println!("Executing SQL: {}", sql);
}
let result = crate::vm::execute_vm(sql, self.database, self.config.verbose())?;
for table_name in result.modified_tables {
self.modified_tables.insert(table_name);
}
self.affected_rows = result.affected_rows;
self.dml_executed = result.dml_executed;
let mut tables = result.tables;
if let Some(delim) = self.config.field_separator() {
for t in &mut tables {
t.set_delimiter(delim.clone());
}
}
Ok(tables)
}
pub fn save_modified_tables(&self) -> Result<usize> {
let mut names: Vec<&String> = self.modified_tables.iter().collect();
names.sort();
for table_name in &names {
self.file_handler.check_table_writable(table_name)?;
}
for table_name in &names {
self.file_handler.save_table(table_name)?;
}
Ok(names.len())
}
pub fn is_table_modified(&self, table_name: &str) -> bool {
self.modified_tables.contains(table_name)
}
pub fn table_names(&self) -> Vec<String> {
self.database.table_names()
}
pub fn get_table_columns(&self, table_name: &str) -> SqawkResult<Vec<String>> {
let table = self.database.get_table(table_name)?;
Ok(table.columns().to_vec())
}
pub fn get_table_column_types(&self, table_name: &str) -> SqawkResult<Vec<(String, DataType)>> {
let table = self.database.get_table(table_name)?;
let column_types = table
.column_metadata()
.iter()
.map(|col| (col.name.clone(), col.data_type))
.collect();
Ok(column_types)
}
pub fn has_modified_tables(&self) -> bool {
!self.modified_tables.is_empty()
}
pub fn load_file(&mut self, file_spec: &str) -> SqawkResult<Option<(String, String)>> {
self.file_handler.load_file(file_spec)
}
pub fn execute_sql(&mut self, sql: &str) -> Result<Vec<ResultSet>> {
let result = self.execute(sql)?;
Ok(result
.iter()
.map(|table| ResultSet {
columns: table.columns().to_vec(),
rows: table.rows_as_strings(),
})
.collect())
}
pub fn last_statement_changed_rows(&self) -> bool {
self.dml_executed
}
pub fn table_exists(&self, table_name: &str) -> bool {
self.file_handler.has_table(table_name)
}
pub fn table_is_modified(&self, table_name: &str) -> bool {
self.modified_tables.contains(table_name)
}
pub fn save_table(&self, table_name: &str) -> SqawkResult<()> {
if !self.file_handler.has_table(table_name) {
return Err(SqawkError::TableNotFound(table_name.to_string()));
}
if !self.modified_tables.contains(table_name) {
return Ok(());
}
self.file_handler.save_table(table_name)
}
pub fn get_affected_row_count(&self) -> SqawkResult<usize> {
Ok(self.affected_rows)
}
}
#[derive(Debug)]
pub struct ResultSet {
pub columns: Vec<String>,
pub rows: Vec<Vec<String>>,
}