uqa_sql/ast/types/
identity.rs1use super::ColumnType;
10
11const ENUM_PREFIX: &str = "enum#";
12const DOMAIN_PREFIX: &str = "domain#";
13const COMPOSITE_PREFIX: &str = "composite#";
14
15pub(super) fn write_identity(ty: &ColumnType, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16 match ty {
17 ColumnType::Enum(reference) => write!(f, "{ENUM_PREFIX}{}", reference.oid),
18 ColumnType::Composite(reference) => write!(f, "{COMPOSITE_PREFIX}{}", reference.oid),
19 ColumnType::Domain { oid, .. } => write!(f, "{DOMAIN_PREFIX}{oid}"),
20 _ => unreachable!("catalog identity formatter requires a user-defined scalar type"),
21 }
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum UserTypeKind {
26 Enum,
27 Domain,
28 Composite,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub struct UserTypeIdentity {
34 pub kind: UserTypeKind,
35 pub oid: u32,
36 pub dimensions: usize,
37}
38
39impl UserTypeIdentity {
40 #[must_use]
42 pub fn parse(name: &str) -> Option<Self> {
43 let mut element = name.trim();
44 let mut dimensions = 0usize;
45 while let Some(inner) = element.strip_suffix("[]") {
46 element = inner;
47 dimensions += 1;
48 }
49 let (kind, oid) = if let Some(oid) = element.strip_prefix(ENUM_PREFIX) {
50 (UserTypeKind::Enum, oid)
51 } else if let Some(oid) = element.strip_prefix(COMPOSITE_PREFIX) {
52 (UserTypeKind::Composite, oid)
53 } else {
54 (UserTypeKind::Domain, element.strip_prefix(DOMAIN_PREFIX)?)
55 };
56 if oid.is_empty() || !oid.bytes().all(|byte| byte.is_ascii_digit()) {
57 return None;
58 }
59 Some(Self {
60 kind,
61 oid: oid.parse().ok()?,
62 dimensions,
63 })
64 }
65}
66
67impl ColumnType {
68 #[must_use]
70 pub fn user_type_identity(&self) -> Option<String> {
71 match self {
72 ColumnType::Enum(reference) => Some(format!("{ENUM_PREFIX}{}", reference.oid)),
73 ColumnType::Composite(reference) => {
74 Some(format!("{COMPOSITE_PREFIX}{}", reference.oid))
75 }
76 ColumnType::Domain { oid, .. } => Some(format!("{DOMAIN_PREFIX}{oid}")),
77 ColumnType::Array(element) => element
78 .user_type_identity()
79 .map(|element| format!("{element}[]")),
80 _ => None,
81 }
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::{UserTypeIdentity, UserTypeKind};
88 use crate::ast::{ColumnType, EnumTypeReference};
89
90 #[test]
91 fn identities_round_trip_through_arrays() {
92 let mood = ColumnType::Enum(EnumTypeReference {
93 schema: "public".into(),
94 name: "mood".into(),
95 oid: 20_000,
96 array_oid: 20_001,
97 });
98 let array = ColumnType::Array(Box::new(ColumnType::Array(Box::new(mood))));
99 let identity = array.user_type_identity().unwrap();
100 assert_eq!(identity, "enum#20000[][]");
101 assert_eq!(
102 UserTypeIdentity::parse(&identity),
103 Some(UserTypeIdentity {
104 kind: UserTypeKind::Enum,
105 oid: 20_000,
106 dimensions: 2,
107 })
108 );
109 assert_eq!(
110 UserTypeIdentity::parse("composite#9[]"),
111 Some(UserTypeIdentity {
112 kind: UserTypeKind::Composite,
113 oid: 9,
114 dimensions: 1,
115 })
116 );
117 assert_eq!(
118 UserTypeIdentity::parse("domain#7"),
119 Some(UserTypeIdentity {
120 kind: UserTypeKind::Domain,
121 oid: 7,
122 dimensions: 0,
123 })
124 );
125 for name in ["\"enum#1\"", "enum#", "enum#x", "domain#-1", "integer"] {
126 assert_eq!(UserTypeIdentity::parse(name), None, "{name}");
127 }
128 assert_eq!(ColumnType::Integer.user_type_identity(), None);
129 }
130}