use crate::catalog::services::{CatalogSession, CatalogSnapshotSource};
use crate::row_locks::shared_objects::SharedObjectLockSession;
use std::collections::BTreeSet;
use uqa_sql::schema::constraint_metadata::{
CatalogObjectAllocator, CatalogOidClass, ConstraintMetadataError, ConstraintMetadataResult,
};
#[derive(Clone, Copy)]
pub struct CatalogIdentityReservationContext<'a> {
pub catalog: &'a dyn CatalogSnapshotSource,
pub session: &'a dyn CatalogSession,
pub locks: &'a dyn SharedObjectLockSession,
}
impl<'a> CatalogIdentityReservationContext<'a> {
pub fn allocator(
self,
allocate: fn(&str) -> ConstraintMetadataResult<[u8; 16]>,
) -> ReservedCatalogIdentityAllocator<'a> {
ReservedCatalogIdentityAllocator {
context: self,
allocate,
assigned: BTreeSet::new(),
}
}
}
pub struct ReservedCatalogIdentityAllocator<'a> {
context: CatalogIdentityReservationContext<'a>,
allocate: fn(&str) -> ConstraintMetadataResult<[u8; 16]>,
assigned: BTreeSet<(CatalogOidClass, i64)>,
}
impl ReservedCatalogIdentityAllocator<'_> {
pub fn allocate_relation_oids(
&mut self,
kind: uqa_sql::catalog::relation_oids::RelationOidKind,
relation: &uqa_core::RelationIdentity,
) -> Result<uqa_sql::catalog::relation_oids::RelationCatalogOids, uqa_sql::SQLError> {
use uqa_sql::catalog::relation_oids::{RelationCatalogOids, RelationOidKind};
let temporary_schema = self
.context
.session
.relation_name_resolution()
.temporary_schema;
let mut allocate = |class| {
self.allocate_catalog_oid(class, &[0; 16])
.map_err(|error| uqa_sql::catalog::errors::storage_error("relation OID", &error))
.and_then(|oid| {
u32::try_from(oid).map_err(|_| {
uqa_sql::SQLError::Internal(format!("invalid {} OID {oid}", class.label()))
})
})
};
let relation_oid = allocate(CatalogOidClass::Relation)?;
uqa_sql::catalog::resolution::creation::ensure_relation_namespace_writable(
relation,
&temporary_schema,
)?;
let (array_type, row_type) = if kind == RelationOidKind::Sequence {
(None, None)
} else {
let array_type = allocate(CatalogOidClass::Type)?;
(Some(array_type), Some(allocate(CatalogOidClass::Type)?))
};
let rule = if kind == RelationOidKind::View {
Some(allocate(CatalogOidClass::Rewrite)?)
} else {
None
};
Ok(RelationCatalogOids {
relation: relation_oid,
row_type,
array_type,
rule,
})
}
pub fn allocate_catalog_oid_matching(
&mut self,
class: CatalogOidClass,
accept: impl Fn(i64) -> bool,
) -> Result<i64, uqa_sql::SQLError> {
let mut resolution = self.context.session.relation_name_resolution();
resolution.set_lookup_mode(crate::catalog::RelationLookupMode::Bound);
let locks = self.context.locks;
let oid = super::reserve_catalog_oid(
locks,
class.class_id(),
class.label(),
|oid| {
if self.assigned.contains(&(class, oid)) {
return Ok(true);
}
crate::catalog::projection::catalog_oid_in_use(
&self.context.catalog.current_catalog_snapshot(),
&resolution,
class,
oid,
)
},
|| loop {
let oid = i64::from(locks.next_catalog_oid()?);
if accept(oid) {
return Ok(oid);
}
},
)?;
self.assigned.insert((class, oid));
Ok(oid)
}
pub fn allocate_namespace_oid(
&mut self,
namespace_in_use: impl FnMut(i64) -> Result<bool, uqa_sql::SQLError>,
) -> Result<u32, uqa_sql::SQLError> {
let oid = super::reserve_new_catalog_oid(
self.context.locks,
NAMESPACE_CLASS_ID,
"schema",
namespace_in_use,
)?;
u32::try_from(oid)
.map_err(|_| uqa_sql::SQLError::Internal(format!("invalid schema OID {oid}")))
}
}
const NAMESPACE_CLASS_ID: u32 = 2615;
impl CatalogObjectAllocator for ReservedCatalogIdentityAllocator<'_> {
fn include_catalog_identity(
&mut self,
relation: &uqa_core::RelationIdentity,
class: CatalogOidClass,
identity: uqa_sql::ast::ConstraintCatalogIdentity,
) -> ConstraintMetadataResult<()> {
if !identity.is_valid() {
return Err(ConstraintMetadataError::Invalid(
"invalid supplied catalog identity".into(),
));
}
let mut resolution = self.context.session.relation_name_resolution();
resolution.set_lookup_mode(crate::catalog::RelationLookupMode::Bound);
let exists = || {
crate::catalog::projection::validate_catalog_identity_claim(
&self.context.catalog.current_catalog_snapshot(),
&resolution,
relation,
class,
identity,
)
};
if !exists().map_err(|error| ConstraintMetadataError::Execution(Box::new(error)))? {
super::reserve_catalog_oid(
self.context.locks,
class.class_id(),
class.label(),
|_| exists().map(|_| false),
|| Ok(identity.oid),
)
.map_err(|error| ConstraintMetadataError::Execution(Box::new(error)))?;
}
self.assigned.insert((class, identity.oid));
Ok(())
}
fn allocate_object_id(&mut self, kind: &str) -> ConstraintMetadataResult<[u8; 16]> {
(self.allocate)(kind)
}
fn allocate_catalog_oid(
&mut self,
class: CatalogOidClass,
_object_id: &[u8; 16],
) -> ConstraintMetadataResult<i64> {
let mut resolution = self.context.session.relation_name_resolution();
resolution.set_lookup_mode(crate::catalog::RelationLookupMode::Bound);
let oid = super::reserve_new_catalog_oid(
self.context.locks,
class.class_id(),
class.label(),
|oid| {
if self.assigned.contains(&(class, oid)) {
return Ok(true);
}
crate::catalog::projection::catalog_oid_in_use(
&self.context.catalog.current_catalog_snapshot(),
&resolution,
class,
oid,
)
},
)
.map_err(|error| ConstraintMetadataError::Execution(Box::new(error)))?;
self.assigned.insert((class, oid));
Ok(oid)
}
}
mod graphs;
pub use graphs::LabelShape;
mod temporary_namespaces;
#[cfg(test)]
mod tests;