1use serde::{Deserialize, Serialize};
8
9use super::{IntervalFields, RangeSubtype};
10
11mod catalog_names;
12mod display;
13mod identity;
14mod modifiers;
15mod names;
16mod parsing;
17mod production;
18mod references;
19
20pub use display::TypeDisplayScope;
21pub use identity::{UserTypeIdentity, UserTypeKind};
22
23pub(crate) use modifiers::split_type_modifier_with_control;
24
25#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
26pub enum ColumnType {
27 Named(String),
29 SmallInteger,
30 Integer,
31 BigInteger,
32 Oid,
34 Xid,
36 Boolean,
37 Void,
39 Text,
40 RefCursor,
42 Name,
43 Uuid,
44 Varchar(Option<u32>),
45 Bpchar,
47 Character(u32),
51 Real,
52 DoublePrecision,
53 Numeric {
58 precision: Option<u32>,
59 scale: Option<i32>,
60 },
61 Json,
63 JsonB,
65 Bytea,
67 InternalChar,
69 Regproc,
70 Regprocedure,
72 Regclass,
74 Regnamespace,
76 Regcollation,
78 Regrole,
80 Regtype,
81 PgNodeTree,
82 AclItem,
83 Int2Vector,
84 OidVector,
85 AnyArray,
86 Record,
88 Array(Box<ColumnType>),
91 Date,
93 Time,
95 TimePrecision(u32),
97 TimeTz,
99 TimeTzPrecision(u32),
101 Timestamp,
104 TimestampPrecision(u32),
106 TimestampTz,
109 TimestampTzPrecision(u32),
111 Interval,
112 IntervalWithFields {
114 fields: IntervalFields,
115 precision: Option<u32>,
116 },
117 Range(RangeSubtype),
121 Multirange(RangeSubtype),
123 Vector(u32),
125 Tensor(u32),
129 Domain {
132 schema: String,
133 name: String,
134 oid: u32,
135 #[serde(default, skip_serializing_if = "Option::is_none")]
137 array_oid: Option<u32>,
138 #[serde(default, skip_serializing_if = "Option::is_none")]
140 collation_oid: Option<u32>,
141 base: Box<ColumnType>,
142 },
143 Enum(super::EnumTypeReference),
145 Composite(super::CompositeTypeReference),
147}
148
149pub(crate) fn builtin_array_element_name(type_name: &str) -> Option<&'static str> {
150 Some(match type_name {
151 "_bool" => "bool",
152 "_bytea" => "bytea",
153 "_char" => "\"char\"",
154 "_name" => "name",
155 "_int8" => "int8",
156 "_int2" => "int2",
157 "_int2vector" => "int2vector",
158 "_int4" => "int4",
159 "_regproc" => "regproc",
160 "_regprocedure" => "regprocedure",
161 "_regclass" => "regclass",
162 "_regrole" => "regrole",
163 "_text" => "text",
164 "_refcursor" => "refcursor",
165 "_oid" => "oid",
166 "_oidvector" => "oidvector",
167 "_bpchar" => "bpchar",
168 "_varchar" => "varchar",
169 "_float4" => "float4",
170 "_float8" => "float8",
171 "_aclitem" => "aclitem",
172 "_date" => "date",
173 "_time" => "time",
174 "_timestamp" => "timestamp",
175 "_timestamptz" => "timestamptz",
176 "_interval" => "interval",
177 "_numeric" => "numeric",
178 "_timetz" => "timetz",
179 "_record" => "record",
180 "_uuid" => "uuid",
181 "_json" => "json",
182 "_jsonb" => "jsonb",
183 "_regtype" => "regtype",
184 "_xid" => "xid",
185 "_int4range" => "int4range",
186 "_int8range" => "int8range",
187 "_numrange" => "numrange",
188 "_daterange" => "daterange",
189 "_tsrange" => "tsrange",
190 "_tstzrange" => "tstzrange",
191 "_int4multirange" => "int4multirange",
192 "_int8multirange" => "int8multirange",
193 "_nummultirange" => "nummultirange",
194 "_datemultirange" => "datemultirange",
195 "_tsmultirange" => "tsmultirange",
196 "_tstzmultirange" => "tstzmultirange",
197 _ => return None,
198 })
199}
200
201impl ColumnType {
202 pub(crate) fn has_type_modifiers(&self) -> bool {
204 match self {
205 Self::Varchar(length) => length.is_some(),
206 Self::Numeric { precision, scale } => precision.is_some() || scale.is_some(),
207 Self::Array(element) => element.has_type_modifiers(),
208 Self::Character(_)
209 | Self::TimePrecision(_)
210 | Self::TimeTzPrecision(_)
211 | Self::TimestampPrecision(_)
212 | Self::TimestampTzPrecision(_)
213 | Self::IntervalWithFields { .. } => true,
214 _ => false,
215 }
216 }
217
218 #[must_use]
220 pub fn without_type_modifiers(&self) -> Self {
221 self.without_type_modifiers_with_control(
222 &uqa_core::memory::ProductionControl::uncontrolled(),
223 )
224 .expect("ordinary type modifier removal cannot be limited or cancelled")
225 .into_uncontrolled()
226 .expect("ordinary type modifier removal has no reservation")
227 }
228
229 #[must_use]
230 pub const fn temporal_precision(&self) -> Option<u32> {
231 match self {
232 Self::IntervalWithFields { precision, .. } => *precision,
233 Self::TimePrecision(p)
234 | Self::TimeTzPrecision(p)
235 | Self::TimestampPrecision(p)
236 | Self::TimestampTzPrecision(p) => Some(*p),
237 _ => None,
238 }
239 }
240
241 #[must_use]
242 pub const fn without_temporal_modifiers(&self) -> &Self {
243 match self {
244 Self::IntervalWithFields { .. } => &Self::Interval,
245 Self::TimePrecision(_) => &Self::Time,
246 Self::TimeTzPrecision(_) => &Self::TimeTz,
247 Self::TimestampPrecision(_) => &Self::Timestamp,
248 Self::TimestampTzPrecision(_) => &Self::TimestampTz,
249 other => other,
250 }
251 }
252
253 pub(crate) fn with_temporal_precision(
254 self,
255 precision: Option<i64>,
256 ) -> Result<Self, crate::SQLError> {
257 let Some(precision) = precision else {
258 return Ok(self);
259 };
260 if precision < 0 {
261 return Err(crate::SQLError::Routine {
262 sqlstate: "22023".into(),
263 message: format!(
264 "{} precision must not be negative",
265 self.regtype_name().to_uppercase()
266 ),
267 });
268 }
269 let precision = u32::try_from(precision.min(6)).expect("bounded temporal precision");
270 Ok(match self {
271 Self::Time => Self::TimePrecision(precision),
272 Self::TimeTz => Self::TimeTzPrecision(precision),
273 Self::Timestamp => Self::TimestampPrecision(precision),
274 Self::TimestampTz => Self::TimestampTzPrecision(precision),
275 other => other,
276 })
277 }
278
279 pub(crate) fn with_interval_modifiers(
280 fields: IntervalFields,
281 precision: Option<i64>,
282 ) -> Result<Self, crate::SQLError> {
283 let precision = precision
284 .map(|precision| {
285 if precision < 0 {
286 return Err(crate::SQLError::Routine {
287 sqlstate: "22023".into(),
288 message: "INTERVAL precision must not be negative".into(),
289 });
290 }
291 Ok(u32::try_from(precision.min(6)).expect("bounded interval precision"))
292 })
293 .transpose()?;
294 if fields == IntervalFields::All && precision.is_none() {
295 return Ok(Self::Interval);
296 }
297 Ok(Self::IntervalWithFields { fields, precision })
298 }
299
300 #[must_use]
301 pub fn is_integer(&self) -> bool {
302 match self {
303 Self::SmallInteger | Self::Integer | Self::BigInteger | Self::Oid | Self::Xid => true,
304 Self::Domain { base, .. } => base.is_integer(),
305 _ => false,
306 }
307 }
308
309 #[must_use]
310 pub fn is_character_string(&self) -> bool {
311 match self {
312 Self::Text
313 | Self::Name
314 | Self::Varchar(_)
315 | Self::Bpchar
316 | Self::Character(_)
317 | Self::InternalChar
318 | Self::PgNodeTree
319 | Self::AclItem => true,
320 Self::Domain { base, .. } => base.is_character_string(),
321 _ => false,
322 }
323 }
324}