1use std::borrow::Cow;
15use std::sync::Arc;
16
17use bytes::Bytes;
18
19use mssql_types::decode::{TypeInfo, decode_value};
20use mssql_types::{FromSql, SqlValue, TypeError};
21
22#[derive(Debug, Clone, Copy)]
27pub struct ColumnSlice {
28 pub offset: u32,
30 pub length: u32,
32 pub is_null: bool,
34}
35
36impl ColumnSlice {
37 pub fn new(offset: u32, length: u32, is_null: bool) -> Self {
39 Self {
40 offset,
41 length,
42 is_null,
43 }
44 }
45
46 pub fn null() -> Self {
48 Self {
49 offset: 0,
50 length: 0,
51 is_null: true,
52 }
53 }
54}
55
56#[derive(Debug, Clone)]
58pub struct Column {
59 pub name: String,
61 pub index: usize,
63 pub type_name: String,
65 pub nullable: bool,
67 pub max_length: Option<u32>,
69 pub precision: Option<u8>,
71 pub scale: Option<u8>,
73}
74
75impl Column {
76 pub fn new(name: impl Into<String>, index: usize, type_name: impl Into<String>) -> Self {
78 Self {
79 name: name.into(),
80 index,
81 type_name: type_name.into(),
82 nullable: true,
83 max_length: None,
84 precision: None,
85 scale: None,
86 }
87 }
88
89 #[must_use]
91 pub fn with_nullable(mut self, nullable: bool) -> Self {
92 self.nullable = nullable;
93 self
94 }
95
96 #[must_use]
98 pub fn with_max_length(mut self, max_length: u32) -> Self {
99 self.max_length = Some(max_length);
100 self
101 }
102
103 #[must_use]
105 pub fn with_precision_scale(mut self, precision: u8, scale: u8) -> Self {
106 self.precision = Some(precision);
107 self.scale = Some(scale);
108 self
109 }
110
111 pub fn to_type_info(&self) -> TypeInfo {
115 let type_id = type_name_to_id(&self.type_name);
116 TypeInfo {
117 type_id,
118 length: self.max_length,
119 scale: self.scale,
120 precision: self.precision,
121 collation: None,
122 }
123 }
124}
125
126fn type_name_to_id(name: &str) -> u8 {
128 match name.to_uppercase().as_str() {
129 "INT" | "INTEGER" => 0x38,
131 "BIGINT" => 0x7F,
132 "SMALLINT" => 0x34,
133 "TINYINT" => 0x30,
134 "BIT" => 0x32,
135
136 "FLOAT" => 0x3E,
138 "REAL" => 0x3B,
139
140 "DECIMAL" | "NUMERIC" => 0x6C,
142 "MONEY" | "SMALLMONEY" => 0x6E,
143
144 "NVARCHAR" | "NCHAR" | "NTEXT" => 0xE7,
146 "VARCHAR" | "CHAR" | "TEXT" => 0xA7,
147
148 "VARBINARY" | "BINARY" | "IMAGE" => 0xA5,
150
151 "DATE" => 0x28,
153 "TIME" => 0x29,
154 "DATETIME2" => 0x2A,
155 "DATETIMEOFFSET" => 0x2B,
156 "DATETIME" => 0x3D,
157 "SMALLDATETIME" => 0x3F,
158
159 "UNIQUEIDENTIFIER" => 0x24,
161
162 "XML" => 0xF1,
164
165 _ if name.ends_with("N") => 0x26,
167
168 _ => 0xA5,
170 }
171}
172
173#[derive(Debug, Clone)]
178pub struct ColMetaData {
179 pub columns: Arc<[Column]>,
181}
182
183impl ColMetaData {
184 pub fn new(columns: Vec<Column>) -> Self {
186 Self {
187 columns: columns.into(),
188 }
189 }
190
191 #[must_use]
193 pub fn len(&self) -> usize {
194 self.columns.len()
195 }
196
197 #[must_use]
199 pub fn is_empty(&self) -> bool {
200 self.columns.is_empty()
201 }
202
203 #[must_use]
205 pub fn get(&self, index: usize) -> Option<&Column> {
206 self.columns.get(index)
207 }
208
209 #[must_use]
211 pub fn find_by_name(&self, name: &str) -> Option<usize> {
212 self.columns
213 .iter()
214 .position(|c| c.name.eq_ignore_ascii_case(name))
215 }
216}
217
218#[derive(Clone)]
243pub struct Row {
244 buffer: Arc<Bytes>,
246 slices: Arc<[ColumnSlice]>,
248 metadata: Arc<ColMetaData>,
250 values: Option<Arc<[SqlValue]>>,
253}
254
255impl Row {
256 pub fn new(buffer: Arc<Bytes>, slices: Arc<[ColumnSlice]>, metadata: Arc<ColMetaData>) -> Self {
260 Self {
261 buffer,
262 slices,
263 metadata,
264 values: None,
265 }
266 }
267
268 #[allow(dead_code)]
273 pub(crate) fn from_values(columns: Vec<Column>, values: Vec<SqlValue>) -> Self {
274 let metadata = Arc::new(ColMetaData::new(columns));
275 let slices: Arc<[ColumnSlice]> = values
276 .iter()
277 .enumerate()
278 .map(|(i, v)| ColumnSlice::new(i as u32, 0, v.is_null()))
279 .collect::<Vec<_>>()
280 .into();
281
282 Self {
283 buffer: Arc::new(Bytes::new()),
284 slices,
285 metadata,
286 values: Some(values.into()),
287 }
288 }
289
290 #[must_use]
298 pub fn get_bytes(&self, index: usize) -> Option<&[u8]> {
299 let slice = self.slices.get(index)?;
300 if slice.is_null {
301 return None;
302 }
303
304 let start = slice.offset as usize;
305 let end = start + slice.length as usize;
306
307 if end <= self.buffer.len() {
308 Some(&self.buffer[start..end])
309 } else {
310 None
311 }
312 }
313
314 #[must_use]
319 pub fn get_str(&self, index: usize) -> Option<Cow<'_, str>> {
320 let bytes = self.get_bytes(index)?;
321
322 match std::str::from_utf8(bytes) {
324 Ok(s) => Some(Cow::Borrowed(s)),
325 Err(_) => {
326 let utf16: Vec<u16> = bytes
329 .chunks_exact(2)
330 .map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]]))
331 .collect();
332
333 String::from_utf16(&utf16).ok().map(Cow::Owned)
334 }
335 }
336 }
337
338 #[must_use]
342 pub fn get_string(&self, index: usize) -> Option<String> {
343 self.get_str(index).map(|cow| cow.into_owned())
344 }
345
346 pub fn get<T: FromSql>(&self, index: usize) -> Result<T, TypeError> {
354 if let Some(ref values) = self.values {
356 return values
357 .get(index)
358 .ok_or_else(|| TypeError::TypeMismatch {
359 expected: "valid column index",
360 actual: format!("index {index} out of bounds"),
361 })
362 .and_then(T::from_sql);
363 }
364
365 let slice = self
367 .slices
368 .get(index)
369 .ok_or_else(|| TypeError::TypeMismatch {
370 expected: "valid column index",
371 actual: format!("index {index} out of bounds"),
372 })?;
373
374 if slice.is_null {
375 return Err(TypeError::UnexpectedNull);
376 }
377
378 let value = self.parse_value(index, slice)?;
381 T::from_sql(&value)
382 }
383
384 pub fn get_by_name<T: FromSql>(&self, name: &str) -> Result<T, TypeError> {
386 let index = self
387 .metadata
388 .find_by_name(name)
389 .ok_or_else(|| TypeError::TypeMismatch {
390 expected: "valid column name",
391 actual: format!("column '{name}' not found"),
392 })?;
393
394 self.get(index)
395 }
396
397 pub fn try_get<T: FromSql>(&self, index: usize) -> Option<T> {
399 if let Some(ref values) = self.values {
401 return values
402 .get(index)
403 .and_then(|v| T::from_sql_nullable(v).ok().flatten());
404 }
405
406 let slice = self.slices.get(index)?;
408 if slice.is_null {
409 return None;
410 }
411
412 self.get(index).ok()
413 }
414
415 pub fn try_get_by_name<T: FromSql>(&self, name: &str) -> Option<T> {
417 let index = self.metadata.find_by_name(name)?;
418 self.try_get(index)
419 }
420
421 #[must_use]
429 pub fn get_raw(&self, index: usize) -> Option<SqlValue> {
430 if let Some(ref values) = self.values {
431 return values.get(index).cloned();
432 }
433
434 let slice = self.slices.get(index)?;
435 self.parse_value(index, slice).ok()
436 }
437
438 #[must_use]
440 pub fn get_raw_by_name(&self, name: &str) -> Option<SqlValue> {
441 let index = self.metadata.find_by_name(name)?;
442 self.get_raw(index)
443 }
444
445 #[must_use]
451 pub fn len(&self) -> usize {
452 self.slices.len()
453 }
454
455 #[must_use]
457 pub fn is_empty(&self) -> bool {
458 self.slices.is_empty()
459 }
460
461 #[must_use]
463 pub fn columns(&self) -> &[Column] {
464 &self.metadata.columns
465 }
466
467 #[must_use]
469 pub fn metadata(&self) -> &Arc<ColMetaData> {
470 &self.metadata
471 }
472
473 #[must_use]
475 pub fn is_null(&self, index: usize) -> bool {
476 self.slices.get(index).map(|s| s.is_null).unwrap_or(true)
477 }
478
479 #[must_use]
481 pub fn is_null_by_name(&self, name: &str) -> bool {
482 self.metadata
483 .find_by_name(name)
484 .map(|i| self.is_null(i))
485 .unwrap_or(true)
486 }
487
488 fn parse_value(&self, index: usize, slice: &ColumnSlice) -> Result<SqlValue, TypeError> {
497 if slice.is_null {
498 return Ok(SqlValue::Null);
499 }
500
501 let column = self
502 .metadata
503 .get(index)
504 .ok_or_else(|| TypeError::TypeMismatch {
505 expected: "valid column metadata",
506 actual: format!("no metadata for column {index}"),
507 })?;
508
509 let start = slice.offset as usize;
511 let end = start + slice.length as usize;
512
513 if end > self.buffer.len() {
515 return Err(TypeError::TypeMismatch {
516 expected: "valid byte range",
517 actual: format!(
518 "range {}..{} exceeds buffer length {}",
519 start,
520 end,
521 self.buffer.len()
522 ),
523 });
524 }
525
526 let type_info = column.to_type_info();
528
529 let mut buf = self.buffer.slice(start..end);
532
533 decode_value(&mut buf, &type_info)
535 }
536}
537
538impl std::fmt::Debug for Row {
539 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
540 f.debug_struct("Row")
541 .field("columns", &self.metadata.columns.len())
542 .field("buffer_size", &self.buffer.len())
543 .field("has_cached_values", &self.values.is_some())
544 .finish()
545 }
546}
547
548pub struct RowIter<'a> {
550 row: &'a Row,
551 index: usize,
552}
553
554impl Iterator for RowIter<'_> {
555 type Item = SqlValue;
556
557 fn next(&mut self) -> Option<Self::Item> {
558 if self.index >= self.row.len() {
559 return None;
560 }
561 let value = self.row.get_raw(self.index);
562 self.index += 1;
563 value
564 }
565
566 fn size_hint(&self) -> (usize, Option<usize>) {
567 let remaining = self.row.len() - self.index;
568 (remaining, Some(remaining))
569 }
570}
571
572impl<'a> IntoIterator for &'a Row {
573 type Item = SqlValue;
574 type IntoIter = RowIter<'a>;
575
576 fn into_iter(self) -> Self::IntoIter {
577 RowIter {
578 row: self,
579 index: 0,
580 }
581 }
582}
583
584#[cfg(test)]
585#[allow(clippy::unwrap_used)]
586mod tests {
587 use super::*;
588
589 #[test]
590 fn test_column_slice_null() {
591 let slice = ColumnSlice::null();
592 assert!(slice.is_null);
593 assert_eq!(slice.offset, 0);
594 assert_eq!(slice.length, 0);
595 }
596
597 #[test]
598 fn test_column_metadata() {
599 let col = Column::new("id", 0, "INT")
600 .with_nullable(false)
601 .with_precision_scale(10, 0);
602
603 assert_eq!(col.name, "id");
604 assert_eq!(col.index, 0);
605 assert!(!col.nullable);
606 assert_eq!(col.precision, Some(10));
607 }
608
609 #[test]
610 fn test_col_metadata_find_by_name() {
611 let meta = ColMetaData::new(vec![
612 Column::new("id", 0, "INT"),
613 Column::new("Name", 1, "NVARCHAR"),
614 ]);
615
616 assert_eq!(meta.find_by_name("id"), Some(0));
617 assert_eq!(meta.find_by_name("ID"), Some(0)); assert_eq!(meta.find_by_name("name"), Some(1));
619 assert_eq!(meta.find_by_name("unknown"), None);
620 }
621
622 #[test]
623 fn test_row_from_values_backward_compat() {
624 let columns = vec![
625 Column::new("id", 0, "INT"),
626 Column::new("name", 1, "NVARCHAR"),
627 ];
628 let values = vec![SqlValue::Int(42), SqlValue::String("Alice".to_string())];
629
630 let row = Row::from_values(columns, values);
631
632 assert_eq!(row.len(), 2);
633 assert_eq!(row.get::<i32>(0).unwrap(), 42);
634 assert_eq!(row.get_by_name::<String>("name").unwrap(), "Alice");
635 }
636
637 #[test]
638 fn test_row_is_null() {
639 let columns = vec![
640 Column::new("id", 0, "INT"),
641 Column::new("nullable_col", 1, "NVARCHAR"),
642 ];
643 let values = vec![SqlValue::Int(1), SqlValue::Null];
644
645 let row = Row::from_values(columns, values);
646
647 assert!(!row.is_null(0));
648 assert!(row.is_null(1));
649 assert!(row.is_null(99)); }
651
652 #[test]
653 fn test_row_get_bytes_with_buffer() {
654 let buffer = Arc::new(Bytes::from_static(b"Hello World"));
655 let slices: Arc<[ColumnSlice]> = vec![
656 ColumnSlice::new(0, 5, false), ColumnSlice::new(6, 5, false), ]
659 .into();
660 let meta = Arc::new(ColMetaData::new(vec![
661 Column::new("greeting", 0, "VARCHAR"),
662 Column::new("subject", 1, "VARCHAR"),
663 ]));
664
665 let row = Row::new(buffer, slices, meta);
666
667 assert_eq!(row.get_bytes(0), Some(b"Hello".as_slice()));
668 assert_eq!(row.get_bytes(1), Some(b"World".as_slice()));
669 }
670
671 #[test]
672 fn test_row_get_str() {
673 let buffer = Arc::new(Bytes::from_static(b"Test"));
674 let slices: Arc<[ColumnSlice]> = vec![ColumnSlice::new(0, 4, false)].into();
675 let meta = Arc::new(ColMetaData::new(vec![Column::new("val", 0, "VARCHAR")]));
676
677 let row = Row::new(buffer, slices, meta);
678
679 let s = row.get_str(0).unwrap();
680 assert_eq!(s, "Test");
681 assert!(matches!(s, Cow::Borrowed(_)));
683 }
684
685 #[test]
686 fn test_row_metadata_access() {
687 let columns = vec![Column::new("col1", 0, "INT")];
688 let row = Row::from_values(columns, vec![SqlValue::Int(1)]);
689
690 assert_eq!(row.columns().len(), 1);
691 assert_eq!(row.columns()[0].name, "col1");
692 assert_eq!(row.metadata().len(), 1);
693 }
694}