1use std::cell::{Cell, RefCell};
11use std::hash::{Hash, Hasher};
12use std::rc::{Rc, Weak};
13
14use wasm_dbms_api::prelude::{DbmsResult, TransactionId};
15use wasm_dbms_memory::prelude::{MemoryManager, MemoryProvider, SchemaRegistry, TableRegistryPage};
16
17use crate::transaction::journal::Journal;
18use crate::transaction::session::TransactionSession;
19
20#[derive(Clone, Debug)]
25pub struct ContextId(Weak<()>);
26
27impl ContextId {
28 #[must_use]
30 pub fn is_alive(&self) -> bool {
31 self.0.strong_count() > 0
32 }
33}
34
35impl PartialEq for ContextId {
36 fn eq(&self, other: &Self) -> bool {
37 Weak::ptr_eq(&self.0, &other.0)
38 }
39}
40
41impl Eq for ContextId {}
42
43impl Hash for ContextId {
44 fn hash<H>(&self, state: &mut H)
45 where
46 H: Hasher,
47 {
48 self.0.as_ptr().hash(state);
49 }
50}
51
52pub struct DbmsContext<M>
67where
68 M: MemoryProvider,
69{
70 identity: Rc<()>,
72
73 pub(crate) mm: RefCell<MemoryManager<M>>,
75
76 pub(crate) schema_registry: RefCell<SchemaRegistry>,
78
79 pub(crate) transaction_session: RefCell<TransactionSession>,
81
82 pub(crate) journal: RefCell<Option<Journal>>,
84
85 pub(crate) drift: Cell<Option<(u64, bool)>>,
88
89 pub(crate) migrating: Cell<bool>,
93}
94
95impl<M> DbmsContext<M>
96where
97 M: MemoryProvider,
98{
99 pub fn new(memory: M) -> Self {
102 let mut mm = MemoryManager::init(memory);
103 let schema_registry = SchemaRegistry::load(&mut mm).unwrap_or_default();
104 Self {
105 identity: Rc::new(()),
106 mm: RefCell::new(mm),
107 schema_registry: RefCell::new(schema_registry),
108 transaction_session: RefCell::new(TransactionSession::default()),
109 journal: RefCell::new(None),
110 drift: Cell::new(None),
111 migrating: Cell::new(false),
112 }
113 }
114
115 pub fn id(&self) -> ContextId {
117 ContextId(Rc::downgrade(&self.identity))
118 }
119
120 pub fn register_table<T: wasm_dbms_api::prelude::TableSchema>(
122 &self,
123 ) -> DbmsResult<TableRegistryPage> {
124 let mut sr = self.schema_registry.borrow_mut();
125 let mut mm = self.mm.borrow_mut();
126 sr.register_table::<T>(&mut *mm).map_err(Into::into)
127 }
128
129 pub fn has_table(&self, name: &str) -> bool {
131 self.schema_registry
132 .borrow()
133 .table_registry_page_by_name(name)
134 .is_some()
135 }
136
137 pub fn begin_transaction(&self) -> TransactionId {
144 self.transaction_session.borrow_mut().begin_transaction()
145 }
146
147 pub fn has_transaction(&self, tx_id: &TransactionId) -> bool {
152 self.transaction_session.borrow().has_transaction(tx_id)
153 }
154
155 pub(crate) fn cached_drift_for(&self, compiled_hash: u64) -> Option<bool> {
157 self.drift
158 .get()
159 .and_then(|(hash, drifted)| (hash == compiled_hash).then_some(drifted))
160 }
161
162 pub(crate) fn set_drift(&self, compiled_hash: u64, value: bool) {
164 self.drift.set(Some((compiled_hash, value)));
165 }
166
167 pub(crate) fn clear_drift(&self) {
169 self.drift.set(None);
170 }
171
172 pub(crate) fn is_migrating(&self) -> bool {
174 self.migrating.get()
175 }
176
177 pub(crate) fn set_migrating(&self, value: bool) {
179 self.migrating.set(value);
180 }
181}
182
183impl<M> std::fmt::Debug for DbmsContext<M>
184where
185 M: MemoryProvider,
186{
187 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
188 f.debug_struct("DbmsContext")
189 .field("schema_registry", &self.schema_registry)
190 .field("transaction_session", &self.transaction_session)
191 .finish_non_exhaustive()
192 }
193}
194
195#[cfg(test)]
196mod tests {
197 use wasm_dbms_memory::prelude::HeapMemoryProvider;
198
199 use super::*;
200
201 #[test]
202 fn test_should_create_context() {
203 let ctx = DbmsContext::new(HeapMemoryProvider::default());
204 assert!(!ctx.has_table("users"));
205 }
206
207 #[test]
208 fn test_should_begin_transaction() {
209 let ctx = DbmsContext::new(HeapMemoryProvider::default());
210 let tx_id = ctx.begin_transaction();
211 assert!(ctx.has_transaction(&tx_id));
212 assert!(!ctx.has_transaction(&(tx_id + 1)));
213 }
214
215 #[test]
216 fn test_transaction_ids_restart_with_the_context() {
217 let first = DbmsContext::new(HeapMemoryProvider::default());
218 let second = DbmsContext::new(HeapMemoryProvider::default());
219 assert_eq!(first.begin_transaction(), second.begin_transaction());
220 }
221
222 #[test]
223 fn test_context_identity_is_stable_and_unique() {
224 let first = DbmsContext::new(HeapMemoryProvider::default());
225 let first_id = first.id();
226 let moved = first;
227 let second = DbmsContext::new(HeapMemoryProvider::default());
228
229 assert_eq!(first_id, moved.id());
230 assert_ne!(first_id, second.id());
231 }
232
233 #[test]
234 fn test_should_debug_context() {
235 let ctx = DbmsContext::new(HeapMemoryProvider::default());
236 let debug = format!("{ctx:?}");
237 assert!(debug.contains("DbmsContext"));
238 }
239}