use crate::{
db::{
DbSession, MissingRowPolicy, PersistedRow, QueryError,
executor::EntityAuthority,
session::sql::SqlDispatchResult,
session::sql::computed_projection,
sql::lowering::{
LoweredSqlQuery, bind_lowered_sql_select_query_structural,
lower_sql_command_from_prepared_statement, prepare_sql_statement,
},
sql::parser::{SqlExplainMode, SqlExplainStatement, SqlExplainTarget, SqlStatement},
},
traits::{CanisterKind, EntityValue},
};
impl<C: CanisterKind> DbSession<C> {
pub(in crate::db::session::sql::dispatch) fn execute_computed_sql_projection_dispatch<E>(
&self,
plan: computed_projection::SqlComputedProjectionPlan,
) -> Result<SqlDispatchResult, QueryError>
where
E: PersistedRow<Canister = C> + EntityValue,
{
self.execute_computed_sql_projection_dispatch_for_authority(
plan,
EntityAuthority::for_type::<E>(),
)
}
pub(in crate::db::session::sql::dispatch) fn execute_computed_sql_projection_dispatch_for_authority(
&self,
plan: computed_projection::SqlComputedProjectionPlan,
authority: EntityAuthority,
) -> Result<SqlDispatchResult, QueryError> {
let lowered = lower_sql_command_from_prepared_statement(
prepare_sql_statement(plan.cloned_base_statement(), authority.model().name())
.map_err(QueryError::from_sql_lowering_error)?,
authority.model().primary_key.name,
)
.map_err(QueryError::from_sql_lowering_error)?;
let Some(LoweredSqlQuery::Select(select)) = lowered.query().cloned() else {
return Err(QueryError::unsupported_query(
"computed SQL projection requires a lowered SELECT statement",
));
};
let structural = bind_lowered_sql_select_query_structural(
authority.model(),
select,
MissingRowPolicy::Ignore,
)
.map_err(QueryError::from_sql_lowering_error)?;
let base_payload = self.execute_structural_sql_projection(structural, authority)?;
let projected =
computed_projection::apply_computed_sql_projection_payload(base_payload, &plan)?;
Ok(projected.into_dispatch_result())
}
pub(in crate::db::session::sql::dispatch) fn explain_computed_sql_projection_dispatch<E>(
&self,
mode: SqlExplainMode,
plan: computed_projection::SqlComputedProjectionPlan,
) -> Result<String, QueryError>
where
E: PersistedRow<Canister = C> + EntityValue,
{
self.explain_computed_sql_projection_dispatch_for_authority(
mode,
plan,
EntityAuthority::for_type::<E>(),
)
}
pub(in crate::db::session::sql::dispatch) fn explain_computed_sql_projection_dispatch_for_authority(
&self,
mode: SqlExplainMode,
plan: computed_projection::SqlComputedProjectionPlan,
authority: EntityAuthority,
) -> Result<String, QueryError> {
let SqlStatement::Select(base_select) = plan.into_base_statement() else {
return Err(QueryError::invariant(
"computed SQL projection explain requires a base SELECT statement",
));
};
let explain_statement = SqlStatement::Explain(SqlExplainStatement {
mode,
statement: SqlExplainTarget::Select(base_select),
});
let lowered = lower_sql_command_from_prepared_statement(
prepare_sql_statement(explain_statement, authority.model().name())
.map_err(QueryError::from_sql_lowering_error)?,
authority.model().primary_key.name,
)
.map_err(QueryError::from_sql_lowering_error)?;
self.explain_lowered_sql_for_authority(&lowered, authority)
}
}