Skip to main content

drizzle_postgres/values/
mod.rs

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