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