use std::sync::Arc;
#[derive(Clone)]
pub struct SnapshotContext {
pub snapshot: super::snapshot::Snapshot,
pub manager: Arc<super::snapshot::SnapshotManager>,
pub own_xids: std::collections::HashSet<super::snapshot::Xid>,
pub requires_index_fallback: bool,
pub serializable_reader: Option<super::snapshot::Xid>,
}
pub fn record_serializable_read(
ctx: &SnapshotContext,
entity: &crate::storage::entity::UnifiedEntity,
) {
let Some(reader) = ctx.serializable_reader else {
return;
};
if !matches!(
&entity.kind,
crate::storage::entity::EntityKind::TableRow { .. }
) {
return;
}
ctx.manager
.record_serializable_read(reader, entity.kind.collection(), entity.logical_id());
}
#[inline]
pub fn visibility_check(ctx: &SnapshotContext, xmin: u64, xmax: u64) -> bool {
if xmin != 0 && ctx.manager.is_aborted(xmin) {
return false;
}
let effective_xmax = if xmax != 0 && ctx.manager.is_aborted(xmax) {
0
} else {
xmax
};
let own_xmin = xmin != 0 && ctx.own_xids.contains(&xmin);
let own_xmax = effective_xmax != 0 && ctx.own_xids.contains(&effective_xmax);
if own_xmax {
return false;
}
if own_xmin {
return true;
}
ctx.snapshot.sees(xmin, effective_xmax)
}
pub trait TableRowVersionStore {
fn table_row_versions_by_logical_id(
&self,
collection: &str,
logical_id: crate::storage::entity::EntityId,
) -> Vec<crate::storage::entity::UnifiedEntity>;
fn get_table_row_by_logical_id(
&self,
collection: &str,
logical_id: crate::storage::entity::EntityId,
) -> Option<crate::storage::entity::UnifiedEntity>;
}
pub fn resolve_logical_id(
store: &impl TableRowVersionStore,
collection: &str,
logical_id: crate::storage::entity::EntityId,
select_versions: bool,
mut visible: impl FnMut(&crate::storage::entity::UnifiedEntity) -> bool,
) -> Option<crate::storage::entity::UnifiedEntity> {
if select_versions {
return store
.table_row_versions_by_logical_id(collection, logical_id)
.into_iter()
.filter(|entity| visible(entity))
.max_by_key(|entity| entity.xmin);
}
store
.get_table_row_by_logical_id(collection, logical_id)
.filter(|entity| visible(entity))
}
pub trait FallibleTableRowVersionStore {
type Error;
fn try_get_table_row_by_logical_id(
&self,
collection: &str,
logical_id: crate::storage::entity::EntityId,
) -> Result<Option<crate::storage::entity::UnifiedEntity>, Self::Error>;
fn try_for_each_table_row_version(
&self,
collection: &str,
logical_id: crate::storage::entity::EntityId,
visit: impl FnMut(&crate::storage::entity::UnifiedEntity) -> bool,
) -> Result<(), Self::Error>;
}
pub fn try_resolve_logical_id<S: FallibleTableRowVersionStore>(
store: &S,
collection: &str,
logical_id: crate::storage::entity::EntityId,
select_versions: bool,
mut visible: impl FnMut(&crate::storage::entity::UnifiedEntity) -> bool,
) -> Result<Option<crate::storage::entity::UnifiedEntity>, S::Error> {
if !select_versions {
return store
.try_get_table_row_by_logical_id(collection, logical_id)
.map(|entity| entity.filter(|entity| visible(entity)));
}
let mut selected: Option<crate::storage::entity::UnifiedEntity> = None;
store.try_for_each_table_row_version(collection, logical_id, |entity| {
if visible(entity) && selected.as_ref().is_none_or(|old| entity.xmin >= old.xmin) {
selected = Some(entity.clone());
}
true
})?;
Ok(selected)
}