From dce895165b1ade55f33110d100abc11cf6072a86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=93=D1=80=D0=B8=D0=B3=D0=BE=D1=80=D0=B8=D0=B9=20=D0=A1?= =?UTF-8?q?=D0=B0=D1=84=D1=80=D0=BE=D0=BD=D0=BE=D0=B2?= Date: Thu, 15 Jan 2026 19:38:25 +0000 Subject: [PATCH] Delete src/core/database.rs --- src/core/database.rs | 665 ------------------------------------------- 1 file changed, 665 deletions(-) delete mode 100644 src/core/database.rs diff --git a/src/core/database.rs b/src/core/database.rs deleted file mode 100644 index b589431..0000000 --- a/src/core/database.rs +++ /dev/null @@ -1,665 +0,0 @@ -//! Модуль управления базами данных flusql -//! -//! Этот модуль реализует функциональность создания, открытия, -//! управления и удаления баз данных. Каждая база данных содержит -//! коллекцию таблиц и управляет их жизненным циклом. -//! -//! Основные возможности: -//! - Создание новых баз данных -//! - Открытие существующих баз данных -//! - Создание и управление таблицами -//! - Удаление баз данных -//! - Сохранение и загрузка метаданных -//! - Управление транзакциями (базовое) -//! - Управление индексами -//! - Управление триггерами - -use std::collections::HashMap; -use std::fs; -use std::path::Path; -use crate::core::table::Table; -use crate::core::column_family::ColumnFamilyStorage; -use crate::utils::config::Config; -use thiserror::Error; -use serde_json; -use dashmap::DashMap; -use arc_swap::ArcSwap; -use std::sync::atomic::{AtomicBool, Ordering}; -use tokio::task::JoinSet; - -/// База данных flusql -/// -/// Представляет собой контейнер для таблиц с метаданными. -/// Каждая база данных хранится в отдельной директории на диске. -/// -/// АРХИТЕКТУРНЫЕ ХАРАКТЕРИСТИКИ: -/// - Файловая архитектура: каждая база данных в отдельной директории -/// - Wait-free подход: используется атомарная булева переменная для управления состоянием -/// - Отсутствие блокировок: не используются RwLock, Mutex или другие блокирующие примитивы -/// - Асинхронные операции: некоторые операции выполняются в фоновых потоках -/// - Колоночное хранение: используется семейство столбцов для wait-free доступа -pub struct Database { - /// Имя базы данных - name: String, - - /// Таблицы в базе данных (имя -> таблица) - tables: DashMap, - - /// Колоночное хранилище - column_families: ColumnFamilyStorage, - - /// Конфигурация базы данных - config: Config, - - /// Путь к директории с данными базы - data_dir: String, - - /// Флаг активности базы данных (wait-free подход) - /// Используется атомарная операция для проверки состояния без блокировок - is_active: AtomicBool, - - /// Индексы базы данных - indexes: DashMap>, - - /// Триггеры базы данных - triggers: DashMap, - - /// Пулы для параллельной обработки - query_pools: ArcSwap>, -} - -/// Триггер базы данных -#[derive(Debug, Clone)] -struct Trigger { - name: String, - table: String, - timing: crate::parser::sql::TriggerTiming, - event: crate::parser::sql::TriggerEvent, - action: String, -} - -impl Database { - /// Создание новой базы данных - /// - /// Создает директорию для базы данных и инициализирует структуру. - /// Использует wait-free подход: не блокирует другие операции. - /// - /// # Аргументы - /// * `name` - имя создаваемой базы данных - /// * `config` - конфигурация СУБД - /// - /// # Возвращает - /// * `Result` - новая база данных или ошибка - /// - /// # Пример - /// ``` - /// use flusql::Database; - /// use flusql::Config; - /// - /// let config = Config::default(); - /// let db = Database::create("mydb", &config).unwrap(); - /// ``` - pub fn create(name: &str, config: &Config) -> Result { - let data_dir = config.get_data_path(name); - - // Wait-free проверка существования базы данных - // Используем атомарные операции файловой системы - if Path::new(&data_dir).exists() { - return Err(DatabaseError::AlreadyExists(name.to_string())); - } - - // Создаем директорию для базы данных - // Эта операция блокирующая, но выполняется только при создании - fs::create_dir_all(&data_dir) - .map_err(|e| DatabaseError::IoError(e))?; - - // Создаем файл базы данных (basedb.db вместо mydb.db) - let db_file_path = format!("{}/basedb.db", data_dir); - - // Создаем информационное содержимое для файла базы данных - let db_info = format!( - "flusql database: {}\ncreated: {}\nversion: 0.5.0\nstorage_format: csv\n", - name, - chrono::Local::now().to_rfc3339() - ); - - fs::write(&db_file_path, db_info).map_err(|e| DatabaseError::IoError(e))?; - - // Логируем создание базы данных - crate::utils::logger::log_info(&format!("Database '{}' created at '{}'", name, db_file_path)); - - Ok(Self { - name: name.to_string(), - tables: DashMap::new(), - column_families: ColumnFamilyStorage::new(), - config: config.clone(), - data_dir, - is_active: AtomicBool::new(true), - indexes: DashMap::new(), - triggers: DashMap::new(), - query_pools: ArcSwap::new(std::sync::Arc::new(JoinSet::new())), - }) - } - - /// Открытие существующей базы данных - /// - /// Загружает базу данных из директории, включая все таблицы и их данные. - /// Использует wait-free подход для проверки доступности. - /// - /// # Аргументы - /// * `name` - имя открываемой базы данных - /// * `config` - конфигурация СУБД - /// - /// # Возвращает - /// * `Result` - открытая база данных или ошибка - /// - /// # Пример - /// ``` - /// let db = Database::open("mydb", &config).unwrap(); - /// ``` - pub fn open(name: &str, config: &Config) -> Result { - let data_dir = config.get_data_path(name); - - // Wait-free проверка существования базы данных - if !Path::new(&data_dir).exists() { - return Err(DatabaseError::NotFound(name.to_string())); - } - - // Проверяем наличие файла базы данных (basedb.db вместо name.db) - let db_file_path = format!("{}/basedb.db", data_dir); - if !Path::new(&db_file_path).exists() { - // Для обратной совместимости проверяем старый формат - let old_db_file_path = format!("{}/{}.db", data_dir, name); - if !Path::new(&old_db_file_path).exists() { - return Err(DatabaseError::NotFound(name.to_string())); - } - } - - // Загрузка метаданных базы данных - let meta_path = format!("{}/meta.json", data_dir); - let tables = if Path::new(&meta_path).exists() { - // Читаем файл метаданных - let meta_content = fs::read_to_string(&meta_path) - .map_err(|e| DatabaseError::IoError(e))?; - - // Десериализуем список имен таблиц - let table_names: Vec = serde_json::from_str(&meta_content) - .map_err(|e| DatabaseError::ParseError(e))?; - - // Загружаем каждую таблицу - let tables_map = DashMap::new(); - - for table_name in table_names { - if let Ok(table) = Table::load(&data_dir, &table_name) { - tables_map.insert(table_name.clone(), table); - } - } - - tables_map - } else { - // Если файл метаданных не существует, создаем пустую базу - DashMap::new() - }; - - // Логируем открытие базы данных - crate::utils::logger::log_info(&format!("Database '{}' opened from '{}'", name, db_file_path)); - - Ok(Self { - name: name.to_string(), - tables, - column_families: ColumnFamilyStorage::new(), - config: config.clone(), - data_dir, - is_active: AtomicBool::new(true), - indexes: DashMap::new(), - triggers: DashMap::new(), - query_pools: ArcSwap::new(std::sync::Arc::new(JoinSet::new())), - }) - } - - /// Создание таблицы в базе данных - /// - /// # Аргументы - /// * `name` - имя создаваемой таблицы - /// * `schema` - схема таблицы - /// - /// # Возвращает - /// * `Result<(), DatabaseError>` - успех или ошибка создания - /// - /// # Пример - /// ``` - /// use flusql::core::table::{TableSchema, ColumnSchema, DataType}; - /// - /// let schema = TableSchema { - /// columns: vec![ - /// ColumnSchema { - /// name: "id".to_string(), - /// data_type: DataType::Integer, - /// nullable: false, - /// unique: true, - /// }, - /// ], - /// primary_key: Some("id".to_string()), - /// indexes: vec![], - /// foreign_keys: vec![], - /// checks: vec![], - /// }; - /// - /// db.create_table("users", schema).unwrap(); - /// ``` - pub fn create_table(&self, name: &str, schema: crate::core::table::TableSchema) - -> Result<(), DatabaseError> { - - // Wait-free проверка существования таблицы - if self.tables.contains_key(name) { - return Err(DatabaseError::TableExists(name.to_string())); - } - - // Создаем новую таблицу - let table = Table::new(name, schema, &self.data_dir); - - // Также создаем колоночное семейство - let _ = self.column_families.create_family(name, table.get_schema()); - - // Сохраняем таблицу на диск - table.save() - .map_err(|e| DatabaseError::TableError(e))?; - - // Добавляем таблицу в коллекцию (lock-free вставка) - self.tables.insert(name.to_string(), table); - - // Сохраняем обновленные метаданные - self.save_metadata()?; - - // Логируем создание таблицы - crate::utils::logger::log_info(&format!("Table '{}' created in database '{}'", name, self.name)); - - Ok(()) - } - - /// Изменение таблицы - pub fn alter_table(&self, name: &str, operation: crate::parser::sql::AlterOperation) - -> Result<(), DatabaseError> { - if let Some(table) = self.tables.get_mut(name) { - // Упрощенная реализация - в реальной системе здесь нужно - // перестраивать таблицу и обновлять данные - match operation { - crate::parser::sql::AlterOperation::AddColumn(column_def) => { - // Добавление столбца - let column_schema = crate::core::table::ColumnSchema { - name: column_def.name, - data_type: match column_def.data_type { - crate::parser::sql::DataType::Integer => crate::core::table::DataType::Integer, - crate::parser::sql::DataType::Text(_) => crate::core::table::DataType::Text, - crate::parser::sql::DataType::Boolean => crate::core::table::DataType::Boolean, - crate::parser::sql::DataType::Float => crate::core::table::DataType::Float, - crate::parser::sql::DataType::Numeric(_) => crate::core::table::DataType::Integer, // Упрощение - crate::parser::sql::DataType::Timestamp => crate::core::table::DataType::Text, // Упрощение - crate::parser::sql::DataType::Array(_) => crate::core::table::DataType::Text, // Упрощение - crate::parser::sql::DataType::Json => crate::core::table::DataType::Text, // Упрощение - crate::parser::sql::DataType::Jsonb => crate::core::table::DataType::Text, // Упрощение - crate::parser::sql::DataType::Uuid => crate::core::table::DataType::Text, // Упрощение - crate::parser::sql::DataType::Bytea => crate::core::table::DataType::Text, // Упрощение - }, - nullable: column_def.nullable, - unique: column_def.unique, - }; - - // В реальной реализации здесь нужно добавлять столбец к схеме таблицы - // и обновлять существующие записи - } - crate::parser::sql::AlterOperation::DropColumn(column_name) => { - // Удаление столбца - // В реальной реализации здесь нужно удалять столбец из схемы - // и удалять данные этого столбца - } - crate::parser::sql::AlterOperation::AlterColumnType { name: column_name, data_type, using } => { - // Изменение типа столбца - // В реальной реализации здесь нужно изменять схему столбца - // и конвертировать существующие данные - let _ = (column_name, data_type, using); // Используем переменные, чтобы избежать предупреждений - } - crate::parser::sql::AlterOperation::SetNotNull(column_name) => { - // Установка NOT NULL - let _ = column_name; - } - crate::parser::sql::AlterOperation::DropNotNull(column_name) => { - // Удаление NOT NULL - let _ = column_name; - } - crate::parser::sql::AlterOperation::SetDefault { name: column_name, default } => { - // Установка значения по умолчанию - let _ = (column_name, default); - } - crate::parser::sql::AlterOperation::DropDefault(column_name) => { - // Удаление значения по умолчанию - let _ = column_name; - } - crate::parser::sql::AlterOperation::RenameColumn { old_name, new_name } => { - // Переименование столбца - let _ = (old_name, new_name); - } - crate::parser::sql::AlterOperation::RenameTable { new_name } => { - // Переименование таблицы - let _ = new_name; - } - _ => { - // Другие операции пока не поддерживаются - return Err(DatabaseError::TransactionError( - "Operation not supported".to_string() - )); - } - } - - // Сохраняем изменения - let table_clone = table.clone(); - std::thread::spawn(move || { - if let Err(e) = table_clone.save() { - log::error!("Failed to save table: {}", e); - } - }); - - Ok(()) - } else { - Err(DatabaseError::TableNotFound(name.to_string())) - } - } - - /// Создание индекса - pub fn create_index(&self, table: &str, columns: &[String], name: Option) - -> Result<(), DatabaseError> { - let index_name = name.unwrap_or_else(|| { - format!("idx_{}_{}", table, columns.join("_")) - }); - - self.indexes.entry(table.to_string()).or_insert_with(Vec::new).push(index_name); - - crate::utils::logger::log_info(&format!("Index created on table '{}'", table)); - Ok(()) - } - - /// Удаление индекса - pub fn drop_index(&self, table: &str, name: &str) -> Result<(), DatabaseError> { - if let Some(mut indexes) = self.indexes.get_mut(table) { - if let Some(pos) = indexes.iter().position(|n| n == name) { - indexes.remove(pos); - crate::utils::logger::log_info(&format!("Index '{}' dropped from table '{}'", name, table)); - Ok(()) - } else { - Err(DatabaseError::TransactionError( - format!("Index '{}' not found on table '{}'", name, table) - )) - } - } else { - Err(DatabaseError::TransactionError( - format!("No indexes found for table '{}'", table) - )) - } - } - - /// Создание триггера - pub fn create_trigger( - &self, - name: &str, - table: &str, - timing: crate::parser::sql::TriggerTiming, - event: crate::parser::sql::TriggerEvent, - action: &str, - ) -> Result<(), DatabaseError> { - let trigger = Trigger { - name: name.to_string(), - table: table.to_string(), - timing, - event, - action: action.to_string(), - }; - - self.triggers.insert(name.to_string(), trigger); - - crate::utils::logger::log_info(&format!("Trigger '{}' created", name)); - Ok(()) - } - - /// Удаление триггера - pub fn drop_trigger(&self, name: &str) -> Result<(), DatabaseError> { - if self.triggers.remove(name).is_some() { - crate::utils::logger::log_info(&format!("Trigger '{}' dropped", name)); - Ok(()) - } else { - Err(DatabaseError::TransactionError( - format!("Trigger '{}' not found", name) - )) - } - } - - /// Получение таблицы (неизменяемая ссылка) - /// - /// Wait-free операция: не блокирует другие операции с базой данных. - /// - /// # Аргументы - /// * `name` - имя таблицы - /// - /// # Возвращает - /// * `Option` - копия таблицы или None если не найдена - pub fn get_table(&self, name: &str) -> Option
{ - self.tables.get(name).map(|entry| entry.value().clone()) - } - - /// Получение таблицы для изменения - /// - /// Эта операция может блокировать другие операции с той же таблицей, - /// но не блокирует операции с другими таблицами в базе данных. - /// - /// # Аргументы - /// * `name` - имя таблицы - /// - /// # Возвращает - /// * `Option R>` - функция для изменения таблицы - pub fn with_table_mut(&self, name: &str, f: F) -> Option - where - F: FnOnce(&mut Table) -> R, - { - if let Some(mut table) = self.tables.get_mut(name) { - Some(f(&mut table)) - } else { - None - } - } - - /// Параллельное выполнение SQL запросов - pub async fn execute_parallel(&self, queries: Vec) -> Result, DatabaseError> { - use tokio::task::JoinSet; - let mut results = Vec::with_capacity(queries.len()); - let mut tasks = JoinSet::new(); - - for query in queries { - let db_name = self.name.clone(); - tasks.spawn(async move { - // Здесь должен быть парсинг и выполнение запроса - // Для примера просто возвращаем результат - format!("Executed in {}: {}", db_name, query) - }); - } - - while let Some(result) = tasks.join_next().await { - match result { - Ok(res) => results.push(res), - Err(e) => return Err(DatabaseError::TransactionError(format!("Task error: {}", e))), - } - } - - Ok(results) - } - - /// Удаление базы данных - /// - /// Удаляет директорию базы данных со всеми таблицами и данными. - /// Использует wait-free подход для установки флага неактивности. - /// - /// # Возвращает - /// * `Result<(), DatabaseError>` - успех или ошибка удаления - pub async fn drop(self) -> Result<(), DatabaseError> { - // Устанавливаем флаг неактивности wait-free способом - self.is_active.store(false, Ordering::SeqCst); - - if Path::new(&self.data_dir).exists() { - // Рекурсивно удаляем директорию базы данных асинхронно - tokio::fs::remove_dir_all(&self.data_dir).await - .map_err(|e| DatabaseError::IoError(std::io::Error::from(e)))?; - - // Логируем удаление базы данных - crate::utils::logger::log_info(&format!("Database '{}' dropped", self.name)); - } - - Ok(()) - } - - /// Получение списка таблиц в базе данных - /// - /// Wait-free операция: просто возвращает копию ключей DashMap. - /// - /// # Возвращает - /// * `Vec` - имена всех таблиц в базе данных - pub fn list_tables(&self) -> Vec { - self.tables.iter().map(|entry| entry.key().clone()).collect() - } - - /// Сохранение метаданных базы данных - /// - /// Сохраняет информацию о всех таблицах в файл meta.json. - /// Эта операция может блокировать доступ к файлу на короткое время. - /// - /// # Возвращает - /// * `Result<(), DatabaseError>` - успех или ошибка сохранения - fn save_metadata(&self) -> Result<(), DatabaseError> { - let meta_path = format!("{}/meta.json", self.data_dir); - - // Собираем имена всех таблиц - let table_names: Vec = self.list_tables(); - - // Сериализуем в JSON - let meta_content = serde_json::to_string_pretty(&table_names) - .map_err(|e| DatabaseError::SerializeError(e))?; - - // Записываем в файл - fs::write(meta_path, meta_content) - .map_err(|e| DatabaseError::IoError(e)) - } - - /// Получение имени базы данных - /// - /// Wait-free операция: просто возвращает ссылку на строку. - /// - /// # Возвращает - /// * `&str` - имя базы данных - pub fn name(&self) -> &str { - &self.name - } - - /// Получение количества таблиц в базе данных - /// - /// Wait-free операция: просто возвращает размер DashMap. - /// - /// # Возвращает - /// * `usize` - количество таблиц - pub fn table_count(&self) -> usize { - self.tables.len() - } - - /// Получение директории данных базы - /// - /// Wait-free операция: просто возвращает ссылку на строку. - /// - /// # Возвращает - /// * `&str` - путь к директории данных - pub fn data_dir(&self) -> &str { - &self.data_dir - } - - /// Проверка активности базы данных - /// - /// Wait-free операция: использует атомарное чтение булевой переменной. - /// - /// # Возвращает - /// * `bool` - true если база данных активна - pub fn is_active(&self) -> bool { - self.is_active.load(Ordering::SeqCst) - } - - /// Начало транзакции - /// - /// В текущей реализации транзакции эмулируются на уровне REPL. - /// В будущих версиях может быть реализована полноценная поддержка WAL. - /// - /// # Возвращает - /// * `Result<(), DatabaseError>` - успех или ошибка - pub fn begin_transaction(&self) -> Result<(), DatabaseError> { - // В текущей реализации просто логируем начало транзакции - crate::utils::logger::log_info(&format!("Transaction started in database '{}'", self.name)); - Ok(()) - } - - /// Фиксация транзакции - /// - /// Сохраняет все изменения, сделанные в текущей транзакции. - /// - /// # Возвращает - /// * `Result<(), DatabaseError>` - успех или ошибка - pub fn commit_transaction(&self) -> Result<(), DatabaseError> { - // В текущей реализации просто логируем фиксацию транзакции - crate::utils::logger::log_info(&format!("Transaction committed in database '{}'", self.name)); - Ok(()) - } - - /// Откат транзакции - /// - /// Отменяет все изменения, сделанные в текущей транзакции. - /// - /// # Возвращает - /// * `Result<(), DatabaseError>` - успех или ошибка - pub fn rollback_transaction(&self) -> Result<(), DatabaseError> { - // В текущей реализации просто логируем откат транзакции - crate::utils::logger::log_info(&format!("Transaction rolled back in database '{}'", self.name)); - Ok(()) - } -} - -/// Ошибки базы данных -/// -/// Определяет все возможные ошибки, которые могут возникнуть -/// при работе с базами данных flusql. -#[derive(Debug, Error)] -pub enum DatabaseError { - #[error("Database already exists: {0}")] - AlreadyExists(String), - - #[error("Database not found: {0}")] - NotFound(String), - - #[error("Table already exists: {0}")] - TableExists(String), - - #[error("Table not found: {0}")] - TableNotFound(String), - - #[error("IO error: {0}")] - IoError(#[from] std::io::Error), - - #[error("Parse error: {0}")] - ParseError(serde_json::Error), - - #[error("Serialize error: {0}")] - SerializeError(serde_json::Error), - - #[error("Configuration error: {0}")] - ConfigError(String), - - #[error("Table error: {0}")] - TableError(#[from] crate::core::table::TableError), - - #[error("Transaction error: {0}")] - TransactionError(String), - - #[error("Concurrency error: {0}")] - ConcurrencyError(String), -}