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 | Self::PgNodeTree) {
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 catalog_name = base
54 .strip_prefix("pg_catalog.")
55 .or_else(|| base.strip_prefix("\"pg_catalog\"."));
56 let catalog_modifiers = catalog_name.is_some() || base.starts_with('"');
57 let catalog_type;
58 let base = if let Some(name) =
59 catalog_name.or_else(|| base.strip_prefix('"').map(|_| &**base))
60 {
61 let name = name
62 .strip_prefix('"')
63 .and_then(|name| name.strip_suffix('"'))
64 .unwrap_or(name);
65 if let Some(element) = builtin_array_element_name(name) {
66 return Self::array_with_control(
67 Self::from_sql_name_with_control(element, control)?,
68 control,
69 )
70 .map_err(Into::into);
71 }
72 let ty = crate::catalog::type_metadata::builtin_scalar_type_by_name(name).ok_or_else(
73 || crate::SQLError::Unsupported(format!("SQL type `{}` is not supported", &**base)),
74 )?;
75 if modifier.is_none() && !matches!(ty, Self::Vector(_) | Self::Tensor(_)) {
77 return ty.clone_with_control(control).map_err(Into::into);
78 }
79 catalog_type = ty.regtype_name_with_control(control)?;
80 catalog_type.as_str()
81 } else {
82 &base
83 };
84 let temporal_precision = || {
85 modifier
86 .map(|value| {
87 value.trim().parse::<i64>().map_err(|_| {
88 crate::SQLError::TypeMismatch(format!(
89 "invalid temporal precision: {value}"
90 ))
91 })
92 })
93 .transpose()
94 };
95 let character_length = || -> Result<Option<u32>, crate::SQLError> {
96 modifier
97 .map(|value| {
98 value
99 .parse::<u32>()
100 .ok()
101 .filter(|length| *length > 0)
102 .ok_or_else(|| {
103 crate::SQLError::TypeMismatch(format!(
104 "character length must be greater than zero, got {value}"
105 ))
106 })
107 })
108 .transpose()
109 };
110 let parsed = match base {
111 "smallint" | "int2" => Ok(Self::SmallInteger),
112 "integer" | "int" | "int4" => Ok(Self::Integer),
113 "bigint" | "int8" => Ok(Self::BigInteger),
114 "oid" => Ok(Self::Oid),
115 "xid" => Ok(Self::Xid),
116 "boolean" | "bool" => Ok(Self::Boolean),
117 "void" => Ok(Self::Void),
118 "text" => Ok(Self::Text),
119 "refcursor" => Ok(Self::RefCursor),
120 "name" => Ok(Self::Name),
121 "uuid" => Ok(Self::Uuid),
122 "varchar" | "character varying" => Ok(Self::Varchar(character_length()?)),
123 "character" | "char" => Ok(Self::Character(character_length()?.unwrap_or(1))),
124 "bpchar" => Ok(character_length()?.map_or(Self::Bpchar, Self::Character)),
125 "real" | "float4" => Ok(Self::Real),
126 "double" | "double precision" | "float8" => Ok(Self::DoublePrecision),
127 "numeric" | "decimal" => {
128 let (precision, scale) = match modifier {
129 None => (None, None),
130 Some(modifier) => {
131 let mut parts = modifier.split(',').map(str::trim);
132 let precision = parts
133 .next()
134 .and_then(|value| value.parse::<u32>().ok())
135 .ok_or_else(|| {
136 crate::SQLError::TypeMismatch(format!(
137 "invalid numeric modifier `{modifier}`"
138 ))
139 })?;
140 let scale = parts
141 .next()
142 .map(|value| value.parse::<i32>())
143 .transpose()
144 .map_err(|_| {
145 crate::SQLError::TypeMismatch(format!(
146 "invalid numeric modifier `{modifier}`"
147 ))
148 })?
149 .unwrap_or(0);
150 if parts.next().is_some() {
151 return Err(crate::SQLError::TypeMismatch(format!(
152 "invalid numeric modifier `{modifier}`"
153 )));
154 }
155 (Some(precision), Some(scale))
156 }
157 };
158 Ok(Self::Numeric { precision, scale })
159 }
160 "json" => Ok(Self::Json),
161 "jsonb" => Ok(Self::JsonB),
162 "bytea" => Ok(Self::Bytea),
163 "\"char\"" => Ok(Self::InternalChar),
164 "regproc" => Ok(Self::Regproc),
165 "regprocedure" => Ok(Self::Regprocedure),
166 "regclass" => Ok(Self::Regclass),
167 "regcollation" => Ok(Self::Regcollation),
168 "regnamespace" => Ok(Self::Regnamespace),
169 "regrole" => Ok(Self::Regrole),
170 "regtype" => Ok(Self::Regtype),
171 "pg_node_tree" => Ok(Self::PgNodeTree),
172 "aclitem" => Ok(Self::AclItem),
173 "int2vector" => Ok(Self::Int2Vector),
174 "oidvector" => Ok(Self::OidVector),
175 "anyarray" => Ok(Self::AnyArray),
176 "record" => Ok(Self::Record),
177 "date" => Ok(Self::Date),
178 "time" | "time without time zone" => {
179 Self::Time.with_temporal_precision(temporal_precision()?)
180 }
181 "timetz" | "time with time zone" => {
182 Self::TimeTz.with_temporal_precision(temporal_precision()?)
183 }
184 "timestamp" | "datetime" | "timestamp without time zone" => {
185 Self::Timestamp.with_temporal_precision(temporal_precision()?)
186 }
187 "timestamptz" | "timestamp with time zone" => {
188 Self::TimestampTz.with_temporal_precision(temporal_precision()?)
189 }
190 "interval" if catalog_modifiers => catalog_interval_type(modifier),
191 "interval" => Self::with_interval_modifiers(IntervalFields::All, temporal_precision()?),
192 other if other.starts_with("interval ") => {
193 let fields = IntervalFields::from_sql_suffix(&other[9..]).ok_or_else(|| {
194 crate::SQLError::TypeMismatch(format!("invalid interval fields: {other}"))
195 })?;
196 Self::with_interval_modifiers(fields, temporal_precision()?)
197 }
198 "int4range" => Ok(Self::Range(RangeSubtype::Integer)),
199 "int8range" => Ok(Self::Range(RangeSubtype::BigInteger)),
200 "numrange" => Ok(Self::Range(RangeSubtype::Numeric)),
201 "daterange" => Ok(Self::Range(RangeSubtype::Date)),
202 "tsrange" => Ok(Self::Range(RangeSubtype::Timestamp)),
203 "tstzrange" => Ok(Self::Range(RangeSubtype::TimestampTz)),
204 "int4multirange" => Ok(Self::Multirange(RangeSubtype::Integer)),
205 "int8multirange" => Ok(Self::Multirange(RangeSubtype::BigInteger)),
206 "nummultirange" => Ok(Self::Multirange(RangeSubtype::Numeric)),
207 "datemultirange" => Ok(Self::Multirange(RangeSubtype::Date)),
208 "tsmultirange" => Ok(Self::Multirange(RangeSubtype::Timestamp)),
209 "tstzmultirange" => Ok(Self::Multirange(RangeSubtype::TimestampTz)),
210 "vector" => modifier
211 .and_then(|value| value.parse::<u32>().ok())
212 .filter(|dimension| *dimension > 0)
213 .map(Self::Vector)
214 .ok_or_else(|| crate::SQLError::TypeMismatch("VECTOR requires a dimension".into())),
215 "tensor" => modifier
216 .and_then(|value| value.parse::<u32>().ok())
217 .filter(|dimension| *dimension > 0)
218 .map(Self::Tensor)
219 .ok_or_else(|| crate::SQLError::TypeMismatch("TENSOR requires a dimension".into())),
220 other => Err(crate::SQLError::Unsupported(format!(
221 "SQL type `{other}` is not supported"
222 ))),
223 }?;
224 control
225 .finish(parsed, control.empty_reservation())
226 .map_err(Into::into)
227 }
228}
229
230fn normalized_type_name<'a>(
231 name: &'a str,
232 control: &ProductionControl<'_>,
233) -> Result<Produced<Cow<'a, str>>, ValueRetentionError> {
234 control.check()?;
235 let name = name.trim();
236 let mut uppercase = false;
237 for chunk in name.as_bytes().chunks(4096) {
238 control.check()?;
239 if chunk.iter().any(u8::is_ascii_uppercase) {
240 uppercase = true;
241 break;
242 }
243 }
244 if !uppercase {
245 return control.finish(Cow::Borrowed(name), control.empty_reservation());
246 }
247 let mut normalized = ProductionString::new(*control);
248 normalized.reserve(name.len())?;
249 let mut quoted = false;
250 for character in name.chars() {
251 if character == '"' {
252 quoted = !quoted;
253 }
254 normalized.push(if quoted {
255 character
256 } else {
257 character.to_ascii_lowercase()
258 })?;
259 }
260 let (normalized, memory) = normalized.finish()?.into_parts();
261 control.finish(Cow::Owned(normalized), memory)
262}
263
264fn catalog_interval_type(modifier: Option<&str>) -> Result<ColumnType, crate::SQLError> {
265 let Some(modifier) = modifier else {
266 return Ok(ColumnType::Interval);
267 };
268 let mut parts = modifier.split(',').map(str::trim);
269 let mask = parts
270 .next()
271 .and_then(|mask| mask.parse::<i64>().ok())
272 .and_then(IntervalFields::from_modifier_mask)
273 .ok_or_else(|| crate::SQLError::TypeMismatch("invalid interval fields".into()))?;
274 let precision = parts
275 .next()
276 .map(|precision| {
277 precision.parse::<i64>().map_err(|_| {
278 crate::SQLError::TypeMismatch(format!("invalid temporal precision: {precision}"))
279 })
280 })
281 .transpose()?;
282 if parts.next().is_some() {
283 return Err(crate::SQLError::TypeMismatch(
284 "invalid interval type modifier".into(),
285 ));
286 }
287 ColumnType::with_interval_modifiers(mask, precision)
288}