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