Skip to main content

uqa_sql/ast/types/
names.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Type names stream into the caller's ordinary or admitted destination without intermediate owned names.
8
9use super::ColumnType;
10use std::fmt;
11use uqa_core::{
12    memory::{Produced, ProductionControl},
13    ValueRetentionError,
14};
15
16impl ColumnType {
17    #[must_use]
18    pub fn sql_name(&self) -> String {
19        TypeName {
20            ty: self,
21            regtype: false,
22        }
23        .to_string()
24    }
25
26    /// Name emitted by `PostgreSQL`'s regtype output, including `pg_typeof`.
27    #[must_use]
28    pub fn regtype_name(&self) -> String {
29        TypeName {
30            ty: self,
31            regtype: true,
32        }
33        .to_string()
34    }
35
36    pub fn sql_name_with_control(
37        &self,
38        control: &ProductionControl<'_>,
39    ) -> Result<Produced<String>, ValueRetentionError> {
40        control.format(format_args!(
41            "{}",
42            TypeName {
43                ty: self,
44                regtype: false
45            }
46        ))
47    }
48
49    pub fn regtype_name_with_control(
50        &self,
51        control: &ProductionControl<'_>,
52    ) -> Result<Produced<String>, ValueRetentionError> {
53        control.format(format_args!(
54            "{}",
55            TypeName {
56                ty: self,
57                regtype: true
58            }
59        ))
60    }
61}
62
63struct TypeName<'a> {
64    ty: &'a ColumnType,
65    regtype: bool,
66}
67
68impl fmt::Display for TypeName<'_> {
69    #[expect(
70        clippy::too_many_lines,
71        reason = "one formatter preserves exhaustive SQL and regtype spellings"
72    )]
73    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74        if self.regtype {
75            if matches!(self.ty, ColumnType::IntervalWithFields { .. }) {
76                return f.write_str("interval");
77            }
78            if self.ty.temporal_precision().is_some() {
79                return TypeName {
80                    ty: self.ty.without_temporal_modifiers(),
81                    regtype: true,
82                }
83                .fmt(f);
84            }
85            match self.ty {
86                ColumnType::Varchar(_) => return f.write_str("character varying"),
87                ColumnType::Bpchar | ColumnType::Character(_) => return f.write_str("character"),
88                ColumnType::Numeric { .. } => return f.write_str("numeric"),
89                ColumnType::Vector(_) => return f.write_str("vector"),
90                ColumnType::Tensor(_) => return f.write_str("tensor"),
91                ColumnType::Array(element) => {
92                    return write!(
93                        f,
94                        "{}[]",
95                        TypeName {
96                            ty: element,
97                            regtype: true
98                        }
99                    )
100                }
101                _ => {}
102            }
103        }
104        match self.ty {
105            ColumnType::Named(name) => f.write_str(name),
106            // Regtype output qualifies a user-defined type whose schema is not visible, as `format_type_be` does; other spellings name an enum without its schema.
107            ColumnType::Enum(reference) if self.regtype => f.write_str(
108                &super::display::visible_type_name(&reference.schema, &reference.name),
109            ),
110            ColumnType::Enum(reference) => f.write_str(&crate::expr::quote_ident(&reference.name)),
111            ColumnType::Composite(reference) if self.regtype => f.write_str(
112                &super::display::visible_type_name(&reference.schema, &reference.name),
113            ),
114            ColumnType::Composite(reference) => {
115                f.write_str(&crate::expr::quote_ident(&reference.name))
116            }
117            ColumnType::SmallInteger => f.write_str("smallint"),
118            ColumnType::Integer => f.write_str("integer"),
119            ColumnType::BigInteger => f.write_str("bigint"),
120            ColumnType::Oid => f.write_str("oid"),
121            ColumnType::Xid => f.write_str("xid"),
122            ColumnType::Boolean => f.write_str("boolean"),
123            ColumnType::Void => f.write_str("void"),
124            ColumnType::Text => f.write_str("text"),
125            ColumnType::RefCursor => f.write_str("refcursor"),
126            ColumnType::Name => f.write_str("name"),
127            ColumnType::Uuid => f.write_str("uuid"),
128            ColumnType::Varchar(Some(length)) => write!(f, "character varying({length})"),
129            ColumnType::Varchar(None) => f.write_str("character varying"),
130            ColumnType::Bpchar => f.write_str("bpchar"),
131            ColumnType::Character(length) => write!(f, "character({length})"),
132            ColumnType::Real => f.write_str("real"),
133            ColumnType::DoublePrecision => f.write_str("double precision"),
134            ColumnType::Numeric {
135                precision: Some(precision),
136                scale: Some(scale),
137            } => write!(f, "numeric({precision},{scale})"),
138            ColumnType::Numeric { .. } => f.write_str("numeric"),
139            ColumnType::Json => f.write_str("json"),
140            ColumnType::JsonB => f.write_str("jsonb"),
141            ColumnType::Bytea => f.write_str("bytea"),
142            ColumnType::InternalChar => f.write_str("\"char\""),
143            ColumnType::Regproc => f.write_str("regproc"),
144            ColumnType::Regprocedure => f.write_str("regprocedure"),
145            ColumnType::Regclass => f.write_str("regclass"),
146            ColumnType::Regcollation => f.write_str("regcollation"),
147            ColumnType::Regnamespace => f.write_str("regnamespace"),
148            ColumnType::Regrole => f.write_str("regrole"),
149            ColumnType::Regtype => f.write_str("regtype"),
150            ColumnType::PgNodeTree => f.write_str("pg_node_tree"),
151            ColumnType::AclItem => f.write_str("aclitem"),
152            ColumnType::Int2Vector => f.write_str("int2vector"),
153            ColumnType::OidVector => f.write_str("oidvector"),
154            ColumnType::AnyArray => f.write_str("anyarray"),
155            ColumnType::Record => f.write_str("record"),
156            ColumnType::Array(element) => write!(
157                f,
158                "{}[]",
159                TypeName {
160                    ty: element,
161                    regtype: false
162                }
163            ),
164            ColumnType::Date => f.write_str("date"),
165            ColumnType::Time => f.write_str("time without time zone"),
166            ColumnType::TimePrecision(p) => write!(f, "time({p}) without time zone"),
167            ColumnType::TimeTz => f.write_str("time with time zone"),
168            ColumnType::TimeTzPrecision(p) => write!(f, "time({p}) with time zone"),
169            ColumnType::Timestamp => f.write_str("timestamp without time zone"),
170            ColumnType::TimestampPrecision(p) => write!(f, "timestamp({p}) without time zone"),
171            ColumnType::TimestampTz => f.write_str("timestamp with time zone"),
172            ColumnType::TimestampTzPrecision(p) => write!(f, "timestamp({p}) with time zone"),
173            ColumnType::Interval => f.write_str("interval"),
174            ColumnType::IntervalWithFields { fields, precision } => {
175                write!(f, "interval{}", fields.sql_suffix())?;
176                if let Some(precision) = precision {
177                    write!(f, "({precision})")?;
178                }
179                Ok(())
180            }
181            ColumnType::Range(subtype) => f.write_str(subtype.range_name()),
182            ColumnType::Multirange(subtype) => f.write_str(subtype.multirange_name()),
183            ColumnType::Vector(dimension) => write!(f, "vector({dimension})"),
184            ColumnType::Tensor(dimension) => write!(f, "tensor({dimension})"),
185            ColumnType::Domain { schema, name, .. } if self.regtype => {
186                f.write_str(&super::display::visible_type_name(schema, name))
187            }
188            ColumnType::Domain { schema, name, .. } => {
189                crate::compiler::write_relation_component(schema, f)?;
190                f.write_str(".")?;
191                crate::compiler::write_relation_component(name, f)
192            }
193        }
194    }
195}
196
197#[cfg(test)]
198mod tests;