1use std::sync::Arc;
4
5use bytes::Bytes;
6use postgresql_protocol::{backend::FieldDescription, oid::Oid};
7
8use crate::{decode::FromSql, error::Error, json::Json};
9
10#[derive(Debug, Clone)]
12pub struct Column {
13 pub name: String,
15 pub type_oid: Oid,
17 pub type_size: i16,
19 pub type_mod: i32,
21 pub table_oid: u32,
23 pub column_attr: i16,
25}
26
27#[derive(Debug)]
29pub struct Columns {
30 columns: Vec<Column>,
31}
32
33impl Columns {
34 pub(crate) fn from_fields(fields: &[FieldDescription]) -> Arc<Self> {
36 let columns: Vec<Column> = fields
37 .iter()
38 .map(|f| Column {
39 name: f.name.clone(),
40 type_oid: f.type_oid,
41 type_size: f.type_size,
42 type_mod: f.type_mod,
43 table_oid: f.table_oid,
44 column_attr: f.column_attr,
45 })
46 .collect();
47 Arc::new(Columns {
48 columns,
49 })
50 }
51
52 pub fn as_slice(&self) -> &[Column] {
54 &self.columns
55 }
56
57 pub fn len(&self) -> usize {
59 self.columns.len()
60 }
61
62 pub fn is_empty(&self) -> bool {
64 self.columns.is_empty()
65 }
66
67 pub fn index_of(&self, name: &str) -> Option<usize> {
72 self.columns.iter().position(|column| column.name == name)
73 }
74}
75
76#[derive(Debug, Clone)]
81pub struct Row {
82 columns: Arc<Columns>,
83 values: Box<[Option<Bytes>]>,
84 zone: time::TimeZone,
85}
86
87impl Row {
88 pub(crate) fn new(columns: Arc<Columns>, values: Box<[Option<Bytes>]>, zone: time::TimeZone) -> Self {
89 Row {
90 columns,
91 values,
92 zone,
93 }
94 }
95
96 pub(crate) fn parse(columns: Arc<Columns>, payload: &Bytes, zone: time::TimeZone) -> Result<Self, Error> {
103 if payload.len() < 2 {
104 return Err(Error::Decode("data row is truncated".into()));
105 }
106 let raw_count = i16::from_be_bytes(payload[0..2].try_into().unwrap());
107 let count = usize::try_from(raw_count)
108 .map_err(|_| Error::Decode(format!("data row has an invalid column count {raw_count}")))?;
109 if count != columns.len() {
110 return Err(Error::Decode(format!(
111 "data row has {count} columns but the statement returns {}",
112 columns.len()
113 )));
114 }
115 let mut pos = 2usize;
116
117 let mut values = Vec::with_capacity(count);
118 for _ in 0..count {
119 if pos + 4 > payload.len() {
120 return Err(Error::Decode("data row is truncated".into()));
121 }
122 let raw_len = i32::from_be_bytes(payload[pos..pos + 4].try_into().unwrap());
123 pos += 4;
124 if raw_len == -1 {
125 values.push(None);
126 } else {
127 let len = usize::try_from(raw_len)
128 .map_err(|_| Error::Decode(format!("data row column has an invalid length {raw_len}")))?;
129 if pos + len > payload.len() {
130 return Err(Error::Decode("data row column exceeds frame".into()));
131 }
132 values.push(Some(payload.slice(pos..pos + len)));
133 pos += len;
134 }
135 }
136 Ok(Row::new(columns, values.into_boxed_slice(), zone))
137 }
138
139 pub fn columns(&self) -> &[Column] {
141 self.columns.as_slice()
142 }
143
144 pub fn len(&self) -> usize {
146 self.values.len()
147 }
148
149 pub fn is_empty(&self) -> bool {
151 self.values.is_empty()
152 }
153
154 pub fn is_null(&self, index: usize) -> bool {
156 self.values.get(index).map(|v| v.is_none()).unwrap_or(true)
157 }
158
159 pub fn try_get<T: FromSql>(&self, name: &str) -> Result<T, Error> {
167 let index = self
168 .columns
169 .index_of(name)
170 .ok_or_else(|| Error::ColumnNotFound(name.to_string()))?;
171 self.try_get_by_index(index)
172 }
173
174 pub fn try_get_by_index<T: FromSql>(&self, index: usize) -> Result<T, Error> {
183 let column = self
184 .columns
185 .as_slice()
186 .get(index)
187 .ok_or_else(|| Error::ColumnNotFound(format!("index {index}")))?;
188 if !T::accepts(column.type_oid) {
189 return Err(Error::Decode(format!(
190 "column `{}` has type OID {} which cannot be decoded as the requested type",
191 column.name, column.type_oid
192 )));
193 }
194 match self.values.get(index).and_then(|v| v.as_ref()) {
195 None => T::from_sql_null().map_err(|_| Error::UnexpectedNull(column.name.clone())),
196 Some(bytes) => T::from_sql_zoned(column.type_oid, bytes, self.zone),
197 }
198 }
199
200 fn value(&self, name: &str) -> Result<Option<&Bytes>, Error> {
201 let index = self
202 .columns
203 .index_of(name)
204 .ok_or_else(|| Error::ColumnNotFound(name.to_string()))?;
205 Ok(self.values.get(index).and_then(|v| v.as_ref()))
206 }
207
208 pub fn get_str<'a>(&'a self, name: &str) -> Result<&'a str, Error> {
216 match self.value(name)? {
217 None => Err(Error::UnexpectedNull(name.to_string())),
218 Some(bytes) => Ok(std::str::from_utf8(bytes)?),
219 }
220 }
221
222 pub fn get_bytes<'a>(&'a self, name: &str) -> Result<Option<&'a [u8]>, Error> {
230 Ok(self.value(name)?.map(|bytes| bytes.as_ref()))
231 }
232}
233
234pub trait FromRow: Sized {
239 fn from_row(row: &Row) -> Result<Self, Error>;
241}
242
243macro_rules! impl_from_row_scalar {
244 ($($ty:ty),* $(,)?) => {
245 $(
246 impl FromRow for $ty {
247 fn from_row(row: &Row) -> Result<Self, Error> {
248 row.try_get_by_index(0)
249 }
250 }
251 )*
252 };
253}
254
255impl_from_row_scalar!(
256 i16,
257 i32,
258 i64,
259 f32,
260 f64,
261 bool,
262 String,
263 Vec<u8>,
264 uuid::Uuid,
265 time::DateTime,
266 serde_json::Value,
267 ipnetwork::IpNetwork,
268 crate::interval::Interval,
269);
270
271impl<T: FromSql> FromRow for Option<T> {
272 fn from_row(row: &Row) -> Result<Self, Error> {
273 row.try_get_by_index(0)
274 }
275}
276
277impl FromRow for Row {
278 fn from_row(row: &Row) -> Result<Self, Error> {
279 Ok(row.clone())
280 }
281}
282
283impl<T: FromSql> FromRow for Vec<T> {
284 fn from_row(row: &Row) -> Result<Self, Error> {
285 row.try_get_by_index(0)
286 }
287}
288
289impl<T: serde::de::DeserializeOwned> FromRow for Json<T> {
290 fn from_row(row: &Row) -> Result<Self, Error> {
291 row.try_get_by_index(0)
292 }
293}
294
295macro_rules! impl_from_row_tuple {
296 ($($name:ident),+) => {
297 #[allow(unused_assignments)]
298 impl<$($name: FromSql),+> FromRow for ($($name,)+) {
299 fn from_row(row: &Row) -> Result<Self, Error> {
300 let mut index = 0usize;
301 Ok((
302 $({
303 let value = row.try_get_by_index::<$name>(index)?;
304 index += 1;
305 value
306 },)+
307 ))
308 }
309 }
310 };
311}
312
313impl_from_row_tuple!(A);
314impl_from_row_tuple!(A, B);
315impl_from_row_tuple!(A, B, C);
316impl_from_row_tuple!(A, B, C, D);
317impl_from_row_tuple!(A, B, C, D, E);
318impl_from_row_tuple!(A, B, C, D, E, F);
319impl_from_row_tuple!(A, B, C, D, E, F, G);
320impl_from_row_tuple!(A, B, C, D, E, F, G, H);
321
322#[cfg(test)]
323mod tests {
324 use bytes::BytesMut;
325 use postgresql_protocol::backend::FieldDescription;
326
327 use super::*;
328
329 fn columns(names: &[(&str, Oid)]) -> Arc<Columns> {
330 let fields: Vec<FieldDescription> = names
331 .iter()
332 .map(|(name, oid)| FieldDescription {
333 name: (*name).to_string(),
334 table_oid: 0,
335 column_attr: 0,
336 type_oid: *oid,
337 type_size: -1,
338 type_mod: -1,
339 format: 1,
340 })
341 .collect();
342 Columns::from_fields(&fields)
343 }
344
345 fn frame(parts: &[Option<&[u8]>]) -> Bytes {
346 let mut buf = BytesMut::new();
347 buf.extend_from_slice(&(parts.len() as i16).to_be_bytes());
348 for part in parts {
349 match part {
350 None => buf.extend_from_slice(&(-1i32).to_be_bytes()),
351 Some(bytes) => {
352 buf.extend_from_slice(&(bytes.len() as i32).to_be_bytes());
353 buf.extend_from_slice(bytes);
354 }
355 }
356 }
357 buf.freeze()
358 }
359
360 #[test]
361 fn parses_values_and_nulls() {
362 let cols = columns(&[("a", 25), ("b", 23)]);
363 let payload = frame(&[Some(b"hi"), None]);
364 let row = Row::parse(cols, &payload, time::TimeZone::UTC).unwrap();
365 assert_eq!(row.get_str("a").unwrap(), "hi");
366 assert!(row.is_null(1));
367 assert_eq!(row.len(), 2);
368 }
369
370 #[test]
371 fn negative_column_count_is_rejected() {
372 let cols = columns(&[("a", 25)]);
373 let mut payload = BytesMut::new();
374 payload.extend_from_slice(&(-1i16).to_be_bytes());
375 payload.extend_from_slice(&0i32.to_be_bytes());
376 let err = Row::parse(cols, &payload.freeze(), time::TimeZone::UTC).unwrap_err();
377 assert!(matches!(err, Error::Decode(_)), "{err}");
378 }
379
380 #[test]
381 fn column_count_must_match_the_statement() {
382 let cols = columns(&[("a", 25), ("b", 25)]);
385 let payload = frame(&[Some(b"only one")]);
386 let err = Row::parse(cols, &payload, time::TimeZone::UTC).unwrap_err();
387 assert!(matches!(err, Error::Decode(_)), "{err}");
388 }
389
390 #[test]
391 fn truncated_rows_are_rejected() {
392 let cols = columns(&[("a", 25)]);
393 let mut payload = BytesMut::new();
395 payload.extend_from_slice(&1i16.to_be_bytes());
396 payload.extend_from_slice(&100i32.to_be_bytes());
397 payload.extend_from_slice(b"short");
398 assert!(Row::parse(cols.clone(), &payload.freeze(), time::TimeZone::UTC).is_err());
399
400 let mut payload = BytesMut::new();
402 payload.extend_from_slice(&1i16.to_be_bytes());
403 payload.extend_from_slice(&(-2i32).to_be_bytes());
404 assert!(Row::parse(cols.clone(), &payload.freeze(), time::TimeZone::UTC).is_err());
405
406 let mut payload = BytesMut::new();
408 payload.extend_from_slice(&1i16.to_be_bytes());
409 payload.extend_from_slice(&[1, 2]);
410 assert!(Row::parse(cols, &payload.freeze(), time::TimeZone::UTC).is_err());
411 }
412
413 #[test]
414 fn values_are_zero_copy_slices_of_the_frame() {
415 let cols = columns(&[("a", 25)]);
416 let payload = frame(&[Some(b"hello")]);
417 let row = Row::parse(cols, &payload, time::TimeZone::UTC).unwrap();
418 let text = row.get_str("a").unwrap();
419 assert!(payload.as_ptr_range().contains(&text.as_ptr()));
421 }
422
423 #[derive(serde::Serialize, serde::Deserialize, postgresql_derive::Json, Debug, PartialEq)]
424 struct JsonUser {
425 name: String,
426 }
427
428 #[test]
429 fn json_types_decode_a_whole_column() {
430 let mut jsonb = Vec::new();
431 jsonb.push(1u8); jsonb.extend_from_slice(br#"{"name":"carol"}"#);
433 let payload = frame(&[Some(&jsonb)]);
434
435 let cols = columns(&[("user", postgresql_protocol::oid::JSONBOID)]);
436 let row = Row::parse(cols, &payload, time::TimeZone::UTC).unwrap();
437
438 let derived = <JsonUser as FromRow>::from_row(&row).unwrap();
439 assert_eq!(
440 derived,
441 JsonUser {
442 name: "carol".to_string()
443 }
444 );
445
446 let wrapped = <Json<JsonUser> as FromRow>::from_row(&row).unwrap();
447 assert_eq!(
448 wrapped.into_inner(),
449 JsonUser {
450 name: "carol".to_string()
451 }
452 );
453 }
454
455 #[test]
456 fn decoding_into_the_wrong_type_is_an_error() {
457 let cols = columns(&[("n", 23)]);
458 let payload = frame(&[Some(&1i32.to_be_bytes())]);
459 let row = Row::parse(cols, &payload, time::TimeZone::UTC).unwrap();
460 assert!(row.try_get::<bool>("n").is_err());
462 assert!(row.try_get::<String>("n").is_err());
463 assert_eq!(row.try_get::<i32>("n").unwrap(), 1);
464 }
465}