Skip to main content

uqa_sql/expr/
value_catalog.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Borrowed catalog identities and comparison support for SQL value consumers.
8
9use super::enums::{EnumTypeComparisonStates, EnumTypeLabels};
10use super::Result;
11use std::sync::Arc;
12use uqa_core::EnumValue;
13
14/// Type metadata and enum support used to observe already admitted SQL values. Implementations borrow one statement catalog generation and never invoke type input.
15pub trait SQLValueCatalog {
16    /// The declared type for a physical OID, including domains, arrays and composites.
17    fn value_type_by_oid(&self, _oid: u32) -> Result<Option<crate::ColumnType>> {
18        Ok(None)
19    }
20
21    /// Current SQL spelling of an admitted catalog OID alias; `None` retains numeric output.
22    fn value_regtype_output(&self, _ty: &crate::ColumnType, _oid: i64) -> Result<Option<String>> {
23        Ok(None)
24    }
25
26    /// The current descriptor of an admitted composite value.
27    fn value_composite_type(
28        &self,
29        _oid: u32,
30    ) -> Result<Option<Arc<super::composites::CompositeTypeDescriptor>>> {
31        Ok(None)
32    }
33
34    /// The session's comparison support functions, shared by declared type across nested array and record calls. Pure catalog readers do not own runtime state.
35    fn enum_type_comparison_states(&self) -> Option<&EnumTypeComparisonStates> {
36        None
37    }
38
39    /// The labels of one enum type in the statement's catalog generation, or `None` when the catalog has no such type.
40    fn enum_type_labels(&self, type_oid: u32) -> Result<Option<Arc<EnumTypeLabels>>>;
41
42    /// Resolve an already admitted physical label OID across enum types. Output uses the label's actual identity even if a retained tuple now declares another enum type; it does not repeat input safety checks.
43    fn enum_value_by_oid(&self, _label_oid: u32) -> Result<Option<EnumValue>> {
44        Ok(None)
45    }
46
47    /// The actual enum type and zero-based label position of an admitted physical OID in this generation. Positions have declaration/key order and avoid copying label keys for comparison.
48    fn enum_label_position(&self, _label_oid: u32) -> Result<Option<(u32, usize)>> {
49        Ok(None)
50    }
51
52    /// Whether the current transaction added this label to a type that it did not create. `PostgreSQL` rejects such a label until the transaction commits.
53    fn enum_label_uncommitted(&self, label_oid: u32) -> bool;
54
55    /// `format_type_be` of the type, which qualifies a type hidden by the search path.
56    fn enum_type_name(&self, type_oid: u32) -> Result<Option<String>>;
57
58    /// Whether the statement catalog defines any enum type; binding skips enum literal validation otherwise.
59    fn has_enum_types(&self) -> bool;
60}
61
62/// Adapt a scalar context without requiring embedders that only provide domain or composite metadata to implement enum support.
63pub struct EngineValueCatalog<'a>(pub &'a dyn super::EngineHook);
64
65impl SQLValueCatalog for EngineValueCatalog<'_> {
66    fn value_regtype_output(&self, ty: &crate::ColumnType, oid: i64) -> Result<Option<String>> {
67        self.0
68            .resolve_regtype_output(ty, oid)
69            .map_err(crate::SQLError::Internal)
70    }
71
72    fn value_type_by_oid(&self, oid: u32) -> Result<Option<crate::ColumnType>> {
73        self.0
74            .resolve_type_oid(oid)
75            .map_err(crate::SQLError::Internal)
76    }
77
78    fn value_composite_type(
79        &self,
80        oid: u32,
81    ) -> Result<Option<Arc<super::composites::CompositeTypeDescriptor>>> {
82        self.0
83            .composite_types()
84            .map(|catalog| catalog.composite_type(oid))
85            .transpose()
86            .map(Option::flatten)
87    }
88
89    fn enum_type_comparison_states(&self) -> Option<&EnumTypeComparisonStates> {
90        self.0.enum_labels()?.enum_type_comparison_states()
91    }
92
93    fn enum_type_labels(&self, oid: u32) -> Result<Option<Arc<EnumTypeLabels>>> {
94        self.0
95            .enum_labels()
96            .map(|catalog| catalog.enum_type_labels(oid))
97            .transpose()
98            .map(Option::flatten)
99    }
100
101    fn enum_value_by_oid(&self, oid: u32) -> Result<Option<EnumValue>> {
102        self.0
103            .enum_labels()
104            .map(|catalog| catalog.enum_value_by_oid(oid))
105            .transpose()
106            .map(Option::flatten)
107    }
108
109    fn enum_label_position(&self, oid: u32) -> Result<Option<(u32, usize)>> {
110        self.0
111            .enum_labels()
112            .map(|catalog| catalog.enum_label_position(oid))
113            .transpose()
114            .map(Option::flatten)
115    }
116
117    fn enum_label_uncommitted(&self, oid: u32) -> bool {
118        self.0
119            .enum_labels()
120            .is_some_and(|catalog| catalog.enum_label_uncommitted(oid))
121    }
122
123    fn enum_type_name(&self, oid: u32) -> Result<Option<String>> {
124        self.0
125            .enum_labels()
126            .map(|catalog| catalog.enum_type_name(oid))
127            .transpose()
128            .map(Option::flatten)
129    }
130
131    fn has_enum_types(&self) -> bool {
132        self.0
133            .enum_labels()
134            .is_some_and(SQLValueCatalog::has_enum_types)
135    }
136}