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
205impl SqlLoweringError {
206 fn entity_mismatch(sql_entity: impl Into<String>, expected_entity: &'static str) -> Self {
208 Self::EntityMismatch {
209 sql_entity: sql_entity.into(),
210 expected_entity,
211 }
212 }
213
214 const fn unsupported_select_projection() -> Self {
216 Self::UnsupportedSelectProjection
217 }
218
219 const fn unsupported_select_distinct() -> Self {
221 Self::UnsupportedSelectDistinct
222 }
223
224 const fn unsupported_select_group_by() -> Self {
226 Self::UnsupportedSelectGroupBy
227 }
228
229 const fn unsupported_select_having() -> Self {
231 Self::UnsupportedSelectHaving
232 }
233}
234
235impl From<QueryError> for SqlLoweringError {
236 fn from(value: QueryError) -> Self {
237 Self::Query(Box::new(value))
238 }
239}
240
241#[derive(Clone, Debug)]
251pub(crate) struct PreparedSqlStatement {
252 pub(in crate::db::sql::lowering) statement: SqlStatement,
253}
254
255#[derive(Clone, Copy, Debug, Eq, PartialEq)]
256pub(crate) enum LoweredSqlLaneKind {
257 Query,
258 Explain,
259 Describe,
260 ShowIndexes,
261 ShowColumns,
262 ShowEntities,
263}
264
265#[cfg(test)]
267pub(crate) fn compile_sql_command<E: EntityKind>(
268 sql: &str,
269 consistency: MissingRowPolicy,
270) -> Result<SqlCommand<E>, SqlLoweringError> {
271 let statement = crate::db::sql::parser::parse_sql(sql)?;
272
273 compile_sql_command_from_statement::<E>(statement, consistency)
274}
275
276#[cfg(test)]
278pub(crate) fn compile_sql_command_from_statement<E: EntityKind>(
279 statement: SqlStatement,
280 consistency: MissingRowPolicy,
281) -> Result<SqlCommand<E>, SqlLoweringError> {
282 let prepared = prepare_sql_statement(statement, E::MODEL.name())?;
283
284 compile_sql_command_from_prepared_statement::<E>(prepared, consistency)
285}
286
287#[cfg(test)]
289pub(crate) fn compile_sql_command_from_prepared_statement<E: EntityKind>(
290 prepared: PreparedSqlStatement,
291 consistency: MissingRowPolicy,
292) -> Result<SqlCommand<E>, SqlLoweringError> {
293 let lowered = lower_sql_command_from_prepared_statement(prepared, E::MODEL.primary_key.name)?;
294
295 bind_lowered_sql_command::<E>(lowered, consistency)
296}
297
298pub(crate) const fn lowered_sql_command_lane(command: &LoweredSqlCommand) -> LoweredSqlLaneKind {
299 match command.0 {
300 LoweredSqlCommandInner::Query(_) => LoweredSqlLaneKind::Query,
301 LoweredSqlCommandInner::Explain { .. }
302 | LoweredSqlCommandInner::ExplainGlobalAggregate { .. } => LoweredSqlLaneKind::Explain,
303 LoweredSqlCommandInner::DescribeEntity => LoweredSqlLaneKind::Describe,
304 LoweredSqlCommandInner::ShowIndexesEntity => LoweredSqlLaneKind::ShowIndexes,
305 LoweredSqlCommandInner::ShowColumnsEntity => LoweredSqlLaneKind::ShowColumns,
306 LoweredSqlCommandInner::ShowEntities => LoweredSqlLaneKind::ShowEntities,
307 }
308}
309
310#[cfg(test)]
312pub(crate) fn bind_lowered_sql_command<E: EntityKind>(
313 lowered: LoweredSqlCommand,
314 consistency: MissingRowPolicy,
315) -> Result<SqlCommand<E>, SqlLoweringError> {
316 match lowered.0 {
317 LoweredSqlCommandInner::Query(query) => Ok(SqlCommand::Query(bind_lowered_sql_query::<E>(
318 query,
319 consistency,
320 )?)),
321 LoweredSqlCommandInner::Explain { mode, query } => Ok(SqlCommand::Explain {
322 mode,
323 query: bind_lowered_sql_query::<E>(query, consistency)?,
324 }),
325 LoweredSqlCommandInner::ExplainGlobalAggregate { mode, command } => {
326 Ok(SqlCommand::ExplainGlobalAggregate {
327 mode,
328 command: aggregate::bind_lowered_sql_global_aggregate_command::<E>(
329 command,
330 consistency,
331 )?,
332 })
333 }
334 LoweredSqlCommandInner::DescribeEntity => Ok(SqlCommand::DescribeEntity),
335 LoweredSqlCommandInner::ShowIndexesEntity => Ok(SqlCommand::ShowIndexesEntity),
336 LoweredSqlCommandInner::ShowColumnsEntity => Ok(SqlCommand::ShowColumnsEntity),
337 LoweredSqlCommandInner::ShowEntities => Ok(SqlCommand::ShowEntities),
338 }
339}