Skip to main content

uqa_sql/catalog/
relation_oids.rs

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