From b7d377601ac0abe2c2a1ed0ef36ade10d3327f62 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:34 +0000 Subject: [PATCH] Delete src/core/table.rs --- src/core/table.rs | 938 ---------------------------------------------- 1 file changed, 938 deletions(-) delete mode 100644 src/core/table.rs diff --git a/src/core/table.rs b/src/core/table.rs deleted file mode 100644 index 9407b95..0000000 --- a/src/core/table.rs +++ /dev/null @@ -1,938 +0,0 @@ -//! Модуль управления таблицами flusql -//! -//! Этот модуль реализует функциональность таблиц базы данных: -//! - Создание и загрузку таблиц -//! - Вставку и выборку данных -//! - Экспорт/импорт данных в формате CSV -//! - Управление индексами -//! - Валидацию данных по схеме таблицы -//! - Поддержку внешних ключей, проверок и триггеров - -use std::collections::HashMap; -use std::fs; -use std::path::Path; -use serde::{Deserialize, Serialize}; -use crate::parser::sql::{Value, WhereClause, Operator, Expression}; -use crate::core::index::Index; -use thiserror::Error; - -/// Схема таблицы -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct TableSchema { - /// Список столбцов таблицы - pub columns: Vec, - - /// Имя столбца, являющегося первичным ключом - pub primary_key: Option, - - /// Список индексированных столбцов - pub indexes: Vec, - - /// Внешние ключи - pub foreign_keys: Vec, - - /// Проверочные ограничения - pub checks: Vec, -} - -/// Схема внешнего ключа -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ForeignKeyDef { - pub name: String, - pub local_columns: Vec, - pub referenced_table: String, - pub referenced_columns: Vec, - pub on_delete: String, - pub on_update: String, -} - -/// Схема столбца -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ColumnSchema { - /// Имя столбца - pub name: String, - - /// Тип данных столбца - pub data_type: DataType, - - /// Может ли столбец содержать NULL значения - pub nullable: bool, - - /// Должны ли значения в столбце быть уникальными - pub unique: bool, -} - -/// Тип данных -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum DataType { - /// Целочисленный тип - Integer, - - /// Текстовый тип - Text, - - /// Логический тип - Boolean, - - /// Число с плавающей точкой - Float, -} - -/// Запись в таблице -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct Record { - /// Уникальный идентификатор записи - pub id: u64, - - /// Значения записи по столбцам - pub values: HashMap, -} - -/// Таблица базы данных -#[derive(Debug, Clone)] -pub struct Table { - /// Имя таблицы - name: String, - - /// Схема таблицы - schema: TableSchema, - - /// Директория для хранения данных таблицы - data_dir: String, - - /// Список записей таблицы (для обратной совместимости) - records: Vec, - - /// Следующий доступный ID для новой записи - next_id: u64, - - /// Первичный индекс (если есть первичный ключ) - primary_index: Option, - - /// Вторичные индексы - secondary_indexes: HashMap, -} - -impl Table { - /// Создание новой таблицы - pub fn new(name: &str, schema: TableSchema, data_dir: &str) -> Self { - Self { - name: name.to_string(), - schema: schema.clone(), - data_dir: data_dir.to_string(), - records: Vec::new(), - next_id: 1, - primary_index: if schema.primary_key.is_some() { - Some(Index::new(&schema.primary_key.clone().unwrap())) - } else { - None - }, - secondary_indexes: HashMap::new(), - } - } - - /// Загрузка таблицы из файла - pub fn load(data_dir: &str, name: &str) -> Result { - let schema_path = format!("{}/{}/schema.json", data_dir, name); - let data_path = format!("{}/{}/data.json", data_dir, name); - - if !Path::new(&schema_path).exists() { - return Err(TableError::NotFound(name.to_string())); - } - - // Загрузка схемы - let schema_content = fs::read_to_string(&schema_path) - .map_err(|e| TableError::IoError(e))?; - let schema: TableSchema = serde_json::from_str(&schema_content) - .map_err(|e| TableError::ParseError(e))?; - - // Загрузка данных - let mut table = Self::new(name, schema, data_dir); - - if Path::new(&data_path).exists() { - let data_content = fs::read_to_string(&data_path) - .map_err(|e| TableError::IoError(e))?; - let records: Vec = serde_json::from_str(&data_content) - .map_err(|e| TableError::ParseError(e))?; - - table.records = records; - table.next_id = table.records.iter().map(|r| r.id).max().unwrap_or(0) + 1; - - // Восстановление индексов - table.rebuild_indexes(); - } - - Ok(table) - } - - /// Сохранение таблицы - pub fn save(&self) -> Result<(), TableError> { - let table_dir = format!("{}/{}", self.data_dir, self.name); - - if !Path::new(&table_dir).exists() { - fs::create_dir_all(&table_dir) - .map_err(|e| TableError::IoError(e))?; - } - - // Сохранение схемы - let schema_path = format!("{}/schema.json", table_dir); - let schema_content = serde_json::to_string_pretty(&self.schema) - .map_err(|e| TableError::SerializeError(e))?; - fs::write(schema_path, schema_content) - .map_err(|e| TableError::IoError(e))?; - - // Сохранение данных - let data_path = format!("{}/data.json", table_dir); - let data_content = serde_json::to_string_pretty(&self.records) - .map_err(|e| TableError::SerializeError(e))?; - fs::write(data_path, data_content) - .map_err(|e| TableError::IoError(e))?; - - Ok(()) - } - - /// Вставка записи с автоматическим добавлением timestamp - pub fn insert(&mut self, values: HashMap) -> Result { - // Валидация данных - self.validate_record(&values)?; - - let id = self.next_id; - self.next_id += 1; - - let mut record = Record { id, values }; - - // Применение значений по умолчанию и добавление timestamp - for column in &self.schema.columns { - if !record.values.contains_key(&column.name) { - if column.name == "timestamp" { - // Автоматическое добавление текущего времени - let timestamp = chrono::Local::now().to_rfc3339(); - record.values.insert(column.name.clone(), Value::Text(timestamp)); - } else if column.nullable { - record.values.insert(column.name.clone(), Value::Null); - } - } - } - - // Проверка уникальности - for column in &self.schema.columns { - if column.unique && column.name != "timestamp" { - if let Some(value) = record.values.get(&column.name) { - for existing_record in &self.records { - if let Some(existing_value) = existing_record.values.get(&column.name) { - if values_equal(value, existing_value) { - return Err(TableError::DuplicateValue( - column.name.clone(), - format!("{:?}", value) - )); - } - } - } - } - } - } - - // Проверка внешних ключей - self.validate_foreign_keys(&record)?; - - // Проверка ограничений CHECK - self.validate_checks(&record)?; - - self.records.push(record.clone()); - - // Обновление индексов - self.update_indexes(&record); - - // Выполнение триггеров (если есть) - self.execute_triggers("INSERT", &record); - - // Используем функцию логирования вместо макроса - crate::utils::logger::log_debug(&format!("Inserted record with id {} into table '{}'", id, self.name)); - Ok(id) - } - - /// Выборка записей с сохранением порядка столбцов - pub fn select(&self, columns: &[String], where_clause: Option<&WhereClause>, limit: Option, - order_by: Option>, group_by: Option>, - joins: Option>) - -> Result>, TableError> { - - let mut results = Vec::new(); - - // Определяем порядок столбцов для результата - let result_columns = if columns.len() == 1 && columns[0] == "*" { - // Если SELECT *, используем порядок столбцов из схемы - self.schema.columns.iter().map(|c| c.name.clone()).collect::>() - } else { - // Иначе используем порядок из запроса - columns.to_vec() - }; - - // Используем индекс для поиска, если возможно - if let Some(clause) = where_clause { - let column_name = self.extract_column_from_where(clause); - if let Some(index) = self.get_index_for_column(&column_name) { - let clause_value = self.extract_value_from_where(clause); - if let Some(clause_value) = clause_value { - if let Some(record_ids) = index.search(&clause_value) { - for id in record_ids { - if let Some(record) = self.records.iter().find(|r| r.id == id) { - if self.matches_where(record, clause) { - results.push(self.project_record(record, &result_columns)); - } - } - } - } - } - } else { - // Полный перебор - for record in &self.records { - if self.matches_where(record, clause) { - results.push(self.project_record(record, &result_columns)); - } - } - } - } else { - // Без условия WHERE - for record in &self.records { - results.push(self.project_record(record, &result_columns)); - } - } - - // Применяем сортировку - if let Some(order_by) = order_by { - self.sort_results(&mut results, &order_by); - } - - // Применяем группировку - if let Some(group_by) = group_by { - results = self.group_results(results, &group_by); - } - - // Применяем объединения - if let Some(joins) = joins { - // Упрощенная реализация JOIN - for join in joins { - // В реальной реализации здесь нужно выполнять JOIN - let _ = join; - } - } - - // Применяем LIMIT - if let Some(limit) = limit { - results.truncate(limit); - } - - Ok(results) - } - - /// Обновление записей - pub fn update(&mut self, updates: HashMap, where_clause: Option) - -> Result { - let mut count = 0; - - // Сначала собираем ID записей, которые нужно обновить - let mut ids_to_update = Vec::new(); - let mut record_data = Vec::new(); - - for record in &self.records { - let mut matches = true; - - if let Some(clause) = &where_clause { - if !self.matches_where(record, clause) { - matches = false; - } - } - - if matches { - ids_to_update.push(record.id); - // Сохраняем копию значений для проверки - let mut new_values = record.values.clone(); - for (column, value) in &updates { - new_values.insert(column.clone(), value.clone()); - } - record_data.push((record.id, new_values, record.values.clone())); - } - } - - // Проверяем валидность всех обновлений - for (id, new_values, _) in &record_data { - self.validate_record(new_values)?; - } - - // Применяем обновления - for (id, new_values, old_values) in record_data { - // Находим запись по ID и обновляем ее - if let Some(record_idx) = self.records.iter().position(|r| r.id == id) { - // Выполнение триггеров BEFORE UPDATE - let record_before = &self.records[record_idx]; - self.execute_triggers("BEFORE_UPDATE", record_before); - - // Сохраняем старые значения для индексов - let old_values_for_indexes: HashMap = updates.keys() - .filter_map(|col| old_values.get(col).map(|v| (col.clone(), v.clone()))) - .collect(); - - // Применяем обновления к записи - let record = &mut self.records[record_idx]; - for (column, value) in &updates { - record.values.insert(column.clone(), value.clone()); - } - - // Обновляем индексы - let record_clone = record.clone(); - self.update_indexes_for_record(id, &updates, &old_values_for_indexes); - - // Выполнение триггеров AFTER UPDATE - self.execute_triggers("AFTER_UPDATE", &record_clone); - - count += 1; - } - } - - if count > 0 { - crate::utils::logger::log_debug(&format!("Updated {} records in table '{}'", count, self.name)); - } - - Ok(count) - } - - /// Удаление записей - pub fn delete(&mut self, where_clause: Option) -> Result { - let mut count = 0; - let mut to_remove = Vec::new(); - - for (i, record) in self.records.iter().enumerate() { - let mut matches = true; - - if let Some(clause) = &where_clause { - if !self.matches_where(record, clause) { - matches = false; - } - } - - if matches { - // Выполнение триггеров BEFORE DELETE - self.execute_triggers("BEFORE_DELETE", record); - - // Проверка внешних ключей (ограничение удаления) - self.check_foreign_key_constraints(record)?; - - to_remove.push((i, record.clone())); - - // Выполнение триггеров AFTER DELETE - self.execute_triggers("AFTER_DELETE", record); - - count += 1; - } - } - - // Удаляем записи в обратном порядке - for (i, record) in to_remove.into_iter().rev() { - self.records.remove(i); - self.remove_from_indexes(&record); - } - - if count > 0 { - crate::utils::logger::log_debug(&format!("Deleted {} records from table '{}'", count, self.name)); - } - - Ok(count) - } - - /// Экспорт в CSV - pub fn export_csv(&self, file_path: &str) -> Result<(), TableError> { - use csv::Writer; - - let mut wtr = Writer::from_path(file_path) - .map_err(|e| TableError::IoError(std::io::Error::new( - std::io::ErrorKind::Other, e.to_string() - )))?; - - // Записываем заголовки в порядке из схемы - let headers: Vec<&str> = self.schema.columns.iter() - .map(|c| c.name.as_str()) - .collect(); - wtr.write_record(&headers) - .map_err(|e| TableError::ExportError(e.to_string()))?; - - // Записываем данные - for record in &self.records { - let mut row = Vec::new(); - for column in &self.schema.columns { - // Используем метод to_string из модуля parser::sql - let value = record.values.get(&column.name) - .map(|v| v.to_string()) - .unwrap_or_else(|| "NULL".to_string()); - row.push(value); - } - wtr.write_record(&row) - .map_err(|e| TableError::ExportError(e.to_string()))?; - } - - wtr.flush() - .map_err(|e| TableError::IoError(std::io::Error::new( - std::io::ErrorKind::Other, e.to_string() - )))?; - - // Используем функцию логирования вместо макроса - crate::utils::logger::log_info(&format!("Exported table '{}' to '{}'", self.name, file_path)); - Ok(()) - } - - /// Импорт из CSV - pub fn import_csv(&mut self, file_path: &str) -> Result { - use csv::Reader; - - let mut rdr = Reader::from_path(file_path) - .map_err(|e| TableError::IoError(std::io::Error::new( - std::io::ErrorKind::Other, e.to_string() - )))?; - - let headers = rdr.headers() - .map_err(|e| TableError::ImportError(e.to_string()))? - .iter() - .map(|s| s.to_string()) - .collect::>(); - - let mut count = 0; - for result in rdr.records() { - let record = result - .map_err(|e| TableError::ImportError(e.to_string()))?; - - let mut values = HashMap::new(); - for (i, field) in record.iter().enumerate() { - if i < headers.len() { - let value = if field == "NULL" { - Value::Null - } else if let Ok(int_val) = field.parse::() { - Value::Integer(int_val) - } else if let Ok(float_val) = field.parse::() { - Value::Float(float_val) - } else if field == "TRUE" { - Value::Boolean(true) - } else if field == "FALSE" { - Value::Boolean(false) - } else { - Value::Text(field.to_string()) - }; - - values.insert(headers[i].clone(), value); - } - } - - // Автоматически добавляем timestamp если его нет - if !values.contains_key("timestamp") { - let timestamp = chrono::Local::now().to_rfc3339(); - values.insert("timestamp".to_string(), Value::Text(timestamp)); - } - - self.insert(values)?; - count += 1; - } - - // Используем функцию логирования вместо макроса - crate::utils::logger::log_info(&format!("Imported {} records into table '{}' from '{}'", count, self.name, file_path)); - Ok(count) - } - - /// Получение схемы таблицы - pub fn get_schema(&self) -> TableSchema { - self.schema.clone() - } - - /// Извлечение имени столбца из условия WHERE - fn extract_column_from_where(&self, clause: &WhereClause) -> String { - match &clause.left { - Expression::Column(name) => name.clone(), - _ => "".to_string(), - } - } - - /// Извлечение значения из условия WHERE - fn extract_value_from_where(&self, clause: &WhereClause) -> Option { - match &clause.right { - Some(Expression::Value(value)) => Some(value.clone()), - _ => None, - } - } - - /// Валидация записи - fn validate_record(&self, values: &HashMap) -> Result<(), TableError> { - for column in &self.schema.columns { - if let Some(value) = values.get(&column.name) { - // Пропускаем валидацию timestamp если он автоматический - if column.name == "timestamp" { - continue; - } - - // Проверка типа данных - match (&column.data_type, value) { - (DataType::Integer, Value::Integer(_)) => {}, - (DataType::Text, Value::Text(_)) => {}, - (DataType::Boolean, Value::Boolean(_)) => {}, - (DataType::Float, Value::Float(_)) => {}, - (DataType::Float, Value::Integer(i)) => { - // Разрешаем целые числа для float - let _ = i; - }, - _ => { - return Err(TableError::TypeMismatch( - column.name.clone(), - format!("{:?}", column.data_type), - format!("{:?}", value) - )); - } - } - } else if !column.nullable && column.name != "timestamp" { - return Err(TableError::NotNullViolation(column.name.clone())); - } - } - - Ok(()) - } - - /// Проверка внешних ключей - fn validate_foreign_keys(&self, record: &Record) -> Result<(), TableError> { - // Упрощенная реализация - // В реальной системе здесь нужно проверять ссылочную целостность - Ok(()) - } - - /// Проверка ограничений CHECK - fn validate_checks(&self, record: &Record) -> Result<(), TableError> { - // Упрощенная реализация - // В реальной системе здесь нужно проверять CHECK ограничения - Ok(()) - } - - /// Проверка ограничений внешних ключей при удалении - fn check_foreign_key_constraints(&self, record: &Record) -> Result<(), TableError> { - // Упрощенная реализация - // В реальной системе здесь нужно проверять, что удаление не нарушает - // ссылочную целостность в других таблицах - Ok(()) - } - - /// Выполнение триггеров - fn execute_triggers(&self, event: &str, record: &Record) { - // Упрощенная реализация - // В реальной системе здесь нужно выполнять зарегистрированные триггеры - crate::utils::logger::log_debug(&format!("Trigger {} executed for record {} in table '{}'", - event, record.id, self.name)); - } - - /// Сортировка результатов - fn sort_results(&self, results: &mut Vec>, order_by: &[(String, bool)]) { - results.sort_by(|a, b| { - for (column, ascending) in order_by { - let a_val = a.get(column); - let b_val = b.get(column); - - match (a_val, b_val) { - (Some(a), Some(b)) => { - let cmp = self.compare_values(a, b); - if cmp != std::cmp::Ordering::Equal { - return if *ascending { cmp } else { cmp.reverse() }; - } - } - _ => {} - } - } - std::cmp::Ordering::Equal - }); - } - - /// Группировка результатов - fn group_results(&self, results: Vec>, group_by: &[String]) - -> Vec> { - // Упрощенная реализация группировки - let mut grouped = Vec::new(); - let mut groups = HashMap::new(); - - for row in results { - let key: Vec = group_by.iter() - .filter_map(|col| row.get(col).map(|v| v.to_string())) - .collect(); - let key_str = key.join("|"); - - groups.entry(key_str).or_insert_with(Vec::new).push(row); - } - - for (_, rows) in groups { - // Для упрощения берем первую строку из каждой группы - if let Some(first_row) = rows.into_iter().next() { - grouped.push(first_row); - } - } - - grouped - } - - /// Сравнение значений для сортировки - fn compare_values(&self, a: &Value, b: &Value) -> std::cmp::Ordering { - match (a, b) { - (Value::Integer(a), Value::Integer(b)) => a.cmp(b), - (Value::Float(a), Value::Float(b)) => { - if a < b { - std::cmp::Ordering::Less - } else if a > b { - std::cmp::Ordering::Greater - } else { - std::cmp::Ordering::Equal - } - }, - (Value::Text(a), Value::Text(b)) => a.cmp(b), - (Value::Boolean(a), Value::Boolean(b)) => a.cmp(b), - (Value::Null, Value::Null) => std::cmp::Ordering::Equal, - (Value::Null, _) => std::cmp::Ordering::Less, - (_, Value::Null) => std::cmp::Ordering::Greater, - _ => std::cmp::Ordering::Equal, - } - } - - /// Проверка условия WHERE - fn matches_where(&self, record: &Record, clause: &WhereClause) -> bool { - // В новой структуре WhereClause нет полей column и value - // Используем временную реализацию для совместимости - let column_name = match &clause.left { - Expression::Column(name) => name, - _ => return false, - }; - - let clause_value = match &clause.right { - Some(Expression::Value(value)) => value, - None => { - // Обработка IS NULL и IS NOT NULL - return match &clause.operator { - Operator::IsNull => { - match record.values.get(column_name) { - Some(value) => matches!(value, Value::Null), - None => true, - } - } - Operator::IsNotNull => { - match record.values.get(column_name) { - Some(value) => !matches!(value, Value::Null), - None => false, - } - } - _ => false, - }; - } - _ => return false, - }; - - match record.values.get(column_name) { - Some(value) => match &clause.operator { - Operator::Eq => values_equal(value, clause_value), - Operator::Ne => !values_equal(value, clause_value), - Operator::Gt => value_gt(value, clause_value), - Operator::Lt => value_lt(value, clause_value), - Operator::Ge => values_equal(value, clause_value) || value_gt(value, clause_value), - Operator::Le => values_equal(value, clause_value) || value_lt(value, clause_value), - Operator::Like => { - if let (Value::Text(s), Value::Text(pattern)) = (value, clause_value) { - // Простая реализация LIKE - pattern == "%" || s.contains(pattern.trim_matches('%')) - } else { - false - } - } - _ => false, // Другие операторы пока не поддерживаются - }, - None => false, - } - } - - /// Проекция записи с сохранением порядка столбцов - fn project_record(&self, record: &Record, columns: &[String]) -> HashMap { - let mut result = HashMap::new(); - - // Добавляем значения в заданном порядке столбцов - for column_name in columns { - if let Some(value) = record.values.get(column_name) { - result.insert(column_name.clone(), value.clone()); - } else { - // Если столбец не существует в записи, добавляем NULL - result.insert(column_name.clone(), Value::Null); - } - } - - result - } - - /// Получение индекса для столбца - fn get_index_for_column(&self, column: &str) -> Option<&Index> { - if Some(column) == self.schema.primary_key.as_deref() { - self.primary_index.as_ref() - } else { - self.secondary_indexes.get(column) - } - } - - /// Перестроение индексов - fn rebuild_indexes(&mut self) { - // Перестраиваем первичный индекс - if let Some(pk) = &self.schema.primary_key { - let mut index = Index::new(pk); - for record in &self.records { - if let Some(value) = record.values.get(pk) { - index.insert(value.clone(), record.id); - } - } - self.primary_index = Some(index); - } - - // Перестраиваем вторичные индексы - for index_name in &self.schema.indexes { - let mut index = Index::new(index_name); - for record in &self.records { - if let Some(value) = record.values.get(index_name) { - index.insert(value.clone(), record.id); - } - } - self.secondary_indexes.insert(index_name.clone(), index); - } - } - - /// Обновление индексов для новой записи - fn update_indexes(&mut self, record: &Record) { - // Обновляем первичный индекс - if let (Some(pk), Some(index)) = (&self.schema.primary_key, &mut self.primary_index) { - if let Some(value) = record.values.get(pk) { - index.insert(value.clone(), record.id); - } - } - - // Обновляем вторичные индексы - for (index_name, index) in &mut self.secondary_indexes { - if let Some(value) = record.values.get(index_name) { - index.insert(value.clone(), record.id); - } - } - } - - /// Обновление индексов для измененной записи - fn update_indexes_for_record(&mut self, record_id: u64, updates: &HashMap, old_values: &HashMap) { - // Обновляем первичный индекс - if let (Some(pk), Some(index)) = (&self.schema.primary_key, &mut self.primary_index) { - if let Some(new_value) = updates.get(pk) { - // Удаляем старое значение - if let Some(old_value) = old_values.get(pk) { - index.remove(old_value, record_id); - } - // Вставляем новое значение - index.insert(new_value.clone(), record_id); - } - } - - // Обновляем вторичные индексы - for (index_name, index) in &mut self.secondary_indexes { - if let Some(new_value) = updates.get(index_name) { - // Удаляем старое значение - if let Some(old_value) = old_values.get(index_name) { - index.remove(old_value, record_id); - } - // Вставляем новое значение - index.insert(new_value.clone(), record_id); - } - } - } - - /// Удаление из индексов - fn remove_from_indexes(&mut self, record: &Record) { - // Удаляем из первичного индекса - if let (Some(pk), Some(index)) = (&self.schema.primary_key, &mut self.primary_index) { - if let Some(value) = record.values.get(pk) { - index.remove(value, record.id); - } - } - - // Удаляем из вторичных индексов - for (index_name, index) in &mut self.secondary_indexes { - if let Some(value) = record.values.get(index_name) { - index.remove(value, record.id); - } - } - } - - /// Получение имени таблицы - pub fn name(&self) -> &str { - &self.name - } - - /// Получение количества записей в таблице - pub fn record_count(&self) -> usize { - self.records.len() - } -} - -/// Сравнение значений на равенство -fn values_equal(v1: &Value, v2: &Value) -> bool { - match (v1, v2) { - (Value::Integer(a), Value::Integer(b)) => a == b, - (Value::Text(a), Value::Text(b)) => a == b, - (Value::Boolean(a), Value::Boolean(b)) => a == b, - (Value::Float(a), Value::Float(b)) => (a - b).abs() < f64::EPSILON, - (Value::Null, Value::Null) => true, - _ => false, - } -} - -/// Сравнение значений (больше) -fn value_gt(v1: &Value, v2: &Value) -> bool { - match (v1, v2) { - (Value::Integer(a), Value::Integer(b)) => a > b, - (Value::Float(a), Value::Float(b)) => a > b, - (Value::Text(a), Value::Text(b)) => a > b, - _ => false, - } -} - -/// Сравнение значений (меньше) -fn value_lt(v1: &Value, v2: &Value) -> bool { - match (v1, v2) { - (Value::Integer(a), Value::Integer(b)) => a < b, - (Value::Float(a), Value::Float(b)) => a < b, - (Value::Text(a), Value::Text(b)) => a < b, - _ => false, - } -} - -/// Ошибки таблицы -#[derive(Debug, Error)] -pub enum TableError { - #[error("Table not found: {0}")] - NotFound(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("Type mismatch in column '{0}': expected {1}, got {2}")] - TypeMismatch(String, String, String), - - #[error("NOT NULL violation in column '{0}'")] - NotNullViolation(String), - - #[error("Duplicate value in unique column '{0}': {1}")] - DuplicateValue(String, String), - - #[error("Foreign key violation: {0}")] - ForeignKeyViolation(String), - - #[error("Check constraint violation: {0}")] - CheckViolation(String), - - #[error("Export error: {0}")] - ExportError(String), - - #[error("Import error: {0}")] - ImportError(String), -}