1use sea_query::RcOrArc;
2#[cfg(feature = "with-serde")]
3use serde::{Deserialize, Serialize};
4
5#[derive(Clone, Debug, PartialEq)]
6#[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))]
7pub enum Type {
9 SmallInt,
12 Integer,
14 BigInt,
16 Decimal(ArbitraryPrecisionNumericAttr),
18 Numeric(ArbitraryPrecisionNumericAttr),
20 Real,
22 DoublePrecision,
24 SmallSerial,
26 Serial,
28 BigSerial,
30
31 Money,
34
35 Varchar(StringAttr),
38 Char(StringAttr),
40 Text,
42
43 Bytea,
45
46 Timestamp(TimeAttr),
49 TimestampWithTimeZone(TimeAttr),
50 Date,
52 Time(TimeAttr),
54 TimeWithTimeZone(TimeAttr),
55 Interval(IntervalAttr),
57
58 Boolean,
60
61 Point,
68 Line,
70 Lseg,
72 Box,
74 Path,
76 Polygon,
78 Circle,
80
81 Cidr,
84 Inet,
86 MacAddr,
88 MacAddr8,
90
91 Bit(BitAttr),
93
94 VarBit(BitAttr),
96
97 TsVector,
101 TsQuery,
104
105 Uuid,
107
108 Xml,
110
111 Json,
113 JsonBinary,
115
116 Array(ArrayDef),
118
119 #[cfg(feature = "postgres-vector")]
120 Vector(VectorDef),
122
123 Int4Range,
130 Int8Range,
132 NumRange,
134 TsRange,
136 TsTzRange,
138 DateRange,
140
141 PgLsn,
149 Unknown(String),
151 Enum(EnumDef),
153}
154
155impl Type {
156 #[allow(clippy::should_implement_trait)]
158 pub fn from_str(column_type: &str, udt_name: Option<&str>, is_enum: bool) -> Type {
159 match column_type.to_lowercase().as_str() {
160 "smallint" | "int2" => Type::SmallInt,
161 "integer" | "int" | "int4" => Type::Integer,
162 "bigint" | "int8" => Type::BigInt,
163 "decimal" => Type::Decimal(ArbitraryPrecisionNumericAttr::default()),
164 "numeric" => Type::Numeric(ArbitraryPrecisionNumericAttr::default()),
165 "real" | "float4" => Type::Real,
166 "double precision" | "double" | "float8" => Type::DoublePrecision,
167 "smallserial" | "serial2" => Type::SmallSerial,
168 "serial" | "serial4" => Type::Serial,
169 "bigserial" | "serial8" => Type::BigSerial,
170 "money" => Type::Money,
171 "character varying" | "varchar" => Type::Varchar(StringAttr::default()),
172 "character" | "char" => Type::Char(StringAttr::default()),
173 "text" => Type::Text,
174 "bytea" => Type::Bytea,
175 "timestamp" | "timestamp without time zone" => Type::Timestamp(TimeAttr::default()),
176 "timestamp with time zone" => Type::TimestampWithTimeZone(TimeAttr::default()),
177 "date" => Type::Date,
178 "time" | "time without time zone" => Type::Time(TimeAttr::default()),
179 "time with time zone" => Type::TimeWithTimeZone(TimeAttr::default()),
180 "interval" => Type::Interval(IntervalAttr::default()),
181 "boolean" | "bool" => Type::Boolean,
182 "point" => Type::Point,
183 "line" => Type::Line,
184 "lseg" => Type::Lseg,
185 "box" => Type::Box,
186 "path" => Type::Path,
187 "polygon" => Type::Polygon,
188 "circle" => Type::Circle,
189 "cidr" => Type::Cidr,
190 "inet" => Type::Inet,
191 "macaddr" => Type::MacAddr,
192 "macaddr8" => Type::MacAddr8,
193 "bit" => Type::Bit(BitAttr::default()),
194 "bit varying" | "varbit" => Type::VarBit(BitAttr::default()),
195 "tsvector" => Type::TsVector,
196 "tsquery" => Type::TsQuery,
197 "uuid" => Type::Uuid,
198 "xml" => Type::Xml,
199 "json" => Type::Json,
200 "jsonb" => Type::JsonBinary,
201 "int4range" => Type::Int4Range,
203 "int8range" => Type::Int8Range,
204 "numrange" => Type::NumRange,
205 "tsrange" => Type::TsRange,
206 "tstzrange" => Type::TsTzRange,
207 "daterange" => Type::DateRange,
208 "pg_lsn" => Type::PgLsn,
210 "user-defined" => match (is_enum, udt_name) {
211 (true, _) => Type::Enum(EnumDef::default()),
212 #[cfg(feature = "postgres-vector")]
213 (false, Some("vector")) => Type::Vector(VectorDef::default()),
214 (false, Some(other_name)) => Type::Unknown(other_name.to_owned()),
215 _ => Type::Unknown("user-defined".to_owned()),
216 },
217 "array" => Type::Array(ArrayDef::default()),
218 _ => Type::Unknown(column_type.to_owned()),
219 }
220 }
221}
222
223#[derive(Clone, Debug, PartialEq, Default)]
224#[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))]
225pub struct ArbitraryPrecisionNumericAttr {
229 pub precision: Option<u16>,
231 pub scale: Option<u16>,
233}
234
235#[derive(Clone, Debug, PartialEq, Default)]
236#[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))]
237pub struct StringAttr {
238 pub length: Option<u16>,
239}
240
241#[derive(Clone, Debug, PartialEq, Default)]
242#[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))]
243pub struct TimeAttr {
244 pub precision: Option<u16>,
245}
246
247#[derive(Clone, Debug, PartialEq, Default)]
248#[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))]
249pub struct IntervalAttr {
250 pub field: Option<String>,
251 pub precision: Option<u16>,
252}
253
254#[derive(Clone, Debug, PartialEq, Default)]
255#[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))]
256pub struct BitAttr {
257 pub length: Option<u16>,
258}
259
260#[derive(Clone, Debug, PartialEq, Default)]
262#[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))]
263pub struct EnumDef {
264 pub values: Vec<String>,
266 pub typename: String,
268}
269
270#[derive(Clone, Debug, PartialEq, Default)]
272#[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))]
273pub struct ArrayDef {
274 pub col_type: Option<RcOrArc<Type>>,
276}
277
278#[cfg(feature = "postgres-vector")]
279#[derive(Clone, Debug, PartialEq, Default)]
281#[cfg_attr(feature = "with-serde", derive(Serialize, Deserialize))]
282pub struct VectorDef {
283 pub length: Option<u32>,
284}
285
286impl Type {
287 pub fn has_numeric_attr(&self) -> bool {
288 matches!(self, Type::Numeric(_) | Type::Decimal(_))
289 }
290
291 pub fn has_string_attr(&self) -> bool {
292 matches!(self, Type::Varchar(_) | Type::Char(_))
293 }
294
295 pub fn has_time_attr(&self) -> bool {
296 matches!(
297 self,
298 Type::Timestamp(_)
299 | Type::TimestampWithTimeZone(_)
300 | Type::Time(_)
301 | Type::TimeWithTimeZone(_)
302 )
303 }
304
305 pub fn has_interval_attr(&self) -> bool {
306 matches!(self, Type::Interval(_))
307 }
308
309 pub fn has_bit_attr(&self) -> bool {
310 matches!(self, Type::Bit(_) | Type::VarBit(_))
311 }
312
313 pub fn has_enum_attr(&self) -> bool {
314 matches!(self, Type::Enum(_))
315 }
316
317 pub fn has_array_attr(&self) -> bool {
318 matches!(self, Type::Array(_))
319 }
320
321 #[cfg(feature = "postgres-vector")]
322 pub fn has_vector_attr(&self) -> bool {
323 matches!(self, Type::Vector(_))
324 }
325}
326
327#[cfg(test)]
328mod tests {
329 use super::*;
330
331 #[test]
332 fn parses_user_defined_enum_and_vector() {
333 assert_eq!(
334 Type::from_str("user-defined", None, true),
335 Type::Enum(EnumDef::default())
336 );
337
338 #[cfg(feature = "postgres-vector")]
339 assert_eq!(
340 Type::from_str("user-defined", Some("vector"), false),
341 Type::Vector(VectorDef::default())
342 );
343
344 #[cfg(not(feature = "postgres-vector"))]
345 assert_eq!(
346 Type::from_str("user-defined", Some("vector"), false),
347 Type::Unknown("vector".into())
348 );
349
350 assert_eq!(
351 Type::from_str("user-defined", Some("foo_bar"), false),
352 Type::Unknown("foo_bar".into())
353 );
354 }
355}