1use super::{builtin_array_element_name, split_type_modifier_with_control, ColumnType};
10use crate::ast::{IntervalFields, RangeSubtype};
11use std::borrow::Cow;
12use uqa_core::memory::{Produced, ProductionControl, ProductionString};
13use uqa_core::ValueRetentionError;
14
15impl ColumnType {
16 pub fn from_sql_name(name: &str) -> Result<Self, crate::SQLError> {
18 Self::from_sql_name_with_control(name, &ProductionControl::uncontrolled()).map(|value| {
19 value
20 .into_uncontrolled()
21 .expect("ordinary type parsing has no reservation")
22 })
23 }
24
25 #[expect(
27 clippy::too_many_lines,
28 reason = "one type grammar preserves every accepted spelling and diagnostic"
29 )]
30 pub fn from_sql_name_with_control(
31 name: &str,
32 control: &ProductionControl<'_>,
33 ) -> Result<Produced<Self>, crate::SQLError> {
34 let normalized = normalized_type_name(name, control)?;
35 if let Some(element) = builtin_array_element_name(&normalized) {
36 return Self::array_with_control(
37 Self::from_sql_name_with_control(element, control)?,
38 control,
39 )
40 .map_err(Into::into);
41 }
42 if let Some(element) = normalized.strip_suffix("[]") {
43 let element_type = Self::from_sql_name_with_control(element, control)?;
44 if matches!(*element_type, Self::Void) {
45 return Err(crate::SQLError::Routine {
46 sqlstate: "42704".into(),
47 message: format!("type \"{}\" does not exist", normalized.as_ref()),
48 });
49 }
50 return Self::array_with_control(element_type, control).map_err(Into::into);
51 }
52 let (base, modifier) = split_type_modifier_with_control(&normalized, control)?;
53 let base = base.strip_prefix("pg_catalog.").unwrap_or(&base);
54 let temporal_precision = || {
55 modifier
56 .map(|value| {
57 value.trim().parse::<i64>().map_err(|_| {
58 crate::SQLError::TypeMismatch(format!(
59 "invalid temporal precision: {value}"
60 ))
61 })
62 })
63 .transpose()
64 };
65 let character_length = || -> Result<Option<u32>, crate::SQLError> {
66 modifier
67 .map(|value| {
68 value
69 .parse::<u32>()
70 .ok()
71 .filter(|length| *length > 0)
72 .ok_or_else(|| {
73 crate::SQLError::TypeMismatch(format!(
74 "character length must be greater than zero, got {value}"
75 ))
76 })
77 })
78 .transpose()
79 };
80 let parsed = match base {
81 "smallint" | "int2" => Ok(Self::SmallInteger),
82 "integer" | "int" | "int4" => Ok(Self::Integer),
83 "bigint" | "int8" => Ok(Self::BigInteger),
84 "oid" => Ok(Self::Oid),
85 "xid" => Ok(Self::Xid),
86 "boolean" | "bool" => Ok(Self::Boolean),
87 "void" => Ok(Self::Void),
88 "text" => Ok(Self::Text),
89 "refcursor" => Ok(Self::RefCursor),
90 "name" => Ok(Self::Name),
91 "uuid" => Ok(Self::Uuid),
92 "varchar" | "character varying" => Ok(Self::Varchar(character_length()?)),
93 "character" | "char" => Ok(Self::Character(character_length()?.unwrap_or(1))),
94 "bpchar" => Ok(character_length()?.map_or(Self::Bpchar, Self::Character)),
95 "real" | "float4" => Ok(Self::Real),
96 "double" | "double precision" | "float8" => Ok(Self::DoublePrecision),
97 "numeric" | "decimal" => {
98 let (precision, scale) = match modifier {
99 None => (None, None),
100 Some(modifier) => {
101 let mut parts = modifier.split(',').map(str::trim);
102 let precision = parts
103 .next()
104 .and_then(|value| value.parse::<u32>().ok())
105 .ok_or_else(|| {
106 crate::SQLError::TypeMismatch(format!(
107 "invalid numeric modifier `{modifier}`"
108 ))
109 })?;
110 let scale = parts
111 .next()
112 .map(|value| value.parse::<i32>())
113 .transpose()
114 .map_err(|_| {
115 crate::SQLError::TypeMismatch(format!(
116 "invalid numeric modifier `{modifier}`"
117 ))
118 })?
119 .unwrap_or(0);
120 if parts.next().is_some() {
121 return Err(crate::SQLError::TypeMismatch(format!(
122 "invalid numeric modifier `{modifier}`"
123 )));
124 }
125 (Some(precision), Some(scale))
126 }
127 };
128 Ok(Self::Numeric { precision, scale })
129 }
130 "json" => Ok(Self::Json),
131 "jsonb" => Ok(Self::JsonB),
132 "bytea" => Ok(Self::Bytea),
133 "\"char\"" => Ok(Self::InternalChar),
134 "regproc" => Ok(Self::Regproc),
135 "regprocedure" => Ok(Self::Regprocedure),
136 "regclass" => Ok(Self::Regclass),
137 "regcollation" => Ok(Self::Regcollation),
138 "regnamespace" => Ok(Self::Regnamespace),
139 "regrole" => Ok(Self::Regrole),
140 "regtype" => Ok(Self::Regtype),
141 "pg_node_tree" => Ok(Self::PgNodeTree),
142 "aclitem" => Ok(Self::AclItem),
143 "int2vector" => Ok(Self::Int2Vector),
144 "oidvector" => Ok(Self::OidVector),
145 "anyarray" => Ok(Self::AnyArray),
146 "record" => Ok(Self::Record),
147 "date" => Ok(Self::Date),
148 "time" | "time without time zone" => {
149 Self::Time.with_temporal_precision(temporal_precision()?)
150 }
151 "timetz" | "time with time zone" => {
152 Self::TimeTz.with_temporal_precision(temporal_precision()?)
153 }
154 "timestamp" | "datetime" | "timestamp without time zone" => {
155 Self::Timestamp.with_temporal_precision(temporal_precision()?)
156 }
157 "timestamptz" | "timestamp with time zone" => {
158 Self::TimestampTz.with_temporal_precision(temporal_precision()?)
159 }
160 "interval" => Self::with_interval_modifiers(IntervalFields::All, temporal_precision()?),
161 other if other.starts_with("interval ") => {
162 let fields = IntervalFields::from_sql_suffix(&other[9..]).ok_or_else(|| {
163 crate::SQLError::TypeMismatch(format!("invalid interval fields: {other}"))
164 })?;
165 Self::with_interval_modifiers(fields, temporal_precision()?)
166 }
167 "int4range" => Ok(Self::Range(RangeSubtype::Integer)),
168 "int8range" => Ok(Self::Range(RangeSubtype::BigInteger)),
169 "numrange" => Ok(Self::Range(RangeSubtype::Numeric)),
170 "daterange" => Ok(Self::Range(RangeSubtype::Date)),
171 "tsrange" => Ok(Self::Range(RangeSubtype::Timestamp)),
172 "tstzrange" => Ok(Self::Range(RangeSubtype::TimestampTz)),
173 "int4multirange" => Ok(Self::Multirange(RangeSubtype::Integer)),
174 "int8multirange" => Ok(Self::Multirange(RangeSubtype::BigInteger)),
175 "nummultirange" => Ok(Self::Multirange(RangeSubtype::Numeric)),
176 "datemultirange" => Ok(Self::Multirange(RangeSubtype::Date)),
177 "tsmultirange" => Ok(Self::Multirange(RangeSubtype::Timestamp)),
178 "tstzmultirange" => Ok(Self::Multirange(RangeSubtype::TimestampTz)),
179 "vector" => modifier
180 .and_then(|value| value.parse::<u32>().ok())
181 .filter(|dimension| *dimension > 0)
182 .map(Self::Vector)
183 .ok_or_else(|| crate::SQLError::TypeMismatch("VECTOR requires a dimension".into())),
184 "tensor" => modifier
185 .and_then(|value| value.parse::<u32>().ok())
186 .filter(|dimension| *dimension > 0)
187 .map(Self::Tensor)
188 .ok_or_else(|| crate::SQLError::TypeMismatch("TENSOR requires a dimension".into())),
189 other => Err(crate::SQLError::Unsupported(format!(
190 "SQL type `{other}` is not supported"
191 ))),
192 }?;
193 control
194 .finish(parsed, control.empty_reservation())
195 .map_err(Into::into)
196 }
197}
198
199fn normalized_type_name<'a>(
200 name: &'a str,
201 control: &ProductionControl<'_>,
202) -> Result<Produced<Cow<'a, str>>, ValueRetentionError> {
203 control.check()?;
204 let name = name.trim();
205 let mut uppercase = false;
206 for chunk in name.as_bytes().chunks(4096) {
207 control.check()?;
208 if chunk.iter().any(u8::is_ascii_uppercase) {
209 uppercase = true;
210 break;
211 }
212 }
213 if !uppercase {
214 return control.finish(Cow::Borrowed(name), control.empty_reservation());
215 }
216 let mut normalized = ProductionString::new(*control);
217 normalized.reserve(name.len())?;
218 for character in name.chars() {
219 normalized.push(character.to_ascii_lowercase())?;
220 }
221 let (normalized, memory) = normalized.finish()?.into_parts();
222 control.finish(Cow::Owned(normalized), memory)
223}