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