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