use super::{Engine, SQLError, StorageSavepointId, TransactionFrame, TransactionRowChange};
use crate::row_locks::PendingRowChangeKind;
use uqa_execution::query::document_changes::{
ChangedIdentities, ChangedIdentitiesView, RelationGeneration,
};
use uqa_execution::storage_errors::storage_error;
use uqa_storage::read_control::StorageReadControl;
use uqa_storage::StorageBackendResult;
fn note(
identities: &mut ChangedIdentities,
change: &TransactionRowChange,
control: &StorageReadControl,
) -> StorageBackendResult<()> {
let id = change.pending.key.doc_id;
match change.pending.kind {
PendingRowChangeKind::Insert => {
identities.note(&change.source_generation, id, false, true, control)
}
PendingRowChangeKind::Update => {
identities.note(&change.source_generation, id, true, true, control)
}
PendingRowChangeKind::Delete => {
identities.note(&change.source_generation, id, true, false, control)
}
PendingRowChangeKind::Rewrite(successor) => {
identities.note(&change.source_generation, id, true, false, control)?;
match &change.successor_generation {
Some(generation) => {
identities.note(generation, successor.doc_id, false, true, control)
}
None => Ok(()),
}
}
}
}
pub(super) fn follow_identities(
stack: &mut [TransactionFrame],
operation: impl FnOnce(&mut ChangedIdentities) -> StorageBackendResult<bool>,
) {
let Some(outer) = stack.first_mut() else {
return;
};
if let Some(identities) = outer.fixed_identities.as_mut() {
if !operation(identities).unwrap_or(false) {
outer.fixed_identities = None;
}
}
}
pub(super) fn save_identities(stack: &mut [TransactionFrame], id: StorageSavepointId) {
follow_identities(stack, |identities| identities.savepoint(id).map(|()| true));
}
pub(super) fn rollback_identities(stack: &mut [TransactionFrame], id: StorageSavepointId) {
follow_identities(stack, |identities| {
Ok(identities.rollback_to(id)? && identities.release(id)?)
});
}
impl Engine {
pub(super) fn follow_row_change(
&self,
stack: &mut [TransactionFrame],
change: &TransactionRowChange,
) -> Result<(), SQLError> {
if stack
.first()
.is_none_or(|outer| outer.fixed_identities.is_none())
{
return Ok(());
}
let control = self.query_retention_control()?;
follow_identities(stack, |identities| {
note(identities, change, &control).map(|()| true)
});
Ok(())
}
pub(crate) fn fixed_identities_view(
&self,
generation: &RelationGeneration,
) -> Result<Option<ChangedIdentitiesView>, SQLError> {
let control = self.query_retention_control()?;
let mut stack = self.session.transactions.lock();
if stack.is_empty() {
return Ok(None);
}
if stack[0].fixed_identities.is_none() {
let mut identities = ChangedIdentities::new(&control)
.map_err(|error| storage_error("index transaction row changes", &error))?;
for change in stack.iter().flat_map(|frame| frame.row_changes.iter()) {
note(&mut identities, change, &control)
.map_err(|error| storage_error("index transaction row changes", &error))?;
}
stack[0].fixed_identities = Some(identities);
}
stack[0]
.fixed_identities
.as_mut()
.expect("an indexed transaction")
.view(generation)
.map_err(|error| storage_error("view transaction row changes", &error))
}
}