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_with_schema,
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;
53#[cfg(test)]
54pub(crate) use prepare::lower_sql_command_from_prepared_statement;
55pub(crate) use prepare::{
56 extract_prepared_sql_insert_select_source, extract_prepared_sql_insert_statement,
57 extract_prepared_sql_update_statement, lower_prepared_sql_delete_statement,
58 lower_prepared_sql_select_statement_with_schema,
59 lower_sql_command_from_prepared_statement_with_schema, prepare_sql_statement,
60};
61pub(crate) use select::LoweredDeleteShape;
62pub(in crate::db::sql::lowering) use select::LoweredSqlFilter;
63#[cfg(test)]
64pub(in crate::db::sql::lowering) use select::apply_lowered_base_query_shape;
65pub(in crate::db::sql::lowering) use select::apply_lowered_base_query_shape_with_schema;
66#[cfg(test)]
67pub(in crate::db) use select::apply_lowered_select_shape;
68#[cfg(test)]
69pub(in crate::db) use select::bind_lowered_sql_query;
70pub(in crate::db::sql::lowering) use select::validate_base_query_sql_capabilities;
71pub(crate) use select::{LoweredBaseQueryShape, LoweredSelectShape};
72pub(in crate::db) use select::{
73 bind_lowered_sql_delete_query_structural_with_schema,
74 bind_lowered_sql_query_structural_with_schema,
75 bind_lowered_sql_select_query_structural_with_schema,
76 bind_sql_update_selector_query_structural_with_schema,
77};
78
79#[derive(Clone, Debug)]
88pub struct LoweredSqlCommand(pub(in crate::db::sql::lowering) LoweredSqlCommandInner);
89
90#[derive(Clone, Debug)]
91#[cfg_attr(not(test), allow(dead_code))]
92pub(in crate::db::sql::lowering) enum LoweredSqlCommandInner {
93 Query(LoweredSqlQuery),
94 Explain {
95 mode: SqlExplainMode,
96 verbose: bool,
97 query: LoweredSqlQuery,
98 },
99 ExplainGlobalAggregate {
100 mode: SqlExplainMode,
101 verbose: bool,
102 command: LoweredSqlGlobalAggregateCommand,
103 },
104 DescribeEntity,
105 ShowIndexesEntity,
106 ShowColumnsEntity,
107 ShowEntities,
108}
109
110#[cfg(test)]
118#[derive(Debug)]
119pub(crate) enum SqlCommand<E: EntityKind> {
120 Query(Query<E>),
121 GlobalAggregate(SqlGlobalAggregateCommand<E>),
122 Explain {
123 mode: SqlExplainMode,
124 verbose: bool,
125 query: Query<E>,
126 },
127 ExplainGlobalAggregate {
128 mode: SqlExplainMode,
129 verbose: bool,
130 command: SqlGlobalAggregateCommand<E>,
131 },
132 DescribeEntity,
133 ShowIndexesEntity,
134 ShowColumnsEntity,
135 ShowEntities,
136}
137
138impl LoweredSqlCommand {
139 #[cfg(test)]
140 #[must_use]
141 pub(in crate::db) const fn query(&self) -> Option<&LoweredSqlQuery> {
142 match &self.0 {
143 LoweredSqlCommandInner::Query(query) => Some(query),
144 LoweredSqlCommandInner::Explain { .. }
145 | LoweredSqlCommandInner::ExplainGlobalAggregate { .. }
146 | LoweredSqlCommandInner::DescribeEntity
147 | LoweredSqlCommandInner::ShowIndexesEntity
148 | LoweredSqlCommandInner::ShowColumnsEntity
149 | LoweredSqlCommandInner::ShowEntities => None,
150 }
151 }
152
153 #[cfg(test)]
154 #[must_use]
155 pub(in crate::db) fn into_query(self) -> Option<LoweredSqlQuery> {
156 match self.0 {
157 LoweredSqlCommandInner::Query(query) => Some(query),
158 LoweredSqlCommandInner::Explain { .. }
159 | LoweredSqlCommandInner::ExplainGlobalAggregate { .. }
160 | LoweredSqlCommandInner::DescribeEntity
161 | LoweredSqlCommandInner::ShowIndexesEntity
162 | LoweredSqlCommandInner::ShowColumnsEntity
163 | LoweredSqlCommandInner::ShowEntities => None,
164 }
165 }
166
167 #[must_use]
168 pub(in crate::db) const fn explain_query(
169 &self,
170 ) -> Option<(SqlExplainMode, bool, &LoweredSqlQuery)> {
171 match &self.0 {
172 LoweredSqlCommandInner::Explain {
173 mode,
174 verbose,
175 query,
176 } => Some((*mode, *verbose, query)),
177 LoweredSqlCommandInner::Query(_)
178 | LoweredSqlCommandInner::ExplainGlobalAggregate { .. }
179 | LoweredSqlCommandInner::DescribeEntity
180 | LoweredSqlCommandInner::ShowIndexesEntity
181 | LoweredSqlCommandInner::ShowColumnsEntity
182 | LoweredSqlCommandInner::ShowEntities => None,
183 }
184 }
185}
186
187#[derive(Clone, Debug)]
194pub(crate) enum LoweredSqlQuery {
195 Select(LoweredSelectShape),
196 Delete(LoweredBaseQueryShape),
197}
198
199#[derive(Debug, ThisError)]
205pub(crate) enum SqlLoweringError {
206 #[error("{0}")]
207 Parse(#[from] crate::db::sql::parser::SqlParseError),
208
209 #[error("{0}")]
210 Query(Box<QueryError>),
211
212 #[error("SQL entity '{sql_entity}' does not match requested entity type '{expected_entity}'")]
213 EntityMismatch {
214 sql_entity: String,
215 expected_entity: String,
216 },
217
218 #[error(
219 "unsupported SQL SELECT projection; supported forms are SELECT *, field lists, global aggregate terminal lists, or grouped aggregate shapes"
220 )]
221 UnsupportedSelectProjection,
222
223 #[error("unsupported SQL SELECT DISTINCT")]
224 UnsupportedSelectDistinct,
225
226 #[error("SELECT DISTINCT ORDER BY terms must be derivable from the projected distinct tuple")]
227 DistinctOrderByRequiresProjectedTuple,
228
229 #[error(
230 "unsupported global aggregate SQL projection; supported forms are aggregate projections such as COUNT(*), SUM(field), AVG(expr), or scalar wrappers over aggregate results"
231 )]
232 UnsupportedGlobalAggregateProjection,
233
234 #[error("global aggregate SQL does not support GROUP BY")]
235 GlobalAggregateDoesNotSupportGroupBy,
236
237 #[error("unsupported SQL GROUP BY projection shape")]
238 UnsupportedSelectGroupBy,
239
240 #[error("grouped SELECT requires an explicit projection list")]
241 GroupedProjectionRequiresExplicitList,
242
243 #[error("grouped SELECT projection must include at least one aggregate expression")]
244 GroupedProjectionRequiresAggregate,
245
246 #[error(
247 "grouped projection expression at index={index} references fields outside GROUP BY keys"
248 )]
249 GroupedProjectionReferencesNonGroupField { index: usize },
250
251 #[error(
252 "grouped projection expression at index={index} appears after aggregate expressions started"
253 )]
254 GroupedProjectionScalarAfterAggregate { index: usize },
255
256 #[error("HAVING requires GROUP BY")]
257 HavingRequiresGroupBy,
258
259 #[error("unsupported SQL HAVING shape")]
260 UnsupportedSelectHaving,
261
262 #[error("aggregate input expressions are not executable in this release")]
263 UnsupportedAggregateInputExpressions,
264
265 #[error("unsupported SQL WHERE expression shape")]
266 UnsupportedWhereExpression,
267
268 #[error("unknown field '{field}'")]
269 UnknownField { field: String },
270
271 #[error("{message}")]
272 UnsupportedParameterPlacement { message: String },
273
274 #[error("query-lane lowering reached a non query-compatible statement")]
275 UnexpectedQueryLaneStatement,
276}
277
278impl SqlLoweringError {
279 fn entity_mismatch(sql_entity: impl Into<String>, expected_entity: impl Into<String>) -> Self {
281 Self::EntityMismatch {
282 sql_entity: sql_entity.into(),
283 expected_entity: expected_entity.into(),
284 }
285 }
286
287 const fn unsupported_select_projection() -> Self {
289 Self::UnsupportedSelectProjection
290 }
291
292 pub(crate) const fn unexpected_query_lane_statement() -> Self {
294 Self::UnexpectedQueryLaneStatement
295 }
296
297 const fn unsupported_select_distinct() -> Self {
299 Self::UnsupportedSelectDistinct
300 }
301
302 const fn distinct_order_by_requires_projected_tuple() -> Self {
304 Self::DistinctOrderByRequiresProjectedTuple
305 }
306
307 const fn unsupported_global_aggregate_projection() -> Self {
309 Self::UnsupportedGlobalAggregateProjection
310 }
311
312 pub(crate) const fn unsupported_where_expression() -> Self {
314 Self::UnsupportedWhereExpression
315 }
316
317 const fn global_aggregate_does_not_support_group_by() -> Self {
319 Self::GlobalAggregateDoesNotSupportGroupBy
320 }
321
322 const fn unsupported_select_group_by() -> Self {
324 Self::UnsupportedSelectGroupBy
325 }
326
327 const fn grouped_projection_requires_explicit_list() -> Self {
329 Self::GroupedProjectionRequiresExplicitList
330 }
331
332 const fn grouped_projection_requires_aggregate() -> Self {
334 Self::GroupedProjectionRequiresAggregate
335 }
336
337 const fn grouped_projection_references_non_group_field(index: usize) -> Self {
339 Self::GroupedProjectionReferencesNonGroupField { index }
340 }
341
342 const fn grouped_projection_scalar_after_aggregate(index: usize) -> Self {
344 Self::GroupedProjectionScalarAfterAggregate { index }
345 }
346
347 const fn having_requires_group_by() -> Self {
349 Self::HavingRequiresGroupBy
350 }
351
352 const fn unsupported_select_having() -> Self {
354 Self::UnsupportedSelectHaving
355 }
356
357 const fn unsupported_aggregate_input_expressions() -> Self {
359 Self::UnsupportedAggregateInputExpressions
360 }
361
362 pub(crate) fn unknown_field(field: impl Into<String>) -> Self {
364 Self::UnknownField {
365 field: field.into(),
366 }
367 }
368
369 pub(crate) fn unsupported_parameter_placement(
371 index: Option<usize>,
372 message: impl Into<String>,
373 ) -> Self {
374 let message = match index {
375 Some(index) => format!("parameter slot ${index}: {}", message.into()),
376 None => message.into(),
377 };
378
379 Self::UnsupportedParameterPlacement { message }
380 }
381}
382
383impl From<QueryError> for SqlLoweringError {
384 fn from(value: QueryError) -> Self {
385 Self::Query(Box::new(value))
386 }
387}
388
389#[derive(Clone, Debug)]
399pub(crate) struct PreparedSqlStatement {
400 pub(in crate::db::sql::lowering) statement: SqlStatement,
401}
402
403impl PreparedSqlStatement {
404 #[must_use]
406 pub(in crate::db) const fn statement(&self) -> &SqlStatement {
407 &self.statement
408 }
409
410 #[must_use]
412 pub(in crate::db) fn into_statement(self) -> SqlStatement {
413 self.statement
414 }
415}
416
417#[derive(Clone, Copy, Debug, Eq, PartialEq)]
418pub(crate) enum LoweredSqlLaneKind {
419 Query,
420 Explain,
421 Describe,
422 ShowIndexes,
423 ShowColumns,
424 ShowEntities,
425}
426
427#[cfg(test)]
429pub(crate) fn compile_sql_command<E: EntityKind>(
430 sql: &str,
431 consistency: MissingRowPolicy,
432) -> Result<SqlCommand<E>, SqlLoweringError> {
433 let statement = crate::db::sql::parser::parse_sql(sql)?;
434 let prepared = prepare_sql_statement(&statement, E::MODEL.name())?;
435
436 if prepared.statement().is_global_aggregate_lane_shape() {
437 return Ok(SqlCommand::GlobalAggregate(
438 aggregate::compile_sql_global_aggregate_command_from_prepared::<E>(
439 prepared,
440 consistency,
441 )?,
442 ));
443 }
444
445 let lowered = lower_sql_command_from_prepared_statement(prepared, E::MODEL)?;
446
447 match lowered.0 {
450 LoweredSqlCommandInner::Query(query) => Ok(SqlCommand::Query(bind_lowered_sql_query::<E>(
451 query,
452 consistency,
453 )?)),
454 LoweredSqlCommandInner::ExplainGlobalAggregate {
455 mode,
456 verbose,
457 command,
458 } => Ok(SqlCommand::ExplainGlobalAggregate {
459 mode,
460 verbose,
461 command: aggregate::bind_lowered_sql_global_aggregate_command::<E>(
462 command,
463 consistency,
464 )?,
465 }),
466 LoweredSqlCommandInner::Explain {
467 mode,
468 verbose,
469 query,
470 } => Ok(SqlCommand::Explain {
471 mode,
472 verbose,
473 query: bind_lowered_sql_query::<E>(query, consistency)?,
474 }),
475 LoweredSqlCommandInner::DescribeEntity => Ok(SqlCommand::DescribeEntity),
476 LoweredSqlCommandInner::ShowIndexesEntity => Ok(SqlCommand::ShowIndexesEntity),
477 LoweredSqlCommandInner::ShowColumnsEntity => Ok(SqlCommand::ShowColumnsEntity),
478 LoweredSqlCommandInner::ShowEntities => Ok(SqlCommand::ShowEntities),
479 }
480}
481
482pub(crate) const fn lowered_sql_command_lane(command: &LoweredSqlCommand) -> LoweredSqlLaneKind {
483 match command.0 {
484 LoweredSqlCommandInner::Query(_) => LoweredSqlLaneKind::Query,
485 LoweredSqlCommandInner::Explain { .. }
486 | LoweredSqlCommandInner::ExplainGlobalAggregate { .. } => LoweredSqlLaneKind::Explain,
487 LoweredSqlCommandInner::DescribeEntity => LoweredSqlLaneKind::Describe,
488 LoweredSqlCommandInner::ShowIndexesEntity => LoweredSqlLaneKind::ShowIndexes,
489 LoweredSqlCommandInner::ShowColumnsEntity => LoweredSqlLaneKind::ShowColumns,
490 LoweredSqlCommandInner::ShowEntities => LoweredSqlLaneKind::ShowEntities,
491 }
492}