1use 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
25wit_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
34pub(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};
48fn 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
58fn f64_from_tuple(t: &(u64, i16, i8)) -> f64 {
60 f64_from_components(t.0, t.1, t.2)
61}
62
63fn 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
77fn 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
88fn 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
96fn 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
251pub(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 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 | 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 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 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 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 PgValue::Macaddr(m) => {
487 m.to_sql(ty, out)
488 }
489 PgValue::MacaddrArray(macs) => {
490 macs.clone().to_sql(ty, out)
491 }
492
493 PgValue::Macaddr8(m) => {
495 m.to_sql(ty, out)
496 }
497 PgValue::Macaddr8Array(macs) => {
498 macs.clone().to_sql(ty, out)
499 }
500
501 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 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 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 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 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 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 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 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 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 .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 &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 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 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 true
1136 }
1137
1138 fn from_sql_null(_ty: &PgType) -> Result<Self, Box<dyn Error + Sync + Send>> {
1139 Ok(PgValue::Null)
1140 }
1141}