drizzle_types/postgres/
sql_type.rs

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