wasmcloud_provider_sqldb_postgres/
bindings.rs

1//! This module contains generated bindings, and code to make bindings more ergonomic
2//!
3
4use core::net::IpAddr;
5use std::collections::HashMap;
6use std::error::Error;
7use std::str::FromStr;
8
9use anyhow::{bail, Context as _};
10use bigdecimal::num_traits::Float;
11use bit_vec::BitVec;
12use bytes::Bytes;
13use bytes::{BufMut, BytesMut};
14use chrono::{
15    DateTime, Datelike, FixedOffset, NaiveDate, NaiveDateTime, NaiveTime, Offset as _, Timelike,
16    Utc,
17};
18use cidr::IpCidr;
19use geo_types::{coord, LineString, Point, Rect};
20use pg_bigdecimal::PgNumeric;
21use postgres_types::{FromSql, IsNull, PgLsn, ToSql, Type as PgType};
22use tokio_postgres::Row;
23use uuid::Uuid;
24
25// Bindgen happens here
26wit_bindgen_wrpc::generate!({
27  with: {
28      "wasmcloud:postgres/types@0.1.1-draft": generate,
29      "wasmcloud:postgres/query@0.1.1-draft": generate,
30      "wasmcloud:postgres/prepared@0.1.1-draft": generate,
31  },
32});
33
34// Start bindgen-generated type imports
35pub(crate) use exports::wasmcloud::postgres::prepared;
36pub(crate) use exports::wasmcloud::postgres::query;
37
38pub(crate) use query::{PgValue, QueryError, ResultRow};
39
40pub(crate) use prepared::{
41    PreparedStatementExecError, PreparedStatementToken, StatementPrepareError,
42};
43
44use crate::bindings::wasmcloud::postgres::types::{
45    Date, HashableF64, MacAddressEui48, MacAddressEui64, Numeric, Offset, ResultRowEntry, Time,
46    Timestamp, TimestampTz,
47};
48// End of bindgen-generated type imports
49
50/// Build an `f64` from a mantissa, exponent and sign
51fn f64_from_components(mantissa: u64, exponent: i16, sign: i8) -> f64 {
52    let sign_f = sign as f64;
53    let mantissa_f = mantissa as f64;
54    let exponent_f = 2f64.powf(exponent as f64);
55    sign_f * mantissa_f * exponent_f
56}
57
58/// Build an `f64` from a simple tuple of mantissa, exponent and sign
59fn f64_from_tuple(t: &(u64, i16, i8)) -> f64 {
60    f64_from_components(t.0, t.1, t.2)
61}
62
63/// Convert [`Rect`] which *should* contain points for two opposite corners into
64/// a tuple of points (AKA two `HashableF64`s)
65fn rect_to_hashable_f64s(r: Rect<f64>) -> ((HashableF64, HashableF64), (HashableF64, HashableF64)) {
66    let (bottom_left_x, bottom_left_y) = r.min().x_y();
67    let (top_right_x, top_right_y) = r.max().x_y();
68    (
69        (
70            bottom_left_x.integer_decode(),
71            bottom_left_y.integer_decode(),
72        ),
73        (top_right_x.integer_decode(), top_right_y.integer_decode()),
74    )
75}
76
77/// Convert a [`Linestring`] which represents a line/line segment and is expected to
78/// contain only *two* points, into a tuple of `HashableF64`s
79fn linestring_to_hashable_f64s_tuple(
80    l: LineString<f64>,
81) -> anyhow::Result<((HashableF64, HashableF64), (HashableF64, HashableF64))> {
82    match linestring_to_hashable_f64s(l)[..] {
83        [start, end] => Ok((start, end)),
84        _ => bail!("unexpected number of points in line string"),
85    }
86}
87
88/// Convert a [`Linestring`] into a vector of points (AKA two bindgen-generated `HashableF64`s)
89fn linestring_to_hashable_f64s(l: LineString<f64>) -> Vec<(HashableF64, HashableF64)> {
90    l.into_points()
91        .into_iter()
92        .map(point_to_hashable_f64s)
93        .collect::<Vec<_>>()
94}
95
96/// Convert a [`Point`] into two bindgen-generated `HashableF64`s
97fn point_to_hashable_f64s(p: Point<f64>) -> (HashableF64, HashableF64) {
98    let (x, y) = p.x_y();
99    (x.integer_decode(), y.integer_decode())
100}
101
102impl MacAddressEui48 {
103    fn as_bytes(&self) -> [u8; 6] {
104        [
105            self.bytes.0,
106            self.bytes.1,
107            self.bytes.2,
108            self.bytes.3,
109            self.bytes.4,
110            self.bytes.5,
111        ]
112    }
113}
114
115impl MacAddressEui64 {
116    fn as_bytes(&self) -> [u8; 8] {
117        [
118            self.bytes.0,
119            self.bytes.1,
120            self.bytes.2,
121            self.bytes.3,
122            self.bytes.4,
123            self.bytes.5,
124            self.bytes.6,
125            self.bytes.7,
126        ]
127    }
128}
129
130impl From<MacAddressEui48> for PgValue {
131    fn from(m: MacAddressEui48) -> PgValue {
132        PgValue::Macaddr(m)
133    }
134}
135
136impl From<MacAddressEui64> for PgValue {
137    fn from(m: MacAddressEui64) -> PgValue {
138        PgValue::Macaddr8(m)
139    }
140}
141
142impl TryFrom<&Date> for chrono::NaiveDate {
143    type Error = anyhow::Error;
144
145    fn try_from(d: &Date) -> anyhow::Result<NaiveDate> {
146        match d {
147            Date::PositiveInfinity => Ok(NaiveDate::MAX),
148            Date::NegativeInfinity => Ok(NaiveDate::MAX),
149            Date::Ymd((year, month, day)) => NaiveDate::from_ymd_opt(*year, *month, *day)
150                .with_context(|| format!("failed to build date from ymd ({year}/{month}/{day})")),
151        }
152    }
153}
154
155impl From<NaiveDate> for Date {
156    fn from(nd: NaiveDate) -> Date {
157        match nd {
158            NaiveDate::MAX => Date::PositiveInfinity,
159            NaiveDate::MIN => Date::NegativeInfinity,
160            nd => Date::Ymd((nd.year(), nd.month(), nd.day())),
161        }
162    }
163}
164
165impl TryFrom<&Time> for NaiveTime {
166    type Error = anyhow::Error;
167
168    fn try_from(
169        Time {
170            hour,
171            min,
172            sec,
173            micro,
174        }: &Time,
175    ) -> anyhow::Result<NaiveTime> {
176        NaiveTime::from_hms_micro_opt(*hour, *min, *sec, *micro)
177            .with_context(|| format!("failed to convert time [{hour}h {min}m {sec}s {micro}micro]"))
178    }
179}
180
181impl From<NaiveTime> for Time {
182    fn from(nt: NaiveTime) -> Time {
183        Time {
184            hour: nt.hour(),
185            min: nt.minute(),
186            sec: nt.second(),
187            micro: nt.nanosecond() / 1_000,
188        }
189    }
190}
191
192impl TryFrom<&Timestamp> for NaiveDateTime {
193    type Error = anyhow::Error;
194
195    fn try_from(Timestamp { date, time }: &Timestamp) -> anyhow::Result<NaiveDateTime> {
196        match (date, time) {
197            (Date::NegativeInfinity, _) | (Date::PositiveInfinity, _) => {
198                bail!("negative/positive infinite date times are not supported")
199            }
200            (Date::Ymd(_), time) => Ok(NaiveDateTime::new(date.try_into()?, time.try_into()?)),
201        }
202    }
203}
204
205impl From<NaiveDateTime> for Timestamp {
206    fn from(ndt: NaiveDateTime) -> Timestamp {
207        Timestamp {
208            date: ndt.date().into(),
209            time: ndt.time().into(),
210        }
211    }
212}
213
214impl TryFrom<&Offset> for FixedOffset {
215    type Error = anyhow::Error;
216
217    fn try_from(timezone: &Offset) -> anyhow::Result<FixedOffset> {
218        match timezone {
219            Offset::EasternHemisphereSecs(secs) => FixedOffset::east_opt(*secs)
220                .with_context(|| format!("failed to convert eastern hemisphere seconds [{secs}]")),
221            Offset::WesternHemisphereSecs(secs) => FixedOffset::west_opt(*secs)
222                .with_context(|| format!("failed to convert western hemisphere seconds [{secs}]")),
223        }
224    }
225}
226
227impl TryFrom<&TimestampTz> for DateTime<Utc> {
228    type Error = anyhow::Error;
229
230    fn try_from(
231        TimestampTz { timestamp, offset }: &TimestampTz,
232    ) -> anyhow::Result<chrono::DateTime<Utc>> {
233        let fixed_offset: FixedOffset = offset.try_into()?;
234        let timestamp: NaiveDateTime = timestamp.try_into()?;
235        Ok(
236            chrono::DateTime::<FixedOffset>::from_naive_utc_and_offset(timestamp, fixed_offset)
237                .into(),
238        )
239    }
240}
241
242impl From<DateTime<Utc>> for TimestampTz {
243    fn from(dt: DateTime<Utc>) -> TimestampTz {
244        TimestampTz {
245            offset: Offset::WesternHemisphereSecs(dt.offset().fix().local_minus_utc()),
246            timestamp: dt.naive_local().into(),
247        }
248    }
249}
250
251/// Build a `ResultRow` from a [`Row`]
252pub(crate) fn into_result_row(r: Row) -> ResultRow {
253    let mut rr = Vec::new();
254    for (idx, col) in r.columns().iter().enumerate() {
255        rr.push(ResultRowEntry {
256            column_name: col.name().into(),
257            value: r.get(idx),
258        });
259    }
260    rr
261}
262
263impl ToSql for MacAddressEui48 {
264    fn to_sql(
265        &self,
266        ty: &PgType,
267        out: &mut BytesMut,
268    ) -> core::result::Result<IsNull, Box<dyn Error + Sync + Send>> {
269        match ty {
270            &tokio_postgres::types::Type::MACADDR => {
271                out.put_slice(&self.as_bytes());
272                Ok(IsNull::No)
273            }
274            _ => Err("invalid Postgres type for EUI48 MAC address".into()),
275        }
276    }
277
278    fn accepts(ty: &PgType) -> bool {
279        matches!(ty, &tokio_postgres::types::Type::MACADDR)
280    }
281
282    tokio_postgres::types::to_sql_checked!();
283}
284
285impl ToSql for MacAddressEui64 {
286    fn to_sql(
287        &self,
288        ty: &PgType,
289        out: &mut BytesMut,
290    ) -> core::result::Result<IsNull, Box<dyn Error + Sync + Send>> {
291        match ty {
292            &tokio_postgres::types::Type::MACADDR => {
293                out.put_slice(&self.as_bytes());
294                Ok(IsNull::No)
295            }
296            _ => Err("invalid Postgres type for EUI64 MAC address".into()),
297        }
298    }
299
300    fn accepts(ty: &PgType) -> bool {
301        matches!(ty, &tokio_postgres::types::Type::MACADDR)
302    }
303
304    tokio_postgres::types::to_sql_checked!();
305}
306
307impl ToSql for PgValue {
308    fn to_sql(
309        &self,
310        ty: &PgType,
311        out: &mut BytesMut,
312    ) -> core::result::Result<IsNull, Box<dyn Error + Sync + Send>> {
313        match self {
314            PgValue::Null => Ok(IsNull::Yes),
315            // Numeric
316            PgValue::BigInt(n) | PgValue::Int8(n) => n.to_sql(ty, out),
317            PgValue::Int8Array(ns) => ns.to_sql(ty, out),
318            PgValue::BigSerial(n) | PgValue::Serial8(n) => n.to_sql(ty, out),
319            PgValue::Bool(n) | PgValue::Boolean(n) => n.to_sql(ty, out),
320            PgValue::BoolArray(ns) => ns.to_sql(ty, out),
321            PgValue::Double(d)
322            | PgValue::Float8(d) => {
323                f64_from_tuple(d).to_sql(ty, out)
324            }
325            PgValue::Float8Array(ds) => {
326                ds.iter().map(f64_from_tuple).collect::<Vec<_>>().to_sql(ty, out)
327            }
328            PgValue::Real(d)
329            | PgValue::Float4(d) => {
330                f64_from_tuple(d).to_sql(ty, out)
331            }
332            PgValue::Float4Array(ds) => {
333                ds.iter().map(f64_from_tuple).collect::<Vec<_>>().to_sql(ty, out)
334            }
335            PgValue::Integer(n) | PgValue::Int(n) | PgValue::Int4(n) => n.to_sql(ty, out),
336            PgValue::Int4Array(ns) => ns.to_sql(ty, out),
337            PgValue::Numeric(s)
338            | PgValue::Decimal(s)
339            // Money (use is discouraged)
340            //
341            // fractional precision is determined by the database's `lc_monetary` setting.
342            //
343            // NOTE: if you are storing currency amounts, consider
344            // using integer (whole number) counts of smallest indivisible pieces of currency
345            // (ex. cent amounts to represent United States Dollars; 100 cents = 1 USD)
346            | PgValue::Money(s) => {
347                let bigd = pg_bigdecimal::BigDecimal::parse_bytes(s.as_bytes(), 10).ok_or_else(|| {
348                    format!("failed to parse bigint [{s}]")
349                })?;
350                PgNumeric::new(Some(bigd)).to_sql(ty, out)
351            }
352            PgValue::NumericArray(ss) | PgValue::MoneyArray(ss) => {
353                ss.
354                    iter()
355                    .map(|s| {
356                        pg_bigdecimal::BigDecimal::parse_bytes(s.as_bytes(), 10)
357                            .map(|v| PgNumeric::new(Some(v)))
358                            .ok_or_else(|| {
359                                format!("failed to parse bigint [{s}]")
360                            })
361                    })
362                    .collect::<Result<Vec<PgNumeric>, _>>()?
363                    .to_sql(ty, out)
364            }
365
366            PgValue::SmallInt(n) | PgValue::Int2(n) => n.to_sql(ty, out),
367            PgValue::Int2Array(ns) => ns.to_sql(ty, out),
368            PgValue::Int2Vector(ns) => ns.to_sql(ty, out),
369            PgValue::Int2VectorArray(ns) => ns.to_sql(ty, out),
370
371            PgValue::Serial(n) | PgValue::Serial4(n) => n.to_sql(ty, out),
372            PgValue::SmallSerial(n) | PgValue::Serial2(n) => n.to_sql(ty, out),
373
374            // Bytes
375            PgValue::Bit((exact_size, bytes)) => {
376                if bytes.len() != *exact_size as usize {
377                    return Err("bitfield size does not match".into());
378                }
379                bytes.as_ref().to_sql(ty, out)
380            }
381            PgValue::BitArray(many_bits) => {
382                let mut vec: Vec<Vec<u8>> = Vec::new();
383                for (exact_size, bytes) in many_bits.iter() {
384                    if bytes.len() != *exact_size as usize {
385                        return Err("bitfield size does not match".into());
386                    }
387                    vec.push(bytes.to_vec())
388                }
389                vec.to_sql(ty, out)
390            }
391            PgValue::BitVarying((limit, bytes)) | PgValue::Varbit((limit, bytes)) => {
392                if limit.is_some_and(|limit| bytes.len() > limit as usize) {
393                    return Err("bit field length is greater than limit".into());
394                }
395                bytes.as_ref().to_sql(ty, out)
396            }
397            PgValue::VarbitArray(many_varbits) => {
398                let mut valid_varbits: Vec<Vec<u8>> = Vec::new();
399                for (limit, bytes) in many_varbits {
400                    if limit.is_some_and(|limit| bytes.len() > limit as usize) {
401                        return Err("bit field length is greater than limit".into());
402                    }
403                    valid_varbits.push(bytes.to_vec())
404                }
405                valid_varbits.to_sql(ty, out)
406
407            }
408            PgValue::Bytea(bytes) => bytes.as_ref().to_sql(ty, out),
409            PgValue::ByteaArray(many_bytes) => many_bytes.iter().map(AsRef::as_ref).collect::<Vec<_>>().to_sql(ty, out),
410
411            // Characters
412            PgValue::Char((len, bytes)) => {
413                if bytes.len() != *len as usize {
414                    return Err("char length does not match specified size".into());
415                }
416                bytes.as_ref().to_sql(ty, out)
417            }
418            PgValue::CharArray(many_chars) => {
419                let mut valid_chars = Vec::new();
420                for (len, bytes) in many_chars {
421                    if bytes.len() != *len as usize {
422                        return Err("char length does not match specified size".into());
423                    }
424                    valid_chars.push(bytes.as_ref());
425                }
426                valid_chars.to_sql(ty, out)
427            }
428            PgValue::Varchar((maybe_len, bytes)) => {
429                if let Some(limit) = maybe_len {
430                    if bytes.len() > *limit as usize {
431                        return Err(format!(
432                            "char length [{}] does not match specified limit [{limit}]",
433                            bytes.len(),
434                        )
435                        .into());
436                    }
437                }
438                bytes.as_ref().to_sql(ty, out)
439            }
440            PgValue::VarcharArray(vs) => {
441                let mut valid_varchars = Vec::new();
442                for (maybe_len, bytes) in vs {
443                    if let Some(limit) = maybe_len {
444                        if bytes.len() > *limit as usize {
445                            return Err(format!(
446                                "char length [{}] does not match specified limit [{limit}]",
447                                bytes.len(),
448                            )
449                                       .into());
450                        }
451                    }
452                    valid_varchars.push(bytes.as_ref())
453                }
454                valid_varchars.to_sql(ty, out)
455            }
456
457            // Networking
458            PgValue::Cidr(cidr) => {
459                IpCidr::from_str(cidr)
460                    .map_err(|e| format!("invalid cidr: {e}"))?
461                    .to_sql(ty, out)
462            }
463            PgValue::CidrArray(cidrs) => {
464                cidrs
465                    .iter()
466                    .map(|v| IpCidr::from_str(v).map_err(|e| format!("invalid cidr: {e}")))
467                    .collect::<Result<Vec<IpCidr>, _>>()?
468                    .to_sql(ty, out)
469            }
470
471            PgValue::Inet(addr) => {
472                IpAddr::from_str(addr)
473                    .map_err(|e| format!("invalid address: {e}"))?
474                    .to_sql(ty, out)
475            }
476            PgValue::InetArray(inets) => {
477                inets
478                    .iter()
479                    .map(|v| IpAddr::from_str(v).map_err(|e| format!("invalid address: {e}")))
480                    .collect::<Result<Vec<IpAddr>, _>>()?
481                    .to_sql(ty, out)
482
483            }
484
485            // EUI-48 (octets)
486            PgValue::Macaddr(m) => {
487                m.to_sql(ty, out)
488            }
489            PgValue::MacaddrArray(macs) => {
490                macs.clone().to_sql(ty, out)
491            }
492
493            // EUI-64 (deprecated)
494            PgValue::Macaddr8(m) => {
495                m.to_sql(ty, out)
496            }
497            PgValue::Macaddr8Array(macs) => {
498                macs.clone().to_sql(ty, out)
499            }
500
501            // Geo
502            PgValue::Circle(_) | PgValue::CircleArray(_) => {
503                Err("circle & circle[] are not supported".into())
504            },
505            PgValue::Box(((start_x, start_y), (end_x, end_y)))  => {
506                let start_x = f64_from_tuple(start_x);
507                let start_y = f64_from_tuple(start_y);
508                let end_x = f64_from_tuple(end_x);
509                let end_y = f64_from_tuple(end_y);
510                Rect::<f64>::new(
511                    coord! { x: start_x, y: start_y },
512                    coord! { x: end_x, y: end_y },
513                ).to_sql(ty, out)
514            }
515
516            PgValue::Line(((start_x, start_y), (end_x, end_y))) | PgValue::Lseg(((start_x, start_y), (end_x, end_y))) => {
517                LineString::<f64>::new(vec![
518                    coord!{ x: f64_from_tuple(start_x), y: f64_from_tuple(start_y) },
519                    coord!{ x: f64_from_tuple(end_x), y: f64_from_tuple(end_y) },
520                ]).to_sql(ty, out)
521            },
522            PgValue::LineArray(lines) | PgValue::LsegArray(lines) => {
523                lines
524                    .iter()
525                    .map(|((start_x, start_y), (end_x, end_y))| LineString::<f64>::new(vec![
526                        coord! { x: f64_from_tuple(start_x), y: f64_from_tuple(start_y) },
527                        coord! { x: f64_from_tuple(end_x), y: f64_from_tuple(end_y) },
528                    ]))
529                    .collect::<Vec<LineString>>()
530                    .to_sql(ty, out)
531            }
532
533            PgValue::BoxArray(boxes) => {
534                boxes
535                    .iter()
536                    .map(|((start_x, start_y), (end_x, end_y))| Rect::<f64>::new(
537                        coord! { x: f64_from_tuple(start_x), y: f64_from_tuple(start_y) },
538                        coord! { x: f64_from_tuple(end_x), y: f64_from_tuple(end_y) },
539                    ))
540                    .collect::<Vec<Rect<f64>>>()
541                    .to_sql(ty, out)
542
543            }
544
545            PgValue::Point((x, y)) => {
546                Point::<f64>::new(f64_from_tuple(x), f64_from_tuple(y)).to_sql(ty, out)
547            },
548            PgValue::PointArray(points) => {
549                points.iter().map(|(x, y)| Point::<f64>::new(f64_from_tuple(x), f64_from_tuple(y))).collect::<Vec<Point>>().to_sql(ty, out)
550            }
551
552            PgValue::Path(points) | PgValue::Polygon(points) => {
553                if points.is_empty() { return Err("invalid polygon, no points specified".into()) }
554                points
555                    .iter()
556                    .map(|(x, y)|  Point::<f64>::new(f64_from_tuple(x), f64_from_tuple(y)))
557                    .collect::<Vec<Point<f64>>>()
558                    .to_sql(ty, out)
559            },
560
561            PgValue::PathArray(paths) | PgValue::PolygonArray(paths) => {
562                paths
563                    .iter()
564                    .map(|path| {
565                        path
566                            .iter()
567                            .map(|(x, y)|  Point::<f64>::new(f64_from_tuple(x), f64_from_tuple(y)))
568                            .collect::<Vec<Point<f64>>>()
569                    })
570                    .collect::<Vec<Vec<Point<f64>>>>()
571                    .to_sql(ty, out)
572            }
573
574
575            // Date-time
576            PgValue::Date(d) => {
577                let d: NaiveDate = d.try_into()?;
578                d.to_sql(ty, out)
579            }
580            PgValue::DateArray(ds) => {
581                ds
582                    .iter()
583                    .map(|v| v.try_into())
584                    .collect::<Result<Vec<NaiveDate>, _>>()?
585                    .to_sql(ty, out)
586            }
587
588            PgValue::Interval(_) | PgValue::IntervalArray(_) => {
589                Err("interval not supported (consider using a cast like 'value'::text::interval)".into())
590            },
591
592            PgValue::Time(t) => {
593                let t: chrono::NaiveTime = t.try_into()?;
594                t.to_sql(ty, out)
595            }
596            PgValue::TimeArray(ts) => {
597                ts
598                    .iter()
599                    .map(|t| t.try_into())
600                    .collect::<Result<Vec<NaiveTime>, _>>()?
601                    .to_sql(ty, out)
602            }
603
604            PgValue::TimeTz(_) | PgValue::TimeTzArray(_) => {
605                Err("timetz not supported (consider using a cast like 'value'::text::timetz)".into())
606            }
607
608            PgValue::Timestamp(ts) => {
609                let ts: NaiveDateTime = ts.try_into()?;
610                ts.to_sql(ty, out)
611            }
612            PgValue::TimestampArray(tss) => {
613                tss
614                    .iter()
615                    .map(|ts| ts.try_into())
616                    .collect::<Result<Vec<NaiveDateTime>, _>>()?
617                    .to_sql(ty, out)
618            }
619
620            PgValue::TimestampTz(tstz) => {
621                let tstz: chrono::DateTime<Utc> = tstz.try_into()?;
622                tstz.to_sql(ty, out)
623            }
624            PgValue::TimestampTzArray(tstzs) => {
625                tstzs
626                    .iter()
627                    .map(|tstz| tstz.try_into())
628                    .collect::<Result<Vec<chrono::DateTime<Utc>>, _>>()?
629                    .to_sql(ty, out)
630            }
631
632            // JSON
633            PgValue::Json(s) | PgValue::Jsonb(s) => {
634                serde_json::Value::from_str(s)
635                    .map_err(|e| format!("failed to parse JSON: {e}"))?
636                    .to_sql(ty, out)
637            },
638            PgValue::JsonArray(json_strings) | PgValue::JsonbArray(json_strings) => {
639                json_strings
640                    .iter()
641                    .map(|s| serde_json::Value::from_str(s))
642                    .collect::<Result<Vec<serde_json::Value>, _>>()?
643                    .to_sql(ty, out)
644            },
645            // Postgres-internal
646            PgValue::PgLsn(offset) => PgLsn::from(*offset).to_sql(ty, out),
647            PgValue::PgLsnArray(offsets) => {
648                offsets
649                    .iter()
650                    .cloned()
651                    .map(PgLsn::from)
652                    .collect::<Vec<PgLsn>>()
653                    .to_sql(ty, out)
654            }
655            PgValue::PgSnapshot((xmin, xmax, xip_list)) => {
656                format!(
657                    "{xmin}:{xmax}:{}",
658                    xip_list
659                        .iter()
660                        .map(|v| v.to_string())
661                        .collect::<Vec<String>>()
662                        .join(","),
663                ).to_sql(ty, out)
664            },
665            PgValue::TxidSnapshot(n) => n.to_sql(ty, out),
666
667            // Text
668            PgValue::Name(s) => s.to_sql(ty, out),
669            PgValue::NameArray(ss) => ss.to_sql(ty, out),
670            PgValue::Text(s) => s.to_sql(ty, out),
671            PgValue::TextArray(ss) => ss.to_sql(ty, out),
672            PgValue::Xml(s) => s.to_sql(ty, out),
673            PgValue::XmlArray(ss) => ss.to_sql(ty, out),
674
675            // Full Text Search
676            PgValue::TsQuery(s) => s.to_sql(ty, out),
677            PgValue::TsVector(_) => {
678                Err("tsvector not supported (consider using a cast like 'value'::text::tsvector)".into())
679            }
680
681            // UUIDs
682            PgValue::Uuid(s) => {
683                Uuid::from_str(s)?.to_sql(ty, out)
684            }
685            PgValue::UuidArray(ss) => {
686                ss
687                    .iter()
688                    .map(|v| Uuid::from_str(v.as_ref()))
689                    .collect::<Result<Vec<Uuid>, _>>()?
690                    .to_sql(ty, out)
691            }
692
693            // UUIDs
694            PgValue::Hstore(h) => {
695                let map  = HashMap::<String, Option<String>>::from_iter(h.iter().cloned());
696                map.to_sql(ty, out)
697            }
698        }
699    }
700
701    fn accepts(_ty: &PgType) -> bool {
702        // NOTE: we don't actually support all types, but pretend to, in order to
703        // use more specific/tailored error messages
704        true
705    }
706
707    tokio_postgres::types::to_sql_checked!();
708}
709
710impl TryFrom<&[u8]> for MacAddressEui48 {
711    type Error = Box<dyn Error + Sync + Send>;
712
713    fn try_from(bytes: &[u8]) -> Result<Self, Box<dyn Error + Sync + Send>> {
714        match bytes[..] {
715            [octet0, octet1, octet2, octet3, octet4, octet5] => Ok(Self {
716                bytes: (octet0, octet1, octet2, octet3, octet4, octet5),
717            }),
718            _ => Err(format!(
719                "unexpected number of bytes ({}) in EUI48 mac address",
720                bytes.len()
721            )
722            .into()),
723        }
724    }
725}
726
727impl TryFrom<&[u8]> for MacAddressEui64 {
728    type Error = Box<dyn Error + Sync + Send>;
729
730    fn try_from(bytes: &[u8]) -> Result<Self, Box<dyn Error + Sync + Send>> {
731        match bytes[..] {
732            [octet0, octet1, octet2, octet3, octet4, octet5, octet6, octet7] => Ok(Self {
733                bytes: (
734                    octet0, octet1, octet2, octet3, octet4, octet5, octet6, octet7,
735                ),
736            }),
737            _ => Err(format!(
738                "unexpected number of bytes ({}) in EUI64 mac address",
739                bytes.len()
740            )
741            .into()),
742        }
743    }
744}
745
746impl FromSql<'_> for MacAddressEui48 {
747    fn from_sql(ty: &PgType, raw: &[u8]) -> Result<Self, Box<dyn Error + Sync + Send>> {
748        match (ty, raw) {
749            (&tokio_postgres::types::Type::MACADDR, bytes) if bytes.len() == 6 => {
750                MacAddressEui48::try_from(bytes)
751            }
752            _ => Err("invalid type/raw input for EUI48 MAC address".into()),
753        }
754    }
755
756    fn accepts(ty: &PgType) -> bool {
757        matches!(ty, &tokio_postgres::types::Type::MACADDR)
758    }
759}
760
761impl FromSql<'_> for MacAddressEui64 {
762    fn from_sql(ty: &PgType, raw: &[u8]) -> Result<Self, Box<dyn Error + Sync + Send>> {
763        match (ty, raw) {
764            (&tokio_postgres::types::Type::MACADDR8, bytes) if bytes.len() == 8 => {
765                MacAddressEui64::try_from(bytes)
766            }
767            _ => Err("invalid type/raw input for EUI64 MAC address".into()),
768        }
769    }
770
771    fn accepts(ty: &PgType) -> bool {
772        matches!(ty, &tokio_postgres::types::Type::MACADDR8)
773    }
774}
775
776impl FromSql<'_> for PgValue {
777    fn from_sql(ty: &PgType, raw: &[u8]) -> Result<Self, Box<dyn Error + Sync + Send>> {
778        match ty {
779            &tokio_postgres::types::Type::BOOL => Ok(PgValue::Bool(bool::from_sql(ty, raw)?)),
780            &tokio_postgres::types::Type::BOOL_ARRAY => {
781                Ok(PgValue::BoolArray(Vec::<bool>::from_sql(ty, raw)?))
782            }
783            &tokio_postgres::types::Type::BYTEA => {
784                let buf = Vec::<u8>::from_sql(ty, raw)?;
785                Ok(PgValue::Bytea(buf.into()))
786            }
787            &tokio_postgres::types::Type::BYTEA_ARRAY => {
788                let buf = Vec::<Vec<u8>>::from_sql(ty, raw)?;
789                Ok(PgValue::ByteaArray(buf.into_iter().map(Bytes::from).collect()))
790            }
791            &tokio_postgres::types::Type::CHAR => {
792                let s = Vec::<u8>::from_sql(ty, raw)?;
793                let len = s.len().try_into()?;
794                Ok(PgValue::Char((len, s.into())))
795            }
796            &tokio_postgres::types::Type::CHAR_ARRAY => {
797                let list = Vec::<Vec<u8>>::from_sql(ty, raw)?;
798                let mut cs = Vec::new();
799                for c in list {
800                    cs.push((c.len().try_into()?, c.into()));
801                }
802                Ok(PgValue::CharArray(cs))
803            }
804            &tokio_postgres::types::Type::BIT => {
805                let vec = BitVec::from_sql(ty, raw)?;
806                let len = vec.len().try_into()?;
807                Ok(PgValue::Bit((len, vec.to_bytes().into())))
808            }
809            &tokio_postgres::types::Type::BIT_ARRAY => {
810                let vecs = Vec::<BitVec>::from_sql(ty, raw)?
811                    .into_iter()
812                    .map(|v| v.len().try_into().map(|len| (len, v.to_bytes().into())))
813                    .collect::<Result<Vec<_>, _>>()?;
814                Ok(PgValue::BitArray(vecs))
815            }
816            &tokio_postgres::types::Type::VARBIT => {
817                let vec = BitVec::from_sql(ty, raw)?;
818                let len = vec.len().try_into()?;
819                Ok(PgValue::Bit((len, vec.to_bytes().into())))
820            }
821            &tokio_postgres::types::Type::VARBIT_ARRAY => {
822                let varbits = Vec::<BitVec>::from_sql(ty, raw)?
823                    .into_iter()
824                    // NOTE: we don't know what the  limit of this varbit was if we only
825                    // have the bytes, default to allowing it to be unbounded
826                    .map(|v| (None, v.to_bytes().into()))
827                    .collect::<Vec<_>>();
828                Ok(PgValue::VarbitArray(varbits))
829            }
830            &tokio_postgres::types::Type::FLOAT4 => {
831                Ok(PgValue::Float4(f32::from_sql(ty, raw)?.integer_decode()))
832            }
833            &tokio_postgres::types::Type::FLOAT4_ARRAY => Ok(PgValue::Float4Array(
834                Vec::<f32>::from_sql(ty, raw)?
835                    .into_iter()
836                    .map(|f| f.integer_decode())
837                    .collect::<Vec<_>>(),
838            )),
839            &tokio_postgres::types::Type::FLOAT8 => {
840                Ok(PgValue::Float8(f64::from_sql(ty, raw)?.integer_decode()))
841            }
842            &tokio_postgres::types::Type::FLOAT8_ARRAY => Ok(PgValue::Float8Array(
843                Vec::<f64>::from_sql(ty, raw)?
844                    .into_iter()
845                    .map(|f| f.integer_decode())
846                    .collect::<Vec<_>>(),
847            )),
848            &tokio_postgres::types::Type::INT2 => Ok(PgValue::Int2(i16::from_sql(ty, raw)?)),
849            &tokio_postgres::types::Type::INT2_ARRAY => {
850                Ok(PgValue::Int2Array(Vec::<i16>::from_sql(ty, raw)?))
851            }
852            &tokio_postgres::types::Type::INT2_VECTOR => {
853                Ok(PgValue::Int2Vector(Vec::<i16>::from_sql(ty, raw)?))
854            }
855            &tokio_postgres::types::Type::INT2_VECTOR_ARRAY => Ok(PgValue::Int2VectorArray(
856                Vec::<Vec<i16>>::from_sql(ty, raw)?,
857            )),
858            &tokio_postgres::types::Type::INT4 => Ok(PgValue::Int4(i32::from_sql(ty, raw)?)),
859            &tokio_postgres::types::Type::INT4_RANGE
860            | &tokio_postgres::types::Type::INT4_RANGE_ARRAY
861            | &tokio_postgres::types::Type::INT4MULTI_RANGE
862            | &tokio_postgres::types::Type::INT4MULTI_RANGE_ARRAY => {
863                Err("int4 ranges are not yet supported".into())
864            }
865            &tokio_postgres::types::Type::INT4_ARRAY => Ok(PgValue::Int4Array(
866                Vec::<i32>::from_sql(ty, raw)?.into_iter().collect(),
867            )),
868            &tokio_postgres::types::Type::INT8 => Ok(PgValue::Int8(i64::from_sql(ty, raw)?)),
869            &tokio_postgres::types::Type::INT8_ARRAY => {
870                Ok(PgValue::Int8Array(Vec::<i64>::from_sql(ty, raw)?))
871            }
872            &tokio_postgres::types::Type::INT8MULTI_RANGE
873            | &tokio_postgres::types::Type::INT8MULTI_RANGE_ARRAY
874            | &tokio_postgres::types::Type::INT8_RANGE
875            | &tokio_postgres::types::Type::INT8_RANGE_ARRAY => {
876                Err("int8 ranges are not yet supported".into())
877            }
878
879            &tokio_postgres::types::Type::MONEY => Ok(PgValue::Money(Numeric::from_sql(ty, raw)?)),
880            &tokio_postgres::types::Type::MONEY_ARRAY => {
881                Ok(PgValue::MoneyArray(Vec::<Numeric>::from_sql(ty, raw)?))
882            }
883            &tokio_postgres::types::Type::NUMERIC => {
884                Ok(PgValue::Numeric(Numeric::from_sql(ty, raw)?))
885            }
886            &tokio_postgres::types::Type::NUMERIC_ARRAY => {
887                Ok(PgValue::NumericArray(Vec::<Numeric>::from_sql(ty, raw)?))
888            }
889            &tokio_postgres::types::Type::NUMMULTI_RANGE
890            | &tokio_postgres::types::Type::NUMMULTI_RANGE_ARRAY
891            | &tokio_postgres::types::Type::NUM_RANGE
892            | &tokio_postgres::types::Type::NUM_RANGE_ARRAY => {
893                Err("numeric ranges are not yet supported".into())
894            }
895
896            // JSON
897            &tokio_postgres::types::Type::JSON => Ok(PgValue::Json(
898                serde_json::Value::from_sql(ty, raw)?.to_string(),
899            )),
900            &tokio_postgres::types::Type::JSON_ARRAY => Ok(PgValue::JsonArray(
901                Vec::<serde_json::Value>::from_sql(ty, raw)?
902                    .into_iter()
903                    .map(|v| v.to_string())
904                    .collect::<Vec<_>>(),
905            )),
906            &tokio_postgres::types::Type::JSONB => Ok(PgValue::Json(
907                serde_json::Value::from_sql(ty, raw)?.to_string(),
908            )),
909            &tokio_postgres::types::Type::JSONB_ARRAY => Ok(PgValue::JsonbArray(
910                Vec::<serde_json::Value>::from_sql(ty, raw)?
911                    .into_iter()
912                    .map(|v| v.to_string())
913                    .collect::<Vec<_>>(),
914            )),
915            &tokio_postgres::types::Type::VARCHAR => {
916                Ok(PgValue::Varchar(
917                    // We cannot know whether the varchar had a limit
918                    Vec::<u8>::from_sql(ty, raw).map(|v| (None, v.into()))?,
919                ))
920            }
921            &tokio_postgres::types::Type::VARCHAR_ARRAY => Ok(PgValue::VarcharArray(
922                Vec::<Vec<u8>>::from_sql(ty, raw)?
923                    .into_iter()
924                    .map(|s| (None, s.into()))
925                    .collect::<Vec<_>>(),
926            )),
927            &tokio_postgres::types::Type::NAME => Ok(PgValue::Name(String::from_sql(ty, raw)?)),
928            &tokio_postgres::types::Type::NAME_ARRAY => {
929                Ok(PgValue::NameArray(Vec::<String>::from_sql(ty, raw)?))
930            }
931            &tokio_postgres::types::Type::TEXT => Ok(PgValue::Text(String::from_sql(ty, raw)?)),
932            &tokio_postgres::types::Type::TEXT_ARRAY => {
933                Ok(PgValue::TextArray(Vec::<String>::from_sql(ty, raw)?))
934            }
935            &tokio_postgres::types::Type::XML => Ok(PgValue::Xml(String::from_sql(ty, raw)?)),
936            &tokio_postgres::types::Type::XML_ARRAY => {
937                Ok(PgValue::XmlArray(Vec::<String>::from_sql(ty, raw)?))
938            }
939            &tokio_postgres::types::Type::BOX => {
940                Ok(PgValue::Box(rect_to_hashable_f64s(Rect::<f64>::from_sql(
941                    ty, raw,
942                )?)))
943            }
944            &tokio_postgres::types::Type::BOX_ARRAY => Ok(PgValue::BoxArray(
945                Vec::<Rect<f64>>::from_sql(ty, raw)?
946                    .into_iter()
947                    .map(rect_to_hashable_f64s)
948                    .collect::<Vec<_>>(),
949            )),
950            &tokio_postgres::types::Type::CIRCLE | &tokio_postgres::types::Type::CIRCLE_ARRAY => {
951                Err("circle & circle[] are not supported".into())
952            }
953            &tokio_postgres::types::Type::LINE => Ok(PgValue::Line(
954                linestring_to_hashable_f64s_tuple(LineString::<f64>::from_sql(ty, raw)?)?,
955            )),
956            &tokio_postgres::types::Type::LINE_ARRAY => Ok(PgValue::LineArray(
957                Vec::<LineString<f64>>::from_sql(ty, raw)?
958                    .into_iter()
959                    .map(linestring_to_hashable_f64s_tuple)
960                    .collect::<Result<Vec<_>, _>>()?,
961            )),
962            &tokio_postgres::types::Type::LSEG => Ok(PgValue::Lseg(
963                linestring_to_hashable_f64s_tuple(LineString::<f64>::from_sql(ty, raw)?)?,
964            )),
965            &tokio_postgres::types::Type::LSEG_ARRAY => Ok(PgValue::LsegArray(
966                Vec::<LineString<f64>>::from_sql(ty, raw)?
967                    .into_iter()
968                    .map(linestring_to_hashable_f64s_tuple)
969                    .collect::<Result<Vec<_>, _>>()?,
970            )),
971            &tokio_postgres::types::Type::PATH => Ok(PgValue::Path(
972                Vec::<Point<f64>>::from_sql(ty, raw)?
973                    .into_iter()
974                    .map(point_to_hashable_f64s)
975                    .collect::<Vec<_>>(),
976            )),
977            &tokio_postgres::types::Type::PATH_ARRAY => Ok(PgValue::PathArray(
978                Vec::<Vec<Point<f64>>>::from_sql(ty, raw)?
979                    .into_iter()
980                    .map(|points| points.into_iter().map(point_to_hashable_f64s).collect())
981                    .collect::<Vec<_>>(),
982            )),
983            &tokio_postgres::types::Type::POINT => {
984                let point = Point::<f64>::from_sql(ty, raw)?;
985                Ok(PgValue::Point(point_to_hashable_f64s(point)))
986            }
987            &tokio_postgres::types::Type::POINT_ARRAY => Ok(PgValue::PointArray(
988                Vec::<Point<f64>>::from_sql(ty, raw)?
989                    .into_iter()
990                    .map(point_to_hashable_f64s)
991                    .collect::<Vec<_>>(),
992            )),
993            &tokio_postgres::types::Type::POLYGON => Ok(PgValue::Polygon(
994                Vec::<Point<f64>>::from_sql(ty, raw)?
995                    .into_iter()
996                    .map(point_to_hashable_f64s)
997                    .collect::<Vec<_>>(),
998            )),
999            &tokio_postgres::types::Type::POLYGON_ARRAY => Ok(PgValue::PolygonArray(
1000                Vec::<Vec<Point<f64>>>::from_sql(ty, raw)?
1001                    .into_iter()
1002                    .map(|v| v.into_iter().map(point_to_hashable_f64s).collect())
1003                    .collect::<Vec<Vec<_>>>(),
1004            )),
1005
1006            &tokio_postgres::types::Type::CIDR => {
1007                let cidr = IpCidr::from_sql(ty, raw)?;
1008                Ok(PgValue::Cidr(cidr.to_string()))
1009            }
1010            &tokio_postgres::types::Type::CIDR_ARRAY => Ok(PgValue::CidrArray(
1011                Vec::<IpCidr>::from_sql(ty, raw)?
1012                    .into_iter()
1013                    .map(|c| c.to_string())
1014                    .collect::<Vec<String>>(),
1015            )),
1016            &tokio_postgres::types::Type::INET => {
1017                let inet = IpAddr::from_sql(ty, raw)?;
1018                Ok(PgValue::Inet(inet.to_string()))
1019            }
1020            &tokio_postgres::types::Type::INET_ARRAY => Ok(PgValue::InetArray(
1021                Vec::<IpAddr>::from_sql(ty, raw)?
1022                    .into_iter()
1023                    .map(|i| i.to_string())
1024                    .collect::<Vec<String>>(),
1025            )),
1026
1027            &tokio_postgres::types::Type::MACADDR => {
1028                Ok(MacAddressEui48::from_sql(ty, raw)?.into())
1029            }
1030            &tokio_postgres::types::Type::MACADDR_ARRAY => Ok(PgValue::MacaddrArray(
1031                Vec::<MacAddressEui48>::from_sql(ty, raw)?
1032            )),
1033            &tokio_postgres::types::Type::MACADDR8 => {
1034                Ok(MacAddressEui64::from_sql(ty, raw)?.into())
1035            }
1036            &tokio_postgres::types::Type::MACADDR8_ARRAY => Ok(PgValue::Macaddr8Array(
1037                Vec::<MacAddressEui64>::from_sql(ty, raw)?
1038            )),
1039            &tokio_postgres::types::Type::DATE => {
1040                Ok(PgValue::Date(NaiveDate::from_sql(ty, raw)?.into()))
1041            }
1042            &tokio_postgres::types::Type::DATE_ARRAY => {
1043                Ok(PgValue::DateArray(Vec::<NaiveDate>::from_sql(ty, raw)?
1044                   .into_iter()
1045                   .map(|t| t.into())
1046                   .collect::<Vec<Date>>()))
1047            }
1048
1049            &tokio_postgres::types::Type::DATE_RANGE
1050            | &tokio_postgres::types::Type::DATE_RANGE_ARRAY
1051            | &tokio_postgres::types::Type::DATEMULTI_RANGE => {
1052                Err("date ranges are not yet supported".into())
1053            }
1054
1055            &tokio_postgres::types::Type::TIME => {
1056                Ok(PgValue::Time(NaiveTime::from_sql(ty, raw)?.into()))
1057            }
1058            &tokio_postgres::types::Type::TIME_ARRAY => {
1059                Ok(PgValue::TimeArray(Vec::<NaiveTime>::from_sql(ty, raw)?
1060                   .into_iter()
1061                   .map(|t| t.into())
1062                   .collect::<Vec<Time>>()))
1063            }
1064            &tokio_postgres::types::Type::TIMESTAMP => {
1065                Ok(PgValue::Timestamp(NaiveDateTime::from_sql(ty, raw)?.into()))
1066
1067            },
1068             &tokio_postgres::types::Type::TIMESTAMP_ARRAY => {
1069                Ok(PgValue::TimestampArray(Vec::<NaiveDateTime>::from_sql(ty, raw)?
1070                   .into_iter()
1071                   .map(|t| t.into())
1072                   .collect::<Vec<Timestamp>>()))
1073             }
1074
1075            &tokio_postgres::types::Type::INTERVAL
1076            | &tokio_postgres::types::Type::INTERVAL_ARRAY => {
1077                Err("intervals are not yet supported".into())
1078            }
1079
1080            &tokio_postgres::types::Type::TIMETZ |
1081            &tokio_postgres::types::Type::TIMETZ_ARRAY => {
1082                Err("timetz is not supported".into())
1083            }
1084
1085            &tokio_postgres::types::Type::TS_RANGE
1086            | &tokio_postgres::types::Type::TS_RANGE_ARRAY
1087            | &tokio_postgres::types::Type::TSMULTI_RANGE
1088            | &tokio_postgres::types::Type::TSMULTI_RANGE_ARRAY => {
1089                Err("timestamp ranges are not yet supported".into())
1090            }
1091
1092            &tokio_postgres::types::Type::TIMESTAMPTZ => {
1093                Ok(PgValue::TimestampTz(DateTime::<Utc>::from_sql(ty, raw)?.into()))
1094            }
1095            &tokio_postgres::types::Type::TIMESTAMPTZ_ARRAY => Ok(PgValue::TimestampTzArray(
1096                Vec::<DateTime<Utc>>::from_sql(ty, raw)?
1097                    .into_iter()
1098                    .map(|v| v.into())
1099                    .collect::<Vec<TimestampTz>>()
1100            )),
1101
1102            &tokio_postgres::types::Type::TSTZ_RANGE
1103            | &tokio_postgres::types::Type::TSTZ_RANGE_ARRAY
1104            | &tokio_postgres::types::Type::TSTZMULTI_RANGE => {
1105                Err("timestamptz ranges are not yet supported".into())
1106            }
1107
1108            &tokio_postgres::types::Type::UUID => {
1109                Ok(PgValue::Uuid(Uuid::from_sql(ty, raw)?.to_string()))
1110            }
1111            &tokio_postgres::types::Type::UUID_ARRAY => Ok(PgValue::UuidArray(
1112                Vec::<Uuid>::from_sql(ty, raw)?
1113                    .into_iter()
1114                    .map(|v| v.to_string())
1115                    .collect::<Vec<String>>(),
1116            )),
1117            &tokio_postgres::types::Type::PG_LSN => {
1118                Ok(PgValue::PgLsn(PgLsn::from_sql(ty, raw)?.into()))
1119            }
1120            &tokio_postgres::types::Type::PG_LSN_ARRAY => Ok(PgValue::PgLsnArray(
1121                Vec::<PgLsn>::from_sql(ty, raw)?
1122                    .into_iter()
1123                    .map(|v| v.into())
1124                    .collect::<Vec<u64>>(),
1125            )),
1126
1127            // All other types are unsupported
1128            t => Err(format!("unsupported type [{t}], consider using a cast like 'value'::string or 'value'::jsonb").into()),
1129        }
1130    }
1131
1132    fn accepts(_ty: &PgType) -> bool {
1133        // NOTE: we don't actually support all types, but pretend to, in order to
1134        // use a more specific error message that encourages using a cast where possible
1135        true
1136    }
1137
1138    fn from_sql_null(_ty: &PgType) -> Result<Self, Box<dyn Error + Sync + Send>> {
1139        Ok(PgValue::Null)
1140    }
1141}