1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
//
// Unified Query Algebra
//
// Copyright (c) 2023-2026 Cognica, Inc.
//
//! The public OIDs of one relation incarnation: its `pg_class` row, the row type and array type `heap_create_with_catalog` creates with it, and a view's `_RETURN` rule.
use serde::{Deserialize, Serialize};
/// The kinds of relation whose OIDs differ in which companion objects exist.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RelationOidKind {
/// Tables and partitioned tables: a row type and its array type.
Table,
/// Views and materialized views: a row type, its array type and the `_RETURN` rule.
View,
/// Foreign tables: a row type and its array type.
ForeignTable,
/// Sequences have no row type.
Sequence,
/// Standalone composite types: the composite relation, its row type and the row type's array type.
CompositeType,
}
/// Relation OIDs as the database's counter allocated them, in `PostgreSQL`'s order: the relation, the array type, the row type and then a view's rule. Relations created before OIDs were recorded derive them from their identity instead, and never change them.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct RelationCatalogOids {
pub relation: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub row_type: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub array_type: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rule: Option<u32>,
}
impl RelationCatalogOids {
/// The OIDs a relation created before OIDs were recorded derives from its identity. Such relations have no recorded array type.
#[must_use]
pub fn legacy(kind: RelationOidKind, object_id: &[u8; 16]) -> Self {
let derive = |label| {
u32::try_from(super::oids::stable_object_oid(label, object_id))
.expect("derived catalog OIDs fit in u32")
};
Self {
relation: derive("relation"),
row_type: (kind != RelationOidKind::Sequence).then(|| derive("rowtype")),
array_type: None,
rule: (kind == RelationOidKind::View).then(|| derive("view-rule")),
}
}
/// The row type OID `pg_class.reltype` reports: zero for relations without one.
#[must_use]
pub fn reltype(&self) -> u32 {
self.row_type.unwrap_or(0)
}
/// Every OID the relation claims, for occupancy checks.
pub fn claimed(&self) -> impl Iterator<Item = u32> {
[
Some(self.relation),
self.row_type,
self.array_type,
self.rule,
]
.into_iter()
.flatten()
}
/// Whether the OIDs are well formed for the kind: every recorded OID is a user OID, and exactly the companions the kind has are present.
#[must_use]
pub fn is_valid_for(&self, kind: RelationOidKind) -> bool {
let user = |oid: u32| oid >= super::oids::FIRST_NORMAL_OBJECT_ID;
let has_row_type = kind != RelationOidKind::Sequence;
user(self.relation)
&& self.row_type.is_some() == has_row_type
&& self.array_type.is_some() == has_row_type
&& self.rule.is_some() == (kind == RelationOidKind::View)
&& self.claimed().all(user)
}
}
#[cfg(test)]
mod tests;