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