1use crate::value::Value;
54use std::collections::HashMap;
55
56#[derive(Debug, Clone, PartialEq)]
58pub enum QueryError {
59 ColumnCountMismatch {
61 expected: usize,
63 actual: usize,
65 },
66 TypeMismatch {
68 column: std::borrow::Cow<'static, str>,
70 expected: &'static str,
72 },
73 MissingColumn {
75 column: &'static str,
77 },
78 Custom(String),
80}
81
82impl std::fmt::Display for QueryError {
83 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84 match self {
85 QueryError::ColumnCountMismatch { expected, actual } => {
86 write!(f, "列数不匹配: 期望 {}, 实际 {}", expected, actual)
87 }
88 QueryError::TypeMismatch { column, expected } => {
89 write!(f, "列 {:?} 类型不匹配, 期望 {}", column, expected)
90 }
91 QueryError::MissingColumn { column } => {
92 write!(f, "缺少列: {}", column)
93 }
94 QueryError::Custom(msg) => write!(f, "{}", msg),
95 }
96 }
97}
98
99impl std::error::Error for QueryError {}
100
101#[derive(Debug, Clone)]
106pub struct RowDesc {
107 pub columns: Vec<String>,
109}
110
111impl RowDesc {
112 pub fn new(columns: Vec<String>) -> Self {
114 Self { columns }
115 }
116
117 pub fn len(&self) -> usize {
119 self.columns.len()
120 }
121
122 pub fn is_empty(&self) -> bool {
124 self.columns.is_empty()
125 }
126
127 pub fn index_of(&self, name: &str) -> Option<usize> {
129 self.columns.iter().position(|c| c == name)
130 }
131}
132
133pub trait Queryable: Sized {
138 fn from_values(values: Vec<Value>) -> Result<Self, QueryError>;
140
141 fn from_values_with_desc(values: Vec<Value>, desc: &RowDesc) -> Result<Self, QueryError> {
143 if values.len() != desc.len() {
144 return Err(QueryError::ColumnCountMismatch {
145 expected: desc.len(),
146 actual: values.len(),
147 });
148 }
149 Self::from_values(values)
150 }
151}
152
153pub trait FromRow: Sized {
158 fn from_row(row: HashMap<String, Value>) -> Result<Self, QueryError>;
160}
161
162impl Queryable for Value {
166 fn from_values(values: Vec<Value>) -> Result<Self, QueryError> {
167 if values.len() != 1 {
168 return Err(QueryError::ColumnCountMismatch {
169 expected: 1,
170 actual: values.len(),
171 });
172 }
173 Ok(values.into_iter().next().unwrap()) }
175}
176
177impl Queryable for (Value, Value) {
179 fn from_values(values: Vec<Value>) -> Result<Self, QueryError> {
180 if values.len() != 2 {
181 return Err(QueryError::ColumnCountMismatch {
182 expected: 2,
183 actual: values.len(),
184 });
185 }
186 let mut iter = values.into_iter();
187 Ok((iter.next().unwrap(), iter.next().unwrap())) }
189}
190
191impl Queryable for (Value, Value, Value) {
193 fn from_values(values: Vec<Value>) -> Result<Self, QueryError> {
194 if values.len() != 3 {
195 return Err(QueryError::ColumnCountMismatch {
196 expected: 3,
197 actual: values.len(),
198 });
199 }
200 let mut iter = values.into_iter();
201 Ok((
202 iter.next().unwrap(), iter.next().unwrap(),
204 iter.next().unwrap(),
205 ))
206 }
207}
208
209pub fn value_as_i64(v: &Value) -> Option<i64> {
213 v.as_i64()
214}
215
216pub fn value_as_f64(v: &Value) -> Option<f64> {
218 v.as_f64()
219}
220
221pub fn value_as_string(v: &Value) -> Option<String> {
223 v.as_str().map(|s| s.to_string())
224}
225
226pub fn value_as_bool(v: &Value) -> Option<bool> {
228 v.as_bool()
229}
230
231pub fn value_as_nullable_i64(v: &Value) -> Option<i64> {
233 if v.is_null() {
234 None
235 } else {
236 v.as_i64()
237 }
238}
239
240pub fn value_as_nullable_string(v: &Value) -> Option<String> {
242 if v.is_null() {
243 None
244 } else {
245 v.as_str().map(|s| s.to_string())
246 }
247}
248
249pub struct Query {
285 sql: String,
286}
287
288impl Query {
289 pub fn new(sql: impl Into<String>) -> Self {
291 Self { sql: sql.into() }
292 }
293
294 pub fn sql(&self) -> &str {
296 &self.sql
297 }
298}
299
300impl std::fmt::Display for Query {
301 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
302 write!(f, "{}", self.sql)
303 }
304}
305
306impl Query {
307 pub async fn fetch_all(
309 &self,
310 conn: &mut dyn crate::Connection,
311 ) -> Result<Vec<std::collections::HashMap<String, crate::value::Value>>, crate::DbError> {
312 conn.query(&self.sql).await
313 }
314
315 pub async fn fetch_one(
317 &self,
318 conn: &mut dyn crate::Connection,
319 ) -> Result<std::collections::HashMap<String, crate::value::Value>, crate::DbError> {
320 let rows = conn.query(&self.sql).await?;
321 match rows.len() {
322 0 => Err(crate::DbError::NotFound(
323 "fetch_one: no rows returned".into(),
324 )),
325 1 => Ok(rows.into_iter().next().unwrap()), n => Err(crate::DbError::QueryError(format!(
327 "fetch_one expected 1 row, got {}",
328 n
329 ))),
330 }
331 }
332
333 pub async fn fetch_optional(
335 &self,
336 conn: &mut dyn crate::Connection,
337 ) -> Result<Option<std::collections::HashMap<String, crate::value::Value>>, crate::DbError>
338 {
339 let rows = conn.query(&self.sql).await?;
340 Ok(rows.into_iter().next())
341 }
342}
343
344pub struct QueryAs<T> {
364 sql: String,
365 _marker: std::marker::PhantomData<T>,
366}
367
368impl<T> QueryAs<T> {
369 pub fn new(sql: impl Into<String>) -> Self {
371 Self {
372 sql: sql.into(),
373 _marker: std::marker::PhantomData,
374 }
375 }
376
377 pub fn sql(&self) -> &str {
379 &self.sql
380 }
381}
382
383impl<T> std::fmt::Display for QueryAs<T> {
384 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
385 write!(f, "{}", self.sql)
386 }
387}
388
389impl<T: crate::value::FromQueryResult> QueryAs<T> {
390 pub async fn fetch_all(
400 &self,
401 conn: &mut dyn crate::Connection,
402 ) -> Result<Vec<T>, crate::DbError> {
403 let rows = conn.query(&self.sql).await?;
404 if rows.is_empty() {
405 return Ok(Vec::new());
406 }
407 let expected: Vec<&str> = T::row_desc();
409 if !expected.is_empty() {
410 let actual: Vec<&str> = rows[0].keys().map(|s| s.as_str()).collect();
411 validate_columns(&actual, &expected)?;
412 validate_column_types(&rows[0], T::column_types())?;
414 }
415 rows.into_iter()
416 .map(|row| {
417 T::from_query_result(&row).map_err(|e| crate::DbError::QueryError(e.to_string()))
418 })
419 .collect()
420 }
421
422 pub async fn fetch_one(&self, conn: &mut dyn crate::Connection) -> Result<T, crate::DbError> {
424 let rows = conn.query(&self.sql).await?;
425 match rows.len() {
426 0 => Err(crate::DbError::NotFound(
427 "fetch_one: no rows returned".into(),
428 )),
429 1 => {
430 let row = &rows[0];
431 let expected: Vec<&str> = T::row_desc();
432 if !expected.is_empty() {
433 let actual: Vec<&str> = row.keys().map(|s| s.as_str()).collect();
434 validate_columns(&actual, &expected)?;
435 validate_column_types(row, T::column_types())?;
436 }
437 T::from_query_result(row).map_err(|e| crate::DbError::QueryError(e.to_string()))
438 }
439 n => Err(crate::DbError::QueryError(format!(
440 "fetch_one expected 1 row, got {}",
441 n
442 ))),
443 }
444 }
445
446 pub async fn fetch_optional(
448 &self,
449 conn: &mut dyn crate::Connection,
450 ) -> Result<Option<T>, crate::DbError> {
451 let rows = conn.query(&self.sql).await?;
452 match rows.len() {
453 0 => Ok(None),
454 _ => {
455 let row = &rows[0];
456 let expected: Vec<&str> = T::row_desc();
457 if !expected.is_empty() {
458 let actual: Vec<&str> = row.keys().map(|s| s.as_str()).collect();
459 validate_columns(&actual, &expected)?;
460 validate_column_types(row, T::column_types())?;
461 }
462 T::from_query_result(row)
463 .map_err(|e| crate::DbError::QueryError(e.to_string()))
464 .map(Some)
465 }
466 }
467 }
468}
469
470fn validate_columns(actual: &[&str], expected: &[&str]) -> Result<(), crate::DbError> {
472 use std::collections::HashSet;
473 let actual_set: HashSet<&str> = actual.iter().copied().collect();
474 let expected_set: HashSet<&str> = expected.iter().copied().collect();
475 let missing: Vec<&str> = expected_set.difference(&actual_set).copied().collect();
476 let extra: Vec<&str> = actual_set.difference(&expected_set).copied().collect();
477 if !missing.is_empty() || !extra.is_empty() {
478 return Err(crate::DbError::QueryError(format!(
479 "query_as! column mismatch: struct expects {:?}, DB returned {:?}{}",
480 expected,
481 actual,
482 if missing.is_empty() {
483 String::new()
484 } else {
485 format!(" (missing: {:?})", missing)
486 }
487 )));
488 }
489 Ok(())
490}
491
492fn validate_column_types(
500 row: &std::collections::HashMap<String, crate::value::Value>,
501 expected: &[(&str, &str)],
502) -> Result<(), crate::DbError> {
503 use crate::value::{ColType, Value};
504
505 for (col_name, expected_type) in expected {
506 let value = match row.get(*col_name) {
507 Some(v) => v,
508 None => continue, };
510 if matches!(value, Value::Null) {
511 continue; }
513 let actual_type = value_to_sql_type_name(value);
514 let actual_col = ColType::from_type_name(actual_type);
515 let expected_col = ColType::from_type_name(expected_type);
516 if actual_col != expected_col
517 && actual_col != ColType::Unknown
518 && expected_col != ColType::Unknown
519 {
520 return Err(crate::DbError::QueryError(format!(
521 "query_as! column TYPE mismatch: column '{}' has actual type '{}' (ColType::{:?}), \
522 but struct expects '{}' (ColType::{:?})",
523 col_name, actual_type, actual_col, expected_type, expected_col
524 )));
525 }
526 }
527 Ok(())
528}
529
530fn value_to_sql_type_name(value: &Value) -> &'static str {
532 match value {
533 Value::Null => "NULL",
534 Value::Bool(_) => "BOOLEAN",
535 Value::I8(_) | Value::U8(_) => "TINYINT",
536 Value::I16(_) | Value::U16(_) => "SMALLINT",
537 Value::I32(_) | Value::U32(_) => "INT",
538 Value::I64(_) | Value::U64(_) => "BIGINT",
539 Value::F32(_) => "FLOAT",
540 Value::F64(_) => "DOUBLE",
541 Value::Decimal(_) => "DECIMAL",
542 Value::String(_)
543 | Value::Uuid(_)
544 | Value::Date(_)
545 | Value::DateTime(_)
546 | Value::Time(_)
547 | Value::Json(_) => "VARCHAR",
548 #[cfg(feature = "perf-box-str")]
549 Value::BoxedStr(_) => "VARCHAR",
550 Value::Bytes(_) => "BLOB",
551 Value::Array(_) | Value::Object(_) => "JSON",
552 }
553}
554
555#[cfg(test)]
556mod tests {
557 use super::*;
558
559 #[derive(Debug, Default, PartialEq)]
566 struct UserRow {
567 id: i64,
568 name: String,
569 }
570
571 impl Queryable for UserRow {
572 fn from_values(values: Vec<Value>) -> Result<Self, QueryError> {
573 if values.len() != 2 {
574 return Err(QueryError::ColumnCountMismatch {
575 expected: 2,
576 actual: values.len(),
577 });
578 }
579 let id = values[0].as_i64().ok_or(QueryError::TypeMismatch {
580 column: "0".into(),
581 expected: "i64",
582 })?;
583 let name = values[1]
584 .as_str()
585 .ok_or(QueryError::TypeMismatch {
586 column: "1".into(),
587 expected: "String",
588 })?
589 .to_string();
590 Ok(UserRow { id, name })
591 }
592 }
593
594 impl FromRow for UserRow {
595 fn from_row(row: HashMap<String, Value>) -> Result<Self, QueryError> {
596 let id = row
597 .get("id")
598 .ok_or(QueryError::MissingColumn { column: "id" })?
599 .as_i64()
600 .ok_or(QueryError::TypeMismatch {
601 column: "id".into(),
602 expected: "i64",
603 })?;
604 let name = row
605 .get("name")
606 .ok_or(QueryError::MissingColumn { column: "name" })?
607 .as_str()
608 .ok_or(QueryError::TypeMismatch {
609 column: "name".into(),
610 expected: "String",
611 })?
612 .to_string();
613 Ok(UserRow { id, name })
614 }
615 }
616
617 #[test]
620 fn test_query_error_display() {
621 let e = QueryError::ColumnCountMismatch {
622 expected: 3,
623 actual: 2,
624 };
625 assert!(format!("{}", e).contains("3"));
626 assert!(format!("{}", e).contains("2"));
627
628 let e = QueryError::TypeMismatch {
629 column: "age".into(),
630 expected: "i64",
631 };
632 assert!(format!("{}", e).contains("age"));
633
634 let e = QueryError::MissingColumn { column: "id" };
635 assert!(format!("{}", e).contains("id"));
636
637 let e = QueryError::Custom("custom".into());
638 assert_eq!(format!("{}", e), "custom");
639 }
640
641 #[test]
644 fn test_row_desc_basic() {
645 let desc = RowDesc::new(vec!["id".into(), "name".into(), "age".into()]);
646 assert_eq!(desc.len(), 3);
647 assert!(!desc.is_empty());
648 assert_eq!(desc.index_of("name"), Some(1));
649 assert_eq!(desc.index_of("missing"), None);
650 }
651
652 #[test]
653 fn test_row_desc_empty() {
654 let desc = RowDesc::new(vec![]);
655 assert!(desc.is_empty());
656 assert_eq!(desc.len(), 0);
657 }
658
659 #[test]
662 fn test_user_row_from_values_success() {
663 let row =
664 UserRow::from_values(vec![Value::I64(42), Value::String("Alice".into())]).unwrap();
665 assert_eq!(row.id, 42);
666 assert_eq!(row.name, "Alice");
667 }
668
669 #[test]
670 fn test_user_row_from_values_count_mismatch() {
671 let result = UserRow::from_values(vec![Value::I64(42)]);
672 assert!(matches!(
673 result,
674 Err(QueryError::ColumnCountMismatch {
675 expected: 2,
676 actual: 1
677 })
678 ));
679 }
680
681 #[test]
682 fn test_user_row_from_values_type_mismatch() {
683 let result = UserRow::from_values(vec![
684 Value::String("not_an_int".into()),
685 Value::String("Alice".into()),
686 ]);
687 assert!(matches!(result, Err(QueryError::TypeMismatch { .. })));
688 }
689
690 #[test]
691 fn test_user_row_from_values_with_desc() {
692 let desc = RowDesc::new(vec!["id".into(), "name".into()]);
693 let row =
694 UserRow::from_values_with_desc(vec![Value::I64(1), Value::String("Bob".into())], &desc)
695 .unwrap();
696 assert_eq!(row.id, 1);
697 assert_eq!(row.name, "Bob");
698 }
699
700 #[test]
701 fn test_user_row_from_values_with_desc_mismatch() {
702 let desc = RowDesc::new(vec!["id".into(), "name".into(), "age".into()]);
703 let result =
704 UserRow::from_values_with_desc(vec![Value::I64(1), Value::String("Bob".into())], &desc);
705 assert!(matches!(
706 result,
707 Err(QueryError::ColumnCountMismatch { .. })
708 ));
709 }
710
711 #[test]
714 fn test_user_row_from_row_success() {
715 let mut map = HashMap::new();
716 map.insert("id".into(), Value::I64(99));
717 map.insert("name".into(), Value::String("Charlie".into()));
718 let row = UserRow::from_row(map).unwrap();
719 assert_eq!(row.id, 99);
720 assert_eq!(row.name, "Charlie");
721 }
722
723 #[test]
724 fn test_user_row_from_row_missing_column() {
725 let mut map = HashMap::new();
726 map.insert("id".into(), Value::I64(99));
727 let result = UserRow::from_row(map);
729 assert!(matches!(
730 result,
731 Err(QueryError::MissingColumn { column: "name" })
732 ));
733 }
734
735 #[test]
736 fn test_user_row_from_row_extra_columns_ignored() {
737 let mut map = HashMap::new();
738 map.insert("id".into(), Value::I64(1));
739 map.insert("name".into(), Value::String("X".into()));
740 map.insert("extra".into(), Value::String("ignored".into()));
741 let row = UserRow::from_row(map).unwrap();
742 assert_eq!(row.id, 1);
743 }
744
745 #[test]
748 fn test_value_queryable_single() {
749 let v = Value::from_values(vec![Value::I64(42)]).unwrap();
750 assert_eq!(v.as_i64(), Some(42));
751 }
752
753 #[test]
754 fn test_value_queryable_count_mismatch() {
755 let result = Value::from_values(vec![Value::I64(1), Value::I64(2)]);
756 assert!(matches!(
757 result,
758 Err(QueryError::ColumnCountMismatch { .. })
759 ));
760 }
761
762 #[test]
763 fn test_tuple_2_queryable() {
764 let (a, b) =
765 <(Value, Value)>::from_values(vec![Value::I64(1), Value::String("hello".into())])
766 .unwrap();
767 assert_eq!(a.as_i64(), Some(1));
768 assert_eq!(b.as_str(), Some("hello"));
769 }
770
771 #[test]
772 fn test_tuple_3_queryable() {
773 let (a, b, c) = <(Value, Value, Value)>::from_values(vec![
774 Value::I64(1),
775 Value::String("two".into()),
776 Value::F64(3.5),
777 ])
778 .unwrap();
779 assert_eq!(a.as_i64(), Some(1));
780 assert_eq!(b.as_str(), Some("two"));
781 assert_eq!(c.as_f64(), Some(3.5));
782 }
783
784 #[test]
787 fn test_value_helpers() {
788 assert_eq!(value_as_i64(&Value::I64(42)), Some(42));
789 assert_eq!(value_as_i64(&Value::String("42".into())), Some(42));
790 assert_eq!(value_as_f64(&Value::F64(3.5)), Some(3.5));
791 assert_eq!(
792 value_as_string(&Value::String("hi".into())),
793 Some("hi".into())
794 );
795 assert_eq!(value_as_bool(&Value::Bool(true)), Some(true));
796 }
797
798 #[test]
799 fn test_nullable_helpers() {
800 assert_eq!(value_as_nullable_i64(&Value::Null), None);
801 assert_eq!(value_as_nullable_i64(&Value::I64(42)), Some(42));
802 assert_eq!(value_as_nullable_string(&Value::Null), None);
803 assert_eq!(
804 value_as_nullable_string(&Value::String("hi".into())),
805 Some("hi".into())
806 );
807 }
808
809 #[test]
812 fn test_full_flow_queryable() {
813 let values = vec![Value::I64(1), Value::String("Alice".into())];
815 let row = UserRow::from_values(values).unwrap();
816 assert_eq!(
817 row,
818 UserRow {
819 id: 1,
820 name: "Alice".into()
821 }
822 );
823 }
824
825 #[test]
826 fn test_full_flow_from_row_with_extra_data() {
827 let mut map = HashMap::new();
829 map.insert("id".into(), Value::I64(7));
830 map.insert("name".into(), Value::String("Bob".into()));
831 map.insert("email".into(), Value::String("bob@example.com".into()));
832 map.insert("created_at".into(), Value::String("2026-01-01".into()));
833
834 let row = UserRow::from_row(map).unwrap();
835 assert_eq!(row.id, 7);
836 assert_eq!(row.name, "Bob");
837 }
838
839 #[test]
842 fn test_query_new_and_sql() {
843 let q = Query::new("SELECT id, name FROM users");
844 assert_eq!(q.sql(), "SELECT id, name FROM users");
845 }
846
847 #[test]
848 fn test_query_from_str() {
849 let q = Query::new("SELECT 1");
850 assert_eq!(q.sql(), "SELECT 1");
851 }
852
853 #[test]
854 fn test_query_with_params() {
855 let q = Query::new("SELECT * FROM users WHERE id = ? AND name = ?");
856 assert!(q.sql().contains("WHERE id = ?"));
857 }
858
859 #[test]
862 fn test_validate_columns_match() {
863 let actual = vec!["id", "name"];
865 let expected = vec!["id", "name"];
866 assert!(validate_columns(&actual, &expected).is_ok());
867 }
868
869 #[test]
870 fn test_validate_columns_missing() {
871 let actual = vec!["id"];
873 let expected = vec!["id", "name"];
874 let result = validate_columns(&actual, &expected);
875 assert!(result.is_err());
876 let err = result.unwrap_err().to_string();
877 assert!(err.contains("missing"));
878 }
879
880 #[test]
881 fn test_validate_columns_extra() {
882 let actual = vec!["id", "name", "age"];
884 let expected = vec!["id", "name"];
885 let result = validate_columns(&actual, &expected);
886 assert!(result.is_err());
887 let err = result.unwrap_err().to_string();
888 assert!(err.contains("column mismatch"));
889 }
890
891 #[test]
892 fn test_query_as_new_and_sql() {
893 let q = QueryAs::<UserRow>::new("SELECT id, name FROM users");
894 assert_eq!(q.sql(), "SELECT id, name FROM users");
895 }
896
897 #[test]
898 fn test_query_as_fetch_all_empty() {
899 let _q = QueryAs::<UserRow>::new("SELECT 1");
901 }
902
903 #[test]
904 fn test_query_as_fetch_optional_empty_result() {
905 let _q = QueryAs::<(i64, String)>::new("SELECT id, name FROM t");
907 }
908
909 #[test]
912 fn test_validate_column_types_compatible() {
913 let mut row = HashMap::new();
915 row.insert("id".to_string(), Value::I64(42));
916 row.insert("name".to_string(), Value::String("Alice".into()));
917 let expected = vec![("id", "BIGINT"), ("name", "TEXT")];
918 assert!(validate_column_types(&row, &expected).is_ok());
919 }
920
921 #[test]
922 fn test_validate_column_types_case_insensitive() {
923 let mut row = HashMap::new();
925 row.insert("id".to_string(), Value::I64(1));
926 let expected = vec![("id", "bigint")];
927 assert!(validate_column_types(&row, &expected).is_ok());
928 }
929
930 #[test]
931 fn test_validate_column_types_null_skipped() {
932 let mut row = HashMap::new();
934 row.insert("id".to_string(), Value::Null);
935 let expected = vec![("id", "TEXT")]; assert!(validate_column_types(&row, &expected).is_ok());
937 }
938
939 #[test]
940 fn test_validate_column_types_missing_column_skipped() {
941 let row = HashMap::new();
943 let expected = vec![("missing", "BIGINT")];
944 assert!(validate_column_types(&row, &expected).is_ok());
945 }
946
947 #[test]
948 fn test_validate_column_types_type_mismatch() {
949 let mut row = HashMap::new();
951 row.insert("id".to_string(), Value::I64(42));
952 let expected = vec![("id", "TEXT")];
953 let result = validate_column_types(&row, &expected);
954 assert!(result.is_err());
955 let err = result.unwrap_err().to_string();
956 assert!(err.contains("TYPE mismatch"));
957 assert!(err.contains("id"));
958 }
959
960 #[test]
961 fn test_validate_column_types_bool_vs_int_mismatch() {
962 let mut row = HashMap::new();
964 row.insert("flag".to_string(), Value::Bool(true));
965 let expected = vec![("flag", "INT")];
966 let result = validate_column_types(&row, &expected);
967 assert!(result.is_err());
968 }
969
970 #[test]
971 fn test_validate_column_types_unknown_actual_skipped() {
972 let mut row = HashMap::new();
974 row.insert("data".to_string(), Value::String("x".into()));
975 let expected = vec![("data", "UNKNOWN_CUSTOM_TYPE")];
976 assert!(validate_column_types(&row, &expected).is_ok());
978 }
979
980 #[test]
981 fn test_value_to_sql_type_name_coverage() {
982 use crate::value::Value;
984 assert_eq!(super::value_to_sql_type_name(&Value::Bool(true)), "BOOLEAN");
985 assert_eq!(super::value_to_sql_type_name(&Value::I64(1)), "BIGINT");
986 assert_eq!(super::value_to_sql_type_name(&Value::F64(1.0)), "DOUBLE");
987 assert_eq!(
988 super::value_to_sql_type_name(&Value::Decimal("1.0".into())),
989 "DECIMAL"
990 );
991 assert_eq!(
992 super::value_to_sql_type_name(&Value::Bytes(vec![1, 2])),
993 "BLOB"
994 );
995 assert_eq!(super::value_to_sql_type_name(&Value::Null), "NULL");
996 }
997}