Skip to main content

rupg_catalog/
opclass.rs

1//! The operator families of `pg_opfamily`, the operator classes of `pg_opclass` and their members in `pg_amop` and `pg_amproc`, as `CreateOpFamily`, `DefineOpClass`, `storeOperators` and `storeProcedures` of `opclasscmds.c` make them, with the same rows of `pg_depend`.
2
3use super::{
4    Catalog, DepKind, ObjRef, PG_NAMESPACE, PG_OPCLASS, PG_OPERATOR, PG_PROC, PG_TYPE, is_pinned,
5};
6
7/// The OID of `pg_am`.
8pub const PG_AM: u32 = 2601;
9/// The OID of `pg_amop`.
10pub const PG_AMOP: u32 = 2602;
11/// The OID of `pg_amproc`.
12pub const PG_AMPROC: u32 = 2603;
13/// The OID of `pg_opfamily`.
14pub const PG_OPFAMILY: u32 = 2753;
15
16/// An operator family, one row of `pg_opfamily`.
17#[derive(Clone, Debug, PartialEq, Eq)]
18pub struct OpFamily {
19    /// The OID. [`Catalog::create_opfamily`] sets it.
20    pub oid: u32,
21    /// `opfmethod`: the OID of the access method.
22    pub method: u32,
23    /// The name.
24    pub name: String,
25    /// The OID of the schema.
26    pub namespace: u32,
27    /// The OID of the owner role.
28    pub owner: u32,
29}
30
31/// An operator class, one row of `pg_opclass`.
32#[derive(Clone, Debug, PartialEq, Eq)]
33pub struct OpClass {
34    /// The OID. [`Catalog::create_opclass`] sets it.
35    pub oid: u32,
36    /// `opcmethod`: the OID of the access method.
37    pub method: u32,
38    /// The name.
39    pub name: String,
40    /// The OID of the schema.
41    pub namespace: u32,
42    /// The OID of the owner role.
43    pub owner: u32,
44    /// `opcfamily`: the OID of the operator family.
45    pub family: u32,
46    /// `opcintype`: the type that the class indexes.
47    pub intype: u32,
48    /// `opcdefault`: true for the default class of the type and the access method.
49    pub default: bool,
50    /// `opckeytype`: the type of the data in the index, or 0 for the type that the class indexes.
51    pub keytype: u32,
52}
53
54/// An operator of an operator family, one row of `pg_amop`.
55#[derive(Clone, Debug, PartialEq, Eq)]
56pub struct AmOp {
57    /// The OID. [`Catalog::add_amop`] sets it.
58    pub oid: u32,
59    /// `amopfamily`: the OID of the operator family.
60    pub family: u32,
61    /// `amoplefttype`.
62    pub left: u32,
63    /// `amoprighttype`.
64    pub right: u32,
65    /// `amopstrategy`: the number of the operator in the family.
66    pub strategy: i16,
67    /// `amoppurpose`: `s` for search or `o` for ordering.
68    pub purpose: u8,
69    /// `amopopr`: the OID of the operator.
70    pub operator: u32,
71    /// `amopmethod`: the OID of the access method.
72    pub method: u32,
73    /// `amopsortfamily`: the B-tree family of the order of an ordering operator, or 0.
74    pub sortfamily: u32,
75}
76
77/// A support function of an operator family, one row of `pg_amproc`.
78#[derive(Clone, Debug, PartialEq, Eq)]
79pub struct AmProc {
80    /// The OID. [`Catalog::add_amproc`] sets it.
81    pub oid: u32,
82    /// `amprocfamily`: the OID of the operator family.
83    pub family: u32,
84    /// `amproclefttype`.
85    pub left: u32,
86    /// `amprocrighttype`.
87    pub right: u32,
88    /// `amprocnum`: the number of the function in the family.
89    pub number: i16,
90    /// `amproc`: the OID of the function.
91    pub function: u32,
92}
93
94/// The dependency facts of a member of a family, the fields `ref_is_hard`, `ref_is_family` and `refobjid` of `OpFamilyMember`.
95#[derive(Clone, Debug, PartialEq, Eq)]
96pub struct MemberLink {
97    /// `ref_is_hard`: the member is a part of `owner`, and its operator or function needs `CASCADE` to go.
98    pub hard: bool,
99    /// The operator class or the operator family that the member depends on.
100    pub owner: ObjRef,
101    /// The input types of the operator or the function. A type of the member that is one of them needs no dependency, as `typeDepNeeded` finds.
102    pub inputs: Vec<u32>,
103}
104
105impl MemberLink {
106    /// The kind of the dependencies on the operator or the function and on the types: `Normal` for a hard member, else `Auto`.
107    fn kind(&self) -> DepKind {
108        if self.hard { DepKind::Normal } else { DepKind::Auto }
109    }
110
111    /// `typeDepNeeded`.
112    fn needs_type(&self, ty: u32) -> bool {
113        !is_pinned(ObjRef::new(PG_TYPE, ty)) && !self.inputs.contains(&ty)
114    }
115}
116
117impl Catalog {
118    /// `CreateOpFamily`: a new operator family, which gets the next OID. It depends on its access method and its schema.
119    pub fn create_opfamily(&mut self, mut new: OpFamily) -> u32 {
120        let oid = self.new_oid();
121        new.oid = oid;
122        let me = ObjRef::new(PG_OPFAMILY, oid);
123        self.depend(me, ObjRef::new(PG_AM, new.method), DepKind::Auto);
124        self.depend(me, ObjRef::new(PG_NAMESPACE, new.namespace), DepKind::Normal);
125        self.opfamilies.insert(oid, new);
126        oid
127    }
128
129    /// The row of `pg_opclass` of `DefineOpClass`, which gets the next OID. The caller adds the members and then runs [`Catalog::opclass_depends`], in the order of `DefineOpClass`.
130    pub fn create_opclass(&mut self, mut new: OpClass) -> u32 {
131        let oid = self.new_oid();
132        new.oid = oid;
133        self.opclasses.insert(oid, new);
134        oid
135    }
136
137    /// The dependencies of `DefineOpClass` for the class: on its schema, its family, its type and its storage type.
138    pub fn opclass_depends(&mut self, oid: u32) {
139        let Some(class) = self.opclasses.get(&oid) else { return };
140        let me = ObjRef::new(PG_OPCLASS, oid);
141        let mut refs = vec![
142            (ObjRef::new(PG_NAMESPACE, class.namespace), DepKind::Normal),
143            (ObjRef::new(PG_OPFAMILY, class.family), DepKind::Auto),
144            (ObjRef::new(PG_TYPE, class.intype), DepKind::Normal),
145        ];
146        if class.keytype != 0 {
147            refs.push((ObjRef::new(PG_TYPE, class.keytype), DepKind::Normal));
148        }
149        for (referenced, kind) in refs {
150            self.depend(me, referenced, kind);
151        }
152    }
153
154    /// The part of `storeOperators` for one operator: the row of `pg_amop`, which gets the next OID, and its dependencies.
155    pub fn add_amop(&mut self, mut new: AmOp, link: &MemberLink) -> u32 {
156        let oid = self.new_oid();
157        new.oid = oid;
158        let me = ObjRef::new(PG_AMOP, oid);
159        self.depend(me, ObjRef::new(PG_OPERATOR, new.operator), link.kind());
160        self.member_depends(me, link, new.left, new.right);
161        if new.sortfamily != 0 {
162            self.depend(me, ObjRef::new(PG_OPFAMILY, new.sortfamily), link.kind());
163        }
164        self.amops.insert(oid, new);
165        oid
166    }
167
168    /// The part of `storeProcedures` for one function: the row of `pg_amproc`, which gets the next OID, and its dependencies.
169    pub fn add_amproc(&mut self, mut new: AmProc, link: &MemberLink) -> u32 {
170        let oid = self.new_oid();
171        new.oid = oid;
172        let me = ObjRef::new(PG_AMPROC, oid);
173        self.depend(me, ObjRef::new(PG_PROC, new.function), link.kind());
174        self.member_depends(me, link, new.left, new.right);
175        self.amprocs.insert(oid, new);
176        oid
177    }
178
179    /// The dependencies of a member on its class or family and on its types.
180    fn member_depends(&mut self, me: ObjRef, link: &MemberLink, left: u32, right: u32) {
181        let owner = if link.hard { DepKind::Internal } else { DepKind::Auto };
182        self.depend(me, link.owner, owner);
183        if link.needs_type(left) {
184            self.depend(me, ObjRef::new(PG_TYPE, left), link.kind());
185        }
186        if left != right && link.needs_type(right) {
187            self.depend(me, ObjRef::new(PG_TYPE, right), link.kind());
188        }
189    }
190
191    /// Changes the row of the operator family.
192    pub fn alter_opfamily(&mut self, oid: u32, change: impl FnOnce(&mut OpFamily)) {
193        if let Some(family) = self.opfamilies.get_mut(&oid) {
194            change(family);
195        }
196    }
197
198    /// Changes the row of the operator class.
199    pub fn alter_opclass(&mut self, oid: u32, change: impl FnOnce(&mut OpClass)) {
200        if let Some(class) = self.opclasses.get_mut(&oid) {
201            change(class);
202        }
203    }
204
205    /// `AlterObjectNamespace_internal` for an operator family, or for an operator class when `family` is false: the move to the schema, with `changeDependencyFor` for the dependency on the schema.
206    pub fn set_opcf_namespace(&mut self, family: bool, oid: u32, namespace: u32) {
207        let (class, old) = if family {
208            let row = self.opfamilies.get_mut(&oid);
209            (PG_OPFAMILY, row.map(|f| std::mem::replace(&mut f.namespace, namespace)))
210        } else {
211            let row = self.opclasses.get_mut(&oid);
212            (PG_OPCLASS, row.map(|c| std::mem::replace(&mut c.namespace, namespace)))
213        };
214        if let Some(old) = old {
215            self.change_namespace_dependency(ObjRef::new(class, oid), old, namespace);
216        }
217    }
218
219    /// The operator families, in the order of their OIDs.
220    pub fn opfamilies(&self) -> impl Iterator<Item = &OpFamily> {
221        self.opfamilies.values()
222    }
223
224    /// The operator family with the OID.
225    pub fn opfamily(&self, oid: u32) -> Option<&OpFamily> {
226        self.opfamilies.get(&oid)
227    }
228
229    /// The operator classes, in the order of their OIDs.
230    pub fn opclasses(&self) -> impl Iterator<Item = &OpClass> {
231        self.opclasses.values()
232    }
233
234    /// The operator class with the OID.
235    pub fn opclass(&self, oid: u32) -> Option<&OpClass> {
236        self.opclasses.get(&oid)
237    }
238
239    /// The rows of `pg_amop`, in the order of their OIDs.
240    pub fn amops(&self) -> impl Iterator<Item = &AmOp> {
241        self.amops.values()
242    }
243
244    /// The row of `pg_amop` with the OID.
245    pub fn amop(&self, oid: u32) -> Option<&AmOp> {
246        self.amops.get(&oid)
247    }
248
249    /// The rows of `pg_amproc`, in the order of their OIDs.
250    pub fn amprocs(&self) -> impl Iterator<Item = &AmProc> {
251        self.amprocs.values()
252    }
253
254    /// The row of `pg_amproc` with the OID.
255    pub fn amproc(&self, oid: u32) -> Option<&AmProc> {
256        self.amprocs.get(&oid)
257    }
258}