drizzle_postgres/values/
mod.rs

1//! PostgreSQL value conversion traits and types
2
3mod conversions;
4mod drivers;
5mod insert;
6mod owned;
7
8pub use insert::*;
9pub use owned::*;
10
11use drizzle_core::{error::DrizzleError, sql::SQL, traits::SQLParam};
12
13#[cfg(feature = "uuid")]
14use uuid::Uuid;
15
16#[cfg(feature = "chrono")]
17use chrono::{DateTime, Duration, FixedOffset, NaiveDate, NaiveDateTime, NaiveTime};
18
19#[cfg(feature = "cidr")]
20use cidr::{IpCidr, IpInet};
21
22#[cfg(feature = "geo-types")]
23use geo_types::{LineString, Point, Rect};
24
25#[cfg(feature = "bit-vec")]
26use bit_vec::BitVec;
27
28use std::borrow::Cow;
29
30use crate::{PostgresEnum, traits::FromPostgresValue};
31
32//------------------------------------------------------------------------------
33// PostgresValue Definition
34//------------------------------------------------------------------------------
35
36/// Represents a PostgreSQL value.
37///
38/// This enum provides type-safe value handling for PostgreSQL operations.
39///
40/// # Examples
41///
42/// ```
43/// use drizzle_postgres::values::PostgresValue;
44///
45/// // Integer conversion
46/// let int_val: PostgresValue<'_> = 42i32.into();
47/// assert!(matches!(int_val, PostgresValue::Integer(42)));
48///
49/// // String conversion
50/// let str_val: PostgresValue<'_> = "hello".into();
51/// assert!(matches!(str_val, PostgresValue::Text(_)));
52///
53/// // Boolean conversion
54/// let bool_val: PostgresValue<'_> = true.into();
55/// assert!(matches!(bool_val, PostgresValue::Boolean(true)));
56/// ```
57#[derive(Debug, Clone, PartialEq, Default)]
58pub enum PostgresValue<'a> {
59    /// SMALLINT values (16-bit signed integer)
60    Smallint(i16),
61    /// INTEGER values (32-bit signed integer)
62    Integer(i32),
63    /// BIGINT values (64-bit signed integer)
64    Bigint(i64),
65    /// REAL values (32-bit floating point)
66    Real(f32),
67    /// DOUBLE PRECISION values (64-bit floating point)
68    DoublePrecision(f64),
69    /// TEXT, VARCHAR, CHAR values
70    Text(Cow<'a, str>),
71    /// BYTEA values (binary data)
72    Bytea(Cow<'a, [u8]>),
73    /// BOOLEAN values
74    Boolean(bool),
75    /// UUID values
76    #[cfg(feature = "uuid")]
77    Uuid(Uuid),
78    /// JSON values (stored as text in PostgreSQL)
79    #[cfg(feature = "serde")]
80    Json(serde_json::Value),
81    /// JSONB values (stored as binary in PostgreSQL)
82    #[cfg(feature = "serde")]
83    Jsonb(serde_json::Value),
84    /// Native PostgreSQL ENUM values
85    Enum(Box<dyn PostgresEnum>),
86
87    // Date and time types
88    /// DATE values
89    #[cfg(feature = "chrono")]
90    Date(NaiveDate),
91    /// TIME values
92    #[cfg(feature = "chrono")]
93    Time(NaiveTime),
94    /// TIMESTAMP values (without timezone)
95    #[cfg(feature = "chrono")]
96    Timestamp(NaiveDateTime),
97    /// TIMESTAMPTZ values (with timezone)
98    #[cfg(feature = "chrono")]
99    TimestampTz(DateTime<FixedOffset>),
100    /// INTERVAL values
101    #[cfg(feature = "chrono")]
102    Interval(Duration),
103
104    // Network address types
105    /// INET values (host address with optional netmask)
106    #[cfg(feature = "cidr")]
107    Inet(IpInet),
108    /// CIDR values (network specification)
109    #[cfg(feature = "cidr")]
110    Cidr(IpCidr),
111    /// MACADDR values (MAC addresses)
112    #[cfg(feature = "cidr")]
113    MacAddr([u8; 6]),
114    /// MACADDR8 values (EUI-64 MAC addresses)
115    #[cfg(feature = "cidr")]
116    MacAddr8([u8; 8]),
117
118    // Geometric types (native PostgreSQL support via postgres-rs)
119    /// POINT values
120    #[cfg(feature = "geo-types")]
121    Point(Point<f64>),
122    /// PATH values (open path from LineString)
123    #[cfg(feature = "geo-types")]
124    LineString(LineString<f64>),
125    /// BOX values (bounding rectangle)
126    #[cfg(feature = "geo-types")]
127    Rect(Rect<f64>),
128
129    // Bit string types
130    /// BIT, BIT VARYING values
131    #[cfg(feature = "bit-vec")]
132    BitVec(BitVec),
133
134    // Array types (using Vec for simplicity)
135    /// Array of any PostgreSQL type
136    Array(Vec<PostgresValue<'a>>),
137
138    /// NULL value
139    #[default]
140    Null,
141}
142
143impl<'a> std::fmt::Display for PostgresValue<'a> {
144    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
145        let value = match self {
146            PostgresValue::Smallint(i) => i.to_string(),
147            PostgresValue::Integer(i) => i.to_string(),
148            PostgresValue::Bigint(i) => i.to_string(),
149            PostgresValue::Real(r) => r.to_string(),
150            PostgresValue::DoublePrecision(r) => r.to_string(),
151            PostgresValue::Text(cow) => cow.to_string(),
152            PostgresValue::Bytea(cow) => format!(
153                "\\x{}",
154                cow.iter().map(|b| format!("{:02x}", b)).collect::<String>()
155            ),
156            PostgresValue::Boolean(b) => b.to_string(),
157            #[cfg(feature = "uuid")]
158            PostgresValue::Uuid(uuid) => uuid.to_string(),
159            #[cfg(feature = "serde")]
160            PostgresValue::Json(json) => json.to_string(),
161            #[cfg(feature = "serde")]
162            PostgresValue::Jsonb(json) => json.to_string(),
163            PostgresValue::Enum(enum_val) => enum_val.variant_name().to_string(),
164
165            // Date and time types
166            #[cfg(feature = "chrono")]
167            PostgresValue::Date(date) => date.format("%Y-%m-%d").to_string(),
168            #[cfg(feature = "chrono")]
169            PostgresValue::Time(time) => time.format("%H:%M:%S%.f").to_string(),
170            #[cfg(feature = "chrono")]
171            PostgresValue::Timestamp(ts) => ts.format("%Y-%m-%d %H:%M:%S%.f").to_string(),
172            #[cfg(feature = "chrono")]
173            PostgresValue::TimestampTz(ts) => ts.format("%Y-%m-%d %H:%M:%S%.f %:z").to_string(),
174            #[cfg(feature = "chrono")]
175            PostgresValue::Interval(dur) => format!("{} seconds", dur.num_seconds()),
176
177            // Network address types
178            #[cfg(feature = "cidr")]
179            PostgresValue::Inet(net) => net.to_string(),
180            #[cfg(feature = "cidr")]
181            PostgresValue::Cidr(net) => net.to_string(),
182            #[cfg(feature = "cidr")]
183            PostgresValue::MacAddr(mac) => format!(
184                "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
185                mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
186            ),
187            #[cfg(feature = "cidr")]
188            PostgresValue::MacAddr8(mac) => format!(
189                "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
190                mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], mac[6], mac[7]
191            ),
192
193            // Geometric types
194            #[cfg(feature = "geo-types")]
195            PostgresValue::Point(point) => format!("({},{})", point.x(), point.y()),
196            #[cfg(feature = "geo-types")]
197            PostgresValue::LineString(line) => {
198                let coords: Vec<String> = line
199                    .coords()
200                    .map(|coord| format!("({},{})", coord.x, coord.y))
201                    .collect();
202                format!("[{}]", coords.join(","))
203            }
204            #[cfg(feature = "geo-types")]
205            PostgresValue::Rect(rect) => {
206                format!(
207                    "(({},{}),({},{}))",
208                    rect.min().x,
209                    rect.min().y,
210                    rect.max().x,
211                    rect.max().y
212                )
213            }
214
215            // Bit string types
216            #[cfg(feature = "bit-vec")]
217            PostgresValue::BitVec(bv) => bv
218                .iter()
219                .map(|b| if b { '1' } else { '0' })
220                .collect::<String>(),
221
222            // Array types
223            PostgresValue::Array(arr) => {
224                let elements: Vec<String> = arr.iter().map(|v| v.to_string()).collect();
225                format!("{{{}}}", elements.join(","))
226            }
227
228            PostgresValue::Null => String::new(),
229        };
230        write!(f, "{value}")
231    }
232}
233
234impl<'a> PostgresValue<'a> {
235    /// Returns true if this value is NULL.
236    #[inline]
237    pub const fn is_null(&self) -> bool {
238        matches!(self, PostgresValue::Null)
239    }
240
241    /// Returns the boolean value if this is BOOLEAN.
242    #[inline]
243    pub const fn as_bool(&self) -> Option<bool> {
244        match self {
245            PostgresValue::Boolean(value) => Some(*value),
246            _ => None,
247        }
248    }
249
250    /// Returns the i16 value if this is SMALLINT.
251    #[inline]
252    pub const fn as_i16(&self) -> Option<i16> {
253        match self {
254            PostgresValue::Smallint(value) => Some(*value),
255            _ => None,
256        }
257    }
258
259    /// Returns the i32 value if this is INTEGER.
260    #[inline]
261    pub const fn as_i32(&self) -> Option<i32> {
262        match self {
263            PostgresValue::Integer(value) => Some(*value),
264            _ => None,
265        }
266    }
267
268    /// Returns the i64 value if this is BIGINT.
269    #[inline]
270    pub const fn as_i64(&self) -> Option<i64> {
271        match self {
272            PostgresValue::Bigint(value) => Some(*value),
273            _ => None,
274        }
275    }
276
277    /// Returns the f32 value if this is REAL.
278    #[inline]
279    pub const fn as_f32(&self) -> Option<f32> {
280        match self {
281            PostgresValue::Real(value) => Some(*value),
282            _ => None,
283        }
284    }
285
286    /// Returns the f64 value if this is DOUBLE PRECISION.
287    #[inline]
288    pub const fn as_f64(&self) -> Option<f64> {
289        match self {
290            PostgresValue::DoublePrecision(value) => Some(*value),
291            _ => None,
292        }
293    }
294
295    /// Returns the text value if this is TEXT.
296    #[inline]
297    pub fn as_str(&self) -> Option<&str> {
298        match self {
299            PostgresValue::Text(value) => Some(value.as_ref()),
300            _ => None,
301        }
302    }
303
304    /// Returns the bytea value if this is BYTEA.
305    #[inline]
306    pub fn as_bytes(&self) -> Option<&[u8]> {
307        match self {
308            PostgresValue::Bytea(value) => Some(value.as_ref()),
309            _ => None,
310        }
311    }
312
313    /// Returns the UUID value if this is UUID.
314    #[inline]
315    #[cfg(feature = "uuid")]
316    pub fn as_uuid(&self) -> Option<Uuid> {
317        match self {
318            PostgresValue::Uuid(value) => Some(*value),
319            _ => None,
320        }
321    }
322
323    /// Returns the JSON value if this is JSON.
324    #[inline]
325    #[cfg(feature = "serde")]
326    pub fn as_json(&self) -> Option<&serde_json::Value> {
327        match self {
328            PostgresValue::Json(value) => Some(value),
329            _ => None,
330        }
331    }
332
333    /// Returns the JSONB value if this is JSONB.
334    #[inline]
335    #[cfg(feature = "serde")]
336    pub fn as_jsonb(&self) -> Option<&serde_json::Value> {
337        match self {
338            PostgresValue::Jsonb(value) => Some(value),
339            _ => None,
340        }
341    }
342
343    /// Returns the enum value if this is a PostgreSQL enum.
344    #[inline]
345    pub fn as_enum(&self) -> Option<&dyn PostgresEnum> {
346        match self {
347            PostgresValue::Enum(value) => Some(value.as_ref()),
348            _ => None,
349        }
350    }
351
352    /// Returns the date value if this is DATE.
353    #[inline]
354    #[cfg(feature = "chrono")]
355    pub fn as_date(&self) -> Option<&NaiveDate> {
356        match self {
357            PostgresValue::Date(value) => Some(value),
358            _ => None,
359        }
360    }
361
362    /// Returns the time value if this is TIME.
363    #[inline]
364    #[cfg(feature = "chrono")]
365    pub fn as_time(&self) -> Option<&NaiveTime> {
366        match self {
367            PostgresValue::Time(value) => Some(value),
368            _ => None,
369        }
370    }
371
372    /// Returns the timestamp value if this is TIMESTAMP.
373    #[inline]
374    #[cfg(feature = "chrono")]
375    pub fn as_timestamp(&self) -> Option<&NaiveDateTime> {
376        match self {
377            PostgresValue::Timestamp(value) => Some(value),
378            _ => None,
379        }
380    }
381
382    /// Returns the timestamp with timezone value if this is TIMESTAMPTZ.
383    #[inline]
384    #[cfg(feature = "chrono")]
385    pub fn as_timestamp_tz(&self) -> Option<&DateTime<FixedOffset>> {
386        match self {
387            PostgresValue::TimestampTz(value) => Some(value),
388            _ => None,
389        }
390    }
391
392    /// Returns the interval value if this is INTERVAL.
393    #[inline]
394    #[cfg(feature = "chrono")]
395    pub fn as_interval(&self) -> Option<&Duration> {
396        match self {
397            PostgresValue::Interval(value) => Some(value),
398            _ => None,
399        }
400    }
401
402    /// Returns the inet value if this is INET.
403    #[inline]
404    #[cfg(feature = "cidr")]
405    pub fn as_inet(&self) -> Option<&IpInet> {
406        match self {
407            PostgresValue::Inet(value) => Some(value),
408            _ => None,
409        }
410    }
411
412    /// Returns the cidr value if this is CIDR.
413    #[inline]
414    #[cfg(feature = "cidr")]
415    pub fn as_cidr(&self) -> Option<&IpCidr> {
416        match self {
417            PostgresValue::Cidr(value) => Some(value),
418            _ => None,
419        }
420    }
421
422    /// Returns the MAC address if this is MACADDR.
423    #[inline]
424    #[cfg(feature = "cidr")]
425    pub const fn as_macaddr(&self) -> Option<[u8; 6]> {
426        match self {
427            PostgresValue::MacAddr(value) => Some(*value),
428            _ => None,
429        }
430    }
431
432    /// Returns the MAC address if this is MACADDR8.
433    #[inline]
434    #[cfg(feature = "cidr")]
435    pub const fn as_macaddr8(&self) -> Option<[u8; 8]> {
436        match self {
437            PostgresValue::MacAddr8(value) => Some(*value),
438            _ => None,
439        }
440    }
441
442    /// Returns the point value if this is POINT.
443    #[inline]
444    #[cfg(feature = "geo-types")]
445    pub fn as_point(&self) -> Option<&Point<f64>> {
446        match self {
447            PostgresValue::Point(value) => Some(value),
448            _ => None,
449        }
450    }
451
452    /// Returns the line string value if this is PATH.
453    #[inline]
454    #[cfg(feature = "geo-types")]
455    pub fn as_line_string(&self) -> Option<&LineString<f64>> {
456        match self {
457            PostgresValue::LineString(value) => Some(value),
458            _ => None,
459        }
460    }
461
462    /// Returns the rect value if this is BOX.
463    #[inline]
464    #[cfg(feature = "geo-types")]
465    pub fn as_rect(&self) -> Option<&Rect<f64>> {
466        match self {
467            PostgresValue::Rect(value) => Some(value),
468            _ => None,
469        }
470    }
471
472    /// Returns the bit vector if this is BIT/VARBIT.
473    #[inline]
474    #[cfg(feature = "bit-vec")]
475    pub fn as_bitvec(&self) -> Option<&BitVec> {
476        match self {
477            PostgresValue::BitVec(value) => Some(value),
478            _ => None,
479        }
480    }
481
482    /// Returns the array elements if this is an ARRAY.
483    #[inline]
484    pub fn as_array(&self) -> Option<&[PostgresValue<'a>]> {
485        match self {
486            PostgresValue::Array(values) => Some(values),
487            _ => None,
488        }
489    }
490
491    /// Converts this value into an owned representation.
492    #[inline]
493    pub fn into_owned(self) -> OwnedPostgresValue {
494        self.into()
495    }
496
497    /// Convert this PostgreSQL value to a Rust type using the `FromPostgresValue` trait.
498    pub fn convert<T: FromPostgresValue>(self) -> Result<T, DrizzleError> {
499        match self {
500            PostgresValue::Boolean(value) => T::from_postgres_bool(value),
501            PostgresValue::Smallint(value) => T::from_postgres_i16(value),
502            PostgresValue::Integer(value) => T::from_postgres_i32(value),
503            PostgresValue::Bigint(value) => T::from_postgres_i64(value),
504            PostgresValue::Real(value) => T::from_postgres_f32(value),
505            PostgresValue::DoublePrecision(value) => T::from_postgres_f64(value),
506            PostgresValue::Text(value) => T::from_postgres_text(&value),
507            PostgresValue::Bytea(value) => T::from_postgres_bytes(&value),
508            #[cfg(feature = "uuid")]
509            PostgresValue::Uuid(value) => T::from_postgres_uuid(value),
510            #[cfg(feature = "serde")]
511            PostgresValue::Json(value) => T::from_postgres_json(value),
512            #[cfg(feature = "serde")]
513            PostgresValue::Jsonb(value) => T::from_postgres_jsonb(value),
514            PostgresValue::Enum(value) => T::from_postgres_text(value.variant_name()),
515            #[cfg(feature = "chrono")]
516            PostgresValue::Date(value) => T::from_postgres_date(value),
517            #[cfg(feature = "chrono")]
518            PostgresValue::Time(value) => T::from_postgres_time(value),
519            #[cfg(feature = "chrono")]
520            PostgresValue::Timestamp(value) => T::from_postgres_timestamp(value),
521            #[cfg(feature = "chrono")]
522            PostgresValue::TimestampTz(value) => T::from_postgres_timestamptz(value),
523            #[cfg(feature = "chrono")]
524            PostgresValue::Interval(value) => T::from_postgres_interval(value),
525            #[cfg(feature = "cidr")]
526            PostgresValue::Inet(value) => T::from_postgres_inet(value),
527            #[cfg(feature = "cidr")]
528            PostgresValue::Cidr(value) => T::from_postgres_cidr(value),
529            #[cfg(feature = "cidr")]
530            PostgresValue::MacAddr(value) => T::from_postgres_macaddr(value),
531            #[cfg(feature = "cidr")]
532            PostgresValue::MacAddr8(value) => T::from_postgres_macaddr8(value),
533            #[cfg(feature = "geo-types")]
534            PostgresValue::Point(value) => T::from_postgres_point(value),
535            #[cfg(feature = "geo-types")]
536            PostgresValue::LineString(value) => T::from_postgres_linestring(value),
537            #[cfg(feature = "geo-types")]
538            PostgresValue::Rect(value) => T::from_postgres_rect(value),
539            #[cfg(feature = "bit-vec")]
540            PostgresValue::BitVec(value) => T::from_postgres_bitvec(value),
541            PostgresValue::Array(value) => T::from_postgres_array(value),
542            PostgresValue::Null => T::from_postgres_null(),
543        }
544    }
545
546    /// Convert a reference to this PostgreSQL value to a Rust type.
547    pub fn convert_ref<T: FromPostgresValue>(&self) -> Result<T, DrizzleError> {
548        match self {
549            PostgresValue::Boolean(value) => T::from_postgres_bool(*value),
550            PostgresValue::Smallint(value) => T::from_postgres_i16(*value),
551            PostgresValue::Integer(value) => T::from_postgres_i32(*value),
552            PostgresValue::Bigint(value) => T::from_postgres_i64(*value),
553            PostgresValue::Real(value) => T::from_postgres_f32(*value),
554            PostgresValue::DoublePrecision(value) => T::from_postgres_f64(*value),
555            PostgresValue::Text(value) => T::from_postgres_text(value),
556            PostgresValue::Bytea(value) => T::from_postgres_bytes(value),
557            #[cfg(feature = "uuid")]
558            PostgresValue::Uuid(value) => T::from_postgres_uuid(*value),
559            #[cfg(feature = "serde")]
560            PostgresValue::Json(value) => T::from_postgres_json(value.clone()),
561            #[cfg(feature = "serde")]
562            PostgresValue::Jsonb(value) => T::from_postgres_jsonb(value.clone()),
563            PostgresValue::Enum(value) => T::from_postgres_text(value.variant_name()),
564            #[cfg(feature = "chrono")]
565            PostgresValue::Date(value) => T::from_postgres_date(*value),
566            #[cfg(feature = "chrono")]
567            PostgresValue::Time(value) => T::from_postgres_time(*value),
568            #[cfg(feature = "chrono")]
569            PostgresValue::Timestamp(value) => T::from_postgres_timestamp(*value),
570            #[cfg(feature = "chrono")]
571            PostgresValue::TimestampTz(value) => T::from_postgres_timestamptz(*value),
572            #[cfg(feature = "chrono")]
573            PostgresValue::Interval(value) => T::from_postgres_interval(*value),
574            #[cfg(feature = "cidr")]
575            PostgresValue::Inet(value) => T::from_postgres_inet(*value),
576            #[cfg(feature = "cidr")]
577            PostgresValue::Cidr(value) => T::from_postgres_cidr(*value),
578            #[cfg(feature = "cidr")]
579            PostgresValue::MacAddr(value) => T::from_postgres_macaddr(*value),
580            #[cfg(feature = "cidr")]
581            PostgresValue::MacAddr8(value) => T::from_postgres_macaddr8(*value),
582            #[cfg(feature = "geo-types")]
583            PostgresValue::Point(value) => T::from_postgres_point(*value),
584            #[cfg(feature = "geo-types")]
585            PostgresValue::LineString(value) => T::from_postgres_linestring(value.clone()),
586            #[cfg(feature = "geo-types")]
587            PostgresValue::Rect(value) => T::from_postgres_rect(*value),
588            #[cfg(feature = "bit-vec")]
589            PostgresValue::BitVec(value) => T::from_postgres_bitvec(value.clone()),
590            PostgresValue::Array(value) => T::from_postgres_array(value.clone()),
591            PostgresValue::Null => T::from_postgres_null(),
592        }
593    }
594}
595
596// Implement core traits required by Drizzle
597impl<'a> SQLParam for PostgresValue<'a> {
598    const DIALECT: drizzle_core::dialect::Dialect = drizzle_core::dialect::Dialect::PostgreSQL;
599}
600
601impl<'a> From<PostgresValue<'a>> for SQL<'a, PostgresValue<'a>> {
602    fn from(value: PostgresValue<'a>) -> Self {
603        SQL::param(value)
604    }
605}
606
607// Cow integration for SQL struct
608impl<'a> From<PostgresValue<'a>> for Cow<'a, PostgresValue<'a>> {
609    fn from(value: PostgresValue<'a>) -> Self {
610        Cow::Owned(value)
611    }
612}
613
614impl<'a> From<&'a PostgresValue<'a>> for Cow<'a, PostgresValue<'a>> {
615    fn from(value: &'a PostgresValue<'a>) -> Self {
616        Cow::Borrowed(value)
617    }
618}