22
33use crate :: row:: sealed:: { AsName , Sealed } ;
44use crate :: simple_query:: SimpleColumn ;
5- use crate :: statement:: Column ;
5+ use crate :: statement:: { Column , RowDescription } ;
66use crate :: types:: { FromSql , Type , WrongType } ;
7- use crate :: { Error , Statement } ;
7+ use crate :: Error ;
8+ use bytes:: { BufMut , BytesMut } ;
89use fallible_iterator:: FallibleIterator ;
910use postgres_protocol:: message:: backend:: DataRowBody ;
11+ use postgres_types:: { IsNull , ToSql } ;
1012use std:: fmt;
1113use std:: ops:: Range ;
1214use std:: str;
9698
9799/// A row of data returned from the database by a query.
98100pub struct Row {
99- statement : Statement ,
101+ description : Arc < dyn RowDescription > ,
100102 body : DataRowBody ,
101103 ranges : Vec < Option < Range < usize > > > ,
102104}
@@ -110,18 +112,26 @@ impl fmt::Debug for Row {
110112}
111113
112114impl Row {
113- pub ( crate ) fn new ( statement : Statement , body : DataRowBody ) -> Result < Row , Error > {
115+ pub ( crate ) fn new (
116+ description : Arc < dyn RowDescription > ,
117+ body : DataRowBody ,
118+ ) -> Result < Row , Error > {
114119 let ranges = body. ranges ( ) . collect ( ) . map_err ( Error :: parse) ?;
115120 Ok ( Row {
116- statement ,
121+ description ,
117122 body,
118123 ranges,
119124 } )
120125 }
121126
127+ /// Returns description about the data in the row.
128+ pub fn description ( & self ) -> Arc < dyn RowDescription > {
129+ self . description . clone ( )
130+ }
131+
122132 /// Returns information about the columns of data in the row.
123133 pub fn columns ( & self ) -> & [ Column ] {
124- self . statement . columns ( )
134+ self . description . columns ( )
125135 }
126136
127137 /// Determines if the row contains no values.
@@ -270,3 +280,111 @@ impl SimpleQueryRow {
270280 FromSql :: from_sql_nullable ( & Type :: TEXT , buf) . map_err ( |e| Error :: from_sql ( e, idx) )
271281 }
272282}
283+ /// Builder for building a [`Row`].
284+ pub struct RowBuilder {
285+ desc : Arc < dyn RowDescription > ,
286+ buf : BytesMut ,
287+ n : usize ,
288+ }
289+
290+ impl RowBuilder {
291+ /// Creates a new builder using the provided row description.
292+ pub fn new ( desc : Arc < dyn RowDescription > ) -> Self {
293+ Self {
294+ desc,
295+ buf : BytesMut :: new ( ) ,
296+ n : 0 ,
297+ }
298+ }
299+
300+ /// Appends a column's value and returns a value indicates if this value should be represented
301+ /// as NULL.
302+ pub fn push ( & mut self , value : Option < impl ToSql > ) -> Result < IsNull , Error > {
303+ let columns = self . desc . columns ( ) ;
304+
305+ if columns. len ( ) == self . n {
306+ return Err ( Error :: column (
307+ "exceeded expected number of columns" . to_string ( ) ,
308+ ) ) ;
309+ }
310+
311+ let db_type = columns[ self . n ] . type_ ( ) ;
312+ let start = self . buf . len ( ) ;
313+
314+ // Reserve 4 bytes for the length of the binary data to be written
315+ self . buf . put_i32 ( -1i32 ) ;
316+
317+ let is_null = value
318+ . to_sql ( db_type, & mut self . buf )
319+ . map_err ( |e| Error :: to_sql ( e, self . n ) ) ?;
320+
321+ // Calculate the length of data just written.
322+ if is_null == IsNull :: No {
323+ let len = ( self . buf . len ( ) - start - 4 ) as i32 ;
324+ // Update the length of data
325+ self . buf [ start..start + 4 ] . copy_from_slice ( & len. to_be_bytes ( ) ) ;
326+ } ;
327+
328+ self . n += 1 ;
329+ Ok ( is_null)
330+ }
331+
332+ /// Builds the row.
333+ pub fn build ( self ) -> Result < Row , Error > {
334+ Row :: new (
335+ self . desc . clone ( ) ,
336+ DataRowBody :: new ( self . buf . freeze ( ) , self . n as u16 ) ,
337+ )
338+ }
339+ }
340+
341+ #[ cfg( test) ]
342+ mod tests {
343+ use postgres_types:: IsNull ;
344+
345+ use super :: * ;
346+ use std:: net:: IpAddr ;
347+
348+ struct TestRowDescription {
349+ columns : Vec < Column > ,
350+ }
351+
352+ impl RowDescription for TestRowDescription {
353+ fn columns ( & self ) -> & [ Column ] {
354+ & self . columns
355+ }
356+ }
357+
358+ #[ test]
359+ fn test_row_builder ( ) {
360+ let mut builder = RowBuilder :: new ( Arc :: new ( TestRowDescription {
361+ columns : vec ! [
362+ Column :: new( "id" . to_string( ) , Type :: INT8 ) ,
363+ Column :: new( "name" . to_string( ) , Type :: VARCHAR ) ,
364+ Column :: new( "ip" . to_string( ) , Type :: INET ) ,
365+ ] ,
366+ } ) ) ;
367+
368+ let expected_id = 1234i64 ;
369+ let is_null = builder. push ( Some ( expected_id) ) . unwrap ( ) ;
370+ assert_eq ! ( IsNull :: No , is_null) ;
371+
372+ let expected_name = "row builder" ;
373+ let is_null = builder. push ( Some ( expected_name) ) . unwrap ( ) ;
374+ assert_eq ! ( IsNull :: No , is_null) ;
375+
376+ let is_null = builder. push ( None :: < IpAddr > ) . unwrap ( ) ;
377+ assert_eq ! ( IsNull :: Yes , is_null) ;
378+
379+ let row = builder. build ( ) . unwrap ( ) ;
380+
381+ let actual_id: i64 = row. try_get ( "id" ) . unwrap ( ) ;
382+ assert_eq ! ( expected_id, actual_id) ;
383+
384+ let actual_name: String = row. try_get ( "name" ) . unwrap ( ) ;
385+ assert_eq ! ( expected_name, actual_name) ;
386+
387+ let actual_dt: Option < IpAddr > = row. try_get ( "ip" ) . unwrap ( ) ;
388+ assert_eq ! ( None , actual_dt) ;
389+ }
390+ }
0 commit comments