icydb_core/db/sql/lowering/
mod.rs1mod aggregate;
7mod normalize;
8mod prepare;
9mod select;
10
11#[cfg(test)]
16mod tests;
17
18use crate::db::{
19 query::intent::QueryError,
20 sql::parser::{SqlExplainMode, SqlStatement},
21};
22#[cfg(test)]
23use crate::{
24 db::{predicate::MissingRowPolicy, query::intent::Query},
25 traits::EntityKind,
26};
27use thiserror::Error as ThisError;
28
29pub(in crate::db::sql::lowering) use aggregate::LoweredSqlGlobalAggregateCommand;
30pub(crate) use aggregate::compile_sql_global_aggregate_command_from_prepared;
31pub(in crate::db) use aggregate::is_sql_global_aggregate_statement;
32#[cfg(test)]
33pub(crate) use aggregate::{
34 PreparedSqlScalarAggregateDescriptorShape, PreparedSqlScalarAggregateDomain,
35 PreparedSqlScalarAggregateEmptySetBehavior, PreparedSqlScalarAggregateOrderingRequirement,
36 PreparedSqlScalarAggregateRowSource, TypedSqlGlobalAggregateTerminal,
37 compile_sql_global_aggregate_command,
38};
39pub(crate) use aggregate::{
40 PreparedSqlScalarAggregateRuntimeDescriptor, PreparedSqlScalarAggregateStrategy,
41 SqlGlobalAggregateCommand, SqlGlobalAggregateCommandCore,
42 bind_lowered_sql_explain_global_aggregate_structural,
43};
44pub(crate) use prepare::{lower_sql_command_from_prepared_statement, prepare_sql_statement};
45pub(in crate::db::sql::lowering) use select::apply_lowered_base_query_shape;
46#[cfg(test)]
47pub(in crate::db) use select::apply_lowered_select_shape;
48pub(crate) use select::{LoweredBaseQueryShape, LoweredSelectShape};
49pub(in crate::db) use select::{
50 bind_lowered_sql_delete_query_structural, bind_lowered_sql_query,
51 bind_lowered_sql_query_structural, bind_lowered_sql_select_query_structural,
52};
53
54#[derive(Clone, Debug)]
63pub struct LoweredSqlCommand(pub(in crate::db::sql::lowering) LoweredSqlCommandInner);
64
65#[derive(Clone, Debug)]
66pub(in crate::db::sql::lowering) enum LoweredSqlCommandInner {
67 Query(LoweredSqlQuery),
68 Explain {
69 mode: SqlExplainMode,
70 query: LoweredSqlQuery,
71 },
72 ExplainGlobalAggregate {
73 mode: SqlExplainMode,
74 command: LoweredSqlGlobalAggregateCommand,
75 },
76 DescribeEntity,
77 ShowIndexesEntity,
78 ShowColumnsEntity,
79 ShowEntities,
80}
81
82#[cfg(test)]
90#[derive(Debug)]
91pub(crate) enum SqlCommand<E: EntityKind> {
92 Query(Query<E>),
93 Explain {
94 mode: SqlExplainMode,
95 query: Query<E>,
96 },
97 ExplainGlobalAggregate {
98 mode: SqlExplainMode,
99 command: SqlGlobalAggregateCommand<E>,
100 },
101 DescribeEntity,
102 ShowIndexesEntity,
103 ShowColumnsEntity,
104 ShowEntities,
105}
106
107impl LoweredSqlCommand {
108 #[must_use]
109 pub(in crate::db) const fn query(&self) -> Option<&LoweredSqlQuery> {
110 match &self.0 {
111 LoweredSqlCommandInner::Query(query) => Some(query),
112 LoweredSqlCommandInner::Explain { .. }
113 | LoweredSqlCommandInner::ExplainGlobalAggregate { .. }
114 | LoweredSqlCommandInner::DescribeEntity
115 | LoweredSqlCommandInner::ShowIndexesEntity
116 | LoweredSqlCommandInner::ShowColumnsEntity
117 | LoweredSqlCommandInner::ShowEntities => None,
118 }
119 }
120
121 #[must_use]
122 pub(in crate::db) fn into_query(self) -> Option<LoweredSqlQuery> {
123 match self.0 {
124 LoweredSqlCommandInner::Query(query) => Some(query),
125 LoweredSqlCommandInner::Explain { .. }
126 | LoweredSqlCommandInner::ExplainGlobalAggregate { .. }
127 | LoweredSqlCommandInner::DescribeEntity
128 | LoweredSqlCommandInner::ShowIndexesEntity
129 | LoweredSqlCommandInner::ShowColumnsEntity
130 | LoweredSqlCommandInner::ShowEntities => None,
131 }
132 }
133
134 #[must_use]
135 pub(in crate::db) const fn explain_query(&self) -> Option<(SqlExplainMode, &LoweredSqlQuery)> {
136 match &self.0 {
137 LoweredSqlCommandInner::Explain { mode, query } => Some((*mode, query)),
138 LoweredSqlCommandInner::Query(_)
139 | LoweredSqlCommandInner::ExplainGlobalAggregate { .. }
140 | LoweredSqlCommandInner::DescribeEntity
141 | LoweredSqlCommandInner::ShowIndexesEntity
142 | LoweredSqlCommandInner::ShowColumnsEntity
143 | LoweredSqlCommandInner::ShowEntities => None,
144 }
145 }
146}
147
148#[derive(Clone, Debug)]
155pub(crate) enum LoweredSqlQuery {
156 Select(LoweredSelectShape),
157 Delete(LoweredBaseQueryShape),
158}
159
160impl LoweredSqlQuery {
161 pub(crate) const fn has_grouping(&self) -> bool {
163 match self {
164 Self::Select(select) => select.has_grouping(),
165 Self::Delete(_) => false,
166 }
167 }
168}
169
170#[derive(Debug, ThisError)]
176pub(crate) enum SqlLoweringError {
177 #[error("{0}")]
178 Parse(#[from] crate::db::sql::parser::SqlParseError),
179
180 #[error("{0}")]
181 Query(Box<QueryError>),
182
183 #[error("SQL entity '{sql_entity}' does not match requested entity type '{expected_entity}'")]
184 EntityMismatch {
185 sql_entity: String,
186 expected_entity: &'static str,
187 },
188
189 #[error(
190 "unsupported SQL SELECT projection; supported forms are SELECT *, field lists, or grouped aggregate shapes"
191 )]
192 UnsupportedSelectProjection,
193
194 #[error("unsupported SQL SELECT DISTINCT")]
195 UnsupportedSelectDistinct,
196
197 #[error("unsupported SQL GROUP BY projection shape")]
198 UnsupportedSelectGroupBy,
199
200 #[error("unsupported SQL HAVING shape")]
201 UnsupportedSelectHaving,
202}
203
204impl SqlLoweringError {
205 fn entity_mismatch(sql_entity: impl Into<String>, expected_entity: &'static str) -> Self {
207 Self::EntityMismatch {
208 sql_entity: sql_entity.into(),
209 expected_entity,
210 }
211 }
212
213 const fn unsupported_select_projection() -> Self {
215 Self::UnsupportedSelectProjection
216 }
217
218 const fn unsupported_select_distinct() -> Self {
220 Self::UnsupportedSelectDistinct
221 }
222
223 const fn unsupported_select_group_by() -> Self {
225 Self::UnsupportedSelectGroupBy
226 }
227
228 const fn unsupported_select_having() -> Self {
230 Self::UnsupportedSelectHaving
231 }
232}
233
234impl From<QueryError> for SqlLoweringError {
235 fn from(value: QueryError) -> Self {
236 Self::Query(Box::new(value))
237 }
238}
239
240#[derive(Clone, Debug)]
250pub(crate) struct PreparedSqlStatement {
251 pub(in crate::db::sql::lowering) statement: SqlStatement,
252}
253
254#[derive(Clone, Copy, Debug, Eq, PartialEq)]
255pub(crate) enum LoweredSqlLaneKind {
256 Query,
257 Explain,
258 Describe,
259 ShowIndexes,
260 ShowColumns,
261 ShowEntities,
262}
263
264#[cfg(test)]
266pub(crate) fn compile_sql_command<E: EntityKind>(
267 sql: &str,
268 consistency: MissingRowPolicy,
269) -> Result<SqlCommand<E>, SqlLoweringError> {
270 let statement = crate::db::sql::parser::parse_sql(sql)?;
271
272 compile_sql_command_from_statement::<E>(statement, consistency)
273}
274
275#[cfg(test)]
277pub(crate) fn compile_sql_command_from_statement<E: EntityKind>(
278 statement: SqlStatement,
279 consistency: MissingRowPolicy,
280) -> Result<SqlCommand<E>, SqlLoweringError> {
281 let prepared = prepare_sql_statement(statement, E::MODEL.name())?;
282
283 compile_sql_command_from_prepared_statement::<E>(prepared, consistency)
284}
285
286#[cfg(test)]
288pub(crate) fn compile_sql_command_from_prepared_statement<E: EntityKind>(
289 prepared: PreparedSqlStatement,
290 consistency: MissingRowPolicy,
291) -> Result<SqlCommand<E>, SqlLoweringError> {
292 let lowered = lower_sql_command_from_prepared_statement(prepared, E::MODEL.primary_key.name)?;
293
294 bind_lowered_sql_command::<E>(lowered, consistency)
295}
296
297pub(crate) const fn lowered_sql_command_lane(command: &LoweredSqlCommand) -> LoweredSqlLaneKind {
298 match command.0 {
299 LoweredSqlCommandInner::Query(_) => LoweredSqlLaneKind::Query,
300 LoweredSqlCommandInner::Explain { .. }
301 | LoweredSqlCommandInner::ExplainGlobalAggregate { .. } => LoweredSqlLaneKind::Explain,
302 LoweredSqlCommandInner::DescribeEntity => LoweredSqlLaneKind::Describe,
303 LoweredSqlCommandInner::ShowIndexesEntity => LoweredSqlLaneKind::ShowIndexes,
304 LoweredSqlCommandInner::ShowColumnsEntity => LoweredSqlLaneKind::ShowColumns,
305 LoweredSqlCommandInner::ShowEntities => LoweredSqlLaneKind::ShowEntities,
306 }
307}
308
309#[cfg(test)]
311pub(crate) fn bind_lowered_sql_command<E: EntityKind>(
312 lowered: LoweredSqlCommand,
313 consistency: MissingRowPolicy,
314) -> Result<SqlCommand<E>, SqlLoweringError> {
315 match lowered.0 {
316 LoweredSqlCommandInner::Query(query) => Ok(SqlCommand::Query(bind_lowered_sql_query::<E>(
317 query,
318 consistency,
319 )?)),
320 LoweredSqlCommandInner::Explain { mode, query } => Ok(SqlCommand::Explain {
321 mode,
322 query: bind_lowered_sql_query::<E>(query, consistency)?,
323 }),
324 LoweredSqlCommandInner::ExplainGlobalAggregate { mode, command } => {
325 Ok(SqlCommand::ExplainGlobalAggregate {
326 mode,
327 command: aggregate::bind_lowered_sql_global_aggregate_command::<E>(
328 command,
329 consistency,
330 )?,
331 })
332 }
333 LoweredSqlCommandInner::DescribeEntity => Ok(SqlCommand::DescribeEntity),
334 LoweredSqlCommandInner::ShowIndexesEntity => Ok(SqlCommand::ShowIndexesEntity),
335 LoweredSqlCommandInner::ShowColumnsEntity => Ok(SqlCommand::ShowColumnsEntity),
336 LoweredSqlCommandInner::ShowEntities => Ok(SqlCommand::ShowEntities),
337 }
338}