Skip to main content

rupg_catalog/
stats.rs

1//! The extended statistics objects of `pg_statistic_ext`. They change as `CreateStatistics` and `AlterStatistics` of `statscmds.c` change them, with the same rows of `pg_depend`.
2
3use rupg_common::{Error, Object, Result, SqlState};
4
5use super::{Catalog, DepKind, ObjRef, PG_CLASS, PG_NAMESPACE};
6
7/// The OID of `pg_statistic_ext`.
8pub const PG_STATISTIC_EXT: u32 = 3381;
9
10/// An extended statistics object, one row of `pg_statistic_ext`.
11#[derive(Clone, Debug, PartialEq, Eq)]
12pub struct StatExt {
13    /// The OID. [`Catalog::create_stat_ext`] sets it.
14    pub oid: u32,
15    /// `stxname`: the name.
16    pub name: String,
17    /// `stxnamespace`: the OID of the schema.
18    pub namespace: u32,
19    /// `stxowner`: the OID of the owner role.
20    pub owner: u32,
21    /// `stxrelid`: the OID of the table.
22    pub relation: u32,
23    /// `stxkeys`: the numbers of the plain columns, in order.
24    pub keys: Vec<i16>,
25    /// `stxkind`: the kinds, `d` for ndistinct, `f` for dependencies, `m` for MCV and `e` for the expressions.
26    pub kinds: Vec<u8>,
27    /// `stxstattarget`: the statistics target, or `None` for the default.
28    pub target: Option<i16>,
29    /// `stxexprs`: the expressions in the form of `nodeToString` of a list, or `None` when there is no expression.
30    pub exprs: Option<String>,
31}
32
33impl Catalog {
34    /// `CreateStatistics`: a new statistics object, which gets the next OID. It depends on each key column, or on the table when there is no key column, on the objects `refs` that the expressions refer to, and on its schema.
35    ///
36    /// # Errors
37    ///
38    /// `23505` when the schema has a statistics object with the name.
39    pub fn create_stat_ext(&mut self, mut new: StatExt, refs: &[ObjRef]) -> Result<u32> {
40        let oid = self.new_oid();
41        if self.stat_ext_by_name(new.namespace, &new.name).is_some() {
42            return Err(Error::new(
43                SqlState::UNIQUE_VIOLATION,
44                "duplicate key value violates unique constraint \"pg_statistic_ext_name_index\"",
45            )
46            .with_detail(format!(
47                "Key (stxname, stxnamespace)=({}, {}) already exists.",
48                new.name, new.namespace
49            ))
50            .with_object(Object::Schema, "pg_catalog")
51            .with_object(Object::Table, "pg_statistic_ext")
52            .with_object(Object::Constraint, "pg_statistic_ext_name_index"));
53        }
54        new.oid = oid;
55        let me = ObjRef::new(PG_STATISTIC_EXT, oid);
56        for &number in &new.keys {
57            self.depend(me, ObjRef::column(new.relation, number), DepKind::Auto);
58        }
59        if new.keys.is_empty() {
60            self.depend(me, ObjRef::new(PG_CLASS, new.relation), DepKind::Auto);
61        }
62        self.depend_on_expr(me, refs, new.relation, DepKind::Auto);
63        self.depend(me, ObjRef::new(PG_NAMESPACE, new.namespace), DepKind::Normal);
64        self.stat_exts.insert(oid, new);
65        Ok(oid)
66    }
67
68    /// The statistics objects, in the order of their OIDs.
69    pub fn stat_exts(&self) -> impl Iterator<Item = &StatExt> {
70        self.stat_exts.values()
71    }
72
73    /// The statistics object with the OID.
74    pub fn stat_ext(&self, oid: u32) -> Option<&StatExt> {
75        self.stat_exts.get(&oid)
76    }
77
78    /// The statistics object with the name in the schema.
79    pub fn stat_ext_by_name(&self, namespace: u32, name: &str) -> Option<&StatExt> {
80        self.stat_exts.values().find(|s| s.namespace == namespace && s.name == name)
81    }
82
83    /// Changes the row of the statistics object.
84    pub fn alter_stat_ext(&mut self, oid: u32, change: impl FnOnce(&mut StatExt)) {
85        if let Some(stat) = self.stat_exts.get_mut(&oid) {
86            change(stat);
87        }
88    }
89
90    /// `AlterObjectNamespace_internal` for a statistics object: the move to the schema, with `changeDependencyFor` for the dependency on the schema.
91    pub fn set_stat_ext_namespace(&mut self, oid: u32, namespace: u32) {
92        let old =
93            self.stat_exts.get_mut(&oid).map(|s| std::mem::replace(&mut s.namespace, namespace));
94        if let Some(old) = old {
95            self.change_namespace_dependency(ObjRef::new(PG_STATISTIC_EXT, oid), old, namespace);
96        }
97    }
98}