use crate::{
db::{
DbSession, QueryError, SqlStatementDispatch,
session::{
AcceptedSchemaCatalogContext,
sql::{
CompiledSqlCommand, SqlCompiledCommandCacheContext, SqlCompiledCommandCacheKey,
SqlCompiledCommandExecutionContext, SqlCompiledCommandSurface,
sql_statement_entity_name_from_statement,
},
},
sql::parser::SqlStatement,
},
error::InternalError,
traits::CanisterKind,
};
impl<C: CanisterKind> DbSession<C> {
#[cfg(test)]
pub(in crate::db) fn compile_sql_query_for_tests(
&self,
sql: &str,
) -> Result<(SqlCompiledCommandExecutionContext, String), QueryError> {
let dispatch = crate::db::sql_statement_dispatch(sql)?;
self.compile_sql_query_with_dispatch_execution_context(&dispatch)
}
#[inline]
pub(in crate::db::session::sql) fn compile_sql_query_with_dispatch_execution_context(
&self,
dispatch: &SqlStatementDispatch<'_>,
) -> Result<(SqlCompiledCommandExecutionContext, String), QueryError> {
let parsed = dispatch.statement();
let entity_name = sql_statement_entity_name_from_statement(parsed).map(str::to_string);
let catalog = match entity_name.as_deref() {
Some(entity_name) => self
.find_accepted_schema_catalog_context_for_entity_name(entity_name)
.map_err(QueryError::execute)?
.ok_or_else(|| QueryError::execute(InternalError::sql_query_entity_not_found()))?,
None => self
.accepted_schema_catalog_context_for_entity_name(None)
.map_err(QueryError::execute)?,
};
let context = self.compile_sql_surface_with_catalog(
dispatch.sql(),
parsed,
SqlCompiledCommandSurface::Query,
catalog,
)?;
Ok((context, entity_name.unwrap_or_default()))
}
pub(in crate::db) fn compile_sql_mutation_with_execution_context(
&self,
dispatch: &SqlStatementDispatch<'_>,
) -> Result<SqlCompiledCommandExecutionContext, QueryError> {
let parsed = dispatch.statement();
let entity_name = dispatch.entity_name();
let catalog = self
.accepted_schema_catalog_context_for_entity_name(entity_name)
.map_err(QueryError::execute)?;
self.compile_sql_surface_with_catalog(
dispatch.sql(),
parsed,
SqlCompiledCommandSurface::Mutation,
catalog,
)
}
fn compile_sql_surface_with_catalog(
&self,
sql: &str,
parsed: &SqlStatement,
surface: SqlCompiledCommandSurface,
catalog: AcceptedSchemaCatalogContext,
) -> Result<SqlCompiledCommandExecutionContext, QueryError> {
let entity_path = catalog.identity().entity_path_handle();
let context = SqlCompiledCommandCacheContext::from_catalog(surface, sql, catalog);
let (cache_key, catalog) = context.into_cache_inputs();
let (compiled, accepted_authority) = self.compile_sql_statement_with_cache(
cache_key,
&catalog,
parsed,
surface,
entity_path.as_ref(),
)?;
let context =
SqlCompiledCommandExecutionContext::new(compiled, catalog, accepted_authority, surface);
Ok(context)
}
fn compile_sql_statement_with_cache(
&self,
cache_key: SqlCompiledCommandCacheKey,
catalog: &AcceptedSchemaCatalogContext,
parsed: &SqlStatement,
surface: SqlCompiledCommandSurface,
_entity_path: &str,
) -> Result<
(
CompiledSqlCommand,
Option<crate::db::executor::EntityAuthority>,
),
QueryError,
> {
let cached = self.with_sql_compiled_command_cache(|cache| cache.get(&cache_key).cloned());
if let Some(compiled) = cached {
return Ok((compiled, None));
}
let authority = catalog.accepted_entity_authority();
let schema = catalog.accepted_schema_info();
let compiled = Self::compile_sql_statement(parsed, surface, schema)?;
self.with_sql_compiled_command_cache(|cache| {
cache.insert(cache_key, compiled.clone());
});
Ok((compiled, Some(authority)))
}
}