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