1use crate::tds::stream::ReceivedToken;
2use crate::{row::ColumnType, Column, Row};
3use crate::{ColumnData, CommandResult, ResultMetadata};
4use futures_util::{
5 ready,
6 stream::{BoxStream, Peekable, Stream, StreamExt, TryStreamExt},
7};
8use std::{
9 fmt::Debug,
10 pin::Pin,
11 sync::Arc,
12 task::{self, Poll},
13};
14
15pub struct CommandStream<'a> {
83 token_stream: Peekable<BoxStream<'a, crate::Result<ReceivedToken>>>,
84 columns: Option<Arc<Vec<Column>>>,
85 result_set_index: Option<usize>,
86}
87
88impl<'a> Debug for CommandStream<'a> {
89 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90 f.debug_struct("CommandStream")
91 .field(
92 "token_stream",
93 &"BoxStream<'a, crate::Result<ReceivedToken>>",
94 )
95 .finish()
96 }
97}
98
99impl<'a> CommandStream<'a> {
100 pub(crate) fn new(token_stream: BoxStream<'a, crate::Result<ReceivedToken>>) -> Self {
101 Self {
102 token_stream: token_stream.peekable(),
103 columns: None,
104 result_set_index: None,
105 }
106 }
107
108 pub async fn into_command_result(mut self) -> crate::Result<CommandResult> {
111 let mut results: Vec<Vec<Row>> = Vec::new();
112 let mut result: Option<Vec<Row>> = None;
113 let mut return_status = 0;
114 let mut return_values = Vec::new();
115 let mut rows_affected = Vec::new();
116
117 while let Some(item) = self.try_next().await? {
118 match (item, &mut result) {
119 (CommandItem::Row(row), None) => {
120 result = Some(vec![row]);
121 }
122 (CommandItem::Row(row), Some(ref mut result)) => result.push(row),
123 (CommandItem::Metadata(_), None) => {
124 result = Some(Vec::new());
125 }
126 (CommandItem::Metadata(_), ref mut previous_result) => {
127 results.push(previous_result.take().unwrap());
128 result = None;
129 }
130 (CommandItem::ReturnStatus(rs), _) => return_status = rs,
131 (CommandItem::ReturnValue(rv), _) => return_values.push(rv),
132 (CommandItem::RowsAffected(rows), _) => rows_affected.push(rows),
133 }
134 }
135
136 if let Some(result) = result {
137 results.push(result);
138 }
139
140 Ok(CommandResult {
141 return_code: return_status,
142 return_values,
143 query_results: results,
144 rows_affected,
145 })
146 }
147
148 pub fn into_row_stream(self) -> BoxStream<'a, crate::Result<Row>> {
150 let s = self.try_filter_map(|item| async {
151 match item {
152 CommandItem::Row(row) => Ok(Some(row)),
153 _ => Ok(None),
154 }
155 });
156
157 Box::pin(s)
158 }
159}
160
161#[derive(Debug)]
165pub struct CommandReturnValue {
166 pub(crate) name: String,
167 pub(crate) ord: u16,
168 pub(crate) data: ColumnData<'static>,
169}
170
171impl CommandReturnValue {
172 pub fn name(&self) -> &str {
174 &self.name
175 }
176
177 pub fn ordinal(&self) -> u16 {
179 self.ord
180 }
181
182 pub fn data(&self) -> &ColumnData<'static> {
184 &self.data
185 }
186}
187
188#[derive(Debug)]
190pub enum CommandItem {
191 Row(Row),
193 Metadata(ResultMetadata),
195 ReturnStatus(u32),
197 ReturnValue(CommandReturnValue),
199 RowsAffected(u64),
201}
202
203impl CommandItem {
204 pub(crate) fn metadata(columns: Arc<Vec<Column>>, result_index: usize) -> Self {
205 Self::Metadata(ResultMetadata {
206 columns,
207 result_index,
208 })
209 }
210
211 pub fn as_metadata(&self) -> Option<&ResultMetadata> {
213 match self {
214 CommandItem::Metadata(ref metadata) => Some(metadata),
215 _ => None,
216 }
217 }
218
219 pub fn as_row(&self) -> Option<&Row> {
221 match self {
222 CommandItem::Row(ref row) => Some(row),
223 _ => None,
224 }
225 }
226
227 pub fn into_metadata(self) -> Option<ResultMetadata> {
229 match self {
230 CommandItem::Metadata(metadata) => Some(metadata),
231 _ => None,
232 }
233 }
234
235 pub fn into_row(self) -> Option<Row> {
237 match self {
238 CommandItem::Row(row) => Some(row),
239 _ => None,
240 }
241 }
242
243 pub fn as_return_status(&self) -> Option<u32> {
245 match self {
246 CommandItem::ReturnStatus(rs) => Some(*rs),
247 _ => None,
248 }
249 }
250
251 pub fn as_return_value(&self) -> Option<&CommandReturnValue> {
253 match self {
254 CommandItem::ReturnValue(rv) => Some(rv),
255 _ => None,
256 }
257 }
258
259 pub fn into_return_value(self) -> Option<CommandReturnValue> {
261 match self {
262 CommandItem::ReturnValue(rv) => Some(rv),
263 _ => None,
264 }
265 }
266}
267
268impl<'a> Stream for CommandStream<'a> {
269 type Item = crate::Result<CommandItem>;
270
271 fn poll_next(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Option<Self::Item>> {
272 let this = self.get_mut();
273
274 loop {
275 let token = match ready!(this.token_stream.poll_next_unpin(cx)) {
276 Some(res) => res?,
277 None => return Poll::Ready(None),
278 };
279
280 return match token {
281 ReceivedToken::NewResultset(meta) => {
282 let column_meta = meta
283 .columns
284 .iter()
285 .map(|x| Column {
286 name: x.col_name.to_string(),
287 column_type: ColumnType::from(&x.base.ty),
288 })
289 .collect::<Vec<_>>();
290
291 let column_meta = Arc::new(column_meta);
292 this.columns = Some(column_meta.clone());
293
294 this.result_set_index = this.result_set_index.map(|i| i + 1);
295
296 let query_item =
297 CommandItem::metadata(column_meta, *this.result_set_index.get_or_insert(0));
298
299 Poll::Ready(Some(Ok(query_item)))
300 }
301 ReceivedToken::Row(data) => {
302 let Some(columns) = this.columns.as_ref() else {
303 return Poll::Ready(Some(Err(crate::Error::Protocol(
304 "ROW token arrived before any column metadata".into(),
305 ))));
306 };
307 let columns = columns.clone();
308 let result_index = this.result_set_index.unwrap_or(0);
309
310 let row = Row {
311 columns,
312 data,
313 result_index,
314 };
315
316 Poll::Ready(Some(Ok(CommandItem::Row(row))))
317 }
318 ReceivedToken::ReturnStatus(rs) => {
319 Poll::Ready(Some(Ok(CommandItem::ReturnStatus(rs))))
320 }
321 ReceivedToken::ReturnValue(rv) => {
322 Poll::Ready(Some(Ok(CommandItem::ReturnValue(CommandReturnValue {
323 name: rv.param_name,
324 ord: rv.param_ordinal,
325 data: rv.value,
326 }))))
327 }
328 ReceivedToken::DoneProc(done) if done.is_final() => continue,
329 ReceivedToken::DoneProc(done) => {
330 Poll::Ready(Some(Ok(CommandItem::RowsAffected(done.rows()))))
331 }
332 ReceivedToken::DoneInProc(done) => {
333 Poll::Ready(Some(Ok(CommandItem::RowsAffected(done.rows()))))
334 }
335 ReceivedToken::Done(done) => {
336 Poll::Ready(Some(Ok(CommandItem::RowsAffected(done.rows()))))
337 }
338 _ => continue,
339 };
340 }
341 }
342}