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 #[cfg_attr(not(any(test, feature = "perf-attribution")), allow(dead_code))]
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
160#[derive(Debug, ThisError)]
166pub(crate) enum SqlLoweringError {
167 #[error("{0}")]
168 Parse(#[from] crate::db::sql::parser::SqlParseError),
169
170 #[error("{0}")]
171 Query(Box<QueryError>),
172
173 #[error("SQL entity '{sql_entity}' does not match requested entity type '{expected_entity}'")]
174 EntityMismatch {
175 sql_entity: String,
176 expected_entity: &'static str,
177 },
178
179 #[error(
180 "unsupported SQL SELECT projection; supported forms are SELECT *, field lists, global aggregate terminal lists, or grouped aggregate shapes"
181 )]
182 UnsupportedSelectProjection,
183
184 #[error("unsupported SQL SELECT DISTINCT")]
185 UnsupportedSelectDistinct,
186
187 #[error("unsupported SQL GROUP BY projection shape")]
188 UnsupportedSelectGroupBy,
189
190 #[error("unsupported SQL HAVING shape")]
191 UnsupportedSelectHaving,
192
193 #[error("unknown field '{field}'")]
194 UnknownField { field: String },
195
196 #[error("ORDER BY alias '{alias}' does not resolve to a supported order target")]
197 UnsupportedOrderByAlias { alias: String },
198
199 #[error("query-lane lowering reached a non query-compatible statement")]
200 UnexpectedQueryLaneStatement,
201}
202
203impl SqlLoweringError {
204 fn entity_mismatch(sql_entity: impl Into<String>, expected_entity: &'static str) -> Self {
206 Self::EntityMismatch {
207 sql_entity: sql_entity.into(),
208 expected_entity,
209 }
210 }
211
212 const fn unsupported_select_projection() -> Self {
214 Self::UnsupportedSelectProjection
215 }
216
217 pub(crate) const fn unexpected_query_lane_statement() -> Self {
219 Self::UnexpectedQueryLaneStatement
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 unknown_field(field: impl Into<String>) -> Self {
239 Self::UnknownField {
240 field: field.into(),
241 }
242 }
243
244 fn unsupported_order_by_alias(alias: impl Into<String>) -> Self {
246 Self::UnsupportedOrderByAlias {
247 alias: alias.into(),
248 }
249 }
250}
251
252impl From<QueryError> for SqlLoweringError {
253 fn from(value: QueryError) -> Self {
254 Self::Query(Box::new(value))
255 }
256}
257
258#[derive(Clone, Debug)]
268pub(crate) struct PreparedSqlStatement {
269 pub(in crate::db::sql::lowering) statement: SqlStatement,
270}
271
272impl PreparedSqlStatement {
273 #[must_use]
275 pub(in crate::db) fn into_statement(self) -> SqlStatement {
276 self.statement
277 }
278}
279
280#[derive(Clone, Copy, Debug, Eq, PartialEq)]
281pub(crate) enum LoweredSqlLaneKind {
282 Query,
283 Explain,
284 Describe,
285 ShowIndexes,
286 ShowColumns,
287 ShowEntities,
288}
289
290#[cfg(test)]
292pub(crate) fn compile_sql_command<E: EntityKind>(
293 sql: &str,
294 consistency: MissingRowPolicy,
295) -> Result<SqlCommand<E>, SqlLoweringError> {
296 let statement = crate::db::sql::parser::parse_sql(sql)?;
297
298 compile_sql_command_from_statement::<E>(statement, consistency)
299}
300
301#[cfg(test)]
303pub(crate) fn compile_sql_command_from_statement<E: EntityKind>(
304 statement: SqlStatement,
305 consistency: MissingRowPolicy,
306) -> Result<SqlCommand<E>, SqlLoweringError> {
307 let prepared = prepare_sql_statement(statement, E::MODEL.name())?;
308
309 compile_sql_command_from_prepared_statement::<E>(prepared, consistency)
310}
311
312#[cfg(test)]
314pub(crate) fn compile_sql_command_from_prepared_statement<E: EntityKind>(
315 prepared: PreparedSqlStatement,
316 consistency: MissingRowPolicy,
317) -> Result<SqlCommand<E>, SqlLoweringError> {
318 let lowered = lower_sql_command_from_prepared_statement(prepared, E::MODEL)?;
319
320 bind_lowered_sql_command::<E>(lowered, consistency)
321}
322
323pub(crate) const fn lowered_sql_command_lane(command: &LoweredSqlCommand) -> LoweredSqlLaneKind {
324 match command.0 {
325 LoweredSqlCommandInner::Query(_) => LoweredSqlLaneKind::Query,
326 LoweredSqlCommandInner::Explain { .. }
327 | LoweredSqlCommandInner::ExplainGlobalAggregate { .. } => LoweredSqlLaneKind::Explain,
328 LoweredSqlCommandInner::DescribeEntity => LoweredSqlLaneKind::Describe,
329 LoweredSqlCommandInner::ShowIndexesEntity => LoweredSqlLaneKind::ShowIndexes,
330 LoweredSqlCommandInner::ShowColumnsEntity => LoweredSqlLaneKind::ShowColumns,
331 LoweredSqlCommandInner::ShowEntities => LoweredSqlLaneKind::ShowEntities,
332 }
333}
334
335#[cfg(test)]
337pub(crate) fn bind_lowered_sql_command<E: EntityKind>(
338 lowered: LoweredSqlCommand,
339 consistency: MissingRowPolicy,
340) -> Result<SqlCommand<E>, SqlLoweringError> {
341 match lowered.0 {
342 LoweredSqlCommandInner::Query(query) => Ok(SqlCommand::Query(bind_lowered_sql_query::<E>(
343 query,
344 consistency,
345 )?)),
346 LoweredSqlCommandInner::Explain { mode, query } => Ok(SqlCommand::Explain {
347 mode,
348 query: bind_lowered_sql_query::<E>(query, consistency)?,
349 }),
350 LoweredSqlCommandInner::ExplainGlobalAggregate { mode, command } => {
351 Ok(SqlCommand::ExplainGlobalAggregate {
352 mode,
353 command: aggregate::bind_lowered_sql_global_aggregate_command::<E>(
354 command,
355 consistency,
356 )?,
357 })
358 }
359 LoweredSqlCommandInner::DescribeEntity => Ok(SqlCommand::DescribeEntity),
360 LoweredSqlCommandInner::ShowIndexesEntity => Ok(SqlCommand::ShowIndexesEntity),
361 LoweredSqlCommandInner::ShowColumnsEntity => Ok(SqlCommand::ShowColumnsEntity),
362 LoweredSqlCommandInner::ShowEntities => Ok(SqlCommand::ShowEntities),
363 }
364}