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            ColumnType::SmallInteger => f.write_str("smallint"),
107            ColumnType::Integer => f.write_str("integer"),
108            ColumnType::BigInteger => f.write_str("bigint"),
109            ColumnType::Oid => f.write_str("oid"),
110            ColumnType::Xid => f.write_str("xid"),
111            ColumnType::Boolean => f.write_str("boolean"),
112            ColumnType::Void => f.write_str("void"),
113            ColumnType::Text => f.write_str("text"),
114            ColumnType::RefCursor => f.write_str("refcursor"),
115            ColumnType::Name => f.write_str("name"),
116            ColumnType::Uuid => f.write_str("uuid"),
117            ColumnType::Varchar(Some(length)) => write!(f, "character varying({length})"),
118            ColumnType::Varchar(None) => f.write_str("character varying"),
119            ColumnType::Bpchar => f.write_str("bpchar"),
120            ColumnType::Character(length) => write!(f, "character({length})"),
121            ColumnType::Real => f.write_str("real"),
122            ColumnType::DoublePrecision => f.write_str("double precision"),
123            ColumnType::Numeric {
124                precision: Some(precision),
125                scale: Some(scale),
126            } => write!(f, "numeric({precision},{scale})"),
127            ColumnType::Numeric { .. } => f.write_str("numeric"),
128            ColumnType::Json => f.write_str("json"),
129            ColumnType::JsonB => f.write_str("jsonb"),
130            ColumnType::Bytea => f.write_str("bytea"),
131            ColumnType::InternalChar => f.write_str("\"char\""),
132            ColumnType::Regproc => f.write_str("regproc"),
133            ColumnType::Regprocedure => f.write_str("regprocedure"),
134            ColumnType::Regclass => f.write_str("regclass"),
135            ColumnType::Regnamespace => f.write_str("regnamespace"),
136            ColumnType::Regrole => f.write_str("regrole"),
137            ColumnType::Regtype => f.write_str("regtype"),
138            ColumnType::PgNodeTree => f.write_str("pg_node_tree"),
139            ColumnType::AclItem => f.write_str("aclitem"),
140            ColumnType::Int2Vector => f.write_str("int2vector"),
141            ColumnType::OidVector => f.write_str("oidvector"),
142            ColumnType::AnyArray => f.write_str("anyarray"),
143            ColumnType::Record => f.write_str("record"),
144            ColumnType::Array(element) => write!(
145                f,
146                "{}[]",
147                TypeName {
148                    ty: element,
149                    regtype: false
150                }
151            ),
152            ColumnType::Date => f.write_str("date"),
153            ColumnType::Time => f.write_str("time without time zone"),
154            ColumnType::TimePrecision(p) => write!(f, "time({p}) without time zone"),
155            ColumnType::TimeTz => f.write_str("time with time zone"),
156            ColumnType::TimeTzPrecision(p) => write!(f, "time({p}) with time zone"),
157            ColumnType::Timestamp => f.write_str("timestamp without time zone"),
158            ColumnType::TimestampPrecision(p) => write!(f, "timestamp({p}) without time zone"),
159            ColumnType::TimestampTz => f.write_str("timestamp with time zone"),
160            ColumnType::TimestampTzPrecision(p) => write!(f, "timestamp({p}) with time zone"),
161            ColumnType::Interval => f.write_str("interval"),
162            ColumnType::IntervalWithFields { fields, precision } => {
163                write!(f, "interval{}", fields.sql_suffix())?;
164                if let Some(precision) = precision {
165                    write!(f, "({precision})")?;
166                }
167                Ok(())
168            }
169            ColumnType::Range(subtype) => f.write_str(subtype.range_name()),
170            ColumnType::Multirange(subtype) => f.write_str(subtype.multirange_name()),
171            ColumnType::Vector(dimension) => write!(f, "vector({dimension})"),
172            ColumnType::Tensor(dimension) => write!(f, "tensor({dimension})"),
173            ColumnType::Domain { schema, name, .. } => {
174                crate::compiler::write_relation_component(schema, f)?;
175                f.write_str(".")?;
176                crate::compiler::write_relation_component(name, f)
177            }
178        }
179    }
180}
181
182#[cfg(test)]
183mod tests;