use std::sync::atomic::Ordering;
use uqa_sql::SQLError;
use crate::{Engine, TableState};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum DocumentCountChange {
Added,
Removed,
Unchanged,
}
impl TableState {
pub(crate) fn apply_document_count_change(&self, change: DocumentCountChange) {
if self.doc_count_dirty.load(Ordering::Acquire) {
return;
}
let update = |count: u64| match change {
DocumentCountChange::Added => count.checked_add(1),
DocumentCountChange::Removed => count.checked_sub(1),
DocumentCountChange::Unchanged => Some(count),
};
if self
.doc_count_cache
.fetch_update(Ordering::AcqRel, Ordering::Acquire, update)
.is_err()
{
self.discard_document_count();
}
}
pub(crate) fn discard_document_count(&self) {
self.doc_count_dirty.store(true, Ordering::Release);
}
pub(crate) fn stored_document_count(&self) -> Result<u64, SQLError> {
if !self.doc_count_dirty.load(Ordering::Acquire) {
return Ok(self.doc_count_cache.load(Ordering::Acquire));
}
let count = self
.document_store
.read()
.len()
.map_err(|error| SQLError::Internal(format!("read document count: {error}")))?;
let count = u64::try_from(count)
.map_err(|_| SQLError::Internal("document count exceeds u64".into()))?;
self.doc_count_cache.store(count, Ordering::Release);
self.doc_count_dirty.store(false, Ordering::Release);
Ok(count)
}
}
impl Engine {
pub(crate) fn discard_persistent_document_counts(&self) {
if self.storage.backend.is_none() {
return;
}
for table in self.storage.tables.read().values() {
if table.persistence != uqa_sql::ast::RelationPersistence::Temporary {
table.discard_document_count();
}
}
}
}
#[cfg(test)]
mod tests;