1use crate::error::{PgError, Result};
2
3pub trait FromSql: Sized {
4 fn from_sql(type_oid: u32, buf: &[u8]) -> Result<Self>;
5}
6
7impl FromSql for i16 {
8 fn from_sql(_type_oid: u32, buf: &[u8]) -> Result<Self> {
9 if buf.len() < 2 {
10 return Err(PgError::Decode("i16: buffer too short".into()));
11 }
12 Ok(i16::from_be_bytes([buf[0], buf[1]]))
13 }
14}
15
16impl FromSql for i32 {
17 fn from_sql(_type_oid: u32, buf: &[u8]) -> Result<Self> {
18 if buf.len() < 4 {
19 return Err(PgError::Decode("i32: buffer too short".into()));
20 }
21 Ok(i32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]]))
22 }
23}
24
25impl FromSql for i64 {
26 fn from_sql(_type_oid: u32, buf: &[u8]) -> Result<Self> {
27 if buf.len() < 8 {
28 return Err(PgError::Decode("i64: buffer too short".into()));
29 }
30 let arr: [u8; 8] = buf[..8].try_into().unwrap();
31 Ok(i64::from_be_bytes(arr))
32 }
33}
34
35impl FromSql for f32 {
36 fn from_sql(_type_oid: u32, buf: &[u8]) -> Result<Self> {
37 if buf.len() < 4 {
38 return Err(PgError::Decode("f32: buffer too short".into()));
39 }
40 let arr: [u8; 4] = buf[..4].try_into().unwrap();
41 Ok(f32::from_be_bytes(arr))
42 }
43}
44
45impl FromSql for f64 {
46 fn from_sql(_type_oid: u32, buf: &[u8]) -> Result<Self> {
47 if buf.len() < 8 {
48 return Err(PgError::Decode("f64: buffer too short".into()));
49 }
50 let arr: [u8; 8] = buf[..8].try_into().unwrap();
51 Ok(f64::from_be_bytes(arr))
52 }
53}
54
55impl FromSql for bool {
56 fn from_sql(_type_oid: u32, buf: &[u8]) -> Result<Self> {
57 if buf.is_empty() {
58 return Err(PgError::Decode("bool: empty buffer".into()));
59 }
60 Ok(buf[0] != 0)
61 }
62}
63
64impl FromSql for String {
65 fn from_sql(_type_oid: u32, buf: &[u8]) -> Result<Self> {
66 String::from_utf8(buf.to_vec()).map_err(|e| PgError::Decode(format!("string: invalid utf-8: {}", e)))
67 }
68}
69
70impl FromSql for Vec<u8> {
71 fn from_sql(_type_oid: u32, buf: &[u8]) -> Result<Self> {
72 Ok(buf.to_vec())
73 }
74}
75
76impl FromSql for uuid::Uuid {
77 fn from_sql(_type_oid: u32, buf: &[u8]) -> Result<Self> {
78 uuid::Uuid::from_slice(buf).map_err(|e| PgError::Decode(format!("uuid: {}", e)))
79 }
80}
81
82impl FromSql for chrono::DateTime<chrono::Utc> {
83 fn from_sql(_type_oid: u32, buf: &[u8]) -> Result<Self> {
84 if buf.len() < 8 {
85 return Err(PgError::Decode("timestamptz: buffer too short".into()));
86 }
87 let arr: [u8; 8] = buf[..8].try_into().unwrap();
88 let micros = i64::from_be_bytes(arr);
89 let pg_epoch = chrono::NaiveDate::from_ymd_opt(2000, 1, 1)
90 .and_then(|d| d.and_hms_opt(0, 0, 0))
91 .map(|d| d.and_utc())
92 .unwrap();
93 let duration = chrono::TimeDelta::microseconds(micros);
94 Ok(pg_epoch + duration)
95 }
96}
97
98impl<T: FromSql> FromSql for Option<T> {
99 fn from_sql(type_oid: u32, buf: &[u8]) -> Result<Self> {
100 if buf.is_empty() {
101 return Ok(None);
102 }
103 T::from_sql(type_oid, buf).map(Some)
104 }
105}
106
107impl<T: FromSql> FromSql for Vec<T> {
108 fn from_sql(_type_oid: u32, buf: &[u8]) -> Result<Self> {
109 if buf.len() < 12 {
110 return Err(PgError::Decode("array: buffer too short".into()));
111 }
112 let _num_dims = i32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]]);
113 let _has_nulls = i32::from_be_bytes([buf[4], buf[5], buf[6], buf[7]]);
114 let _elem_oid = u32::from_be_bytes([buf[8], buf[9], buf[10], buf[11]]);
115
116 let dim_count = i32::from_be_bytes([buf[12], buf[13], buf[14], buf[15]]);
117 let _dim_lbound = i32::from_be_bytes([buf[16], buf[17], buf[18], buf[19]]);
118
119 let mut offset = 20usize;
120 let mut result = Vec::with_capacity(dim_count as usize);
121
122 for _ in 0..dim_count {
123 if offset + 4 > buf.len() {
124 return Err(PgError::Decode("array: invalid element offset".into()));
125 }
126 let elem_len = i32::from_be_bytes([buf[offset], buf[offset + 1], buf[offset + 2], buf[offset + 3]]);
127 offset += 4;
128 if elem_len == -1 {
129 continue;
130 }
131 if offset + elem_len as usize > buf.len() {
132 return Err(PgError::Decode("array: element length exceeds buffer".into()));
133 }
134 let elem = T::from_sql(_elem_oid, &buf[offset..offset + elem_len as usize])?;
135 result.push(elem);
136 offset += elem_len as usize;
137 }
138
139 Ok(result)
140 }
141}