2
2
3
3
use crate :: row:: sealed:: { AsName , Sealed } ;
4
4
use crate :: simple_query:: SimpleColumn ;
5
- use crate :: statement:: Column ;
5
+ use crate :: statement:: { Column , RowDescription } ;
6
6
use crate :: types:: { FromSql , Type , WrongType } ;
7
- use crate :: { Error , Statement } ;
7
+ use crate :: Error ;
8
+ use bytes:: { BufMut , BytesMut } ;
8
9
use fallible_iterator:: FallibleIterator ;
9
10
use postgres_protocol:: message:: backend:: DataRowBody ;
11
+ use postgres_types:: { IsNull , ToSql } ;
10
12
use std:: fmt;
11
13
use std:: ops:: Range ;
12
14
use std:: str;
96
98
97
99
/// A row of data returned from the database by a query.
98
100
pub struct Row {
99
- statement : Statement ,
101
+ description : Arc < dyn RowDescription > ,
100
102
body : DataRowBody ,
101
103
ranges : Vec < Option < Range < usize > > > ,
102
104
}
@@ -110,18 +112,26 @@ impl fmt::Debug for Row {
110
112
}
111
113
112
114
impl 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 > {
114
119
let ranges = body. ranges ( ) . collect ( ) . map_err ( Error :: parse) ?;
115
120
Ok ( Row {
116
- statement ,
121
+ description ,
117
122
body,
118
123
ranges,
119
124
} )
120
125
}
121
126
127
+ /// Returns description about the data in the row.
128
+ pub fn description ( & self ) -> Arc < dyn RowDescription > {
129
+ self . description . clone ( )
130
+ }
131
+
122
132
/// Returns information about the columns of data in the row.
123
133
pub fn columns ( & self ) -> & [ Column ] {
124
- self . statement . columns ( )
134
+ self . description . columns ( )
125
135
}
126
136
127
137
/// Determines if the row contains no values.
@@ -270,3 +280,111 @@ impl SimpleQueryRow {
270
280
FromSql :: from_sql_nullable ( & Type :: TEXT , buf) . map_err ( |e| Error :: from_sql ( e, idx) )
271
281
}
272
282
}
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