1use std::sync::Arc;
7
8use crate::protocol::Oid;
9use crate::types::{Format, FromSql};
10
11use crate::error::{PgError, Result};
12
13#[derive(Debug, Clone, PartialEq, Eq)]
19#[non_exhaustive]
20pub struct FieldDescription {
21 name: String,
23 table_oid: Oid,
25 column_id: i16,
27 type_oid: Oid,
29 type_size: i16,
31 type_modifier: i32,
33 format: i16,
35}
36
37impl FieldDescription {
38 pub fn new(
40 name: String,
41 table_oid: Oid,
42 column_id: i16,
43 type_oid: Oid,
44 type_size: i16,
45 type_modifier: i32,
46 format: i16,
47 ) -> Self {
48 Self {
49 name,
50 table_oid,
51 column_id,
52 type_oid,
53 type_size,
54 type_modifier,
55 format,
56 }
57 }
58
59 pub fn name(&self) -> &str {
61 &self.name
62 }
63
64 pub fn table_oid(&self) -> Oid {
66 self.table_oid
67 }
68
69 pub fn column_id(&self) -> i16 {
71 self.column_id
72 }
73
74 pub fn type_oid(&self) -> Oid {
76 self.type_oid
77 }
78
79 pub fn type_size(&self) -> i16 {
81 self.type_size
82 }
83
84 pub fn type_modifier(&self) -> i32 {
86 self.type_modifier
87 }
88
89 pub fn format(&self) -> i16 {
91 self.format
92 }
93}
94
95#[derive(Debug, Clone)]
101#[non_exhaustive]
102pub struct Row {
103 columns: Arc<Vec<FieldDescription>>,
104 values: Vec<Option<Vec<u8>>>,
105}
106
107impl Row {
108 pub(crate) fn new(columns: Arc<Vec<FieldDescription>>, values: Vec<Option<Vec<u8>>>) -> Self {
110 Self { columns, values }
111 }
112
113 pub fn len(&self) -> usize {
115 self.values.len()
116 }
117
118 pub fn is_empty(&self) -> bool {
120 self.values.is_empty()
121 }
122
123 pub fn columns(&self) -> &[FieldDescription] {
125 &self.columns
126 }
127
128 pub fn is_null(&self, index: usize) -> bool {
130 match self.values.get(index) {
131 Some(value) => value.is_none(),
132 None => true,
133 }
134 }
135
136 pub fn get_raw(&self, index: usize) -> Option<&[u8]> {
138 self.values.get(index).and_then(|v| v.as_deref())
139 }
140
141 #[must_use = "column access errors should be checked"]
165 pub fn get<T: FromSql>(&self, index: usize) -> Result<T> {
166 let raw = self.get_raw(index);
167 let field = self
168 .columns
169 .get(index)
170 .ok_or_else(|| PgError::ColumnIndexOutOfBounds {
171 index,
172 count: self.columns.len(),
173 })?;
174 let ty = crate::types::Type::from_oid(field.type_oid).unwrap_or_else(|| {
175 crate::types::Type::new(
176 "unknown".into(),
177 0,
178 crate::types::Kind::Pseudo,
179 "pg_catalog".into(),
180 )
181 });
182 let format = if field.format() == 1 {
183 Format::Binary
184 } else {
185 Format::Text
186 };
187 T::from_sql(&ty, raw, format).map_err(PgError::TypeConversion)
188 }
189
190 #[must_use = "column access errors should be checked"]
201 pub fn get_by_name<T: FromSql>(&self, name: &str) -> Result<T> {
202 let index = self
203 .columns
204 .iter()
205 .position(|c| c.name() == name)
206 .ok_or_else(|| PgError::ColumnNotFound {
207 name: name.to_string(),
208 })?;
209 self.get(index)
210 }
211
212 pub fn column_name(&self, index: usize) -> Option<&str> {
216 self.columns.get(index).map(|c| c.name())
217 }
218
219 pub fn column_index(&self, name: &str) -> Option<usize> {
224 self.columns.iter().position(|c| c.name() == name)
225 }
226}
227
228#[cfg(test)]
233mod tests {
234 use super::*;
235
236 #[test]
237 fn test_row_get_raw() {
238 let cols = Arc::new(vec![FieldDescription::new(
239 "id".into(),
240 0,
241 0,
242 crate::types::INT4_OID,
243 4,
244 -1,
245 0,
246 )]);
247 let row = Row::new(cols.clone(), vec![Some(vec![b'1', b'2', b'3'])]);
248 assert_eq!(row.get_raw(0), Some(b"123".as_slice()));
249 assert!(!row.is_null(0));
250 }
251
252 #[test]
253 fn test_row_null() {
254 let cols = Arc::new(vec![FieldDescription::new(
255 "name".into(),
256 0,
257 0,
258 crate::types::TEXT_OID,
259 -1,
260 -1,
261 0,
262 )]);
263 let row = Row::new(cols, vec![None]);
264 assert!(row.is_null(0));
265 assert_eq!(row.get_raw(0), None);
266 }
267
268 #[test]
269 fn test_row_get_i32() {
270 let cols = Arc::new(vec![FieldDescription::new(
271 "id".into(),
272 0,
273 0,
274 crate::types::INT4_OID,
275 4,
276 -1,
277 0,
278 )]);
279 let row = Row::new(cols, vec![Some(b"42".to_vec())]);
280 let val: i32 = row.get(0).unwrap();
281 assert_eq!(val, 42);
282 }
283
284 #[test]
285 fn test_row_get_by_name() {
286 let cols = Arc::new(vec![
287 FieldDescription::new("id".into(), 0, 0, crate::types::INT4_OID, 4, -1, 0),
288 FieldDescription::new("name".into(), 0, 0, crate::types::TEXT_OID, -1, -1, 0),
289 ]);
290 let row = Row::new(cols, vec![Some(b"1".to_vec()), Some(b"alice".to_vec())]);
291 let id: i32 = row.get_by_name("id").unwrap();
292 assert_eq!(id, 1);
293 let name: String = row.get_by_name("name").unwrap();
294 assert_eq!(name, "alice");
295 }
296
297 #[test]
298 fn test_row_get_by_name_missing() {
299 let cols = Arc::new(vec![]);
300 let row = Row::new(cols, vec![]);
301 assert!(row.get_by_name::<i32>("missing").is_err());
302 }
303}