Skip to main content

icydb_core/db/session/sql/surface/
route.rs

1//! Module: db::session::sql::surface::route
2//! Responsibility: module-local ownership and contracts for db::session::sql::surface::route.
3//! Does not own: cross-module orchestration outside this module.
4//! Boundary: exposes this module API while keeping implementation details internal.
5
6use crate::db::{
7    QueryError,
8    sql::lowering::{
9        LoweredSqlCommand, LoweredSqlLaneKind, PreparedSqlStatement as CorePreparedSqlStatement,
10        lower_query_surface_command_from_prepared_statement,
11        lower_sql_command_from_prepared_statement, lowered_sql_command_lane,
12        prepare_query_surface_statement, prepare_sql_statement,
13    },
14    sql::parser::{SqlExplainTarget, SqlStatement},
15};
16
17/// Canonical SQL statement routing metadata derived from reduced SQL parser output.
18///
19/// Carries surface kind (`Query` / `Explain` / `Describe` / `ShowIndexes` /
20/// `ShowColumns` / `ShowEntities`) and canonical parsed entity identifier.
21#[derive(Clone, Debug, Eq, PartialEq)]
22pub enum SqlStatementRoute {
23    Query { entity: String },
24    Explain { entity: String },
25    Describe { entity: String },
26    ShowIndexes { entity: String },
27    ShowColumns { entity: String },
28    ShowEntities,
29}
30
31/// Unified SQL dispatch payload returned by shared SQL lane execution.
32#[derive(Debug)]
33pub enum SqlDispatchResult {
34    Projection {
35        columns: Vec<String>,
36        rows: Vec<Vec<crate::value::Value>>,
37        row_count: u32,
38    },
39    Explain(String),
40    Describe(crate::db::EntitySchemaDescription),
41    ShowIndexes(Vec<String>),
42    ShowColumns(Vec<crate::db::EntityFieldDescription>),
43    ShowEntities(Vec<String>),
44}
45
46///
47/// SqlParsedStatement
48///
49/// Opaque parsed SQL statement envelope with stable route metadata.
50/// This allows callers to parse once and reuse parsed authority across route
51/// classification and typed dispatch lowering.
52///
53
54#[derive(Clone, Debug)]
55pub struct SqlParsedStatement {
56    pub(in crate::db::session::sql) statement: SqlStatement,
57    route: SqlStatementRoute,
58}
59
60impl SqlParsedStatement {
61    // Construct one parsed SQL statement envelope inside the session SQL boundary.
62    pub(in crate::db::session::sql) const fn new(
63        statement: SqlStatement,
64        route: SqlStatementRoute,
65    ) -> Self {
66        Self { statement, route }
67    }
68
69    /// Borrow canonical route metadata for this parsed statement.
70    #[must_use]
71    pub const fn route(&self) -> &SqlStatementRoute {
72        &self.route
73    }
74
75    // Prepare this parsed statement for one concrete entity route.
76    pub(in crate::db::session::sql) fn prepare(
77        &self,
78        expected_entity: &'static str,
79    ) -> Result<CorePreparedSqlStatement, QueryError> {
80        prepare_sql_statement(self.statement.clone(), expected_entity)
81            .map_err(QueryError::from_sql_lowering_error)
82    }
83
84    /// Lower this parsed statement into one shared query-lane shape.
85    #[inline(never)]
86    pub fn lower_query_lane_for_entity(
87        &self,
88        expected_entity: &'static str,
89        primary_key_field: &str,
90    ) -> Result<LoweredSqlCommand, QueryError> {
91        let lowered = lower_sql_command_from_prepared_statement(
92            self.prepare(expected_entity)?,
93            primary_key_field,
94        )
95        .map_err(QueryError::from_sql_lowering_error)?;
96        let lane = lowered_sql_command_lane(&lowered);
97
98        match lane {
99            LoweredSqlLaneKind::Query | LoweredSqlLaneKind::Explain => Ok(lowered),
100            LoweredSqlLaneKind::Describe
101            | LoweredSqlLaneKind::ShowIndexes
102            | LoweredSqlLaneKind::ShowColumns
103            | LoweredSqlLaneKind::ShowEntities => {
104                Err(QueryError::unsupported_query_lane_dispatch())
105            }
106        }
107    }
108
109    /// Lower this parsed statement into one generated query-surface-only shape.
110    #[inline(never)]
111    pub fn lower_generated_query_surface_for_entity(
112        &self,
113        expected_entity: &'static str,
114        primary_key_field: &str,
115    ) -> Result<LoweredSqlCommand, QueryError> {
116        let prepared = prepare_query_surface_statement(self.statement.clone(), expected_entity)
117            .map_err(QueryError::from_sql_lowering_error)?;
118
119        lower_query_surface_command_from_prepared_statement(prepared, primary_key_field)
120            .map_err(QueryError::from_sql_lowering_error)
121    }
122}
123
124impl SqlStatementRoute {
125    /// Borrow the parsed SQL entity identifier for this statement.
126    ///
127    /// `SHOW ENTITIES` does not carry an entity identifier and returns an
128    /// empty string for this accessor.
129    #[must_use]
130    pub const fn entity(&self) -> &str {
131        match self {
132            Self::Query { entity }
133            | Self::Explain { entity }
134            | Self::Describe { entity }
135            | Self::ShowIndexes { entity }
136            | Self::ShowColumns { entity } => entity.as_str(),
137            Self::ShowEntities => "",
138        }
139    }
140
141    /// Return whether this route targets the EXPLAIN surface.
142    #[must_use]
143    pub const fn is_explain(&self) -> bool {
144        matches!(self, Self::Explain { .. })
145    }
146
147    /// Return whether this route targets the DESCRIBE surface.
148    #[must_use]
149    pub const fn is_describe(&self) -> bool {
150        matches!(self, Self::Describe { .. })
151    }
152
153    /// Return whether this route targets the `SHOW INDEXES` surface.
154    #[must_use]
155    pub const fn is_show_indexes(&self) -> bool {
156        matches!(self, Self::ShowIndexes { .. })
157    }
158
159    /// Return whether this route targets the `SHOW COLUMNS` surface.
160    #[must_use]
161    pub const fn is_show_columns(&self) -> bool {
162        matches!(self, Self::ShowColumns { .. })
163    }
164
165    /// Return whether this route targets the `SHOW ENTITIES` surface.
166    #[must_use]
167    pub const fn is_show_entities(&self) -> bool {
168        matches!(self, Self::ShowEntities)
169    }
170}
171
172// Resolve one parsed reduced SQL statement to canonical surface route metadata.
173pub(in crate::db::session::sql) fn sql_statement_route_from_statement(
174    statement: &SqlStatement,
175) -> SqlStatementRoute {
176    match statement {
177        SqlStatement::Select(select) => SqlStatementRoute::Query {
178            entity: select.entity.clone(),
179        },
180        SqlStatement::Delete(delete) => SqlStatementRoute::Query {
181            entity: delete.entity.clone(),
182        },
183        SqlStatement::Explain(explain) => match &explain.statement {
184            SqlExplainTarget::Select(select) => SqlStatementRoute::Explain {
185                entity: select.entity.clone(),
186            },
187            SqlExplainTarget::Delete(delete) => SqlStatementRoute::Explain {
188                entity: delete.entity.clone(),
189            },
190        },
191        SqlStatement::Describe(describe) => SqlStatementRoute::Describe {
192            entity: describe.entity.clone(),
193        },
194        SqlStatement::ShowIndexes(show_indexes) => SqlStatementRoute::ShowIndexes {
195            entity: show_indexes.entity.clone(),
196        },
197        SqlStatement::ShowColumns(show_columns) => SqlStatementRoute::ShowColumns {
198            entity: show_columns.entity.clone(),
199        },
200        SqlStatement::ShowEntities(_) => SqlStatementRoute::ShowEntities,
201    }
202}