55
55
#![ warn( missing_docs) ]
56
56
57
57
extern crate fallible_iterator;
58
- extern crate futures;
59
58
extern crate futures_state_stream;
60
59
extern crate postgres_shared;
61
60
extern crate postgres_protocol;
62
61
extern crate tokio_core;
63
62
extern crate tokio_dns;
64
63
64
+ #[ macro_use]
65
+ extern crate futures;
66
+
65
67
#[ cfg( unix) ]
66
68
extern crate tokio_uds;
67
69
@@ -71,23 +73,24 @@ extern crate tokio_openssl;
71
73
extern crate openssl;
72
74
73
75
use fallible_iterator:: FallibleIterator ;
74
- use futures:: { Future , IntoFuture , BoxFuture , Stream , Sink , Poll , StartSend } ;
76
+ use futures:: { Future , IntoFuture , BoxFuture , Stream , Sink , Poll , StartSend , Async } ;
75
77
use futures:: future:: Either ;
76
78
use futures_state_stream:: { StreamEvent , StateStream , BoxStateStream , FutureExt } ;
77
79
use postgres_protocol:: authentication;
78
80
use postgres_protocol:: message:: { backend, frontend} ;
79
81
use postgres_protocol:: message:: backend:: { ErrorResponseBody , ErrorFields } ;
80
82
use postgres_shared:: rows:: RowData ;
81
- use std:: collections:: HashMap ;
83
+ use std:: collections:: { HashMap , VecDeque } ;
82
84
use std:: fmt;
83
85
use std:: io;
84
86
use std:: sync:: Arc ;
85
87
use std:: sync:: atomic:: { AtomicUsize , ATOMIC_USIZE_INIT , Ordering } ;
86
88
use std:: sync:: mpsc:: { self , Sender , Receiver } ;
89
+ use tokio_core:: io:: IoFuture ;
87
90
use tokio_core:: reactor:: Handle ;
88
91
89
92
#[ doc( inline) ]
90
- pub use postgres_shared:: { params, CancelData } ;
93
+ pub use postgres_shared:: { params, CancelData , Notification } ;
91
94
92
95
use error:: { ConnectError , Error , DbError , SqlState } ;
93
96
use params:: { ConnectParams , IntoConnectParams } ;
@@ -166,32 +169,35 @@ struct InnerConnection {
166
169
close_sender : Sender < ( u8 , String ) > ,
167
170
parameters : HashMap < String , String > ,
168
171
types : HashMap < Oid , Other > ,
172
+ notifications : VecDeque < Notification > ,
169
173
cancel_data : CancelData ,
170
174
has_typeinfo_query : bool ,
171
175
has_typeinfo_enum_query : bool ,
172
176
has_typeinfo_composite_query : bool ,
173
177
}
174
178
175
179
impl InnerConnection {
176
- fn read ( self ) -> BoxFuture < ( backend:: Message < Vec < u8 > > , InnerConnection ) , io :: Error > {
180
+ fn read ( self ) -> IoFuture < ( backend:: Message < Vec < u8 > > , InnerConnection ) > {
177
181
self . into_future ( )
178
182
. map_err ( |e| e. 0 )
179
183
. and_then ( |( m, mut s) | {
180
184
match m {
181
- Some ( backend:: Message :: ParameterStatus ( body) ) => {
182
- let name = match body. name ( ) {
183
- Ok ( name) => name. to_owned ( ) ,
185
+ Some ( backend:: Message :: NotificationResponse ( body) ) => {
186
+ let process_id = body. process_id ( ) ;
187
+ let channel = match body. channel ( ) {
188
+ Ok ( channel) => channel. to_owned ( ) ,
184
189
Err ( e) => return Either :: A ( Err ( e) . into_future ( ) ) ,
185
190
} ;
186
- let value = match body. value ( ) {
187
- Ok ( value ) => value . to_owned ( ) ,
191
+ let message = match body. message ( ) {
192
+ Ok ( channel ) => channel . to_owned ( ) ,
188
193
Err ( e) => return Either :: A ( Err ( e) . into_future ( ) ) ,
189
194
} ;
190
- s. parameters . insert ( name, value) ;
191
- Either :: B ( s. read ( ) )
192
- }
193
- Some ( backend:: Message :: NoticeResponse ( _) ) => {
194
- // TODO forward the error
195
+ let notification = Notification {
196
+ process_id : process_id,
197
+ channel : channel,
198
+ payload : message,
199
+ } ;
200
+ s. notifications . push_back ( notification) ;
195
201
Either :: B ( s. read ( ) )
196
202
}
197
203
Some ( m) => Either :: A ( Ok ( ( m, s) ) . into_future ( ) ) ,
@@ -210,7 +216,18 @@ impl Stream for InnerConnection {
210
216
type Error = io:: Error ;
211
217
212
218
fn poll ( & mut self ) -> Poll < Option < backend:: Message < Vec < u8 > > > , io:: Error > {
213
- self . stream . poll ( )
219
+ loop {
220
+ match try_ready ! ( self . stream. poll( ) ) {
221
+ Some ( backend:: Message :: ParameterStatus ( body) ) => {
222
+ let name = body. name ( ) ?. to_owned ( ) ;
223
+ let value = body. value ( ) ?. to_owned ( ) ;
224
+ self . parameters . insert ( name, value) ;
225
+ }
226
+ // TODO forward to a handler
227
+ Some ( backend:: Message :: NoticeResponse ( _) ) => { }
228
+ msg => return Ok ( Async :: Ready ( msg) ) ,
229
+ }
230
+ }
214
231
}
215
232
}
216
233
@@ -279,6 +296,7 @@ impl Connection {
279
296
close_receiver : receiver,
280
297
parameters : HashMap :: new ( ) ,
281
298
types : HashMap :: new ( ) ,
299
+ notifications : VecDeque :: new ( ) ,
282
300
cancel_data : CancelData {
283
301
process_id : 0 ,
284
302
secret_key : 0 ,
@@ -1000,6 +1018,11 @@ impl Connection {
1000
1018
. boxed ( )
1001
1019
}
1002
1020
1021
+ /// Returns a stream of asynchronus notifications receieved from the server.
1022
+ pub fn notifications ( self ) -> Notifications {
1023
+ Notifications ( self )
1024
+ }
1025
+
1003
1026
/// Returns information used to cancel pending queries.
1004
1027
///
1005
1028
/// Used with the `cancel_query` function. The object returned can be used
@@ -1015,6 +1038,41 @@ impl Connection {
1015
1038
}
1016
1039
}
1017
1040
1041
+ /// A stream of asynchronous Postgres notifications.
1042
+ pub struct Notifications ( Connection ) ;
1043
+
1044
+ impl Notifications {
1045
+ /// Consumes the `Notifications`, returning the inner `Connection`.
1046
+ pub fn into_inner ( self ) -> Connection {
1047
+ self . 0
1048
+ }
1049
+ }
1050
+
1051
+ impl Stream for Notifications {
1052
+ type Item = Notification ;
1053
+
1054
+ type Error = Error ;
1055
+
1056
+ fn poll ( & mut self ) -> Poll < Option < Notification > , Error > {
1057
+ if let Some ( notification) = ( self . 0 ) . 0 . notifications . pop_front ( ) {
1058
+ return Ok ( Async :: Ready ( Some ( notification) ) ) ;
1059
+ }
1060
+
1061
+ match try_ready ! ( ( self . 0 ) . 0 . poll( ) ) {
1062
+ Some ( backend:: Message :: NotificationResponse ( body) ) => {
1063
+ let notification = Notification {
1064
+ process_id : body. process_id ( ) ,
1065
+ channel : body. channel ( ) ?. to_owned ( ) ,
1066
+ payload : body. message ( ) ?. to_owned ( ) ,
1067
+ } ;
1068
+ Ok ( Async :: Ready ( Some ( notification) ) )
1069
+ }
1070
+ Some ( _) => Err ( bad_message ( ) ) ,
1071
+ None => Ok ( Async :: Ready ( None ) ) ,
1072
+ }
1073
+ }
1074
+ }
1075
+
1018
1076
fn connect_err ( fields : & mut ErrorFields ) -> ConnectError {
1019
1077
match DbError :: new ( fields) {
1020
1078
Ok ( err) => ConnectError :: Db ( Box :: new ( err) ) ,
0 commit comments