1#![cfg_attr(test, allow(clippy::approx_constant))]
7
8pub mod backend;
9pub mod blob;
10pub mod btree;
11pub mod buffer;
12pub mod change_events;
13pub mod columnar;
14pub mod columnar_cache;
15pub mod database;
16pub mod error;
17pub mod index;
18pub mod page;
19pub mod persistence;
20pub mod progress;
21pub mod query_buffer_pool;
22pub mod row;
23pub mod statistics;
24pub mod table;
25pub mod wal;
26
27#[cfg(not(target_arch = "wasm32"))]
29pub use backend::{NativeFile, NativeStorage};
30#[cfg(target_arch = "wasm32")]
31pub use backend::{OpfsFile, OpfsStorage};
32pub use backend::{StorageBackend, StorageFile};
33pub use blob::{BlobId, BlobMetadata, BlobStorageConfig, BlobStorageService};
34pub use buffer::{BufferPool, BufferPoolStats};
35pub use change_events::{
36 channel as change_event_channel, ChangeEvent, ChangeEventReceiver, ChangeEventSender,
37 RecvError as ChangeEventRecvError, DEFAULT_CHANNEL_CAPACITY,
38};
39pub use columnar::{ColumnData, ColumnarTable};
40pub use columnar_cache::{CacheStats, ColumnarCache};
41pub use database::{
42 print_delete_profile_summary, reset_delete_profile_stats, Database, DatabaseConfig,
43 DeleteProfileStats, IndexData, IndexManager, IndexMetadata, OwnedStreamingRangeScan,
44 SpatialIndexMetadata, SpillPolicy, TransactionState, DELETE_PROFILE_STATS,
45};
46pub use error::{StorageError, StorageResult};
47pub use index::{extract_mbr_from_sql_value, SpatialIndex, SpatialIndexEntry};
48pub use persistence::load::{parse_sql_statements, read_sql_dump};
49pub use query_buffer_pool::{
50 QueryBufferPool, QueryBufferPoolStats, RowBufferGuard, ValueBufferGuard,
51};
52pub use row::{Row, RowValues, ROW_INLINE_CAPACITY};
53pub use statistics::{ColumnStatistics, TableIndexInfo, TableStatistics};
54pub use table::{DeleteResult, Table};
55pub use wal::{
56 DurabilityConfig, DurabilityMode, Lsn, PersistenceConfig, PersistenceEngine, PersistenceStats,
57 TransactionDurability, WalEntry, WalOp, WalOpTag,
58};
59
60#[cfg(test)]
61mod tests {
62 use vibesql_catalog::{ColumnSchema, TableSchema};
63 use vibesql_types::{DataType, SqlValue};
64
65 use super::*;
66 use crate::Row;
67
68 #[test]
69 fn test_hash_indexes_primary_key() {
70 let schema = TableSchema::with_primary_key(
71 "users".to_string(),
72 vec![
73 ColumnSchema::new("id".to_string(), DataType::Integer, false),
74 ColumnSchema::new(
75 "name".to_string(),
76 DataType::Varchar { max_length: Some(100) },
77 false,
78 ),
79 ],
80 vec!["id".to_string()],
81 );
82
83 let mut table = Table::new(schema);
84
85 for i in 0..10 {
87 let row = Row::new(vec![
88 SqlValue::Integer(i),
89 SqlValue::Varchar(arcstr::ArcStr::from(format!("User {}", i))),
90 ]);
91 table.insert(row).unwrap();
92 }
93
94 assert!(table.primary_key_index().is_some());
96 assert_eq!(table.primary_key_index().as_ref().unwrap().len(), 10);
97
98 let duplicate_row = Row::new(vec![
100 SqlValue::Integer(0),
101 SqlValue::Varchar(arcstr::ArcStr::from("Duplicate User")),
102 ]);
103 table.insert(duplicate_row).unwrap(); }
106
107 #[test]
108 fn test_hash_indexes_unique_constraints() {
109 let schema = TableSchema::with_unique_constraints(
110 "products".to_string(),
111 vec![
112 ColumnSchema::new("id".to_string(), DataType::Integer, false),
113 ColumnSchema::new(
114 "sku".to_string(),
115 DataType::Varchar { max_length: Some(50) },
116 false,
117 ),
118 ],
119 vec![vec!["sku".to_string()]], );
121
122 let mut table = Table::new(schema);
123
124 for i in 0..5 {
126 let row = Row::new(vec![
127 SqlValue::Integer(i),
128 SqlValue::Varchar(arcstr::ArcStr::from(format!("SKU{}", i))),
129 ]);
130 table.insert(row).unwrap();
131 }
132
133 assert_eq!(table.unique_indexes().len(), 1);
135 assert_eq!(table.unique_indexes()[0].len(), 5);
136 }
137
138 #[test]
139 fn test_update_row_selective_non_indexed_column() {
140 let schema = TableSchema::with_all_constraints(
142 "users".to_string(),
143 vec![
144 ColumnSchema::new("id".to_string(), DataType::Integer, false),
145 ColumnSchema::new(
146 "email".to_string(),
147 DataType::Varchar { max_length: Some(100) },
148 false,
149 ),
150 ColumnSchema::new(
151 "name".to_string(),
152 DataType::Varchar { max_length: Some(100) },
153 false,
154 ),
155 ],
156 Some(vec!["id".to_string()]),
157 vec![vec!["email".to_string()]],
158 );
159 let mut table = Table::new(schema);
160
161 let row1 = Row::new(vec![
163 SqlValue::Integer(1),
164 SqlValue::Varchar(arcstr::ArcStr::from("alice@example.com")),
165 SqlValue::Varchar(arcstr::ArcStr::from("Alice")),
166 ]);
167 table.insert(row1).unwrap();
168
169 let updated_row = Row::new(vec![
171 SqlValue::Integer(1),
172 SqlValue::Varchar(arcstr::ArcStr::from("alice@example.com")),
173 SqlValue::Varchar(arcstr::ArcStr::from("Alice Smith")),
174 ]);
175 let mut changed_columns = std::collections::HashSet::new();
176 changed_columns.insert(2); table.update_row_selective(0, updated_row, &changed_columns).unwrap();
179
180 let row = table.scan().iter().next().unwrap();
182 assert_eq!(row.get(2), Some(&SqlValue::Varchar(arcstr::ArcStr::from("Alice Smith"))));
183 }
184
185 #[test]
186 fn test_update_row_selective_primary_key_column() {
187 let schema = TableSchema::with_primary_key(
189 "users".to_string(),
190 vec![
191 ColumnSchema::new("id".to_string(), DataType::Integer, false),
192 ColumnSchema::new(
193 "name".to_string(),
194 DataType::Varchar { max_length: Some(100) },
195 false,
196 ),
197 ],
198 vec!["id".to_string()],
199 );
200 let mut table = Table::new(schema);
201
202 table
204 .insert(Row::new(vec![
205 SqlValue::Integer(1),
206 SqlValue::Varchar(arcstr::ArcStr::from("Alice")),
207 ]))
208 .unwrap();
209 table
210 .insert(Row::new(vec![
211 SqlValue::Integer(2),
212 SqlValue::Varchar(arcstr::ArcStr::from("Bob")),
213 ]))
214 .unwrap();
215
216 let updated_row = Row::new(vec![
218 SqlValue::Integer(10), SqlValue::Varchar(arcstr::ArcStr::from("Alice")),
220 ]);
221 let mut changed_columns = std::collections::HashSet::new();
222 changed_columns.insert(0); table.update_row_selective(0, updated_row, &changed_columns).unwrap();
225
226 assert_eq!(table.row_count(), 2);
228 let row = table.scan().iter().next().unwrap();
229 assert_eq!(row.get(0), Some(&SqlValue::Integer(10)));
230 }
231
232 #[test]
233 fn test_update_row_selective_unique_constraint_column() {
234 let schema = TableSchema::with_unique_constraints(
236 "users".to_string(),
237 vec![
238 ColumnSchema::new("id".to_string(), DataType::Integer, false),
239 ColumnSchema::new(
240 "email".to_string(),
241 DataType::Varchar { max_length: Some(100) },
242 false,
243 ),
244 ColumnSchema::new(
245 "name".to_string(),
246 DataType::Varchar { max_length: Some(100) },
247 false,
248 ),
249 ],
250 vec![vec!["email".to_string()]],
251 );
252 let mut table = Table::new(schema);
253
254 table
256 .insert(Row::new(vec![
257 SqlValue::Integer(1),
258 SqlValue::Varchar(arcstr::ArcStr::from("alice@example.com")),
259 SqlValue::Varchar(arcstr::ArcStr::from("Alice")),
260 ]))
261 .unwrap();
262
263 let updated_row = Row::new(vec![
265 SqlValue::Integer(1),
266 SqlValue::Varchar(arcstr::ArcStr::from("alice.smith@example.com")), SqlValue::Varchar(arcstr::ArcStr::from("Alice")),
268 ]);
269 let mut changed_columns = std::collections::HashSet::new();
270 changed_columns.insert(1); table.update_row_selective(0, updated_row, &changed_columns).unwrap();
273
274 let row = table.scan().iter().next().unwrap();
276 assert_eq!(
277 row.get(1),
278 Some(&SqlValue::Varchar(arcstr::ArcStr::from("alice.smith@example.com")))
279 );
280 }
281
282 #[test]
283 fn test_update_row_selective_vs_full_correctness() {
284 let schema1 = TableSchema::with_all_constraints(
286 "users".to_string(),
287 vec![
288 ColumnSchema::new("id".to_string(), DataType::Integer, false),
289 ColumnSchema::new(
290 "email".to_string(),
291 DataType::Varchar { max_length: Some(100) },
292 false,
293 ),
294 ColumnSchema::new(
295 "name".to_string(),
296 DataType::Varchar { max_length: Some(100) },
297 false,
298 ),
299 ],
300 Some(vec!["id".to_string()]),
301 vec![vec!["email".to_string()]],
302 );
303 let mut table1 = Table::new(schema1.clone());
304
305 let schema2 = schema1.clone();
306 let mut table2 = Table::new(schema2);
307
308 let initial_row = Row::new(vec![
310 SqlValue::Integer(1),
311 SqlValue::Varchar(arcstr::ArcStr::from("alice@example.com")),
312 SqlValue::Varchar(arcstr::ArcStr::from("Alice")),
313 ]);
314 table1.insert(initial_row.clone()).unwrap();
315 table2.insert(initial_row).unwrap();
316
317 let updated_row1 = Row::new(vec![
319 SqlValue::Integer(1),
320 SqlValue::Varchar(arcstr::ArcStr::from("alice@example.com")),
321 SqlValue::Varchar(arcstr::ArcStr::from("Alice Smith")),
322 ]);
323 let mut changed_columns = std::collections::HashSet::new();
324 changed_columns.insert(2); table1.update_row_selective(0, updated_row1.clone(), &changed_columns).unwrap();
326
327 table2.update_row(0, updated_row1).unwrap();
329
330 let row1 = table1.scan().iter().next().unwrap();
332 let row2 = table2.scan().iter().next().unwrap();
333 assert_eq!(row1.get(0), row2.get(0));
334 assert_eq!(row1.get(1), row2.get(1));
335 assert_eq!(row1.get(2), row2.get(2));
336 }
337}