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        let identity = Self {
37            identity: CatalogObjectIdentity { object_id, oid },
38            table_object_id,
39            // A SQL-qualified identifier always contains a dot; this namespace cannot collide with a preserved legacy physical name.
40            physical_key: format!("uqa:index:{:032x}", u128::from_be_bytes(object_id)),
41        };
42        identity.validate(table_object_id)?;
43        Ok(identity)
44    }
45}
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50
51    #[test]
52    fn stored_index_identity_rejects_invalid_addresses_tables_and_physical_keys() {
53        let valid = IndexCatalogIdentity {
54            identity: CatalogObjectIdentity {
55                object_id: [1; 16],
56                oid: 17000,
57            },
58            table_object_id: [2; 16],
59            physical_key: "public.legacy_index".into(),
60        };
61        valid.validate([2; 16]).unwrap();
62        assert!(valid.validate([3; 16]).is_err());
63        for malformed in [
64            IndexCatalogIdentity {
65                table_object_id: [0; 16],
66                ..valid.clone()
67            },
68            IndexCatalogIdentity {
69                physical_key: String::new(),
70                ..valid.clone()
71            },
72            IndexCatalogIdentity {
73                physical_key: "invalid\0key".into(),
74                ..valid.clone()
75            },
76            IndexCatalogIdentity {
77                identity: CatalogObjectIdentity {
78                    object_id: [0; 16],
79                    ..valid.identity
80                },
81                ..valid.clone()
82            },
83            IndexCatalogIdentity {
84                identity: CatalogObjectIdentity {
85                    oid: 0,
86                    ..valid.identity
87                },
88                ..valid.clone()
89            },
90            IndexCatalogIdentity {
91                identity: CatalogObjectIdentity {
92                    oid: i64::from(u32::MAX) + 1,
93                    ..valid.identity
94                },
95                ..valid.clone()
96            },
97        ] {
98            assert!(malformed.validate([2; 16]).is_err());
99        }
100    }
101}