Skip to main content

drizzle_postgres/values/
mod.rs

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