rustlite/
lib.rs

1//! # RustLite
2//!
3//! A lightweight, high-performance embedded database written in Rust with ACID guarantees.
4//!
5//! ## Quick Start
6//!
7//! ```rust,no_run
8//! use rustlite::Database;
9//!
10//! fn main() -> Result<(), Box<dyn std::error::Error>> {
11//!     // Create a persistent database (data survives restarts)
12//!     let db = Database::open("./my_database")?;
13//!
14//!     // Insert data
15//!     db.put(b"user:1:name", b"Alice")?;
16//!     db.put(b"user:1:email", b"alice@example.com")?;
17//!
18//!     // Retrieve data
19//!     if let Some(name) = db.get(b"user:1:name")? {
20//!         println!("Name: {}", String::from_utf8_lossy(&name));
21//!     }
22//!
23//!     // Delete data
24//!     db.delete(b"user:1:email")?;
25//!
26//!     // Data is automatically persisted to disk
27//!     Ok(())
28//! }
29//! ```
30//!
31//! ## Database Modes
32//!
33//! ```rust,no_run
34//! use rustlite::Database;
35//!
36//! // Persistent database (recommended for production)
37//! let persistent_db = Database::open("./data")?;
38//!
39//! // In-memory database (fast, but data lost on exit)
40//! let memory_db = Database::in_memory()?;
41//! # Ok::<(), rustlite::Error>(())
42//! ```
43//!
44//! ## Indexing (v0.3.0+)
45//!
46//! ```rust
47//! use rustlite::{Database, IndexType};
48//!
49//! let db = Database::in_memory()?;
50//!
51//! // Create indexes
52//! db.create_index("users_by_name", IndexType::BTree)?;
53//! db.create_index("sessions", IndexType::Hash)?;
54//!
55//! // Use indexes for fast lookups
56//! db.index_insert("users_by_name", b"alice", 100)?;
57//! db.index_insert("users_by_name", b"bob", 101)?;
58//!
59//! let results = db.index_find("users_by_name", b"alice")?;
60//! # Ok::<(), rustlite::Error>(())
61//! ```
62//!
63//! ## Features
64//!
65//! - **v0.1.0**: In-memory key-value store with thread-safe concurrent access
66//! - **v0.2.0**: Persistent storage with WAL, SSTable, and crash recovery
67//! - **v0.3.0**: B-Tree and Hash indexing for fast lookups
68//! - **v0.4.0** (planned): SQL-like query engine
69//! - **v1.0.0** (planned): Production-ready with full ACID guarantees
70//!
71//! See [ROADMAP.md](https://github.com/VIRTUMEM-AI-LABS/rustlite/blob/main/docs/ROADMAP.md) for details.
72
73use std::collections::HashMap;
74use std::path::Path;
75use std::sync::{Arc, RwLock};
76
77use tracing::{debug, info, instrument, warn};
78
79pub mod logging;
80mod security;
81
82// Re-export core types
83pub use rustlite_core::index::{BTreeIndex, HashIndex, Index, IndexInfo, IndexManager, IndexType};
84pub use rustlite_core::{Error, Result};
85
86// Transaction support (v0.5.0+)
87pub use rustlite_core::transaction::{
88    IsolationLevel, MVCCStorage, Timestamp, Transaction, TransactionId, TransactionManager,
89    VersionChain, VersionedValue,
90};
91
92// Query engine (v0.4.0+)
93pub use rustlite_core::query::{
94    Column, ExecutionContext, Executor, Lexer, Parser, PhysicalPlan, Planner, Query, Row, Value,
95};
96
97// WAL components
98pub use rustlite_wal::{
99    RecoveryManager, RecoveryStats, SyncMode, WalConfig, WalManager, WalReader, WalRecord,
100};
101
102// Storage components
103pub use rustlite_storage::{
104    CompactionConfig, CompactionStats, CompactionWorker, Manifest, Memtable, MemtableEntry,
105    SSTableEntry, SSTableMeta, SSTableReader, SSTableWriter, StorageConfig, StorageEngine,
106    StorageStats,
107};
108
109// Snapshot components
110pub use rustlite_snapshot::{
111    SnapshotConfig, SnapshotFile, SnapshotManager, SnapshotMeta, SnapshotType,
112};
113
114// Version information
115pub const VERSION: &str = env!("CARGO_PKG_VERSION");
116
117/// Storage backend for the database
118enum StorageBackend {
119    /// In-memory storage using HashMap
120    Memory(RwLock<HashMap<Vec<u8>, Vec<u8>>>),
121    /// Persistent storage using LSM-tree
122    Persistent(StorageEngine),
123}
124
125/// Inner database state
126struct DatabaseInner {
127    /// Storage backend
128    storage: StorageBackend,
129    /// Index manager for secondary indexes
130    indexes: RwLock<IndexManager>,
131    /// MVCC transaction manager (v0.5.0+)
132    transaction_manager: Option<Arc<TransactionManager>>,
133}
134
135/// The main database handle.
136///
137/// Provides a unified interface for both in-memory and persistent storage.
138/// Thread-safe and can be cloned to share across threads.
139///
140/// # Examples
141///
142/// ```rust,no_run
143/// use rustlite::Database;
144///
145/// // Open a persistent database
146/// let db = Database::open("./my_data")?;
147/// db.put(b"key", b"value")?;
148///
149/// // Data persists across restarts
150/// drop(db);
151/// let db = Database::open("./my_data")?;
152/// assert_eq!(db.get(b"key")?, Some(b"value".to_vec()));
153/// # Ok::<(), rustlite::Error>(())
154/// ```
155#[derive(Clone)]
156pub struct Database {
157    inner: Arc<DatabaseInner>,
158}
159
160impl Database {
161    /// Opens a persistent database at the specified path.
162    ///
163    /// Creates the directory if it doesn't exist. Data is persisted to disk
164    /// using a Write-Ahead Log (WAL) and SSTable files.
165    ///
166    /// # Arguments
167    ///
168    /// * `path` - Directory path where database files will be stored
169    ///
170    /// # Examples
171    ///
172    /// ```rust,no_run
173    /// use rustlite::Database;
174    ///
175    /// let db = Database::open("./my_database")?;
176    /// db.put(b"hello", b"world")?;
177    /// # Ok::<(), rustlite::Error>(())
178    /// ```
179    pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
180        let path_ref = path.as_ref();
181        info!(path = ?path_ref, "Opening RustLite database");
182
183        let engine = StorageEngine::open(path)?;
184        let mvcc_storage = Arc::new(MVCCStorage::new());
185        let tx_manager = TransactionManager::new(mvcc_storage);
186
187        Ok(Database {
188            inner: Arc::new(DatabaseInner {
189                storage: StorageBackend::Persistent(engine),
190                indexes: RwLock::new(IndexManager::new()),
191                transaction_manager: Some(tx_manager),
192            }),
193        })
194    }
195
196    /// Opens a persistent database with custom configuration.
197    ///
198    /// # Arguments
199    ///
200    /// * `path` - Directory path where database files will be stored
201    /// * `config` - Storage configuration options
202    pub fn open_with_config<P: AsRef<Path>>(path: P, config: StorageConfig) -> Result<Self> {
203        let engine = StorageEngine::open_with_config(path, config)?;
204        let mvcc_storage = Arc::new(MVCCStorage::new());
205        let tx_manager = TransactionManager::new(mvcc_storage);
206
207        Ok(Database {
208            inner: Arc::new(DatabaseInner {
209                storage: StorageBackend::Persistent(engine),
210                indexes: RwLock::new(IndexManager::new()),
211                transaction_manager: Some(tx_manager),
212            }),
213        })
214    }
215
216    /// Creates an in-memory database.
217    ///
218    /// Data is stored only in memory and will be lost when the database
219    /// is dropped. Useful for testing or temporary data.
220    ///
221    /// # Examples
222    ///
223    /// ```rust
224    /// use rustlite::Database;
225    ///
226    /// let db = Database::in_memory()?;
227    /// db.put(b"temp", b"data")?;
228    /// // Data is lost when db goes out of scope
229    /// # Ok::<(), rustlite::Error>(())
230    /// ```
231    pub fn in_memory() -> Result<Self> {
232        info!("Creating in-memory RustLite database");
233
234        let mvcc_storage = Arc::new(MVCCStorage::new());
235        let tx_manager = TransactionManager::new(mvcc_storage);
236
237        Ok(Database {
238            inner: Arc::new(DatabaseInner {
239                storage: StorageBackend::Memory(RwLock::new(HashMap::new())),
240                indexes: RwLock::new(IndexManager::new()),
241                transaction_manager: Some(tx_manager),
242            }),
243        })
244    }
245
246    /// Creates a new in-memory database (alias for `in_memory()`).
247    ///
248    /// For backward compatibility with v0.1.0.
249    #[deprecated(
250        since = "0.2.0",
251        note = "Use `Database::open()` for persistent storage or `Database::in_memory()` for temporary storage"
252    )]
253    pub fn new() -> Result<Self> {
254        Self::in_memory()
255    }
256
257    /// Inserts or updates a key-value pair.
258    ///
259    /// If the key already exists, its value will be updated.
260    ///
261    /// # Arguments
262    ///
263    /// * `key` - The key to insert
264    /// * `value` - The value to associate with the key
265    ///
266    /// # Examples
267    ///
268    /// ```rust,no_run
269    /// use rustlite::Database;
270    ///
271    /// let db = Database::open("./data")?;
272    /// db.put(b"name", b"Alice")?;
273    /// db.put(b"name", b"Bob")?; // Updates the value
274    /// # Ok::<(), rustlite::Error>(())
275    /// ```
276    #[instrument(skip(self, key, value), fields(key_len = key.len(), value_len = value.len()))]
277    pub fn put(&self, key: &[u8], value: &[u8]) -> Result<()> {
278        // Security: Validate inputs
279        security::validate_key(key)?;
280        security::validate_value(value)?;
281
282        debug!("Writing key-value pair");
283
284        match &self.inner.storage {
285            StorageBackend::Memory(store) => {
286                let mut store = store.write().map_err(|_| Error::LockPoisoned)?;
287                store.insert(key.to_vec(), value.to_vec());
288                Ok(())
289            }
290            StorageBackend::Persistent(engine) => engine.put(key, value),
291        }
292    }
293
294    /// Retrieves a value by key.
295    ///
296    /// Returns `None` if the key doesn't exist.
297    ///
298    /// # Arguments
299    ///
300    /// * `key` - The key to look up
301    ///
302    /// # Examples
303    ///
304    /// ```rust,no_run
305    /// use rustlite::Database;
306    ///
307    /// let db = Database::open("./data")?;
308    /// db.put(b"greeting", b"Hello!")?;
309    ///
310    /// match db.get(b"greeting")? {
311    ///     Some(value) => println!("Found: {}", String::from_utf8_lossy(&value)),
312    ///     None => println!("Key not found"),
313    /// }
314    /// # Ok::<(), rustlite::Error>(())
315    /// ```
316    #[instrument(skip(self, key), fields(key_len = key.len()))]
317    pub fn get(&self, key: &[u8]) -> Result<Option<Vec<u8>>> {
318        // Security: Validate inputs
319        security::validate_key(key)?;
320
321        debug!("Reading key");
322
323        match &self.inner.storage {
324            StorageBackend::Memory(store) => {
325                let store = store.read().map_err(|_| Error::LockPoisoned)?;
326                Ok(store.get(key).cloned())
327            }
328            StorageBackend::Persistent(engine) => engine.get(key),
329        }
330    }
331
332    /// Deletes a key-value pair.
333    ///
334    /// Returns `true` if the key existed and was deleted, `false` otherwise.
335    ///
336    /// # Arguments
337    ///
338    /// * `key` - The key to delete
339    ///
340    /// # Examples
341    ///
342    /// ```rust,no_run
343    /// use rustlite::Database;
344    ///
345    /// let db = Database::open("./data")?;
346    /// db.put(b"temp", b"value")?;
347    /// db.delete(b"temp")?;
348    /// assert_eq!(db.get(b"temp")?, None);
349    /// # Ok::<(), rustlite::Error>(())
350    /// ```
351    #[instrument(skip(self, key), fields(key_len = key.len()))]
352    pub fn delete(&self, key: &[u8]) -> Result<bool> {
353        // Security: Validate inputs
354        security::validate_key(key)?;
355
356        debug!("Deleting key");
357
358        match &self.inner.storage {
359            StorageBackend::Memory(store) => {
360                let mut store = store.write().map_err(|_| Error::LockPoisoned)?;
361                Ok(store.remove(key).is_some())
362            }
363            StorageBackend::Persistent(engine) => {
364                // Check if key exists before deleting
365                let existed = engine.get(key)?.is_some();
366                if existed {
367                    engine.delete(key)?;
368                }
369                Ok(existed)
370            }
371        }
372    }
373
374    /// Forces all pending writes to disk.
375    ///
376    /// For persistent databases, this flushes the memtable to SSTable
377    /// and syncs the WAL. For in-memory databases, this is a no-op.
378    ///
379    /// # Examples
380    ///
381    /// ```rust,no_run
382    /// use rustlite::Database;
383    ///
384    /// let db = Database::open("./data")?;
385    /// db.put(b"important", b"data")?;
386    /// db.sync()?; // Ensure data is on disk
387    /// # Ok::<(), rustlite::Error>(())
388    /// ```
389    pub fn sync(&self) -> Result<()> {
390        match &self.inner.storage {
391            StorageBackend::Memory(_) => Ok(()),
392            StorageBackend::Persistent(engine) => engine.sync(),
393        }
394    }
395
396    /// Returns whether this is a persistent database.
397    pub fn is_persistent(&self) -> bool {
398        matches!(&self.inner.storage, StorageBackend::Persistent(_))
399    }
400
401    // =========================================================================
402    // Index Operations (v0.3.0+)
403    // =========================================================================
404
405    /// Creates a new index with the specified name and type.
406    ///
407    /// # Arguments
408    ///
409    /// * `name` - Unique name for the index
410    /// * `index_type` - Type of index (BTree for range queries, Hash for fast lookups)
411    ///
412    /// # Examples
413    ///
414    /// ```rust
415    /// use rustlite::{Database, IndexType};
416    ///
417    /// let db = Database::in_memory()?;
418    /// db.create_index("users_by_name", IndexType::BTree)?;
419    /// db.create_index("sessions", IndexType::Hash)?;
420    /// # Ok::<(), rustlite::Error>(())
421    /// ```
422    #[instrument(skip(self), fields(name = %name, index_type = ?index_type))]
423    pub fn create_index(&self, name: &str, index_type: IndexType) -> Result<()> {
424        // Security: Validate index name
425        security::validate_index_name(name)?;
426
427        info!("Creating index");
428
429        let mut indexes = self
430            .inner
431            .indexes
432            .write()
433            .map_err(|_| Error::LockPoisoned)?;
434        indexes.create_index(name, index_type)
435    }
436
437    /// Drops an index by name.
438    ///
439    /// Returns `true` if the index existed and was dropped.
440    ///
441    /// # Examples
442    ///
443    /// ```rust
444    /// use rustlite::{Database, IndexType};
445    ///
446    /// let db = Database::in_memory()?;
447    /// db.create_index("temp_index", IndexType::Hash)?;
448    /// assert!(db.drop_index("temp_index")?);
449    /// assert!(!db.drop_index("temp_index")?); // Already dropped
450    /// # Ok::<(), rustlite::Error>(())
451    /// ```
452    pub fn drop_index(&self, name: &str) -> Result<bool> {
453        let mut indexes = self
454            .inner
455            .indexes
456            .write()
457            .map_err(|_| Error::LockPoisoned)?;
458        indexes.drop_index(name)
459    }
460
461    /// Inserts a key-value pair into a named index.
462    ///
463    /// The value is typically a record ID or offset pointing to the actual data.
464    ///
465    /// # Examples
466    ///
467    /// ```rust
468    /// use rustlite::{Database, IndexType};
469    ///
470    /// let db = Database::in_memory()?;
471    /// db.create_index("names", IndexType::BTree)?;
472    ///
473    /// // Index "alice" pointing to record ID 100
474    /// db.index_insert("names", b"alice", 100)?;
475    /// db.index_insert("names", b"bob", 101)?;
476    /// # Ok::<(), rustlite::Error>(())
477    /// ```
478    pub fn index_insert(&self, name: &str, key: &[u8], value: u64) -> Result<()> {
479        let mut indexes = self
480            .inner
481            .indexes
482            .write()
483            .map_err(|_| Error::LockPoisoned)?;
484        indexes.insert(name, key, value)
485    }
486
487    /// Finds all values matching a key in a named index.
488    ///
489    /// # Examples
490    ///
491    /// ```rust
492    /// use rustlite::{Database, IndexType};
493    ///
494    /// let db = Database::in_memory()?;
495    /// db.create_index("names", IndexType::Hash)?;
496    /// db.index_insert("names", b"alice", 100)?;
497    ///
498    /// let results = db.index_find("names", b"alice")?;
499    /// assert_eq!(results, vec![100]);
500    /// # Ok::<(), rustlite::Error>(())
501    /// ```
502    pub fn index_find(&self, name: &str, key: &[u8]) -> Result<Vec<u64>> {
503        let indexes = self.inner.indexes.read().map_err(|_| Error::LockPoisoned)?;
504        indexes.find(name, key)
505    }
506
507    /// Removes a key from a named index.
508    ///
509    /// Returns `true` if the key existed and was removed.
510    pub fn index_remove(&self, name: &str, key: &[u8]) -> Result<bool> {
511        let mut indexes = self
512            .inner
513            .indexes
514            .write()
515            .map_err(|_| Error::LockPoisoned)?;
516        indexes.remove(name, key)
517    }
518
519    /// Lists all index names in the database.
520    ///
521    /// # Examples
522    ///
523    /// ```rust
524    /// use rustlite::{Database, IndexType};
525    ///
526    /// let db = Database::in_memory()?;
527    /// db.create_index("idx1", IndexType::BTree)?;
528    /// db.create_index("idx2", IndexType::Hash)?;
529    ///
530    /// let names = db.list_indexes()?;
531    /// assert_eq!(names.len(), 2);
532    /// # Ok::<(), rustlite::Error>(())
533    /// ```
534    pub fn list_indexes(&self) -> Result<Vec<String>> {
535        let indexes = self.inner.indexes.read().map_err(|_| Error::LockPoisoned)?;
536        Ok(indexes
537            .list_indexes()
538            .iter()
539            .map(|s| s.to_string())
540            .collect())
541    }
542
543    /// Gets information about all indexes.
544    ///
545    /// # Examples
546    ///
547    /// ```rust
548    /// use rustlite::{Database, IndexType};
549    ///
550    /// let db = Database::in_memory()?;
551    /// db.create_index("users", IndexType::BTree)?;
552    /// db.index_insert("users", b"alice", 1)?;
553    ///
554    /// for info in db.index_info()? {
555    ///     println!("Index: {}, Type: {}, Entries: {}",
556    ///              info.name, info.index_type, info.entry_count);
557    /// }
558    /// # Ok::<(), rustlite::Error>(())
559    /// ```
560    pub fn index_info(&self) -> Result<Vec<IndexInfo>> {
561        let indexes = self.inner.indexes.read().map_err(|_| Error::LockPoisoned)?;
562        Ok(indexes.index_info())
563    }
564
565    /// Executes a SQL-like query and returns results (v0.4.0+).
566    ///
567    /// Parses, plans, and executes a SELECT query against in-memory data.
568    /// Currently supports: SELECT, FROM, WHERE, ORDER BY, LIMIT, JOIN.
569    ///
570    /// # Arguments
571    ///
572    /// * `sql` - SQL-like query string
573    /// * `context` - Execution context with data and indexes
574    ///
575    /// # Examples
576    ///
577    /// ```rust
578    /// use rustlite::{Database, ExecutionContext, Row, Column, Value};
579    /// use std::collections::HashMap;
580    ///
581    /// let db = Database::in_memory()?;
582    ///
583    /// // Prepare test data
584    /// let mut context = ExecutionContext::new();
585    /// context.data.insert("users".to_string(), vec![
586    ///     Row {
587    ///         columns: vec![
588    ///             Column { name: "name".to_string(), alias: None },
589    ///             Column { name: "age".to_string(), alias: None },
590    ///         ],
591    ///         values: vec![Value::String("Alice".to_string()), Value::Integer(30)],
592    ///     },
593    /// ]);
594    ///
595    /// let results = db.query("SELECT name FROM users WHERE age > 18", context)?;
596    /// assert_eq!(results.len(), 1);
597    /// # Ok::<(), rustlite::Error>(())
598    /// ```
599    #[instrument(skip(self, sql, context), fields(sql_len = sql.len()))]
600    pub fn query(&self, sql: &str, context: ExecutionContext) -> Result<Vec<Row>> {
601        // Security: Validate query length
602        security::validate_query(sql)?;
603
604        debug!(sql = %sql, "Executing query");
605
606        // Parse the SQL
607        let mut parser =
608            Parser::new(sql).map_err(|e| Error::InvalidInput(format!("Parse error: {}", e)))?;
609        let query = parser
610            .parse()
611            .map_err(|e| Error::InvalidInput(format!("Parse error: {}", e)))?;
612
613        // Plan the query
614        let planner = Planner::new();
615        let plan = planner
616            .plan(&query)
617            .map_err(|e| Error::InvalidInput(format!("Planning error: {}", e)))?;
618
619        // Execute the query
620        let mut executor = Executor::new(context);
621        executor.execute(&plan)
622    }
623
624    /// Prepares a SQL-like query for repeated execution (v0.4.0+).
625    ///
626    /// Parses and plans the query once, returning a reusable plan.
627    ///
628    /// # Examples
629    ///
630    /// ```rust
631    /// use rustlite::Database;
632    ///
633    /// let db = Database::in_memory()?;
634    /// let plan = db.prepare("SELECT * FROM users WHERE age > 18")?;
635    /// // Plan can be executed multiple times with different contexts
636    /// # Ok::<(), rustlite::Error>(())
637    /// ```
638    pub fn prepare(&self, sql: &str) -> Result<PhysicalPlan> {
639        let mut parser =
640            Parser::new(sql).map_err(|e| Error::InvalidInput(format!("Parse error: {}", e)))?;
641        let query = parser
642            .parse()
643            .map_err(|e| Error::InvalidInput(format!("Parse error: {}", e)))?;
644
645        let planner = Planner::new();
646        planner
647            .plan(&query)
648            .map_err(|e| Error::InvalidInput(format!("Planning error: {}", e)))
649    }
650
651    /// Executes a prepared query plan with given context (v0.4.0+).
652    pub fn execute_plan(&self, plan: &PhysicalPlan, context: ExecutionContext) -> Result<Vec<Row>> {
653        let mut executor = Executor::new(context);
654        executor.execute(plan)
655    }
656
657    // ===== Transaction Methods (v0.5.0+) =====
658
659    /// Begins a new MVCC transaction with the specified isolation level (v0.5.0+).
660    ///
661    /// Returns a Transaction handle that provides snapshot isolation and
662    /// ACID guarantees. Changes are buffered until commit.
663    ///
664    /// # Examples
665    ///
666    /// ```rust
667    /// use rustlite::{Database, IsolationLevel};
668    ///
669    /// let db = Database::in_memory()?;
670    ///
671    /// // Start a transaction
672    /// let mut txn = db.begin_transaction(IsolationLevel::RepeatableRead)?;
673    ///
674    /// // Read and write within transaction
675    /// txn.put(b"key1".to_vec(), b"value1".to_vec())?;
676    /// txn.put(b"key2".to_vec(), b"value2".to_vec())?;
677    ///
678    /// // Commit changes
679    /// txn.commit()?;
680    /// # Ok::<(), rustlite::Error>(())
681    /// ```
682    #[instrument(skip(self), fields(isolation = ?isolation))]
683    pub fn begin_transaction(&self, isolation: IsolationLevel) -> Result<Transaction> {
684        info!("Beginning transaction");
685        if let Some(ref manager) = self.inner.transaction_manager {
686            manager.begin(isolation)
687        } else {
688            Err(Error::Transaction(
689                "Transaction support not initialized".into(),
690            ))
691        }
692    }
693
694    /// Begins a new transaction with default isolation level (RepeatableRead).
695    ///
696    /// Convenience method equivalent to `begin_transaction(IsolationLevel::RepeatableRead)`.
697    ///
698    /// # Examples
699    ///
700    /// ```rust
701    /// use rustlite::Database;
702    ///
703    /// let db = Database::in_memory()?;
704    /// let mut txn = db.begin()?;
705    /// txn.put(b"key".to_vec(), b"value".to_vec())?;
706    /// txn.commit()?;
707    /// # Ok::<(), rustlite::Error>(())
708    /// ```
709    pub fn begin(&self) -> Result<Transaction> {
710        self.begin_transaction(IsolationLevel::default())
711    }
712
713    /// Performs garbage collection on MVCC version chains (v0.5.0+).
714    ///
715    /// Removes old versions that are no longer visible to any active transaction.
716    /// This helps reduce memory usage in long-running databases.
717    ///
718    /// # Examples
719    ///
720    /// ```rust
721    /// use rustlite::Database;
722    ///
723    /// let db = Database::in_memory()?;
724    /// // ... perform many transactions ...
725    /// db.gc()?; // Clean up old versions
726    /// # Ok::<(), rustlite::Error>(())
727    /// ```
728    pub fn gc(&self) -> Result<()> {
729        if let Some(ref manager) = self.inner.transaction_manager {
730            manager.gc()
731        } else {
732            Ok(()) // No-op if transactions not initialized
733        }
734    }
735}
736
737#[cfg(test)]
738mod tests {
739    use super::*;
740    use tempfile::tempdir;
741
742    #[test]
743    fn test_version() {
744        assert_eq!(VERSION, "0.7.0");
745    }
746
747    #[test]
748    fn test_in_memory_database() {
749        let db = Database::in_memory().unwrap();
750        db.put(b"key", b"value").unwrap();
751        assert_eq!(db.get(b"key").unwrap(), Some(b"value".to_vec()));
752        assert!(!db.is_persistent());
753    }
754
755    #[test]
756    fn test_persistent_database() {
757        let dir = tempdir().unwrap();
758        let db = Database::open(dir.path()).unwrap();
759
760        db.put(b"persist", b"data").unwrap();
761        assert_eq!(db.get(b"persist").unwrap(), Some(b"data".to_vec()));
762        assert!(db.is_persistent());
763    }
764
765    #[test]
766    fn test_persistence_across_reopens() {
767        let dir = tempdir().unwrap();
768
769        // Write data
770        {
771            let db = Database::open(dir.path()).unwrap();
772            db.put(b"key1", b"value1").unwrap();
773            db.put(b"key2", b"value2").unwrap();
774            db.sync().unwrap();
775        }
776
777        // Reopen and verify
778        {
779            let db = Database::open(dir.path()).unwrap();
780            assert_eq!(db.get(b"key1").unwrap(), Some(b"value1".to_vec()));
781            assert_eq!(db.get(b"key2").unwrap(), Some(b"value2".to_vec()));
782        }
783    }
784
785    #[test]
786    fn test_delete() {
787        let dir = tempdir().unwrap();
788        let db = Database::open(dir.path()).unwrap();
789
790        db.put(b"key", b"value").unwrap();
791        assert!(db.delete(b"key").unwrap());
792        assert_eq!(db.get(b"key").unwrap(), None);
793        assert!(!db.delete(b"key").unwrap()); // Already deleted
794    }
795
796    #[test]
797    fn test_update() {
798        let db = Database::in_memory().unwrap();
799
800        db.put(b"counter", b"1").unwrap();
801        assert_eq!(db.get(b"counter").unwrap(), Some(b"1".to_vec()));
802
803        db.put(b"counter", b"2").unwrap();
804        assert_eq!(db.get(b"counter").unwrap(), Some(b"2".to_vec()));
805    }
806
807    #[test]
808    #[allow(deprecated)]
809    fn test_backward_compatibility() {
810        // Database::new() still works but is deprecated
811        let db = Database::new().unwrap();
812        db.put(b"key", b"value").unwrap();
813        assert_eq!(db.get(b"key").unwrap(), Some(b"value".to_vec()));
814    }
815
816    // Index tests
817    #[test]
818    fn test_create_and_drop_index() {
819        let db = Database::in_memory().unwrap();
820
821        db.create_index("test_idx", IndexType::BTree).unwrap();
822        assert_eq!(db.list_indexes().unwrap().len(), 1);
823
824        assert!(db.drop_index("test_idx").unwrap());
825        assert_eq!(db.list_indexes().unwrap().len(), 0);
826    }
827
828    #[test]
829    fn test_btree_index_operations() {
830        let db = Database::in_memory().unwrap();
831        db.create_index("names", IndexType::BTree).unwrap();
832
833        db.index_insert("names", b"alice", 100).unwrap();
834        db.index_insert("names", b"bob", 101).unwrap();
835        db.index_insert("names", b"charlie", 102).unwrap();
836
837        assert_eq!(db.index_find("names", b"bob").unwrap(), vec![101]);
838
839        assert!(db.index_remove("names", b"bob").unwrap());
840        assert!(db.index_find("names", b"bob").unwrap().is_empty());
841    }
842
843    #[test]
844    fn test_hash_index_operations() {
845        let db = Database::in_memory().unwrap();
846        db.create_index("sessions", IndexType::Hash).unwrap();
847
848        db.index_insert("sessions", b"sess:abc", 500).unwrap();
849        db.index_insert("sessions", b"sess:def", 501).unwrap();
850
851        assert_eq!(db.index_find("sessions", b"sess:abc").unwrap(), vec![500]);
852        assert!(db
853            .index_find("sessions", b"nonexistent")
854            .unwrap()
855            .is_empty());
856    }
857
858    #[test]
859    fn test_index_info() {
860        let db = Database::in_memory().unwrap();
861        db.create_index("idx1", IndexType::BTree).unwrap();
862        db.create_index("idx2", IndexType::Hash).unwrap();
863
864        db.index_insert("idx1", b"key1", 1).unwrap();
865        db.index_insert("idx1", b"key2", 2).unwrap();
866        db.index_insert("idx2", b"key3", 3).unwrap();
867
868        let info = db.index_info().unwrap();
869        assert_eq!(info.len(), 2);
870    }
871
872    #[test]
873    fn test_simple_query() {
874        let db = Database::in_memory().unwrap();
875
876        let mut context = ExecutionContext::new();
877        context.data.insert(
878            "users".to_string(),
879            vec![
880                Row {
881                    columns: vec![
882                        Column {
883                            name: "name".to_string(),
884                            alias: None,
885                        },
886                        Column {
887                            name: "age".to_string(),
888                            alias: None,
889                        },
890                    ],
891                    values: vec![Value::String("Alice".to_string()), Value::Integer(30)],
892                },
893                Row {
894                    columns: vec![
895                        Column {
896                            name: "name".to_string(),
897                            alias: None,
898                        },
899                        Column {
900                            name: "age".to_string(),
901                            alias: None,
902                        },
903                    ],
904                    values: vec![Value::String("Bob".to_string()), Value::Integer(25)],
905                },
906            ],
907        );
908
909        let results = db.query("SELECT * FROM users", context).unwrap();
910        assert_eq!(results.len(), 2);
911    }
912
913    #[test]
914    fn test_query_with_where() {
915        let db = Database::in_memory().unwrap();
916
917        let mut context = ExecutionContext::new();
918        context.data.insert(
919            "users".to_string(),
920            vec![
921                Row {
922                    columns: vec![
923                        Column {
924                            name: "name".to_string(),
925                            alias: None,
926                        },
927                        Column {
928                            name: "age".to_string(),
929                            alias: None,
930                        },
931                    ],
932                    values: vec![Value::String("Alice".to_string()), Value::Integer(30)],
933                },
934                Row {
935                    columns: vec![
936                        Column {
937                            name: "name".to_string(),
938                            alias: None,
939                        },
940                        Column {
941                            name: "age".to_string(),
942                            alias: None,
943                        },
944                    ],
945                    values: vec![Value::String("Bob".to_string()), Value::Integer(25)],
946                },
947            ],
948        );
949
950        let results = db
951            .query("SELECT name FROM users WHERE age > 26", context)
952            .unwrap();
953        assert_eq!(results.len(), 1);
954        assert_eq!(results[0].values[0], Value::String("Alice".to_string()));
955    }
956
957    #[test]
958    fn test_query_with_limit() {
959        let db = Database::in_memory().unwrap();
960
961        let mut context = ExecutionContext::new();
962        context.data.insert(
963            "users".to_string(),
964            vec![
965                Row {
966                    columns: vec![Column {
967                        name: "name".to_string(),
968                        alias: None,
969                    }],
970                    values: vec![Value::String("Alice".to_string())],
971                },
972                Row {
973                    columns: vec![Column {
974                        name: "name".to_string(),
975                        alias: None,
976                    }],
977                    values: vec![Value::String("Bob".to_string())],
978                },
979                Row {
980                    columns: vec![Column {
981                        name: "name".to_string(),
982                        alias: None,
983                    }],
984                    values: vec![Value::String("Charlie".to_string())],
985                },
986            ],
987        );
988
989        let results = db.query("SELECT * FROM users LIMIT 2", context).unwrap();
990        assert_eq!(results.len(), 2);
991    }
992
993    #[test]
994    fn test_prepare_and_execute() {
995        let db = Database::in_memory().unwrap();
996        let plan = db.prepare("SELECT * FROM users WHERE age > 18").unwrap();
997
998        let mut context = ExecutionContext::new();
999        context.data.insert(
1000            "users".to_string(),
1001            vec![Row {
1002                columns: vec![
1003                    Column {
1004                        name: "name".to_string(),
1005                        alias: None,
1006                    },
1007                    Column {
1008                        name: "age".to_string(),
1009                        alias: None,
1010                    },
1011                ],
1012                values: vec![Value::String("Alice".to_string()), Value::Integer(30)],
1013            }],
1014        );
1015
1016        let results = db.execute_plan(&plan, context).unwrap();
1017        assert_eq!(results.len(), 1);
1018    }
1019
1020    // Transaction tests (v0.5.0+)
1021    #[test]
1022    fn test_transaction_basic() {
1023        let db = Database::in_memory().unwrap();
1024
1025        let mut txn = db.begin().unwrap();
1026        txn.put(b"key1".to_vec(), b"value1".to_vec()).unwrap();
1027        txn.put(b"key2".to_vec(), b"value2".to_vec()).unwrap();
1028        txn.commit().unwrap();
1029
1030        // Verify data is visible after commit
1031        let txn2 = db.begin().unwrap();
1032        assert_eq!(txn2.get(b"key1").unwrap(), Some(b"value1".to_vec()));
1033        assert_eq!(txn2.get(b"key2").unwrap(), Some(b"value2".to_vec()));
1034    }
1035
1036    #[test]
1037    fn test_transaction_isolation() {
1038        let db = Database::in_memory().unwrap();
1039
1040        // Transaction 1: Write data
1041        let mut txn1 = db.begin().unwrap();
1042        txn1.put(b"counter".to_vec(), b"1".to_vec()).unwrap();
1043        txn1.commit().unwrap();
1044
1045        // Transaction 2: Start and read
1046        let txn2 = db.begin().unwrap();
1047        assert_eq!(txn2.get(b"counter").unwrap(), Some(b"1".to_vec()));
1048
1049        // Transaction 3: Update value
1050        let mut txn3 = db.begin().unwrap();
1051        txn3.put(b"counter".to_vec(), b"2".to_vec()).unwrap();
1052        txn3.commit().unwrap();
1053
1054        // Transaction 2 should still see old value (snapshot isolation)
1055        assert_eq!(txn2.get(b"counter").unwrap(), Some(b"1".to_vec()));
1056    }
1057
1058    #[test]
1059    fn test_transaction_rollback() {
1060        let db = Database::in_memory().unwrap();
1061
1062        // Write initial data
1063        let mut txn1 = db.begin().unwrap();
1064        txn1.put(b"key1".to_vec(), b"original".to_vec()).unwrap();
1065        txn1.commit().unwrap();
1066
1067        // Update but rollback
1068        let mut txn2 = db.begin().unwrap();
1069        txn2.put(b"key1".to_vec(), b"updated".to_vec()).unwrap();
1070        txn2.rollback().unwrap();
1071
1072        // Should see original value
1073        let txn3 = db.begin().unwrap();
1074        assert_eq!(txn3.get(b"key1").unwrap(), Some(b"original".to_vec()));
1075    }
1076
1077    #[test]
1078    fn test_transaction_delete() {
1079        let db = Database::in_memory().unwrap();
1080
1081        // Write data
1082        let mut txn1 = db.begin().unwrap();
1083        txn1.put(b"temp".to_vec(), b"data".to_vec()).unwrap();
1084        txn1.commit().unwrap();
1085
1086        // Delete data
1087        let mut txn2 = db.begin().unwrap();
1088        txn2.delete(b"temp").unwrap();
1089        txn2.commit().unwrap();
1090
1091        // Should not exist
1092        let txn3 = db.begin().unwrap();
1093        assert_eq!(txn3.get(b"temp").unwrap(), None);
1094    }
1095
1096    #[test]
1097    fn test_transaction_scan() {
1098        let db = Database::in_memory().unwrap();
1099
1100        // Write multiple keys
1101        let mut txn = db.begin().unwrap();
1102        txn.put(b"user:1".to_vec(), b"alice".to_vec()).unwrap();
1103        txn.put(b"user:2".to_vec(), b"bob".to_vec()).unwrap();
1104        txn.put(b"post:1".to_vec(), b"post1".to_vec()).unwrap();
1105        txn.commit().unwrap();
1106
1107        // Scan with prefix
1108        let txn2 = db.begin().unwrap();
1109        let results = txn2.scan(b"user:").unwrap();
1110        assert_eq!(results.len(), 2);
1111    }
1112
1113    #[test]
1114    fn test_transaction_with_index() {
1115        let db = Database::in_memory().unwrap();
1116        db.create_index("user_idx", IndexType::Hash).unwrap();
1117
1118        // Transaction 1: Insert with index
1119        let mut txn = db.begin().unwrap();
1120        txn.put(b"user:1".to_vec(), b"alice@example.com".to_vec())
1121            .unwrap();
1122        txn.commit().unwrap();
1123
1124        // Manually update index (in real use, this would be automated)
1125        db.index_insert("user_idx", b"alice@example.com", 1)
1126            .unwrap();
1127
1128        // Transaction 2: Query via index
1129        let txn2 = db.begin().unwrap();
1130        let ids = db.index_find("user_idx", b"alice@example.com").unwrap();
1131        assert_eq!(ids, vec![1]);
1132
1133        // Verify data via transaction
1134        assert_eq!(
1135            txn2.get(b"user:1").unwrap(),
1136            Some(b"alice@example.com".to_vec())
1137        );
1138    }
1139
1140    #[test]
1141    fn test_concurrent_transaction_isolation() {
1142        use std::sync::Arc;
1143        use std::thread;
1144
1145        let db = Arc::new(Database::in_memory().unwrap());
1146
1147        // Initial balance
1148        let mut setup = db.begin().unwrap();
1149        setup.put(b"balance".to_vec(), b"1000".to_vec()).unwrap();
1150        setup.commit().unwrap();
1151
1152        // Thread 1: Read balance multiple times
1153        let db1 = db.clone();
1154        let handle1 = thread::spawn(move || {
1155            let txn = db1.begin().unwrap();
1156            let balance1_bytes = txn.get(b"balance").unwrap().unwrap();
1157            let balance1 = String::from_utf8_lossy(&balance1_bytes);
1158            thread::sleep(std::time::Duration::from_millis(10));
1159            let balance2_bytes = txn.get(b"balance").unwrap().unwrap();
1160            let balance2 = String::from_utf8_lossy(&balance2_bytes);
1161            assert_eq!(balance1, balance2); // Should be consistent
1162            balance1.to_string()
1163        });
1164
1165        // Thread 2: Update balance
1166        let db2 = db.clone();
1167        let handle2 = thread::spawn(move || {
1168            thread::sleep(std::time::Duration::from_millis(5));
1169            let mut txn = db2.begin().unwrap();
1170            txn.put(b"balance".to_vec(), b"2000".to_vec()).unwrap();
1171            txn.commit().unwrap();
1172        });
1173
1174        let balance_seen = handle1.join().unwrap();
1175        handle2.join().unwrap();
1176
1177        // Thread 1 should have seen 1000 (snapshot isolation)
1178        assert_eq!(balance_seen, "1000");
1179
1180        // New transaction sees updated value
1181        let final_txn = db.begin().unwrap();
1182        let final_balance_bytes = final_txn.get(b"balance").unwrap().unwrap();
1183        let final_balance = String::from_utf8_lossy(&final_balance_bytes);
1184        assert_eq!(final_balance, "2000");
1185    }
1186
1187    #[test]
1188    fn test_transaction_error_handling() {
1189        let db = Database::in_memory().unwrap();
1190
1191        // Test double commit
1192        let mut txn = db.begin().unwrap();
1193        txn.put(b"key".to_vec(), b"value".to_vec()).unwrap();
1194        txn.commit().unwrap();
1195
1196        // Attempting operations after commit should fail gracefully
1197        // (In current implementation, the transaction is consumed)
1198    }
1199
1200    #[test]
1201    fn test_transaction_with_query() {
1202        let db = Database::in_memory().unwrap();
1203
1204        // Use transaction to populate data
1205        let mut txn = db.begin().unwrap();
1206        txn.put(b"user:1:name".to_vec(), b"Alice".to_vec()).unwrap();
1207        txn.put(b"user:1:age".to_vec(), b"30".to_vec()).unwrap();
1208        txn.put(b"user:2:name".to_vec(), b"Bob".to_vec()).unwrap();
1209        txn.put(b"user:2:age".to_vec(), b"25".to_vec()).unwrap();
1210        txn.commit().unwrap();
1211
1212        // Query within a transaction context
1213        let query_txn = db.begin().unwrap();
1214        let name = query_txn.get(b"user:1:name").unwrap();
1215        assert_eq!(name, Some(b"Alice".to_vec()));
1216    }
1217
1218    #[test]
1219    fn test_garbage_collection() {
1220        let db = Database::in_memory().unwrap();
1221
1222        // Create multiple versions
1223        for i in 0..10 {
1224            let mut txn = db.begin().unwrap();
1225            txn.put(b"key".to_vec(), format!("version{}", i).into_bytes())
1226                .unwrap();
1227            txn.commit().unwrap();
1228        }
1229
1230        // Run GC
1231        db.gc().unwrap();
1232
1233        // Latest value should still be accessible
1234        let txn = db.begin().unwrap();
1235        assert_eq!(txn.get(b"key").unwrap(), Some(b"version9".to_vec()));
1236    }
1237
1238    #[test]
1239    fn test_persistent_transactions() {
1240        let dir = tempdir().unwrap();
1241        let path = dir.path();
1242
1243        // Create DB and do transaction
1244        {
1245            let db = Database::open(path).unwrap();
1246            let mut txn = db.begin().unwrap();
1247            txn.put(b"persistent_key".to_vec(), b"persistent_value".to_vec())
1248                .unwrap();
1249            txn.commit().unwrap();
1250
1251            // Also write to persistent storage (transactions are in-memory MVCC layer)
1252            db.put(b"direct_key", b"direct_value").unwrap();
1253            db.sync().unwrap();
1254        }
1255
1256        // Reopen and verify
1257        {
1258            let db = Database::open(path).unwrap();
1259            // Direct storage access persists
1260            assert_eq!(
1261                db.get(b"direct_key").unwrap(),
1262                Some(b"direct_value".to_vec())
1263            );
1264
1265            // Note: MVCC transactions are in-memory only in current implementation
1266            // This test verifies the database persistence, not transaction persistence
1267        }
1268    }
1269
1270    #[test]
1271    fn test_transaction_with_large_dataset() {
1272        let db = Database::in_memory().unwrap();
1273
1274        // Insert 1000 keys in one transaction
1275        let mut txn = db.begin().unwrap();
1276        for i in 0..1000 {
1277            let key = format!("key:{:04}", i);
1278            let value = format!("value:{}", i);
1279            txn.put(key.into_bytes(), value.into_bytes()).unwrap();
1280        }
1281        txn.commit().unwrap();
1282
1283        // Verify all keys exist
1284        let verify_txn = db.begin().unwrap();
1285        for i in 0..1000 {
1286            let key = format!("key:{:04}", i);
1287            let expected_value = format!("value:{}", i);
1288            assert_eq!(
1289                verify_txn.get(&key.into_bytes()).unwrap(),
1290                Some(expected_value.into_bytes())
1291            );
1292        }
1293    }
1294
1295    #[test]
1296    fn test_mixed_transaction_and_direct_operations() {
1297        let db = Database::in_memory().unwrap();
1298
1299        // Direct put
1300        db.put(b"direct", b"value1").unwrap();
1301
1302        // Transaction put
1303        let mut txn = db.begin().unwrap();
1304        txn.put(b"txn".to_vec(), b"value2".to_vec()).unwrap();
1305        txn.commit().unwrap();
1306
1307        // Both should be readable
1308        assert_eq!(db.get(b"direct").unwrap(), Some(b"value1".to_vec()));
1309
1310        let read_txn = db.begin().unwrap();
1311        assert_eq!(read_txn.get(b"txn").unwrap(), Some(b"value2".to_vec()));
1312    }
1313
1314    #[test]
1315    fn test_serializable_isolation() {
1316        let db = Database::in_memory().unwrap();
1317
1318        // Setup
1319        let mut setup = db.begin().unwrap();
1320        setup.put(b"counter".to_vec(), b"0".to_vec()).unwrap();
1321        setup.commit().unwrap();
1322
1323        // Use serializable isolation
1324        let txn = db.begin_transaction(IsolationLevel::Serializable).unwrap();
1325        assert_eq!(txn.isolation_level(), IsolationLevel::Serializable);
1326
1327        let value = txn.get(b"counter").unwrap();
1328        assert_eq!(value, Some(b"0".to_vec()));
1329    }
1330
1331    #[test]
1332    fn test_multiple_isolation_levels() {
1333        let db = Database::in_memory().unwrap();
1334
1335        // Test all isolation levels can be created
1336        let _txn1 = db
1337            .begin_transaction(IsolationLevel::ReadUncommitted)
1338            .unwrap();
1339        let _txn2 = db.begin_transaction(IsolationLevel::ReadCommitted).unwrap();
1340        let _txn3 = db
1341            .begin_transaction(IsolationLevel::RepeatableRead)
1342            .unwrap();
1343        let _txn4 = db.begin_transaction(IsolationLevel::Serializable).unwrap();
1344    }
1345}