Skip to main content

rupg_catalog/
set_schema.rs

1//! `ALTER ... SET SCHEMA` of a relation: the changes of `AlterTableNamespaceInternal`, `AlterRelationNamespaceInternal`, `AlterTypeNamespaceInternal`, `AlterIndexNamespaces`, `AlterSeqNamespaces` and `AlterConstraintNamespaces`.
2
3use rupg_common::Result;
4
5use super::{Catalog, DepKind, ObjRef, PG_CLASS, PG_NAMESPACE, RelKind, is_pinned, no_object};
6
7impl Catalog {
8    /// The relations that move with a relation to a new schema, as `AlterTableNamespaceInternal` moves them: the relation, then its indexes, then the sequences that its columns own.
9    pub fn moved_relations(&self, oid: u32) -> Vec<u32> {
10        let mut moved = vec![oid];
11        moved.extend(
12            self.relations
13                .values()
14                .filter(|r| r.index.as_ref().is_some_and(|i| i.table == oid))
15                .map(|r| r.oid),
16        );
17        moved.extend(self.owned_sequences(oid));
18        moved
19    }
20
21    /// `sequenceIsOwned`: the table whose column owns the sequence, by an automatic or an internal dependency.
22    pub fn sequence_owner(&self, sequence: u32) -> Option<u32> {
23        let me = ObjRef::new(PG_CLASS, sequence);
24        self.depends
25            .iter()
26            .find(|d| {
27                d.object == me
28                    && d.referenced.class == PG_CLASS
29                    && matches!(d.kind, DepKind::Auto | DepKind::Internal)
30            })
31            .map(|d| d.referenced.oid)
32    }
33
34    /// `AlterTableNamespaceInternal`: moves the relations of [`Catalog::moved_relations`] to the schema, with the row type and the array type of each one and the constraints of the table. `changeDependencyFor` changes the dependency on the schema of each relation that is not an index or a composite type, as the type of a composite type has the dependency. The caller checks the names in the new schema.
35    ///
36    /// # Errors
37    ///
38    /// An internal error for a relation that the catalog does not have.
39    pub fn set_relation_namespace(&mut self, oid: u32, namespace: u32) -> Result<()> {
40        let old = self.relation(oid).ok_or_else(|| no_object("relation", oid))?.namespace;
41        for moved in self.moved_relations(oid) {
42            let rel = self.relation_mut(moved)?;
43            rel.namespace = namespace;
44            let (row_type, rel_kind) = (rel.row_type, rel.kind);
45            let array = self.types.get(&row_type).map_or(0, |t| t.array);
46            for ty in [row_type, array] {
47                if let Some(ty) = self.types.get_mut(&ty) {
48                    ty.namespace = namespace;
49                }
50            }
51            if !matches!(rel_kind, RelKind::Index | RelKind::PartitionedIndex | RelKind::Composite)
52            {
53                self.change_namespace_dependency(ObjRef::new(PG_CLASS, moved), old, namespace);
54            }
55        }
56        for con in self.constraints.values_mut().filter(|c| c.relation == oid) {
57            con.namespace = namespace;
58        }
59        Ok(())
60    }
61
62    /// `changeDependencyFor`: the dependency of the object on the schema `old` becomes a dependency on `new`. A pinned schema has no dependency.
63    pub(super) fn change_namespace_dependency(&mut self, object: ObjRef, old: u32, new: u32) {
64        let (from, to) = (ObjRef::new(PG_NAMESPACE, old), ObjRef::new(PG_NAMESPACE, new));
65        if let Some(at) =
66            self.depends.iter().position(|d| d.object == object && d.referenced == from)
67        {
68            if is_pinned(to) {
69                self.depends.remove(at);
70            } else if let Some(d) = self.depends.get_mut(at) {
71                d.referenced = to;
72            }
73        } else if is_pinned(from) {
74            self.depend(object, to, DepKind::Normal);
75        }
76    }
77}