Skip to main content

drizzle_postgres/values/
mod.rs

1//! `PostgreSQL` value conversion traits and types
2
3mod conversions;
4mod drivers;
5mod insert;
6#[cfg(feature = "serde")]
7mod json;
8mod owned;
9mod update;
10
11pub use insert::*;
12#[cfg(feature = "serde")]
13pub use json::PostgresJsonType;
14pub use owned::*;
15pub use update::*;
16
17use drizzle_core::{error::DrizzleError, sql::SQL, traits::SQLParam};
18
19#[cfg(feature = "uuid")]
20use uuid::Uuid;
21
22#[cfg(feature = "chrono")]
23use chrono::{DateTime, Duration, FixedOffset, NaiveDate, NaiveDateTime, NaiveTime};
24
25#[cfg(feature = "time")]
26use time::{
27    Date as TimeDate, Duration as TimeDuration, OffsetDateTime, PrimitiveDateTime, Time as TimeTime,
28};
29
30#[cfg(feature = "jiff")]
31use jiff::{
32    Timestamp as JiffTimestamp,
33    civil::{Date as JiffDate, DateTime as JiffDateTime, Time as JiffTime},
34};
35
36#[cfg(feature = "cidr")]
37use cidr::{IpCidr, IpInet};
38
39#[cfg(feature = "geo-types")]
40use geo_types::{LineString, Point, Rect};
41
42#[cfg(feature = "bit-vec")]
43use bit_vec::BitVec;
44
45#[cfg(feature = "rust-decimal")]
46use rust_decimal::Decimal;
47
48use crate::prelude::*;
49
50use crate::traits::{FromPostgresValue, PostgresEnum};
51
52//------------------------------------------------------------------------------
53// PostgresValue Definition
54//------------------------------------------------------------------------------
55
56/// Represents a `PostgreSQL` value.
57///
58/// This enum provides type-safe value handling for `PostgreSQL` operations.
59///
60/// # Examples
61///
62/// ```
63/// use drizzle_postgres::values::PostgresValue;
64///
65/// // Integer conversion
66/// let int_val: PostgresValue<'_> = 42i32.into();
67/// assert!(matches!(int_val, PostgresValue::Integer(42)));
68///
69/// // String conversion
70/// let str_val: PostgresValue<'_> = "hello".into();
71/// assert!(matches!(str_val, PostgresValue::Text(_)));
72///
73/// // Boolean conversion
74/// let bool_val: PostgresValue<'_> = true.into();
75/// assert!(matches!(bool_val, PostgresValue::Boolean(true)));
76/// ```
77#[derive(Debug, Clone, PartialEq, Default)]
78pub enum PostgresValue<'a> {
79    /// SMALLINT values (16-bit signed integer)
80    Smallint(i16),
81    /// INTEGER values (32-bit signed integer)
82    Integer(i32),
83    /// BIGINT values (64-bit signed integer)
84    Bigint(i64),
85    /// REAL values (32-bit floating point)
86    Real(f32),
87    /// DOUBLE PRECISION values (64-bit floating point)
88    DoublePrecision(f64),
89    /// NUMERIC/DECIMAL values
90    #[cfg(feature = "rust-decimal")]
91    Numeric(Decimal),
92    /// TEXT, VARCHAR, CHAR values
93    Text(Cow<'a, str>),
94    /// BYTEA values (binary data)
95    Bytea(Cow<'a, [u8]>),
96    /// BOOLEAN values
97    Boolean(bool),
98    /// UUID values
99    #[cfg(feature = "uuid")]
100    Uuid(Uuid),
101    /// JSON values (stored as text in `PostgreSQL`)
102    #[cfg(feature = "serde")]
103    Json(serde_json::Value),
104    /// JSONB values (stored as binary in `PostgreSQL`)
105    #[cfg(feature = "serde")]
106    Jsonb(serde_json::Value),
107    /// Native `PostgreSQL` ENUM values
108    Enum(Box<dyn PostgresEnum>),
109
110    // Date and time types
111    /// DATE values
112    #[cfg(feature = "chrono")]
113    Date(NaiveDate),
114    /// TIME values
115    #[cfg(feature = "chrono")]
116    Time(NaiveTime),
117    /// TIMESTAMP values (without timezone)
118    #[cfg(feature = "chrono")]
119    Timestamp(NaiveDateTime),
120    /// TIMESTAMPTZ values (with timezone)
121    #[cfg(feature = "chrono")]
122    TimestampTz(DateTime<FixedOffset>),
123    /// INTERVAL values
124    #[cfg(feature = "chrono")]
125    Interval(Duration),
126
127    // Date and time types (time crate)
128    /// DATE values (time crate)
129    #[cfg(feature = "time")]
130    TimeDate(TimeDate),
131    /// TIME values (time crate)
132    #[cfg(feature = "time")]
133    TimeTime(TimeTime),
134    /// TIMESTAMP values without timezone (time crate)
135    #[cfg(feature = "time")]
136    TimeTimestamp(PrimitiveDateTime),
137    /// TIMESTAMPTZ values with timezone (time crate)
138    #[cfg(feature = "time")]
139    TimeTimestampTz(OffsetDateTime),
140    /// INTERVAL values (time crate)
141    #[cfg(feature = "time")]
142    TimeInterval(TimeDuration),
143    /// DATE values (`jiff::civil::Date`)
144    #[cfg(feature = "jiff")]
145    JiffDate(JiffDate),
146    /// TIME values (`jiff::civil::Time`)
147    #[cfg(feature = "jiff")]
148    JiffTime(JiffTime),
149    /// TIMESTAMP values without timezone (`jiff::civil::DateTime`)
150    #[cfg(feature = "jiff")]
151    JiffDateTime(JiffDateTime),
152    /// TIMESTAMPTZ values (`jiff::Timestamp`, an instant)
153    #[cfg(feature = "jiff")]
154    JiffTimestamp(JiffTimestamp),
155
156    // Network address types
157    /// INET values (host address with optional netmask)
158    #[cfg(feature = "cidr")]
159    Inet(IpInet),
160    /// CIDR values (network specification)
161    #[cfg(feature = "cidr")]
162    Cidr(IpCidr),
163    /// MACADDR values (MAC addresses)
164    #[cfg(feature = "cidr")]
165    MacAddr([u8; 6]),
166    /// MACADDR8 values (EUI-64 MAC addresses)
167    #[cfg(feature = "cidr")]
168    MacAddr8([u8; 8]),
169
170    // Geometric types (native PostgreSQL support via postgres-rs)
171    /// POINT values
172    #[cfg(feature = "geo-types")]
173    Point(Point<f64>),
174    /// PATH values (open path from `LineString`)
175    #[cfg(feature = "geo-types")]
176    LineString(LineString<f64>),
177    /// BOX values (bounding rectangle)
178    #[cfg(feature = "geo-types")]
179    Rect(Rect<f64>),
180
181    // Bit string types
182    /// BIT, BIT VARYING values
183    #[cfg(feature = "bit-vec")]
184    BitVec(BitVec),
185
186    // Array types (using Vec for simplicity)
187    /// Array of any `PostgreSQL` type
188    Array(Vec<Self>),
189
190    /// NULL value
191    #[default]
192    Null,
193}
194
195impl core::fmt::Display for PostgresValue<'_> {
196    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
197        let value = match self {
198            PostgresValue::Smallint(i) => i.to_string(),
199            PostgresValue::Integer(i) => i.to_string(),
200            PostgresValue::Bigint(i) => i.to_string(),
201            PostgresValue::Real(r) => r.to_string(),
202            PostgresValue::DoublePrecision(r) => r.to_string(),
203            #[cfg(feature = "rust-decimal")]
204            PostgresValue::Numeric(d) => d.to_string(),
205            PostgresValue::Text(cow) => cow.to_string(),
206            PostgresValue::Bytea(cow) => {
207                use core::fmt::Write;
208                let mut s = String::with_capacity(2 + cow.len() * 2);
209                s.push_str("\\x");
210                for byte in cow.iter() {
211                    write!(s, "{byte:02x}").expect("writing to String cannot fail");
212                }
213                s
214            }
215            PostgresValue::Boolean(b) => b.to_string(),
216            #[cfg(feature = "uuid")]
217            PostgresValue::Uuid(uuid) => uuid.to_string(),
218            #[cfg(feature = "serde")]
219            PostgresValue::Json(json) => json.to_string(),
220            #[cfg(feature = "serde")]
221            PostgresValue::Jsonb(json) => json.to_string(),
222            PostgresValue::Enum(enum_val) => enum_val.variant_name().to_string(),
223
224            // Date and time types
225            #[cfg(feature = "chrono")]
226            PostgresValue::Date(date) => date.to_string(),
227            #[cfg(feature = "chrono")]
228            PostgresValue::Time(time) => time.to_string(),
229            #[cfg(feature = "chrono")]
230            PostgresValue::Timestamp(ts) => ts.to_string(),
231            #[cfg(feature = "chrono")]
232            PostgresValue::TimestampTz(ts) => ts.to_string(),
233            #[cfg(feature = "chrono")]
234            PostgresValue::Interval(dur) => format!("{} seconds", dur.num_seconds()),
235
236            // Date and time types (time crate)
237            #[cfg(feature = "time")]
238            PostgresValue::TimeDate(date) => date.to_string(),
239            #[cfg(feature = "time")]
240            PostgresValue::TimeTime(time) => time.to_string(),
241            #[cfg(feature = "time")]
242            PostgresValue::TimeTimestamp(ts) => ts.to_string(),
243            #[cfg(feature = "time")]
244            PostgresValue::TimeTimestampTz(ts) => ts.to_string(),
245            #[cfg(feature = "time")]
246            PostgresValue::TimeInterval(dur) => format!("{} seconds", dur.whole_seconds()),
247            #[cfg(feature = "jiff")]
248            PostgresValue::JiffDate(date) => date.to_string(),
249            #[cfg(feature = "jiff")]
250            PostgresValue::JiffTime(time) => time.to_string(),
251            #[cfg(feature = "jiff")]
252            PostgresValue::JiffDateTime(ts) => ts.to_string(),
253            #[cfg(feature = "jiff")]
254            PostgresValue::JiffTimestamp(ts) => ts.to_string(),
255
256            // Network address types
257            #[cfg(feature = "cidr")]
258            PostgresValue::Inet(net) => net.to_string(),
259            #[cfg(feature = "cidr")]
260            PostgresValue::Cidr(net) => net.to_string(),
261            #[cfg(feature = "cidr")]
262            PostgresValue::MacAddr(mac) => format!(
263                "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
264                mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
265            ),
266            #[cfg(feature = "cidr")]
267            PostgresValue::MacAddr8(mac) => format!(
268                "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
269                mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], mac[6], mac[7]
270            ),
271
272            // Geometric types
273            #[cfg(feature = "geo-types")]
274            PostgresValue::Point(point) => format!("({},{})", point.x(), point.y()),
275            #[cfg(feature = "geo-types")]
276            PostgresValue::LineString(line) => {
277                let coords: Vec<String> = line
278                    .coords()
279                    .map(|coord| format!("({},{})", coord.x, coord.y))
280                    .collect();
281                format!("[{}]", coords.join(","))
282            }
283            #[cfg(feature = "geo-types")]
284            PostgresValue::Rect(rect) => {
285                format!(
286                    "(({},{}),({},{}))",
287                    rect.min().x,
288                    rect.min().y,
289                    rect.max().x,
290                    rect.max().y
291                )
292            }
293
294            // Bit string types
295            #[cfg(feature = "bit-vec")]
296            PostgresValue::BitVec(bv) => bv
297                .iter()
298                .map(|b| if b { '1' } else { '0' })
299                .collect::<String>(),
300
301            // Array types
302            PostgresValue::Array(arr) => {
303                let elements: Vec<String> = arr.iter().map(ToString::to_string).collect();
304                format!("{{{}}}", elements.join(","))
305            }
306
307            PostgresValue::Null => String::new(),
308        };
309        write!(f, "{value}")
310    }
311}
312
313impl PostgresValue<'_> {
314    /// Returns true if this value is NULL.
315    #[inline]
316    #[must_use]
317    pub const fn is_null(&self) -> bool {
318        matches!(self, PostgresValue::Null)
319    }
320
321    /// Returns the boolean value if this is BOOLEAN.
322    #[inline]
323    #[must_use]
324    pub const fn as_bool(&self) -> Option<bool> {
325        match self {
326            PostgresValue::Boolean(value) => Some(*value),
327            _ => None,
328        }
329    }
330
331    /// Returns the i16 value if this is SMALLINT.
332    #[inline]
333    #[must_use]
334    pub const fn as_i16(&self) -> Option<i16> {
335        match self {
336            PostgresValue::Smallint(value) => Some(*value),
337            _ => None,
338        }
339    }
340
341    /// Returns the i32 value if this is INTEGER.
342    #[inline]
343    #[must_use]
344    pub const fn as_i32(&self) -> Option<i32> {
345        match self {
346            PostgresValue::Integer(value) => Some(*value),
347            _ => None,
348        }
349    }
350
351    /// Returns the i64 value if this is BIGINT.
352    #[inline]
353    #[must_use]
354    pub const fn as_i64(&self) -> Option<i64> {
355        match self {
356            PostgresValue::Bigint(value) => Some(*value),
357            _ => None,
358        }
359    }
360
361    /// Returns the f32 value if this is REAL.
362    #[inline]
363    #[must_use]
364    pub const fn as_f32(&self) -> Option<f32> {
365        match self {
366            PostgresValue::Real(value) => Some(*value),
367            _ => None,
368        }
369    }
370
371    /// Returns the f64 value if this is DOUBLE PRECISION.
372    #[inline]
373    #[must_use]
374    pub const fn as_f64(&self) -> Option<f64> {
375        match self {
376            PostgresValue::DoublePrecision(value) => Some(*value),
377            _ => None,
378        }
379    }
380
381    /// Returns the decimal value if this is NUMERIC.
382    #[inline]
383    #[cfg(feature = "rust-decimal")]
384    #[must_use]
385    pub const fn as_decimal(&self) -> Option<&Decimal> {
386        match self {
387            PostgresValue::Numeric(value) => Some(value),
388            _ => None,
389        }
390    }
391
392    /// Returns the text value if this is TEXT.
393    #[inline]
394    #[must_use]
395    pub fn as_str(&self) -> Option<&str> {
396        match self {
397            PostgresValue::Text(value) => Some(value.as_ref()),
398            _ => None,
399        }
400    }
401
402    /// Returns the bytea value if this is BYTEA.
403    #[inline]
404    #[must_use]
405    pub fn as_bytes(&self) -> Option<&[u8]> {
406        match self {
407            PostgresValue::Bytea(value) => Some(value.as_ref()),
408            _ => None,
409        }
410    }
411
412    /// Returns the UUID value if this is UUID.
413    #[inline]
414    #[cfg(feature = "uuid")]
415    #[must_use]
416    pub const fn as_uuid(&self) -> Option<Uuid> {
417        match self {
418            PostgresValue::Uuid(value) => Some(*value),
419            _ => None,
420        }
421    }
422
423    /// Returns the JSON value if this is JSON.
424    #[inline]
425    #[cfg(feature = "serde")]
426    #[must_use]
427    pub const fn as_json(&self) -> Option<&serde_json::Value> {
428        match self {
429            PostgresValue::Json(value) => Some(value),
430            _ => None,
431        }
432    }
433
434    /// Returns the JSONB value if this is JSONB.
435    #[inline]
436    #[cfg(feature = "serde")]
437    #[must_use]
438    pub const fn as_jsonb(&self) -> Option<&serde_json::Value> {
439        match self {
440            PostgresValue::Jsonb(value) => Some(value),
441            _ => None,
442        }
443    }
444
445    /// Returns the enum value if this is a `PostgreSQL` enum.
446    #[inline]
447    #[must_use]
448    pub fn as_enum(&self) -> Option<&dyn PostgresEnum> {
449        match self {
450            PostgresValue::Enum(value) => Some(value.as_ref()),
451            _ => None,
452        }
453    }
454
455    /// Returns the date value if this is DATE.
456    #[inline]
457    #[cfg(feature = "chrono")]
458    #[must_use]
459    pub const fn as_date(&self) -> Option<&NaiveDate> {
460        match self {
461            PostgresValue::Date(value) => Some(value),
462            _ => None,
463        }
464    }
465
466    /// Returns the time value if this is TIME.
467    #[inline]
468    #[cfg(feature = "chrono")]
469    #[must_use]
470    pub const fn as_time(&self) -> Option<&NaiveTime> {
471        match self {
472            PostgresValue::Time(value) => Some(value),
473            _ => None,
474        }
475    }
476
477    /// Returns the timestamp value if this is TIMESTAMP.
478    #[inline]
479    #[cfg(feature = "chrono")]
480    #[must_use]
481    pub const fn as_timestamp(&self) -> Option<&NaiveDateTime> {
482        match self {
483            PostgresValue::Timestamp(value) => Some(value),
484            _ => None,
485        }
486    }
487
488    /// Returns the timestamp with timezone value if this is TIMESTAMPTZ.
489    #[inline]
490    #[cfg(feature = "chrono")]
491    #[must_use]
492    pub const fn as_timestamp_tz(&self) -> Option<&DateTime<FixedOffset>> {
493        match self {
494            PostgresValue::TimestampTz(value) => Some(value),
495            _ => None,
496        }
497    }
498
499    /// Returns the interval value if this is INTERVAL.
500    #[inline]
501    #[cfg(feature = "chrono")]
502    #[must_use]
503    pub const fn as_interval(&self) -> Option<&Duration> {
504        match self {
505            PostgresValue::Interval(value) => Some(value),
506            _ => None,
507        }
508    }
509
510    /// Returns the inet value if this is INET.
511    #[inline]
512    #[cfg(feature = "cidr")]
513    #[must_use]
514    pub const fn as_inet(&self) -> Option<&IpInet> {
515        match self {
516            PostgresValue::Inet(value) => Some(value),
517            _ => None,
518        }
519    }
520
521    /// Returns the cidr value if this is CIDR.
522    #[inline]
523    #[cfg(feature = "cidr")]
524    #[must_use]
525    pub const fn as_cidr(&self) -> Option<&IpCidr> {
526        match self {
527            PostgresValue::Cidr(value) => Some(value),
528            _ => None,
529        }
530    }
531
532    /// Returns the MAC address if this is MACADDR.
533    #[inline]
534    #[cfg(feature = "cidr")]
535    #[must_use]
536    pub const fn as_macaddr(&self) -> Option<[u8; 6]> {
537        match self {
538            PostgresValue::MacAddr(value) => Some(*value),
539            _ => None,
540        }
541    }
542
543    /// Returns the MAC address if this is MACADDR8.
544    #[inline]
545    #[cfg(feature = "cidr")]
546    #[must_use]
547    pub const fn as_macaddr8(&self) -> Option<[u8; 8]> {
548        match self {
549            PostgresValue::MacAddr8(value) => Some(*value),
550            _ => None,
551        }
552    }
553
554    /// Returns the point value if this is POINT.
555    #[inline]
556    #[cfg(feature = "geo-types")]
557    #[must_use]
558    pub const fn as_point(&self) -> Option<&Point<f64>> {
559        match self {
560            PostgresValue::Point(value) => Some(value),
561            _ => None,
562        }
563    }
564
565    /// Returns the line string value if this is PATH.
566    #[inline]
567    #[cfg(feature = "geo-types")]
568    #[must_use]
569    pub const fn as_line_string(&self) -> Option<&LineString<f64>> {
570        match self {
571            PostgresValue::LineString(value) => Some(value),
572            _ => None,
573        }
574    }
575
576    /// Returns the rect value if this is BOX.
577    #[inline]
578    #[cfg(feature = "geo-types")]
579    #[must_use]
580    pub const fn as_rect(&self) -> Option<&Rect<f64>> {
581        match self {
582            PostgresValue::Rect(value) => Some(value),
583            _ => None,
584        }
585    }
586
587    /// Returns the bit vector if this is BIT/VARBIT.
588    #[inline]
589    #[cfg(feature = "bit-vec")]
590    #[must_use]
591    pub const fn as_bitvec(&self) -> Option<&BitVec> {
592        match self {
593            PostgresValue::BitVec(value) => Some(value),
594            _ => None,
595        }
596    }
597
598    /// Returns the date value if this is DATE (time crate).
599    #[inline]
600    #[cfg(feature = "time")]
601    #[must_use]
602    pub const fn as_time_date(&self) -> Option<&TimeDate> {
603        match self {
604            PostgresValue::TimeDate(value) => Some(value),
605            _ => None,
606        }
607    }
608
609    /// Returns the time value if this is TIME (time crate).
610    #[inline]
611    #[cfg(feature = "time")]
612    #[must_use]
613    pub const fn as_time_time(&self) -> Option<&TimeTime> {
614        match self {
615            PostgresValue::TimeTime(value) => Some(value),
616            _ => None,
617        }
618    }
619
620    /// Returns the timestamp value if this is TIMESTAMP (time crate).
621    #[inline]
622    #[cfg(feature = "time")]
623    #[must_use]
624    pub const fn as_time_timestamp(&self) -> Option<&PrimitiveDateTime> {
625        match self {
626            PostgresValue::TimeTimestamp(value) => Some(value),
627            _ => None,
628        }
629    }
630
631    /// Returns the timestamp with timezone value if this is TIMESTAMPTZ (time crate).
632    #[inline]
633    #[cfg(feature = "time")]
634    #[must_use]
635    pub const fn as_time_timestamp_tz(&self) -> Option<&OffsetDateTime> {
636        match self {
637            PostgresValue::TimeTimestampTz(value) => Some(value),
638            _ => None,
639        }
640    }
641
642    /// Returns the interval value if this is INTERVAL (time crate).
643    #[inline]
644    #[cfg(feature = "time")]
645    #[must_use]
646    pub const fn as_time_interval(&self) -> Option<&TimeDuration> {
647        match self {
648            PostgresValue::TimeInterval(value) => Some(value),
649            _ => None,
650        }
651    }
652
653    /// Returns the date value if this is DATE (jiff).
654    #[inline]
655    #[cfg(feature = "jiff")]
656    #[must_use]
657    pub const fn as_jiff_date(&self) -> Option<&JiffDate> {
658        match self {
659            PostgresValue::JiffDate(value) => Some(value),
660            _ => None,
661        }
662    }
663
664    /// Returns the time value if this is TIME (jiff).
665    #[inline]
666    #[cfg(feature = "jiff")]
667    #[must_use]
668    pub const fn as_jiff_time(&self) -> Option<&JiffTime> {
669        match self {
670            PostgresValue::JiffTime(value) => Some(value),
671            _ => None,
672        }
673    }
674
675    /// Returns the timestamp value if this is TIMESTAMP (jiff).
676    #[inline]
677    #[cfg(feature = "jiff")]
678    #[must_use]
679    pub const fn as_jiff_datetime(&self) -> Option<&JiffDateTime> {
680        match self {
681            PostgresValue::JiffDateTime(value) => Some(value),
682            _ => None,
683        }
684    }
685
686    /// Returns the timestamp with timezone value if this is TIMESTAMPTZ (jiff).
687    #[inline]
688    #[cfg(feature = "jiff")]
689    #[must_use]
690    pub const fn as_jiff_timestamp(&self) -> Option<&JiffTimestamp> {
691        match self {
692            PostgresValue::JiffTimestamp(value) => Some(value),
693            _ => None,
694        }
695    }
696
697    /// Returns the array elements if this is an ARRAY.
698    #[inline]
699    #[must_use]
700    pub fn as_array(&self) -> Option<&[Self]> {
701        match self {
702            PostgresValue::Array(values) => Some(values),
703            _ => None,
704        }
705    }
706
707    /// Converts this value into an owned representation.
708    #[inline]
709    #[must_use]
710    pub fn into_owned(self) -> OwnedPostgresValue {
711        self.into()
712    }
713
714    /// Convert this `PostgreSQL` value to a Rust type using the `FromPostgresValue` trait.
715    ///
716    /// # Errors
717    ///
718    /// Returns [`DrizzleError::ConversionError`] when the stored variant's
719    /// native type does not match the target type `T`.
720    pub fn convert<T: FromPostgresValue>(self) -> Result<T, DrizzleError> {
721        match self {
722            PostgresValue::Boolean(value) => T::from_postgres_bool(value),
723            PostgresValue::Smallint(value) => T::from_postgres_i16(value),
724            PostgresValue::Integer(value) => T::from_postgres_i32(value),
725            PostgresValue::Bigint(value) => T::from_postgres_i64(value),
726            PostgresValue::Real(value) => T::from_postgres_f32(value),
727            PostgresValue::DoublePrecision(value) => T::from_postgres_f64(value),
728            #[cfg(feature = "rust-decimal")]
729            PostgresValue::Numeric(value) => {
730                let text = value.to_string();
731                T::from_postgres_text(&text)
732            }
733            PostgresValue::Text(value) => T::from_postgres_text(&value),
734            PostgresValue::Bytea(value) => T::from_postgres_bytes(&value),
735            #[cfg(feature = "uuid")]
736            PostgresValue::Uuid(value) => T::from_postgres_uuid(value),
737            #[cfg(feature = "serde")]
738            PostgresValue::Json(value) => T::from_postgres_json(value),
739            #[cfg(feature = "serde")]
740            PostgresValue::Jsonb(value) => T::from_postgres_jsonb(value),
741            PostgresValue::Enum(value) => T::from_postgres_text(value.variant_name()),
742            #[cfg(feature = "chrono")]
743            PostgresValue::Date(value) => T::from_postgres_date(value),
744            #[cfg(feature = "chrono")]
745            PostgresValue::Time(value) => T::from_postgres_time(value),
746            #[cfg(feature = "chrono")]
747            PostgresValue::Timestamp(value) => T::from_postgres_timestamp(value),
748            #[cfg(feature = "chrono")]
749            PostgresValue::TimestampTz(value) => T::from_postgres_timestamptz(value),
750            #[cfg(feature = "chrono")]
751            PostgresValue::Interval(value) => T::from_postgres_interval(value),
752            #[cfg(feature = "time")]
753            PostgresValue::TimeDate(value) => T::from_postgres_time_date(value),
754            #[cfg(feature = "time")]
755            PostgresValue::TimeTime(value) => T::from_postgres_time_time(value),
756            #[cfg(feature = "time")]
757            PostgresValue::TimeTimestamp(value) => T::from_postgres_time_timestamp(value),
758            #[cfg(feature = "time")]
759            PostgresValue::TimeTimestampTz(value) => T::from_postgres_time_timestamptz(value),
760            #[cfg(feature = "time")]
761            PostgresValue::TimeInterval(value) => T::from_postgres_time_interval(value),
762            #[cfg(feature = "jiff")]
763            PostgresValue::JiffDate(value) => T::from_postgres_jiff_date(value),
764            #[cfg(feature = "jiff")]
765            PostgresValue::JiffTime(value) => T::from_postgres_jiff_time(value),
766            #[cfg(feature = "jiff")]
767            PostgresValue::JiffDateTime(value) => T::from_postgres_jiff_datetime(value),
768            #[cfg(feature = "jiff")]
769            PostgresValue::JiffTimestamp(value) => T::from_postgres_jiff_timestamp(value),
770            #[cfg(feature = "cidr")]
771            PostgresValue::Inet(value) => T::from_postgres_inet(value),
772            #[cfg(feature = "cidr")]
773            PostgresValue::Cidr(value) => T::from_postgres_cidr(value),
774            #[cfg(feature = "cidr")]
775            PostgresValue::MacAddr(value) => T::from_postgres_macaddr(value),
776            #[cfg(feature = "cidr")]
777            PostgresValue::MacAddr8(value) => T::from_postgres_macaddr8(value),
778            #[cfg(feature = "geo-types")]
779            PostgresValue::Point(value) => T::from_postgres_point(value),
780            #[cfg(feature = "geo-types")]
781            PostgresValue::LineString(value) => T::from_postgres_linestring(value),
782            #[cfg(feature = "geo-types")]
783            PostgresValue::Rect(value) => T::from_postgres_rect(value),
784            #[cfg(feature = "bit-vec")]
785            PostgresValue::BitVec(value) => T::from_postgres_bitvec(value),
786            PostgresValue::Array(value) => T::from_postgres_array(value),
787            PostgresValue::Null => T::from_postgres_null(),
788        }
789    }
790
791    /// Convert a reference to this `PostgreSQL` value to a Rust type.
792    ///
793    /// # Errors
794    ///
795    /// Returns [`DrizzleError::ConversionError`] when the stored variant's
796    /// native type does not match the target type `T`.
797    pub fn convert_ref<T: FromPostgresValue>(&self) -> Result<T, DrizzleError> {
798        match self {
799            PostgresValue::Boolean(value) => T::from_postgres_bool(*value),
800            PostgresValue::Smallint(value) => T::from_postgres_i16(*value),
801            PostgresValue::Integer(value) => T::from_postgres_i32(*value),
802            PostgresValue::Bigint(value) => T::from_postgres_i64(*value),
803            PostgresValue::Real(value) => T::from_postgres_f32(*value),
804            PostgresValue::DoublePrecision(value) => T::from_postgres_f64(*value),
805            #[cfg(feature = "rust-decimal")]
806            PostgresValue::Numeric(value) => {
807                let text = value.to_string();
808                T::from_postgres_text(&text)
809            }
810            PostgresValue::Text(value) => T::from_postgres_text(value),
811            PostgresValue::Bytea(value) => T::from_postgres_bytes(value),
812            #[cfg(feature = "uuid")]
813            PostgresValue::Uuid(value) => T::from_postgres_uuid(*value),
814            #[cfg(feature = "serde")]
815            PostgresValue::Json(value) => T::from_postgres_json(value.clone()),
816            #[cfg(feature = "serde")]
817            PostgresValue::Jsonb(value) => T::from_postgres_jsonb(value.clone()),
818            PostgresValue::Enum(value) => T::from_postgres_text(value.variant_name()),
819            #[cfg(feature = "chrono")]
820            PostgresValue::Date(value) => T::from_postgres_date(*value),
821            #[cfg(feature = "chrono")]
822            PostgresValue::Time(value) => T::from_postgres_time(*value),
823            #[cfg(feature = "chrono")]
824            PostgresValue::Timestamp(value) => T::from_postgres_timestamp(*value),
825            #[cfg(feature = "chrono")]
826            PostgresValue::TimestampTz(value) => T::from_postgres_timestamptz(*value),
827            #[cfg(feature = "chrono")]
828            PostgresValue::Interval(value) => T::from_postgres_interval(*value),
829            #[cfg(feature = "time")]
830            PostgresValue::TimeDate(value) => T::from_postgres_time_date(*value),
831            #[cfg(feature = "time")]
832            PostgresValue::TimeTime(value) => T::from_postgres_time_time(*value),
833            #[cfg(feature = "time")]
834            PostgresValue::TimeTimestamp(value) => T::from_postgres_time_timestamp(*value),
835            #[cfg(feature = "time")]
836            PostgresValue::TimeTimestampTz(value) => T::from_postgres_time_timestamptz(*value),
837            #[cfg(feature = "time")]
838            PostgresValue::TimeInterval(value) => T::from_postgres_time_interval(*value),
839            #[cfg(feature = "jiff")]
840            PostgresValue::JiffDate(value) => T::from_postgres_jiff_date(*value),
841            #[cfg(feature = "jiff")]
842            PostgresValue::JiffTime(value) => T::from_postgres_jiff_time(*value),
843            #[cfg(feature = "jiff")]
844            PostgresValue::JiffDateTime(value) => T::from_postgres_jiff_datetime(*value),
845            #[cfg(feature = "jiff")]
846            PostgresValue::JiffTimestamp(value) => T::from_postgres_jiff_timestamp(*value),
847            #[cfg(feature = "cidr")]
848            PostgresValue::Inet(value) => T::from_postgres_inet(*value),
849            #[cfg(feature = "cidr")]
850            PostgresValue::Cidr(value) => T::from_postgres_cidr(*value),
851            #[cfg(feature = "cidr")]
852            PostgresValue::MacAddr(value) => T::from_postgres_macaddr(*value),
853            #[cfg(feature = "cidr")]
854            PostgresValue::MacAddr8(value) => T::from_postgres_macaddr8(*value),
855            #[cfg(feature = "geo-types")]
856            PostgresValue::Point(value) => T::from_postgres_point(*value),
857            #[cfg(feature = "geo-types")]
858            PostgresValue::LineString(value) => T::from_postgres_linestring(value.clone()),
859            #[cfg(feature = "geo-types")]
860            PostgresValue::Rect(value) => T::from_postgres_rect(*value),
861            #[cfg(feature = "bit-vec")]
862            PostgresValue::BitVec(value) => T::from_postgres_bitvec(value.clone()),
863            PostgresValue::Array(value) => T::from_postgres_array(value.clone()),
864            PostgresValue::Null => T::from_postgres_null(),
865        }
866    }
867}
868
869// Implement core traits required by Drizzle
870impl SQLParam for PostgresValue<'_> {
871    const DIALECT: drizzle_core::dialect::Dialect = drizzle_core::dialect::Dialect::PostgreSQL;
872    type DialectMarker = drizzle_core::dialect::PostgresDialect;
873
874    /// Binds LIMIT/OFFSET values as `BIGINT` parameters so paginated queries
875    /// share one SQL text (and one cached prepared statement) across pages.
876    #[inline]
877    fn pagination_param(value: usize) -> Option<Self> {
878        i64::try_from(value).ok().map(Self::Bigint)
879    }
880
881    fn write_literal(&self, buf: &mut String) -> bool {
882        let mut literal = String::new();
883        let written = write_postgres_literal(self, &mut literal).is_some();
884        if written {
885            buf.push_str(&literal);
886        }
887        written
888    }
889}
890
891/// Appends a quoted string literal; `None` for text PostgreSQL cannot store.
892fn write_quoted_literal(buf: &mut String, text: &str) -> Option<()> {
893    if text.contains('\0') {
894        return None;
895    }
896    buf.push('\'');
897    buf.push_str(&text.replace('\'', "''"));
898    buf.push('\'');
899    Some(())
900}
901
902/// Appends `'text'::type`.
903fn write_cast_literal(buf: &mut String, text: &str, sql_type: &str) -> Option<()> {
904    write_quoted_literal(buf, text)?;
905    buf.push_str("::");
906    buf.push_str(sql_type);
907    Some(())
908}
909
910fn float_text(value: f64) -> String {
911    if value.is_nan() {
912        "NaN".to_string()
913    } else if value.is_infinite() {
914        if value.is_sign_positive() {
915            "Infinity"
916        } else {
917            "-Infinity"
918        }
919        .to_string()
920    } else {
921        format!("{value:?}")
922    }
923}
924
925/// Writes `value` as a PostgreSQL literal. Built-in types carry an explicit
926/// cast so the literal keeps its type in any context; an enum variant is an
927/// untyped string that PostgreSQL resolves against the enum it is compared
928/// with, since the enum's type may live outside the `search_path`.
929fn write_postgres_literal(value: &PostgresValue<'_>, buf: &mut String) -> Option<()> {
930    use core::fmt::Write;
931    match value {
932        PostgresValue::Null => buf.push_str("NULL"),
933        PostgresValue::Smallint(v) => {
934            let _ = write!(buf, "{v}::smallint");
935        }
936        PostgresValue::Integer(v) => {
937            let _ = write!(buf, "{v}::integer");
938        }
939        PostgresValue::Bigint(v) => {
940            let _ = write!(buf, "{v}::bigint");
941        }
942        PostgresValue::Real(v) => write_cast_literal(buf, &float_text(f64::from(*v)), "real")?,
943        PostgresValue::DoublePrecision(v) => {
944            write_cast_literal(buf, &float_text(*v), "double precision")?;
945        }
946        #[cfg(feature = "rust-decimal")]
947        PostgresValue::Numeric(v) => write_cast_literal(buf, &v.to_string(), "numeric")?,
948        PostgresValue::Text(text) => write_quoted_literal(buf, text)?,
949        PostgresValue::Bytea(bytes) => {
950            let mut hex = String::with_capacity(2 + bytes.len() * 2);
951            hex.push_str("\\x");
952            for byte in bytes.iter() {
953                let _ = write!(hex, "{byte:02x}");
954            }
955            write_cast_literal(buf, &hex, "bytea")?;
956        }
957        PostgresValue::Boolean(v) => buf.push_str(if *v { "TRUE" } else { "FALSE" }),
958        #[cfg(feature = "uuid")]
959        PostgresValue::Uuid(v) => write_cast_literal(buf, &v.to_string(), "uuid")?,
960        #[cfg(feature = "serde")]
961        PostgresValue::Json(v) => write_cast_literal(buf, &v.to_string(), "json")?,
962        #[cfg(feature = "serde")]
963        PostgresValue::Jsonb(v) => write_cast_literal(buf, &v.to_string(), "jsonb")?,
964        PostgresValue::Enum(v) => write_quoted_literal(buf, v.variant_name())?,
965        #[cfg(feature = "chrono")]
966        PostgresValue::Date(v) => write_cast_literal(buf, &v.to_string(), "date")?,
967        #[cfg(feature = "chrono")]
968        PostgresValue::Time(v) => write_cast_literal(buf, &v.to_string(), "time")?,
969        #[cfg(feature = "chrono")]
970        PostgresValue::Timestamp(v) => write_cast_literal(buf, &v.to_string(), "timestamp")?,
971        #[cfg(feature = "chrono")]
972        PostgresValue::TimestampTz(v) => {
973            write_cast_literal(buf, &v.to_rfc3339(), "timestamptz")?;
974        }
975        #[cfg(feature = "chrono")]
976        PostgresValue::Interval(v) => {
977            let micros = v.num_microseconds()?;
978            write_cast_literal(buf, &format!("{micros} microseconds"), "interval")?;
979        }
980        #[cfg(feature = "time")]
981        PostgresValue::TimeDate(v) => write_cast_literal(buf, &v.to_string(), "date")?,
982        #[cfg(feature = "time")]
983        PostgresValue::TimeTime(v) => write_cast_literal(buf, &v.to_string(), "time")?,
984        #[cfg(feature = "time")]
985        PostgresValue::TimeTimestamp(v) => {
986            write_cast_literal(buf, &format!("{} {}", v.date(), v.time()), "timestamp")?;
987        }
988        #[cfg(feature = "time")]
989        PostgresValue::TimeTimestampTz(v) => {
990            let utc = v.to_offset(time::UtcOffset::UTC);
991            write_cast_literal(
992                buf,
993                &format!("{} {}+00", utc.date(), utc.time()),
994                "timestamptz",
995            )?;
996        }
997        #[cfg(feature = "time")]
998        PostgresValue::TimeInterval(v) => {
999            let micros = v.whole_microseconds();
1000            write_cast_literal(buf, &format!("{micros} microseconds"), "interval")?;
1001        }
1002        #[cfg(feature = "jiff")]
1003        PostgresValue::JiffDate(v) => write_cast_literal(buf, &v.to_string(), "date")?,
1004        #[cfg(feature = "jiff")]
1005        PostgresValue::JiffTime(v) => write_cast_literal(buf, &v.to_string(), "time")?,
1006        #[cfg(feature = "jiff")]
1007        PostgresValue::JiffDateTime(v) => write_cast_literal(buf, &v.to_string(), "timestamp")?,
1008        #[cfg(feature = "jiff")]
1009        PostgresValue::JiffTimestamp(v) => write_cast_literal(buf, &v.to_string(), "timestamptz")?,
1010        #[cfg(feature = "cidr")]
1011        PostgresValue::Inet(v) => write_cast_literal(buf, &v.to_string(), "inet")?,
1012        #[cfg(feature = "cidr")]
1013        PostgresValue::Cidr(v) => write_cast_literal(buf, &v.to_string(), "cidr")?,
1014        #[cfg(feature = "cidr")]
1015        PostgresValue::MacAddr(bytes) => {
1016            let text = bytes
1017                .iter()
1018                .map(|byte| format!("{byte:02x}"))
1019                .collect::<Vec<_>>()
1020                .join(":");
1021            write_cast_literal(buf, &text, "macaddr")?;
1022        }
1023        #[cfg(feature = "cidr")]
1024        PostgresValue::MacAddr8(bytes) => {
1025            let text = bytes
1026                .iter()
1027                .map(|byte| format!("{byte:02x}"))
1028                .collect::<Vec<_>>()
1029                .join(":");
1030            write_cast_literal(buf, &text, "macaddr8")?;
1031        }
1032        #[cfg(feature = "geo-types")]
1033        PostgresValue::Point(v) => {
1034            let text = format!("({:?},{:?})", v.x(), v.y());
1035            write_cast_literal(buf, &text, "point")?;
1036        }
1037        #[cfg(feature = "geo-types")]
1038        PostgresValue::LineString(v) => {
1039            let points = v
1040                .coords()
1041                .map(|coord| format!("({:?},{:?})", coord.x, coord.y))
1042                .collect::<Vec<_>>()
1043                .join(",");
1044            write_cast_literal(buf, &format!("[{points}]"), "path")?;
1045        }
1046        #[cfg(feature = "geo-types")]
1047        PostgresValue::Rect(v) => {
1048            let (min, max) = (v.min(), v.max());
1049            let text = format!("(({:?},{:?}),({:?},{:?}))", max.x, max.y, min.x, min.y);
1050            write_cast_literal(buf, &text, "box")?;
1051        }
1052        #[cfg(feature = "bit-vec")]
1053        PostgresValue::BitVec(v) => {
1054            buf.push_str("B'");
1055            for bit in v.iter() {
1056                buf.push(if bit { '1' } else { '0' });
1057            }
1058            buf.push('\'');
1059        }
1060        PostgresValue::Array(values) if values.is_empty() => buf.push_str("'{}'"),
1061        PostgresValue::Array(values) => {
1062            buf.push_str("ARRAY[");
1063            for (index, value) in values.iter().enumerate() {
1064                if index > 0 {
1065                    buf.push_str(", ");
1066                }
1067                write_postgres_literal(value, buf)?;
1068            }
1069            buf.push(']');
1070        }
1071    }
1072    Some(())
1073}
1074
1075impl<'a> From<PostgresValue<'a>> for SQL<'a, PostgresValue<'a>> {
1076    fn from(value: PostgresValue<'a>) -> Self {
1077        SQL::param(value)
1078    }
1079}
1080
1081// Cow integration for SQL struct
1082impl<'a> From<PostgresValue<'a>> for Cow<'a, PostgresValue<'a>> {
1083    fn from(value: PostgresValue<'a>) -> Self {
1084        Cow::Owned(value)
1085    }
1086}
1087
1088impl<'a> From<&'a PostgresValue<'a>> for Cow<'a, PostgresValue<'a>> {
1089    fn from(value: &'a PostgresValue<'a>) -> Self {
1090        Cow::Borrowed(value)
1091    }
1092}