Delete src/core/table.rs

This commit is contained in:
Григорий Сафронов 2026-01-15 19:38:34 +00:00
parent dce895165b
commit b7d377601a

View File

@ -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<ColumnSchema>,
/// Имя столбца, являющегося первичным ключом
pub primary_key: Option<String>,
/// Список индексированных столбцов
pub indexes: Vec<String>,
/// Внешние ключи
pub foreign_keys: Vec<ForeignKeyDef>,
/// Проверочные ограничения
pub checks: Vec<String>,
}
/// Схема внешнего ключа
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ForeignKeyDef {
pub name: String,
pub local_columns: Vec<String>,
pub referenced_table: String,
pub referenced_columns: Vec<String>,
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<String, Value>,
}
/// Таблица базы данных
#[derive(Debug, Clone)]
pub struct Table {
/// Имя таблицы
name: String,
/// Схема таблицы
schema: TableSchema,
/// Директория для хранения данных таблицы
data_dir: String,
/// Список записей таблицы (для обратной совместимости)
records: Vec<Record>,
/// Следующий доступный ID для новой записи
next_id: u64,
/// Первичный индекс (если есть первичный ключ)
primary_index: Option<Index>,
/// Вторичные индексы
secondary_indexes: HashMap<String, Index>,
}
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<Self, TableError> {
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<Record> = 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<String, Value>) -> Result<u64, TableError> {
// Валидация данных
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<usize>,
order_by: Option<Vec<(String, bool)>>, group_by: Option<Vec<String>>,
joins: Option<Vec<crate::parser::sql::JoinClause>>)
-> Result<Vec<HashMap<String, Value>>, 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::<Vec<_>>()
} 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<String, Value>, where_clause: Option<WhereClause>)
-> Result<usize, TableError> {
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<String, Value> = 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<WhereClause>) -> Result<usize, TableError> {
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<usize, TableError> {
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::<Vec<_>>();
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::<i64>() {
Value::Integer(int_val)
} else if let Ok(float_val) = field.parse::<f64>() {
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<Value> {
match &clause.right {
Some(Expression::Value(value)) => Some(value.clone()),
_ => None,
}
}
/// Валидация записи
fn validate_record(&self, values: &HashMap<String, Value>) -> 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<HashMap<String, Value>>, 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<HashMap<String, Value>>, group_by: &[String])
-> Vec<HashMap<String, Value>> {
// Упрощенная реализация группировки
let mut grouped = Vec::new();
let mut groups = HashMap::new();
for row in results {
let key: Vec<String> = 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<String, Value> {
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<String, Value>, old_values: &HashMap<String, Value>) {
// Обновляем первичный индекс
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),
}