mod aggregate;
mod expr;
mod normalize;
mod predicate;
mod prepare;
mod select;
#[cfg(test)]
mod tests;
use crate::db::{
query::intent::QueryError,
sql::parser::{SqlExplainMode, SqlStatement},
};
#[cfg(test)]
use crate::{
db::{predicate::MissingRowPolicy, query::intent::Query},
traits::EntityKind,
};
use thiserror::Error as ThisError;
pub(in crate::db::sql::lowering) use aggregate::LoweredSqlGlobalAggregateCommand;
pub(in crate::db) use aggregate::compile_sql_global_aggregate_command_core_from_prepared;
pub(in crate::db) use aggregate::is_sql_global_aggregate_statement;
#[cfg(test)]
pub(crate) use aggregate::{
PreparedSqlScalarAggregateDescriptorShape, PreparedSqlScalarAggregateDomain,
PreparedSqlScalarAggregateEmptySetBehavior, PreparedSqlScalarAggregateOrderingRequirement,
PreparedSqlScalarAggregateRowSource, SqlGlobalAggregateCommand,
compile_sql_global_aggregate_command,
};
pub(crate) use aggregate::{
PreparedSqlScalarAggregateRuntimeDescriptor, PreparedSqlScalarAggregateStrategy,
SqlGlobalAggregateCommandCore, bind_lowered_sql_explain_global_aggregate_structural,
};
pub(in crate::db) use predicate::lower_sql_where_expr;
pub(crate) use prepare::{lower_sql_command_from_prepared_statement, prepare_sql_statement};
pub(in crate::db::sql::lowering) use select::apply_lowered_base_query_shape;
#[cfg(test)]
pub(in crate::db) use select::apply_lowered_select_shape;
pub(crate) use select::{LoweredBaseQueryShape, LoweredSelectShape};
pub(in crate::db) use select::{
bind_lowered_sql_query, bind_lowered_sql_query_structural,
bind_lowered_sql_select_query_structural, canonicalize_sql_predicate_for_model,
canonicalize_strict_sql_literal_for_kind,
};
#[derive(Clone, Debug)]
pub struct LoweredSqlCommand(pub(in crate::db::sql::lowering) LoweredSqlCommandInner);
#[derive(Clone, Debug)]
pub(in crate::db::sql::lowering) enum LoweredSqlCommandInner {
Query(LoweredSqlQuery),
Explain {
mode: SqlExplainMode,
query: LoweredSqlQuery,
},
ExplainGlobalAggregate {
mode: SqlExplainMode,
command: LoweredSqlGlobalAggregateCommand,
},
DescribeEntity,
ShowIndexesEntity,
ShowColumnsEntity,
ShowEntities,
}
#[cfg(test)]
#[derive(Debug)]
pub(crate) enum SqlCommand<E: EntityKind> {
Query(Query<E>),
Explain {
mode: SqlExplainMode,
query: Query<E>,
},
ExplainGlobalAggregate {
mode: SqlExplainMode,
command: SqlGlobalAggregateCommand<E>,
},
DescribeEntity,
ShowIndexesEntity,
ShowColumnsEntity,
ShowEntities,
}
impl LoweredSqlCommand {
#[must_use]
#[allow(dead_code)]
pub(in crate::db) const fn query(&self) -> Option<&LoweredSqlQuery> {
match &self.0 {
LoweredSqlCommandInner::Query(query) => Some(query),
LoweredSqlCommandInner::Explain { .. }
| LoweredSqlCommandInner::ExplainGlobalAggregate { .. }
| LoweredSqlCommandInner::DescribeEntity
| LoweredSqlCommandInner::ShowIndexesEntity
| LoweredSqlCommandInner::ShowColumnsEntity
| LoweredSqlCommandInner::ShowEntities => None,
}
}
#[must_use]
pub(in crate::db) fn into_query(self) -> Option<LoweredSqlQuery> {
match self.0 {
LoweredSqlCommandInner::Query(query) => Some(query),
LoweredSqlCommandInner::Explain { .. }
| LoweredSqlCommandInner::ExplainGlobalAggregate { .. }
| LoweredSqlCommandInner::DescribeEntity
| LoweredSqlCommandInner::ShowIndexesEntity
| LoweredSqlCommandInner::ShowColumnsEntity
| LoweredSqlCommandInner::ShowEntities => None,
}
}
#[must_use]
pub(in crate::db) const fn explain_query(&self) -> Option<(SqlExplainMode, &LoweredSqlQuery)> {
match &self.0 {
LoweredSqlCommandInner::Explain { mode, query } => Some((*mode, query)),
LoweredSqlCommandInner::Query(_)
| LoweredSqlCommandInner::ExplainGlobalAggregate { .. }
| LoweredSqlCommandInner::DescribeEntity
| LoweredSqlCommandInner::ShowIndexesEntity
| LoweredSqlCommandInner::ShowColumnsEntity
| LoweredSqlCommandInner::ShowEntities => None,
}
}
}
#[derive(Clone, Debug)]
pub(crate) enum LoweredSqlQuery {
Select(LoweredSelectShape),
Delete(LoweredBaseQueryShape),
}
#[derive(Debug, ThisError)]
pub(crate) enum SqlLoweringError {
#[error("{0}")]
Parse(#[from] crate::db::sql::parser::SqlParseError),
#[error("{0}")]
Query(Box<QueryError>),
#[error("SQL entity '{sql_entity}' does not match requested entity type '{expected_entity}'")]
EntityMismatch {
sql_entity: String,
expected_entity: &'static str,
},
#[error(
"unsupported SQL SELECT projection; supported forms are SELECT *, field lists, global aggregate terminal lists, or grouped aggregate shapes"
)]
UnsupportedSelectProjection,
#[error("unsupported SQL SELECT DISTINCT")]
UnsupportedSelectDistinct,
#[error(
"unsupported global aggregate SQL projection; supported forms are aggregate projections such as COUNT(*), SUM(field), AVG(expr), or scalar wrappers over aggregate results"
)]
UnsupportedGlobalAggregateProjection,
#[error("global aggregate SQL does not support GROUP BY")]
GlobalAggregateDoesNotSupportGroupBy,
#[error("unsupported SQL GROUP BY projection shape")]
UnsupportedSelectGroupBy,
#[error("grouped SELECT requires an explicit projection list")]
GroupedProjectionRequiresExplicitList,
#[error("grouped SELECT projection must include at least one aggregate expression")]
GroupedProjectionRequiresAggregate,
#[error(
"grouped projection expression at index={index} references fields outside GROUP BY keys"
)]
GroupedProjectionReferencesNonGroupField { index: usize },
#[error(
"grouped projection expression at index={index} appears after aggregate expressions started"
)]
GroupedProjectionScalarAfterAggregate { index: usize },
#[error("HAVING requires GROUP BY")]
HavingRequiresGroupBy,
#[error("unsupported SQL HAVING shape")]
UnsupportedSelectHaving,
#[error("aggregate input expressions are not executable in this release")]
UnsupportedAggregateInputExpressions,
#[error("unsupported SQL WHERE expression shape")]
UnsupportedWhereExpression,
#[error("unknown field '{field}'")]
UnknownField { field: String },
#[error("query-lane lowering reached a non query-compatible statement")]
UnexpectedQueryLaneStatement,
}
impl SqlLoweringError {
fn entity_mismatch(sql_entity: impl Into<String>, expected_entity: &'static str) -> Self {
Self::EntityMismatch {
sql_entity: sql_entity.into(),
expected_entity,
}
}
const fn unsupported_select_projection() -> Self {
Self::UnsupportedSelectProjection
}
pub(crate) const fn unexpected_query_lane_statement() -> Self {
Self::UnexpectedQueryLaneStatement
}
const fn unsupported_select_distinct() -> Self {
Self::UnsupportedSelectDistinct
}
const fn unsupported_global_aggregate_projection() -> Self {
Self::UnsupportedGlobalAggregateProjection
}
pub(crate) const fn unsupported_where_expression() -> Self {
Self::UnsupportedWhereExpression
}
const fn global_aggregate_does_not_support_group_by() -> Self {
Self::GlobalAggregateDoesNotSupportGroupBy
}
const fn unsupported_select_group_by() -> Self {
Self::UnsupportedSelectGroupBy
}
const fn grouped_projection_requires_explicit_list() -> Self {
Self::GroupedProjectionRequiresExplicitList
}
const fn grouped_projection_requires_aggregate() -> Self {
Self::GroupedProjectionRequiresAggregate
}
const fn grouped_projection_references_non_group_field(index: usize) -> Self {
Self::GroupedProjectionReferencesNonGroupField { index }
}
const fn grouped_projection_scalar_after_aggregate(index: usize) -> Self {
Self::GroupedProjectionScalarAfterAggregate { index }
}
const fn having_requires_group_by() -> Self {
Self::HavingRequiresGroupBy
}
const fn unsupported_select_having() -> Self {
Self::UnsupportedSelectHaving
}
const fn unsupported_aggregate_input_expressions() -> Self {
Self::UnsupportedAggregateInputExpressions
}
fn unknown_field(field: impl Into<String>) -> Self {
Self::UnknownField {
field: field.into(),
}
}
}
impl From<QueryError> for SqlLoweringError {
fn from(value: QueryError) -> Self {
Self::Query(Box::new(value))
}
}
#[derive(Clone, Debug)]
pub(crate) struct PreparedSqlStatement {
pub(in crate::db::sql::lowering) statement: SqlStatement,
}
impl PreparedSqlStatement {
#[must_use]
pub(in crate::db) fn into_statement(self) -> SqlStatement {
self.statement
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum LoweredSqlLaneKind {
Query,
Explain,
Describe,
ShowIndexes,
ShowColumns,
ShowEntities,
}
#[cfg(test)]
pub(crate) fn compile_sql_command<E: EntityKind>(
sql: &str,
consistency: MissingRowPolicy,
) -> Result<SqlCommand<E>, SqlLoweringError> {
let statement = crate::db::sql::parser::parse_sql(sql)?;
let prepared = prepare_sql_statement(statement, E::MODEL.name())?;
let lowered = lower_sql_command_from_prepared_statement(prepared, E::MODEL)?;
match lowered.0 {
LoweredSqlCommandInner::Query(query) => Ok(SqlCommand::Query(bind_lowered_sql_query::<E>(
query,
consistency,
)?)),
LoweredSqlCommandInner::Explain { mode, query } => Ok(SqlCommand::Explain {
mode,
query: bind_lowered_sql_query::<E>(query, consistency)?,
}),
LoweredSqlCommandInner::ExplainGlobalAggregate { mode, command } => {
Ok(SqlCommand::ExplainGlobalAggregate {
mode,
command: aggregate::bind_lowered_sql_global_aggregate_command::<E>(
command,
consistency,
)?,
})
}
LoweredSqlCommandInner::DescribeEntity => Ok(SqlCommand::DescribeEntity),
LoweredSqlCommandInner::ShowIndexesEntity => Ok(SqlCommand::ShowIndexesEntity),
LoweredSqlCommandInner::ShowColumnsEntity => Ok(SqlCommand::ShowColumnsEntity),
LoweredSqlCommandInner::ShowEntities => Ok(SqlCommand::ShowEntities),
}
}
pub(crate) const fn lowered_sql_command_lane(command: &LoweredSqlCommand) -> LoweredSqlLaneKind {
match command.0 {
LoweredSqlCommandInner::Query(_) => LoweredSqlLaneKind::Query,
LoweredSqlCommandInner::Explain { .. }
| LoweredSqlCommandInner::ExplainGlobalAggregate { .. } => LoweredSqlLaneKind::Explain,
LoweredSqlCommandInner::DescribeEntity => LoweredSqlLaneKind::Describe,
LoweredSqlCommandInner::ShowIndexesEntity => LoweredSqlLaneKind::ShowIndexes,
LoweredSqlCommandInner::ShowColumnsEntity => LoweredSqlLaneKind::ShowColumns,
LoweredSqlCommandInner::ShowEntities => LoweredSqlLaneKind::ShowEntities,
}
}