1use std::time::Duration;
23
24use crate::protocol::{BackendMessage, TransactionStatus};
25
26use crate::connection::{Connection, ConnectionState};
27use crate::error::{PgError, Result};
28
29#[cfg(feature = "tracing")]
30use crate::tracing_ext::TARGET_NOTIFICATION;
31
32#[derive(Debug, Clone, PartialEq, Eq)]
48#[non_exhaustive]
49pub struct Notification {
50 pub process_id: i32,
52 pub channel: String,
54 pub payload: String,
56}
57
58impl Connection {
63 #[must_use = "listen errors should be checked"]
74 pub async fn listen(&mut self, channel: &str) -> Result<()> {
75 let sql = Self::build_listen_sql(channel);
76 self.execute(&sql).await?;
77 self.session_state.track_listen(channel);
78 #[cfg(feature = "tracing")]
79 tracing::info!(target: TARGET_NOTIFICATION, channel = %channel, "LISTEN: subscribed to channel");
80 Ok(())
81 }
82
83 #[must_use = "unlisten errors should be checked"]
87 pub async fn unlisten(&mut self, channel: &str) -> Result<()> {
88 let sql = Self::build_unlisten_sql(channel);
89 self.execute(&sql).await?;
90 self.session_state.track_unlisten(channel);
91 Ok(())
92 }
93
94 #[must_use = "unlisten errors should be checked"]
98 pub async fn unlisten_all(&mut self) -> Result<()> {
99 self.execute("UNLISTEN *").await?;
100 self.session_state.clear_listen_channels();
101 Ok(())
102 }
103
104 #[must_use = "notify errors should be checked"]
114 pub async fn notify(&mut self, channel: &str, payload: &str) -> Result<()> {
115 #[cfg(feature = "tracing")]
116 tracing::debug!(target: TARGET_NOTIFICATION, channel = %channel, payload_len = payload.len(), "NOTIFY: sending notification");
117 self.execute_params("SELECT pg_notify($1, $2)", &[&channel, &payload])
118 .await?;
119 Ok(())
120 }
121
122 pub fn notifications(&mut self) -> Vec<Notification> {
130 self.notification_queue.drain(..).collect()
131 }
132
133 #[allow(dead_code)]
134 async fn read_next_notification_blocking(&mut self) -> Result<Notification> {
135 if !self.is_idle() {
136 return Err(PgError::InvalidState(
137 "connection must be idle while waiting for notifications".into(),
138 ));
139 }
140
141 loop {
142 let msg = self.codec.read_message(&mut self.transport).await?;
143 match msg {
144 BackendMessage::NotificationResponse(body) => {
145 return Ok(Notification {
146 process_id: body.process_id(),
147 channel: body.channel().unwrap_or("").to_string(),
148 payload: body.message().unwrap_or("").to_string(),
149 });
150 }
151 BackendMessage::NoticeResponse(body) => {
152 if let Ok(notice) = crate::query::Notice::from_fields(&body) {
153 self.handle_notice(¬ice);
154 }
155 }
156 BackendMessage::ParameterStatus(body) => {
157 if let (Ok(name), Ok(value)) = (body.name(), body.value()) {
158 self.server_params
159 .params
160 .insert(name.to_string(), value.to_string());
161 }
162 }
163 BackendMessage::ReadyForQuery(body) => {
164 self.transaction_status = TransactionStatus::from_u8(body.status())
165 .unwrap_or(TransactionStatus::Idle);
166 self.state = ConnectionState::Idle;
167 }
168 BackendMessage::EmptyQueryResponse => {}
169 _ => {
170 return Err(PgError::InvalidState(
171 "received unexpected backend message while waiting for notification".into(),
172 ));
173 }
174 }
175 }
176 }
177
178 #[must_use = "notification errors should be checked"]
197 pub async fn wait_for_notification(
198 &mut self,
199 timeout: Option<Duration>,
200 ) -> Result<Option<Notification>> {
201 if let Some(n) = self.notification_queue.pop_front() {
203 return Ok(Some(n));
204 }
205
206 if let Some(d) = timeout {
208 if d.is_zero() {
209 return Ok(None);
210 }
211 }
212
213 #[cfg(all(not(target_arch = "wasm32"), feature = "tokio-transport"))]
214 {
215 match timeout {
216 Some(deadline) => {
217 match tokio::time::timeout(deadline, self.read_next_notification_blocking())
218 .await
219 {
220 Ok(result) => result.map(Some),
221 Err(_) => Ok(None),
222 }
223 }
224 None => self.read_next_notification_blocking().await.map(Some),
225 }
226 }
227
228 #[cfg(not(all(not(target_arch = "wasm32"), feature = "tokio-transport")))]
229 {
230 let _ = timeout;
232
233 self.transition(ConnectionState::ActiveSimpleQuery)?;
237
238 self.codec
239 .send(
240 &mut self.transport,
241 &crate::protocol::FrontendMessage::Query { sql: String::new() },
242 )
243 .await
244 .map_err(crate::error::Error::from)?;
245
246 loop {
248 let msg = self.codec.read_message(&mut self.transport).await?;
249 match msg {
250 BackendMessage::NotificationResponse(body) => {
251 let notification = Notification {
252 process_id: body.process_id(),
253 channel: body.channel().unwrap_or("").to_string(),
254 payload: body.message().unwrap_or("").to_string(),
255 };
256
257 self.read_until_ready().await?;
259
260 return Ok(Some(notification));
261 }
262 BackendMessage::EmptyQueryResponse => {}
263 BackendMessage::ReadyForQuery(body) => {
264 self.transaction_status = TransactionStatus::from_u8(body.status())
265 .unwrap_or(TransactionStatus::Idle);
266 self.state = ConnectionState::Idle;
267 break;
268 }
269 BackendMessage::NoticeResponse(body) => {
270 if let Ok(notice) = crate::query::Notice::from_fields(&body) {
271 self.handle_notice(¬ice);
272 }
273 }
274 BackendMessage::ParameterStatus(body) => {
275 if let (Ok(name), Ok(value)) = (body.name(), body.value()) {
276 self.server_params
277 .params
278 .insert(name.to_string(), value.to_string());
279 }
280 }
281 _ => {}
282 }
283 }
284
285 Ok(self.notification_queue.pop_front())
287 }
288 }
289
290 #[must_use = "notification errors should be checked"]
303 pub async fn wait_for_notification_with_timeout(
304 &mut self,
305 timeout: Duration,
306 ) -> Result<Option<Notification>> {
307 self.wait_for_notification(Some(timeout)).await
308 }
309}
310
311#[cfg(test)]
316mod tests {
317 use super::*;
318 use crate::auth::{Codec, ServerParams};
319 use crate::config::Config;
320 use crate::connection::ConnectionState;
321 use crate::protocol::TransactionStatus;
322 use crate::transport::{BufferedTransport, ClientTransport, MockTransport, PgTransport};
323 use std::collections::VecDeque;
324
325 fn make_connection(read_data: Vec<u8>) -> Connection {
326 let transport = PgTransport::Plain(BufferedTransport::new(ClientTransport::Mock(
327 MockTransport::new(read_data),
328 )));
329 Connection {
330 transport,
331 codec: Codec::new(),
332 server_params: ServerParams::default(),
333 state: ConnectionState::Idle,
334 config: Config::new(),
335 transaction_status: TransactionStatus::Idle,
336 notification_queue: VecDeque::new(),
337 notice_handler: None,
338 statement_counter: 0,
339 needs_recovery: false,
340 health: crate::reconnect::session::ConnectionHealth::new(),
341 session_state: crate::reconnect::session::SessionState::new(),
342 }
343 }
344
345 fn build_command_complete_msg(tag: &str) -> Vec<u8> {
346 let mut buf = vec![b'C'];
347 let mut body = Vec::new();
348 body.extend_from_slice(tag.as_bytes());
349 body.push(0);
350 let len = (body.len() + 4) as i32;
351 buf.extend_from_slice(&len.to_be_bytes());
352 buf.extend_from_slice(&body);
353 buf
354 }
355
356 fn build_ready_for_query(status: u8) -> Vec<u8> {
357 vec![b'Z', 0, 0, 0, 5, status]
358 }
359
360 fn build_notification_response(pid: i32, channel: &str, payload: &str) -> Vec<u8> {
361 let mut buf = vec![b'A'];
362 let mut body = Vec::new();
363 body.extend_from_slice(&pid.to_be_bytes());
364 body.extend_from_slice(channel.as_bytes());
365 body.push(0);
366 body.extend_from_slice(payload.as_bytes());
367 body.push(0);
368 let len = (body.len() + 4) as i32;
369 buf.extend_from_slice(&len.to_be_bytes());
370 buf.extend_from_slice(&body);
371 buf
372 }
373
374 fn build_row_description_msg(fields: &[(&str, u32)]) -> Vec<u8> {
375 let mut buf = vec![b'T'];
376 let mut body = Vec::new();
377 body.extend_from_slice(&(fields.len() as i16).to_be_bytes());
378 for (name, type_oid) in fields {
379 body.extend_from_slice(name.as_bytes());
380 body.push(0);
381 body.extend_from_slice(&0u32.to_be_bytes()); body.extend_from_slice(&0i16.to_be_bytes()); body.extend_from_slice(&type_oid.to_be_bytes()); body.extend_from_slice(&(-1i16).to_be_bytes()); body.extend_from_slice(&(-1i32).to_be_bytes()); body.extend_from_slice(&0i16.to_be_bytes()); }
388 let len = (body.len() + 4) as i32;
389 buf.extend_from_slice(&len.to_be_bytes());
390 buf.extend_from_slice(&body);
391 buf
392 }
393
394 fn build_data_row_msg(values: &[Option<&str>]) -> Vec<u8> {
395 let mut buf = vec![b'D'];
396 let mut body = Vec::new();
397 body.extend_from_slice(&(values.len() as i16).to_be_bytes());
398 for val in values {
399 match val {
400 Some(v) => {
401 let bytes = v.as_bytes();
402 body.extend_from_slice(&(bytes.len() as i32).to_be_bytes());
403 body.extend_from_slice(bytes);
404 }
405 None => {
406 body.extend_from_slice(&(-1i32).to_be_bytes());
407 }
408 }
409 }
410 let len = (body.len() + 4) as i32;
411 buf.extend_from_slice(&len.to_be_bytes());
412 buf.extend_from_slice(&body);
413 buf
414 }
415
416 #[tokio::test]
417 async fn test_listen() {
418 let mut data = Vec::new();
419 data.extend_from_slice(&build_command_complete_msg("LISTEN"));
420 data.extend_from_slice(&build_ready_for_query(b'I'));
421
422 let mut conn = make_connection(data);
423 conn.listen("my_channel").await.unwrap();
424 assert!(conn.is_idle());
425 assert!(conn
426 .session_state()
427 .listen_channels()
428 .contains("my_channel"));
429 }
430
431 #[tokio::test]
432 async fn test_unlisten() {
433 let mut data = Vec::new();
434 data.extend_from_slice(&build_command_complete_msg("UNLISTEN"));
435 data.extend_from_slice(&build_ready_for_query(b'I'));
436
437 let mut conn = make_connection(data);
438 conn.session_state.track_listen("my_channel");
439 conn.unlisten("my_channel").await.unwrap();
440 assert!(conn.is_idle());
441 assert!(!conn
442 .session_state()
443 .listen_channels()
444 .contains("my_channel"));
445 }
446
447 #[tokio::test]
448 async fn test_unlisten_all() {
449 let mut data = Vec::new();
450 data.extend_from_slice(&build_command_complete_msg("UNLISTEN"));
451 data.extend_from_slice(&build_ready_for_query(b'I'));
452
453 let mut conn = make_connection(data);
454 conn.session_state.track_listen("ch1");
455 conn.session_state.track_listen("ch2");
456 conn.unlisten_all().await.unwrap();
457 assert!(conn.is_idle());
458 assert!(conn.session_state().listen_channels().is_empty());
459 }
460
461 #[tokio::test]
462 async fn test_notify() {
463 let mut data = Vec::new();
464 data.extend_from_slice(&build_row_description_msg(&[(
466 "pg_notify",
467 crate::types::TEXT_OID,
468 )]));
469 data.extend_from_slice(&build_data_row_msg(&[Some("LISTEN")]));
470 data.extend_from_slice(&build_command_complete_msg("SELECT 1"));
471 data.extend_from_slice(&build_ready_for_query(b'I'));
472
473 let mut conn = make_connection(data);
474 conn.notify("my_channel", "hello").await.unwrap();
475 assert!(conn.is_idle());
476 }
477
478 #[tokio::test]
479 async fn test_notifications_buffered() {
480 let mut conn = make_connection(vec![]);
481
482 conn.notification_queue.push_back(Notification {
484 process_id: 1,
485 channel: "ch1".to_string(),
486 payload: "hello".to_string(),
487 });
488 conn.notification_queue.push_back(Notification {
489 process_id: 2,
490 channel: "ch2".to_string(),
491 payload: "world".to_string(),
492 });
493
494 let notifications = conn.notifications();
495 assert_eq!(notifications.len(), 2);
496 assert_eq!(notifications[0].channel, "ch1");
497 assert_eq!(notifications[1].channel, "ch2");
498
499 assert!(conn.notifications().is_empty());
501 }
502
503 #[tokio::test]
504 async fn test_wait_for_notification_from_queue() {
505 let mut conn = make_connection(vec![]);
506
507 conn.notification_queue.push_back(Notification {
509 process_id: 42,
510 channel: "test".to_string(),
511 payload: "payload".to_string(),
512 });
513
514 let n = conn.wait_for_notification(None).await.unwrap();
516 assert!(n.is_some());
517 let n = n.unwrap();
518 assert_eq!(n.process_id, 42);
519 assert_eq!(n.channel, "test");
520 assert_eq!(n.payload, "payload");
521 }
522
523 #[tokio::test]
524 async fn test_wait_for_notification_from_server() {
525 let mut data = Vec::new();
526 data.extend_from_slice(&[b'I', 0, 0, 0, 4]); data.extend_from_slice(&build_notification_response(99, "events", "user_login"));
530 data.extend_from_slice(&build_ready_for_query(b'I'));
532
533 let mut conn = make_connection(data);
534 let n = conn.wait_for_notification(None).await.unwrap();
535 assert!(n.is_some());
536 let n = n.unwrap();
537 assert_eq!(n.process_id, 99);
538 assert_eq!(n.channel, "events");
539 assert_eq!(n.payload, "user_login");
540 }
541
542 #[tokio::test]
543 async fn test_wait_for_notification_with_timeout_from_queue() {
544 let mut conn = make_connection(vec![]);
545
546 conn.notification_queue.push_back(Notification {
548 process_id: 7,
549 channel: "timeout_ch".to_string(),
550 payload: "timeout_payload".to_string(),
551 });
552
553 let n = conn
555 .wait_for_notification_with_timeout(Duration::from_secs(60))
556 .await
557 .unwrap();
558 assert!(n.is_some());
559 let n = n.unwrap();
560 assert_eq!(n.process_id, 7);
561 assert_eq!(n.channel, "timeout_ch");
562 assert_eq!(n.payload, "timeout_payload");
563 }
564
565 #[tokio::test]
566 async fn test_wait_for_notification_zero_timeout() {
567 let mut conn = make_connection(vec![]);
568
569 let n = conn
571 .wait_for_notification(Some(Duration::ZERO))
572 .await
573 .unwrap();
574 assert!(n.is_none());
575 }
576
577 #[tokio::test]
578 async fn test_wait_for_notification_with_timeout_zero() {
579 let mut conn = make_connection(vec![]);
580
581 let n = conn
583 .wait_for_notification_with_timeout(Duration::ZERO)
584 .await
585 .unwrap();
586 assert!(n.is_none());
587 }
588}