uqa_sql/ast/types/
display.rs1use std::cell::RefCell;
10use std::sync::Arc;
11
12use super::ColumnType;
13
14thread_local! {
15 static SEARCH_PATH: RefCell<Option<Arc<[String]>>> = const { RefCell::new(None) };
16}
17
18pub struct TypeDisplayScope {
20 previous: Option<Arc<[String]>>,
21}
22
23impl TypeDisplayScope {
24 #[must_use]
25 pub fn enter(search_path: &[String]) -> Self {
26 let previous = SEARCH_PATH.with(|current| current.replace(Some(search_path.into())));
27 Self { previous }
28 }
29
30 pub fn refresh(&self, search_path: &[String]) {
32 SEARCH_PATH.with(|current| {
33 current.replace(Some(search_path.into()));
34 });
35 }
36}
37
38impl Drop for TypeDisplayScope {
39 fn drop(&mut self) {
40 SEARCH_PATH.with(|current| {
41 current.replace(self.previous.take());
42 });
43 }
44}
45
46fn schema_visible(schema: &str) -> bool {
47 schema == "pg_catalog"
48 || SEARCH_PATH.with(|current| {
49 current.borrow().as_ref().map_or_else(
50 || schema == "public",
51 |path| path.iter().any(|entry| entry == schema),
52 )
53 })
54}
55
56pub(super) fn visible_type_name(schema: &str, name: &str) -> String {
58 let local = crate::expr::quote_ident(name);
59 if schema_visible(schema) {
60 local
61 } else {
62 format!("{}.{local}", crate::expr::quote_ident(schema))
63 }
64}
65
66impl ColumnType {
67 #[must_use]
69 pub fn display_name(&self) -> String {
70 match self {
71 ColumnType::Enum(reference) => visible_type_name(&reference.schema, &reference.name),
72 ColumnType::Composite(reference) => {
73 visible_type_name(&reference.schema, &reference.name)
74 }
75 ColumnType::Domain { schema, name, .. } => visible_type_name(schema, name),
76 ColumnType::Array(element) => {
78 let mut leaf = element.as_ref();
79 while let ColumnType::Array(inner) = leaf {
80 leaf = inner;
81 }
82 if matches!(
83 leaf,
84 ColumnType::Enum(_) | ColumnType::Composite(_) | ColumnType::Domain { .. }
85 ) {
86 format!("{}[]", leaf.display_name())
87 } else {
88 self.regtype_name()
89 }
90 }
91 other => other.regtype_name(),
92 }
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::TypeDisplayScope;
99 use crate::ast::{ColumnType, EnumTypeReference};
100
101 #[test]
102 fn hidden_user_types_are_qualified_in_diagnostics() {
103 let mood = ColumnType::Enum(EnumTypeReference {
104 schema: "hidden".into(),
105 name: "hmood".into(),
106 oid: 1,
107 array_oid: 2,
108 });
109 assert_eq!(mood.display_name(), "hidden.hmood");
111 let scope = TypeDisplayScope::enter(&["public".into()]);
112 assert_eq!(mood.display_name(), "hidden.hmood");
113 assert_eq!(
114 ColumnType::Array(Box::new(mood.clone())).display_name(),
115 "hidden.hmood[]"
116 );
117 scope.refresh(&["hidden".into(), "public".into()]);
118 assert_eq!(mood.display_name(), "hmood");
119 drop(scope);
120 assert_eq!(mood.display_name(), "hidden.hmood");
121 assert_eq!(ColumnType::Integer.display_name(), "integer");
122 }
123}