1use std::collections::VecDeque;
36use std::pin::Pin;
37use std::sync::Arc;
38use std::task::{Context, Poll};
39
40use futures_core::Stream;
41use tds_protocol::token::{ColMetaData, NbcRow, RawRow};
42
43use crate::error::Error;
44use crate::row::{Column, Row};
45
46#[derive(Debug, Clone)]
53pub(crate) enum PendingRow {
54 Parsed(Row),
56 Raw(RawRow),
58 Nbc(NbcRow),
60}
61
62#[must_use = "streams must be consumed; dropping a stream discards remaining rows"]
88pub struct QueryStream<'a> {
89 row_meta: Arc<crate::row::ColMetaData>,
91 rows: VecDeque<PendingRow>,
93 meta: Option<ColMetaData>,
96 #[cfg(feature = "always-encrypted")]
101 decryptor: Option<std::sync::Arc<crate::column_decryptor::ColumnDecryptor>>,
102 finished: bool,
104 _marker: std::marker::PhantomData<&'a ()>,
106}
107
108impl QueryStream<'_> {
109 #[cfg(test)]
116 pub(crate) fn new(columns: Vec<Column>, rows: Vec<Row>) -> Self {
117 Self {
118 row_meta: Arc::new(crate::row::ColMetaData::new(columns)),
119 rows: rows.into_iter().map(PendingRow::Parsed).collect(),
120 meta: None,
121 #[cfg(feature = "always-encrypted")]
122 decryptor: None,
123 finished: false,
124 _marker: std::marker::PhantomData,
125 }
126 }
127
128 pub(crate) fn from_raw(
134 columns: Vec<Column>,
135 pending: Vec<PendingRow>,
136 meta: ColMetaData,
137 #[cfg(feature = "always-encrypted")] decryptor: Option<
138 std::sync::Arc<crate::column_decryptor::ColumnDecryptor>,
139 >,
140 ) -> Self {
141 Self {
142 row_meta: Arc::new(crate::row::ColMetaData::new(columns)),
143 rows: pending.into(),
144 meta: Some(meta),
145 #[cfg(feature = "always-encrypted")]
146 decryptor,
147 finished: false,
148 _marker: std::marker::PhantomData,
149 }
150 }
151
152 #[allow(dead_code)]
154 pub(crate) fn empty() -> Self {
155 Self {
156 row_meta: Arc::new(crate::row::ColMetaData::new(Vec::new())),
157 rows: VecDeque::new(),
158 meta: None,
159 #[cfg(feature = "always-encrypted")]
160 decryptor: None,
161 finished: true,
162 _marker: std::marker::PhantomData,
163 }
164 }
165
166 #[must_use]
168 pub fn columns(&self) -> &[Column] {
169 &self.row_meta.columns
170 }
171
172 #[must_use]
174 pub fn is_finished(&self) -> bool {
175 self.finished
176 }
177
178 #[must_use]
180 pub fn rows_remaining(&self) -> usize {
181 self.rows.len()
182 }
183
184 pub async fn collect_all(mut self) -> Result<Vec<Row>, Error> {
194 let mut out = Vec::with_capacity(self.rows.len());
195 while let Some(pending) = self.rows.pop_front() {
196 out.push(self.decode(pending)?);
197 }
198 self.finished = true;
199 Ok(out)
200 }
201
202 pub fn try_next(&mut self) -> Option<Row> {
208 self.next().and_then(|r| r.ok())
209 }
210
211 fn decode(&self, pending: PendingRow) -> Result<Row, Error> {
213 match pending {
214 PendingRow::Parsed(row) => Ok(row),
215 PendingRow::Raw(raw) => {
216 let meta = self
217 .meta
218 .as_ref()
219 .ok_or_else(|| Error::Protocol("row metadata missing for raw row".into()))?;
220 #[cfg(feature = "always-encrypted")]
221 if let Some(ref dec) = self.decryptor {
222 return crate::column_parser::convert_raw_row_decrypted(
223 &raw,
224 meta,
225 &self.row_meta,
226 dec,
227 );
228 }
229 crate::column_parser::convert_raw_row(&raw, meta, &self.row_meta)
230 }
231 PendingRow::Nbc(nbc) => {
232 let meta = self
233 .meta
234 .as_ref()
235 .ok_or_else(|| Error::Protocol("row metadata missing for NBC row".into()))?;
236 #[cfg(feature = "always-encrypted")]
237 if let Some(ref dec) = self.decryptor {
238 return crate::column_parser::convert_nbc_row_decrypted(
239 &nbc,
240 meta,
241 &self.row_meta,
242 dec,
243 );
244 }
245 crate::column_parser::convert_nbc_row(&nbc, meta, &self.row_meta)
246 }
247 }
248 }
249}
250
251impl Stream for QueryStream<'_> {
252 type Item = Result<Row, Error>;
253
254 fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
255 let this = self.get_mut();
256
257 if this.finished {
258 return Poll::Ready(None);
259 }
260
261 match this.rows.pop_front() {
262 Some(pending) => Poll::Ready(Some(this.decode(pending))),
263 None => {
264 this.finished = true;
265 Poll::Ready(None)
266 }
267 }
268 }
269}
270
271impl ExactSizeIterator for QueryStream<'_> {}
272
273impl Iterator for QueryStream<'_> {
274 type Item = Result<Row, Error>;
275
276 fn next(&mut self) -> Option<Self::Item> {
277 if self.finished {
278 return None;
279 }
280
281 match self.rows.pop_front() {
282 Some(pending) => Some(self.decode(pending)),
283 None => {
284 self.finished = true;
285 None
286 }
287 }
288 }
289
290 fn size_hint(&self) -> (usize, Option<usize>) {
291 let remaining = self.rows.len();
292 (remaining, Some(remaining))
293 }
294}
295
296#[derive(Debug, Clone)]
300#[non_exhaustive]
301#[must_use]
302pub struct ExecuteResult {
303 pub rows_affected: u64,
305 pub output_params: Vec<OutputParam>,
307}
308
309#[derive(Debug, Clone)]
311#[non_exhaustive]
312pub struct OutputParam {
313 pub name: String,
315 pub value: mssql_types::SqlValue,
317}
318
319impl ExecuteResult {
320 pub fn new(rows_affected: u64) -> Self {
322 Self {
323 rows_affected,
324 output_params: Vec::new(),
325 }
326 }
327
328 pub fn with_outputs(rows_affected: u64, output_params: Vec<OutputParam>) -> Self {
330 Self {
331 rows_affected,
332 output_params,
333 }
334 }
335
336 #[must_use]
338 pub fn get_output(&self, name: &str) -> Option<&OutputParam> {
339 self.output_params
340 .iter()
341 .find(|p| p.name.eq_ignore_ascii_case(name))
342 }
343}
344
345#[derive(Debug, Clone)]
370#[non_exhaustive]
371#[must_use]
372pub struct ProcedureResult {
373 pub return_value: i32,
378 pub rows_affected: u64,
380 pub output_params: Vec<OutputParam>,
382 pub result_sets: Vec<ResultSet>,
384}
385
386impl ProcedureResult {
387 pub(crate) fn new() -> Self {
389 Self {
390 return_value: 0,
391 rows_affected: 0,
392 output_params: Vec::new(),
393 result_sets: Vec::new(),
394 }
395 }
396
397 #[must_use]
402 pub fn get_return_value(&self) -> i32 {
403 self.return_value
404 }
405
406 #[must_use]
429 pub fn get_output(&self, name: &str) -> Option<&OutputParam> {
430 let search = name.strip_prefix('@').unwrap_or(name);
431 self.output_params.iter().find(|p| {
432 let stored = p.name.strip_prefix('@').unwrap_or(&p.name);
433 stored.eq_ignore_ascii_case(search)
434 })
435 }
436
437 #[must_use]
441 pub fn first_result_set(&self) -> Option<&ResultSet> {
442 self.result_sets.first()
443 }
444
445 #[must_use]
447 pub fn has_result_sets(&self) -> bool {
448 !self.result_sets.is_empty()
449 }
450}
451
452#[derive(Debug, Clone)]
461#[must_use]
462pub struct ResultSet {
463 row_meta: Arc<crate::row::ColMetaData>,
465 pending_rows: VecDeque<PendingRow>,
467 meta: Option<ColMetaData>,
470 #[cfg(feature = "always-encrypted")]
475 decryptor: Option<std::sync::Arc<crate::column_decryptor::ColumnDecryptor>>,
476}
477
478impl ResultSet {
479 pub fn new(columns: Vec<Column>, rows: Vec<Row>) -> Self {
485 Self {
486 row_meta: Arc::new(crate::row::ColMetaData::new(columns)),
487 pending_rows: rows.into_iter().map(PendingRow::Parsed).collect(),
488 meta: None,
489 #[cfg(feature = "always-encrypted")]
490 decryptor: None,
491 }
492 }
493
494 pub(crate) fn from_raw(
501 columns: Vec<Column>,
502 pending: Vec<PendingRow>,
503 meta: ColMetaData,
504 #[cfg(feature = "always-encrypted")] decryptor: Option<
505 std::sync::Arc<crate::column_decryptor::ColumnDecryptor>,
506 >,
507 ) -> Self {
508 Self {
509 row_meta: Arc::new(crate::row::ColMetaData::new(columns)),
510 pending_rows: pending.into(),
511 meta: Some(meta),
512 #[cfg(feature = "always-encrypted")]
513 decryptor,
514 }
515 }
516
517 #[must_use]
519 pub fn columns(&self) -> &[Column] {
520 &self.row_meta.columns
521 }
522
523 #[must_use]
525 pub fn rows_remaining(&self) -> usize {
526 self.pending_rows.len()
527 }
528
529 pub fn next_row(&mut self) -> Option<Result<Row, Error>> {
535 self.pending_rows.pop_front().map(|p| self.decode(p))
536 }
537
538 #[must_use]
540 pub fn is_empty(&self) -> bool {
541 self.pending_rows.is_empty()
542 }
543
544 pub fn collect_all(&mut self) -> Result<Vec<Row>, Error> {
548 let mut out = Vec::with_capacity(self.pending_rows.len());
549 while let Some(pending) = self.pending_rows.pop_front() {
550 out.push(self.decode(pending)?);
551 }
552 Ok(out)
553 }
554
555 fn decode(&self, pending: PendingRow) -> Result<Row, Error> {
557 match pending {
558 PendingRow::Parsed(row) => Ok(row),
559 PendingRow::Raw(raw) => {
560 let meta = self
561 .meta
562 .as_ref()
563 .ok_or_else(|| Error::Protocol("row metadata missing for raw row".into()))?;
564 #[cfg(feature = "always-encrypted")]
565 if let Some(ref dec) = self.decryptor {
566 return crate::column_parser::convert_raw_row_decrypted(
567 &raw,
568 meta,
569 &self.row_meta,
570 dec,
571 );
572 }
573 crate::column_parser::convert_raw_row(&raw, meta, &self.row_meta)
574 }
575 PendingRow::Nbc(nbc) => {
576 let meta = self
577 .meta
578 .as_ref()
579 .ok_or_else(|| Error::Protocol("row metadata missing for NBC row".into()))?;
580 #[cfg(feature = "always-encrypted")]
581 if let Some(ref dec) = self.decryptor {
582 return crate::column_parser::convert_nbc_row_decrypted(
583 &nbc,
584 meta,
585 &self.row_meta,
586 dec,
587 );
588 }
589 crate::column_parser::convert_nbc_row(&nbc, meta, &self.row_meta)
590 }
591 }
592 }
593
594 fn into_query_stream<'a>(self) -> QueryStream<'a> {
600 QueryStream {
601 row_meta: self.row_meta,
602 rows: self.pending_rows,
603 meta: self.meta,
604 #[cfg(feature = "always-encrypted")]
605 decryptor: self.decryptor,
606 finished: false,
607 _marker: std::marker::PhantomData,
608 }
609 }
610}
611
612#[must_use = "streams must be consumed; dropping a stream discards remaining results"]
639pub struct MultiResultStream<'a> {
640 result_sets: Vec<ResultSet>,
642 current_result: usize,
644 _marker: std::marker::PhantomData<&'a ()>,
646}
647
648impl<'a> MultiResultStream<'a> {
649 pub(crate) fn new(result_sets: Vec<ResultSet>) -> Self {
651 Self {
652 result_sets,
653 current_result: 0,
654 _marker: std::marker::PhantomData,
655 }
656 }
657
658 #[must_use]
660 pub fn current_result_index(&self) -> usize {
661 self.current_result
662 }
663
664 #[must_use]
666 pub fn result_count(&self) -> usize {
667 self.result_sets.len()
668 }
669
670 #[must_use]
672 pub fn has_more_results(&self) -> bool {
673 self.current_result + 1 < self.result_sets.len()
674 }
675
676 #[must_use]
680 pub fn columns(&self) -> Option<&[Column]> {
681 self.result_sets
682 .get(self.current_result)
683 .map(|rs| rs.columns())
684 }
685
686 pub async fn next_result(&mut self) -> Result<bool, Error> {
690 if self.current_result + 1 < self.result_sets.len() {
691 self.current_result += 1;
692 Ok(true)
693 } else {
694 Ok(false)
695 }
696 }
697
698 pub async fn next_row(&mut self) -> Result<Option<Row>, Error> {
707 if let Some(result_set) = self.result_sets.get_mut(self.current_result) {
708 result_set.next_row().transpose()
709 } else {
710 Ok(None)
711 }
712 }
713
714 #[must_use]
716 pub fn current_result_set(&mut self) -> Option<&mut ResultSet> {
717 self.result_sets.get_mut(self.current_result)
718 }
719
720 pub fn collect_current(&mut self) -> Result<Vec<Row>, Error> {
726 match self.result_sets.get_mut(self.current_result) {
727 Some(rs) => rs.collect_all(),
728 None => Ok(Vec::new()),
729 }
730 }
731
732 pub fn into_query_streams(self) -> Vec<QueryStream<'a>> {
734 self.result_sets
735 .into_iter()
736 .map(ResultSet::into_query_stream)
737 .collect()
738 }
739}
740
741#[cfg(test)]
742#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
743mod tests {
744 use super::*;
745
746 #[test]
747 fn test_execute_result() {
748 let result = ExecuteResult::new(42);
749 assert_eq!(result.rows_affected, 42);
750 assert!(result.output_params.is_empty());
751 }
752
753 #[test]
754 fn test_procedure_result_defaults() {
755 let result = ProcedureResult::new();
756 assert_eq!(result.return_value, 0);
757 assert_eq!(result.rows_affected, 0);
758 assert!(result.output_params.is_empty());
759 assert!(result.result_sets.is_empty());
760 assert!(!result.has_result_sets());
761 assert!(result.first_result_set().is_none());
762 }
763
764 #[test]
765 fn test_procedure_result_get_output() {
766 let mut result = ProcedureResult::new();
767 result.output_params.push(OutputParam {
768 name: "@Total".to_string(),
769 value: mssql_types::SqlValue::Int(42),
770 });
771 result.output_params.push(OutputParam {
772 name: "@Message".to_string(),
773 value: mssql_types::SqlValue::String("ok".to_string()),
774 });
775
776 assert!(result.get_output("@Total").is_some());
778 assert!(result.get_output("@total").is_some());
779 assert!(result.get_output("@TOTAL").is_some());
780
781 assert!(result.get_output("Total").is_some());
783 assert!(result.get_output("total").is_some());
784
785 assert!(result.get_output("@NotHere").is_none());
787 assert!(result.get_output("NotHere").is_none());
788 }
789
790 #[test]
791 fn test_procedure_result_with_result_sets() {
792 use mssql_types::SqlValue;
793
794 let columns = vec![Column {
795 name: "id".to_string(),
796 index: 0,
797 type_name: "INT".to_string(),
798 nullable: false,
799 max_length: Some(4),
800 precision: None,
801 scale: None,
802 collation: None,
803 }];
804 let rows = vec![Row::from_values(columns.clone(), vec![SqlValue::Int(1)])];
805 let rs = ResultSet::new(columns, rows);
806
807 let mut result = ProcedureResult::new();
808 result.result_sets.push(rs);
809 result.return_value = 7;
810 result.rows_affected = 5;
811
812 assert!(result.has_result_sets());
813 assert_eq!(result.get_return_value(), 7);
814 assert_eq!(result.first_result_set().unwrap().columns().len(), 1);
815 }
816
817 #[test]
818 fn test_execute_result_with_outputs() {
819 let outputs = vec![OutputParam {
820 name: "ReturnValue".to_string(),
821 value: mssql_types::SqlValue::Int(100),
822 }];
823
824 let result = ExecuteResult::with_outputs(10, outputs);
825 assert_eq!(result.rows_affected, 10);
826 assert!(result.get_output("ReturnValue").is_some());
827 assert!(result.get_output("returnvalue").is_some()); assert!(result.get_output("NotFound").is_none());
829 }
830
831 #[test]
832 fn test_query_stream_columns() {
833 let columns = vec![Column {
834 name: "id".to_string(),
835 index: 0,
836 type_name: "INT".to_string(),
837 nullable: false,
838 max_length: Some(4),
839 precision: Some(0),
840 scale: Some(0),
841 collation: None,
842 }];
843
844 let stream = QueryStream::new(columns, Vec::new());
845 assert_eq!(stream.columns().len(), 1);
846 assert_eq!(stream.columns()[0].name, "id");
847 assert!(!stream.is_finished());
848 }
849
850 #[test]
851 fn test_query_stream_with_rows() {
852 use mssql_types::SqlValue;
853
854 let columns = vec![
855 Column {
856 name: "id".to_string(),
857 index: 0,
858 type_name: "INT".to_string(),
859 nullable: false,
860 max_length: Some(4),
861 precision: None,
862 scale: None,
863 collation: None,
864 },
865 Column {
866 name: "name".to_string(),
867 index: 1,
868 type_name: "NVARCHAR".to_string(),
869 nullable: true,
870 max_length: Some(100),
871 precision: None,
872 scale: None,
873 collation: None,
874 },
875 ];
876
877 let rows = vec![
878 Row::from_values(
879 columns.clone(),
880 vec![SqlValue::Int(1), SqlValue::String("Alice".to_string())],
881 ),
882 Row::from_values(
883 columns.clone(),
884 vec![SqlValue::Int(2), SqlValue::String("Bob".to_string())],
885 ),
886 ];
887
888 let mut stream = QueryStream::new(columns, rows);
889 assert_eq!(stream.columns().len(), 2);
890 assert_eq!(stream.rows_remaining(), 2);
891 assert!(!stream.is_finished());
892
893 let row1 = stream.try_next().unwrap();
895 assert_eq!(row1.get::<i32>(0).unwrap(), 1);
896 assert_eq!(row1.get_by_name::<String>("name").unwrap(), "Alice");
897
898 let row2 = stream.try_next().unwrap();
900 assert_eq!(row2.get::<i32>(0).unwrap(), 2);
901 assert_eq!(row2.get_by_name::<String>("name").unwrap(), "Bob");
902
903 assert!(stream.try_next().is_none());
905 assert!(stream.is_finished());
906 }
907
908 #[test]
909 fn test_query_stream_iterator() {
910 use mssql_types::SqlValue;
911
912 let columns = vec![Column {
913 name: "val".to_string(),
914 index: 0,
915 type_name: "INT".to_string(),
916 nullable: false,
917 max_length: None,
918 precision: None,
919 scale: None,
920 collation: None,
921 }];
922
923 let rows = vec![
924 Row::from_values(columns.clone(), vec![SqlValue::Int(10)]),
925 Row::from_values(columns.clone(), vec![SqlValue::Int(20)]),
926 Row::from_values(columns.clone(), vec![SqlValue::Int(30)]),
927 ];
928
929 let mut stream = QueryStream::new(columns, rows);
930
931 let values: Vec<i32> = stream
935 .by_ref()
936 .map(|r| r.unwrap().get::<i32>(0).unwrap())
937 .collect();
938
939 assert_eq!(values, vec![10, 20, 30]);
940 assert!(stream.is_finished());
941 }
942
943 #[test]
944 fn test_query_stream_empty() {
945 let stream = QueryStream::empty();
946 assert!(stream.columns().is_empty());
947 assert_eq!(stream.rows_remaining(), 0);
948 assert!(stream.is_finished());
949 }
950
951 #[test]
956 fn test_query_stream_lazy_raw_row_decoding() {
957 use bytes::Bytes;
958 use tds_protocol::token::{ColMetaData, ColumnData, RawRow, TypeInfo};
959 use tds_protocol::types::TypeId;
960
961 let mut data = Vec::new();
963 data.push(4); data.extend_from_slice(&42i32.to_le_bytes());
965 data.push(0); let meta = ColMetaData {
968 columns: vec![
969 ColumnData {
970 name: "a".to_string(),
971 type_id: TypeId::IntN,
972 col_type: 0x26,
973 flags: 0x00,
974 user_type: 0,
975 type_info: TypeInfo {
976 max_length: Some(4),
977 precision: None,
978 scale: None,
979 collation: None,
980 },
981 crypto_metadata: None,
982 },
983 ColumnData {
984 name: "b".to_string(),
985 type_id: TypeId::IntN,
986 col_type: 0x26,
987 flags: 0x01,
988 user_type: 0,
989 type_info: TypeInfo {
990 max_length: Some(4),
991 precision: None,
992 scale: None,
993 collation: None,
994 },
995 crypto_metadata: None,
996 },
997 ],
998 cek_table: None,
999 };
1000
1001 let columns = vec![
1002 Column {
1003 name: "a".to_string(),
1004 index: 0,
1005 type_name: "INT".to_string(),
1006 nullable: false,
1007 max_length: Some(4),
1008 precision: None,
1009 scale: None,
1010 collation: None,
1011 },
1012 Column {
1013 name: "b".to_string(),
1014 index: 1,
1015 type_name: "INT".to_string(),
1016 nullable: true,
1017 max_length: Some(4),
1018 precision: None,
1019 scale: None,
1020 collation: None,
1021 },
1022 ];
1023
1024 let pending = vec![PendingRow::Raw(RawRow {
1025 data: Bytes::from(data),
1026 })];
1027
1028 #[cfg(feature = "always-encrypted")]
1029 let mut stream = QueryStream::from_raw(columns, pending, meta, None);
1030 #[cfg(not(feature = "always-encrypted"))]
1031 let mut stream = QueryStream::from_raw(columns, pending, meta);
1032
1033 assert_eq!(stream.rows_remaining(), 1);
1034 let row = stream
1035 .next()
1036 .expect("one row pending")
1037 .expect("row decoded successfully");
1038 assert_eq!(row.get::<i32>(0).unwrap(), 42);
1039 assert!(row.is_null(1));
1040 assert!(stream.next().is_none());
1041 assert!(stream.is_finished());
1042 }
1043
1044 #[test]
1048 fn test_query_stream_lazy_decode_error_propagates() {
1049 use bytes::Bytes;
1050 use tds_protocol::token::{ColMetaData, ColumnData, RawRow, TypeInfo};
1051 use tds_protocol::types::TypeId;
1052
1053 let data = vec![0x01u8, 0x02];
1055
1056 let meta = ColMetaData {
1057 columns: vec![ColumnData {
1058 name: "a".to_string(),
1059 type_id: TypeId::Int4,
1060 col_type: 0x38,
1061 flags: 0x00,
1062 user_type: 0,
1063 type_info: TypeInfo {
1064 max_length: Some(4),
1065 precision: None,
1066 scale: None,
1067 collation: None,
1068 },
1069 crypto_metadata: None,
1070 }],
1071 cek_table: None,
1072 };
1073
1074 let columns = vec![Column {
1075 name: "a".to_string(),
1076 index: 0,
1077 type_name: "INT".to_string(),
1078 nullable: false,
1079 max_length: Some(4),
1080 precision: None,
1081 scale: None,
1082 collation: None,
1083 }];
1084
1085 let pending = vec![PendingRow::Raw(RawRow {
1086 data: Bytes::from(data),
1087 })];
1088
1089 #[cfg(feature = "always-encrypted")]
1090 let mut stream = QueryStream::from_raw(columns, pending, meta, None);
1091 #[cfg(not(feature = "always-encrypted"))]
1092 let mut stream = QueryStream::from_raw(columns, pending, meta);
1093
1094 let item = stream.next().expect("pending row present");
1095 assert!(item.is_err(), "truncated bytes must surface a decode error");
1096 assert!(stream.next().is_none());
1097 }
1098
1099 #[cfg(test)]
1102 fn intn_meta_and_columns(
1103 col_name: &str,
1104 nullable: bool,
1105 ) -> (tds_protocol::token::ColMetaData, Vec<Column>) {
1106 use tds_protocol::token::{ColMetaData, ColumnData, TypeInfo};
1107 use tds_protocol::types::TypeId;
1108 (
1109 ColMetaData {
1110 columns: vec![ColumnData {
1111 name: col_name.to_string(),
1112 type_id: TypeId::IntN,
1113 col_type: 0x26,
1114 flags: if nullable { 0x01 } else { 0x00 },
1115 user_type: 0,
1116 type_info: TypeInfo {
1117 max_length: Some(4),
1118 precision: None,
1119 scale: None,
1120 collation: None,
1121 },
1122 crypto_metadata: None,
1123 }],
1124 cek_table: None,
1125 },
1126 vec![Column {
1127 name: col_name.to_string(),
1128 index: 0,
1129 type_name: "INT".to_string(),
1130 nullable,
1131 max_length: Some(4),
1132 precision: None,
1133 scale: None,
1134 collation: None,
1135 }],
1136 )
1137 }
1138
1139 #[test]
1143 fn test_result_set_lazy_raw_row_decoding() {
1144 use bytes::Bytes;
1145 use tds_protocol::token::RawRow;
1146
1147 let (meta, columns) = intn_meta_and_columns("a", false);
1148
1149 let pending = vec![
1151 PendingRow::Raw(RawRow {
1152 data: {
1153 let mut b = Vec::with_capacity(5);
1154 b.push(4);
1155 b.extend_from_slice(&7i32.to_le_bytes());
1156 Bytes::from(b)
1157 },
1158 }),
1159 PendingRow::Raw(RawRow {
1160 data: {
1161 let mut b = Vec::with_capacity(5);
1162 b.push(4);
1163 b.extend_from_slice(&11i32.to_le_bytes());
1164 Bytes::from(b)
1165 },
1166 }),
1167 ];
1168
1169 #[cfg(feature = "always-encrypted")]
1170 let mut rs = ResultSet::from_raw(columns, pending, meta, None);
1171 #[cfg(not(feature = "always-encrypted"))]
1172 let mut rs = ResultSet::from_raw(columns, pending, meta);
1173
1174 assert_eq!(rs.rows_remaining(), 2);
1175 assert!(!rs.is_empty());
1176
1177 let row1 = rs.next_row().expect("row present").expect("decodes");
1178 assert_eq!(row1.get::<i32>(0).unwrap(), 7);
1179
1180 let row2 = rs.next_row().expect("row present").expect("decodes");
1181 assert_eq!(row2.get::<i32>(0).unwrap(), 11);
1182
1183 assert!(rs.next_row().is_none());
1184 assert!(rs.is_empty());
1185 }
1186
1187 #[test]
1191 fn test_result_set_lazy_decode_error_propagates() {
1192 use bytes::Bytes;
1193 use tds_protocol::token::{ColMetaData, ColumnData, RawRow, TypeInfo};
1194 use tds_protocol::types::TypeId;
1195
1196 let meta = ColMetaData {
1198 columns: vec![ColumnData {
1199 name: "a".to_string(),
1200 type_id: TypeId::Int4,
1201 col_type: 0x38,
1202 flags: 0x00,
1203 user_type: 0,
1204 type_info: TypeInfo {
1205 max_length: Some(4),
1206 precision: None,
1207 scale: None,
1208 collation: None,
1209 },
1210 crypto_metadata: None,
1211 }],
1212 cek_table: None,
1213 };
1214 let columns = vec![Column {
1215 name: "a".to_string(),
1216 index: 0,
1217 type_name: "INT".to_string(),
1218 nullable: false,
1219 max_length: Some(4),
1220 precision: None,
1221 scale: None,
1222 collation: None,
1223 }];
1224
1225 let pending = vec![PendingRow::Raw(RawRow {
1226 data: Bytes::from(vec![0x01u8, 0x02]),
1227 })];
1228
1229 #[cfg(feature = "always-encrypted")]
1230 let mut rs = ResultSet::from_raw(columns, pending, meta, None);
1231 #[cfg(not(feature = "always-encrypted"))]
1232 let mut rs = ResultSet::from_raw(columns, pending, meta);
1233
1234 let first = rs.next_row().expect("pending row present");
1235 assert!(
1236 first.is_err(),
1237 "truncated bytes must surface a decode error"
1238 );
1239 assert!(rs.next_row().is_none());
1240 }
1241
1242 #[test]
1246 fn test_result_set_lazy_collect_all_success_and_error() {
1247 use bytes::Bytes;
1248 use tds_protocol::token::RawRow;
1249
1250 let (meta_ok, cols_ok) = intn_meta_and_columns("a", false);
1252 let pending_ok = vec![
1253 PendingRow::Raw(RawRow {
1254 data: {
1255 let mut b = Vec::with_capacity(5);
1256 b.push(4);
1257 b.extend_from_slice(&10i32.to_le_bytes());
1258 Bytes::from(b)
1259 },
1260 }),
1261 PendingRow::Raw(RawRow {
1262 data: {
1263 let mut b = Vec::with_capacity(5);
1264 b.push(4);
1265 b.extend_from_slice(&20i32.to_le_bytes());
1266 Bytes::from(b)
1267 },
1268 }),
1269 ];
1270
1271 #[cfg(feature = "always-encrypted")]
1272 let mut rs_ok = ResultSet::from_raw(cols_ok, pending_ok, meta_ok, None);
1273 #[cfg(not(feature = "always-encrypted"))]
1274 let mut rs_ok = ResultSet::from_raw(cols_ok, pending_ok, meta_ok);
1275 let rows = rs_ok.collect_all().expect("all rows decode");
1276 assert_eq!(rows.len(), 2);
1277 assert_eq!(rows[0].get::<i32>(0).unwrap(), 10);
1278 assert_eq!(rows[1].get::<i32>(0).unwrap(), 20);
1279 assert!(rs_ok.is_empty());
1280
1281 use tds_protocol::token::{ColMetaData, ColumnData, TypeInfo};
1284 use tds_protocol::types::TypeId;
1285 let meta_err = ColMetaData {
1286 columns: vec![ColumnData {
1287 name: "a".to_string(),
1288 type_id: TypeId::Int4,
1289 col_type: 0x38,
1290 flags: 0x00,
1291 user_type: 0,
1292 type_info: TypeInfo {
1293 max_length: Some(4),
1294 precision: None,
1295 scale: None,
1296 collation: None,
1297 },
1298 crypto_metadata: None,
1299 }],
1300 cek_table: None,
1301 };
1302 let cols_err = vec![Column {
1303 name: "a".to_string(),
1304 index: 0,
1305 type_name: "INT".to_string(),
1306 nullable: false,
1307 max_length: Some(4),
1308 precision: None,
1309 scale: None,
1310 collation: None,
1311 }];
1312 let pending_err = vec![PendingRow::Raw(RawRow {
1313 data: Bytes::from(vec![0x01u8, 0x02]),
1314 })];
1315
1316 #[cfg(feature = "always-encrypted")]
1317 let mut rs_err = ResultSet::from_raw(cols_err, pending_err, meta_err, None);
1318 #[cfg(not(feature = "always-encrypted"))]
1319 let mut rs_err = ResultSet::from_raw(cols_err, pending_err, meta_err);
1320 let err = rs_err.collect_all();
1321 assert!(err.is_err(), "collect_all must propagate decode error");
1322 }
1323
1324 #[tokio::test]
1328 async fn test_multi_result_stream_lazy_decode_across_result_sets() {
1329 use bytes::Bytes;
1330 use tds_protocol::token::RawRow;
1331
1332 let (meta1, cols1) = intn_meta_and_columns("a", false);
1333 let pending1 = vec![PendingRow::Raw(RawRow {
1334 data: {
1335 let mut b = Vec::with_capacity(5);
1336 b.push(4);
1337 b.extend_from_slice(&101i32.to_le_bytes());
1338 Bytes::from(b)
1339 },
1340 })];
1341 #[cfg(feature = "always-encrypted")]
1342 let rs1 = ResultSet::from_raw(cols1, pending1, meta1, None);
1343 #[cfg(not(feature = "always-encrypted"))]
1344 let rs1 = ResultSet::from_raw(cols1, pending1, meta1);
1345
1346 let (meta2, cols2) = intn_meta_and_columns("b", false);
1347 let pending2 = vec![PendingRow::Raw(RawRow {
1348 data: {
1349 let mut b = Vec::with_capacity(5);
1350 b.push(4);
1351 b.extend_from_slice(&202i32.to_le_bytes());
1352 Bytes::from(b)
1353 },
1354 })];
1355 #[cfg(feature = "always-encrypted")]
1356 let rs2 = ResultSet::from_raw(cols2, pending2, meta2, None);
1357 #[cfg(not(feature = "always-encrypted"))]
1358 let rs2 = ResultSet::from_raw(cols2, pending2, meta2);
1359
1360 let mut stream = MultiResultStream::new(vec![rs1, rs2]);
1361 assert_eq!(stream.result_count(), 2);
1362 assert_eq!(stream.current_result_index(), 0);
1363
1364 let row = stream
1365 .next_row()
1366 .await
1367 .expect("first row success")
1368 .expect("row present");
1369 assert_eq!(row.get::<i32>(0).unwrap(), 101);
1370 assert!(stream.next_row().await.expect("no more rows").is_none());
1371
1372 assert!(stream.has_more_results());
1373 assert!(stream.next_result().await.expect("advance ok"));
1374 assert_eq!(stream.current_result_index(), 1);
1375
1376 let row = stream
1377 .next_row()
1378 .await
1379 .expect("second row success")
1380 .expect("row present");
1381 assert_eq!(row.get::<i32>(0).unwrap(), 202);
1382 }
1383}