use crate::relational::catalog::{ForeignKey, QualifiedName};
use crate::relational::{Catalog, SqlValue};
use crate::sql::lock::{LockManager, LockMode, LockObject, LockScope, SessionId};
use crate::sql::row::RowSet;
use crate::sql::store::{LoadedTable, Mutation};
use chrono::{DateTime, Utc};
use std::cell::RefCell;
use std::collections::{BTreeSet, HashMap};
use std::sync::atomic::AtomicU32;
use std::sync::{Arc, Mutex};
#[derive(Debug, Clone, Default)]
pub struct ConstraintModes {
pub all_deferred: Option<bool>,
pub named: HashMap<(QualifiedName, String), bool>,
}
impl ConstraintModes {
pub fn is_deferred(&self, table: &QualifiedName, fk: &ForeignKey) -> bool {
if !fk.deferrable.is_deferrable() {
return false;
}
if let Some(v) = self.named.get(&(table.clone(), fk.name.clone())) {
return *v;
}
self.all_deferred
.unwrap_or_else(|| fk.deferrable.initially_deferred())
}
}
pub enum DeferredFkCheck {
Child {
child: QualifiedName,
fk: ForeignKey,
key: String,
key_vals: Vec<SqlValue>,
},
Referenced {
parent: QualifiedName,
child: QualifiedName,
fk: ForeignKey,
key: String,
key_vals: Vec<SqlValue>,
},
MatchFullNullMix {
child: QualifiedName,
fk: ForeignKey,
row_key: String,
},
}
impl DeferredFkCheck {
pub fn identity(&self) -> (&QualifiedName, &str) {
match self {
DeferredFkCheck::Child { child, fk, .. } => (child, fk.name.as_str()),
DeferredFkCheck::Referenced { child, fk, .. } => (child, fk.name.as_str()),
DeferredFkCheck::MatchFullNullMix { child, fk, .. } => (child, fk.name.as_str()),
}
}
}
pub struct Frame<'a> {
pub schema: &'a crate::sql::row::RowSchema,
pub row: &'a crate::sql::row::Tuple,
}
pub struct Exec {
pub catalog: Catalog,
pub tables: HashMap<QualifiedName, LoadedTable>,
pub params: Vec<SqlValue>,
pub now: DateTime<Utc>,
pub mutations: Arc<Mutex<Vec<Mutation>>>,
pub catalog_dirty: bool,
pub cte: HashMap<String, RowSet>,
pub database: String,
pub username: String,
pub locks: Arc<LockManager>,
pub session_id: SessionId,
pub pending_locks: RefCell<Vec<(LockObject, LockMode, LockScope)>>,
pub for_update_filter: Option<(QualifiedName, BTreeSet<String>)>,
pub vars: RefCell<HashMap<String, String>>,
pub rls: crate::sql::rls::RlsContext,
pub udf_depth: Arc<AtomicU32>,
pub trigger_depth: Arc<AtomicU32>,
pub constraint_modes: Option<ConstraintModes>,
pub deferred_checks: RefCell<Vec<DeferredFkCheck>>,
pub deferred_triggers: Vec<crate::sql::trigger::DeferredTriggerFiring>,
}
impl Exec {
#[allow(clippy::too_many_arguments)]
pub fn new(
catalog: Catalog,
tables: HashMap<QualifiedName, LoadedTable>,
params: Vec<SqlValue>,
now: DateTime<Utc>,
database: String,
username: String,
locks: Arc<LockManager>,
session_id: SessionId,
) -> Self {
Self {
catalog,
tables,
params,
now,
mutations: Arc::new(Mutex::new(Vec::new())),
catalog_dirty: false,
cte: HashMap::new(),
database,
username,
locks,
session_id,
pending_locks: RefCell::new(Vec::new()),
for_update_filter: None,
vars: RefCell::new(HashMap::new()),
rls: crate::sql::rls::RlsContext::default(),
udf_depth: Arc::new(AtomicU32::new(0)),
trigger_depth: Arc::new(AtomicU32::new(0)),
constraint_modes: None,
deferred_checks: RefCell::new(Vec::new()),
deferred_triggers: Vec::new(),
}
}
pub fn record_pending(&self, object: LockObject, mode: LockMode, scope: LockScope) {
self.pending_locks.borrow_mut().push((object, mode, scope));
}
pub fn try_lock(&self, object: LockObject, mode: LockMode, scope: LockScope) -> bool {
self.locks.try_acquire(self.session_id, object, mode, scope)
}
pub fn unlock_one(&self, object: LockObject, mode: LockMode) -> bool {
self.locks.release_one(self.session_id, &object, mode)
}
pub fn param(&self, placeholder: &str) -> crate::sql::error::Result<SqlValue> {
let idx = placeholder
.trim_start_matches('$')
.parse::<usize>()
.map_err(|_| {
crate::sql::error::SqlError::Internal(format!("invalid placeholder {placeholder}"))
})?;
self.params
.get(idx.wrapping_sub(1))
.cloned()
.ok_or_else(|| {
crate::sql::error::SqlError::InvalidParameter(format!(
"there is no parameter {placeholder}"
))
})
}
}