Skip to main content

drizzle_types/postgres/
sql_type.rs

1//! `PostgreSQL` column type definitions
2//!
3//! Defines the core `PostgreSQL` data types for schema definition.
4
5#[cfg(feature = "serde")]
6use crate::alloc_prelude::String;
7
8/// Enum representing supported `PostgreSQL` column types.
9///
10/// These correspond to `PostgreSQL` data types.
11/// See: <https://www.postgresql.org/docs/current/datatype.html>
12///
13/// # Examples
14///
15/// ```
16/// use drizzle_types::postgres::PostgreSQLType;
17///
18/// let int_type = PostgreSQLType::Integer;
19/// assert_eq!(int_type.to_sql_type(), "INTEGER");
20///
21/// let serial = PostgreSQLType::Serial;
22/// assert!(serial.is_serial());
23/// ```
24#[derive(Default, Debug, Clone, PartialEq, Eq, Hash)]
25#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
26pub enum PostgreSQLType {
27    /// `PostgreSQL` INTEGER type - 32-bit signed integer
28    ///
29    /// See: <https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-INT>
30    Integer,
31
32    /// `PostgreSQL` BIGINT type - 64-bit signed integer
33    ///
34    /// See: <https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-INT>
35    Bigint,
36
37    /// `PostgreSQL` SMALLINT type - 16-bit signed integer
38    ///
39    /// See: <https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-INT>
40    Smallint,
41
42    /// `PostgreSQL` SERIAL type - auto-incrementing 32-bit integer
43    ///
44    /// See: <https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-SERIAL>
45    Serial,
46
47    /// `PostgreSQL` SMALLSERIAL type - auto-incrementing 16-bit integer
48    ///
49    /// See: <https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-SERIAL>
50    Smallserial,
51
52    /// `PostgreSQL` BIGSERIAL type - auto-incrementing 64-bit integer
53    ///
54    /// See: <https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-SERIAL>
55    Bigserial,
56
57    /// `PostgreSQL` TEXT type - variable-length character string
58    ///
59    /// See: <https://www.postgresql.org/docs/current/datatype-character.html>
60    #[default]
61    Text,
62
63    /// `PostgreSQL` VARCHAR type - variable-length character string with limit
64    ///
65    /// See: <https://www.postgresql.org/docs/current/datatype-character.html>
66    Varchar,
67
68    /// `PostgreSQL` CHAR type - fixed-length character string
69    ///
70    /// See: <https://www.postgresql.org/docs/current/datatype-character.html>
71    Char,
72
73    /// `PostgreSQL` REAL type - single precision floating-point number
74    ///
75    /// See: <https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-FLOAT>
76    Real,
77
78    /// `PostgreSQL` DOUBLE PRECISION type - double precision floating-point number
79    ///
80    /// See: <https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-FLOAT>
81    DoublePrecision,
82
83    /// `PostgreSQL` NUMERIC type - exact numeric with selectable precision
84    ///
85    /// See: <https://www.postgresql.org/docs/current/datatype-numeric.html#DATATYPE-NUMERIC-DECIMAL>
86    Numeric,
87
88    /// `PostgreSQL` BOOLEAN type - true/false
89    ///
90    /// See: <https://www.postgresql.org/docs/current/datatype-boolean.html>
91    Boolean,
92
93    /// `PostgreSQL` BYTEA type - binary data
94    ///
95    /// See: <https://www.postgresql.org/docs/current/datatype-binary.html>
96    Bytea,
97
98    /// `PostgreSQL` UUID type - universally unique identifier
99    ///
100    /// See: <https://www.postgresql.org/docs/current/datatype-uuid.html>
101    #[cfg(feature = "uuid")]
102    Uuid,
103
104    /// `PostgreSQL` JSON type - JSON data
105    ///
106    /// See: <https://www.postgresql.org/docs/current/datatype-json.html>
107    #[cfg(feature = "serde")]
108    Json,
109
110    /// `PostgreSQL` JSONB type - binary JSON data
111    ///
112    /// See: <https://www.postgresql.org/docs/current/datatype-json.html>
113    #[cfg(feature = "serde")]
114    Jsonb,
115
116    /// `PostgreSQL` TIMESTAMP type - date and time
117    ///
118    /// See: <https://www.postgresql.org/docs/current/datatype-datetime.html>
119    Timestamp,
120
121    /// `PostgreSQL` TIMESTAMPTZ type - date and time with time zone
122    ///
123    /// See: <https://www.postgresql.org/docs/current/datatype-datetime.html>
124    Timestamptz,
125
126    /// `PostgreSQL` DATE type - calendar date
127    ///
128    /// See: <https://www.postgresql.org/docs/current/datatype-datetime.html>
129    Date,
130
131    /// `PostgreSQL` TIME type - time of day
132    ///
133    /// See: <https://www.postgresql.org/docs/current/datatype-datetime.html>
134    Time,
135
136    /// `PostgreSQL` TIMETZ type - time of day with time zone
137    ///
138    /// See: <https://www.postgresql.org/docs/current/datatype-datetime.html>
139    Timetz,
140
141    /// `PostgreSQL` INTERVAL type - time interval
142    ///
143    /// See: <https://www.postgresql.org/docs/current/datatype-datetime.html>
144    #[cfg(feature = "chrono")]
145    Interval,
146
147    /// `PostgreSQL` INET type - IPv4 or IPv6 host address
148    ///
149    /// See: <https://www.postgresql.org/docs/current/datatype-net-types.html>
150    #[cfg(feature = "cidr")]
151    Inet,
152
153    /// `PostgreSQL` CIDR type - IPv4 or IPv6 network address
154    ///
155    /// See: <https://www.postgresql.org/docs/current/datatype-net-types.html>
156    #[cfg(feature = "cidr")]
157    Cidr,
158
159    /// `PostgreSQL` MACADDR type - MAC address
160    ///
161    /// See: <https://www.postgresql.org/docs/current/datatype-net-types.html>
162    #[cfg(feature = "cidr")]
163    MacAddr,
164
165    /// `PostgreSQL` MACADDR8 type - EUI-64 MAC address
166    ///
167    /// See: <https://www.postgresql.org/docs/current/datatype-net-types.html>
168    #[cfg(feature = "cidr")]
169    MacAddr8,
170
171    /// `PostgreSQL` POINT type - geometric point
172    ///
173    /// See: <https://www.postgresql.org/docs/current/datatype-geometric.html>
174    #[cfg(feature = "geo-types")]
175    Point,
176
177    /// `PostgreSQL` LINE type - geometric line (infinite)
178    ///
179    /// See: <https://www.postgresql.org/docs/current/datatype-geometric.html>
180    #[cfg(feature = "geo-types")]
181    Line,
182
183    /// `PostgreSQL` LSEG type - geometric line segment
184    ///
185    /// See: <https://www.postgresql.org/docs/current/datatype-geometric.html>
186    #[cfg(feature = "geo-types")]
187    Lseg,
188
189    /// `PostgreSQL` BOX type - geometric box
190    ///
191    /// See: <https://www.postgresql.org/docs/current/datatype-geometric.html>
192    #[cfg(feature = "geo-types")]
193    Box,
194
195    /// `PostgreSQL` PATH type - geometric path
196    ///
197    /// See: <https://www.postgresql.org/docs/current/datatype-geometric.html>
198    #[cfg(feature = "geo-types")]
199    Path,
200
201    /// `PostgreSQL` POLYGON type - geometric polygon
202    ///
203    /// See: <https://www.postgresql.org/docs/current/datatype-geometric.html>
204    #[cfg(feature = "geo-types")]
205    Polygon,
206
207    /// `PostgreSQL` CIRCLE type - geometric circle
208    ///
209    /// See: <https://www.postgresql.org/docs/current/datatype-geometric.html>
210    #[cfg(feature = "geo-types")]
211    Circle,
212
213    /// `PostgreSQL` BIT type - fixed-length bit string
214    ///
215    /// See: <https://www.postgresql.org/docs/current/datatype-bit.html>
216    #[cfg(feature = "bit-vec")]
217    Bit,
218
219    /// `PostgreSQL` BIT VARYING type - variable-length bit string
220    ///
221    /// See: <https://www.postgresql.org/docs/current/datatype-bit.html>
222    #[cfg(feature = "bit-vec")]
223    Varbit,
224
225    /// `PostgreSQL` custom ENUM type - user-defined enumerated type
226    ///
227    /// See: <https://www.postgresql.org/docs/current/datatype-enum.html>
228    #[cfg(feature = "serde")]
229    Enum(String),
230}
231
232impl PostgreSQLType {
233    /// Convert from attribute name to enum variant
234    ///
235    /// For native enums, use [`Self::from_enum_attribute`] instead.
236    #[must_use]
237    pub fn from_attribute_name(name: &str) -> Option<Self> {
238        match name {
239            // Integer types and aliases
240            "integer" | "int" | "int4" => Some(Self::Integer),
241            "bigint" | "int8" => Some(Self::Bigint),
242            "smallint" | "int2" => Some(Self::Smallint),
243            "smallserial" | "serial2" => Some(Self::Smallserial),
244            "serial" | "serial4" => Some(Self::Serial),
245            "bigserial" | "serial8" => Some(Self::Bigserial),
246
247            // Text types and aliases
248            "text" => Some(Self::Text),
249            "varchar" | "character_varying" => Some(Self::Varchar),
250            "char" | "character" => Some(Self::Char),
251
252            // Float types and aliases
253            "real" | "float4" => Some(Self::Real),
254            "double_precision" | "float8" | "double" => Some(Self::DoublePrecision),
255            "numeric" | "decimal" => Some(Self::Numeric),
256
257            // Other basic types
258            "boolean" | "bool" => Some(Self::Boolean),
259            "bytea" => Some(Self::Bytea),
260
261            // UUID
262            #[cfg(feature = "uuid")]
263            "uuid" => Some(Self::Uuid),
264
265            // JSON types
266            #[cfg(feature = "serde")]
267            "json" => Some(Self::Json),
268            #[cfg(feature = "serde")]
269            "jsonb" => Some(Self::Jsonb),
270
271            // Date/time types and aliases
272            "timestamp" | "timestamp_without_time_zone" => Some(Self::Timestamp),
273            "timestamptz" | "timestamp_with_time_zone" => Some(Self::Timestamptz),
274            "date" => Some(Self::Date),
275            "time" | "time_without_time_zone" => Some(Self::Time),
276            "timetz" | "time_with_time_zone" => Some(Self::Timetz),
277            #[cfg(feature = "chrono")]
278            "interval" => Some(Self::Interval),
279
280            // Network address types
281            #[cfg(feature = "cidr")]
282            "inet" => Some(Self::Inet),
283            #[cfg(feature = "cidr")]
284            "cidr" => Some(Self::Cidr),
285            #[cfg(feature = "cidr")]
286            "macaddr" => Some(Self::MacAddr),
287            #[cfg(feature = "cidr")]
288            "macaddr8" => Some(Self::MacAddr8),
289
290            // Geometric types
291            #[cfg(feature = "geo-types")]
292            "point" => Some(Self::Point),
293            #[cfg(feature = "geo-types")]
294            "line" => Some(Self::Line),
295            #[cfg(feature = "geo-types")]
296            "lseg" => Some(Self::Lseg),
297            #[cfg(feature = "geo-types")]
298            "box" => Some(Self::Box),
299            #[cfg(feature = "geo-types")]
300            "path" => Some(Self::Path),
301            #[cfg(feature = "geo-types")]
302            "polygon" => Some(Self::Polygon),
303            #[cfg(feature = "geo-types")]
304            "circle" => Some(Self::Circle),
305
306            // Bit string types
307            #[cfg(feature = "bit-vec")]
308            "bit" => Some(Self::Bit),
309            #[cfg(feature = "bit-vec")]
310            "varbit" | "bit_varying" => Some(Self::Varbit),
311
312            "enum" => None, // enum() requires a parameter, handled separately
313            _ => None,
314        }
315    }
316
317    /// Create a native `PostgreSQL` enum type from enum attribute
318    ///
319    /// Used for `#[enum(MyEnum)]` syntax.
320    #[cfg(feature = "serde")]
321    #[must_use]
322    pub fn from_enum_attribute(enum_name: &str) -> Self {
323        Self::Enum(String::from(enum_name))
324    }
325
326    /// Get the SQL type string for this type
327    #[must_use]
328    pub const fn to_sql_type(&self) -> &str {
329        match self {
330            Self::Integer => "INTEGER",
331            Self::Bigint => "BIGINT",
332            Self::Smallint => "SMALLINT",
333            Self::Smallserial => "SMALLSERIAL",
334            Self::Serial => "SERIAL",
335            Self::Bigserial => "BIGSERIAL",
336            Self::Text => "TEXT",
337            Self::Varchar => "VARCHAR",
338            Self::Char => "CHAR",
339            Self::Real => "REAL",
340            Self::DoublePrecision => "DOUBLE PRECISION",
341            Self::Numeric => "NUMERIC",
342            Self::Boolean => "BOOLEAN",
343            Self::Bytea => "BYTEA",
344            #[cfg(feature = "uuid")]
345            Self::Uuid => "UUID",
346            #[cfg(feature = "serde")]
347            Self::Json => "JSON",
348            #[cfg(feature = "serde")]
349            Self::Jsonb => "JSONB",
350            Self::Timestamp => "TIMESTAMP",
351            Self::Timestamptz => "TIMESTAMPTZ",
352            Self::Date => "DATE",
353            Self::Time => "TIME",
354            Self::Timetz => "TIMETZ",
355            #[cfg(feature = "chrono")]
356            Self::Interval => "INTERVAL",
357            #[cfg(feature = "cidr")]
358            Self::Inet => "INET",
359            #[cfg(feature = "cidr")]
360            Self::Cidr => "CIDR",
361            #[cfg(feature = "cidr")]
362            Self::MacAddr => "MACADDR",
363            #[cfg(feature = "cidr")]
364            Self::MacAddr8 => "MACADDR8",
365            #[cfg(feature = "geo-types")]
366            Self::Point => "POINT",
367            #[cfg(feature = "geo-types")]
368            Self::Line => "LINE",
369            #[cfg(feature = "geo-types")]
370            Self::Lseg => "LSEG",
371            #[cfg(feature = "geo-types")]
372            Self::Box => "BOX",
373            #[cfg(feature = "geo-types")]
374            Self::Path => "PATH",
375            #[cfg(feature = "geo-types")]
376            Self::Polygon => "POLYGON",
377            #[cfg(feature = "geo-types")]
378            Self::Circle => "CIRCLE",
379            #[cfg(feature = "bit-vec")]
380            Self::Bit => "BIT",
381            #[cfg(feature = "bit-vec")]
382            Self::Varbit => "VARBIT",
383            #[cfg(feature = "serde")]
384            Self::Enum(name) => name.as_str(), // Custom enum type name
385        }
386    }
387
388    /// Check if this type is an auto-incrementing type
389    #[must_use]
390    pub const fn is_serial(&self) -> bool {
391        matches!(self, Self::Smallserial | Self::Serial | Self::Bigserial)
392    }
393
394    /// Check if this type supports primary keys
395    #[must_use]
396    pub const fn supports_primary_key(&self) -> bool {
397        core::cfg_select! {
398            feature = "serde" => !matches!(self, Self::Json | Self::Jsonb),
399            _ => true,
400        }
401    }
402
403    /// Check if a flag is valid for this column type
404    #[must_use]
405    pub fn is_valid_flag(&self, flag: &str) -> bool {
406        match (self, flag) {
407            (Self::Smallserial | Self::Serial | Self::Bigserial, "identity") => true,
408            (Self::Text | Self::Bytea, "json") => true,
409            #[cfg(feature = "serde")]
410            (Self::Json | Self::Jsonb, "json") => true,
411            (Self::Text | Self::Integer | Self::Smallint | Self::Bigint, "enum") => true,
412            #[cfg(feature = "serde")]
413            (Self::Enum(_), "enum") => true,
414            (_, "primary" | "primary_key" | "unique" | "not_null" | "check") => true,
415            _ => false,
416        }
417    }
418}
419
420impl core::fmt::Display for PostgreSQLType {
421    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
422        f.write_str(self.to_sql_type())
423    }
424}
425
426#[cfg(test)]
427mod tests {
428    use super::*;
429
430    #[test]
431    fn test_from_attribute_name() {
432        assert_eq!(
433            PostgreSQLType::from_attribute_name("integer"),
434            Some(PostgreSQLType::Integer)
435        );
436        assert_eq!(
437            PostgreSQLType::from_attribute_name("int"),
438            Some(PostgreSQLType::Integer)
439        );
440        assert_eq!(
441            PostgreSQLType::from_attribute_name("text"),
442            Some(PostgreSQLType::Text)
443        );
444        assert_eq!(
445            PostgreSQLType::from_attribute_name("varchar"),
446            Some(PostgreSQLType::Varchar)
447        );
448        assert_eq!(
449            PostgreSQLType::from_attribute_name("serial"),
450            Some(PostgreSQLType::Serial)
451        );
452        assert_eq!(
453            PostgreSQLType::from_attribute_name("smallserial"),
454            Some(PostgreSQLType::Smallserial)
455        );
456        assert_eq!(PostgreSQLType::from_attribute_name("unknown"), None);
457    }
458
459    #[test]
460    fn test_to_sql_type() {
461        assert_eq!(PostgreSQLType::Integer.to_sql_type(), "INTEGER");
462        assert_eq!(PostgreSQLType::Smallserial.to_sql_type(), "SMALLSERIAL");
463        assert_eq!(PostgreSQLType::Bigint.to_sql_type(), "BIGINT");
464        assert_eq!(PostgreSQLType::Text.to_sql_type(), "TEXT");
465        assert_eq!(
466            PostgreSQLType::DoublePrecision.to_sql_type(),
467            "DOUBLE PRECISION"
468        );
469        assert_eq!(PostgreSQLType::Boolean.to_sql_type(), "BOOLEAN");
470    }
471
472    #[test]
473    fn test_is_serial() {
474        assert!(PostgreSQLType::Smallserial.is_serial());
475        assert!(PostgreSQLType::Serial.is_serial());
476        assert!(PostgreSQLType::Bigserial.is_serial());
477        assert!(!PostgreSQLType::Integer.is_serial());
478        assert!(!PostgreSQLType::Text.is_serial());
479    }
480}