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 Value::Bytes(_) => "BLOB",
549 Value::Array(_) | Value::Object(_) => "JSON",
550 }
551}
552
553#[cfg(test)]
554mod tests {
555 use super::*;
556
557 #[derive(Debug, Default, PartialEq)]
560 struct UserRow {
561 id: i64,
562 name: String,
563 }
564
565 impl Queryable for UserRow {
566 fn from_values(values: Vec<Value>) -> Result<Self, QueryError> {
567 if values.len() != 2 {
568 return Err(QueryError::ColumnCountMismatch {
569 expected: 2,
570 actual: values.len(),
571 });
572 }
573 let id = values[0].as_i64().ok_or(QueryError::TypeMismatch {
574 column: "0".into(),
575 expected: "i64",
576 })?;
577 let name = values[1]
578 .as_str()
579 .ok_or(QueryError::TypeMismatch {
580 column: "1".into(),
581 expected: "String",
582 })?
583 .to_string();
584 Ok(UserRow { id, name })
585 }
586 }
587
588 impl FromRow for UserRow {
589 fn from_row(row: HashMap<String, Value>) -> Result<Self, QueryError> {
590 let id = row
591 .get("id")
592 .ok_or(QueryError::MissingColumn { column: "id" })?
593 .as_i64()
594 .ok_or(QueryError::TypeMismatch {
595 column: "id".into(),
596 expected: "i64",
597 })?;
598 let name = row
599 .get("name")
600 .ok_or(QueryError::MissingColumn { column: "name" })?
601 .as_str()
602 .ok_or(QueryError::TypeMismatch {
603 column: "name".into(),
604 expected: "String",
605 })?
606 .to_string();
607 Ok(UserRow { id, name })
608 }
609 }
610
611 #[test]
614 fn test_query_error_display() {
615 let e = QueryError::ColumnCountMismatch {
616 expected: 3,
617 actual: 2,
618 };
619 assert!(format!("{}", e).contains("3"));
620 assert!(format!("{}", e).contains("2"));
621
622 let e = QueryError::TypeMismatch {
623 column: "age".into(),
624 expected: "i64",
625 };
626 assert!(format!("{}", e).contains("age"));
627
628 let e = QueryError::MissingColumn { column: "id" };
629 assert!(format!("{}", e).contains("id"));
630
631 let e = QueryError::Custom("custom".into());
632 assert_eq!(format!("{}", e), "custom");
633 }
634
635 #[test]
638 fn test_row_desc_basic() {
639 let desc = RowDesc::new(vec!["id".into(), "name".into(), "age".into()]);
640 assert_eq!(desc.len(), 3);
641 assert!(!desc.is_empty());
642 assert_eq!(desc.index_of("name"), Some(1));
643 assert_eq!(desc.index_of("missing"), None);
644 }
645
646 #[test]
647 fn test_row_desc_empty() {
648 let desc = RowDesc::new(vec![]);
649 assert!(desc.is_empty());
650 assert_eq!(desc.len(), 0);
651 }
652
653 #[test]
656 fn test_user_row_from_values_success() {
657 let row =
658 UserRow::from_values(vec![Value::I64(42), Value::String("Alice".into())]).unwrap();
659 assert_eq!(row.id, 42);
660 assert_eq!(row.name, "Alice");
661 }
662
663 #[test]
664 fn test_user_row_from_values_count_mismatch() {
665 let result = UserRow::from_values(vec![Value::I64(42)]);
666 assert!(matches!(
667 result,
668 Err(QueryError::ColumnCountMismatch {
669 expected: 2,
670 actual: 1
671 })
672 ));
673 }
674
675 #[test]
676 fn test_user_row_from_values_type_mismatch() {
677 let result = UserRow::from_values(vec![
678 Value::String("not_an_int".into()),
679 Value::String("Alice".into()),
680 ]);
681 assert!(matches!(result, Err(QueryError::TypeMismatch { .. })));
682 }
683
684 #[test]
685 fn test_user_row_from_values_with_desc() {
686 let desc = RowDesc::new(vec!["id".into(), "name".into()]);
687 let row =
688 UserRow::from_values_with_desc(vec![Value::I64(1), Value::String("Bob".into())], &desc)
689 .unwrap();
690 assert_eq!(row.id, 1);
691 assert_eq!(row.name, "Bob");
692 }
693
694 #[test]
695 fn test_user_row_from_values_with_desc_mismatch() {
696 let desc = RowDesc::new(vec!["id".into(), "name".into(), "age".into()]);
697 let result =
698 UserRow::from_values_with_desc(vec![Value::I64(1), Value::String("Bob".into())], &desc);
699 assert!(matches!(
700 result,
701 Err(QueryError::ColumnCountMismatch { .. })
702 ));
703 }
704
705 #[test]
708 fn test_user_row_from_row_success() {
709 let mut map = HashMap::new();
710 map.insert("id".into(), Value::I64(99));
711 map.insert("name".into(), Value::String("Charlie".into()));
712 let row = UserRow::from_row(map).unwrap();
713 assert_eq!(row.id, 99);
714 assert_eq!(row.name, "Charlie");
715 }
716
717 #[test]
718 fn test_user_row_from_row_missing_column() {
719 let mut map = HashMap::new();
720 map.insert("id".into(), Value::I64(99));
721 let result = UserRow::from_row(map);
723 assert!(matches!(
724 result,
725 Err(QueryError::MissingColumn { column: "name" })
726 ));
727 }
728
729 #[test]
730 fn test_user_row_from_row_extra_columns_ignored() {
731 let mut map = HashMap::new();
732 map.insert("id".into(), Value::I64(1));
733 map.insert("name".into(), Value::String("X".into()));
734 map.insert("extra".into(), Value::String("ignored".into()));
735 let row = UserRow::from_row(map).unwrap();
736 assert_eq!(row.id, 1);
737 }
738
739 #[test]
742 fn test_value_queryable_single() {
743 let v = Value::from_values(vec![Value::I64(42)]).unwrap();
744 assert_eq!(v.as_i64(), Some(42));
745 }
746
747 #[test]
748 fn test_value_queryable_count_mismatch() {
749 let result = Value::from_values(vec![Value::I64(1), Value::I64(2)]);
750 assert!(matches!(
751 result,
752 Err(QueryError::ColumnCountMismatch { .. })
753 ));
754 }
755
756 #[test]
757 fn test_tuple_2_queryable() {
758 let (a, b) =
759 <(Value, Value)>::from_values(vec![Value::I64(1), Value::String("hello".into())])
760 .unwrap();
761 assert_eq!(a.as_i64(), Some(1));
762 assert_eq!(b.as_str(), Some("hello"));
763 }
764
765 #[test]
766 fn test_tuple_3_queryable() {
767 let (a, b, c) = <(Value, Value, Value)>::from_values(vec![
768 Value::I64(1),
769 Value::String("two".into()),
770 Value::F64(3.5),
771 ])
772 .unwrap();
773 assert_eq!(a.as_i64(), Some(1));
774 assert_eq!(b.as_str(), Some("two"));
775 assert_eq!(c.as_f64(), Some(3.5));
776 }
777
778 #[test]
781 fn test_value_helpers() {
782 assert_eq!(value_as_i64(&Value::I64(42)), Some(42));
783 assert_eq!(value_as_i64(&Value::String("42".into())), Some(42));
784 assert_eq!(value_as_f64(&Value::F64(3.5)), Some(3.5));
785 assert_eq!(
786 value_as_string(&Value::String("hi".into())),
787 Some("hi".into())
788 );
789 assert_eq!(value_as_bool(&Value::Bool(true)), Some(true));
790 }
791
792 #[test]
793 fn test_nullable_helpers() {
794 assert_eq!(value_as_nullable_i64(&Value::Null), None);
795 assert_eq!(value_as_nullable_i64(&Value::I64(42)), Some(42));
796 assert_eq!(value_as_nullable_string(&Value::Null), None);
797 assert_eq!(
798 value_as_nullable_string(&Value::String("hi".into())),
799 Some("hi".into())
800 );
801 }
802
803 #[test]
806 fn test_full_flow_queryable() {
807 let values = vec![Value::I64(1), Value::String("Alice".into())];
809 let row = UserRow::from_values(values).unwrap();
810 assert_eq!(
811 row,
812 UserRow {
813 id: 1,
814 name: "Alice".into()
815 }
816 );
817 }
818
819 #[test]
820 fn test_full_flow_from_row_with_extra_data() {
821 let mut map = HashMap::new();
823 map.insert("id".into(), Value::I64(7));
824 map.insert("name".into(), Value::String("Bob".into()));
825 map.insert("email".into(), Value::String("bob@example.com".into()));
826 map.insert("created_at".into(), Value::String("2026-01-01".into()));
827
828 let row = UserRow::from_row(map).unwrap();
829 assert_eq!(row.id, 7);
830 assert_eq!(row.name, "Bob");
831 }
832
833 #[test]
836 fn test_query_new_and_sql() {
837 let q = Query::new("SELECT id, name FROM users");
838 assert_eq!(q.sql(), "SELECT id, name FROM users");
839 }
840
841 #[test]
842 fn test_query_from_str() {
843 let q = Query::new("SELECT 1");
844 assert_eq!(q.sql(), "SELECT 1");
845 }
846
847 #[test]
848 fn test_query_with_params() {
849 let q = Query::new("SELECT * FROM users WHERE id = ? AND name = ?");
850 assert!(q.sql().contains("WHERE id = ?"));
851 }
852
853 #[test]
856 fn test_validate_columns_match() {
857 let actual = vec!["id", "name"];
859 let expected = vec!["id", "name"];
860 assert!(validate_columns(&actual, &expected).is_ok());
861 }
862
863 #[test]
864 fn test_validate_columns_missing() {
865 let actual = vec!["id"];
867 let expected = vec!["id", "name"];
868 let result = validate_columns(&actual, &expected);
869 assert!(result.is_err());
870 let err = result.unwrap_err().to_string();
871 assert!(err.contains("missing"));
872 }
873
874 #[test]
875 fn test_validate_columns_extra() {
876 let actual = vec!["id", "name", "age"];
878 let expected = vec!["id", "name"];
879 let result = validate_columns(&actual, &expected);
880 assert!(result.is_err());
881 let err = result.unwrap_err().to_string();
882 assert!(err.contains("column mismatch"));
883 }
884
885 #[test]
886 fn test_query_as_new_and_sql() {
887 let q = QueryAs::<UserRow>::new("SELECT id, name FROM users");
888 assert_eq!(q.sql(), "SELECT id, name FROM users");
889 }
890
891 #[test]
892 fn test_query_as_fetch_all_empty() {
893 let _q = QueryAs::<UserRow>::new("SELECT 1");
895 }
896
897 #[test]
898 fn test_query_as_fetch_optional_empty_result() {
899 let _q = QueryAs::<(i64, String)>::new("SELECT id, name FROM t");
901 }
902
903 #[test]
906 fn test_validate_column_types_compatible() {
907 let mut row = HashMap::new();
909 row.insert("id".to_string(), Value::I64(42));
910 row.insert("name".to_string(), Value::String("Alice".into()));
911 let expected = vec![("id", "BIGINT"), ("name", "TEXT")];
912 assert!(validate_column_types(&row, &expected).is_ok());
913 }
914
915 #[test]
916 fn test_validate_column_types_case_insensitive() {
917 let mut row = HashMap::new();
919 row.insert("id".to_string(), Value::I64(1));
920 let expected = vec![("id", "bigint")];
921 assert!(validate_column_types(&row, &expected).is_ok());
922 }
923
924 #[test]
925 fn test_validate_column_types_null_skipped() {
926 let mut row = HashMap::new();
928 row.insert("id".to_string(), Value::Null);
929 let expected = vec![("id", "TEXT")]; assert!(validate_column_types(&row, &expected).is_ok());
931 }
932
933 #[test]
934 fn test_validate_column_types_missing_column_skipped() {
935 let row = HashMap::new();
937 let expected = vec![("missing", "BIGINT")];
938 assert!(validate_column_types(&row, &expected).is_ok());
939 }
940
941 #[test]
942 fn test_validate_column_types_type_mismatch() {
943 let mut row = HashMap::new();
945 row.insert("id".to_string(), Value::I64(42));
946 let expected = vec![("id", "TEXT")];
947 let result = validate_column_types(&row, &expected);
948 assert!(result.is_err());
949 let err = result.unwrap_err().to_string();
950 assert!(err.contains("TYPE mismatch"));
951 assert!(err.contains("id"));
952 }
953
954 #[test]
955 fn test_validate_column_types_bool_vs_int_mismatch() {
956 let mut row = HashMap::new();
958 row.insert("flag".to_string(), Value::Bool(true));
959 let expected = vec![("flag", "INT")];
960 let result = validate_column_types(&row, &expected);
961 assert!(result.is_err());
962 }
963
964 #[test]
965 fn test_validate_column_types_unknown_actual_skipped() {
966 let mut row = HashMap::new();
968 row.insert("data".to_string(), Value::String("x".into()));
969 let expected = vec![("data", "UNKNOWN_CUSTOM_TYPE")];
970 assert!(validate_column_types(&row, &expected).is_ok());
972 }
973
974 #[test]
975 fn test_value_to_sql_type_name_coverage() {
976 use crate::value::Value;
978 assert_eq!(super::value_to_sql_type_name(&Value::Bool(true)), "BOOLEAN");
979 assert_eq!(super::value_to_sql_type_name(&Value::I64(1)), "BIGINT");
980 assert_eq!(super::value_to_sql_type_name(&Value::F64(1.0)), "DOUBLE");
981 assert_eq!(
982 super::value_to_sql_type_name(&Value::Decimal("1.0".into())),
983 "DECIMAL"
984 );
985 assert_eq!(
986 super::value_to_sql_type_name(&Value::Bytes(vec![1, 2])),
987 "BLOB"
988 );
989 assert_eq!(super::value_to_sql_type_name(&Value::Null), "NULL");
990 }
991}