pub struct CachedDatabaseAdapter<A: DatabaseAdapter> { /* private fields */ }Expand description
Cached database adapter wrapper.
Wraps any DatabaseAdapter implementation with transparent query result caching.
Cache keys include query, variables, WHERE clause, and schema version for security
and correctness.
§Cache Behavior
- Cache Hit: Returns cached result in ~0.1ms (50-200x faster than database)
- Cache Miss: Executes query via underlying adapter, stores result in cache
- Invalidation: Call
invalidate_views()after mutations to clear affected caches
§Thread Safety
This adapter is Send + Sync and can be safely shared across async tasks.
The underlying cache uses Arc<Mutex<>> for thread-safe access.
§Example
use fraiseql_core::cache::{CachedDatabaseAdapter, QueryResultCache, CacheConfig, InvalidationContext};
use fraiseql_core::db::{postgres::PostgresAdapter, DatabaseAdapter};
use fraiseql_db::ViewName;
let db = PostgresAdapter::new("postgresql://localhost/db").await?;
let cache = QueryResultCache::new(CacheConfig::default());
let adapter = CachedDatabaseAdapter::new(db, cache, "1.0.0".to_string());
// First query - cache miss (slower)
let users1 = adapter.execute_where_query("v_user", None, None, None, None).await?;
// Second query - cache hit (fast!)
let users2 = adapter.execute_where_query("v_user", None, None, None, None).await?;
// After mutation, invalidate. `InvalidationContext` keeps its `Vec<String>`
// shape for compatibility with the audit-logging facade; callers convert at
// the adapter boundary.
let invalidation = InvalidationContext::for_mutation(
"createUser",
vec!["v_user".to_string()],
);
let views: Vec<ViewName> = invalidation
.modified_views
.iter()
.map(ViewName::from)
.collect();
adapter.invalidate_views(&views)?;Implementations§
Source§impl<A: DatabaseAdapter> CachedDatabaseAdapter<A>
impl<A: DatabaseAdapter> CachedDatabaseAdapter<A>
Sourcepub fn new(adapter: A, cache: QueryResultCache, schema_version: String) -> Self
pub fn new(adapter: A, cache: QueryResultCache, schema_version: String) -> Self
Create new cached database adapter.
§Arguments
adapter- Underlying database adapter to wrapcache- Query result cache instanceschema_version- Uniquely identifies the compiled schema. Useschema.content_hash()(NOTenv!("CARGO_PKG_VERSION")) so that any schema content change automatically invalidates cached entries across deploys.
§Example
use fraiseql_core::cache::{CachedDatabaseAdapter, QueryResultCache, CacheConfig};
use fraiseql_core::db::postgres::PostgresAdapter;
use fraiseql_core::schema::CompiledSchema;
let db = PostgresAdapter::new("postgresql://localhost/db").await?;
let cache = QueryResultCache::new(CacheConfig::default());
let adapter = CachedDatabaseAdapter::new(
db,
cache,
schema.content_hash() // Use content hash for automatic invalidation
);Sourcepub fn with_view_ttl_overrides(self, overrides: HashMap<String, u64>) -> Self
pub fn with_view_ttl_overrides(self, overrides: HashMap<String, u64>) -> Self
Set per-view TTL overrides.
Maps sql_source (view name) → TTL in seconds. Built at server startup
from compiled QueryDefinition::cache_ttl_seconds entries.
§Example
let overrides = std::collections::HashMap::from([
("v_country".to_string(), 3600_u64), // 1 h for reference data
("v_live_price".to_string(), 0_u64), // no TTL — mutation-invalidated only
]);
let adapter = CachedDatabaseAdapter::new(db, cache, "1.0.0".to_string())
.with_view_ttl_overrides(overrides);⚠ A 0 TTL means the entry lives until a mutation invalidates it, so its
correctness rests entirely on invalidation being unmissable. That is what the
generation fence guarantees (#1079): a read whose database round trip straddled an
invalidation refuses to cache, instead of storing pre-mutation rows that — at TTL 0
— nothing would ever expire.
Sourcepub const fn with_rls(self, has_rls: bool) -> Self
pub const fn with_rls(self, has_rls: bool) -> Self
Enable or disable the RLS unauthenticated-request cache bypass.
When true, any request without a SecurityContext will bypass both
cache read and write, preventing unauthenticated requests from being
served stale data that belongs to an authenticated tenant.
Set this to schema.has_rls_configured() at server startup.
Sourcepub fn with_cascade_invalidator(self, invalidator: CascadeInvalidator) -> Self
pub fn with_cascade_invalidator(self, invalidator: CascadeInvalidator) -> Self
Set a cascade invalidator for transitive view dependency expansion.
When set, invalidate_views() uses BFS to expand the initial view list
to include all views that transitively depend on the invalidated views.
§Example
let mut cascade = CascadeInvalidator::new();
cascade.add_dependency("v_user_stats", "v_user")?;
cascade.add_dependency("v_dashboard", "v_user_stats")?;
let adapter = CachedDatabaseAdapter::new(db, cache, "1.0.0".to_string())
.with_cascade_invalidator(cascade);Sourcepub fn with_cache_metadata_from_schema(self, schema: &CompiledSchema) -> Self
pub fn with_cache_metadata_from_schema(self, schema: &CompiledSchema) -> Self
Read every per-query cache declaration out of a compiled schema.
This is the single seam between the compiled schema and the row cache.
Both things it reads are per-query annotations that the runtime otherwise
has no way to see, because the DatabaseAdapter boundary carries only a
view name:
cache_ttl_seconds→ the view’s TTL override, and its membership ofcacheable_views(opt-in mode).additional_views→ the secondary views a query also reads, so a mutation on one of them evicts the joined query’s cached rows (#761).
Add any further per-query cache annotation here, not at a call site:
rebuilt_for_schema (hot reload) delegates to the same code, so an
annotation wired in one place and not the other silently stops working
after the first schema reload.
§Example
let adapter = CachedDatabaseAdapter::new(db, cache, "1.0.0".to_string())
.with_cache_metadata_from_schema(&schema);Sourcepub fn rebuilt_for_schema(self, schema: &CompiledSchema) -> Self
pub fn rebuilt_for_schema(self, schema: &CompiledSchema) -> Self
Rebuild this adapter for a new compiled schema.
Returns a new CachedDatabaseAdapter that:
- Shares the same underlying
QueryResultCache(viaArc) - Uses the new schema’s content hash as schema version
- Has per-view TTL overrides from the new schema
- Clears the shared cache (stale entries from old schema)
The underlying database adapter (connection pool) is reused — no connections are closed or reopened.
This is the hot-reload counterpart to the startup path
(new() + with_cache_metadata_from_schema()).
Sourcepub fn with_fact_table_config(
adapter: A,
cache: QueryResultCache,
schema_version: String,
fact_table_config: FactTableCacheConfig,
) -> Self
pub fn with_fact_table_config( adapter: A, cache: QueryResultCache, schema_version: String, fact_table_config: FactTableCacheConfig, ) -> Self
Create new cached database adapter with fact table caching configuration.
§Arguments
adapter- Underlying database adapter to wrapcache- Query result cache instanceschema_version- Current schema version (e.g., git hash, semver)fact_table_config- Configuration for fact table aggregation caching
§Example
use fraiseql_core::cache::{
CachedDatabaseAdapter, QueryResultCache, CacheConfig,
FactTableCacheConfig, FactTableVersionStrategy,
};
use fraiseql_core::db::postgres::PostgresAdapter;
let db = PostgresAdapter::new("postgresql://localhost/db").await?;
let cache = QueryResultCache::new(CacheConfig::default());
// Configure fact table caching strategies
let mut ft_config = FactTableCacheConfig::default();
ft_config.set_strategy("tf_sales", FactTableVersionStrategy::VersionTable);
ft_config.set_strategy("tf_events", FactTableVersionStrategy::time_based(300));
let adapter = CachedDatabaseAdapter::with_fact_table_config(
db,
cache,
"1.0.0".to_string(),
ft_config,
);Sourcepub const fn inner(&self) -> &A
pub const fn inner(&self) -> &A
Get reference to underlying adapter.
Useful for accessing adapter-specific methods not in the DatabaseAdapter trait.
§Example
// Access PostgreSQL-specific functionality
let pg_adapter = adapter.inner();Sourcepub fn cache(&self) -> &QueryResultCache
pub fn cache(&self) -> &QueryResultCache
Get reference to cache.
Useful for metrics and monitoring.
§Example
let metrics = adapter.cache().metrics()?;
println!("Cache hit rate: {:.1}%", metrics.hit_rate() * 100.0);Sourcepub fn schema_version(&self) -> &str
pub fn schema_version(&self) -> &str
Sourcepub const fn fact_table_config(&self) -> &FactTableCacheConfig
pub const fn fact_table_config(&self) -> &FactTableCacheConfig
Get fact table cache configuration.
Sourcepub fn version_provider(&self) -> &FactTableVersionProvider
pub fn version_provider(&self) -> &FactTableVersionProvider
Get the version provider for fact tables.
Sourcepub async fn validate_rls_active(&self, schema: &CompiledSchema) -> Result<()>
pub async fn validate_rls_active(&self, schema: &CompiledSchema) -> Result<()>
Verify that Row-Level Security is genuinely enforceable on the relations this schema reads.
Call this during server initialization when a multi-tenant schema declares
[security.rls] enabled = true. Without real RLS, users sharing the same
query parameters receive the same cached response regardless of tenant.
§What this checks
Every relation named by a query’s sql_source, resolved through
to_regclass, must be one of:
- a table with
relrowsecurityset and at least onepg_policyrow, or - a
security_invokerview — a default view executes with its owner’s privileges and bypasses the caller’s RLS entirely, so a non-invoker view over an RLS-protected table provides no isolation at all.
A relation that does not exist fails the check: an absent relation cannot be protected.
§What it used to check, and why that was worthless
It ran SELECT current_setting('row_security', true) and passed on 'on' or
'force'. row_security governs whether existing policies are applied; it
defaults to on and says nothing about whether any policy exists. So the
documented “refuse startup if RLS appears inactive” gate returned Ok(()) on
a stock PostgreSQL with no RLS anywhere — reporting success for a guarantee it
never checked (#762).
§Errors
Returns FraiseQLError::Configuration naming every relation that is not
RLS-protected, or FraiseQLError::Database if the catalog cannot be read
(including on non-PostgreSQL adapters, which have no equivalent mechanism and
therefore fail closed).
Sourcepub async fn enforce_rls(
&self,
schema: &CompiledSchema,
enforcement: RlsEnforcement,
) -> Result<()>
pub async fn enforce_rls( &self, schema: &CompiledSchema, enforcement: RlsEnforcement, ) -> Result<()>
Apply the RLS enforcement policy from CacheConfig.
Runs validate_rls_active and handles the result
according to enforcement:
RlsEnforcement::Error: propagates the error (default)RlsEnforcement::Warn: logs a warning and returnsOk(())RlsEnforcement::Off: skips the check entirely
§Errors
Returns the error from validate_rls_active when enforcement is Error.
Source§impl<A: DatabaseAdapter> CachedDatabaseAdapter<A>
impl<A: DatabaseAdapter> CachedDatabaseAdapter<A>
Sourcepub async fn execute_aggregation_query(
&self,
sql: &str,
) -> Result<Vec<HashMap<String, Value>>>
pub async fn execute_aggregation_query( &self, sql: &str, ) -> Result<Vec<HashMap<String, Value>>>
Execute aggregation query with caching based on fact table versioning strategy.
This method provides transparent caching for aggregation queries on fact tables. The caching behavior depends on the configured strategy for the fact table.
§Arguments
sql- The aggregation SQL query
§Returns
Query results (from cache or database)
§Example
// This query will be cached according to tf_sales strategy
let results = adapter.execute_aggregation_query(
"SELECT SUM(revenue) FROM tf_sales WHERE year = 2024"
).await?;§Errors
Returns FraiseQLError if the underlying database query fails.
Source§impl<A: DatabaseAdapter> CachedDatabaseAdapter<A>
impl<A: DatabaseAdapter> CachedDatabaseAdapter<A>
Sourcepub fn invalidate_views(&self, views: &[ViewName]) -> Result<u64>
pub fn invalidate_views(&self, views: &[ViewName]) -> Result<u64>
Invalidate cache entries that read from specified views.
Call this after mutations to ensure cache consistency. All cache entries that accessed any of the modified views will be removed.
§Arguments
views- List of views/tables that were modified
§Returns
Number of cache entries invalidated
§Errors
Returns error if cache mutex is poisoned (very rare).
§Example
// After creating a user
let count = adapter.invalidate_views(&[ViewName::from("v_user")])?;
println!("Invalidated {} cache entries", count);Sourcepub fn invalidate_by_entity(
&self,
entity_type: &str,
entity_id: &str,
) -> Result<u64>
pub fn invalidate_by_entity( &self, entity_type: &str, entity_id: &str, ) -> Result<u64>
Evict cache entries that contain the given entity UUID.
Delegates to QueryResultCache::invalidate_by_entity. Only entries
whose entity-ID index (built at put() time) contains the given UUID
are removed; all other entries remain warm.
§Returns
Number of cache entries evicted.
§Errors
Returns error if the cache mutex is poisoned.
Trait Implementations§
Source§impl<A: DatabaseAdapter + Clone> Clone for CachedDatabaseAdapter<A>
impl<A: DatabaseAdapter + Clone> Clone for CachedDatabaseAdapter<A>
Source§impl<A: DatabaseAdapter> DatabaseAdapter for CachedDatabaseAdapter<A>
impl<A: DatabaseAdapter> DatabaseAdapter for CachedDatabaseAdapter<A>
Source§fn stream_with_projection<'life0, 'life1, 'life2, 'life3, 'life4, 'life5, 'async_trait>(
&'life0 self,
request: &'life1 ProjectionRequest<'life2>,
session_vars: &'life3 [(&'life4 str, &'life5 str)],
routing: ReadRouting,
) -> Pin<Box<dyn Future<Output = Result<JsonbRowStream>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
'life5: 'async_trait,
fn stream_with_projection<'life0, 'life1, 'life2, 'life3, 'life4, 'life5, 'async_trait>(
&'life0 self,
request: &'life1 ProjectionRequest<'life2>,
session_vars: &'life3 [(&'life4 str, &'life5 str)],
routing: ReadRouting,
) -> Pin<Box<dyn Future<Output = Result<JsonbRowStream>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
'life5: 'async_trait,
Forwarded to the inner adapter, and deliberately uncached (#958).
Forwarding at all is the load-bearing part. The trait’s default would satisfy this method by calling the cached read above and replaying the buffer, which returns exactly the right rows — and silently converts every streaming caller in the process back into one that materialises the whole result set. A wrapper that inherits the default is how a memory bound disappears without a single test failing.
Uncached because the two are incompatible by construction: populating a result-cache entry means holding every row, which is the cost this call exists to avoid, and serving one means the caller never streams. A read that wants the cache asks for the buffered method.
Source§fn execute_composed_with_session<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>(
&'life0 self,
read: &'life1 ComposedLevel,
session_vars: &'life2 [(&'life3 str, &'life4 str)],
routing: ReadRouting,
) -> Pin<Box<dyn Future<Output = Result<Arc<Vec<JsonbValue>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
fn execute_composed_with_session<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>(
&'life0 self,
read: &'life1 ComposedLevel,
session_vars: &'life2 [(&'life3 str, &'life4 str)],
routing: ReadRouting,
) -> Pin<Box<dyn Future<Output = Result<Arc<Vec<JsonbValue>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
Forwarded to the inner adapter, uncached.
A composed read spans every view its embedded levels name, and this cache invalidates by the view a read targets. A cached tree would therefore survive a write to any view but the root’s — serving related rows that no longer exist. Caching it correctly needs invalidation by every view in the tree, which is a change to the cache, not to this forwarder.
Source§fn stream_row_query<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>(
&'life0 self,
view_name: &'life1 str,
columns: &'life2 [ColumnSpec],
where_sql: Option<&'life3 str>,
order_by: Option<&'life4 str>,
limit: Option<u32>,
offset: Option<u32>,
) -> Pin<Box<dyn Future<Output = Result<ColumnRowStream>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
fn stream_row_query<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>(
&'life0 self,
view_name: &'life1 str,
columns: &'life2 [ColumnSpec],
where_sql: Option<&'life3 str>,
order_by: Option<&'life4 str>,
limit: Option<u32>,
offset: Option<u32>,
) -> Pin<Box<dyn Future<Output = Result<ColumnRowStream>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
Forwarded to the inner adapter — see
stream_with_projection. The row-shaped
read was never cached in the first place.
Source§fn execute_row_query_with_session<'life0, 'life1, 'life2, 'life3, 'life4, 'life5, 'life6, 'life7, 'async_trait>(
&'life0 self,
view_name: &'life1 str,
columns: &'life2 [ColumnSpec],
where_sql: Option<&'life3 str>,
order_by: Option<&'life4 str>,
limit: Option<u32>,
offset: Option<u32>,
session_vars: &'life5 [(&'life6 str, &'life7 str)],
) -> Pin<Box<dyn Future<Output = Result<Vec<Vec<ColumnValue>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
'life5: 'async_trait,
'life6: 'async_trait,
'life7: 'async_trait,
fn execute_row_query_with_session<'life0, 'life1, 'life2, 'life3, 'life4, 'life5, 'life6, 'life7, 'async_trait>(
&'life0 self,
view_name: &'life1 str,
columns: &'life2 [ColumnSpec],
where_sql: Option<&'life3 str>,
order_by: Option<&'life4 str>,
limit: Option<u32>,
offset: Option<u32>,
session_vars: &'life5 [(&'life6 str, &'life7 str)],
) -> Pin<Box<dyn Future<Output = Result<Vec<Vec<ColumnValue>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
'life5: 'async_trait,
'life6: 'async_trait,
'life7: 'async_trait,
Forwarded to the inner adapter — see
stream_row_query.
Forwarding is load-bearing: the trait default drops the session variables,
so a wrapper that inherited it would leave every row read behind this cache
running without the set_config an RLS policy reads (#329). The shipped
binary always wraps its adapter in this one, so “the default is fine” is
never true here.
Source§fn stream_row_query_with_session<'life0, 'life1, 'life2, 'life3, 'life4, 'life5, 'life6, 'life7, 'async_trait>(
&'life0 self,
view_name: &'life1 str,
columns: &'life2 [ColumnSpec],
where_sql: Option<&'life3 str>,
order_by: Option<&'life4 str>,
limit: Option<u32>,
offset: Option<u32>,
session_vars: &'life5 [(&'life6 str, &'life7 str)],
) -> Pin<Box<dyn Future<Output = Result<ColumnRowStream>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
'life5: 'async_trait,
'life6: 'async_trait,
'life7: 'async_trait,
fn stream_row_query_with_session<'life0, 'life1, 'life2, 'life3, 'life4, 'life5, 'life6, 'life7, 'async_trait>(
&'life0 self,
view_name: &'life1 str,
columns: &'life2 [ColumnSpec],
where_sql: Option<&'life3 str>,
order_by: Option<&'life4 str>,
limit: Option<u32>,
offset: Option<u32>,
session_vars: &'life5 [(&'life6 str, &'life7 str)],
) -> Pin<Box<dyn Future<Output = Result<ColumnRowStream>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
'life5: 'async_trait,
'life6: 'async_trait,
'life7: 'async_trait,
Forwarded to the inner adapter — the streaming twin of
execute_row_query_with_session,
load-bearing for the same reason.
Source§fn execute_admin_sql<'life0, 'life1, 'async_trait>(
&'life0 self,
request: &'life1 AdminSqlRequest,
) -> Pin<Box<dyn Future<Output = Result<AdminSqlOutcome>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn execute_admin_sql<'life0, 'life1, 'async_trait>(
&'life0 self,
request: &'life1 AdminSqlRequest,
) -> Pin<Box<dyn Future<Output = Result<AdminSqlOutcome>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Forwarded to the inner adapter, and deliberately uncached (#962).
Forwarding is load-bearing for the same reason as
stream_with_projection, in the other
direction: the trait’s default here refuses, so a wrapper that inherited
it would turn every PostgreSQL deployment — which is every deployment,
since the shipped binary always wraps its adapter in this one — into a
server whose SQL console answers “unsupported”.
Uncached because the statement is not a query FraiseQL knows anything
about: it has no cache key, its result is a preview of a moment, and a
committed one changes rows other entries describe. Invalidation is
deliberately not attempted either — see the endpoint’s documentation for
why a committed console write is an operator action that should be
followed by cache/clear.
Source§fn result_cache_stats(&self) -> Option<ResultCacheStats>
fn result_cache_stats(&self) -> Option<ResultCacheStats>
The operator-visible snapshot of the cache that actually serves queries (#941).
None when the cache is disabled: an operator asking “what is cached?” of a
server running cache_enabled = false must be told there is no cache, not
handed a zero-entry snapshot of one.
Source§fn execute_with_projection<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>(
&'life0 self,
view: &'life1 str,
projection: Option<&'life2 SqlProjectionHint>,
where_clause: Option<&'life3 WhereClause>,
limit: Option<u32>,
offset: Option<u32>,
order_by: Option<&'life4 [OrderByClause]>,
) -> Pin<Box<dyn Future<Output = Result<Vec<JsonbValue>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
fn execute_with_projection<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>(
&'life0 self,
view: &'life1 str,
projection: Option<&'life2 SqlProjectionHint>,
where_clause: Option<&'life3 WhereClause>,
limit: Option<u32>,
offset: Option<u32>,
order_by: Option<&'life4 [OrderByClause]>,
) -> Pin<Box<dyn Future<Output = Result<Vec<JsonbValue>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
Source§fn execute_where_query<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
view: &'life1 str,
where_clause: Option<&'life2 WhereClause>,
limit: Option<u32>,
offset: Option<u32>,
order_by: Option<&'life3 [OrderByClause]>,
) -> Pin<Box<dyn Future<Output = Result<Vec<JsonbValue>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
fn execute_where_query<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
view: &'life1 str,
where_clause: Option<&'life2 WhereClause>,
limit: Option<u32>,
offset: Option<u32>,
order_by: Option<&'life3 [OrderByClause]>,
) -> Pin<Box<dyn Future<Output = Result<Vec<JsonbValue>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
Source§fn execute_with_projection_arc<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
request: &'life1 ProjectionRequest<'life2>,
) -> Pin<Box<dyn Future<Output = Result<Arc<Vec<JsonbValue>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn execute_with_projection_arc<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
request: &'life1 ProjectionRequest<'life2>,
) -> Pin<Box<dyn Future<Output = Result<Arc<Vec<JsonbValue>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Source§fn execute_where_query_arc<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
view: &'life1 str,
where_clause: Option<&'life2 WhereClause>,
limit: Option<u32>,
offset: Option<u32>,
order_by: Option<&'life3 [OrderByClause]>,
) -> Pin<Box<dyn Future<Output = Result<Arc<Vec<JsonbValue>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
fn execute_where_query_arc<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
view: &'life1 str,
where_clause: Option<&'life2 WhereClause>,
limit: Option<u32>,
offset: Option<u32>,
order_by: Option<&'life3 [OrderByClause]>,
) -> Pin<Box<dyn Future<Output = Result<Arc<Vec<JsonbValue>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
Source§fn database_type(&self) -> DatabaseType
fn database_type(&self) -> DatabaseType
Source§fn serves_reads_from_standbys(&self) -> bool
fn serves_reads_from_standbys(&self) -> bool
Source§fn health_check<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn health_check<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Source§fn pool_metrics(&self) -> PoolMetrics
fn pool_metrics(&self) -> PoolMetrics
Source§fn execute_raw_query<'life0, 'life1, 'async_trait>(
&'life0 self,
sql: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<Vec<HashMap<String, Value>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn execute_raw_query<'life0, 'life1, 'async_trait>(
&'life0 self,
sql: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<Vec<HashMap<String, Value>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§fn execute_parameterized_aggregate<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
sql: &'life1 str,
params: &'life2 [Value],
) -> Pin<Box<dyn Future<Output = Result<Vec<HashMap<String, Value>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn execute_parameterized_aggregate<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
sql: &'life1 str,
params: &'life2 [Value],
) -> Pin<Box<dyn Future<Output = Result<Vec<HashMap<String, Value>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Source§fn execute_parameterized_aggregate_with_session<'life0, 'life1, 'life2, 'life3, 'life4, 'life5, 'async_trait>(
&'life0 self,
sql: &'life1 str,
params: &'life2 [Value],
session_vars: &'life3 [(&'life4 str, &'life5 str)],
routing: ReadRouting,
) -> Pin<Box<dyn Future<Output = Result<Vec<HashMap<String, Value>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
'life5: 'async_trait,
fn execute_parameterized_aggregate_with_session<'life0, 'life1, 'life2, 'life3, 'life4, 'life5, 'async_trait>(
&'life0 self,
sql: &'life1 str,
params: &'life2 [Value],
session_vars: &'life3 [(&'life4 str, &'life5 str)],
routing: ReadRouting,
) -> Pin<Box<dyn Future<Output = Result<Vec<HashMap<String, Value>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
'life5: 'async_trait,
execute_parameterized_aggregate. Read moreSource§fn supports_composed_reads(&self) -> bool
fn supports_composed_reads(&self) -> bool
execute_composed_with_session — whether a
read can have related resources composed into it (the REST ?select= embed). Read moreSource§fn count_where_query<'life0, 'life1, 'life2, 'life3, 'life4, 'life5, 'async_trait>(
&'life0 self,
view: &'life1 str,
where_clause: Option<&'life2 WhereClause>,
session_vars: &'life3 [(&'life4 str, &'life5 str)],
routing: ReadRouting,
) -> Pin<Box<dyn Future<Output = Result<u64>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
'life5: 'async_trait,
fn count_where_query<'life0, 'life1, 'life2, 'life3, 'life4, 'life5, 'async_trait>(
&'life0 self,
view: &'life1 str,
where_clause: Option<&'life2 WhereClause>,
session_vars: &'life3 [(&'life4 str, &'life5 str)],
routing: ReadRouting,
) -> Pin<Box<dyn Future<Output = Result<u64>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
'life5: 'async_trait,
Source§fn execute_where_query_arc_with_session<'life0, 'life1, 'life2, 'life3, 'life4, 'life5, 'life6, 'async_trait>(
&'life0 self,
view: &'life1 str,
where_clause: Option<&'life2 WhereClause>,
limit: Option<u32>,
offset: Option<u32>,
order_by: Option<&'life3 [OrderByClause]>,
session_vars: &'life4 [(&'life5 str, &'life6 str)],
routing: ReadRouting,
) -> Pin<Box<dyn Future<Output = Result<Arc<Vec<JsonbValue>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
'life5: 'async_trait,
'life6: 'async_trait,
fn execute_where_query_arc_with_session<'life0, 'life1, 'life2, 'life3, 'life4, 'life5, 'life6, 'async_trait>(
&'life0 self,
view: &'life1 str,
where_clause: Option<&'life2 WhereClause>,
limit: Option<u32>,
offset: Option<u32>,
order_by: Option<&'life3 [OrderByClause]>,
session_vars: &'life4 [(&'life5 str, &'life6 str)],
routing: ReadRouting,
) -> Pin<Box<dyn Future<Output = Result<Arc<Vec<JsonbValue>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
'life5: 'async_trait,
'life6: 'async_trait,
execute_where_query_arc. Read moreSource§fn execute_with_projection_arc_with_session<'life0, 'life1, 'life2, 'life3, 'life4, 'life5, 'async_trait>(
&'life0 self,
request: &'life1 ProjectionRequest<'life2>,
session_vars: &'life3 [(&'life4 str, &'life5 str)],
routing: ReadRouting,
) -> Pin<Box<dyn Future<Output = Result<Arc<Vec<JsonbValue>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
'life5: 'async_trait,
fn execute_with_projection_arc_with_session<'life0, 'life1, 'life2, 'life3, 'life4, 'life5, 'async_trait>(
&'life0 self,
request: &'life1 ProjectionRequest<'life2>,
session_vars: &'life3 [(&'life4 str, &'life5 str)],
routing: ReadRouting,
) -> Pin<Box<dyn Future<Output = Result<Arc<Vec<JsonbValue>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
'life5: 'async_trait,
execute_with_projection_arc. Read moreSource§fn invalidate_views<'life0, 'life1, 'async_trait>(
&'life0 self,
views: &'life1 [ViewName],
) -> Pin<Box<dyn Future<Output = Result<u64>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn invalidate_views<'life0, 'life1, 'async_trait>(
&'life0 self,
views: &'life1 [ViewName],
) -> Pin<Box<dyn Future<Output = Result<u64>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§fn invalidate_by_entity<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
entity_type: &'life1 str,
entity_id: &'life2 str,
) -> Pin<Box<dyn Future<Output = Result<u64>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
fn invalidate_by_entity<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
entity_type: &'life1 str,
entity_id: &'life2 str,
) -> Pin<Box<dyn Future<Output = Result<u64>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Source§fn clear_result_cache<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Option<usize>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn clear_result_cache<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<Option<usize>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Source§fn query_stats<'life0, 'async_trait>(
&'life0 self,
limit: u32,
) -> Pin<Box<dyn Future<Output = Result<Vec<QueryStatEntry>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn query_stats<'life0, 'async_trait>(
&'life0 self,
limit: u32,
) -> Pin<Box<dyn Future<Output = Result<Vec<QueryStatEntry>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Source§fn query_stats_by_id<'life0, 'life1, 'async_trait>(
&'life0 self,
id: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<Option<QueryStatEntry>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn query_stats_by_id<'life0, 'life1, 'async_trait>(
&'life0 self,
id: &'life1 str,
) -> Pin<Box<dyn Future<Output = Result<Option<QueryStatEntry>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
Source§fn reset_query_stats<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
fn reset_query_stats<'life0, 'async_trait>(
&'life0 self,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
Source§fn on_schema_reload(&self)
fn on_schema_reload(&self)
Source§fn execute_row_query<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>(
&'life0 self,
view_name: &'life1 str,
columns: &'life2 [ColumnSpec],
where_sql: Option<&'life3 str>,
order_by: Option<&'life4 str>,
limit: Option<u32>,
offset: Option<u32>,
) -> Pin<Box<dyn Future<Output = Result<Vec<Vec<ColumnValue>>, FraiseQLError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
Self: 'async_trait,
fn execute_row_query<'life0, 'life1, 'life2, 'life3, 'life4, 'async_trait>(
&'life0 self,
view_name: &'life1 str,
columns: &'life2 [ColumnSpec],
where_sql: Option<&'life3 str>,
order_by: Option<&'life4 str>,
limit: Option<u32>,
offset: Option<u32>,
) -> Pin<Box<dyn Future<Output = Result<Vec<Vec<ColumnValue>>, FraiseQLError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
'life4: 'async_trait,
Self: 'async_trait,
Source§fn explain_query<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
_sql: &'life1 str,
_params: &'life2 [Value],
) -> Pin<Box<dyn Future<Output = Result<Value, FraiseQLError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait,
fn explain_query<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
_sql: &'life1 str,
_params: &'life2 [Value],
) -> Pin<Box<dyn Future<Output = Result<Value, FraiseQLError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait,
EXPLAIN on a SQL statement without executing it. Read moreSource§fn explain_where_query<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
_view: &'life1 str,
_where_clause: Option<&'life2 WhereClause>,
_limit: Option<u32>,
_offset: Option<u32>,
) -> Pin<Box<dyn Future<Output = Result<Value, FraiseQLError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait,
fn explain_where_query<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
_view: &'life1 str,
_where_clause: Option<&'life2 WhereClause>,
_limit: Option<u32>,
_offset: Option<u32>,
) -> Pin<Box<dyn Future<Output = Result<Value, FraiseQLError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
Self: 'async_trait,
EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON) against a view with the
same parameterized WHERE clause that execute_where_query would use. Read moreSource§impl<A: RelayDatabaseAdapter + DatabaseAdapter> RelayDatabaseAdapter for CachedDatabaseAdapter<A>
impl<A: RelayDatabaseAdapter + DatabaseAdapter> RelayDatabaseAdapter for CachedDatabaseAdapter<A>
Source§async fn execute_relay_page(
&self,
view: &str,
cursor_column: &str,
after: Option<CursorValue>,
before: Option<CursorValue>,
limit: u32,
forward: bool,
where_clause: Option<&WhereClause>,
order_by: Option<&[OrderByClause]>,
include_total_count: bool,
) -> Result<RelayPageResult>
async fn execute_relay_page( &self, view: &str, cursor_column: &str, after: Option<CursorValue>, before: Option<CursorValue>, limit: u32, forward: bool, where_clause: Option<&WhereClause>, order_by: Option<&[OrderByClause]>, include_total_count: bool, ) -> Result<RelayPageResult>
Source§async fn execute_relay_page_with_session(
&self,
view: &str,
cursor_column: &str,
after: Option<CursorValue>,
before: Option<CursorValue>,
limit: u32,
forward: bool,
where_clause: Option<&WhereClause>,
order_by: Option<&[OrderByClause]>,
include_total_count: bool,
session_vars: &[(&str, &str)],
routing: ReadRouting,
) -> Result<RelayPageResult>
async fn execute_relay_page_with_session( &self, view: &str, cursor_column: &str, after: Option<CursorValue>, before: Option<CursorValue>, limit: u32, forward: bool, where_clause: Option<&WhereClause>, order_by: Option<&[OrderByClause]>, include_total_count: bool, session_vars: &[(&str, &str)], routing: ReadRouting, ) -> Result<RelayPageResult>
execute_relay_page. Read moreSource§impl<A: Writer> Writer for CachedDatabaseAdapter<A>
A cache-wrapped adapter writes exactly when the adapter it wraps does. Writes are never
cached: every one passes through, in either mode (ruling AA 6).
impl<A: Writer> Writer for CachedDatabaseAdapter<A>
A cache-wrapped adapter writes exactly when the adapter it wraps does. Writes are never cached: every one passes through, in either mode (ruling AA 6).
Source§fn execute_write<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
request: &'life1 WriteRequest<'life2>,
gate: MutationRowGate<'life3>,
) -> Pin<Box<dyn Future<Output = Result<Vec<HashMap<String, Value>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
fn execute_write<'life0, 'life1, 'life2, 'life3, 'async_trait>(
&'life0 self,
request: &'life1 WriteRequest<'life2>,
gate: MutationRowGate<'life3>,
) -> Pin<Box<dyn Future<Output = Result<Vec<HashMap<String, Value>>>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
'life3: 'async_trait,
Source§fn bump_fact_table_versions<'life0, 'life1, 'async_trait>(
&'life0 self,
tables: &'life1 [String],
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
fn bump_fact_table_versions<'life0, 'life1, 'async_trait>(
&'life0 self,
tables: &'life1 [String],
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'async_trait>>where
Self: 'async_trait,
'life0: 'async_trait,
'life1: 'async_trait,
tables after a committed write that changed them, so
cached aggregates over them are invalidated by version key. Read more