use crate::{
db::{
DbSession,
schema::{AcceptedSchemaRevision, AcceptedSchemaRuntimeRootIdentity, SchemaVersion},
session::{
AcceptedSchemaCatalogContext,
bounded_cache::BoundedCache,
sql::compiled::{CompiledSqlCommand, SqlCompiledSchemaFingerprint},
},
},
traits::CanisterKind,
};
use std::{cell::RefCell, collections::HashMap, rc::Rc};
const SQL_COMPILED_COMMAND_CACHE_MAX_ENTRIES: usize = 1024;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub(in crate::db::session::sql) enum SqlCompiledCommandSurface {
Query,
Mutation,
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub(in crate::db) struct SqlCompiledCommandCacheKey {
surface: SqlCompiledCommandSurface,
entity_path: Rc<str>,
accepted_runtime_root_identity: AcceptedSchemaRuntimeRootIdentity,
accepted_schema_revision: AcceptedSchemaRevision,
schema_version: SchemaVersion,
schema_fingerprint: SqlCompiledSchemaFingerprint,
sql: String,
}
pub(in crate::db) type SqlCompiledCommandCache =
BoundedCache<SqlCompiledCommandCacheKey, CompiledSqlCommand>;
#[derive(Debug)]
pub(in crate::db::session::sql) struct SqlCompiledCommandCacheContext {
key: SqlCompiledCommandCacheKey,
catalog: AcceptedSchemaCatalogContext,
}
impl SqlCompiledCommandCacheContext {
#[must_use]
pub(in crate::db::session::sql) fn from_catalog(
surface: SqlCompiledCommandSurface,
sql: &str,
catalog: AcceptedSchemaCatalogContext,
) -> Self {
Self {
key: SqlCompiledCommandCacheKey::new(
surface,
catalog.identity().entity_path(),
catalog.runtime_root_identity(),
catalog.revision(),
catalog.schema_version(),
SqlCompiledSchemaFingerprint::from_catalog(&catalog),
sql,
),
catalog,
}
}
#[must_use]
pub(in crate::db::session::sql) fn into_cache_inputs(
self,
) -> (SqlCompiledCommandCacheKey, AcceptedSchemaCatalogContext) {
(self.key, self.catalog)
}
}
thread_local! {
static SQL_COMPILED_COMMAND_CACHES: RefCell<HashMap<usize, SqlCompiledCommandCache>> =
RefCell::new(HashMap::default());
}
impl SqlCompiledCommandCacheKey {
fn new(
surface: SqlCompiledCommandSurface,
entity_path: impl Into<Rc<str>>,
accepted_runtime_root_identity: AcceptedSchemaRuntimeRootIdentity,
accepted_schema_revision: AcceptedSchemaRevision,
schema_version: SchemaVersion,
schema_fingerprint: SqlCompiledSchemaFingerprint,
sql: &str,
) -> Self {
Self {
surface,
entity_path: entity_path.into(),
accepted_runtime_root_identity,
accepted_schema_revision,
schema_version,
schema_fingerprint,
sql: sql.to_string(),
}
}
}
impl<C: CanisterKind> DbSession<C> {
pub(in crate::db::session::sql) fn with_sql_compiled_command_cache<R>(
&self,
f: impl FnOnce(&mut SqlCompiledCommandCache) -> R,
) -> R {
let scope_id = self.db.cache_scope_id();
SQL_COMPILED_COMMAND_CACHES.with(|caches| {
let mut caches = caches.borrow_mut();
let cache = caches.entry(scope_id).or_insert_with(|| {
SqlCompiledCommandCache::new(SQL_COMPILED_COMMAND_CACHE_MAX_ENTRIES)
});
f(cache)
})
}
}