Skip to main content

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

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