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}