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