1mod aggregate;
7mod analysis;
8mod expr;
9mod normalize;
10#[cfg(test)]
11mod order_expr;
12mod predicate;
13mod prepare;
14mod select;
15
16#[cfg(test)]
21mod tests;
22
23use crate::db::{
24 query::intent::QueryError,
25 sql::parser::{SqlExplainMode, SqlStatement},
26};
27#[cfg(test)]
28use crate::{
29 db::{predicate::MissingRowPolicy, query::intent::Query},
30 traits::EntityKind,
31};
32use thiserror::Error as ThisError;
33
34pub(in crate::db::sql::lowering) use aggregate::LoweredSqlGlobalAggregateCommand;
35pub(in crate::db) use aggregate::compile_sql_global_aggregate_command_core_from_prepared_with_schema;
36#[cfg(test)]
37pub(crate) use aggregate::{
38 PreparedSqlScalarAggregateDescriptorShape, SqlGlobalAggregateCommand,
39 compile_sql_global_aggregate_command,
40};
41pub(crate) use aggregate::{
42 PreparedSqlScalarAggregatePlanFragment, PreparedSqlScalarAggregateStrategy,
43};
44pub(crate) use aggregate::{
45 SqlGlobalAggregateCommandCore, bind_lowered_sql_explain_global_aggregate_structural,
46};
47pub(in crate::db::sql::lowering) use analysis::{LoweredExprAnalysis, analyze_lowered_expr};
48#[cfg(test)]
49pub(in crate::db) use order_expr::{
50 lower_grouped_post_aggregate_order_expr_text, lower_supported_order_expr_text,
51};
52pub(in crate::db) use prepare::bind_prepared_sql_select_statement_structural_with_schema;
53pub(crate) use prepare::{
54 extract_prepared_sql_insert_select_source, extract_prepared_sql_insert_statement,
55 extract_prepared_sql_update_statement, lower_prepared_sql_delete_statement,
56 lower_prepared_sql_select_statement, lower_sql_command_from_prepared_statement,
57 prepare_sql_statement,
58};
59pub(crate) use select::LoweredDeleteShape;
60pub(in crate::db::sql::lowering) use select::LoweredSqlFilter;
61#[cfg(test)]
62pub(in crate::db) use select::apply_lowered_select_shape;
63#[cfg(test)]
64pub(in crate::db) use select::bind_lowered_sql_query;
65pub(crate) use select::{LoweredBaseQueryShape, LoweredSelectShape};
66pub(in crate::db::sql::lowering) use select::{
67 apply_lowered_base_query_shape, validate_base_query_sql_capabilities,
68};
69pub(in crate::db) use select::{
70 bind_lowered_sql_delete_query_structural_with_schema, bind_lowered_sql_query_structural,
71 bind_lowered_sql_select_query_structural_with_schema,
72 bind_sql_update_selector_query_structural_with_schema,
73};
74
75#[derive(Clone, Debug)]
84pub struct LoweredSqlCommand(pub(in crate::db::sql::lowering) LoweredSqlCommandInner);
85
86#[derive(Clone, Debug)]
87pub(in crate::db::sql::lowering) enum LoweredSqlCommandInner {
88 Query(LoweredSqlQuery),
89 Explain {
90 mode: SqlExplainMode,
91 verbose: bool,
92 query: LoweredSqlQuery,
93 },
94 ExplainGlobalAggregate {
95 mode: SqlExplainMode,
96 verbose: bool,
97 command: LoweredSqlGlobalAggregateCommand,
98 },
99 DescribeEntity,
100 ShowIndexesEntity,
101 ShowColumnsEntity,
102 ShowEntities,
103}
104
105#[cfg(test)]
113#[derive(Debug)]
114pub(crate) enum SqlCommand<E: EntityKind> {
115 Query(Query<E>),
116 GlobalAggregate(SqlGlobalAggregateCommand<E>),
117 Explain {
118 mode: SqlExplainMode,
119 verbose: bool,
120 query: Query<E>,
121 },
122 ExplainGlobalAggregate {
123 mode: SqlExplainMode,
124 verbose: bool,
125 command: SqlGlobalAggregateCommand<E>,
126 },
127 DescribeEntity,
128 ShowIndexesEntity,
129 ShowColumnsEntity,
130 ShowEntities,
131}
132
133impl LoweredSqlCommand {
134 #[cfg(test)]
135 #[must_use]
136 pub(in crate::db) const fn query(&self) -> Option<&LoweredSqlQuery> {
137 match &self.0 {
138 LoweredSqlCommandInner::Query(query) => Some(query),
139 LoweredSqlCommandInner::Explain { .. }
140 | LoweredSqlCommandInner::ExplainGlobalAggregate { .. }
141 | LoweredSqlCommandInner::DescribeEntity
142 | LoweredSqlCommandInner::ShowIndexesEntity
143 | LoweredSqlCommandInner::ShowColumnsEntity
144 | LoweredSqlCommandInner::ShowEntities => None,
145 }
146 }
147
148 #[must_use]
149 pub(in crate::db) fn into_query(self) -> Option<LoweredSqlQuery> {
150 match self.0 {
151 LoweredSqlCommandInner::Query(query) => Some(query),
152 LoweredSqlCommandInner::Explain { .. }
153 | LoweredSqlCommandInner::ExplainGlobalAggregate { .. }
154 | LoweredSqlCommandInner::DescribeEntity
155 | LoweredSqlCommandInner::ShowIndexesEntity
156 | LoweredSqlCommandInner::ShowColumnsEntity
157 | LoweredSqlCommandInner::ShowEntities => None,
158 }
159 }
160
161 #[must_use]
163 pub(in crate::db) fn into_select_query(self) -> Option<LoweredSelectShape> {
164 let LoweredSqlQuery::Select(select) = self.into_query()? else {
165 return None;
166 };
167
168 Some(select)
169 }
170
171 #[must_use]
172 pub(in crate::db) const fn explain_query(
173 &self,
174 ) -> Option<(SqlExplainMode, bool, &LoweredSqlQuery)> {
175 match &self.0 {
176 LoweredSqlCommandInner::Explain {
177 mode,
178 verbose,
179 query,
180 } => Some((*mode, *verbose, query)),
181 LoweredSqlCommandInner::Query(_)
182 | LoweredSqlCommandInner::ExplainGlobalAggregate { .. }
183 | LoweredSqlCommandInner::DescribeEntity
184 | LoweredSqlCommandInner::ShowIndexesEntity
185 | LoweredSqlCommandInner::ShowColumnsEntity
186 | LoweredSqlCommandInner::ShowEntities => None,
187 }
188 }
189}
190
191#[derive(Clone, Debug)]
198pub(crate) enum LoweredSqlQuery {
199 Select(LoweredSelectShape),
200 Delete(LoweredBaseQueryShape),
201}
202
203#[derive(Debug, ThisError)]
209pub(crate) enum SqlLoweringError {
210 #[error("{0}")]
211 Parse(#[from] crate::db::sql::parser::SqlParseError),
212
213 #[error("{0}")]
214 Query(Box<QueryError>),
215
216 #[error("SQL entity '{sql_entity}' does not match requested entity type '{expected_entity}'")]
217 EntityMismatch {
218 sql_entity: String,
219 expected_entity: &'static str,
220 },
221
222 #[error(
223 "unsupported SQL SELECT projection; supported forms are SELECT *, field lists, global aggregate terminal lists, or grouped aggregate shapes"
224 )]
225 UnsupportedSelectProjection,
226
227 #[error("unsupported SQL SELECT DISTINCT")]
228 UnsupportedSelectDistinct,
229
230 #[error("SELECT DISTINCT ORDER BY terms must be derivable from the projected distinct tuple")]
231 DistinctOrderByRequiresProjectedTuple,
232
233 #[error(
234 "unsupported global aggregate SQL projection; supported forms are aggregate projections such as COUNT(*), SUM(field), AVG(expr), or scalar wrappers over aggregate results"
235 )]
236 UnsupportedGlobalAggregateProjection,
237
238 #[error("global aggregate SQL does not support GROUP BY")]
239 GlobalAggregateDoesNotSupportGroupBy,
240
241 #[error("unsupported SQL GROUP BY projection shape")]
242 UnsupportedSelectGroupBy,
243
244 #[error("grouped SELECT requires an explicit projection list")]
245 GroupedProjectionRequiresExplicitList,
246
247 #[error("grouped SELECT projection must include at least one aggregate expression")]
248 GroupedProjectionRequiresAggregate,
249
250 #[error(
251 "grouped projection expression at index={index} references fields outside GROUP BY keys"
252 )]
253 GroupedProjectionReferencesNonGroupField { index: usize },
254
255 #[error(
256 "grouped projection expression at index={index} appears after aggregate expressions started"
257 )]
258 GroupedProjectionScalarAfterAggregate { index: usize },
259
260 #[error("HAVING requires GROUP BY")]
261 HavingRequiresGroupBy,
262
263 #[error("unsupported SQL HAVING shape")]
264 UnsupportedSelectHaving,
265
266 #[error("aggregate input expressions are not executable in this release")]
267 UnsupportedAggregateInputExpressions,
268
269 #[error("unsupported SQL WHERE expression shape")]
270 UnsupportedWhereExpression,
271
272 #[error("unknown field '{field}'")]
273 UnknownField { field: String },
274
275 #[error("{message}")]
276 UnsupportedParameterPlacement { message: String },
277
278 #[error("query-lane lowering reached a non query-compatible statement")]
279 UnexpectedQueryLaneStatement,
280}
281
282impl SqlLoweringError {
283 fn entity_mismatch(sql_entity: impl Into<String>, expected_entity: &'static str) -> Self {
285 Self::EntityMismatch {
286 sql_entity: sql_entity.into(),
287 expected_entity,
288 }
289 }
290
291 const fn unsupported_select_projection() -> Self {
293 Self::UnsupportedSelectProjection
294 }
295
296 pub(crate) const fn unexpected_query_lane_statement() -> Self {
298 Self::UnexpectedQueryLaneStatement
299 }
300
301 const fn unsupported_select_distinct() -> Self {
303 Self::UnsupportedSelectDistinct
304 }
305
306 const fn distinct_order_by_requires_projected_tuple() -> Self {
308 Self::DistinctOrderByRequiresProjectedTuple
309 }
310
311 const fn unsupported_global_aggregate_projection() -> Self {
313 Self::UnsupportedGlobalAggregateProjection
314 }
315
316 pub(crate) const fn unsupported_where_expression() -> Self {
318 Self::UnsupportedWhereExpression
319 }
320
321 const fn global_aggregate_does_not_support_group_by() -> Self {
323 Self::GlobalAggregateDoesNotSupportGroupBy
324 }
325
326 const fn unsupported_select_group_by() -> Self {
328 Self::UnsupportedSelectGroupBy
329 }
330
331 const fn grouped_projection_requires_explicit_list() -> Self {
333 Self::GroupedProjectionRequiresExplicitList
334 }
335
336 const fn grouped_projection_requires_aggregate() -> Self {
338 Self::GroupedProjectionRequiresAggregate
339 }
340
341 const fn grouped_projection_references_non_group_field(index: usize) -> Self {
343 Self::GroupedProjectionReferencesNonGroupField { index }
344 }
345
346 const fn grouped_projection_scalar_after_aggregate(index: usize) -> Self {
348 Self::GroupedProjectionScalarAfterAggregate { index }
349 }
350
351 const fn having_requires_group_by() -> Self {
353 Self::HavingRequiresGroupBy
354 }
355
356 const fn unsupported_select_having() -> Self {
358 Self::UnsupportedSelectHaving
359 }
360
361 const fn unsupported_aggregate_input_expressions() -> Self {
363 Self::UnsupportedAggregateInputExpressions
364 }
365
366 pub(crate) fn unknown_field(field: impl Into<String>) -> Self {
368 Self::UnknownField {
369 field: field.into(),
370 }
371 }
372
373 pub(crate) fn unsupported_parameter_placement(
375 index: Option<usize>,
376 message: impl Into<String>,
377 ) -> Self {
378 let message = match index {
379 Some(index) => format!("parameter slot ${index}: {}", message.into()),
380 None => message.into(),
381 };
382
383 Self::UnsupportedParameterPlacement { message }
384 }
385}
386
387impl From<QueryError> for SqlLoweringError {
388 fn from(value: QueryError) -> Self {
389 Self::Query(Box::new(value))
390 }
391}
392
393#[derive(Clone, Debug)]
403pub(crate) struct PreparedSqlStatement {
404 pub(in crate::db::sql::lowering) statement: SqlStatement,
405}
406
407impl PreparedSqlStatement {
408 #[must_use]
410 pub(in crate::db) const fn statement(&self) -> &SqlStatement {
411 &self.statement
412 }
413
414 #[must_use]
416 pub(in crate::db) fn into_statement(self) -> SqlStatement {
417 self.statement
418 }
419}
420
421#[derive(Clone, Copy, Debug, Eq, PartialEq)]
422pub(crate) enum LoweredSqlLaneKind {
423 Query,
424 Explain,
425 Describe,
426 ShowIndexes,
427 ShowColumns,
428 ShowEntities,
429}
430
431#[cfg(test)]
433pub(crate) fn compile_sql_command<E: EntityKind>(
434 sql: &str,
435 consistency: MissingRowPolicy,
436) -> Result<SqlCommand<E>, SqlLoweringError> {
437 let statement = crate::db::sql::parser::parse_sql(sql)?;
438 let prepared = prepare_sql_statement(&statement, E::MODEL.name())?;
439
440 if prepared.statement().is_global_aggregate_lane_shape() {
441 return Ok(SqlCommand::GlobalAggregate(
442 aggregate::compile_sql_global_aggregate_command_from_prepared::<E>(
443 prepared,
444 consistency,
445 )?,
446 ));
447 }
448
449 let lowered = lower_sql_command_from_prepared_statement(prepared, E::MODEL)?;
450
451 match lowered.0 {
454 LoweredSqlCommandInner::Query(query) => Ok(SqlCommand::Query(bind_lowered_sql_query::<E>(
455 query,
456 consistency,
457 )?)),
458 LoweredSqlCommandInner::ExplainGlobalAggregate {
459 mode,
460 verbose,
461 command,
462 } => Ok(SqlCommand::ExplainGlobalAggregate {
463 mode,
464 verbose,
465 command: aggregate::bind_lowered_sql_global_aggregate_command::<E>(
466 command,
467 consistency,
468 )?,
469 }),
470 LoweredSqlCommandInner::Explain {
471 mode,
472 verbose,
473 query,
474 } => Ok(SqlCommand::Explain {
475 mode,
476 verbose,
477 query: bind_lowered_sql_query::<E>(query, consistency)?,
478 }),
479 LoweredSqlCommandInner::DescribeEntity => Ok(SqlCommand::DescribeEntity),
480 LoweredSqlCommandInner::ShowIndexesEntity => Ok(SqlCommand::ShowIndexesEntity),
481 LoweredSqlCommandInner::ShowColumnsEntity => Ok(SqlCommand::ShowColumnsEntity),
482 LoweredSqlCommandInner::ShowEntities => Ok(SqlCommand::ShowEntities),
483 }
484}
485
486pub(crate) const fn lowered_sql_command_lane(command: &LoweredSqlCommand) -> LoweredSqlLaneKind {
487 match command.0 {
488 LoweredSqlCommandInner::Query(_) => LoweredSqlLaneKind::Query,
489 LoweredSqlCommandInner::Explain { .. }
490 | LoweredSqlCommandInner::ExplainGlobalAggregate { .. } => LoweredSqlLaneKind::Explain,
491 LoweredSqlCommandInner::DescribeEntity => LoweredSqlLaneKind::Describe,
492 LoweredSqlCommandInner::ShowIndexesEntity => LoweredSqlLaneKind::ShowIndexes,
493 LoweredSqlCommandInner::ShowColumnsEntity => LoweredSqlLaneKind::ShowColumns,
494 LoweredSqlCommandInner::ShowEntities => LoweredSqlLaneKind::ShowEntities,
495 }
496}