Skip to main content

uqa_sql/catalog/index/
identity.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Validate stored index incarnations independently of their SQL names.
8
9use super::IndexCatalogIdentity;
10use crate::schema::constraint_metadata::{
11    CatalogObjectAllocator, CatalogOidClass, ConstraintMetadataError, ConstraintMetadataResult,
12};
13use uqa_core::catalog_identity::CatalogObjectIdentity;
14
15impl IndexCatalogIdentity {
16    pub fn validate(&self, table_object_id: [u8; 16]) -> ConstraintMetadataResult<()> {
17        if !self.identity.is_valid()
18            || self.table_object_id == [0; 16]
19            || self.table_object_id != table_object_id
20            || self.physical_key.is_empty()
21            || self.physical_key.contains('\0')
22        {
23            return Err(ConstraintMetadataError::Invalid(
24                "invalid index catalog identity or indexed table incarnation".into(),
25            ));
26        }
27        Ok(())
28    }
29
30    pub fn allocate(
31        table_object_id: [u8; 16],
32        allocate: &mut dyn CatalogObjectAllocator,
33    ) -> ConstraintMetadataResult<Self> {
34        let object_id = allocate.allocate_object_id("index")?;
35        let oid = allocate.allocate_catalog_oid(CatalogOidClass::Relation, &object_id)?;
36        Self::reserved(table_object_id, CatalogObjectIdentity { object_id, oid })
37    }
38
39    /// The identity of an index whose address was reserved in advance, as a key constraint reserves its enforcing index's.
40    pub fn reserved(
41        table_object_id: [u8; 16],
42        identity: CatalogObjectIdentity,
43    ) -> ConstraintMetadataResult<Self> {
44        let identity = Self {
45            identity,
46            table_object_id,
47            // A SQL-qualified identifier always contains a dot; this namespace cannot collide with a preserved legacy physical name.
48            physical_key: format!("uqa:index:{:032x}", u128::from_be_bytes(identity.object_id)),
49        };
50        identity.validate(table_object_id)?;
51        Ok(identity)
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use super::*;
58
59    #[test]
60    fn stored_index_identity_rejects_invalid_addresses_tables_and_physical_keys() {
61        let valid = IndexCatalogIdentity {
62            identity: CatalogObjectIdentity {
63                object_id: [1; 16],
64                oid: 17000,
65            },
66            table_object_id: [2; 16],
67            physical_key: "public.legacy_index".into(),
68        };
69        valid.validate([2; 16]).unwrap();
70        assert!(valid.validate([3; 16]).is_err());
71        for malformed in [
72            IndexCatalogIdentity {
73                table_object_id: [0; 16],
74                ..valid.clone()
75            },
76            IndexCatalogIdentity {
77                physical_key: String::new(),
78                ..valid.clone()
79            },
80            IndexCatalogIdentity {
81                physical_key: "invalid\0key".into(),
82                ..valid.clone()
83            },
84            IndexCatalogIdentity {
85                identity: CatalogObjectIdentity {
86                    object_id: [0; 16],
87                    ..valid.identity
88                },
89                ..valid.clone()
90            },
91            IndexCatalogIdentity {
92                identity: CatalogObjectIdentity {
93                    oid: 0,
94                    ..valid.identity
95                },
96                ..valid.clone()
97            },
98            IndexCatalogIdentity {
99                identity: CatalogObjectIdentity {
100                    oid: i64::from(u32::MAX) + 1,
101                    ..valid.identity
102                },
103                ..valid.clone()
104            },
105        ] {
106            assert!(malformed.validate([2; 16]).is_err());
107        }
108    }
109}