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