1- use std:: io:: prelude:: * ;
2- use std:: error;
3- use byteorder:: { ReadBytesExt , WriteBytesExt , BigEndian } ;
4-
5- use postgres:: { self , Result } ;
6- use postgres:: error:: Error ;
7- use postgres:: types:: { Type , Kind , ToSql , FromSql , Oid , IsNull , SessionInfo } ;
1+ use fallible_iterator:: FallibleIterator ;
2+ use postgres:: types:: { Type , Kind , ToSql , FromSql , IsNull , SessionInfo } ;
3+ use postgres_protocol:: types;
4+ use postgres_protocol;
5+ use std:: error:: Error ;
86
97use { Array , Dimension } ;
108
119impl < T > FromSql for Array < T >
1210 where T : FromSql
1311{
14- fn from_sql < R : Read > ( ty : & Type , raw : & mut R , info : & SessionInfo ) -> postgres :: Result < Array < T > > {
15- let element_type = match ty. kind ( ) {
16- & Kind :: Array ( ref ty) => ty,
17- _ => panic ! ( "unexpected type {:?}" , ty ) ,
12+ fn from_sql ( ty : & Type , raw : & [ u8 ] , info : & SessionInfo ) -> Result < Array < T > , Box < Error + Sync + Send > > {
13+ let element_type = match * ty. kind ( ) {
14+ Kind :: Array ( ref ty) => ty,
15+ _ => unreachable ! ( ) ,
1816 } ;
1917
20- let ndim = try!( raw. read_u32 :: < BigEndian > ( ) ) as usize ;
21- let _has_null = try!( raw. read_i32 :: < BigEndian > ( ) ) == 1 ;
22- let _element_type: Oid = try!( raw. read_u32 :: < BigEndian > ( ) ) ;
18+ let array = try!( types:: array_from_sql ( raw) ) ;
2319
24- let mut dim_info = Vec :: with_capacity ( ndim) ;
25- for _ in 0 ..ndim {
26- dim_info. push ( Dimension {
27- len : try!( raw. read_u32 :: < BigEndian > ( ) ) as usize ,
28- lower_bound : try!( raw. read_i32 :: < BigEndian > ( ) ) as isize ,
29- } ) ;
30- }
31- let nele = if dim_info. len ( ) == 0 {
32- 0
33- } else {
34- dim_info. iter ( ) . fold ( 1 , |product, info| product * info. len )
35- } ;
20+ let dimensions = try!( array. dimensions ( )
21+ . map ( |d| {
22+ Dimension { len : d. len , lower_bound : d. lower_bound }
23+ } )
24+ . collect ( ) ) ;
3625
37- let mut elements = Vec :: with_capacity ( nele) ;
38- for _ in 0 ..nele {
39- let len = try!( raw. read_i32 :: < BigEndian > ( ) ) ;
40- if len < 0 {
41- elements. push ( try!( FromSql :: from_sql_null ( & element_type, info) ) ) ;
42- } else {
43- let mut limit = raw. take ( len as u64 ) ;
44- elements. push ( try!( FromSql :: from_sql ( & element_type, & mut limit, info) ) ) ;
45- if limit. limit ( ) != 0 {
46- let err: Box < error:: Error + Sync + Send > = "from_sql call did not consume all \
47- data"
48- . into ( ) ;
49- return Err ( Error :: Conversion ( err) ) ;
50- }
51- }
52- }
26+ let elements = try!( array. values ( )
27+ . and_then ( |v| FromSql :: from_sql_nullable ( element_type, v, info) )
28+ . collect ( ) ) ;
5329
54- Ok ( Array :: from_parts ( elements, dim_info ) )
30+ Ok ( Array :: from_parts ( elements, dimensions ) )
5531 }
5632
5733 fn accepts ( ty : & Type ) -> bool {
@@ -65,44 +41,34 @@ impl<T> FromSql for Array<T>
6541impl < T > ToSql for Array < T >
6642 where T : ToSql
6743{
68- fn to_sql < W : ?Sized + Write > ( & self ,
69- ty : & Type ,
70- mut w : & mut W ,
71- info : & SessionInfo )
72- -> postgres:: Result < IsNull > {
44+ fn to_sql ( & self , ty : & Type , w : & mut Vec < u8 > , info : & SessionInfo ) -> Result < IsNull , Box < Error + Sync + Send > > {
7345 let element_type = match ty. kind ( ) {
7446 & Kind :: Array ( ref ty) => ty,
75- _ => panic ! ( "unexpected type {:?}" , ty ) ,
47+ _ => unreachable ! ( ) ,
7648 } ;
7749
78- try!( w. write_i32 :: < BigEndian > ( try!( downcast ( self . dimensions ( ) . len ( ) ) ) ) ) ;
79- try!( w. write_i32 :: < BigEndian > ( 1 ) ) ;
80- try!( w. write_u32 :: < BigEndian > ( element_type. oid ( ) ) ) ;
81-
82- for info in self . dimensions ( ) {
83- try!( w. write_i32 :: < BigEndian > ( try!( downcast ( info. len ) ) ) ) ;
84-
85- let bound = if info. lower_bound > i32:: max_value ( ) as isize ||
86- info. lower_bound < i32:: min_value ( ) as isize {
87- let err: Box < error:: Error + Sync + Send > = "value too large to transmit" . into ( ) ;
88- return Err ( Error :: Conversion ( err) ) ;
89- } else {
90- info. lower_bound as i32
91- } ;
92- try!( w. write_i32 :: < BigEndian > ( bound) ) ;
93- }
94-
95- let mut inner_buf = vec ! [ ] ;
96- for v in self {
97- match try!( v. to_sql ( element_type, & mut inner_buf, info) ) {
98- IsNull :: Yes => try!( w. write_i32 :: < BigEndian > ( -1 ) ) ,
99- IsNull :: No => {
100- try!( w. write_i32 :: < BigEndian > ( try!( downcast ( inner_buf. len ( ) ) ) ) ) ;
101- try!( w. write_all ( & inner_buf) ) ;
50+ let dimensions = self . dimensions ( )
51+ . iter ( )
52+ . map ( |d| {
53+ types:: ArrayDimension {
54+ len : d. len ,
55+ lower_bound : d. lower_bound ,
10256 }
103- }
104- inner_buf. clear ( ) ;
105- }
57+ } ) ;
58+ let elements = self . iter ( ) ;
59+
60+ try!( types:: array_to_sql ( dimensions,
61+ true ,
62+ element_type. oid ( ) ,
63+ elements,
64+ |v, w| {
65+ match v. to_sql ( element_type, w, info) {
66+ Ok ( IsNull :: Yes ) => Ok ( postgres_protocol:: IsNull :: Yes ) ,
67+ Ok ( IsNull :: No ) => Ok ( postgres_protocol:: IsNull :: No ) ,
68+ Err ( e) => Err ( e) ,
69+ }
70+ } ,
71+ w) ) ;
10672
10773 Ok ( IsNull :: No )
10874 }
@@ -117,26 +83,17 @@ impl<T> ToSql for Array<T>
11783 to_sql_checked ! ( ) ;
11884}
11985
120- fn downcast ( len : usize ) -> Result < i32 > {
121- if len > i32:: max_value ( ) as usize {
122- let err: Box < error:: Error + Sync + Send > = "value too large to transmit" . into ( ) ;
123- Err ( Error :: Conversion ( err) )
124- } else {
125- Ok ( len as i32 )
126- }
127- }
128-
12986#[ cfg( test) ]
13087mod test {
13188 use std:: fmt;
13289
133- use postgres:: { Connection , SslMode } ;
90+ use postgres:: { Connection , TlsMode } ;
13491 use postgres:: types:: { FromSql , ToSql } ;
13592 use Array ;
13693
13794 fn test_type < T : PartialEq + FromSql + ToSql , S : fmt:: Display > ( sql_type : & str ,
13895 checks : & [ ( T , S ) ] ) {
139- let conn = Connection :: connect ( "postgres://postgres@localhost" , SslMode :: None ) . unwrap ( ) ;
96+ let conn = Connection :: connect ( "postgres://postgres@localhost" , TlsMode :: None ) . unwrap ( ) ;
14097 for & ( ref val, ref repr) in checks. iter ( ) {
14198 let stmt = conn. prepare ( & format ! ( "SELECT {}::{}" , * repr, sql_type) ) . unwrap ( ) ;
14299 let result = stmt. query ( & [ ] ) . unwrap ( ) . iter ( ) . next ( ) . unwrap ( ) . get ( 0 ) ;
@@ -256,7 +213,7 @@ mod test {
256213
257214 #[ test]
258215 fn test_empty_array ( ) {
259- let conn = Connection :: connect ( "postgres://postgres@localhost" , SslMode :: None ) . unwrap ( ) ;
216+ let conn = Connection :: connect ( "postgres://postgres@localhost" , TlsMode :: None ) . unwrap ( ) ;
260217 let stmt = conn. prepare ( "SELECT '{}'::INT4[]" ) . unwrap ( ) ;
261218 stmt. query ( & [ ] ) . unwrap ( ) . iter ( ) . next ( ) . unwrap ( ) . get :: < _ , Array < i32 > > ( 0 ) ;
262219 }
0 commit comments