use std::sync::Arc;
use fraiseql_db::types::ReadRouting;
use super::CachedDatabaseAdapter;
use crate::{
backend::{
DatabaseAdapter, ProjectionRequest, WhereClause,
types::{JsonbValue, sql_hints::OrderByClause},
},
cache::key::{generate_projection_query_key, generate_view_query_key},
error::Result,
};
#[must_use]
pub fn view_name_to_entity_type(view: &str) -> Option<String> {
let after_prefix = view.split_once('_')?.1;
if after_prefix.is_empty() {
return None;
}
let pascal = after_prefix
.split('_')
.map(|segment| {
let mut chars = segment.chars();
match chars.next() {
None => String::new(),
Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
}
})
.collect::<String>();
Some(pascal)
}
impl<A: DatabaseAdapter> CachedDatabaseAdapter<A> {
#[tracing::instrument(skip_all, fields(cache.view = request.view))]
pub(super) async fn execute_with_projection_impl(
&self,
request: &ProjectionRequest<'_>,
session_vars: &[(&str, &str)],
) -> Result<Arc<Vec<JsonbValue>>> {
let view = request.view;
if !self.cache.is_enabled() || (self.opt_in_mode && !self.cacheable_views.contains(view)) {
return self
.adapter
.execute_with_projection_arc_with_session(request, session_vars, ReadRouting::Any)
.await;
}
let cache_key = generate_projection_query_key(request, session_vars, &self.schema_version);
if let Some(cached_arc) = self.cache.get(cache_key)? {
return Ok(cached_arc);
}
let fence = self.cache.invalidation_generation();
let arc = self
.adapter
.execute_with_projection_arc_with_session(request, session_vars, ReadRouting::Any)
.await?;
let ttl = self.view_ttl_overrides.get(view).copied();
let entity_type = view_name_to_entity_type(view);
self.cache.put_arc(
cache_key,
Arc::clone(&arc),
self.accessed_views_for(view),
ttl,
entity_type.as_deref(),
Some(fence),
)?;
Ok(arc)
}
#[tracing::instrument(skip_all, fields(cache.view = view))]
pub(super) async fn execute_where_query_impl(
&self,
view: &str,
where_clause: Option<&WhereClause>,
limit: Option<u32>,
offset: Option<u32>,
order_by: Option<&[OrderByClause]>,
session_vars: &[(&str, &str)],
) -> Result<Arc<Vec<JsonbValue>>> {
if !self.cache.is_enabled() || (self.opt_in_mode && !self.cacheable_views.contains(view)) {
return self
.adapter
.execute_where_query_arc_with_session(
view,
where_clause,
limit,
offset,
order_by,
session_vars,
ReadRouting::Any,
)
.await;
}
let cache_key = generate_view_query_key(
view,
where_clause,
limit,
offset,
order_by,
session_vars,
&self.schema_version,
);
if let Some(cached_arc) = self.cache.get(cache_key)? {
return Ok(cached_arc);
}
let fence = self.cache.invalidation_generation();
let arc = self
.adapter
.execute_where_query_arc_with_session(
view,
where_clause,
limit,
offset,
order_by,
session_vars,
ReadRouting::Any,
)
.await?;
let ttl = self.view_ttl_overrides.get(view).copied();
let entity_type = view_name_to_entity_type(view);
self.cache.put_arc(
cache_key,
Arc::clone(&arc),
self.accessed_views_for(view),
ttl,
entity_type.as_deref(),
Some(fence),
)?;
Ok(arc)
}
}