mod aggregate;
mod normalize;
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(crate) use aggregate::compile_sql_global_aggregate_command_from_prepared;
pub(in crate::db) use aggregate::is_sql_global_aggregate_statement;
#[cfg(test)]
pub(crate) use aggregate::{
PreparedSqlScalarAggregateDescriptorShape, PreparedSqlScalarAggregateDomain,
PreparedSqlScalarAggregateEmptySetBehavior, PreparedSqlScalarAggregateOrderingRequirement,
PreparedSqlScalarAggregateRowSource, TypedSqlGlobalAggregateTerminal,
compile_sql_global_aggregate_command,
};
pub(crate) use aggregate::{
PreparedSqlScalarAggregateRuntimeDescriptor, PreparedSqlScalarAggregateStrategy,
SqlGlobalAggregateCommand, SqlGlobalAggregateCommandCore,
bind_lowered_sql_explain_global_aggregate_structural,
};
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_delete_query_structural, bind_lowered_sql_query,
bind_lowered_sql_query_structural, bind_lowered_sql_select_query_structural,
};
#[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]
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),
}
impl LoweredSqlQuery {
pub(crate) const fn has_grouping(&self) -> bool {
match self {
Self::Select(select) => select.has_grouping(),
Self::Delete(_) => false,
}
}
}
#[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, or grouped aggregate shapes"
)]
UnsupportedSelectProjection,
#[error("unsupported SQL SELECT DISTINCT")]
UnsupportedSelectDistinct,
#[error("unsupported SQL GROUP BY projection shape")]
UnsupportedSelectGroupBy,
#[error("unsupported SQL HAVING shape")]
UnsupportedSelectHaving,
}
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
}
const fn unsupported_select_distinct() -> Self {
Self::UnsupportedSelectDistinct
}
const fn unsupported_select_group_by() -> Self {
Self::UnsupportedSelectGroupBy
}
const fn unsupported_select_having() -> Self {
Self::UnsupportedSelectHaving
}
}
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,
}
#[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)?;
compile_sql_command_from_statement::<E>(statement, consistency)
}
#[cfg(test)]
pub(crate) fn compile_sql_command_from_statement<E: EntityKind>(
statement: SqlStatement,
consistency: MissingRowPolicy,
) -> Result<SqlCommand<E>, SqlLoweringError> {
let prepared = prepare_sql_statement(statement, E::MODEL.name())?;
compile_sql_command_from_prepared_statement::<E>(prepared, consistency)
}
#[cfg(test)]
pub(crate) fn compile_sql_command_from_prepared_statement<E: EntityKind>(
prepared: PreparedSqlStatement,
consistency: MissingRowPolicy,
) -> Result<SqlCommand<E>, SqlLoweringError> {
let lowered = lower_sql_command_from_prepared_statement(prepared, E::MODEL.primary_key.name)?;
bind_lowered_sql_command::<E>(lowered, consistency)
}
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,
}
}
#[cfg(test)]
pub(crate) fn bind_lowered_sql_command<E: EntityKind>(
lowered: LoweredSqlCommand,
consistency: MissingRowPolicy,
) -> Result<SqlCommand<E>, SqlLoweringError> {
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),
}
}