use crate::{
db::{
MissingRowPolicy, QueryError,
query::builder::aggregate::AggregateExpr,
query::plan::{FieldSlot, resolve_aggregate_target_field_slot},
session::sql::surface::{SqlSurface, session_sql_lane, unsupported_sql_lane_message},
sql::lowering::{
LoweredSqlCommand, SqlGlobalAggregateCommandCore, SqlGlobalAggregateTerminal,
bind_lowered_sql_explain_global_aggregate_structural,
render_lowered_sql_explain_plan_or_json,
},
sql::parser::SqlExplainMode,
},
model::EntityModel,
};
fn resolve_sql_aggregate_target_slot_with_model(
model: &'static EntityModel,
field: &str,
) -> Result<FieldSlot, QueryError> {
resolve_aggregate_target_field_slot(model, field)
}
pub(in crate::db::session::sql) fn resolve_sql_aggregate_target_slot<E>(
field: &str,
) -> Result<FieldSlot, QueryError>
where
E: crate::traits::EntityKind,
{
resolve_sql_aggregate_target_slot_with_model(E::MODEL, field)
}
fn sql_global_aggregate_terminal_to_expr_with_model(
model: &'static EntityModel,
terminal: &SqlGlobalAggregateTerminal,
) -> Result<AggregateExpr, QueryError> {
match terminal {
SqlGlobalAggregateTerminal::CountRows => Ok(crate::db::query::builder::aggregate::count()),
SqlGlobalAggregateTerminal::CountField(field) => {
let _ = resolve_sql_aggregate_target_slot_with_model(model, field)?;
Ok(crate::db::query::builder::aggregate::count_by(
field.as_str(),
))
}
SqlGlobalAggregateTerminal::SumField(field) => {
let _ = resolve_sql_aggregate_target_slot_with_model(model, field)?;
Ok(crate::db::query::builder::aggregate::sum(field.as_str()))
}
SqlGlobalAggregateTerminal::AvgField(field) => {
let _ = resolve_sql_aggregate_target_slot_with_model(model, field)?;
Ok(crate::db::query::builder::aggregate::avg(field.as_str()))
}
SqlGlobalAggregateTerminal::MinField(field) => {
let _ = resolve_sql_aggregate_target_slot_with_model(model, field)?;
Ok(crate::db::query::builder::aggregate::min_by(field.as_str()))
}
SqlGlobalAggregateTerminal::MaxField(field) => {
let _ = resolve_sql_aggregate_target_slot_with_model(model, field)?;
Ok(crate::db::query::builder::aggregate::max_by(field.as_str()))
}
}
}
impl LoweredSqlCommand {
#[inline(never)]
pub fn explain_for_model(&self, model: &'static EntityModel) -> Result<String, QueryError> {
let lane = session_sql_lane(self);
if lane != crate::db::session::sql::surface::SqlLaneKind::Explain {
return Err(QueryError::unsupported_query(unsupported_sql_lane_message(
SqlSurface::Explain,
lane,
)));
}
if let Some(rendered) =
render_lowered_sql_explain_plan_or_json(self, model, MissingRowPolicy::Ignore)
.map_err(QueryError::from_sql_lowering_error)?
{
return Ok(rendered);
}
if let Some((mode, command)) = bind_lowered_sql_explain_global_aggregate_structural(
self,
model,
MissingRowPolicy::Ignore,
) {
return explain_sql_global_aggregate_structural(mode, command);
}
Err(QueryError::unsupported_query(
"shared EXPLAIN dispatch could not classify lowered SQL shape",
))
}
}
#[inline(never)]
fn explain_sql_global_aggregate_structural(
mode: SqlExplainMode,
command: SqlGlobalAggregateCommandCore,
) -> Result<String, QueryError> {
let model = command.query().model();
match mode {
SqlExplainMode::Plan => {
let _ = sql_global_aggregate_terminal_to_expr_with_model(model, command.terminal())?;
Ok(command
.query()
.build_plan()?
.explain_with_model(model)
.render_text_canonical())
}
SqlExplainMode::Execution => {
let aggregate =
sql_global_aggregate_terminal_to_expr_with_model(model, command.terminal())?;
let plan = command.query().explain_aggregate_terminal(aggregate)?;
Ok(plan.execution_node_descriptor().render_text_tree())
}
SqlExplainMode::Json => {
let _ = sql_global_aggregate_terminal_to_expr_with_model(model, command.terminal())?;
Ok(command
.query()
.build_plan()?
.explain_with_model(model)
.render_json_canonical())
}
}
}