// src-tauri/src/extension/database/executor.rs use crate::crdt::hlc::HlcService; use crate::crdt::transformer::CrdtTransformer; use crate::crdt::trigger; use crate::database::core::{convert_value_ref_to_json, parse_sql_statements}; use crate::database::error::DatabaseError; use rusqlite::{params_from_iter, types::Value as SqliteValue, ToSql, Transaction}; use serde_json::Value as JsonValue; use sqlparser::ast::Statement; use std::collections::HashSet; /// SQL-Executor OHNE Berechtigungsprüfung - für interne Nutzung pub struct SqlExecutor; impl SqlExecutor { /// Führt ein SQL Statement OHNE RETURNING aus (mit CRDT) /// Returns: modified_schema_tables pub fn execute_internal_typed( tx: &Transaction, hlc_service: &HlcService, sql: &str, params: &[&dyn ToSql], ) -> Result, DatabaseError> { let mut ast_vec = parse_sql_statements(sql)?; if ast_vec.len() != 1 { return Err(DatabaseError::ExecutionError { sql: sql.to_string(), reason: "execute_internal_typed should only receive a single SQL statement" .to_string(), table: None, }); } let mut statement = ast_vec.pop().unwrap(); let transformer = CrdtTransformer::new(); let hlc_timestamp = hlc_service .new_timestamp_and_persist(tx) .map_err(|e| DatabaseError::HlcError { reason: e.to_string(), })?; let mut modified_schema_tables = HashSet::new(); if let Some(table_name) = transformer.transform_execute_statement_with_table_info( &mut statement, &hlc_timestamp, )? { modified_schema_tables.insert(table_name); } let sql_str = statement.to_string(); eprintln!("DEBUG: Transformed execute SQL: {sql_str}"); // Führe Statement aus tx.execute(&sql_str, params) .map_err(|e| DatabaseError::ExecutionError { sql: sql_str.clone(), table: None, reason: format!("Execute failed: {e}"), })?; // Trigger-Logik für CREATE TABLE if let Statement::CreateTable(create_table_details) = statement { let raw_name = create_table_details.name.to_string(); // Remove quotes from table name let table_name_str = raw_name .trim_matches('"') .trim_matches('`') .to_string(); eprintln!("DEBUG: Setting up triggers for table: {table_name_str}"); trigger::setup_triggers_for_table(tx, &table_name_str, false)?; } Ok(modified_schema_tables) } /// Führt ein SQL Statement MIT RETURNING aus (mit CRDT) /// Returns: (modified_schema_tables, returning_results) pub fn query_internal_typed( tx: &Transaction, hlc_service: &HlcService, sql: &str, params: &[&dyn ToSql], ) -> Result<(HashSet, Vec>), DatabaseError> { let mut ast_vec = parse_sql_statements(sql)?; if ast_vec.len() != 1 { return Err(DatabaseError::ExecutionError { sql: sql.to_string(), reason: "query_internal_typed should only receive a single SQL statement" .to_string(), table: None, }); } let mut statement = ast_vec.pop().unwrap(); let transformer = CrdtTransformer::new(); let hlc_timestamp = hlc_service .new_timestamp_and_persist(tx) .map_err(|e| DatabaseError::HlcError { reason: e.to_string(), })?; let mut modified_schema_tables = HashSet::new(); if let Some(table_name) = transformer.transform_execute_statement_with_table_info( &mut statement, &hlc_timestamp, )? { modified_schema_tables.insert(table_name); } let sql_str = statement.to_string(); eprintln!("DEBUG: Transformed SQL (with RETURNING): {sql_str}"); // Prepare und query ausführen let mut stmt = tx .prepare(&sql_str) .map_err(|e| DatabaseError::ExecutionError { sql: sql_str.clone(), table: None, reason: e.to_string(), })?; let column_names: Vec = stmt .column_names() .into_iter() .map(|s| s.to_string()) .collect(); let num_columns = column_names.len(); let mut rows = stmt .query(params_from_iter(params.iter())) .map_err(|e| DatabaseError::ExecutionError { sql: sql_str.clone(), table: None, reason: e.to_string(), })?; let mut result_vec: Vec> = Vec::new(); // Lese alle RETURNING Zeilen while let Some(row) = rows.next().map_err(|e| DatabaseError::ExecutionError { sql: sql_str.clone(), table: None, reason: e.to_string(), })? { let mut row_values: Vec = Vec::new(); for i in 0..num_columns { let value_ref = row.get_ref(i).map_err(|e| DatabaseError::ExecutionError { sql: sql_str.clone(), table: None, reason: e.to_string(), })?; let json_value = convert_value_ref_to_json(value_ref)?; row_values.push(json_value); } result_vec.push(row_values); } // Trigger-Logik für CREATE TABLE if let Statement::CreateTable(create_table_details) = statement { let raw_name = create_table_details.name.to_string(); // Remove quotes from table name let table_name_str = raw_name .trim_matches('"') .trim_matches('`') .to_string(); eprintln!("DEBUG: Setting up triggers for table (RETURNING): {table_name_str}"); trigger::setup_triggers_for_table(tx, &table_name_str, false)?; } Ok((modified_schema_tables, result_vec)) } /// Führt ein einzelnes SQL Statement OHNE Typinformationen aus (JSON params) pub fn execute_internal( tx: &Transaction, hlc_service: &HlcService, sql: &str, params: &[JsonValue], ) -> Result, DatabaseError> { let sql_params: Vec = params .iter() .map(crate::database::core::ValueConverter::json_to_rusqlite_value) .collect::, _>>()?; let param_refs: Vec<&dyn ToSql> = sql_params.iter().map(|p| p as &dyn ToSql).collect(); Self::execute_internal_typed(tx, hlc_service, sql, ¶m_refs) } /// Query-Variante (mit RETURNING) OHNE Typinformationen (JSON params) pub fn query_internal( tx: &Transaction, hlc_service: &HlcService, sql: &str, params: &[JsonValue], ) -> Result<(HashSet, Vec>), DatabaseError> { let sql_params: Vec = params .iter() .map(crate::database::core::ValueConverter::json_to_rusqlite_value) .collect::, _>>()?; let param_refs: Vec<&dyn ToSql> = sql_params.iter().map(|p| p as &dyn ToSql).collect(); Self::query_internal_typed(tx, hlc_service, sql, ¶m_refs) } /// Führt mehrere SQL Statements als Batch aus pub fn execute_batch_internal( tx: &Transaction, hlc_service: &HlcService, sqls: &[String], params: &[Vec], ) -> Result, DatabaseError> { if sqls.len() != params.len() { return Err(DatabaseError::ExecutionError { sql: format!("{} statements but {} param sets", sqls.len(), params.len()), reason: "Statement count and parameter count mismatch".to_string(), table: None, }); } let mut all_modified_tables = HashSet::new(); for (sql, param_set) in sqls.iter().zip(params.iter()) { let modified_tables = Self::execute_internal(tx, hlc_service, sql, param_set)?; all_modified_tables.extend(modified_tables); } Ok(all_modified_tables) } /// Query für SELECT-Statements (read-only, kein CRDT nötig außer Filter) pub fn query_select( conn: &rusqlite::Connection, sql: &str, params: &[JsonValue], ) -> Result>, DatabaseError> { let mut ast_vec = parse_sql_statements(sql)?; if ast_vec.len() != 1 { return Err(DatabaseError::ExecutionError { sql: sql.to_string(), reason: "query_select should only receive a single SELECT statement".to_string(), table: None, }); } // Hard Delete: Keine SELECT-Transformation mehr nötig let stmt_to_execute = ast_vec.pop().unwrap(); let transformed_sql = stmt_to_execute.to_string(); eprintln!("DEBUG: SELECT (no transformation): {transformed_sql}"); // Convert JSON params to SQLite values let sql_params: Vec = params .iter() .map(crate::database::core::ValueConverter::json_to_rusqlite_value) .collect::, _>>()?; let mut prepared_stmt = conn.prepare(&transformed_sql)?; let num_columns = prepared_stmt.column_count(); let param_refs: Vec<&dyn ToSql> = sql_params.iter().map(|p| p as &dyn ToSql).collect(); let mut rows = prepared_stmt.query(params_from_iter(param_refs.iter()))?; let mut result: Vec> = Vec::new(); while let Some(row) = rows.next()? { let mut row_values: Vec = Vec::new(); for i in 0..num_columns { let value_ref = row.get_ref(i)?; let json_value = convert_value_ref_to_json(value_ref)?; row_values.push(json_value); } result.push(row_values); } Ok(result) } }