1use super::{
13 PgConnection, PgError, PgResult, io::MAX_MESSAGE_SIZE, is_ignorable_session_message,
14 unexpected_backend_message,
15};
16use crate::protocol::PgEncoder;
17
18#[derive(Debug, Clone)]
20pub struct Notification {
21 pub process_id: i32,
23 pub channel: String,
25 pub payload: String,
27}
28
29#[inline]
30fn return_with_desync<T>(conn: &mut PgConnection, err: PgError) -> PgResult<T> {
31 if matches!(
32 err,
33 PgError::Protocol(_) | PgError::Connection(_) | PgError::Timeout(_)
34 ) {
35 conn.mark_io_desynced();
36 }
37 Err(err)
38}
39
40impl PgConnection {
41 pub async fn listen(&mut self, channel: &str) -> PgResult<()> {
47 let sql = format!("LISTEN \"{}\"", channel.replace('"', "\"\""));
49 self.execute_simple(&sql).await
50 }
51
52 pub async fn unlisten(&mut self, channel: &str) -> PgResult<()> {
54 let sql = format!("UNLISTEN \"{}\"", channel.replace('"', "\"\""));
55 self.execute_simple(&sql).await
56 }
57
58 pub async fn unlisten_all(&mut self) -> PgResult<()> {
60 self.execute_simple("UNLISTEN *").await
61 }
62
63 pub fn poll_notifications(&mut self) -> Vec<Notification> {
69 self.notifications.drain(..).collect()
70 }
71
72 pub async fn recv_notification(&mut self) -> PgResult<Notification> {
80 use crate::protocol::BackendMessage;
81
82 if let Some(n) = self.notifications.pop_front() {
84 return Ok(n);
85 }
86
87 let bytes = PgEncoder::try_encode_query_string("")?;
89 self.send_bytes(&bytes).await?;
90
91 let mut got_ready = false;
94 loop {
95 if self.buffer.len() >= 5 {
97 let msg_len = u32::from_be_bytes([
98 self.buffer[1],
99 self.buffer[2],
100 self.buffer[3],
101 self.buffer[4],
102 ]) as usize;
103
104 if msg_len < 4 {
105 return return_with_desync(
106 self,
107 PgError::Protocol(format!(
108 "Invalid message length: {} (minimum 4)",
109 msg_len
110 )),
111 );
112 }
113
114 if msg_len > MAX_MESSAGE_SIZE {
115 return return_with_desync(
116 self,
117 PgError::Protocol(format!(
118 "Message too large: {} bytes (max {})",
119 msg_len, MAX_MESSAGE_SIZE
120 )),
121 );
122 }
123
124 if self.buffer.len() > msg_len {
125 let msg_bytes = self.buffer.split_to(msg_len + 1);
126 let (msg, _) = match BackendMessage::decode(&msg_bytes) {
127 Ok(decoded) => decoded,
128 Err(err) => return return_with_desync(self, PgError::Protocol(err)),
129 };
130
131 match msg {
132 BackendMessage::NotificationResponse {
133 process_id,
134 channel,
135 payload,
136 } => {
137 let notification = Notification {
138 process_id,
139 channel,
140 payload,
141 };
142 if got_ready {
143 return Ok(notification);
144 }
145 self.notifications.push_back(notification);
146 continue;
147 }
148 BackendMessage::EmptyQueryResponse => continue,
149 BackendMessage::NoticeResponse(_) => continue,
150 BackendMessage::ParameterStatus { .. } => continue,
151 BackendMessage::CommandComplete(_) => continue,
152 BackendMessage::ReadyForQuery(_) => {
153 got_ready = true;
154 if let Some(n) = self.notifications.pop_front() {
156 return Ok(n);
157 }
158 continue;
159 }
160 BackendMessage::ErrorResponse(err) => {
161 self.mark_io_desynced();
162 return Err(PgError::QueryServer(err.into()));
163 }
164 msg if is_ignorable_session_message(&msg) => continue,
165 other => {
166 return return_with_desync(
167 self,
168 unexpected_backend_message("listen/notify wait", &other),
169 );
170 }
171 }
172 }
173 }
174
175 if self.buffer.capacity() - self.buffer.len() < 65536 {
178 self.buffer.reserve(131072);
179 }
180
181 if got_ready {
182 let n = if self.buffer.is_empty() {
186 self.read_without_timeout().await?
187 } else {
188 self.read_with_timeout().await?
189 };
190 if n == 0 {
191 return return_with_desync(
192 self,
193 PgError::Connection("Connection closed".to_string()),
194 );
195 }
196 } else {
197 let n = self.read_with_timeout().await?;
200 if n == 0 {
201 return return_with_desync(
202 self,
203 PgError::Connection("Connection closed".to_string()),
204 );
205 }
206 }
207 }
208 }
209}
210
211#[cfg(test)]
212mod tests {
213 use super::return_with_desync;
214 use crate::driver::{PgConnection, PgError};
215
216 #[cfg(unix)]
217 fn test_conn_with_peer() -> (PgConnection, tokio::net::UnixStream) {
218 use crate::driver::connection::StatementCache;
219 use crate::driver::stream::PgStream;
220 use bytes::BytesMut;
221 use std::collections::{HashMap, VecDeque};
222 use std::num::NonZeroUsize;
223 use tokio::net::UnixStream;
224
225 let (unix_stream, peer) = UnixStream::pair().expect("unix stream pair");
226 (
227 PgConnection {
228 stream: PgStream::Unix(unix_stream),
229 buffer: BytesMut::with_capacity(1024),
230 write_buf: BytesMut::with_capacity(1024),
231 sql_buf: BytesMut::with_capacity(256),
232 params_buf: Vec::new(),
233 prepared_statements: HashMap::new(),
234 stmt_cache: StatementCache::new(NonZeroUsize::new(2).expect("non-zero")),
235 column_info_cache: HashMap::new(),
236 process_id: 0,
237 cancel_key_bytes: Vec::new(),
238 requested_protocol_minor: PgConnection::default_protocol_minor(),
239 negotiated_protocol_minor: PgConnection::default_protocol_minor(),
240 notifications: VecDeque::new(),
241 replication_stream_active: false,
242 replication_mode_enabled: false,
243 last_replication_wal_end: None,
244 io_desynced: false,
245 pending_statement_closes: Vec::new(),
246 draining_statement_closes: false,
247 },
248 peer,
249 )
250 }
251
252 #[cfg(unix)]
253 fn test_conn() -> PgConnection {
254 test_conn_with_peer().0
255 }
256
257 #[cfg(unix)]
258 fn push_backend_frame(conn: &mut PgConnection, msg_type: u8, payload: &[u8]) {
259 conn.buffer.extend_from_slice(&[msg_type]);
260 conn.buffer
261 .extend_from_slice(&((payload.len() + 4) as u32).to_be_bytes());
262 conn.buffer.extend_from_slice(payload);
263 }
264
265 #[cfg(unix)]
266 fn notification_payload(process_id: i32, channel: &str, payload: &str) -> Vec<u8> {
267 let mut bytes = Vec::new();
268 bytes.extend_from_slice(&process_id.to_be_bytes());
269 bytes.extend_from_slice(channel.as_bytes());
270 bytes.push(0);
271 bytes.extend_from_slice(payload.as_bytes());
272 bytes.push(0);
273 bytes
274 }
275
276 fn error_response_payload(code: &str, message: &str) -> Vec<u8> {
277 let mut payload = Vec::new();
278 payload.push(b'S');
279 payload.extend_from_slice(b"ERROR\0");
280 payload.push(b'C');
281 payload.extend_from_slice(code.as_bytes());
282 payload.push(0);
283 payload.push(b'M');
284 payload.extend_from_slice(message.as_bytes());
285 payload.push(0);
286 payload.push(0);
287 payload
288 }
289
290 #[cfg(unix)]
291 #[tokio::test]
292 async fn notification_return_with_desync_marks_protocol_error() {
293 let mut conn = test_conn();
294
295 let err =
296 return_with_desync::<()>(&mut conn, PgError::Protocol("bad notify frame".to_string()))
297 .expect_err("protocol error must be returned");
298
299 assert!(err.to_string().contains("bad notify frame"));
300 assert!(conn.is_io_desynced());
301 }
302
303 #[cfg(unix)]
304 #[tokio::test]
305 async fn recv_notification_drains_empty_query_before_returning_pre_ready_notify() {
306 let (mut conn, _peer) = test_conn_with_peer();
307 let payload = notification_payload(42, "jobs", "ready");
308
309 push_backend_frame(&mut conn, b'A', &payload);
310 push_backend_frame(&mut conn, b'I', &[]);
311 push_backend_frame(&mut conn, b'Z', b"I");
312
313 let notification = conn
314 .recv_notification()
315 .await
316 .expect("pre-ready notification should be returned after flush drain");
317
318 assert_eq!(notification.process_id, 42);
319 assert_eq!(notification.channel, "jobs");
320 assert_eq!(notification.payload, "ready");
321 assert!(
322 conn.buffer.is_empty(),
323 "empty-query flush frames must not remain buffered"
324 );
325 assert!(!conn.is_io_desynced());
326 }
327
328 #[cfg(unix)]
329 #[tokio::test]
330 async fn recv_notification_error_response_marks_connection_desynced() {
331 let (mut conn, _peer) = test_conn_with_peer();
332 let payload = error_response_payload("XX000", "notify wait failed");
333 push_backend_frame(&mut conn, b'E', &payload);
334
335 let err = conn
336 .recv_notification()
337 .await
338 .expect_err("server error must fail");
339
340 assert!(matches!(err, PgError::QueryServer(_)));
341 assert!(conn.is_io_desynced());
342 }
343}