|
1 | 1 | use tokio::time::timeout; |
2 | 2 |
|
3 | 3 | use crate::{ |
4 | | - frontend::client::TransactionType, |
| 4 | + frontend::{client::TransactionType, router::parser::explain_trace::ExplainTrace}, |
5 | 5 | net::{ |
6 | | - FromBytes, Message, Protocol, ProtocolMessage, Query, ReadyForQuery, ToBytes, |
7 | | - TransactionState, |
| 6 | + DataRow, FromBytes, Message, Protocol, ProtocolMessage, Query, ReadyForQuery, |
| 7 | + RowDescription, ToBytes, TransactionState, |
8 | 8 | }, |
9 | 9 | state::State, |
10 | 10 | }; |
@@ -96,9 +96,35 @@ impl QueryEngine { |
96 | 96 | self.streaming = message.streaming(); |
97 | 97 |
|
98 | 98 | let code = message.code(); |
| 99 | + let payload = if code == 'T' { |
| 100 | + Some(message.payload()) |
| 101 | + } else { |
| 102 | + None |
| 103 | + }; |
99 | 104 | let mut message = message.backend(); |
100 | 105 | let has_more_messages = self.backend.has_more_messages(); |
101 | 106 |
|
| 107 | + if let Some(bytes) = payload { |
| 108 | + if let Some(state) = self.pending_explain.as_mut() { |
| 109 | + if let Ok(row_description) = RowDescription::from_bytes(bytes) { |
| 110 | + state.capture_row_description(row_description); |
| 111 | + } else { |
| 112 | + state.annotated = true; |
| 113 | + } |
| 114 | + } |
| 115 | + } |
| 116 | + |
| 117 | + if code == 'C' { |
| 118 | + self.emit_explain_rows(context).await?; |
| 119 | + } |
| 120 | + |
| 121 | + if code == 'E' { |
| 122 | + if let Some(state) = self.pending_explain.as_mut() { |
| 123 | + state.annotated = true; |
| 124 | + } |
| 125 | + self.pending_explain = None; |
| 126 | + } |
| 127 | + |
102 | 128 | // Messages that we need to send to the client immediately. |
103 | 129 | // ReadyForQuery (B) | CopyInResponse (B) | ErrorResponse(B) | NoticeResponse(B) | NotificationResponse (B) |
104 | 130 | let flush = matches!(code, 'Z' | 'G' | 'E' | 'N' | 'A') |
@@ -185,6 +211,38 @@ impl QueryEngine { |
185 | 211 | context.stream.send(&message).await?; |
186 | 212 | } |
187 | 213 |
|
| 214 | + if code == 'Z' { |
| 215 | + self.pending_explain = None; |
| 216 | + } |
| 217 | + |
| 218 | + Ok(()) |
| 219 | + } |
| 220 | + |
| 221 | + async fn emit_explain_rows( |
| 222 | + &mut self, |
| 223 | + context: &mut QueryEngineContext<'_>, |
| 224 | + ) -> Result<(), Error> { |
| 225 | + if let Some(state) = self.pending_explain.as_mut() { |
| 226 | + if !state.should_emit() { |
| 227 | + return Ok(()); |
| 228 | + } |
| 229 | + |
| 230 | + if state.row_description.is_none() { |
| 231 | + return Ok(()); |
| 232 | + } |
| 233 | + |
| 234 | + for line in state.lines.clone() { |
| 235 | + let mut row = DataRow::new(); |
| 236 | + row.add(line); |
| 237 | + let message = row.message()?; |
| 238 | + let len = message.len(); |
| 239 | + context.stream.send(&message).await?; |
| 240 | + self.stats.sent(len); |
| 241 | + } |
| 242 | + |
| 243 | + state.annotated = true; |
| 244 | + } |
| 245 | + |
188 | 246 | Ok(()) |
189 | 247 | } |
190 | 248 |
|
@@ -310,3 +368,36 @@ impl QueryEngine { |
310 | 368 | } |
311 | 369 | } |
312 | 370 | } |
| 371 | + |
| 372 | +#[derive(Debug, Default, Clone)] |
| 373 | +pub(super) struct ExplainResponseState { |
| 374 | + lines: Vec<String>, |
| 375 | + row_description: Option<RowDescription>, |
| 376 | + annotated: bool, |
| 377 | + supported: bool, |
| 378 | +} |
| 379 | + |
| 380 | +impl ExplainResponseState { |
| 381 | + pub fn new(trace: ExplainTrace) -> Self { |
| 382 | + Self { |
| 383 | + lines: trace.render_lines(), |
| 384 | + row_description: None, |
| 385 | + annotated: false, |
| 386 | + supported: false, |
| 387 | + } |
| 388 | + } |
| 389 | + |
| 390 | + pub fn capture_row_description(&mut self, row_description: RowDescription) { |
| 391 | + self.supported = row_description.fields.len() == 1 |
| 392 | + && matches!(row_description.field(0).map(|f| f.type_oid), Some(25)); |
| 393 | + if self.supported { |
| 394 | + self.row_description = Some(row_description); |
| 395 | + } else { |
| 396 | + self.annotated = true; |
| 397 | + } |
| 398 | + } |
| 399 | + |
| 400 | + pub fn should_emit(&self) -> bool { |
| 401 | + self.supported && !self.annotated |
| 402 | + } |
| 403 | +} |
0 commit comments