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