1use std::borrow::Cow;
6use std::fmt;
7
8use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
12#[non_exhaustive]
13pub enum Value {
14 #[default]
16 Null,
17
18 Bool(bool),
20
21 I8(i8),
23
24 I16(i16),
26
27 I32(i32),
29
30 I64(i64),
32
33 U8(u8),
35
36 U16(u16),
38
39 U32(u32),
41
42 U64(u64),
44
45 F32(f32),
47
48 F64(f64),
50
51 Decimal(String),
53
54 String(String),
56
57 Bytes(Vec<u8>),
59
60 Uuid(String),
62
63 Date(String),
65
66 DateTime(String),
68
69 Time(String),
71
72 Json(String),
74
75 Array(Vec<Value>),
77
78 Object(std::collections::HashMap<String, Value>),
80
81 #[cfg(feature = "perf-box-str")]
86 BoxedStr(Box<str>),
87}
88
89pub trait FromQueryResult: Sized {
117 fn from_value(_value: &Value) -> Result<Self, String> {
123 Err(
124 "from_value not implemented for this type; use from_query_result for structs"
125 .to_string(),
126 )
127 }
128
129 fn from_row(row: &std::collections::HashMap<String, Value>) -> Result<Self, String> {
131 Self::from_query_result(row)
132 }
133
134 fn from_query_result(_row: &std::collections::HashMap<String, Value>) -> Result<Self, String> {
136 Err("from_query_result not implemented for this type".to_string())
137 }
138
139 fn row_desc() -> Vec<&'static str> {
144 Vec::new()
145 }
146
147 fn column_types() -> &'static [(&'static str, &'static str)] {
156 &[]
157 }
158}
159
160pub trait ColumnTrait {
173 fn as_str(&self) -> &'static str;
175 fn all() -> Vec<Self>
177 where
178 Self: Sized;
179}
180
181pub const fn __sz_orm_const_str_eq(a: &str, b: &str) -> bool {
186 if a.len() != b.len() {
187 return false;
188 }
189 let ab = a.as_bytes();
190 let bb = b.as_bytes();
191 let mut i = 0;
192 while i < ab.len() {
193 if ab[i] != bb[i] {
194 return false;
195 }
196 i += 1;
197 }
198 true
199}
200
201pub const fn __sz_orm_const_types_compatible(
207 actual_db_type: &str,
208 expected_rust_type: &str,
209) -> bool {
210 const fn ci_eq(t: &str, pat: &[u8]) -> bool {
212 let tb = t.as_bytes();
213 if tb.len() != pat.len() {
214 return false;
215 }
216 let mut i = 0;
217 while i < tb.len() {
218 let c = tb[i];
219 let u = if c >= b'a' && c <= b'z' { c - 32 } else { c };
220 if u != pat[i] {
221 return false;
222 }
223 i += 1;
224 }
225 true
226 }
227 const fn classify(t: &str) -> u8 {
228 if ci_eq(t, b"BOOLEAN") || ci_eq(t, b"BOOL") {
229 1
230 } else if ci_eq(t, b"TINYINT") {
231 2
232 } else if ci_eq(t, b"SMALLINT") || ci_eq(t, b"INT2") {
233 3
234 } else if ci_eq(t, b"INT")
235 || ci_eq(t, b"INT4")
236 || ci_eq(t, b"OID")
237 || ci_eq(t, b"MEDIUMINT")
238 || ci_eq(t, b"INTEGER")
239 {
240 4
241 } else if ci_eq(t, b"BIGINT") || ci_eq(t, b"INT8") {
242 5
243 } else if ci_eq(t, b"TINYINT UNSIGNED") {
244 6
245 } else if ci_eq(t, b"SMALLINT UNSIGNED") {
246 7
247 } else if ci_eq(t, b"INT UNSIGNED") || ci_eq(t, b"MEDIUMINT UNSIGNED") {
248 8
249 } else if ci_eq(t, b"BIGINT UNSIGNED") {
250 9
251 } else if ci_eq(t, b"FLOAT") || ci_eq(t, b"FLOAT4") || ci_eq(t, b"REAL") {
252 10
253 } else if ci_eq(t, b"DOUBLE") || ci_eq(t, b"FLOAT8") {
254 11
255 } else if ci_eq(t, b"DECIMAL")
256 || ci_eq(t, b"NUMERIC")
257 || ci_eq(t, b"NEWDECIMAL")
258 || ci_eq(t, b"MONEY")
259 {
260 12
261 } else if ci_eq(t, b"TEXT")
262 || ci_eq(t, b"VARCHAR")
263 || ci_eq(t, b"CHAR")
264 || ci_eq(t, b"NAME")
265 || ci_eq(t, b"CLOB")
266 || ci_eq(t, b"STRING")
267 {
268 13
269 } else if ci_eq(t, b"BLOB")
270 || ci_eq(t, b"BYTEA")
271 || ci_eq(t, b"BINARY")
272 || ci_eq(t, b"VARBINARY")
273 {
274 14
275 } else if ci_eq(t, b"DATE") {
276 15
277 } else if ci_eq(t, b"DATETIME") || ci_eq(t, b"TIMESTAMP") || ci_eq(t, b"TIMESTAMPTZ") {
278 16
279 } else if ci_eq(t, b"TIME") || ci_eq(t, b"TIMETZ") {
280 17
281 } else if ci_eq(t, b"JSON") || ci_eq(t, b"JSONB") {
282 18
283 } else if ci_eq(t, b"UUID") {
284 19
285 } else {
286 0
287 }
288 }
289 let a = classify(actual_db_type);
290 let b = classify(expected_rust_type);
291 a == b || a == 0 || b == 0
292}
293
294macro_rules! impl_from_query_result_int {
297 ($t:ty, $variant:ident) => {
298 impl FromQueryResult for $t {
299 fn from_value(value: &Value) -> Result<Self, String> {
300 match value {
301 Value::$variant(n) => Ok(*n as $t),
304 Value::Null => Err("NULL value cannot be converted to integer".to_string()),
305 other => Err(format!("cannot convert {:?} to {}", other, stringify!($t))),
306 }
307 }
308 }
309 };
310}
311
312impl_from_query_result_int!(i64, I64);
313impl_from_query_result_int!(i32, I32);
314impl_from_query_result_int!(i16, I16);
315impl_from_query_result_int!(i8, I8);
316impl_from_query_result_int!(u64, U64);
317impl_from_query_result_int!(u32, U32);
318impl_from_query_result_int!(u16, U16);
319impl_from_query_result_int!(u8, U8);
320
321macro_rules! impl_from_query_result_float {
322 ($t:ty, $variant:ident) => {
323 impl FromQueryResult for $t {
324 fn from_value(value: &Value) -> Result<Self, String> {
325 match value {
326 Value::$variant(n) => Ok(*n as $t),
327 Value::Null => Err("NULL value cannot be converted to float".to_string()),
328 other => Err(format!("cannot convert {:?} to {}", other, stringify!($t))),
329 }
330 }
331 }
332 };
333}
334
335impl_from_query_result_float!(f64, F64);
336impl_from_query_result_float!(f32, F32);
337
338impl FromQueryResult for bool {
339 fn from_value(value: &Value) -> Result<Self, String> {
340 match value {
341 Value::Bool(b) => Ok(*b),
342 Value::I64(n) => Ok(*n != 0),
343 Value::Null => Err("NULL value cannot be converted to bool".to_string()),
344 other => Err(format!("cannot convert {:?} to bool", other)),
345 }
346 }
347}
348
349impl FromQueryResult for String {
350 fn from_value(value: &Value) -> Result<Self, String> {
351 match value {
352 Value::String(s) => Ok(s.clone()),
353 Value::Decimal(s) => Ok(s.clone()),
354 Value::Uuid(s) => Ok(s.clone()),
355 Value::Date(s) => Ok(s.clone()),
356 Value::DateTime(s) => Ok(s.clone()),
357 Value::Time(s) => Ok(s.clone()),
358 Value::Json(s) => Ok(s.clone()),
359 Value::Null => Err("NULL value cannot be converted to String".to_string()),
360 other => Err(format!("cannot convert {:?} to String", other)),
361 }
362 }
363}
364
365impl<T: FromQueryResult> FromQueryResult for Option<T> {
366 fn from_value(value: &Value) -> Result<Self, String> {
367 match value {
368 Value::Null => Ok(None),
369 other => T::from_value(other).map(Some),
370 }
371 }
372}
373
374impl FromQueryResult for () {
376 fn from_value(_value: &Value) -> Result<Self, String> {
377 Ok(())
378 }
379}
380
381pub fn rows_to<T: FromQueryResult>(rows: &crate::pool::QueryRows) -> Result<Vec<T>, String> {
393 rows.iter().map(T::from_query_result).collect()
394}
395
396impl Value {
397 pub fn is_null(&self) -> bool {
399 matches!(self, Value::Null)
400 }
401
402 pub fn is_bool(&self) -> bool {
404 matches!(self, Value::Bool(_))
405 }
406
407 pub fn is_i64(&self) -> bool {
409 matches!(self, Value::I64(_))
410 }
411
412 pub fn is_f64(&self) -> bool {
414 matches!(self, Value::F64(_))
415 }
416
417 pub fn is_string(&self) -> bool {
419 matches!(self, Value::String(_))
420 }
421
422 pub fn is_bytes(&self) -> bool {
424 matches!(self, Value::Bytes(_))
425 }
426
427 pub fn is_object(&self) -> bool {
429 matches!(self, Value::Object(_))
430 }
431
432 pub fn from_map(map: std::collections::HashMap<String, Value>) -> Self {
434 Value::Object(map)
435 }
436
437 pub fn as_str(&self) -> Option<&str> {
439 match self {
440 Value::String(s) => Some(s),
441 Value::Decimal(s) => Some(s),
442 _ => None,
443 }
444 }
445
446 pub fn as_i64(&self) -> Option<i64> {
450 match self {
451 Value::I8(v) => Some(*v as i64),
452 Value::I16(v) => Some(*v as i64),
453 Value::I32(v) => Some(*v as i64),
454 Value::I64(v) => Some(*v),
455 Value::U8(v) => Some(*v as i64),
456 Value::U16(v) => Some(*v as i64),
457 Value::U32(v) => Some(*v as i64),
458 Value::U64(v) => i64::try_from(*v).ok(),
459 Value::F32(v) => Some(*v as i64),
460 Value::F64(v) => Some(*v as i64),
461 Value::Bool(v) => Some(if *v { 1 } else { 0 }),
462 Value::String(s) => s.parse::<i64>().ok(),
463 Value::Decimal(s) => s.parse::<i64>().ok(),
464 _ => None,
465 }
466 }
467
468 pub fn as_f64(&self) -> Option<f64> {
471 match self {
472 Value::F32(v) => Some(*v as f64),
473 Value::F64(v) => Some(*v),
474 Value::I8(v) => Some(*v as f64),
475 Value::I16(v) => Some(*v as f64),
476 Value::I32(v) => Some(*v as f64),
477 Value::I64(v) => Some(*v as f64),
478 Value::U8(v) => Some(*v as f64),
479 Value::U16(v) => Some(*v as f64),
480 Value::U32(v) => Some(*v as f64),
481 Value::U64(v) => Some(*v as f64),
482 Value::Bool(v) => Some(if *v { 1.0 } else { 0.0 }),
483 Value::Decimal(s) => s.parse::<f64>().ok(),
484 _ => None,
485 }
486 }
487
488 pub fn as_bool(&self) -> Option<bool> {
491 match self {
492 Value::Bool(v) => Some(*v),
493 Value::I8(v) => Some(*v != 0),
494 Value::I16(v) => Some(*v != 0),
495 Value::I32(v) => Some(*v != 0),
496 Value::I64(v) => Some(*v != 0),
497 Value::U8(v) => Some(*v != 0),
498 Value::U16(v) => Some(*v != 0),
499 Value::U32(v) => Some(*v != 0),
500 Value::U64(v) => Some(*v != 0),
501 Value::F32(v) => Some(*v != 0.0),
502 Value::F64(v) => Some(*v != 0.0),
503 Value::String(s) => match s.to_lowercase().as_str() {
504 "1" | "true" | "yes" | "on" => Some(true),
505 "0" | "false" | "no" | "off" => Some(false),
506 _ => None,
507 },
508 Value::Null => Some(false),
509 _ => None,
510 }
511 }
512
513 pub fn as_bytes(&self) -> Option<&[u8]> {
516 match self {
517 Value::Bytes(v) => Some(v),
518 Value::String(s) => Some(s.as_bytes()),
519 _ => None,
520 }
521 }
522
523 pub fn to_param(&self) -> Cow<'_, str> {
533 match self {
534 Value::Null => Cow::Borrowed("NULL"),
535 Value::Bool(b) => Cow::Owned(if *b { "TRUE" } else { "FALSE" }.to_string()),
536 Value::I8(v) => Cow::Owned(v.to_string()),
537 Value::I16(v) => Cow::Owned(v.to_string()),
538 Value::I32(v) => Cow::Owned(v.to_string()),
539 Value::I64(v) => Cow::Owned(v.to_string()),
540 Value::U8(v) => Cow::Owned(v.to_string()),
541 Value::U16(v) => Cow::Owned(v.to_string()),
542 Value::U32(v) => Cow::Owned(v.to_string()),
543 Value::U64(v) => Cow::Owned(v.to_string()),
544 Value::F32(v) => Cow::Owned(v.to_string()),
545 Value::F64(v) => Cow::Owned(v.to_string()),
546 Value::Decimal(s) => Cow::Owned(s.clone()),
547 Value::String(s) => Cow::Owned(format!("'{}'", escape_string(s))),
548 #[cfg(feature = "perf-box-str")]
549 Value::BoxedStr(s) => Cow::Owned(format!("'{}'", escape_string(s))),
550 Value::Bytes(b) => Cow::Owned(format!("X'{}'", hex_encode(b))),
551 Value::Uuid(s) => Cow::Owned(format!("'{}'", escape_string(s))),
552 Value::Date(s) => Cow::Owned(format!("'{}'", escape_string(s))),
553 Value::DateTime(s) => Cow::Owned(format!("'{}'", escape_string(s))),
554 Value::Time(s) => Cow::Owned(format!("'{}'", escape_string(s))),
555 Value::Json(s) => Cow::Owned(format!("'{}'", escape_string(s))),
556 Value::Array(arr) => {
557 let params: Vec<String> = arr.iter().map(|v| v.to_param().into_owned()).collect();
558 Cow::Owned(format!("({})", params.join(", ")))
559 }
560 Value::Object(_) => Cow::Borrowed("NULL"),
561 }
562 }
563
564 pub fn to_param_with_dialect(&self, dialect: &dyn crate::dialect::Dialect) -> Cow<'_, str> {
582 match self {
583 Value::Null => Cow::Borrowed("NULL"),
584 Value::Bool(b) => Cow::Owned(if *b { "TRUE" } else { "FALSE" }.to_string()),
585 Value::I8(v) => Cow::Owned(v.to_string()),
586 Value::I16(v) => Cow::Owned(v.to_string()),
587 Value::I32(v) => Cow::Owned(v.to_string()),
588 Value::I64(v) => Cow::Owned(v.to_string()),
589 Value::U8(v) => Cow::Owned(v.to_string()),
590 Value::U16(v) => Cow::Owned(v.to_string()),
591 Value::U32(v) => Cow::Owned(v.to_string()),
592 Value::U64(v) => Cow::Owned(v.to_string()),
593 Value::F32(v) => Cow::Owned(v.to_string()),
594 Value::F64(v) => Cow::Owned(v.to_string()),
595 Value::Decimal(s) => Cow::Owned(s.clone()),
596 Value::String(s) => Cow::Owned(format!("'{}'", dialect.escape_string(s))),
597 #[cfg(feature = "perf-box-str")]
598 Value::BoxedStr(s) => Cow::Owned(format!("'{}'", dialect.escape_string(s))),
599 Value::Bytes(b) => Cow::Owned(format!("X'{}'", hex_encode(b))),
600 Value::Uuid(s) => Cow::Owned(format!("'{}'", dialect.escape_string(s))),
601 Value::Date(s) => Cow::Owned(format!("'{}'", dialect.escape_string(s))),
602 Value::DateTime(s) => Cow::Owned(format!("'{}'", dialect.escape_string(s))),
603 Value::Time(s) => Cow::Owned(format!("'{}'", dialect.escape_string(s))),
604 Value::Json(s) => Cow::Owned(format!("'{}'", dialect.escape_string(s))),
605 Value::Array(arr) => {
606 let params: Vec<String> = arr
607 .iter()
608 .map(|v| v.to_param_with_dialect(dialect).into_owned())
609 .collect();
610 Cow::Owned(format!("({})", params.join(", ")))
611 }
612 Value::Object(_) => Cow::Borrowed("NULL"),
613 }
614 }
615
616 pub fn from<T: Into<Value>>(v: T) -> Self {
618 v.into()
619 }
620
621 #[cfg(feature = "perf-box-str")]
626 pub fn boxed_str(s: impl Into<Box<str>>) -> Self {
627 Value::BoxedStr(s.into())
628 }
629}
630
631impl fmt::Display for Value {
632 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
633 match self {
634 Value::Null => write!(f, "NULL"),
635 Value::Bool(b) => write!(f, "{}", b),
636 Value::I8(v) => write!(f, "{}", v),
637 Value::I16(v) => write!(f, "{}", v),
638 Value::I32(v) => write!(f, "{}", v),
639 Value::I64(v) => write!(f, "{}", v),
640 Value::U8(v) => write!(f, "{}", v),
641 Value::U16(v) => write!(f, "{}", v),
642 Value::U32(v) => write!(f, "{}", v),
643 Value::U64(v) => write!(f, "{}", v),
644 Value::F32(v) => write!(f, "{}", v),
645 Value::F64(v) => write!(f, "{}", v),
646 Value::Decimal(v) => write!(f, "{}", v),
647 Value::String(v) => write!(f, "'{}'", v),
648 #[cfg(feature = "perf-box-str")]
649 Value::BoxedStr(v) => write!(f, "'{}'", v),
650 Value::Bytes(v) => write!(f, "X'{}'", hex_encode(v)),
651 Value::Uuid(v) => write!(f, "'{}'", v),
652 Value::Date(v) => write!(f, "'{}'", v),
653 Value::DateTime(v) => write!(f, "'{}'", v),
654 Value::Time(v) => write!(f, "'{}'", v),
655 Value::Json(v) => write!(f, "'{}'", v),
656 Value::Array(v) => {
657 let items: Vec<String> = v.iter().map(|i| format!("{}", i)).collect();
658 write!(f, "({})", items.join(", "))
659 }
660 Value::Object(map) => {
661 let items: Vec<String> = map.iter().map(|(k, v)| format!("{}: {}", k, v)).collect();
662 write!(f, "{{{}}}", items.join(", "))
663 }
664 }
665 }
666}
667
668impl From<()> for Value {
669 fn from(_: ()) -> Self {
670 Value::Null
671 }
672}
673
674impl From<bool> for Value {
675 fn from(v: bool) -> Self {
676 Value::Bool(v)
677 }
678}
679
680impl From<i8> for Value {
681 fn from(v: i8) -> Self {
682 Value::I8(v)
683 }
684}
685
686impl From<i16> for Value {
687 fn from(v: i16) -> Self {
688 Value::I16(v)
689 }
690}
691
692impl From<i32> for Value {
693 fn from(v: i32) -> Self {
694 Value::I32(v)
695 }
696}
697
698impl From<i64> for Value {
699 fn from(v: i64) -> Self {
700 Value::I64(v)
701 }
702}
703
704impl From<u8> for Value {
705 fn from(v: u8) -> Self {
706 Value::U8(v)
707 }
708}
709
710impl From<u16> for Value {
711 fn from(v: u16) -> Self {
712 Value::U16(v)
713 }
714}
715
716impl From<u32> for Value {
717 fn from(v: u32) -> Self {
718 Value::U32(v)
719 }
720}
721
722impl From<u64> for Value {
723 fn from(v: u64) -> Self {
724 Value::U64(v)
725 }
726}
727
728impl From<f32> for Value {
729 fn from(v: f32) -> Self {
730 Value::F32(v)
731 }
732}
733
734impl From<f64> for Value {
735 fn from(v: f64) -> Self {
736 Value::F64(v)
737 }
738}
739
740impl From<String> for Value {
741 fn from(v: String) -> Self {
742 Value::String(v)
743 }
744}
745
746impl From<&str> for Value {
747 fn from(v: &str) -> Self {
748 Value::String(v.to_string())
749 }
750}
751
752impl From<Vec<u8>> for Value {
753 fn from(v: Vec<u8>) -> Self {
754 Value::Bytes(v)
755 }
756}
757
758impl From<&[u8]> for Value {
759 fn from(v: &[u8]) -> Self {
760 Value::Bytes(v.to_vec())
761 }
762}
763
764impl From<Vec<Value>> for Value {
765 fn from(v: Vec<Value>) -> Self {
766 Value::Array(v)
767 }
768}
769
770fn escape_string(s: &str) -> String {
792 let mut escaped = String::with_capacity(s.len() + s.chars().filter(|&c| c == '\'').count());
793 for c in s.chars() {
794 if c == '\'' {
795 escaped.push_str("''");
796 } else {
797 escaped.push(c);
798 }
799 }
800 escaped
801}
802
803fn hex_encode(bytes: &[u8]) -> String {
804 bytes.iter().map(|b| format!("{:02x}", b)).collect()
805}
806
807#[derive(Debug, Clone, Copy, PartialEq, Eq)]
818#[non_exhaustive]
819pub enum ColType {
820 Bool,
822 I8,
824 I16,
826 I32,
828 I64,
830 U8,
832 U16,
834 U32,
836 U64,
838 F32,
840 F64,
842 Decimal,
844 String,
846 Bytes,
848 Date,
850 DateTime,
852 Time,
854 Json,
856 Uuid,
858 Unknown,
860}
861
862impl ColType {
863 pub fn from_type_name(type_name: &str) -> Self {
875 match type_name {
876 "BOOLEAN" | "BOOL" => Self::Bool,
877 "TINYINT" => Self::I8,
878 "SMALLINT" | "INT2" => Self::I16,
879 "INT" | "INT4" | "OID" | "MEDIUMINT" | "INTEGER" => Self::I32,
880 "BIGINT" | "INT8" => Self::I64,
881 "TINYINT UNSIGNED" => Self::U8,
882 "SMALLINT UNSIGNED" => Self::U16,
883 "INT UNSIGNED" | "MEDIUMINT UNSIGNED" => Self::U32,
884 "BIGINT UNSIGNED" => Self::U64,
885 "FLOAT" | "FLOAT4" | "REAL" => Self::F32,
886 "DOUBLE" | "FLOAT8" => Self::F64,
887 "DECIMAL" | "NUMERIC" | "NEWDECIMAL" | "MONEY" => Self::Decimal,
888 "TEXT" | "VARCHAR" | "CHAR" | "NAME" => Self::String,
889 "BLOB" | "BYTEA" => Self::Bytes,
890 "DATE" => Self::Date,
891 "DATETIME" | "TIMESTAMP" => Self::DateTime,
892 "TIME" => Self::Time,
893 "JSON" => Self::Json,
894 "UUID" => Self::Uuid,
895 _ => Self::Unknown,
896 }
897 }
898
899 pub fn parse_sqlite(type_name: &str) -> Self {
915 if type_name.is_empty() {
917 return Self::Unknown;
918 }
919 match type_name.to_uppercase().as_str() {
920 "INTEGER" | "INT" | "BIGINT" | "INT8" | "INT4" | "INT2" | "TINYINT" | "SMALLINT"
922 | "MEDIUMINT" => Self::I64,
923 "BOOLEAN" | "BOOL" => Self::Bool,
924 "REAL" | "FLOAT" | "DOUBLE" | "FLOAT8" | "DOUBLE PRECISION" => Self::F64,
925 "DECIMAL" | "NUMERIC" => Self::Decimal,
926 "TEXT" | "CLOB" | "VARCHAR" | "CHAR" | "NAME" => Self::String,
927 "BLOB" => Self::Bytes,
928 "DATE" => Self::Date,
929 "DATETIME" | "TIMESTAMP" => Self::DateTime,
930 "TIME" => Self::Time,
931 "JSON" => Self::Json,
932 _ => Self::Unknown,
933 }
934 }
935
936 pub fn parse_mysql(type_name: &str) -> Self {
940 match type_name.to_uppercase().as_str() {
941 "TINYINT" => Self::I8,
942 "SMALLINT" => Self::I16,
943 "INT" | "INTEGER" | "MEDIUMINT" => Self::I32,
944 "BIGINT" => Self::I64,
945 "TINYINT UNSIGNED" => Self::U8,
946 "SMALLINT UNSIGNED" => Self::U16,
947 "INT UNSIGNED" | "MEDIUMINT UNSIGNED" => Self::U32,
948 "BIGINT UNSIGNED" => Self::U64,
949 "FLOAT" => Self::F32,
950 "DOUBLE" => Self::F64,
951 "DECIMAL" | "NUMERIC" | "NEWDECIMAL" => Self::Decimal,
952 "VARCHAR" | "CHAR" | "TEXT" | "TINYTEXT" | "MEDIUMTEXT" | "LONGTEXT" | "ENUM"
953 | "SET" => Self::String,
954 "BLOB" | "TINYBLOB" | "MEDIUMBLOB" | "LONGBLOB" | "BINARY" | "VARBINARY" => Self::Bytes,
955 "DATE" => Self::Date,
956 "DATETIME" | "TIMESTAMP" => Self::DateTime,
957 "TIME" => Self::Time,
958 "YEAR" => Self::I16,
959 "JSON" => Self::Json,
960 "BOOLEAN" | "BOOL" => Self::Bool,
961 _ => Self::from_type_name(type_name),
962 }
963 }
964
965 pub fn parse_postgres(type_name: &str) -> Self {
969 match type_name.to_uppercase().as_str() {
970 "BOOL" => Self::Bool,
971 "INT2" | "SMALLINT" => Self::I16,
972 "INT4" | "INTEGER" | "INT" => Self::I32,
973 "INT8" | "BIGINT" => Self::I64,
974 "FLOAT4" | "REAL" => Self::F32,
975 "FLOAT8" | "DOUBLE PRECISION" => Self::F64,
976 "NUMERIC" | "DECIMAL" | "MONEY" => Self::Decimal,
977 "TEXT" | "VARCHAR" | "CHAR" | "BPCHAR" | "NAME" | "CITEXT" => Self::String,
978 "BYTEA" => Self::Bytes,
979 "DATE" => Self::Date,
980 "TIMESTAMP" | "TIMESTAMPTZ" => Self::DateTime,
981 "TIME" | "TIMETZ" => Self::Time,
982 "JSON" | "JSONB" => Self::Json,
983 "UUID" => Self::Uuid,
984 "OID" => Self::I32,
985 _ => Self::from_type_name(type_name),
986 }
987 }
988}
989
990pub type QueryValues = (Vec<String>, Vec<Vec<Value>>);
1010
1011#[cfg(test)]
1012mod tests {
1013 use super::*;
1014
1015 #[test]
1016 fn test_value_is_null() {
1017 assert!(Value::Null.is_null());
1018 assert!(!Value::I64(0).is_null());
1019 }
1020
1021 #[test]
1024 fn test_const_str_eq() {
1025 assert!(__sz_orm_const_str_eq("id", "id"));
1026 assert!(__sz_orm_const_str_eq("user_id", "user_id"));
1027 assert!(!__sz_orm_const_str_eq("id", "ID"));
1028 assert!(!__sz_orm_const_str_eq("id", "idd"));
1029 assert!(!__sz_orm_const_str_eq("", "id"));
1030 assert!(__sz_orm_const_str_eq("", ""));
1031 }
1032
1033 #[test]
1034 fn test_const_types_compatible_same_category() {
1035 assert!(__sz_orm_const_types_compatible("BIGINT", "BIGINT"));
1037 assert!(__sz_orm_const_types_compatible("bigint", "BIGINT"));
1038 assert!(__sz_orm_const_types_compatible("INT8", "BIGINT")); assert!(__sz_orm_const_types_compatible("varchar", "TEXT"));
1040 assert!(__sz_orm_const_types_compatible("VARCHAR", "VARCHAR"));
1041 assert!(__sz_orm_const_types_compatible("timestamp", "DATETIME"));
1042 assert!(__sz_orm_const_types_compatible("int4", "INT"));
1043 assert!(__sz_orm_const_types_compatible("jsonb", "JSON"));
1044 assert!(__sz_orm_const_types_compatible("numeric", "DECIMAL"));
1045 }
1046
1047 #[test]
1048 fn test_const_types_compatible_different_category() {
1049 assert!(!__sz_orm_const_types_compatible("BIGINT", "TEXT"));
1051 assert!(!__sz_orm_const_types_compatible("VARCHAR", "INT"));
1052 assert!(!__sz_orm_const_types_compatible("JSON", "BIGINT"));
1053 assert!(!__sz_orm_const_types_compatible("BLOB", "DATE"));
1054 assert!(!__sz_orm_const_types_compatible("DOUBLE", "INT"));
1055 }
1056
1057 #[test]
1058 fn test_const_types_compatible_unknown_tolerant() {
1059 assert!(__sz_orm_const_types_compatible("CUSTOM_TYPE", "BIGINT"));
1061 assert!(__sz_orm_const_types_compatible("BIGINT", "CUSTOM_TYPE"));
1062 assert!(__sz_orm_const_types_compatible("UNKNOWN1", "UNKNOWN2"));
1063 }
1064
1065 #[test]
1066 fn test_col_type_from_type_name() {
1067 assert_eq!(ColType::from_type_name("BOOLEAN"), ColType::Bool);
1069 assert_eq!(ColType::from_type_name("TINYINT"), ColType::I8);
1070 assert_eq!(ColType::from_type_name("SMALLINT"), ColType::I16);
1071 assert_eq!(ColType::from_type_name("INT"), ColType::I32);
1072 assert_eq!(ColType::from_type_name("BIGINT"), ColType::I64);
1073 assert_eq!(ColType::from_type_name("INT UNSIGNED"), ColType::U32);
1074 assert_eq!(ColType::from_type_name("FLOAT"), ColType::F32);
1075 assert_eq!(ColType::from_type_name("DOUBLE"), ColType::F64);
1076 assert_eq!(ColType::from_type_name("TEXT"), ColType::String);
1077 assert_eq!(ColType::from_type_name("BLOB"), ColType::Bytes);
1078 assert_eq!(ColType::from_type_name("DATE"), ColType::Date);
1079 assert_eq!(ColType::from_type_name("TIMESTAMP"), ColType::DateTime);
1080 assert_eq!(ColType::from_type_name("JSON"), ColType::Json);
1081 assert_eq!(ColType::from_type_name("INT2"), ColType::I16);
1083 assert_eq!(ColType::from_type_name("INT4"), ColType::I32);
1084 assert_eq!(ColType::from_type_name("INT8"), ColType::I64);
1085 assert_eq!(ColType::from_type_name("FLOAT4"), ColType::F32);
1086 assert_eq!(ColType::from_type_name("FLOAT8"), ColType::F64);
1087 assert_eq!(ColType::from_type_name("BYTEA"), ColType::Bytes);
1088 assert_eq!(ColType::from_type_name("UNKNOWN_TYPE"), ColType::Unknown);
1090 assert_eq!(ColType::from_type_name(""), ColType::Unknown);
1091 }
1092
1093 #[test]
1094 fn test_value_as_i64() {
1095 assert_eq!(Value::I64(42).as_i64(), Some(42));
1096 assert_eq!(Value::I32(42).as_i64(), Some(42));
1097 assert_eq!(Value::Bool(true).as_i64(), Some(1));
1098 assert!(Value::String("test".to_string()).as_i64().is_none());
1099 }
1100
1101 #[test]
1102 fn test_value_as_f64() {
1103 assert_eq!(Value::F64(2.5).as_f64(), Some(2.5));
1104 assert_eq!(Value::I64(42).as_f64(), Some(42.0));
1105 }
1106
1107 #[test]
1108 fn test_value_as_str() {
1109 assert_eq!(Value::String("hello".to_string()).as_str(), Some("hello"));
1110 }
1111
1112 #[test]
1113 fn test_value_to_param() {
1114 assert_eq!(Value::Null.to_param(), "NULL");
1115 assert_eq!(Value::Bool(true).to_param(), "TRUE");
1116 assert_eq!(Value::I64(42).to_param(), "42");
1117 assert_eq!(Value::String("test".to_string()).to_param(), "'test'");
1118 assert_eq!(Value::String("it's".to_string()).to_param(), "'it''s'");
1119 }
1120
1121 #[test]
1122 fn test_value_into() {
1123 let v: Value = 42i64.into();
1124 assert_eq!(v, Value::I64(42));
1125
1126 let v: Value = "hello".into();
1127 assert_eq!(v, Value::String("hello".to_string()));
1128
1129 let arr: Vec<Value> = vec![Value::I64(1), Value::I64(2)];
1130 let v: Value = arr.into();
1131 assert_eq!(v, Value::Array(vec![Value::I64(1), Value::I64(2)]));
1132 }
1133
1134 #[test]
1135 fn test_value_display() {
1136 assert_eq!(format!("{}", Value::Null), "NULL");
1137 assert_eq!(format!("{}", Value::Bool(true)), "true");
1138 assert_eq!(format!("{}", Value::I64(42)), "42");
1139 assert_eq!(format!("{}", Value::String("test".to_string())), "'test'");
1140 }
1141
1142 #[test]
1144 fn test_box_str_size() {
1145 let string_size = std::mem::size_of::<String>();
1146 let box_str_size = std::mem::size_of::<Box<str>>();
1147 assert_eq!(string_size, 24);
1148 assert_eq!(box_str_size, 16);
1149 assert_eq!(string_size - box_str_size, 8);
1150 }
1151
1152 #[cfg(feature = "perf-box-str")]
1154 #[test]
1155 fn test_boxed_str_variant() {
1156 let v = Value::boxed_str("hello");
1157 assert_eq!(format!("{}", v), "'hello'");
1158 assert_eq!(v.to_param(), "'hello'");
1159 }
1160}