mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-18 23:10:51 +01:00
refatored vault
This commit is contained in:
@ -1,246 +1,536 @@
|
||||
// database/core.rs
|
||||
// src-tauri/src/database/core.rs
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::database::error::DatabaseError;
|
||||
use crate::database::DbConnection;
|
||||
use base64::{engine::general_purpose::STANDARD, Engine as _};
|
||||
use rusqlite::types::Value as SqlValue;
|
||||
use rusqlite::{
|
||||
types::{Value as RusqliteValue, ValueRef},
|
||||
Connection, OpenFlags, ToSql,
|
||||
};
|
||||
use serde_json::Value as JsonValue;
|
||||
use tauri::State;
|
||||
// --- Hilfsfunktion: Konvertiert JSON Value zu etwas, das rusqlite versteht ---
|
||||
// Diese Funktion ist etwas knifflig wegen Ownership und Lifetimes.
|
||||
// Eine einfachere Variante ist oft, direkt rusqlite::types::Value zu erstellen.
|
||||
// Hier ein Beispiel, das owned Values erstellt (braucht evtl. Anpassung je nach rusqlite-Version/Nutzung)
|
||||
fn json_to_rusqlite_value(json_val: &JsonValue) -> Result<RusqliteValue, String> {
|
||||
match json_val {
|
||||
JsonValue::Null => Ok(RusqliteValue::Null),
|
||||
JsonValue::Bool(b) => Ok(RusqliteValue::Integer(*b as i64)), // SQLite hat keinen BOOLEAN
|
||||
JsonValue::Number(n) => {
|
||||
if let Some(i) = n.as_i64() {
|
||||
Ok(RusqliteValue::Integer(i))
|
||||
} else if let Some(f) = n.as_f64() {
|
||||
Ok(RusqliteValue::Real(f))
|
||||
} else {
|
||||
Err("Ungültiger Zahlenwert".to_string())
|
||||
}
|
||||
}
|
||||
JsonValue::String(s) => Ok(RusqliteValue::Text(s.clone())),
|
||||
JsonValue::Array(_) | JsonValue::Object(_) => {
|
||||
// SQLite kann Arrays/Objects nicht direkt speichern (außer als TEXT/BLOB)
|
||||
// Konvertiere sie zu JSON-Strings, wenn das gewünscht ist
|
||||
Ok(RusqliteValue::Text(
|
||||
serde_json::to_string(json_val).map_err(|e| e.to_string())?,
|
||||
))
|
||||
// Oder gib einen Fehler zurück, wenn Arrays/Objekte nicht erlaubt sind
|
||||
// Err("Arrays oder Objekte werden nicht direkt als Parameter unterstützt".to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn execute(
|
||||
sql: String,
|
||||
params: Vec<JsonValue>,
|
||||
state: &State<'_, DbConnection>,
|
||||
) -> Result<usize, String> {
|
||||
// Gibt Anzahl betroffener Zeilen zurück
|
||||
|
||||
let params_converted: Vec<RusqliteValue> = params
|
||||
.iter()
|
||||
.map(json_to_rusqlite_value)
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let params_sql: Vec<&dyn ToSql> = params_converted.iter().map(|v| v as &dyn ToSql).collect();
|
||||
|
||||
let db_lock = state
|
||||
.0
|
||||
.lock()
|
||||
.map_err(|e| format!("Mutex Lock Fehler: {}", e))?;
|
||||
let conn = db_lock.as_ref().ok_or("Keine Datenbankverbindung")?;
|
||||
|
||||
let affected_rows = conn
|
||||
.execute(&sql, ¶ms_sql[..])
|
||||
.map_err(|e| format!("SQL Execute Fehler: {}", e))?;
|
||||
|
||||
Ok(affected_rows)
|
||||
}
|
||||
|
||||
pub async fn select(
|
||||
sql: String,
|
||||
params: Vec<JsonValue>,
|
||||
state: &State<'_, DbConnection>,
|
||||
) -> Result<Vec<Vec<JsonValue>>, String> {
|
||||
// Ergebnis als Vec<RowObject>
|
||||
|
||||
// Konvertiere JSON Params zu rusqlite Values für die Abfrage
|
||||
// Wir sammeln sie als owned Values, da `params_from_iter` Referenzen braucht,
|
||||
// was mit lokalen Konvertierungen schwierig ist.
|
||||
let params_converted: Vec<RusqliteValue> = params
|
||||
.iter()
|
||||
.map(json_to_rusqlite_value)
|
||||
.collect::<Result<Vec<_>, _>>()?; // Sammle Ergebnisse, gibt Fehler weiter
|
||||
|
||||
// Konvertiere zu Slice von ToSql-Referenzen (erfordert, dass die Values leben)
|
||||
let params_sql: Vec<&dyn ToSql> = params_converted.iter().map(|v| v as &dyn ToSql).collect();
|
||||
|
||||
// Zugriff auf die Verbindung (blockierend, okay für SQLite in vielen Fällen)
|
||||
let db_lock = state
|
||||
.0
|
||||
.lock()
|
||||
.map_err(|e| format!("Mutex Lock Fehler: {}", e))?;
|
||||
let conn = db_lock.as_ref().ok_or("Keine Datenbankverbindung")?;
|
||||
|
||||
let mut stmt = conn
|
||||
.prepare(&sql)
|
||||
.map_err(|e| format!("SQL Prepare Fehler: {}", e))?;
|
||||
let column_names: Vec<String> = stmt
|
||||
.column_names()
|
||||
.into_iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
let num_columns = column_names.len();
|
||||
|
||||
let mut rows = stmt
|
||||
.query(¶ms_sql[..])
|
||||
.map_err(|e| format!("SQL Query Fehler: {}", e))?;
|
||||
let mut result_vec: Vec<Vec<JsonValue>> = Vec::new();
|
||||
|
||||
println!();
|
||||
println!();
|
||||
println!();
|
||||
println!();
|
||||
|
||||
while let Some(row) = rows.next().map_err(|e| format!("Row Next Fehler: {}", e))? {
|
||||
//let mut row_map = HashMap::new();
|
||||
let mut row_data: Vec<JsonValue> = Vec::with_capacity(num_columns);
|
||||
for i in 0..num_columns {
|
||||
let col_name = &column_names[i];
|
||||
|
||||
println!(
|
||||
"--- Processing Column --- Index: {}, Name: '{}'",
|
||||
i, col_name
|
||||
);
|
||||
let value_ref = row
|
||||
.get_ref(i)
|
||||
.map_err(|e| format!("Get Ref Fehler Spalte {}: {}", i, e))?;
|
||||
|
||||
// Wandle rusqlite ValueRef zurück zu serde_json Value
|
||||
let json_val = match value_ref {
|
||||
ValueRef::Null => JsonValue::Null,
|
||||
ValueRef::Integer(i) => JsonValue::Number(i.into()),
|
||||
ValueRef::Real(f) => JsonValue::Number(
|
||||
serde_json::Number::from_f64(f).unwrap_or(serde_json::Number::from(0)),
|
||||
), // Fallback für NaN/Infinity
|
||||
ValueRef::Text(t) => {
|
||||
let s = String::from_utf8_lossy(t).to_string();
|
||||
// Versuche, als JSON zu parsen, falls es ursprünglich ein Array/Objekt war
|
||||
//serde_json::from_str(&s).unwrap_or(JsonValue::String(s))
|
||||
JsonValue::String(s)
|
||||
}
|
||||
ValueRef::Blob(b) => {
|
||||
// BLOBs z.B. als Base64-String zurückgeben
|
||||
JsonValue::String(STANDARD.encode(b))
|
||||
}
|
||||
};
|
||||
println!(
|
||||
"new row: name: {} with value: {}",
|
||||
column_names[i].clone(),
|
||||
json_val,
|
||||
);
|
||||
row_data.push(json_val);
|
||||
//row_map.insert(column_names[i].clone(), json_val);
|
||||
}
|
||||
//result_vec.push(row_map);
|
||||
result_vec.push(row_data);
|
||||
}
|
||||
|
||||
Ok(result_vec)
|
||||
}
|
||||
use sqlparser::ast::{Query, Select, SetExpr, Statement, TableFactor, TableObject};
|
||||
use sqlparser::dialect::SQLiteDialect;
|
||||
use sqlparser::parser::Parser;
|
||||
|
||||
/// Öffnet und initialisiert eine Datenbank mit Verschlüsselung
|
||||
pub fn open_and_init_db(path: &str, key: &str, create: bool) -> Result<Connection, String> {
|
||||
pub fn open_and_init_db(path: &str, key: &str, create: bool) -> Result<Connection, DatabaseError> {
|
||||
let flags = if create {
|
||||
OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE
|
||||
} else {
|
||||
OpenFlags::SQLITE_OPEN_READ_WRITE
|
||||
};
|
||||
|
||||
let conn = Connection::open_with_flags(path, flags).map_err(|e| {
|
||||
format!(
|
||||
"Datei gibt es nicht: {}. Habe nach {} gesucht",
|
||||
e.to_string(),
|
||||
path
|
||||
)
|
||||
})?;
|
||||
conn.pragma_update(None, "key", key)
|
||||
.map_err(|e| e.to_string())?;
|
||||
let conn =
|
||||
Connection::open_with_flags(path, flags).map_err(|e| DatabaseError::ConnectionFailed {
|
||||
path: path.to_string(),
|
||||
reason: e.to_string(),
|
||||
})?;
|
||||
|
||||
/* conn.execute_batch("SELECT count(*) from haex_extensions")
|
||||
.map_err(|e| e.to_string())?; */
|
||||
conn.pragma_update(None, "key", key)
|
||||
.map_err(|e| DatabaseError::PragmaError {
|
||||
pragma: "key".to_string(),
|
||||
reason: e.to_string(),
|
||||
})?;
|
||||
|
||||
let journal_mode: String = conn
|
||||
.query_row("PRAGMA journal_mode=WAL;", [], |row| row.get(0))
|
||||
.map_err(|e| e.to_string())?;
|
||||
.map_err(|e| DatabaseError::PragmaError {
|
||||
pragma: "journal_mode=WAL".to_string(),
|
||||
reason: e.to_string(),
|
||||
})?;
|
||||
|
||||
if journal_mode.eq_ignore_ascii_case("wal") {
|
||||
println!("WAL mode successfully enabled.");
|
||||
} else {
|
||||
eprintln!("Failed to enable WAL mode.");
|
||||
eprintln!(
|
||||
"Failed to enable WAL mode, journal_mode is '{}'.",
|
||||
journal_mode
|
||||
);
|
||||
}
|
||||
|
||||
Ok(conn)
|
||||
}
|
||||
|
||||
// Hilfsfunktionen für SQL-Parsing
|
||||
pub fn extract_tables_from_query(query: &sqlparser::ast::Query) -> Vec<String> {
|
||||
/// Utility für SQL-Parsing - parst ein einzelnes SQL-Statement
|
||||
pub fn parse_single_statement(sql: &str) -> Result<Statement, DatabaseError> {
|
||||
let dialect = SQLiteDialect {};
|
||||
let statements = Parser::parse_sql(&dialect, sql).map_err(|e| DatabaseError::ParseError {
|
||||
reason: e.to_string(),
|
||||
sql: sql.to_string(),
|
||||
})?;
|
||||
|
||||
statements
|
||||
.into_iter()
|
||||
.next()
|
||||
.ok_or(DatabaseError::ParseError {
|
||||
reason: "No SQL statement found".to_string(),
|
||||
sql: sql.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Utility für SQL-Parsing - parst mehrere SQL-Statements
|
||||
pub fn parse_sql_statements(sql: &str) -> Result<Vec<Statement>, DatabaseError> {
|
||||
let dialect = SQLiteDialect {};
|
||||
Parser::parse_sql(&dialect, sql).map_err(|e| DatabaseError::ParseError {
|
||||
reason: e.to_string(),
|
||||
sql: sql.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub struct ValueConverter;
|
||||
|
||||
impl ValueConverter {
|
||||
pub fn json_to_rusqlite_value(json_val: &JsonValue) -> Result<SqlValue, DatabaseError> {
|
||||
match json_val {
|
||||
JsonValue::Null => Ok(SqlValue::Null),
|
||||
JsonValue::Bool(b) => {
|
||||
// SQLite hat keinen Bool-Typ; verwende Integer 0/1
|
||||
Ok(SqlValue::Integer(if *b { 1 } else { 0 }))
|
||||
}
|
||||
JsonValue::Number(n) => {
|
||||
if let Some(i) = n.as_i64() {
|
||||
Ok(SqlValue::Integer(i))
|
||||
} else if let Some(f) = n.as_f64() {
|
||||
Ok(SqlValue::Real(f))
|
||||
} else {
|
||||
// Fallback: als Text
|
||||
Ok(SqlValue::Text(n.to_string()))
|
||||
}
|
||||
}
|
||||
JsonValue::String(s) => Ok(SqlValue::Text(s.clone())),
|
||||
JsonValue::Array(_) | JsonValue::Object(_) => {
|
||||
// Arrays/Objects als JSON-Text speichern
|
||||
serde_json::to_string(json_val)
|
||||
.map(SqlValue::Text)
|
||||
.map_err(|e| DatabaseError::SerializationError {
|
||||
reason: format!("Failed to serialize JSON param: {}", e),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn convert_params(params: &[JsonValue]) -> Result<Vec<SqlValue>, DatabaseError> {
|
||||
params.iter().map(Self::json_to_rusqlite_value).collect()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute(
|
||||
sql: String,
|
||||
params: Vec<JsonValue>,
|
||||
connection: &DbConnection,
|
||||
) -> Result<usize, DatabaseError> {
|
||||
// Konvertiere Parameter
|
||||
let params_converted: Vec<RusqliteValue> = params
|
||||
.iter()
|
||||
.map(ValueConverter::json_to_rusqlite_value)
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let params_sql: Vec<&dyn ToSql> = params_converted.iter().map(|v| v as &dyn ToSql).collect();
|
||||
|
||||
with_connection(connection, |conn| {
|
||||
let affected_rows = conn.execute(&sql, ¶ms_sql[..]).map_err(|e| {
|
||||
// "Lazy Parsing": Extrahiere den Tabellennamen nur, wenn ein Fehler auftritt,
|
||||
// um den Overhead bei erfolgreichen Operationen zu vermeiden.
|
||||
let table_name = extract_primary_table_name_from_sql(&sql).unwrap_or(None);
|
||||
|
||||
DatabaseError::ExecutionError {
|
||||
sql: sql.clone(),
|
||||
reason: e.to_string(),
|
||||
table: table_name,
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(affected_rows)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn select(
|
||||
sql: String,
|
||||
params: Vec<JsonValue>,
|
||||
connection: &DbConnection,
|
||||
) -> Result<Vec<HashMap<String, JsonValue>>, DatabaseError> {
|
||||
// Validiere SQL-Statement
|
||||
let statement = parse_single_statement(&sql)?;
|
||||
|
||||
// Stelle sicher, dass es eine Query ist
|
||||
if !matches!(statement, Statement::Query(_)) {
|
||||
return Err(DatabaseError::UnsupportedStatement {
|
||||
statement_type: "Non-Query".to_string(),
|
||||
description: "Only SELECT statements are allowed in select function".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
// Konvertiere Parameter
|
||||
let params_converted: Vec<RusqliteValue> = params
|
||||
.iter()
|
||||
.map(ValueConverter::json_to_rusqlite_value)
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let params_sql: Vec<&dyn ToSql> = params_converted.iter().map(|v| v as &dyn ToSql).collect();
|
||||
|
||||
with_connection(connection, |conn| {
|
||||
let mut stmt = conn
|
||||
.prepare(&sql)
|
||||
.map_err(|e| DatabaseError::PrepareError {
|
||||
reason: e.to_string(),
|
||||
})?;
|
||||
|
||||
let column_names: Vec<String> = stmt
|
||||
.column_names()
|
||||
.into_iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
let num_columns = column_names.len();
|
||||
|
||||
let mut rows = stmt
|
||||
.query(¶ms_sql[..])
|
||||
.map_err(|e| DatabaseError::QueryError {
|
||||
reason: e.to_string(),
|
||||
})?;
|
||||
|
||||
let mut result_vec: Vec<HashMap<String, JsonValue>> = Vec::new();
|
||||
|
||||
while let Some(row) = rows.next().map_err(|e| DatabaseError::RowProcessingError {
|
||||
reason: format!("Row iteration error: {}", e),
|
||||
})? {
|
||||
let mut row_map: HashMap<String, JsonValue> = HashMap::with_capacity(num_columns);
|
||||
|
||||
for i in 0..num_columns {
|
||||
let col_name = &column_names[i];
|
||||
|
||||
/* println!(
|
||||
"--- Processing Column --- Index: {}, Name: '{}'",
|
||||
i, col_name
|
||||
); */
|
||||
|
||||
let value_ref = row
|
||||
.get_ref(i)
|
||||
.map_err(|e| DatabaseError::RowProcessingError {
|
||||
reason: format!("Failed to get column {} ('{}'): {}", i, col_name, e),
|
||||
})?;
|
||||
|
||||
let json_val = convert_value_ref_to_json(value_ref)?;
|
||||
|
||||
//println!("Column: {} = {}", column_names[i], json_val);
|
||||
|
||||
row_map.insert(col_name.clone(), json_val);
|
||||
}
|
||||
result_vec.push(row_map);
|
||||
}
|
||||
|
||||
Ok(result_vec)
|
||||
})
|
||||
}
|
||||
|
||||
/// Konvertiert rusqlite ValueRef zu JSON
|
||||
fn convert_value_ref_to_json(value_ref: ValueRef) -> Result<JsonValue, DatabaseError> {
|
||||
let json_val = match value_ref {
|
||||
ValueRef::Null => JsonValue::Null,
|
||||
ValueRef::Integer(i) => JsonValue::Number(i.into()),
|
||||
ValueRef::Real(f) => JsonValue::Number(
|
||||
serde_json::Number::from_f64(f).unwrap_or_else(|| serde_json::Number::from(0)),
|
||||
),
|
||||
ValueRef::Text(t) => {
|
||||
let s = String::from_utf8_lossy(t).to_string();
|
||||
JsonValue::String(s)
|
||||
}
|
||||
ValueRef::Blob(b) => {
|
||||
// BLOBs als Base64-String zurückgeben
|
||||
JsonValue::String(STANDARD.encode(b))
|
||||
}
|
||||
};
|
||||
Ok(json_val)
|
||||
}
|
||||
// Extrahiert alle Tabellennamen aus einem SQL-Statement über AST-Parsing
|
||||
pub fn extract_table_names_from_sql(sql: &str) -> Result<Vec<String>, DatabaseError> {
|
||||
let statement = parse_single_statement(sql)?;
|
||||
Ok(extract_table_names_from_statement(&statement))
|
||||
}
|
||||
|
||||
/// Extrahiert den ersten/primären Tabellennamen aus einem SQL-Statement
|
||||
pub fn extract_primary_table_name_from_sql(sql: &str) -> Result<Option<String>, DatabaseError> {
|
||||
let table_names = extract_table_names_from_sql(sql)?;
|
||||
Ok(table_names.into_iter().next())
|
||||
}
|
||||
|
||||
/// Extrahiert alle Tabellennamen aus einem AST Statement
|
||||
pub fn extract_table_names_from_statement(statement: &Statement) -> Vec<String> {
|
||||
let mut tables = Vec::new();
|
||||
extract_tables_from_set_expr(&query.body, &mut tables);
|
||||
|
||||
match statement {
|
||||
Statement::Query(query) => {
|
||||
extract_tables_from_query_recursive(query, &mut tables);
|
||||
}
|
||||
Statement::Insert(insert) => {
|
||||
if let TableObject::TableName(name) = &insert.table {
|
||||
tables.push(name.to_string());
|
||||
}
|
||||
}
|
||||
Statement::Update { table, .. } => {
|
||||
extract_tables_from_table_factor(&table.relation, &mut tables);
|
||||
}
|
||||
Statement::Delete(delete) => {
|
||||
use sqlparser::ast::FromTable;
|
||||
match &delete.from {
|
||||
FromTable::WithFromKeyword(table_refs) | FromTable::WithoutKeyword(table_refs) => {
|
||||
for table_ref in table_refs {
|
||||
extract_tables_from_table_factor(&table_ref.relation, &mut tables);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Fallback für DELETE-Syntax ohne FROM
|
||||
for table_name in &delete.tables {
|
||||
tables.push(table_name.to_string());
|
||||
}
|
||||
}
|
||||
Statement::CreateTable(create) => {
|
||||
tables.push(create.name.to_string());
|
||||
}
|
||||
Statement::AlterTable { name, .. } => {
|
||||
tables.push(name.to_string());
|
||||
}
|
||||
Statement::Drop { names, .. } => {
|
||||
for name in names {
|
||||
tables.push(name.to_string());
|
||||
}
|
||||
}
|
||||
Statement::CreateIndex(create_index) => {
|
||||
tables.push(create_index.table_name.to_string());
|
||||
}
|
||||
Statement::Truncate { table_names, .. } => {
|
||||
for table_name in table_names {
|
||||
tables.push(table_name.to_string());
|
||||
}
|
||||
}
|
||||
// Weitere Statement-Typen können hier hinzugefügt werden
|
||||
_ => {
|
||||
// Für unbekannte Statement-Typen geben wir eine leere Liste zurück
|
||||
}
|
||||
}
|
||||
|
||||
tables
|
||||
}
|
||||
|
||||
fn extract_tables_from_set_expr(set_expr: &sqlparser::ast::SetExpr, tables: &mut Vec<String>) {
|
||||
match set_expr {
|
||||
sqlparser::ast::SetExpr::Select(select) => {
|
||||
for from in &select.from {
|
||||
extract_tables_from_table_with_joins(from, tables);
|
||||
}
|
||||
/// Extrahiert Tabellennamen rekursiv aus Query-Strukturen
|
||||
fn extract_tables_from_query_recursive(query: &Query, tables: &mut Vec<String>) {
|
||||
extract_tables_from_set_expr_recursive(&query.body, tables);
|
||||
}
|
||||
|
||||
/// Extrahiert Tabellennamen aus SELECT-Statements
|
||||
fn extract_tables_from_select(select: &Select, tables: &mut Vec<String>) {
|
||||
// FROM clause
|
||||
for table_ref in &select.from {
|
||||
extract_tables_from_table_factor(&table_ref.relation, tables);
|
||||
|
||||
// JOINs
|
||||
for join in &table_ref.joins {
|
||||
extract_tables_from_table_factor(&join.relation, tables);
|
||||
}
|
||||
sqlparser::ast::SetExpr::Query(query) => {
|
||||
extract_tables_from_set_expr(&query.body, tables);
|
||||
}
|
||||
sqlparser::ast::SetExpr::SetOperation { left, right, .. } => {
|
||||
extract_tables_from_set_expr(left, tables);
|
||||
extract_tables_from_set_expr(right, tables);
|
||||
}
|
||||
_ => (), // Andere Fälle wie Values oder Insert ignorieren
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_tables_from_table_with_joins(
|
||||
table_with_joins: &sqlparser::ast::TableWithJoins,
|
||||
tables: &mut Vec<String>,
|
||||
) {
|
||||
extract_tables_from_table_factor(&table_with_joins.relation, tables);
|
||||
for join in &table_with_joins.joins {
|
||||
extract_tables_from_table_factor(&join.relation, tables);
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_tables_from_table_factor(
|
||||
table_factor: &sqlparser::ast::TableFactor,
|
||||
tables: &mut Vec<String>,
|
||||
) {
|
||||
/// Extrahiert Tabellennamen aus TableFactor-Strukturen
|
||||
fn extract_tables_from_table_factor(table_factor: &TableFactor, tables: &mut Vec<String>) {
|
||||
match table_factor {
|
||||
sqlparser::ast::TableFactor::Table { name, .. } => {
|
||||
TableFactor::Table { name, .. } => {
|
||||
tables.push(name.to_string());
|
||||
}
|
||||
sqlparser::ast::TableFactor::Derived { subquery, .. } => {
|
||||
extract_tables_from_set_expr(&subquery.body, tables);
|
||||
TableFactor::Derived { subquery, .. } => {
|
||||
extract_tables_from_query_recursive(subquery, tables);
|
||||
}
|
||||
sqlparser::ast::TableFactor::NestedJoin {
|
||||
TableFactor::TableFunction { .. } => {
|
||||
// Table functions haben normalerweise keine direkten Tabellennamen
|
||||
}
|
||||
TableFactor::NestedJoin {
|
||||
table_with_joins, ..
|
||||
} => {
|
||||
extract_tables_from_table_with_joins(table_with_joins, tables);
|
||||
extract_tables_from_table_factor(&table_with_joins.relation, tables);
|
||||
for join in &table_with_joins.joins {
|
||||
extract_tables_from_table_factor(&join.relation, tables);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// TableFunction, UNNEST, JsonTable, etc. haben normalerweise keine direkten Tabellennamen
|
||||
// oder sind nicht relevant für SQLite
|
||||
}
|
||||
_ => (), // Andere Fälle wie TableFunction ignorieren
|
||||
}
|
||||
}
|
||||
|
||||
/// Extrahiert Tabellennamen rekursiv aus SetExpr-Strukturen.
|
||||
/// Diese Funktion enthält die eigentliche rekursive Logik.
|
||||
fn extract_tables_from_set_expr_recursive(set_expr: &SetExpr, tables: &mut Vec<String>) {
|
||||
match set_expr {
|
||||
SetExpr::Select(select) => {
|
||||
extract_tables_from_select(select, tables);
|
||||
}
|
||||
SetExpr::Query(sub_query) => {
|
||||
extract_tables_from_set_expr_recursive(&sub_query.body, tables);
|
||||
}
|
||||
SetExpr::SetOperation { left, right, .. } => {
|
||||
extract_tables_from_set_expr_recursive(left, tables);
|
||||
extract_tables_from_set_expr_recursive(right, tables);
|
||||
}
|
||||
|
||||
SetExpr::Values(_)
|
||||
| SetExpr::Table(_)
|
||||
| SetExpr::Insert(_)
|
||||
| SetExpr::Update(_)
|
||||
| SetExpr::Delete(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_connection<T, F>(connection: &DbConnection, f: F) -> Result<T, DatabaseError>
|
||||
where
|
||||
F: FnOnce(&mut Connection) -> Result<T, DatabaseError>,
|
||||
{
|
||||
let mut db_lock = connection
|
||||
.0
|
||||
.lock()
|
||||
.map_err(|e| DatabaseError::MutexPoisoned {
|
||||
reason: e.to_string(),
|
||||
})?;
|
||||
|
||||
let conn = db_lock.as_mut().ok_or(DatabaseError::ConnectionError {
|
||||
reason: "Connection to vault failed".to_string(),
|
||||
})?;
|
||||
|
||||
f(conn)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_extract_simple_select() {
|
||||
let sql = "SELECT * FROM users";
|
||||
let tables = extract_table_names_from_sql(sql).unwrap();
|
||||
assert_eq!(tables, vec!["users"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_select_with_join() {
|
||||
let sql = "SELECT u.name, p.title FROM users u JOIN posts p ON u.id = p.user_id";
|
||||
let tables = extract_table_names_from_sql(sql).unwrap();
|
||||
assert_eq!(tables, vec!["users", "posts"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_insert() {
|
||||
let sql = "INSERT INTO users (name, email) VALUES (?, ?)";
|
||||
let tables = extract_table_names_from_sql(sql).unwrap();
|
||||
assert_eq!(tables, vec!["users"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_update() {
|
||||
let sql = "UPDATE users SET name = ? WHERE id = ?";
|
||||
let tables = extract_table_names_from_sql(sql).unwrap();
|
||||
assert_eq!(tables, vec!["users"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_delete() {
|
||||
let sql = "DELETE FROM users WHERE id = ?";
|
||||
let tables = extract_table_names_from_sql(sql).unwrap();
|
||||
assert_eq!(tables, vec!["users"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_create_table() {
|
||||
let sql = "CREATE TABLE new_table (id INTEGER, name TEXT)";
|
||||
let tables = extract_table_names_from_sql(sql).unwrap();
|
||||
assert_eq!(tables, vec!["new_table"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_subquery() {
|
||||
let sql = "SELECT * FROM (SELECT id FROM users) AS sub";
|
||||
let tables = extract_table_names_from_sql(sql).unwrap();
|
||||
assert_eq!(tables, vec!["users"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_primary_table() {
|
||||
let sql = "SELECT u.name FROM users u JOIN posts p ON u.id = p.user_id";
|
||||
let primary_table = extract_primary_table_name_from_sql(sql).unwrap();
|
||||
assert_eq!(primary_table, Some("users".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extract_complex_query() {
|
||||
let sql = r#"
|
||||
SELECT u.name, COUNT(p.id) as post_count
|
||||
FROM users u
|
||||
LEFT JOIN posts p ON u.id = p.user_id
|
||||
WHERE u.created_at > (SELECT MIN(created_at) FROM sessions)
|
||||
GROUP BY u.id
|
||||
"#;
|
||||
let tables = extract_table_names_from_sql(sql).unwrap();
|
||||
assert_eq!(tables, vec!["users", "posts", "sessions"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_sql() {
|
||||
let sql = "INVALID SQL";
|
||||
let result = extract_table_names_from_sql(sql);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_single_statement() {
|
||||
let sql = "SELECT * FROM users WHERE id = ?";
|
||||
let result = parse_single_statement(sql);
|
||||
assert!(result.is_ok());
|
||||
assert!(matches!(result.unwrap(), Statement::Query(_)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_invalid_sql() {
|
||||
let sql = "INVALID SQL STATEMENT";
|
||||
let result = parse_single_statement(sql);
|
||||
assert!(matches!(result, Err(DatabaseError::ParseError { .. })));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_convert_value_ref_to_json() {
|
||||
use rusqlite::types::ValueRef;
|
||||
|
||||
assert_eq!(
|
||||
convert_value_ref_to_json(ValueRef::Null).unwrap(),
|
||||
JsonValue::Null
|
||||
);
|
||||
assert_eq!(
|
||||
convert_value_ref_to_json(ValueRef::Integer(42)).unwrap(),
|
||||
JsonValue::Number(42.into())
|
||||
);
|
||||
assert_eq!(
|
||||
convert_value_ref_to_json(ValueRef::Text(b"hello")).unwrap(),
|
||||
JsonValue::String("hello".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
// Test für die neuen AST-basierten Funktionen
|
||||
#[test]
|
||||
fn test_extract_table_names_comprehensive() {
|
||||
// Test verschiedene SQL-Statement-Typen
|
||||
assert_eq!(
|
||||
extract_primary_table_name_from_sql("SELECT * FROM users WHERE id = 1").unwrap(),
|
||||
Some("users".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
extract_primary_table_name_from_sql("INSERT INTO products (name) VALUES ('test')")
|
||||
.unwrap(),
|
||||
Some("products".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
extract_primary_table_name_from_sql("UPDATE orders SET status = 'completed'").unwrap(),
|
||||
Some("orders".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
extract_primary_table_name_from_sql("DELETE FROM customers").unwrap(),
|
||||
Some("customers".to_string())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
159
src-tauri/src/database/error.rs
Normal file
159
src-tauri/src/database/error.rs
Normal file
@ -0,0 +1,159 @@
|
||||
// src-tauri/src/database/error.rs
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
use ts_rs::TS;
|
||||
|
||||
use crate::crdt::trigger::CrdtSetupError;
|
||||
|
||||
#[derive(Error, Debug, Serialize, Deserialize, TS)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type", content = "details")]
|
||||
pub enum DatabaseError {
|
||||
/// Der SQL-Code konnte nicht geparst werden.
|
||||
#[error("Failed to parse SQL: {reason} - SQL: {sql}")]
|
||||
ParseError { reason: String, sql: String },
|
||||
/// Parameter-Fehler (falsche Anzahl, ungültiger Typ, etc.)
|
||||
#[error("Parameter error: {reason} (expected: {expected}, provided: {provided})")]
|
||||
ParamError {
|
||||
reason: String,
|
||||
expected: usize,
|
||||
provided: usize,
|
||||
},
|
||||
|
||||
#[error("Failed to prepare statement: {reason}")]
|
||||
PrepareError { reason: String },
|
||||
|
||||
#[error("Database error: {reason}")]
|
||||
DatabaseError { reason: String },
|
||||
|
||||
/// Ein Fehler ist während der Ausführung in der Datenbank aufgetreten.
|
||||
#[error("Execution error on table {}: {} - SQL: {}", table.as_deref().unwrap_or("unknown"), reason, sql)]
|
||||
ExecutionError {
|
||||
sql: String,
|
||||
reason: String,
|
||||
table: Option<String>,
|
||||
},
|
||||
/// Ein Fehler ist beim Verwalten der Transaktion aufgetreten.
|
||||
#[error("Transaction error: {reason}")]
|
||||
TransactionError { reason: String },
|
||||
/// Ein SQL-Statement wird vom Proxy nicht unterstützt.
|
||||
#[error("Unsupported statement type '{statement_type}': {description}")]
|
||||
UnsupportedStatement {
|
||||
statement_type: String,
|
||||
description: String,
|
||||
},
|
||||
/// Fehler im HLC-Service
|
||||
#[error("HLC error: {reason}")]
|
||||
HlcError { reason: String },
|
||||
/// Fehler beim Sperren der Datenbankverbindung
|
||||
#[error("Lock error: {reason}")]
|
||||
LockError { reason: String },
|
||||
/// Fehler bei der Datenbankverbindung
|
||||
#[error("Connection error: {reason}")]
|
||||
ConnectionError { reason: String },
|
||||
/// Fehler bei der JSON-Serialisierung
|
||||
#[error("Serialization error: {reason}")]
|
||||
SerializationError { reason: String },
|
||||
|
||||
#[error("Permission error for extension '{extension_id}': {reason} (operation: {}, resource: {})",
|
||||
operation.as_deref().unwrap_or("unknown"),
|
||||
resource.as_deref().unwrap_or("unknown"))]
|
||||
PermissionError {
|
||||
extension_id: String,
|
||||
operation: Option<String>,
|
||||
resource: Option<String>,
|
||||
reason: String,
|
||||
},
|
||||
#[error("Query error: {reason}")]
|
||||
QueryError { reason: String },
|
||||
|
||||
#[error("Row processing error: {reason}")]
|
||||
RowProcessingError { reason: String },
|
||||
|
||||
#[error("Mutex Poisoned error: {reason}")]
|
||||
MutexPoisoned { reason: String },
|
||||
|
||||
#[error("Datenbankverbindung fehlgeschlagen für Pfad '{path}': {reason}")]
|
||||
ConnectionFailed { path: String, reason: String },
|
||||
|
||||
#[error("PRAGMA-Befehl '{pragma}' konnte nicht gesetzt werden: {reason}")]
|
||||
PragmaError { pragma: String, reason: String },
|
||||
|
||||
#[error("Fehler beim Auflösen des Dateipfads: {reason}")]
|
||||
PathResolutionError { reason: String },
|
||||
|
||||
#[error("Datei-I/O-Fehler für Pfad '{path}': {reason}")]
|
||||
IoError { path: String, reason: String },
|
||||
|
||||
#[error("CRDT setup failed: {0}")]
|
||||
CrdtSetup(String),
|
||||
}
|
||||
|
||||
impl From<rusqlite::Error> for DatabaseError {
|
||||
fn from(err: rusqlite::Error) -> Self {
|
||||
DatabaseError::DatabaseError {
|
||||
reason: err.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for DatabaseError {
|
||||
fn from(reason: String) -> Self {
|
||||
DatabaseError::DatabaseError { reason }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CrdtSetupError> for DatabaseError {
|
||||
fn from(err: CrdtSetupError) -> Self {
|
||||
// Wir konvertieren den Fehler in einen String, um ihn einfach zu halten.
|
||||
DatabaseError::CrdtSetup(err.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crate::extension::database::ExtensionDatabaseError> for DatabaseError {
|
||||
fn from(err: crate::extension::database::ExtensionDatabaseError) -> Self {
|
||||
match err {
|
||||
crate::extension::database::ExtensionDatabaseError::Permission { source } => {
|
||||
// Konvertiere PermissionError zu DatabaseError
|
||||
match source {
|
||||
crate::extension::database::permissions::PermissionError::AccessDenied {
|
||||
extension_id,
|
||||
operation,
|
||||
resource,
|
||||
reason,
|
||||
} => DatabaseError::PermissionError {
|
||||
extension_id,
|
||||
operation: Some(operation),
|
||||
resource: Some(resource),
|
||||
reason,
|
||||
},
|
||||
crate::extension::database::permissions::PermissionError::Database {
|
||||
source,
|
||||
} => source,
|
||||
other => DatabaseError::PermissionError {
|
||||
extension_id: "unknown".to_string(),
|
||||
operation: None,
|
||||
resource: None,
|
||||
reason: other.to_string(),
|
||||
},
|
||||
}
|
||||
}
|
||||
crate::extension::database::ExtensionDatabaseError::Database { source } => source,
|
||||
crate::extension::database::ExtensionDatabaseError::ParameterValidation { reason } => {
|
||||
DatabaseError::ParamError {
|
||||
reason: reason.clone(),
|
||||
expected: 0, // Kann nicht aus dem Grund extrahiert werden
|
||||
provided: 0, // Kann nicht aus dem Grund extrahiert werden
|
||||
}
|
||||
}
|
||||
crate::extension::database::ExtensionDatabaseError::StatementExecution { reason } => {
|
||||
DatabaseError::ExecutionError {
|
||||
sql: "unknown".to_string(),
|
||||
reason,
|
||||
table: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,48 +1,41 @@
|
||||
// database/mod.rs
|
||||
// src-tauri/src/database/mod.rs
|
||||
|
||||
pub mod core;
|
||||
pub mod error;
|
||||
|
||||
use rusqlite::Connection;
|
||||
use serde_json::Value as JsonValue;
|
||||
|
||||
use std::fs;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::sync::Mutex;
|
||||
use std::{fs, sync::Arc};
|
||||
use tauri::{path::BaseDirectory, AppHandle, Manager, State};
|
||||
|
||||
use crate::crdt::trigger;
|
||||
use crate::database::core::open_and_init_db;
|
||||
pub struct HlcService(pub Mutex<uhlc::HLC>);
|
||||
use crate::crdt::hlc::HlcService;
|
||||
use crate::database::error::DatabaseError;
|
||||
pub struct DbConnection(pub Arc<Mutex<Option<Connection>>>);
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn sql_select(
|
||||
sql: String,
|
||||
params: Vec<JsonValue>,
|
||||
state: State<'_, DbConnection>,
|
||||
) -> Result<Vec<Vec<JsonValue>>, String> {
|
||||
core::select(sql, params, &state).await
|
||||
pub struct AppState {
|
||||
pub db: DbConnection,
|
||||
pub hlc: Mutex<HlcService>, // Kein Arc hier nötig, da der ganze AppState von Tauri in einem Arc verwaltet wird.
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn sql_execute(
|
||||
pub fn sql_select(
|
||||
sql: String,
|
||||
params: Vec<JsonValue>,
|
||||
state: State<'_, DbConnection>,
|
||||
) -> Result<usize, String> {
|
||||
core::execute(sql, params, &state).await
|
||||
state: State<'_, AppState>,
|
||||
) -> Result<Vec<HashMap<String, JsonValue>>, DatabaseError> {
|
||||
core::select(sql, params, &state.db)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn test(app_handle: AppHandle) -> Result<String, String> {
|
||||
let resource_path = app_handle
|
||||
.path()
|
||||
.resolve("database/vault.db", BaseDirectory::Resource)
|
||||
.map_err(|e| format!("Fehler {}", e));
|
||||
//let file = app_handle.fs().open(resource_path, {}).unwrap().read();
|
||||
Ok(String::from(resource_path.unwrap().to_string_lossy()))
|
||||
/* std::fs::exists(String::from(resource_path.unwrap().to_string_lossy()))
|
||||
.map_err(|e| format!("Fehler: {}", e)) */
|
||||
pub fn sql_execute(
|
||||
sql: String,
|
||||
params: Vec<JsonValue>,
|
||||
state: State<'_, AppState>,
|
||||
) -> Result<usize, DatabaseError> {
|
||||
core::execute(sql, params, &state.db)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
@ -50,8 +43,8 @@ pub fn create_encrypted_database(
|
||||
app_handle: AppHandle,
|
||||
path: String,
|
||||
key: String,
|
||||
state: State<'_, DbConnection>,
|
||||
) -> Result<String, String> {
|
||||
state: State<'_, AppState>,
|
||||
) -> Result<String, DatabaseError> {
|
||||
// Ressourcenpfad zur eingebundenen Datenbank auflösen
|
||||
|
||||
println!("Arbeitsverzeichnis: {:?}", std::env::current_dir());
|
||||
@ -68,14 +61,16 @@ pub fn create_encrypted_database(
|
||||
let resource_path = app_handle
|
||||
.path()
|
||||
.resolve("temp_vault.db", BaseDirectory::AppLocalData)
|
||||
.map_err(|e| format!("Fehler beim Auflösen des Ressourcenpfads: {}", e))?;
|
||||
.map_err(|e| DatabaseError::PathResolutionError {
|
||||
reason: e.to_string(),
|
||||
})?;
|
||||
|
||||
// Prüfen, ob die Ressourcendatei existiert
|
||||
if !resource_path.exists() {
|
||||
return Err(format!(
|
||||
"Ressourcendatenbank wurde nicht gefunden: {}",
|
||||
resource_path.display()
|
||||
));
|
||||
return Err(DatabaseError::IoError {
|
||||
path: resource_path.display().to_string(),
|
||||
reason: "Ressourcendatenbank wurde nicht gefunden.".to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
// Sicherstellen, dass das Zielverzeichnis existiert
|
||||
@ -92,15 +87,10 @@ pub fn create_encrypted_database(
|
||||
|
||||
let target = Path::new(&path);
|
||||
if target.exists() & target.is_file() {
|
||||
println!(
|
||||
"Datei '{}' existiert bereits. Sie wird gelöscht.",
|
||||
target.display()
|
||||
);
|
||||
|
||||
fs::remove_file(target)
|
||||
.map_err(|e| format!("Kann Vault {} nicht löschen. \n {}", target.display(), e))?;
|
||||
} else {
|
||||
println!("Datei '{}' existiert nicht.", target.display());
|
||||
fs::remove_file(target).map_err(|e| DatabaseError::IoError {
|
||||
path: target.display().to_string(),
|
||||
reason: format!("Bestehende Zieldatei konnte nicht gelöscht werden: {}", e),
|
||||
})?;
|
||||
}
|
||||
|
||||
println!(
|
||||
@ -108,37 +98,43 @@ pub fn create_encrypted_database(
|
||||
resource_path.as_path().display()
|
||||
);
|
||||
|
||||
let conn = Connection::open(&resource_path).map_err(|e| {
|
||||
format!(
|
||||
"Fehler beim Öffnen der kopierten Datenbank: {}",
|
||||
e.to_string()
|
||||
)
|
||||
let conn = Connection::open(&resource_path).map_err(|e| DatabaseError::ConnectionFailed {
|
||||
path: resource_path.display().to_string(),
|
||||
reason: format!(
|
||||
"Fehler beim Öffnen der unverschlüsselten Quelldatenbank: {}",
|
||||
e
|
||||
),
|
||||
})?;
|
||||
|
||||
println!("Hänge neue, verschlüsselte Datenbank an unter '{}'", &path);
|
||||
// ATTACH DATABASE 'Dateiname' AS Alias KEY 'Passwort';
|
||||
conn.execute("ATTACH DATABASE ?1 AS encrypted KEY ?2;", [&path, &key])
|
||||
.map_err(|e| format!("Fehler bei ATTACH DATABASE: {}", e.to_string()))?;
|
||||
.map_err(|e| DatabaseError::ExecutionError {
|
||||
sql: "ATTACH DATABASE ...".to_string(),
|
||||
reason: e.to_string(),
|
||||
table: None,
|
||||
})?;
|
||||
|
||||
println!(
|
||||
"Exportiere Daten von 'main' nach 'encrypted' mit password {} ...",
|
||||
&key
|
||||
);
|
||||
|
||||
match conn.query_row("SELECT sqlcipher_export('encrypted');", [], |_row| Ok(())) {
|
||||
Ok(_) => {
|
||||
println!(">>> sqlcipher_export erfolgreich ausgeführt (Rückgabewert ignoriert).");
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("!!! FEHLER während sqlcipher_export: {}", e);
|
||||
conn.execute("DETACH DATABASE encrypted;", []).ok(); // Versuche zu detachen
|
||||
return Err(e.to_string()); // Gib den Fehler zurück
|
||||
}
|
||||
if let Err(e) = conn.query_row("SELECT sqlcipher_export('encrypted');", [], |_| Ok(())) {
|
||||
// Versuche aufzuräumen, ignoriere Fehler dabei
|
||||
let _ = conn.execute("DETACH DATABASE encrypted;", []);
|
||||
return Err(DatabaseError::QueryError {
|
||||
reason: format!("Fehler während sqlcipher_export: {}", e),
|
||||
});
|
||||
}
|
||||
|
||||
println!("Löse die verschlüsselte Datenbank vom Handle...");
|
||||
conn.execute("DETACH DATABASE encrypted;", [])
|
||||
.map_err(|e| format!("Fehler bei DETACH DATABASE: {}", e.to_string()))?;
|
||||
.map_err(|e| DatabaseError::ExecutionError {
|
||||
sql: "DETACH DATABASE ...".to_string(),
|
||||
reason: e.to_string(),
|
||||
table: None,
|
||||
})?;
|
||||
|
||||
println!("Datenbank erfolgreich nach '{}' verschlüsselt.", &path);
|
||||
println!(
|
||||
@ -164,17 +160,19 @@ pub fn create_encrypted_database(
|
||||
|
||||
println!("resource_path: {}", resource_path.display());
|
||||
|
||||
// erstelle Trigger für haex_tables
|
||||
conn.close()
|
||||
.map_err(|(_, e)| DatabaseError::ConnectionFailed {
|
||||
path: resource_path.display().to_string(),
|
||||
reason: format!("Fehler beim Schließen der Quelldatenbank: {}", e),
|
||||
})?;
|
||||
|
||||
conn.close().unwrap();
|
||||
|
||||
let new_conn = open_and_init_db(&path, &key, false)?;
|
||||
let new_conn = core::open_and_init_db(&path, &key, false)?;
|
||||
|
||||
// Aktualisieren der Datenbankverbindung im State
|
||||
let mut db = state
|
||||
.0
|
||||
.lock()
|
||||
.map_err(|e| format!("Mutex-Fehler: {}", e.to_string()))?;
|
||||
let mut db = state.db.0.lock().map_err(|e| DatabaseError::LockError {
|
||||
reason: e.to_string(),
|
||||
})?;
|
||||
|
||||
*db = Some(new_conn);
|
||||
|
||||
Ok(format!("Verschlüsselte CRDT-Datenbank erstellt",))
|
||||
@ -182,11 +180,11 @@ pub fn create_encrypted_database(
|
||||
|
||||
#[tauri::command]
|
||||
pub fn open_encrypted_database(
|
||||
app_handle: AppHandle,
|
||||
//app_handle: AppHandle,
|
||||
path: String,
|
||||
key: String,
|
||||
state: State<'_, DbConnection>,
|
||||
) -> Result<String, String> {
|
||||
state: State<'_, AppState>,
|
||||
) -> Result<String, DatabaseError> {
|
||||
/* let vault_path = app_handle
|
||||
.path()
|
||||
.resolve(format!("vaults/{}", path), BaseDirectory::AppLocalData)
|
||||
@ -194,92 +192,16 @@ pub fn open_encrypted_database(
|
||||
.into_os_string()
|
||||
.into_string()
|
||||
.unwrap(); */
|
||||
if !std::path::Path::new(&path).exists() {
|
||||
/* if !std::path::Path::new(&path).exists() {
|
||||
return Err(format!("File not found {}", path).into());
|
||||
}
|
||||
} */
|
||||
|
||||
let conn =
|
||||
core::open_and_init_db(&path, &key, false).map_err(|e| format!("Error during open: {}", e));
|
||||
let conn = core::open_and_init_db(&path, &key, false)
|
||||
.map_err(|e| format!("Error during open: {}", e))?;
|
||||
|
||||
let mut db = state.0.lock().map_err(|e| e.to_string())?;
|
||||
let mut db = state.db.0.lock().map_err(|e| e.to_string())?;
|
||||
|
||||
*db = Some(conn.unwrap());
|
||||
*db = Some(conn);
|
||||
|
||||
Ok(format!("success"))
|
||||
}
|
||||
|
||||
fn get_target_triple() -> Result<String, String> {
|
||||
let target_triple = if cfg!(target_os = "linux") {
|
||||
if cfg!(target_arch = "x86_64") {
|
||||
"x86_64-unknown-linux-gnu".to_string()
|
||||
} else if cfg!(target_arch = "aarch64") {
|
||||
"aarch64-unknown-linux-gnu".to_string()
|
||||
} else {
|
||||
return Err(format!(
|
||||
"Unbekannte Linux-Architektur: {}",
|
||||
std::env::consts::ARCH
|
||||
));
|
||||
}
|
||||
} else if cfg!(target_os = "macos") {
|
||||
if cfg!(target_arch = "x86_64") {
|
||||
"x86_64-apple-darwin".to_string()
|
||||
} else if cfg!(target_arch = "aarch64") {
|
||||
"aarch64-apple-darwin".to_string()
|
||||
} else {
|
||||
return Err(format!(
|
||||
"Unbekannte macOS-Architektur: {}",
|
||||
std::env::consts::ARCH
|
||||
));
|
||||
}
|
||||
} else if cfg!(target_os = "windows") {
|
||||
if cfg!(target_arch = "x86_64") {
|
||||
"x86_64-pc-windows-msvc".to_string()
|
||||
} else if cfg!(target_arch = "x86") {
|
||||
"i686-pc-windows-msvc".to_string()
|
||||
} else {
|
||||
return Err(format!(
|
||||
"Unbekannte Windows-Architektur: {}",
|
||||
std::env::consts::ARCH
|
||||
));
|
||||
}
|
||||
} else if cfg!(target_os = "android") {
|
||||
if cfg!(target_arch = "aarch64") {
|
||||
"aarch64-linux-android".to_string()
|
||||
} else {
|
||||
return Err(format!(
|
||||
"Unbekannte Android-Architektur: {}",
|
||||
std::env::consts::ARCH
|
||||
));
|
||||
}
|
||||
} else if cfg!(target_os = "ios") {
|
||||
if cfg!(target_arch = "aarch64") {
|
||||
"aarch64-apple-ios".to_string()
|
||||
} else {
|
||||
return Err(format!(
|
||||
"Unbekannte iOS-Architektur: {}",
|
||||
std::env::consts::ARCH
|
||||
));
|
||||
}
|
||||
} else {
|
||||
return Err("Unbekanntes Zielsystem".to_string());
|
||||
};
|
||||
Ok(target_triple)
|
||||
}
|
||||
|
||||
pub fn get_hlc_timestamp(state: tauri::State<HlcService>) -> String {
|
||||
let hlc = state.0.lock().unwrap();
|
||||
hlc.new_timestamp().to_string()
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn update_hlc_from_remote(
|
||||
remote_timestamp_str: String,
|
||||
state: tauri::State<HlcService>,
|
||||
) -> Result<(), String> {
|
||||
let remote_ts =
|
||||
uhlc::Timestamp::from_str(&remote_timestamp_str).map_err(|e| e.cause.to_string())?;
|
||||
|
||||
let hlc = state.0.lock().unwrap();
|
||||
hlc.update_with_timestamp(&remote_ts)
|
||||
.map_err(|e| format!("HLC update failed: {:?}", e))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user