1use std::sync::Arc;
8
9use crate::protocol::{BackendMessage, TransactionStatus};
10
11use crate::connection::{Connection, ConnectionState};
12use crate::error::{PgError, PgServerError, Result};
13use crate::query::result::CommandTag;
14use crate::query::row::{FieldDescription, Row};
15use crate::query::{read_data_row, read_row_description};
16
17#[cfg(feature = "tracing")]
18use crate::tracing_ext::TARGET_QUERY;
19
20#[derive(Debug)]
22enum RowStreamState {
23 WaitingForDescription,
24 ReceivingRows,
25 Finishing { command_tag: CommandTag },
26 Done { command_tag: CommandTag },
27 Error,
28}
29
30#[non_exhaustive]
45pub struct RowStream<'a> {
46 conn: &'a mut Connection,
47 columns: Option<Arc<Vec<FieldDescription>>>,
48 state: RowStreamState,
49 #[allow(dead_code)]
53 extended_protocol: bool,
54 rows_fetched: u64,
56 #[cfg(feature = "tracing")]
58 started_at: std::time::Instant,
59}
60
61impl<'a> RowStream<'a> {
62 pub(crate) fn new_simple(conn: &'a mut Connection) -> Self {
64 RowStream {
65 conn,
66 columns: None,
67 state: RowStreamState::WaitingForDescription,
68 extended_protocol: false,
69 rows_fetched: 0,
70 #[cfg(feature = "tracing")]
71 started_at: std::time::Instant::now(),
72 }
73 }
74
75 #[cfg_attr(not(test), allow(dead_code))]
77 pub(crate) fn new_extended(conn: &'a mut Connection) -> Self {
78 RowStream {
79 conn,
80 columns: None,
81 state: RowStreamState::WaitingForDescription,
82 extended_protocol: true,
83 rows_fetched: 0,
84 #[cfg(feature = "tracing")]
85 started_at: std::time::Instant::now(),
86 }
87 }
88
89 pub(crate) fn new_extended_with_columns(
91 conn: &'a mut Connection,
92 columns: Arc<Vec<FieldDescription>>,
93 ) -> Self {
94 RowStream {
95 conn,
96 columns: Some(columns),
97 state: RowStreamState::WaitingForDescription,
98 extended_protocol: true,
99 rows_fetched: 0,
100 #[cfg(feature = "tracing")]
101 started_at: std::time::Instant::now(),
102 }
103 }
104
105 pub(crate) fn new_extended_receiving(
109 conn: &'a mut Connection,
110 columns: Option<Arc<Vec<FieldDescription>>>,
111 ) -> Self {
112 RowStream {
113 conn,
114 columns,
115 state: RowStreamState::ReceivingRows,
116 extended_protocol: true,
117 rows_fetched: 0,
118 #[cfg(feature = "tracing")]
119 started_at: std::time::Instant::now(),
120 }
121 }
122
123 #[must_use = "stream errors should be checked"]
129 pub async fn next(&mut self) -> Result<Option<Row>> {
130 loop {
131 match self.state {
132 RowStreamState::Done { .. } | RowStreamState::Error => {
133 return Ok(None);
134 }
135
136 RowStreamState::WaitingForDescription => {
137 let msg = self
138 .conn
139 .codec
140 .read_message(&mut self.conn.transport)
141 .await?;
142 match msg {
143 BackendMessage::RowDescription(body) => {
144 self.columns = Some(Arc::new(read_row_description(body)?));
145 self.state = RowStreamState::ReceivingRows;
146 }
147 BackendMessage::CommandComplete(body) => {
148 let tag = CommandTag::new(body.tag().unwrap_or("").into());
149 self.state = RowStreamState::Finishing { command_tag: tag };
150 }
151 BackendMessage::EmptyQueryResponse => {
152 self.state = RowStreamState::Finishing {
153 command_tag: CommandTag::default(),
154 };
155 }
156 BackendMessage::ErrorResponse(body) => {
157 let server_err =
158 PgServerError::from_error_body(&body).map_err(PgError::Io)?;
159 self.conn.read_until_ready().await?;
160 self.conn.state = ConnectionState::Idle;
161 self.state = RowStreamState::Error;
162 return Err(PgError::Server(Box::new(server_err)));
163 }
164 BackendMessage::NotificationResponse(body) => {
165 self.conn.notification_queue.push_back(
166 crate::notification::Notification {
167 process_id: body.process_id(),
168 channel: body.channel().unwrap_or("").to_string(),
169 payload: body.message().unwrap_or("").to_string(),
170 },
171 );
172 continue;
173 }
174 BackendMessage::NoticeResponse(body) => {
175 if let Ok(notice) = crate::query::Notice::from_fields(&body) {
176 self.conn.handle_notice(¬ice);
177 }
178 continue;
179 }
180 BackendMessage::ParameterStatus(body) => {
181 if let (Ok(name), Ok(value)) = (body.name(), body.value()) {
182 self.conn
183 .server_params
184 .params
185 .insert(name.to_string(), value.to_string());
186 }
187 continue;
188 }
189 BackendMessage::ParseComplete
190 | BackendMessage::BindComplete
191 | BackendMessage::NoData => {
192 continue;
193 }
194 _ => continue,
195 }
196 }
197
198 RowStreamState::ReceivingRows => {
199 let msg = self
200 .conn
201 .codec
202 .read_message(&mut self.conn.transport)
203 .await?;
204 match msg {
205 BackendMessage::DataRow(body) => {
206 let values = read_data_row(body)?;
207 let cols = self.columns.clone().unwrap_or_default();
208 self.rows_fetched += 1;
209 return Ok(Some(Row::new(cols, values)));
210 }
211 BackendMessage::CommandComplete(body) => {
212 let tag = CommandTag::new(body.tag().unwrap_or("").into());
213 self.state = RowStreamState::Finishing { command_tag: tag };
214 }
215 BackendMessage::ErrorResponse(body) => {
216 let server_err =
217 PgServerError::from_error_body(&body).map_err(PgError::Io)?;
218 self.conn.read_until_ready().await?;
219 self.conn.state = ConnectionState::Idle;
220 self.state = RowStreamState::Error;
221 return Err(PgError::Server(Box::new(server_err)));
222 }
223 BackendMessage::NotificationResponse(body) => {
224 self.conn.notification_queue.push_back(
225 crate::notification::Notification {
226 process_id: body.process_id(),
227 channel: body.channel().unwrap_or("").to_string(),
228 payload: body.message().unwrap_or("").to_string(),
229 },
230 );
231 continue;
232 }
233 BackendMessage::NoticeResponse(body) => {
234 if let Ok(notice) = crate::query::Notice::from_fields(&body) {
235 self.conn.handle_notice(¬ice);
236 }
237 continue;
238 }
239 BackendMessage::ParameterStatus(body) => {
240 if let (Ok(name), Ok(value)) = (body.name(), body.value()) {
241 self.conn
242 .server_params
243 .params
244 .insert(name.to_string(), value.to_string());
245 }
246 continue;
247 }
248 BackendMessage::PortalSuspended => {
249 continue;
250 }
251 _ => continue,
252 }
253 }
254
255 RowStreamState::Finishing { .. } => {
256 let msg = self
257 .conn
258 .codec
259 .read_message(&mut self.conn.transport)
260 .await?;
261 match msg {
262 BackendMessage::ReadyForQuery(body) => {
263 let tag =
264 match std::mem::replace(&mut self.state, RowStreamState::Error) {
265 RowStreamState::Finishing { command_tag } => command_tag,
266 _ => CommandTag::default(),
267 };
268 self.conn.transaction_status =
269 TransactionStatus::from_u8(body.status())
270 .unwrap_or(TransactionStatus::Idle);
271 self.conn.state = ConnectionState::Idle;
272 self.state = RowStreamState::Done { command_tag: tag };
273 #[cfg(feature = "tracing")]
274 tracing::info!(
275 target: TARGET_QUERY,
276 rows_fetched = self.rows_fetched,
277 command_tag = self.command_tag().map(|t| t.as_str()).unwrap_or(""),
278 elapsed_ms = self.started_at.elapsed().as_millis() as u64,
279 "Query stream completed"
280 );
281 return Ok(None);
282 }
283 BackendMessage::ErrorResponse(body) => {
284 let server_err =
291 PgServerError::from_error_body(&body).map_err(PgError::Io)?;
292 self.conn.read_until_ready().await?;
293 self.conn.state = ConnectionState::Idle;
294 self.state = RowStreamState::Error;
295 return Err(PgError::Server(Box::new(server_err)));
296 }
297 BackendMessage::NotificationResponse(body) => {
298 self.conn.notification_queue.push_back(
299 crate::notification::Notification {
300 process_id: body.process_id(),
301 channel: body.channel().unwrap_or("").to_string(),
302 payload: body.message().unwrap_or("").to_string(),
303 },
304 );
305 continue;
306 }
307 BackendMessage::NoticeResponse(body) => {
308 if let Ok(notice) = crate::query::Notice::from_fields(&body) {
309 self.conn.handle_notice(¬ice);
310 }
311 continue;
312 }
313 BackendMessage::ParameterStatus(body) => {
314 if let (Ok(name), Ok(value)) = (body.name(), body.value()) {
315 self.conn
316 .server_params
317 .params
318 .insert(name.to_string(), value.to_string());
319 }
320 continue;
321 }
322 _ => continue,
323 }
324 }
325 }
326 }
327 }
328
329 pub fn columns(&self) -> Option<&[FieldDescription]> {
331 self.columns.as_ref().map(|c| c.as_slice())
332 }
333
334 pub fn command_tag(&self) -> Option<&CommandTag> {
336 match &self.state {
337 RowStreamState::Done { command_tag } | RowStreamState::Finishing { command_tag } => {
338 Some(command_tag)
339 }
340 _ => None,
341 }
342 }
343
344 pub fn is_done(&self) -> bool {
346 matches!(
347 self.state,
348 RowStreamState::Done { .. } | RowStreamState::Error
349 )
350 }
351
352 #[must_use = "consume errors should be checked"]
354 pub async fn consume(mut self) -> Result<CommandTag> {
355 while self.next().await?.is_some() {}
356 match &self.state {
357 RowStreamState::Done { command_tag } => Ok(command_tag.clone()),
358 _ => Ok(CommandTag::default()),
359 }
360 }
361}
362
363impl<'a> Drop for RowStream<'a> {
364 fn drop(&mut self) {
365 if !self.is_done() {
366 #[cfg(feature = "tracing")]
367 if !std::thread::panicking() {
368 tracing::warn!(target: TARGET_QUERY, "RowStream dropped without full consumption; connection may need recovery");
369 }
370 self.conn.needs_recovery = true;
371 }
372 }
373}
374
375#[cfg(test)]
376mod tests {
377 use super::*;
378 use crate::auth::{Codec, ServerParams};
379 use crate::config::Config;
380 use crate::connection::ConnectionState;
381 use crate::transport::{BufferedTransport, ClientTransport, MockTransport, PgTransport};
382 use std::collections::VecDeque;
383
384 fn make_connection(read_data: Vec<u8>) -> Connection {
385 let transport = PgTransport::Plain(BufferedTransport::new(ClientTransport::Mock(
386 MockTransport::new(read_data),
387 )));
388 Connection {
389 transport,
390 codec: Codec::new(),
391 server_params: ServerParams::default(),
392 state: ConnectionState::Idle,
393 config: Config::new(),
394 transaction_status: TransactionStatus::Idle,
395 notification_queue: VecDeque::new(),
396 notice_handler: None,
397 statement_counter: 0,
398 needs_recovery: false,
399 health: crate::reconnect::session::ConnectionHealth::new(),
400 session_state: crate::reconnect::session::SessionState::new(),
401 }
402 }
403
404 fn build_row_description_msg(fields: &[(&str, u32)]) -> Vec<u8> {
405 let mut buf = vec![b'T'];
406 let mut body = Vec::new();
407 body.extend_from_slice(&(fields.len() as i16).to_be_bytes());
408 for (name, type_oid) in fields {
409 body.extend_from_slice(name.as_bytes());
410 body.push(0);
411 body.extend_from_slice(&0u32.to_be_bytes());
412 body.extend_from_slice(&0i16.to_be_bytes());
413 body.extend_from_slice(&type_oid.to_be_bytes());
414 body.extend_from_slice(&(-1i16).to_be_bytes());
415 body.extend_from_slice(&(-1i32).to_be_bytes());
416 body.extend_from_slice(&0i16.to_be_bytes());
417 }
418 let len = (body.len() + 4) as i32;
419 buf.extend_from_slice(&len.to_be_bytes());
420 buf.extend_from_slice(&body);
421 buf
422 }
423
424 fn build_data_row_msg(values: &[Option<&str>]) -> Vec<u8> {
425 let mut buf = vec![b'D'];
426 let mut body = Vec::new();
427 body.extend_from_slice(&(values.len() as i16).to_be_bytes());
428 for val in values {
429 match val {
430 Some(v) => {
431 let bytes = v.as_bytes();
432 body.extend_from_slice(&(bytes.len() as i32).to_be_bytes());
433 body.extend_from_slice(bytes);
434 }
435 None => {
436 body.extend_from_slice(&(-1i32).to_be_bytes());
437 }
438 }
439 }
440 let len = (body.len() + 4) as i32;
441 buf.extend_from_slice(&len.to_be_bytes());
442 buf.extend_from_slice(&body);
443 buf
444 }
445
446 fn build_command_complete_msg(tag: &str) -> Vec<u8> {
447 let mut buf = vec![b'C'];
448 let mut body = Vec::new();
449 body.extend_from_slice(tag.as_bytes());
450 body.push(0);
451 let len = (body.len() + 4) as i32;
452 buf.extend_from_slice(&len.to_be_bytes());
453 buf.extend_from_slice(&body);
454 buf
455 }
456
457 fn build_ready_for_query(status: u8) -> Vec<u8> {
458 vec![b'Z', 0, 0, 0, 5, status]
459 }
460
461 fn build_error_response_msg(severity: &str, code: &str, message: &str) -> Vec<u8> {
462 let mut buf = vec![b'E'];
463 let mut body = Vec::new();
464 body.push(b'S');
466 body.extend_from_slice(severity.as_bytes());
467 body.push(0);
468 body.push(b'C');
470 body.extend_from_slice(code.as_bytes());
471 body.push(0);
472 body.push(b'M');
474 body.extend_from_slice(message.as_bytes());
475 body.push(0);
476 body.push(0);
478 let len = (body.len() + 4) as i32;
479 buf.extend_from_slice(&len.to_be_bytes());
480 buf.extend_from_slice(&body);
481 buf
482 }
483
484 #[tokio::test]
485 async fn test_row_stream_basic() {
486 let mut data = Vec::new();
487 data.extend_from_slice(&build_row_description_msg(&[
488 ("id", crate::types::INT4_OID),
489 ("name", crate::types::TEXT_OID),
490 ]));
491 data.extend_from_slice(&build_data_row_msg(&[Some("1"), Some("alice")]));
492 data.extend_from_slice(&build_data_row_msg(&[Some("2"), Some("bob")]));
493 data.extend_from_slice(&build_data_row_msg(&[Some("3"), Some("charlie")]));
494 data.extend_from_slice(&build_command_complete_msg("SELECT 3"));
495 data.extend_from_slice(&build_ready_for_query(b'I'));
496 let mut conn = make_connection(data);
497 let mut stream = RowStream::new_simple(&mut conn);
498 assert!(stream.columns().is_none());
499 let row1 = stream.next().await.unwrap().unwrap();
500 let id1: i32 = row1.get(0).unwrap();
501 assert_eq!(id1, 1);
502 assert!(stream.columns().is_some());
503 let row2 = stream.next().await.unwrap().unwrap();
504 let name2: String = row2.get(1).unwrap();
505 assert_eq!(name2, "bob");
506 let row3 = stream.next().await.unwrap().unwrap();
507 let name3: String = row3.get(1).unwrap();
508 assert_eq!(name3, "charlie");
509 let done = stream.next().await.unwrap();
510 assert!(done.is_none());
511 assert!(stream.is_done());
512 assert_eq!(stream.command_tag().unwrap().as_str(), "SELECT 3");
513 }
514
515 #[tokio::test]
516 async fn test_row_stream_empty() {
517 let mut data = Vec::new();
518 data.extend_from_slice(&build_row_description_msg(&[(
519 "id",
520 crate::types::INT4_OID,
521 )]));
522 data.extend_from_slice(&build_command_complete_msg("SELECT 0"));
523 data.extend_from_slice(&build_ready_for_query(b'I'));
524 let mut conn = make_connection(data);
525 let mut stream = RowStream::new_simple(&mut conn);
526 let done = stream.next().await.unwrap();
527 assert!(done.is_none());
528 assert!(stream.is_done());
529 }
530
531 #[tokio::test]
532 async fn test_row_stream_no_row_description() {
533 let mut data = Vec::new();
534 data.extend_from_slice(&build_command_complete_msg("INSERT 0 1"));
535 data.extend_from_slice(&build_ready_for_query(b'I'));
536 let mut conn = make_connection(data);
537 let mut stream = RowStream::new_simple(&mut conn);
538 let done = stream.next().await.unwrap();
539 assert!(done.is_none());
540 assert!(stream.is_done());
541 assert_eq!(stream.command_tag().unwrap().as_str(), "INSERT 0 1");
542 }
543
544 #[tokio::test]
545 async fn test_row_stream_consume() {
546 let mut data = Vec::new();
547 data.extend_from_slice(&build_row_description_msg(&[(
548 "val",
549 crate::types::INT4_OID,
550 )]));
551 data.extend_from_slice(&build_data_row_msg(&[Some("10")]));
552 data.extend_from_slice(&build_data_row_msg(&[Some("20")]));
553 data.extend_from_slice(&build_data_row_msg(&[Some("30")]));
554 data.extend_from_slice(&build_command_complete_msg("SELECT 3"));
555 data.extend_from_slice(&build_ready_for_query(b'I'));
556 let mut conn = make_connection(data);
557 let mut stream = RowStream::new_simple(&mut conn);
558 let row1 = stream.next().await.unwrap().unwrap();
559 let v: i32 = row1.get(0).unwrap();
560 assert_eq!(v, 10);
561 let tag = stream.consume().await.unwrap();
562 assert_eq!(tag.as_str(), "SELECT 3");
563 assert!(!conn.needs_recovery());
564 }
565
566 #[tokio::test]
567 async fn test_row_stream_drop_sets_needs_recovery() {
568 let mut data = Vec::new();
569 data.extend_from_slice(&build_row_description_msg(&[(
570 "val",
571 crate::types::INT4_OID,
572 )]));
573 data.extend_from_slice(&build_data_row_msg(&[Some("10")]));
574 data.extend_from_slice(&build_data_row_msg(&[Some("20")]));
575 data.extend_from_slice(&build_command_complete_msg("SELECT 2"));
576 data.extend_from_slice(&build_ready_for_query(b'I'));
577 let mut conn = make_connection(data);
578 {
579 let mut stream = RowStream::new_simple(&mut conn);
580 let _row1 = stream.next().await.unwrap().unwrap();
581 }
582 assert!(conn.needs_recovery());
583 }
584
585 #[tokio::test]
586 async fn test_row_stream_extended_protocol() {
587 let mut data = Vec::new();
588 data.extend_from_slice(&[b'1', 0, 0, 0, 4]);
589 data.extend_from_slice(&[b'2', 0, 0, 0, 4]);
590 data.extend_from_slice(&build_row_description_msg(&[(
591 "val",
592 crate::types::INT4_OID,
593 )]));
594 data.extend_from_slice(&build_data_row_msg(&[Some("42")]));
595 data.extend_from_slice(&build_command_complete_msg("SELECT 1"));
596 data.extend_from_slice(&build_ready_for_query(b'I'));
597 let mut conn = make_connection(data);
598 let mut stream = RowStream::new_extended(&mut conn);
599 let row = stream.next().await.unwrap().unwrap();
600 let v: i32 = row.get(0).unwrap();
601 assert_eq!(v, 42);
602 let done = stream.next().await.unwrap();
603 assert!(done.is_none());
604 }
605
606 #[tokio::test]
607 async fn test_connection_recover_after_dropped_stream() {
608 let mut data = Vec::new();
610 data.extend_from_slice(&build_row_description_msg(&[(
611 "val",
612 crate::types::INT4_OID,
613 )]));
614 data.extend_from_slice(&build_data_row_msg(&[Some("10")]));
615 data.extend_from_slice(&build_data_row_msg(&[Some("20")]));
616 data.extend_from_slice(&build_command_complete_msg("SELECT 2"));
617 data.extend_from_slice(&build_ready_for_query(b'I'));
618 let mut conn = make_connection(data);
619 {
620 let mut stream = RowStream::new_simple(&mut conn);
621 let _row1 = stream.next().await.unwrap().unwrap();
622 }
624 assert!(
625 conn.needs_recovery(),
626 "should need recovery after dropping stream"
627 );
628
629 conn.recover().await.unwrap();
631 assert!(
632 !conn.needs_recovery(),
633 "should not need recovery after recover"
634 );
635 assert_eq!(
636 conn.state(),
637 ConnectionState::Idle,
638 "should be idle after recover"
639 );
640 }
641
642 #[tokio::test]
643 async fn test_row_stream_error_in_finishing_state() {
644 let mut data = Vec::new();
647 data.extend_from_slice(&build_row_description_msg(&[(
648 "val",
649 crate::types::INT4_OID,
650 )]));
651 data.extend_from_slice(&build_data_row_msg(&[Some("10")]));
652 data.extend_from_slice(&build_command_complete_msg("SELECT 1"));
653 data.extend_from_slice(&build_error_response_msg(
655 "ERROR",
656 "57014", "canceling statement due to user request",
658 ));
659 data.extend_from_slice(&build_ready_for_query(b'I'));
660 let mut conn = make_connection(data);
661 let mut stream = RowStream::new_simple(&mut conn);
662 let row = stream.next().await.unwrap().unwrap();
664 let v: i32 = row.get(0).unwrap();
665 assert_eq!(v, 10);
666 let result = stream.next().await;
669 assert!(
670 result.is_err(),
671 "ErrorResponse in Finishing state should be surfaced as an error"
672 );
673 let err = result.unwrap_err();
674 match err {
675 PgError::Server(server_err) => {
676 assert_eq!(
677 server_err.code, "57014",
678 "error code should be query_canceled"
679 );
680 }
681 other => panic!("expected PgError::Server, got {:?}", other),
682 }
683 drop(stream);
685 assert_eq!(
687 conn.state(),
688 ConnectionState::Idle,
689 "connection should be idle after error in finishing state"
690 );
691 }
692}