uqa-engine 0.4.0

Engine: schema-aware table store, catalog restore, transactions
//
// Unified Query Algebra
//
// Copyright (c) 2023-2026 Cognica, Inc.
//

//! Bind native sequence values to actual Engine session and catalog guards.
use crate::{
    Engine, NontransactionalSequenceValue, SessionLastSequenceReference, SessionSequenceValue,
    SessionStateSnapshot,
};
use std::collections::BTreeMap;
use uqa_core::RelationIdentity;
use uqa_execution::catalog::sequence::values::context::{
    SequenceCachesWrite, SequenceSessionRead, SequenceSessionWrite, SequenceStatesWrite,
    SequenceValueContext, SequenceValueOperation, SequenceValueRuntime, SequenceValueTransactions,
};
use uqa_sql::{catalog::sequence_functions::value_error::SequenceValueError, SQLError};
use uqa_storage::{PersistentStorageSession, StorageBackendResult};
struct SessionRead<'a>(parking_lot::RwLockReadGuard<'a, SessionStateSnapshot>);
struct SessionWrite<'a>(parking_lot::RwLockWriteGuard<'a, SessionStateSnapshot>);
impl SequenceSessionRead for SessionRead<'_> {
    fn currvals(&self) -> &BTreeMap<RelationIdentity, SessionSequenceValue> {
        &self.0.sequence_currvals
    }
    fn last(&self) -> Option<&SessionLastSequenceReference> {
        self.0.last_sequence.as_ref()
    }
}
impl SequenceSessionWrite for SessionWrite<'_> {
    fn currvals_mut(&mut self) -> &mut BTreeMap<RelationIdentity, SessionSequenceValue> {
        &mut self.0.sequence_currvals
    }
    fn last_mut(&mut self) -> &mut Option<SessionLastSequenceReference> {
        &mut self.0.last_sequence
    }
}
impl SequenceValueRuntime for Engine {
    fn cancellation(&self) -> &uqa_core::CancellationToken {
        &self.runtime.cancellation
    }
    fn states_write(&self) -> SequenceStatesWrite<'_> {
        Box::new(self.durable.sequences.write())
    }
    fn caches(&self) -> SequenceCachesWrite<'_> {
        Box::new(self.session.sequence_caches.lock())
    }
    fn session_read(&self) -> Box<dyn SequenceSessionRead + '_> {
        Box::new(SessionRead(self.session.state.read()))
    }
    fn session_write(&self) -> Box<dyn SequenceSessionWrite + '_> {
        Box::new(SessionWrite(self.session.state.write()))
    }
    fn current_transaction_is_read_only(&self) -> bool {
        Engine::current_transaction_is_read_only(self)
    }
    fn open_nontransactional_sequence_session(
        &self,
    ) -> StorageBackendResult<Option<PersistentStorageSession>> {
        Engine::open_independent_catalog_session(self, Some(&self.runtime.cancellation))
    }
    fn prepare_explicit_transaction_writer(&self) -> Result<(), SQLError> {
        self.prepare_transaction_writer().map(|_| ())
    }
    fn record_nontransactional_sequence_value(
        &self,
        definition_generation: [u8; 16],
        value: NontransactionalSequenceValue,
        defines_lastval: bool,
    ) {
        Engine::record_nontransactional_sequence_value(
            self,
            definition_generation,
            value,
            defines_lastval,
        );
    }
}

impl SequenceValueTransactions for Engine {
    fn with_value_transaction(
        &self,
        operation: SequenceValueOperation<'_>,
    ) -> Result<i64, SequenceValueError> {
        // Query workers already belong to the caller's transaction and must not reenter its thread-owned statement gate.
        if self.transaction_depth() != 0 {
            self.ensure_transaction_usable()?;
            operation()
        } else {
            self.transaction_with_error(|_| operation(), SequenceValueError::from)
        }
    }

    fn transaction_lock_mark(&self) -> u32 {
        self.session
            .transactions
            .lock()
            .first()
            .map_or(0, |frame| frame.begin_lock_mark)
    }
}

impl Engine {
    pub(crate) fn sequence_value_context(&self) -> SequenceValueContext<'_> {
        SequenceValueContext {
            locks: self,
            transactions: self,
            snapshots: self,
            privileges: self.sequence_privilege_inquiry(),
            runtime: self,
            storage: self.storage.catalog.as_deref(),
        }
    }

    fn with_sequence_value_session(
        &self,
        operation: impl FnOnce(SequenceValueContext<'_>) -> Result<i64, SequenceValueError>,
    ) -> Result<i64, String> {
        let _statement = self.runtime.statement_gate.lock();
        let outside_statement = self
            .runtime
            .sql_execution_depth
            .load(std::sync::atomic::Ordering::Relaxed)
            == 0
            && self.session.row_lock_statements.lock().is_empty();
        if outside_statement && self.transaction_depth() != 0 {
            self.ensure_transaction_usable()
                .map_err(|error| error.to_string())?;
            self.prepare_explicit_statement_snapshot(true)
                .map_err(|error| error.to_string())?;
        }
        operation(self.sequence_value_context()).map_err(|error| error.to_string())
    }

    pub fn nextval(&self, name: &str) -> Result<i64, String> {
        self.with_sequence_value_session(|values| values.nextval(name))
    }
    pub(crate) fn nextval_sql(&self, name: &str) -> Result<i64, SQLError> {
        self.sequence_value_context()
            .nextval(name)
            .map_err(SequenceValueError::into_sql_error)
    }
    pub fn currval(&self, name: &str) -> Result<i64, String> {
        self.with_sequence_value_session(|values| values.currval(name))
    }
    pub(crate) fn currval_sql(&self, name: &str) -> Result<i64, SQLError> {
        self.sequence_value_context()
            .currval(name)
            .map_err(SequenceValueError::into_sql_error)
    }
    pub fn lastval(&self) -> Result<i64, String> {
        self.with_sequence_value_session(|values| values.lastval())
    }
    pub(crate) fn lastval_sql(&self) -> Result<i64, SQLError> {
        self.sequence_value_context()
            .lastval()
            .map_err(SequenceValueError::into_sql_error)
    }
    pub fn setval(&self, name: &str, value: i64) -> Result<i64, String> {
        self.setval_with_is_called(name, value, true)
    }
    pub fn setval_with_is_called(
        &self,
        name: &str,
        value: i64,
        is_called: bool,
    ) -> Result<i64, String> {
        self.with_sequence_value_session(|values| values.setval(name, value, is_called))
    }
    pub(crate) fn setval_sql(
        &self,
        name: &str,
        value: i64,
        is_called: bool,
    ) -> Result<i64, SQLError> {
        self.sequence_value_context()
            .setval(name, value, is_called)
            .map_err(SequenceValueError::into_sql_error)
    }
}