use crate::{
db::{
DbSession, MissingRowPolicy, QueryError,
executor::{EntityAuthority, execute_sql_delete_projection_for_canister},
session::sql::{
SqlDispatchResult,
projection::{
SqlProjectionPayload, projection_labels_from_entity_model,
sql_projection_rows_from_kernel_rows,
},
surface::{SqlSurface, session_sql_lane, unsupported_sql_lane_message},
},
sql::lowering::{
LoweredBaseQueryShape, LoweredSelectShape, LoweredSqlCommand, LoweredSqlQuery,
apply_lowered_select_shape, bind_lowered_sql_delete_query_structural,
},
},
traits::CanisterKind,
value::Value,
};
type SqlQuerySurfaceRowParts = (Vec<String>, Vec<Vec<Value>>, u32);
impl<C: CanisterKind> DbSession<C> {
#[inline(never)]
fn execute_lowered_sql_projection_core(
&self,
select: &LoweredSelectShape,
authority: EntityAuthority,
) -> Result<SqlProjectionPayload, QueryError> {
let structural = apply_lowered_select_shape(
crate::db::query::intent::StructuralQuery::new(
authority.model(),
MissingRowPolicy::Ignore,
),
select.clone(),
)
.map_err(QueryError::from_sql_lowering_error)?;
self.execute_structural_sql_projection(structural, authority)
}
#[inline(never)]
pub(in crate::db::session::sql::dispatch) fn execute_lowered_sql_dispatch_select_core(
&self,
select: &LoweredSelectShape,
authority: EntityAuthority,
) -> Result<SqlDispatchResult, QueryError> {
self.execute_lowered_sql_projection_core(select, authority)
.map(SqlProjectionPayload::into_dispatch_result)
}
fn execute_lowered_sql_dispatch_delete_core(
&self,
delete: &LoweredBaseQueryShape,
authority: EntityAuthority,
) -> Result<SqlDispatchResult, QueryError> {
let structural = bind_lowered_sql_delete_query_structural(
authority.model(),
delete.clone(),
MissingRowPolicy::Ignore,
);
let deleted = execute_sql_delete_projection_for_canister(
&self.db,
authority,
structural.build_plan()?,
)
.map_err(QueryError::execute)?;
let (rows, row_count) = deleted.into_parts();
let rows = sql_projection_rows_from_kernel_rows(rows);
Ok(SqlProjectionPayload::new(
projection_labels_from_entity_model(authority.model()),
rows,
row_count,
)
.into_dispatch_result())
}
#[doc(hidden)]
pub fn execute_lowered_sql_dispatch_query_for_authority(
&self,
lowered: &LoweredSqlCommand,
authority: EntityAuthority,
) -> Result<SqlDispatchResult, QueryError> {
self.execute_lowered_sql_dispatch_query_core(lowered, authority)
}
#[doc(hidden)]
pub fn execute_lowered_sql_projection_for_authority(
&self,
lowered: &LoweredSqlCommand,
authority: EntityAuthority,
) -> Result<SqlQuerySurfaceRowParts, QueryError> {
let Some(query) = lowered.query() else {
return Err(QueryError::unsupported_query(unsupported_sql_lane_message(
SqlSurface::QueryFrom,
session_sql_lane(lowered),
)));
};
match query {
LoweredSqlQuery::Select(select) => self
.execute_lowered_sql_projection_core(select, authority)
.map(SqlProjectionPayload::into_parts),
LoweredSqlQuery::Delete(delete) => self
.execute_lowered_sql_dispatch_delete_core(delete, authority)
.and_then(|dispatch| match dispatch {
SqlDispatchResult::Projection {
columns,
rows,
row_count,
} => Ok((columns, rows, row_count)),
SqlDispatchResult::Explain(_)
| SqlDispatchResult::Describe(_)
| SqlDispatchResult::ShowIndexes(_)
| SqlDispatchResult::ShowColumns(_)
| SqlDispatchResult::ShowEntities(_) => Err(QueryError::unsupported_query(
"generated SQL query dispatch requires row-shaped SELECT or DELETE",
)),
}),
}
}
fn execute_lowered_sql_dispatch_query_core(
&self,
lowered: &LoweredSqlCommand,
authority: EntityAuthority,
) -> Result<SqlDispatchResult, QueryError> {
let Some(query) = lowered.query() else {
return Err(QueryError::unsupported_query(unsupported_sql_lane_message(
SqlSurface::QueryFrom,
session_sql_lane(lowered),
)));
};
match query {
LoweredSqlQuery::Select(select) => {
self.execute_lowered_sql_dispatch_select_core(select, authority)
}
LoweredSqlQuery::Delete(delete) => {
self.execute_lowered_sql_dispatch_delete_core(delete, authority)
}
}
}
}