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}