#![deny(
clippy::unwrap_used,
clippy::too_many_lines,
clippy::cast_possible_truncation
)]
use super::entity::{EntityData, EntityId, EntityKind, RefType, UnifiedEntity};
use super::error::StoreError;
use super::manager::ManagerConfig;
use super::metadata::Metadata;
use super::resident_store::ResidentStore;
use super::snapshot::{visit_snapshot, SnapshotVisitor};
use std::collections::HashSet;
impl ResidentStore {
pub fn from_snapshot(
bytes: &[u8],
manager_config: &ManagerConfig,
auto_index_refs: bool,
max_cross_refs: usize,
) -> Result<(Self, u32), String> {
struct Restore<'a> {
store: &'a ResidentStore,
manager_config: &'a ManagerConfig,
auto_index_refs: bool,
max_cross_refs: usize,
}
impl SnapshotVisitor for Restore<'_> {
fn collection(&mut self, name: &str) -> Result<(), String> {
self.store
.get_or_create_collection(name, self.manager_config);
Ok(())
}
fn entity(
&mut self,
collection: &str,
entity: UnifiedEntity,
metadata: Option<Metadata>,
) -> Result<(), String> {
let id = entity.id;
self.store
.restore_snapshot_entity(
collection,
entity,
self.manager_config,
self.auto_index_refs,
)
.map_err(|err| err.to_string())?;
if let Some(metadata) = metadata {
if let Some(manager) = self.store.get_collection(collection) {
let _ = manager.set_metadata(id, metadata);
}
}
Ok(())
}
fn cross_ref(
&mut self,
cross_ref: reddb_file::NativeDumpCrossRef,
) -> Result<(), String> {
let source_collection = self
.store
.get_any(EntityId::new(cross_ref.source_id))
.map(|(name, _)| name)
.unwrap_or_else(|| cross_ref.target_collection.clone());
let _ = self.store.add_cross_ref_in_memory(
&source_collection,
EntityId::new(cross_ref.source_id),
&cross_ref.target_collection,
EntityId::new(cross_ref.target_id),
RefType::from_byte(cross_ref.ref_type),
1.0,
self.max_cross_refs,
|_| Ok(()),
);
Ok(())
}
fn auxiliary(&mut self, bytes: &[u8]) -> Result<(), String> {
self.store.set_aux_metadata(bytes.to_vec());
Ok(())
}
}
let store = Self::new();
let version = visit_snapshot(
bytes,
&mut Restore {
store: &store,
manager_config,
auto_index_refs,
max_cross_refs,
},
)?;
Ok((store, version))
}
fn restore_snapshot_entity(
&self,
collection: &str,
mut entity: UnifiedEntity,
manager_config: &ManagerConfig,
auto_index_refs: bool,
) -> Result<EntityId, StoreError> {
let manager = self.get_or_create_collection(collection, manager_config);
if entity.id.raw() == 0 {
entity.id = self.next_entity_id();
} else {
self.register_entity_id(entity.id);
}
if let EntityKind::TableRow { ref mut row_id, .. } = entity.kind {
if *row_id == 0 {
*row_id = manager.next_row_id();
} else {
manager.register_row_id(*row_id);
}
}
entity.ensure_table_logical_id();
self.context_index.index_entity(collection, &entity);
if auto_index_refs {
self.index_cross_refs(&entity, collection)?;
}
let graph_label = match &entity.kind {
EntityKind::GraphNode(node) => Some(node.label.clone()),
_ => None,
};
let id = manager.restore_snapshot_entity(entity)?;
if let Some(label) = graph_label {
self.update_graph_label_index(collection, &label, id);
}
Ok(id)
}
}
impl ResidentStore {
#[allow(clippy::cast_possible_truncation)] pub fn snapshot_bytes(
&self,
pending_versions: &HashSet<EntityId>,
excluded_xids: &HashSet<u64>,
before_footer: impl FnOnce(),
) -> Vec<u8> {
let mut buf = Vec::new();
buf.extend_from_slice(&reddb_file::encode_native_store_header(
reddb_file::STORE_VERSION_CURRENT,
));
let collections = self.collections.read();
reddb_file::encode_native_dump_count(&mut buf, collections.len() as u32);
let fv = reddb_file::STORE_VERSION_CURRENT;
for (name, manager) in collections.iter() {
let entities = manager.query_all(|entity| {
if pending_versions.contains(&entity.id) {
return false;
}
!matches!(entity.data, EntityData::Row(_) | EntityData::Vector(_))
|| !excluded_xids.contains(&entity.xmin)
});
reddb_file::encode_native_dump_collection_header(&mut buf, name, entities.len() as u32);
for mut entity in entities {
if matches!(entity.data, EntityData::Row(_)) && excluded_xids.contains(&entity.xmax)
{
entity.xmax = 0;
}
let metadata = manager.get_metadata(entity.id);
let record =
super::metadata_codec::serialize_entity_record(&entity, metadata.as_ref(), fv);
reddb_file::encode_native_dump_entity_record(&mut buf, &record);
}
}
let cross_refs = self.cross_refs.read();
let total_refs: usize = cross_refs.values().map(|v| v.len()).sum();
reddb_file::encode_native_dump_count(&mut buf, total_refs as u32);
for (source_id, refs) in cross_refs.iter() {
for (target_id, ref_type, collection) in refs {
reddb_file::encode_native_dump_cross_ref(
&mut buf,
source_id.raw(),
target_id.raw(),
ref_type.to_byte(),
collection,
);
}
}
{
let aux = self.aux_metadata.read();
reddb_file::encode_native_dump_entity_record(&mut buf, &aux);
}
before_footer();
reddb_file::append_native_store_crc32_footer(&mut buf);
buf
}
}