llkv_runtime/runtime_storage_namespace/
namespace.rs

1//! Runtime-facing namespace adapters.
2//!
3//! These adapters wrap `RuntimeContext` instances so sessions can route logical
4//! operations (persistent vs. temporary) without exposing the underlying storage
5//! engine to higher layers. They are **not** the same as namespaces inside the
6//! storage engine or column-map crates; they strictly coordinate runtime-level
7//! routing.
8
9use std::any::Any;
10use std::sync::{Arc, RwLock};
11
12use llkv_executor::ExecutorTable;
13use llkv_plan::{
14    AlterTablePlan, CreateIndexPlan, CreateTablePlan, CreateViewPlan, DropIndexPlan, DropTablePlan,
15    DropViewPlan, PlanOperation, RenameTablePlan,
16};
17use llkv_result::Error;
18use llkv_storage::pager::BoxedPager;
19use llkv_transaction::TransactionResult;
20
21use crate::{RuntimeContext, RuntimeStatementResult};
22use llkv_table::{CatalogDdl, SingleColumnIndexDescriptor};
23
24/// Identifier type used by runtime storage namespaces.
25pub type RuntimeNamespaceId = String;
26
27/// Canonical identifier for the persistent runtime namespace.
28pub const PERSISTENT_NAMESPACE_ID: &str = "main";
29/// Canonical identifier for the temporary runtime namespace.
30pub const TEMPORARY_NAMESPACE_ID: &str = "temp";
31
32/// Runtime-level namespace abstraction.
33///
34/// Implementors wrap a `RuntimeContext` for a specific routing domain (e.g.
35/// persistent tables, temporary tables). The namespace knows how to forward
36/// DDL/DML plan execution into the underlying context, but it does **not**
37/// perform storage allocation or manage physical pagers on its own.
38pub trait RuntimeStorageNamespace: Send + Sync + 'static {
39    /// Identifier used when resolving schemas (e.g. "main", "temp").
40    fn namespace_id(&self) -> &RuntimeNamespaceId;
41
42    /// Returns the runtime context bound to this namespace.
43    fn context(&self) -> Arc<RuntimeContext<BoxedPager>>;
44
45    /// Create a table inside this namespace.
46    fn create_table(
47        &self,
48        plan: CreateTablePlan,
49    ) -> crate::Result<RuntimeStatementResult<BoxedPager>>;
50
51    /// Drop a table from this namespace by forwarding the planned request.
52    fn drop_table(&self, plan: DropTablePlan) -> crate::Result<()>;
53
54    /// Rename a table within this namespace.
55    fn rename_table(&self, plan: RenameTablePlan) -> crate::Result<()>;
56
57    /// Alter a table within this namespace.
58    fn alter_table(
59        &self,
60        plan: AlterTablePlan,
61    ) -> crate::Result<RuntimeStatementResult<BoxedPager>>;
62
63    /// Create an index within this namespace.
64    fn create_index(
65        &self,
66        plan: CreateIndexPlan,
67    ) -> crate::Result<RuntimeStatementResult<BoxedPager>>;
68
69    /// Drop an index from this namespace by forwarding the request.
70    fn drop_index(&self, plan: DropIndexPlan)
71    -> crate::Result<Option<SingleColumnIndexDescriptor>>;
72
73    /// Create a view inside this namespace.
74    fn create_view(&self, plan: CreateViewPlan) -> crate::Result<()>;
75
76    /// Drop a view from this namespace by forwarding the planned request.
77    fn drop_view(&self, plan: DropViewPlan) -> crate::Result<()>;
78
79    /// Execute a generic plan operation. Namespaces that do not yet support
80    /// this entry point should override this method.
81    fn execute_operation(
82        &self,
83        operation: PlanOperation,
84    ) -> crate::Result<TransactionResult<BoxedPager>> {
85        let _ = operation;
86        Err(Error::Internal(format!(
87            "runtime namespace '{}' does not yet support execute_operation",
88            self.namespace_id()
89        )))
90    }
91
92    /// Lookup a table by canonical name.
93    fn lookup_table(&self, canonical: &str) -> crate::Result<Arc<ExecutorTable<BoxedPager>>>;
94
95    /// List tables visible to this namespace.
96    fn list_tables(&self) -> Vec<String>;
97}
98
99/// Helper trait for storing runtime namespaces behind trait objects with basic
100/// inspection support.
101pub trait RuntimeStorageNamespaceOps: Send + Sync {
102    fn namespace_id(&self) -> &RuntimeNamespaceId;
103    fn list_tables(&self) -> Vec<String>;
104    fn as_any(&self) -> &dyn Any;
105}
106
107impl<T> RuntimeStorageNamespaceOps for T
108where
109    T: RuntimeStorageNamespace,
110{
111    fn namespace_id(&self) -> &RuntimeNamespaceId {
112        RuntimeStorageNamespace::namespace_id(self)
113    }
114
115    fn list_tables(&self) -> Vec<String> {
116        RuntimeStorageNamespace::list_tables(self)
117    }
118
119    fn as_any(&self) -> &dyn Any {
120        self
121    }
122}
123
124/// Persistent runtime namespace wrapper.
125#[derive(Clone)]
126pub struct PersistentRuntimeNamespace {
127    id: RuntimeNamespaceId,
128    context: Arc<RuntimeContext<BoxedPager>>,
129}
130
131impl PersistentRuntimeNamespace {
132    pub fn new(id: RuntimeNamespaceId, context: Arc<RuntimeContext<BoxedPager>>) -> Self {
133        Self { id, context }
134    }
135}
136
137impl RuntimeStorageNamespace for PersistentRuntimeNamespace {
138    fn namespace_id(&self) -> &RuntimeNamespaceId {
139        &self.id
140    }
141
142    fn context(&self) -> Arc<RuntimeContext<BoxedPager>> {
143        Arc::clone(&self.context)
144    }
145
146    fn create_table(
147        &self,
148        plan: CreateTablePlan,
149    ) -> crate::Result<RuntimeStatementResult<BoxedPager>> {
150        CatalogDdl::create_table(self.context.as_ref(), plan)
151    }
152
153    fn drop_table(&self, plan: DropTablePlan) -> crate::Result<()> {
154        CatalogDdl::drop_table(self.context.as_ref(), plan)
155    }
156
157    fn rename_table(&self, plan: RenameTablePlan) -> crate::Result<()> {
158        CatalogDdl::rename_table(self.context.as_ref(), plan)
159    }
160
161    fn alter_table(
162        &self,
163        plan: AlterTablePlan,
164    ) -> crate::Result<RuntimeStatementResult<BoxedPager>> {
165        CatalogDdl::alter_table(self.context.as_ref(), plan)
166    }
167
168    fn create_index(
169        &self,
170        plan: CreateIndexPlan,
171    ) -> crate::Result<RuntimeStatementResult<BoxedPager>> {
172        CatalogDdl::create_index(self.context.as_ref(), plan)
173    }
174
175    fn drop_index(
176        &self,
177        plan: DropIndexPlan,
178    ) -> crate::Result<Option<SingleColumnIndexDescriptor>> {
179        CatalogDdl::drop_index(self.context.as_ref(), plan)
180    }
181
182    fn create_view(&self, plan: CreateViewPlan) -> crate::Result<()> {
183        let view_definition = plan.view_definition.clone();
184        let select_plan = *plan.select_plan.clone();
185        let context_arc = Arc::clone(&self.context);
186        context_arc.create_view(&plan.name, view_definition, select_plan, plan.if_not_exists)
187    }
188
189    fn drop_view(&self, plan: DropViewPlan) -> crate::Result<()> {
190        self.context.drop_view(&plan.name, plan.if_exists)
191    }
192
193    fn lookup_table(&self, canonical: &str) -> crate::Result<Arc<ExecutorTable<BoxedPager>>> {
194        self.context.lookup_table(canonical)
195    }
196
197    fn list_tables(&self) -> Vec<String> {
198        self.context.table_names()
199    }
200}
201
202/// Temporary runtime namespace wrapper.
203#[derive(Clone)]
204pub struct TemporaryRuntimeNamespace {
205    id: RuntimeNamespaceId,
206    context: Arc<RwLock<Arc<RuntimeContext<BoxedPager>>>>,
207}
208
209impl TemporaryRuntimeNamespace {
210    pub fn new(id: RuntimeNamespaceId, context: Arc<RuntimeContext<BoxedPager>>) -> Self {
211        Self {
212            id,
213            context: Arc::new(RwLock::new(context)),
214        }
215    }
216
217    pub fn replace_context(&self, context: Arc<RuntimeContext<BoxedPager>>) {
218        let mut guard = self
219            .context
220            .write()
221            .expect("temporary context lock poisoned");
222        *guard = context;
223    }
224
225    pub fn clear_tables<I>(&self, canonical_names: I)
226    where
227        I: IntoIterator<Item = String>,
228    {
229        let canonical_names: Vec<String> = canonical_names.into_iter().collect();
230        if canonical_names.is_empty() {
231            return;
232        }
233
234        let catalog = self.context().table_catalog();
235        for canonical in canonical_names {
236            if let Some(table_id) = catalog.table_id(&canonical)
237                && catalog.unregister_table(table_id)
238            {
239                tracing::debug!(
240                    "[TEMP] Unregistered temporary table '{}' from shared catalog",
241                    canonical
242                );
243            }
244        }
245    }
246
247    pub fn context(&self) -> Arc<RuntimeContext<BoxedPager>> {
248        Arc::clone(
249            &self
250                .context
251                .read()
252                .expect("temporary context lock poisoned"),
253        )
254    }
255}
256
257impl RuntimeStorageNamespace for TemporaryRuntimeNamespace {
258    fn namespace_id(&self) -> &RuntimeNamespaceId {
259        &self.id
260    }
261
262    fn context(&self) -> Arc<RuntimeContext<BoxedPager>> {
263        self.context()
264    }
265
266    fn create_table(
267        &self,
268        plan: CreateTablePlan,
269    ) -> crate::Result<RuntimeStatementResult<BoxedPager>> {
270        let context = self.context();
271        let result = CatalogDdl::create_table(context.as_ref(), plan)?;
272        Ok(result)
273    }
274
275    fn drop_table(&self, plan: DropTablePlan) -> crate::Result<()> {
276        let context = self.context();
277        CatalogDdl::drop_table(context.as_ref(), plan)?;
278        Ok(())
279    }
280
281    fn rename_table(&self, plan: RenameTablePlan) -> crate::Result<()> {
282        let context = self.context();
283        CatalogDdl::rename_table(context.as_ref(), plan)?;
284        Ok(())
285    }
286
287    fn alter_table(
288        &self,
289        plan: AlterTablePlan,
290    ) -> crate::Result<RuntimeStatementResult<BoxedPager>> {
291        let context = self.context();
292        CatalogDdl::alter_table(context.as_ref(), plan)
293    }
294
295    fn create_index(
296        &self,
297        plan: CreateIndexPlan,
298    ) -> crate::Result<RuntimeStatementResult<BoxedPager>> {
299        let context = self.context();
300        CatalogDdl::create_index(context.as_ref(), plan)
301    }
302
303    fn drop_index(
304        &self,
305        plan: DropIndexPlan,
306    ) -> crate::Result<Option<SingleColumnIndexDescriptor>> {
307        let context = self.context();
308        CatalogDdl::drop_index(context.as_ref(), plan)
309    }
310
311    fn create_view(&self, plan: CreateViewPlan) -> crate::Result<()> {
312        let context = self.context(); // This returns Arc<RuntimeContext>
313        let view_definition = plan.view_definition.clone();
314        let select_plan = *plan.select_plan.clone();
315        context.create_view(&plan.name, view_definition, select_plan, plan.if_not_exists)
316    }
317
318    fn drop_view(&self, plan: DropViewPlan) -> crate::Result<()> {
319        let context = self.context();
320        context.drop_view(&plan.name, plan.if_exists)
321    }
322
323    fn lookup_table(&self, canonical: &str) -> crate::Result<Arc<ExecutorTable<BoxedPager>>> {
324        // Delegate to context, which handles fallback lookup internally
325        self.context().lookup_table(canonical)
326    }
327
328    fn list_tables(&self) -> Vec<String> {
329        self.context().table_names()
330    }
331}