Skip to main content

icydb_core/db/session/
mod.rs

1//! Module: session
2//! Responsibility: user-facing query/write execution facade over db executors.
3//! Does not own: planning semantics, cursor validation rules, or storage mutation protocol.
4//! Boundary: converts fluent/query intent calls into executor operations and response DTOs.
5
6mod accepted_schema;
7mod bounded_cache;
8mod catalog;
9mod integrity;
10mod mutation_job;
11mod query;
12mod read_set;
13mod request;
14mod response;
15mod resumable_job;
16#[cfg(feature = "sql")]
17mod sql;
18mod write;
19
20#[cfg(all(test, feature = "sql", feature = "diagnostics"))]
21mod tests;
22
23use crate::metrics::sink::with_metrics_sink;
24use crate::{
25    db::{Db, StoreRegistry},
26    metrics::sink::MetricsSink,
27    traits::CanisterKind,
28};
29use std::thread::LocalKey;
30
31pub(in crate::db) use accepted_schema::AcceptedSchemaCatalogContext;
32#[cfg(all(test, feature = "sql", feature = "diagnostics"))]
33pub(in crate::db) use accepted_schema::{
34    AcceptedSchemaRuntimeBuildCounts, accepted_schema_runtime_build_counts_for_tests,
35    reset_accepted_schema_runtime_build_counts_for_tests,
36};
37#[cfg(all(feature = "sql", feature = "diagnostics"))]
38pub use query::{
39    DirectDataRowAttribution, GroupedCountAttribution, GroupedExecutionAttribution,
40    KernelRowAttribution, ScalarAggregateAttribution,
41};
42#[doc(hidden)]
43pub use query::{
44    MAX_TYPED_EXACT_KEY_BATCH_INPUT_BYTES, MAX_TYPED_EXACT_KEY_BATCH_ITEMS,
45    MAX_TYPED_EXACT_KEY_BATCH_RESULT_BYTES, MAX_TYPED_EXACT_KEY_BATCH_STORED_BYTES,
46};
47pub use request::RequestExecutionRoot;
48pub(in crate::db) use request::RequestExecutionScope;
49pub(in crate::db) use response::finalize_structural_grouped_projection_result;
50pub(in crate::db) use response::grouped_cursor_from_bytes;
51#[cfg(all(feature = "sql", feature = "diagnostics"))]
52pub use sql::{
53    SqlCompileAttribution, SqlDistinctProjectionAttribution, SqlExecutionAttribution,
54    SqlHybridCoveringAttribution, SqlOutputBlobAttribution, SqlPureCoveringAttribution,
55    SqlQueryCacheAttribution, SqlQueryExecutionAttribution,
56};
57#[cfg(feature = "sql")]
58pub use sql::{
59    SqlConstraintValidationPage, SqlConstraintValidationRevisionStatus,
60    SqlConstraintValidationState, SqlDdlExecutionStatus, SqlDdlMutationKind,
61    SqlDdlPreparationReport, SqlIntegrityError, SqlStatementDispatch, SqlStatementResult,
62    SqlStatementShellSurface, SqlStatementSurface, TrustedResumableUpdateContinuation,
63    TrustedResumableUpdatePhase, TrustedResumableUpdateReceipt,
64    TrustedResumableUpdateRestartReason, sql_statement_dispatch, sql_statement_entity_name,
65    sql_statement_shell_surface, sql_statement_surface,
66};
67#[cfg(feature = "sql")]
68pub(in crate::db::session) use write::{
69    AcceptedStructuralMutation, AcceptedStructuralMutationTarget,
70    structural_data_key_from_runtime_values,
71};
72
73///
74/// DbSession
75///
76/// Session-scoped database handle with policy (debug, metrics) and execution routing.
77///
78
79pub struct DbSession<C: CanisterKind> {
80    db: Db<C>,
81    debug: bool,
82    metrics: Option<&'static dyn MetricsSink>,
83}
84
85impl<C: CanisterKind> DbSession<C> {
86    /// Construct one session facade over a sealed runtime store registry.
87    #[must_use]
88    pub fn new(
89        store: &'static LocalKey<StoreRegistry>,
90        request_root: &RequestExecutionRoot,
91    ) -> Self {
92        Self {
93            db: Db::new(store, request_root.scope()),
94            debug: false,
95            metrics: None,
96        }
97    }
98
99    /// Advance generated startup recovery without admitting ordinary database work.
100    #[doc(hidden)]
101    pub fn __continue_startup_recovery(&self) -> Result<bool, crate::error::InternalError> {
102        self.db.continue_startup_recovery()
103    }
104
105    /// Construct a session from the active synchronous request scope.
106    ///
107    /// Generated zero-argument `db!()` wiring uses this entry. `None` means
108    /// that the caller did not establish a request execution boundary.
109    #[doc(hidden)]
110    #[must_use]
111    pub fn __new_from_current_request(store: &'static LocalKey<StoreRegistry>) -> Option<Self> {
112        request::current_request_scope().map(|scope| Self {
113            db: Db::new(store, scope),
114            debug: false,
115            metrics: None,
116        })
117    }
118
119    /// Enable bounded request-wide query diagnostics without resetting prior counters.
120    ///
121    /// Returns `true` only when this call enabled collection. Every session
122    /// derived from the same request root observes the same diagnostic state.
123    #[cfg(feature = "diagnostics")]
124    #[must_use]
125    pub fn enable_request_diagnostics(&self) -> bool {
126        self.db.request_execution_scope().enable_diagnostics()
127    }
128
129    /// Snapshot bounded request-wide query diagnostics when collection is enabled.
130    #[cfg(feature = "diagnostics")]
131    #[must_use]
132    pub fn request_diagnostics(&self) -> Option<crate::db::RequestDiagnostics> {
133        self.db.request_execution_scope().diagnostics_snapshot()
134    }
135
136    /// Enable debug execution behavior where supported by executors.
137    #[must_use]
138    pub const fn debug(mut self) -> Self {
139        self.debug = true;
140        self
141    }
142
143    /// Attach one metrics sink for all session-executed operations.
144    #[must_use]
145    pub const fn metrics_sink(mut self, sink: &'static dyn MetricsSink) -> Self {
146        self.metrics = Some(sink);
147        self
148    }
149
150    fn with_metrics<T>(&self, f: impl FnOnce() -> T) -> T {
151        if let Some(sink) = self.metrics {
152            with_metrics_sink(sink, f)
153        } else {
154            f()
155        }
156    }
157}