vibesql_storage/database/
constructors.rs

1// ============================================================================
2// Database Constructors and Initialization
3// ============================================================================
4
5#![allow(clippy::clone_on_copy)]
6
7use std::{
8    collections::HashMap,
9    path::PathBuf,
10    sync::{atomic::AtomicU64, Arc},
11};
12
13use super::{
14    config::{DatabaseConfig, DEFAULT_COLUMNAR_CACHE_BUDGET},
15    core::Database,
16    lifecycle::Lifecycle,
17    metadata::Metadata,
18    operations::Operations,
19};
20use crate::{columnar_cache::ColumnarCache, QueryBufferPool};
21
22impl Clone for Database {
23    fn clone(&self) -> Self {
24        Database {
25            catalog: self.catalog.clone(),
26            lifecycle: self.lifecycle.clone(),
27            metadata: self.metadata.clone(),
28            operations: self.operations.clone(),
29            tables: self.tables.clone(),
30            sql_mode: self.sql_mode.clone(),
31            query_buffer_pool: self.query_buffer_pool.clone(),
32            // Clone creates a new cache with same config but empty data
33            // This is intentional - cloned databases shouldn't share cache state
34            columnar_cache: Arc::new(ColumnarCache::new(self.columnar_cache.max_memory())),
35            // Clone does not inherit change event sender - cloned databases are independent
36            change_sender: None,
37            // Clone resets last_insert_rowid - each database instance tracks independently
38            last_insert_rowid: 0,
39            // Clone resets last_changes_count - each database instance tracks independently
40            last_changes_count: 0,
41            // Clone resets total_changes_count - each database instance tracks independently
42            total_changes_count: 0,
43            // Clone resets search_count - each database instance tracks independently
44            search_count: AtomicU64::new(0),
45            // Clone does not inherit persistence engine - cloned databases are independent
46            persistence_engine: None,
47            // Preserve table ID counter for consistency
48            next_table_id: self.next_table_id,
49            // Clone resets reserved rowids - each database instance tracks independently
50            reserved_rowids: HashMap::new(),
51        }
52    }
53}
54
55impl Database {
56    /// Create a new empty database
57    ///
58    /// Note: Security is disabled by default for backward compatibility with existing code.
59    /// Call `enable_security()` to turn on access control enforcement.
60    pub fn new() -> Self {
61        Database {
62            catalog: vibesql_catalog::Catalog::new(),
63            lifecycle: Lifecycle::new(),
64            metadata: Metadata::new(),
65            operations: Operations::new(),
66            tables: HashMap::new(),
67            sql_mode: vibesql_types::SqlMode::default(),
68            query_buffer_pool: QueryBufferPool::new(),
69            columnar_cache: Arc::new(ColumnarCache::new(DEFAULT_COLUMNAR_CACHE_BUDGET)),
70            change_sender: None,
71            last_insert_rowid: 0,
72            last_changes_count: 0,
73            total_changes_count: 0,
74            search_count: AtomicU64::new(0),
75            persistence_engine: None,
76            next_table_id: 1,
77            reserved_rowids: HashMap::new(),
78        }
79    }
80
81    /// Create a new database with a specific storage path
82    ///
83    /// The provided path will be used as the root directory for database files.
84    /// Index files will be stored in `<path>/data/indexes/`.
85    ///
86    /// # Example
87    /// ```rust
88    /// use std::path::PathBuf;
89    ///
90    /// use vibesql_storage::Database;
91    ///
92    /// let db = Database::with_path(PathBuf::from("/var/lib/myapp/db"));
93    /// // Index files will be stored in /var/lib/myapp/db/data/indexes/
94    /// ```
95    pub fn with_path(path: PathBuf) -> Self {
96        let mut db = Self::new();
97        db.operations.set_database_path(path.join("data"));
98        db
99    }
100
101    /// Create a new database with a specific configuration
102    ///
103    /// Allows setting memory budgets, disk budgets, and spill policy for adaptive
104    /// index management.
105    ///
106    /// # Example
107    /// ```rust
108    /// use vibesql_storage::{Database, DatabaseConfig};
109    ///
110    /// // Browser environment with limited memory
111    /// let db = Database::with_config(DatabaseConfig::browser_default());
112    ///
113    /// // Server environment with abundant memory
114    /// let db = Database::with_config(DatabaseConfig::server_default());
115    /// ```
116    pub fn with_config(config: DatabaseConfig) -> Self {
117        let columnar_cache_budget = config.columnar_cache_budget;
118        let mut db = Self::new();
119        db.sql_mode = config.sql_mode.clone();
120        db.columnar_cache = Arc::new(ColumnarCache::new(columnar_cache_budget));
121        db.operations.set_config(config);
122        db
123    }
124
125    /// Create a new database with both path and configuration
126    ///
127    /// # Example
128    /// ```rust
129    /// use std::path::PathBuf;
130    ///
131    /// use vibesql_storage::{Database, DatabaseConfig};
132    ///
133    /// let db = Database::with_path_and_config(
134    ///     PathBuf::from("/var/lib/myapp/db"),
135    ///     DatabaseConfig::server_default(),
136    /// );
137    /// ```
138    pub fn with_path_and_config(path: PathBuf, config: DatabaseConfig) -> Self {
139        let columnar_cache_budget = config.columnar_cache_budget;
140        let mut db = Self::new();
141        db.sql_mode = config.sql_mode.clone();
142        db.columnar_cache = Arc::new(ColumnarCache::new(columnar_cache_budget));
143        db.operations.set_database_path(path.join("data"));
144        db.operations.set_config(config);
145        db
146    }
147
148    /// Create a new database with both path and configuration (async version for WASM)
149    ///
150    /// This async version is required for WASM to properly initialize OPFS storage
151    /// without blocking the event loop.
152    ///
153    /// # Example
154    /// ```rust,no_run
155    /// # use std::path::PathBuf;
156    /// # use vibesql_storage::{Database, DatabaseConfig};
157    /// # async fn example() {
158    /// let db = Database::with_path_and_config_async(
159    ///     PathBuf::from("/vibesql-data"),
160    ///     DatabaseConfig::browser_default(),
161    /// )
162    /// .await
163    /// .unwrap();
164    /// # }
165    /// ```
166    #[cfg(target_arch = "wasm32")]
167    pub async fn with_path_and_config_async(
168        path: PathBuf,
169        config: DatabaseConfig,
170    ) -> Result<Self, crate::StorageError> {
171        let columnar_cache_budget = config.columnar_cache_budget;
172        let mut db = Self::new();
173        db.sql_mode = config.sql_mode.clone();
174        db.columnar_cache = Arc::new(ColumnarCache::new(columnar_cache_budget));
175        db.operations.set_database_path(path.join("data"));
176        db.operations.set_config(config);
177
178        // Initialize OPFS storage asynchronously
179        db.operations.init_opfs_async().await?;
180
181        Ok(db)
182    }
183
184    /// Reset the database to empty state (more efficient than creating a new instance).
185    ///
186    /// Clears all tables, resets catalog to default state, and clears all indexes and transactions.
187    /// Useful for test scenarios where you need to reuse a Database instance.
188    /// Preserves database configuration (path, storage backend, memory budgets) across resets.
189    /// Note: Persistence engine is preserved (WAL remains active if enabled).
190    pub fn reset(&mut self) {
191        self.catalog = vibesql_catalog::Catalog::new();
192        self.lifecycle.reset();
193        self.metadata = Metadata::new();
194
195        // Reset operations in place to preserve database_path, storage backend, and config
196        self.operations.reset();
197
198        self.tables.clear();
199
200        // Clear the columnar cache
201        self.columnar_cache.clear();
202
203        // Reset table ID counter
204        self.next_table_id = 1;
205    }
206}
207
208impl Default for Database {
209    fn default() -> Self {
210        Self::new()
211    }
212}