1#![cfg_attr(not(feature = "std"), no_std)]
2
3use core::ptr::NonNull;
4
5pub mod types;
7pub mod config;
8pub mod table;
9pub mod index;
10pub mod transaction;
11pub mod memory;
12pub mod platform;
13pub mod monitor;
14pub mod sql;
15
16pub use types::{DataType, FieldDef, TableDef, Value, Result, RemDbError, IndexType, MAX_STRING_LEN};
18pub use table::MemoryTable;
19pub use index::{PrimaryIndex, SecondaryIndex, BTreeIndex, TTreeIndex, IndexStats, AnySecondaryIndex, PrimaryIndexItem};
20pub use transaction::{Transaction, TransactionType, TransactionManager};
21pub use monitor::{DbMetrics, DbMetricsSnapshot, HealthStatus, HealthCheckResult};
22
23pub use remdb_macros::table;
25pub use remdb_macros::database;
26
27extern crate alloc;
29use alloc::vec::Vec;
30
31pub trait DdlExecutor {
33 fn create_table(
35 &mut self,
36 name: &str,
37 fields: &[(&str, DataType)],
38 primary_key: Option<usize>
39 ) -> Result<()>;
40
41 fn create_index(
43 &mut self,
44 table_name: &str,
45 field_name: &str,
46 index_type: IndexType
47 ) -> Result<()>;
48}
49
50pub struct RemDb {
52 pub config: &'static config::DbConfig,
54 tables: Vec<Option<MemoryTable>>,
56 primary_indices: Vec<Option<PrimaryIndex>>,
58 secondary_indices: Vec<Option<AnySecondaryIndex>>,
60 low_power_mode: bool,
62 low_power_memory_limit: usize,
64 pub snapshot_version: u32,
66 pub metrics: monitor::DbMetrics,
68}
69
70unsafe impl Send for RemDb {}
73unsafe impl Sync for RemDb {}
74
75impl RemDb {
76 const SNAPSHOT_MAGIC: u32 = 0x52454D44; const SNAPSHOT_VERSION: u32 = 1;
80
81 pub fn new(
83 config: &'static config::DbConfig
84 ) -> Self {
85 let low_power_memory_limit = if config.low_power_mode_supported {
87 (config.total_memory / 2).max(1024 * 1024) } else {
90 config.total_memory
91 };
92
93 let metrics = monitor::DbMetrics::new(config.total_memory);
95
96 let tables = Vec::with_capacity(config.tables.len());
98 let primary_indices = Vec::with_capacity(config.tables.len());
99 let secondary_indices = Vec::with_capacity(config.tables.len());
100
101 RemDb {
102 config,
103 tables,
104 primary_indices,
105 secondary_indices,
106 low_power_mode: false, low_power_memory_limit,
108 snapshot_version: 0, metrics,
110 }
111 }
112
113 pub fn get_table(&self, table_id: usize) -> Result<&MemoryTable> {
115 if table_id >= self.tables.len() {
116 return Err(RemDbError::RecordNotFound);
117 }
118
119 match &self.tables[table_id] {
120 Some(table) => Ok(table),
121 None => Err(RemDbError::RecordNotFound),
122 }
123 }
124
125 pub fn get_table_mut(&mut self, table_id: usize) -> Result<&mut MemoryTable> {
127 if table_id >= self.tables.len() {
128 return Err(RemDbError::RecordNotFound);
129 }
130
131 match &mut self.tables[table_id] {
132 Some(table) => Ok(table),
133 None => Err(RemDbError::RecordNotFound),
134 }
135 }
136
137 pub fn get_primary_index(&self, table_id: usize) -> Result<&PrimaryIndex> {
139 if table_id >= self.primary_indices.len() {
140 return Err(RemDbError::RecordNotFound);
141 }
142
143 match &self.primary_indices[table_id] {
144 Some(index) => Ok(index),
145 None => Err(RemDbError::RecordNotFound),
146 }
147 }
148
149 pub fn get_primary_index_mut(&mut self, table_id: usize) -> Result<&mut PrimaryIndex> {
151 if table_id >= self.primary_indices.len() {
152 return Err(RemDbError::RecordNotFound);
153 }
154
155 match &mut self.primary_indices[table_id] {
156 Some(index) => Ok(index),
157 None => Err(RemDbError::RecordNotFound),
158 }
159 }
160
161 pub fn get_secondary_index(&self, table_id: usize) -> Result<&AnySecondaryIndex> {
163 if table_id >= self.secondary_indices.len() {
164 return Err(RemDbError::RecordNotFound);
165 }
166
167 match &self.secondary_indices[table_id] {
168 Some(index) => Ok(index),
169 None => Err(RemDbError::RecordNotFound),
170 }
171 }
172
173 pub fn get_secondary_index_mut(&mut self, table_id: usize) -> Result<&mut AnySecondaryIndex> {
175 if table_id >= self.secondary_indices.len() {
176 return Err(RemDbError::RecordNotFound);
177 }
178
179 match &mut self.secondary_indices[table_id] {
180 Some(index) => Ok(index),
181 None => Err(RemDbError::RecordNotFound),
182 }
183 }
184
185 pub fn is_low_power_mode(&self) -> bool {
187 self.low_power_mode
188 }
189
190 pub fn enter_low_power_mode(&mut self) -> Result<()> {
192 if !self.config.low_power_mode_supported {
194 return Err(RemDbError::UnsupportedOperation);
195 }
196
197 if self.low_power_mode {
199 return Ok(());
200 }
201
202 unsafe {
204 let current_memory = self.config.total_memory;
210 if current_memory > self.low_power_memory_limit {
211 }
214
215 crate::transaction::TX_MANAGER.set_low_power_mode(true);
217 }
218
219 for table in &mut self.tables.iter_mut() {
221 if let Some(table) = table {
222 table.set_low_power_mode(true, self.config.low_power_max_records);
223 }
224 }
225
226 self.low_power_mode = true;
228
229 Ok(())
230 }
231
232 pub fn exit_low_power_mode(&mut self) -> Result<()> {
234 if !self.low_power_mode {
236 return Ok(());
237 }
238
239 unsafe {
241 crate::transaction::TX_MANAGER.set_low_power_mode(false);
247 }
248
249 for table in &mut self.tables.iter_mut() {
251 if let Some(table) = table {
252 table.set_low_power_mode(false, None);
253 }
254 }
255
256 self.low_power_mode = false;
258
259 Ok(())
260 }
261
262 pub unsafe fn begin_transaction(
264 &mut self,
265 tx_type: transaction::TransactionType,
266 isolation_level: transaction::IsolationLevel,
267 tx_buffer: *mut transaction::Transaction,
268 log_buffer: *mut transaction::LogItem,
269 max_log_items: usize
270 ) -> Result<NonNull<transaction::Transaction>> {
271 crate::transaction::TX_MANAGER.begin(tx_type, isolation_level, tx_buffer, log_buffer, max_log_items)
272 }
273
274 pub unsafe fn commit_transaction(&mut self) -> Result<()> {
276 crate::transaction::TX_MANAGER.commit()
277 }
278
279 pub unsafe fn rollback_transaction(&mut self) -> Result<()> {
281 crate::transaction::TX_MANAGER.rollback(self)
282 }
283
284 pub fn init(&mut self) -> Result<()> {
286 #[cfg(feature = "posix")]
289 crate::platform::init_platform(crate::platform::posix::get_posix_platform());
290
291 Ok(())
292 }
293
294 pub fn save_snapshot(&mut self, path: &str) -> Result<()> {
296 let handle = crate::platform::file_open(path, crate::platform::FileMode::Write)
298 .map_err(|_| RemDbError::FileIoError)?;
299
300 let _defer = Defer::new(|| {
302 let _ = crate::platform::file_close(handle);
303 });
304
305 self.snapshot_version += 1;
307
308 let magic = Self::SNAPSHOT_MAGIC.to_le_bytes();
310 let written = crate::platform::file_write(handle, magic.as_ptr(), magic.len())
311 .map_err(|_| RemDbError::FileIoError)?;
312 if written != magic.len() {
313 return Err(RemDbError::FileIoError);
314 }
315
316 let version = Self::SNAPSHOT_VERSION.to_le_bytes();
318 let written = crate::platform::file_write(handle, version.as_ptr(), version.len())
319 .map_err(|_| RemDbError::FileIoError)?;
320 if written != version.len() {
321 return Err(RemDbError::FileIoError);
322 }
323
324 let snapshot_type = 0u8;
326 let written = crate::platform::file_write(handle, &snapshot_type as *const u8, 1)
327 .map_err(|_| RemDbError::FileIoError)?;
328 if written != 1 {
329 return Err(RemDbError::FileIoError);
330 }
331
332 let version_bytes = self.snapshot_version.to_le_bytes();
334 let written = crate::platform::file_write(handle, version_bytes.as_ptr(), version_bytes.len())
335 .map_err(|_| RemDbError::FileIoError)?;
336 if written != version_bytes.len() {
337 return Err(RemDbError::FileIoError);
338 }
339
340 let table_count = self.config.tables.len() as u32;
342 let table_count_bytes = table_count.to_le_bytes();
343 let written = crate::platform::file_write(handle, table_count_bytes.as_ptr(), table_count_bytes.len())
344 .map_err(|_| RemDbError::FileIoError)?;
345 if written != table_count_bytes.len() {
346 return Err(RemDbError::FileIoError);
347 }
348
349 for table_id in 0..table_count as usize {
351 if let Some(table) = &mut self.tables[table_id] {
352 table.snapshot_version = self.snapshot_version;
354
355 let table_id_u32 = table_id as u32;
357 let table_id_bytes = table_id_u32.to_le_bytes();
358 let written = crate::platform::file_write(handle, table_id_bytes.as_ptr(), table_id_bytes.len())
359 .map_err(|_| RemDbError::FileIoError)?;
360 if written != table_id_bytes.len() {
361 return Err(RemDbError::FileIoError);
362 }
363
364 let used_count_u32 = table.record_count() as u32;
366 let used_count_bytes = used_count_u32.to_le_bytes();
367 let written = crate::platform::file_write(handle, used_count_bytes.as_ptr(), used_count_bytes.len())
368 .map_err(|_| RemDbError::FileIoError)?;
369 if written != used_count_bytes.len() {
370 return Err(RemDbError::FileIoError);
371 }
372
373 let mut record_size = 0;
375 for field in table.def.fields {
376 record_size += field.size;
377 }
378
379 for i in 0..table.def.max_records {
381 let status_ptr = unsafe { table.get_status_ptr(i) };
382 if unsafe { (*status_ptr).status } == crate::types::RecordStatus::Used {
383 let index_u32 = i as u32;
385 let index_bytes = index_u32.to_le_bytes();
386 let written = crate::platform::file_write(handle, index_bytes.as_ptr(), index_bytes.len())
387 .map_err(|_| RemDbError::FileIoError)?;
388 if written != index_bytes.len() {
389 return Err(RemDbError::FileIoError);
390 }
391
392 let record_ptr = unsafe { table.get_record_ptr(i) };
394 let written = crate::platform::file_write(handle, record_ptr, record_size)
395 .map_err(|_| RemDbError::FileIoError)?;
396 if written != record_size {
397 return Err(RemDbError::FileIoError);
398 }
399 }
400 }
401 }
402 }
403
404 Ok(())
406 }
407
408 pub fn restore_snapshot(&mut self, path: &str) -> Result<()> {
410 let handle = crate::platform::file_open(path, crate::platform::FileMode::Read)
412 .map_err(|_| RemDbError::FileIoError)?;
413
414 let _defer = Defer::new(|| {
416 let _ = crate::platform::file_close(handle);
417 });
418
419 let mut magic_bytes = [0u8; 4];
421 let read = crate::platform::file_read(handle, magic_bytes.as_mut_ptr(), magic_bytes.len())
422 .map_err(|_| RemDbError::FileIoError)?;
423 if read != magic_bytes.len() {
424 return Err(RemDbError::FileIoError);
425 }
426 let magic = u32::from_le_bytes(magic_bytes);
427 if magic != Self::SNAPSHOT_MAGIC {
428 return Err(RemDbError::SnapshotFormatError);
429 }
430
431 let mut version_bytes = [0u8; 4];
433 let read = crate::platform::file_read(handle, version_bytes.as_mut_ptr(), version_bytes.len())
434 .map_err(|_| RemDbError::FileIoError)?;
435 if read != version_bytes.len() {
436 return Err(RemDbError::FileIoError);
437 }
438 let version = u32::from_le_bytes(version_bytes);
439 if version != Self::SNAPSHOT_VERSION {
440 return Err(RemDbError::SnapshotFormatError);
441 }
442
443 let mut snapshot_type_bytes = [0u8; 1];
445 let read = crate::platform::file_read(handle, snapshot_type_bytes.as_mut_ptr(), snapshot_type_bytes.len())
446 .map_err(|_| RemDbError::FileIoError)?;
447 if read != snapshot_type_bytes.len() {
448 return Err(RemDbError::FileIoError);
449 }
450 let snapshot_type = snapshot_type_bytes[0];
451
452 let mut base_version_bytes = [0u8; 4];
454 let read = crate::platform::file_read(handle, base_version_bytes.as_mut_ptr(), base_version_bytes.len())
455 .map_err(|_| RemDbError::FileIoError)?;
456 if read != base_version_bytes.len() {
457 return Err(RemDbError::FileIoError);
458 }
459 let base_version = u32::from_le_bytes(base_version_bytes);
460
461 let mut table_count_bytes = [0u8; 4];
463 let read = crate::platform::file_read(handle, table_count_bytes.as_mut_ptr(), table_count_bytes.len())
464 .map_err(|_| RemDbError::FileIoError)?;
465 if read != table_count_bytes.len() {
466 return Err(RemDbError::FileIoError);
467 }
468 let table_count = u32::from_le_bytes(table_count_bytes) as usize;
469
470 if table_count != self.config.tables.len() {
472 return Err(RemDbError::SnapshotFormatError);
473 }
474
475 for _ in 0..table_count {
477 let mut table_id_bytes = [0u8; 4];
479 let read = crate::platform::file_read(handle, table_id_bytes.as_mut_ptr(), table_id_bytes.len())
480 .map_err(|_| RemDbError::FileIoError)?;
481 if read != table_id_bytes.len() {
482 return Err(RemDbError::FileIoError);
483 }
484 let table_id = u32::from_le_bytes(table_id_bytes) as usize;
485
486 if table_id >= self.tables.len() {
488 return Err(RemDbError::SnapshotFormatError);
489 }
490
491 let table = match &mut self.tables[table_id] {
493 Some(table) => table,
494 None => return Err(RemDbError::SnapshotFormatError),
495 };
496
497 let mut record_count_bytes = [0u8; 4];
499 let read = crate::platform::file_read(handle, record_count_bytes.as_mut_ptr(), record_count_bytes.len())
500 .map_err(|_| RemDbError::FileIoError)?;
501 if read != record_count_bytes.len() {
502 return Err(RemDbError::FileIoError);
503 }
504 let record_count = u32::from_le_bytes(record_count_bytes) as usize;
505
506 let mut record_size = 0;
508 for field in table.def.fields {
509 record_size += field.size;
510 }
511
512 if snapshot_type == 0 {
513 for i in 0..table.def.max_records {
515 let status_ptr = unsafe { table.get_status_ptr(i) };
516 let record_ptr = unsafe { table.get_record_ptr_mut(i) };
517
518 unsafe {
519 (*status_ptr).status = crate::types::RecordStatus::Free;
520 (*status_ptr).version += 1;
521 crate::platform::memset(record_ptr, 0, table.def.record_size);
522 }
523 }
524
525 unsafe {
527 table.set_record_count(0);
528 }
529 }
530
531 for _ in 0..record_count {
533 let mut index_bytes = [0u8; 4];
535 let read = crate::platform::file_read(handle, index_bytes.as_mut_ptr(), index_bytes.len())
536 .map_err(|_| RemDbError::FileIoError)?;
537 if read != index_bytes.len() {
538 return Err(RemDbError::FileIoError);
539 }
540 let i = u32::from_le_bytes(index_bytes) as usize;
541
542 if i >= table.def.max_records {
544 return Err(RemDbError::SnapshotFormatError);
545 }
546
547 let record_ptr = unsafe { table.get_record_ptr_mut(i) };
549 let read = crate::platform::file_read(handle, record_ptr, record_size)
550 .map_err(|_| RemDbError::FileIoError)?;
551 if read != record_size {
552 return Err(RemDbError::FileIoError);
553 }
554
555 let status_ptr = unsafe { table.get_status_ptr(i) };
557 let current_status = unsafe { &mut *status_ptr };
558
559 if current_status.status != crate::types::RecordStatus::Used {
560 unsafe {
562 table.inc_record_count();
563 }
564 }
565
566 current_status.status = crate::types::RecordStatus::Used;
567 current_status.version += 1;
568 }
569 }
570
571 self.snapshot_version = base_version;
573
574 Ok(())
576 }
577
578 pub fn get_metrics(&self) -> &monitor::DbMetrics {
580 &self.metrics
581 }
582
583 pub fn metrics_snapshot(&self) -> monitor::DbMetricsSnapshot {
585 self.metrics.snapshot()
586 }
587
588 pub fn reset_metrics(&self) {
590 self.metrics.reset()
591 }
592
593 pub fn health_check(&self) -> monitor::HealthCheckResult {
595 let metrics = self.metrics.snapshot();
596
597 let memory_usage = metrics.used_memory as f64 / metrics.total_memory as f64;
599
600 let (status, details) = if memory_usage > 0.9 {
601 (monitor::HealthStatus::Unhealthy, alloc::string::String::from("内存使用率过高"))
602 } else if memory_usage > 0.7 {
603 (monitor::HealthStatus::Warning, alloc::string::String::from("内存使用率较高"))
604 } else {
605 (monitor::HealthStatus::Healthy, alloc::string::String::from("数据库运行正常"))
606 };
607
608 monitor::HealthCheckResult::new(status, metrics, details)
609 }
610}
611
612impl DdlExecutor for RemDb {
614 fn create_table(
615 &mut self,
616 name: &str,
617 fields: &[(&str, DataType)],
618 primary_key: Option<usize>
619 ) -> Result<()> {
620 if fields.is_empty() {
622 return Err(RemDbError::ConfigError);
623 }
624
625 if let Some(pk_index) = primary_key {
627 if pk_index >= fields.len() {
628 return Err(RemDbError::ConfigError);
629 }
630 }
631
632 let mut field_defs = Vec::new();
634 let mut offset = 0;
635 let mut record_size = 0;
636
637 for (i, (field_name, data_type)) in fields.iter().enumerate() {
638 let field_size = match data_type {
640 DataType::String => MAX_STRING_LEN,
641 _ => data_type.size(),
642 };
643
644 let field_name_static = Box::leak(field_name.to_string().into_boxed_str());
646
647 let is_primary_key = primary_key == Some(i);
649 let is_auto_increment = is_primary_key &&
650 (data_type == &DataType::Int32 || data_type == &DataType::Int64 ||
651 data_type == &DataType::UInt32 || data_type == &DataType::UInt64);
652
653 let field_def = FieldDef {
655 name: field_name_static,
656 data_type: *data_type,
657 size: field_size,
658 offset,
659 primary_key: is_primary_key, not_null: is_primary_key, unique: is_primary_key, auto_increment: is_auto_increment, };
664
665 field_defs.push(field_def);
666
667 offset += field_size;
669 record_size += field_size;
670 }
671
672 let table_name_static = Box::leak(name.to_string().into_boxed_str());
675 let field_defs_static = Box::leak(field_defs.into_boxed_slice());
676
677 let table_def = TableDef {
678 id: self.tables.len() as u8,
679 name: table_name_static,
680 fields: field_defs_static,
681 primary_key: primary_key.unwrap_or(0),
682 secondary_index: None,
683 secondary_index_type: IndexType::SortedArray,
684 record_size,
685 max_records: self.config.default_max_records,
686 };
687
688 let table_def_arc = alloc::sync::Arc::new(table_def);
690 let table = MemoryTable::new(table_def_arc.clone())?;
691
692 self.tables.push(Some(table));
694
695 let hash_table_size = (table_def.max_records * 2).next_power_of_two(); let index_memory_size = PrimaryIndex::calculate_memory_size(&table_def, hash_table_size, table_def.max_records);
699
700 let index_memory = crate::memory::allocator::alloc(index_memory_size)?;
702 let hash_table_start = index_memory.as_ptr() as *mut Option<NonNull<PrimaryIndexItem>>;
703 let items_start = (index_memory.as_ptr() as usize + hash_table_size * core::mem::size_of::<Option<NonNull<PrimaryIndexItem>>>()) as *mut PrimaryIndexItem;
704
705 let primary_index = unsafe {
707 PrimaryIndex::new(
708 table_def_arc.clone(),
709 hash_table_start,
710 items_start,
711 hash_table_size,
712 table_def.max_records
713 )
714 };
715 self.primary_indices.push(Some(primary_index));
716
717 self.secondary_indices.push(None);
719
720 Ok(())
721 }
722
723 fn create_index(
724 &mut self,
725 table_name: &str,
726 field_name: &str,
727 index_type: IndexType
728 ) -> Result<()> {
729 let table_id = self.tables.iter().position(|t| {
731 if let Some(table) = t {
732 table.def.name == table_name
733 } else {
734 false
735 }
736 }).ok_or(RemDbError::TableNotFound)?;
737
738 let table = self.tables[table_id].as_ref().ok_or(RemDbError::TableNotFound)?;
740 let field_index = table.def.fields.iter().position(|f| f.name == field_name)
741 .ok_or(RemDbError::FieldNotFound)?;
742
743 if self.secondary_indices[table_id].is_some() {
745 return Err(RemDbError::ConfigError);
746 }
747
748 let mut new_fields = Vec::new();
750 for field in table.def.fields {
751 new_fields.push(FieldDef {
752 name: field.name,
753 data_type: field.data_type,
754 size: field.size,
755 offset: field.offset,
756 primary_key: field.primary_key,
757 not_null: field.not_null,
758 unique: field.unique,
759 auto_increment: field.auto_increment,
760 });
761 }
762
763 let new_def = alloc::boxed::Box::new(TableDef {
764 id: table.def.id,
765 name: table.def.name,
766 fields: new_fields.leak(),
767 primary_key: table.def.primary_key,
768 secondary_index: Some(field_index),
769 secondary_index_type: index_type,
770 record_size: table.def.record_size,
771 max_records: table.def.max_records,
772 });
773
774 let max_items = table.def.max_records;
776
777 let index_max_nodes = match index_type {
779 IndexType::BTree | IndexType::TTree => 100, IndexType::SortedArray => max_items, IndexType::Hash => max_items, };
783
784 let index_size = AnySecondaryIndex::calculate_memory_size(new_def.as_ref(), index_max_nodes);
785 let index_memory = crate::memory::allocator::alloc(index_size)?;
786
787 unsafe {
789 let index = AnySecondaryIndex::new(
790 alloc::sync::Arc::from(new_def),
791 index_memory.as_ptr(),
792 index_max_nodes
793 )?;
794
795 self.secondary_indices[table_id] = Some(index);
797 }
798
799 Ok(())
800}
801}
802
803impl RemDb {
804 pub fn dump_metrics(&self) -> alloc::string::String {
806 self.metrics.snapshot().to_text()
807 }
808
809 pub fn sql_query(&mut self, sql: &str) -> Result<sql::ResultSet> {
811 let query = crate::sql::parse_sql_query(sql)
813 .map_err(|_| RemDbError::InvalidSqlQuery)?;
814
815 let result_set = crate::sql::execute_query(self, &query)
817 .map_err(|err| {
818 match err {
819 crate::sql::QueryExecutionError::TableNotFound => RemDbError::TableNotFound,
820 crate::sql::QueryExecutionError::FieldNotFound => RemDbError::FieldNotFound,
821 crate::sql::QueryExecutionError::TypeMismatch => RemDbError::TypeMismatch,
822 crate::sql::QueryExecutionError::ConstraintsConflicts => RemDbError::DuplicateKey,
823 _ => RemDbError::UnsupportedOperation,
824 }
825 })?;
826
827 Ok(result_set)
828 }
829
830 pub fn export_ddl(&self, path: &str) -> Result<()> {
832 use std::fs::File;
834 use std::io::Write;
835
836 let mut file = File::create(path).map_err(|_| RemDbError::FileIoError)?;
837
838 for table_id in 0..self.tables.len() {
840 if let Some(table) = &self.tables[table_id] {
841 let mut create_table_sql = alloc::string::String::new();
843 create_table_sql.push_str(&format!("CREATE TABLE {} (\n", table.def.name.to_lowercase()));
844
845 let mut fields_sql = Vec::new();
847 for field in table.def.fields {
848 let field_sql = format!(" {} {} {}",
849 field.name,
850 field.data_type.to_sql_type(field.size),
851 field.constraints_to_sql());
852 fields_sql.push(field_sql);
853 }
854
855 create_table_sql.push_str(&fields_sql.join(",\n"));
857 create_table_sql.push_str("\n);\n\n");
858
859 file.write_all(create_table_sql.as_bytes()).map_err(|_| RemDbError::FileIoError)?;
861
862 if let Some(secondary_index) = table.def.secondary_index {
864 if secondary_index < table.def.fields.len() {
865 let index_field = &table.def.fields[secondary_index];
866 let index_name = format!("idx_{}_{}", table.def.name.to_lowercase(), index_field.name);
867 let index_type = match table.def.secondary_index_type {
868 IndexType::Hash => "hash",
869 IndexType::SortedArray => "sortedarray",
870 IndexType::BTree => "btree",
871 IndexType::TTree => "ttree",
872 };
873
874 let create_index_sql = format!("CREATE INDEX {} ON {} USING {} ({});\n\n",
875 index_name, table.def.name.to_lowercase(), index_type, index_field.name);
876
877 file.write_all(create_index_sql.as_bytes()).map_err(|_| RemDbError::FileIoError)?;
879 }
880 }
881 }
882 }
883
884 Ok(())
885 }
886
887 pub fn export_data(&self, path: &str) -> Result<()> {
889 use std::fs::File;
891 use std::io::Write;
892
893 let mut sql_statements = alloc::string::String::new();
895
896 for table_id in 0..self.tables.len() {
898 if let Some(table) = &self.tables[table_id] {
899 let table_ref = table.def.clone();
901
902 unsafe {
904 table.iterate(|_id, record_ptr| {
905 let mut insert_sql = alloc::string::String::new();
907 insert_sql.push_str(&format!("INSERT INTO {} (", table_ref.name.to_lowercase()));
908
909 let mut field_names = Vec::new();
911 let mut field_values = Vec::new();
912
913 for field in table_ref.fields {
914 field_names.push(field.name);
915
916 let field_ptr = record_ptr.add(field.offset);
918 let value_str = match field.data_type {
919 DataType::UInt8 => format!("{}", *field_ptr as u8),
920 DataType::UInt16 => format!("{}", core::ptr::read_unaligned(field_ptr as *const u16)),
921 DataType::UInt32 => format!("{}", core::ptr::read_unaligned(field_ptr as *const u32)),
922 DataType::UInt64 => format!("{}", core::ptr::read_unaligned(field_ptr as *const u64)),
923 DataType::Int8 => format!("{}", core::ptr::read_unaligned(field_ptr as *const i8)),
924 DataType::Int16 => format!("{}", core::ptr::read_unaligned(field_ptr as *const i16)),
925 DataType::Int32 => format!("{}", core::ptr::read_unaligned(field_ptr as *const i32)),
926 DataType::Int64 => format!("{}", core::ptr::read_unaligned(field_ptr as *const i64)),
927 DataType::Float32 => format!("{}", core::ptr::read_unaligned(field_ptr as *const f32)),
928 DataType::Float64 => format!("{}", core::ptr::read_unaligned(field_ptr as *const f64)),
929 DataType::Bool => format!("{}", *field_ptr != 0),
930 DataType::Timestamp => format!("{}", core::ptr::read_unaligned(field_ptr as *const u64)),
931 DataType::String => {
932 let mut str_value = alloc::string::String::new();
934 for i in 0..field.size {
935 let c = *field_ptr.add(i);
936 if c == 0 {
937 break;
938 }
939 str_value.push(c as char);
940 }
941 format!("'{}'", str_value)
942 },
943 };
944
945 field_values.push(value_str);
946 }
947
948 insert_sql.push_str(&field_names.join(", "));
950 insert_sql.push_str(") VALUES (");
951 insert_sql.push_str(&field_values.join(", "));
952 insert_sql.push_str(");\n");
953
954 sql_statements.push_str(&insert_sql);
956
957 true
959 }).unwrap_or(());
960 }
961 }
962 }
963
964 let mut file = File::create(path).map_err(|_| RemDbError::FileIoError)?;
966 file.write_all(sql_statements.as_bytes()).map_err(|_| RemDbError::FileIoError)?;
967
968 Ok(())
969 }
970
971 pub fn save_incremental_snapshot(&mut self, path: &str) -> Result<()> {
973 let handle = crate::platform::file_open(path, crate::platform::FileMode::Write)
975 .map_err(|_| RemDbError::FileIoError)?;
976
977 let _defer = Defer::new(|| {
979 let _ = crate::platform::file_close(handle);
980 });
981
982 let magic = Self::SNAPSHOT_MAGIC.to_le_bytes();
984 let written = crate::platform::file_write(handle, magic.as_ptr(), magic.len())
985 .map_err(|_| RemDbError::FileIoError)?;
986 if written != magic.len() {
987 return Err(RemDbError::FileIoError);
988 }
989
990 let version = Self::SNAPSHOT_VERSION.to_le_bytes();
992 let written = crate::platform::file_write(handle, version.as_ptr(), version.len())
993 .map_err(|_| RemDbError::FileIoError)?;
994 if written != version.len() {
995 return Err(RemDbError::FileIoError);
996 }
997
998 let snapshot_type = 1u8;
1000 let written = crate::platform::file_write(handle, &snapshot_type as *const u8, 1)
1001 .map_err(|_| RemDbError::FileIoError)?;
1002 if written != 1 {
1003 return Err(RemDbError::FileIoError);
1004 }
1005
1006 let base_version_bytes = self.snapshot_version.to_le_bytes();
1008 let written = crate::platform::file_write(handle, base_version_bytes.as_ptr(), base_version_bytes.len())
1009 .map_err(|_| RemDbError::FileIoError)?;
1010 if written != base_version_bytes.len() {
1011 return Err(RemDbError::FileIoError);
1012 }
1013
1014 let table_count = self.config.tables.len() as u32;
1016 let table_count_bytes = table_count.to_le_bytes();
1017 let written = crate::platform::file_write(handle, table_count_bytes.as_ptr(), table_count_bytes.len())
1018 .map_err(|_| RemDbError::FileIoError)?;
1019 if written != table_count_bytes.len() {
1020 return Err(RemDbError::FileIoError);
1021 }
1022
1023 for table_id in 0..table_count as usize {
1025 if let Some(table) = &mut self.tables[table_id] {
1026 let mut changed_records = 0;
1028 let mut record_indices = Vec::new();
1029
1030 for i in 0..table.def.max_records {
1031 let status_ptr = unsafe { table.get_status_ptr(i) };
1032 if unsafe { (*status_ptr).status } == crate::types::RecordStatus::Used {
1033 let status = unsafe { &*status_ptr };
1034 if status.version > table.snapshot_version as u16 {
1035 changed_records += 1;
1036 record_indices.push(i);
1037 }
1038 }
1039 }
1040
1041 let table_id_u32 = table_id as u32;
1043 let table_id_bytes = table_id_u32.to_le_bytes();
1044 let written = crate::platform::file_write(handle, table_id_bytes.as_ptr(), table_id_bytes.len())
1045 .map_err(|_| RemDbError::FileIoError)?;
1046 if written != table_id_bytes.len() {
1047 return Err(RemDbError::FileIoError);
1048 }
1049
1050 let changed_count_u32 = changed_records as u32;
1052 let changed_count_bytes = changed_count_u32.to_le_bytes();
1053 let written = crate::platform::file_write(handle, changed_count_bytes.as_ptr(), changed_count_bytes.len())
1054 .map_err(|_| RemDbError::FileIoError)?;
1055 if written != changed_count_bytes.len() {
1056 return Err(RemDbError::FileIoError);
1057 }
1058
1059 let mut record_size = 0;
1061 for field in table.def.fields {
1062 record_size += field.size;
1063 }
1064
1065 for i in record_indices {
1067 let index_u32 = i as u32;
1069 let index_bytes = index_u32.to_le_bytes();
1070 let written = crate::platform::file_write(handle, index_bytes.as_ptr(), index_bytes.len())
1071 .map_err(|_| RemDbError::FileIoError)?;
1072 if written != index_bytes.len() {
1073 return Err(RemDbError::FileIoError);
1074 }
1075
1076 let record_ptr = unsafe { table.get_record_ptr(i) };
1078 let written = crate::platform::file_write(handle, record_ptr, record_size)
1079 .map_err(|_| RemDbError::FileIoError)?;
1080 if written != record_size {
1081 return Err(RemDbError::FileIoError);
1082 }
1083 }
1084
1085 table.snapshot_version = self.snapshot_version;
1087 }
1088 }
1089
1090 Ok(())
1092 }
1093}
1094
1095struct Defer<F: FnMut()>(Option<F>);
1097
1098impl<F: FnMut()> Defer<F> {
1099 pub fn new(f: F) -> Self {
1101 Defer(Some(f))
1102 }
1103}
1104
1105impl<F: FnMut()> Drop for Defer<F> {
1106 fn drop(&mut self) {
1107 if let Some(mut f) = self.0.take() {
1108 f();
1109 }
1110 }
1111}
1112
1113
1114static mut DB_INSTANCE: Option<RemDb> = None;
1116
1117pub fn init_global_db(
1120 config: &'static config::DbConfig
1121) -> Result<&'static mut RemDb> {
1122 unsafe {
1123 let mut db = RemDb::new(config);
1125 db.init()?;
1126
1127 for table_def in config.tables {
1129 let table = MemoryTable::new(alloc::sync::Arc::new(*table_def))?;
1131 db.tables.push(Some(table));
1132
1133 db.primary_indices.push(None);
1135 db.secondary_indices.push(None);
1136 }
1137
1138 DB_INSTANCE = Some(db);
1140
1141 Ok(DB_INSTANCE.as_mut().unwrap())
1142 }
1143}
1144
1145pub fn get_global_db() -> Option<&'static mut RemDb> {
1147 unsafe {
1148 DB_INSTANCE.as_mut()
1149 }
1150}
1151
1152pub fn reset_global_db() {
1155 unsafe {
1156 DB_INSTANCE = None;
1157 }
1158}
1159
1160#[cfg(feature = "c-api")]
1162mod c_api;
1163
1164#[cfg(not(feature = "std"))]
1166#[panic_handler]
1167fn panic_handler(_info: &core::panic::PanicInfo) -> ! {
1168 loop {
1169 core::hint::spin_loop();
1170 }
1171}