db_mover/databases/mysql/
value.rs1use chrono::{NaiveDateTime, TimeZone, Utc};
2
3use crate::databases::table::{Column, ColumnType, Value};
4
5#[derive(Clone, Debug, PartialEq)]
6pub struct MysqlTypeOptions {
7 pub binary_16_as_uuid: bool,
8 pub tinyint_as_bool: bool,
9}
10
11impl Default for MysqlTypeOptions {
12 fn default() -> Self {
13 return MysqlTypeOptions {
14 binary_16_as_uuid: true,
15 tinyint_as_bool: true,
16 };
17 }
18}
19
20impl ColumnType {
21 pub fn try_from_mysql_type(
22 type_name: &str,
23 options: &MysqlTypeOptions,
24 ) -> anyhow::Result<ColumnType> {
25 let formated = type_name.trim().to_lowercase();
26 if options.binary_16_as_uuid && formated == "binary(16)" {
27 return Ok(ColumnType::Uuid);
28 }
29 if options.tinyint_as_bool && formated == "tinyint(1)" {
30 return Ok(ColumnType::Bool);
31 }
32 if formated.starts_with("char") || formated.starts_with("varchar") {
33 return Ok(ColumnType::String);
34 }
35 if formated.starts_with("binary") || formated.starts_with("varbinary") {
36 return Ok(ColumnType::Bytes);
37 }
38 if formated.starts_with("smallint") {
39 return Ok(ColumnType::I16);
40 }
41 if formated.starts_with("int") {
42 return Ok(ColumnType::I32);
43 }
44 if formated.starts_with("bigint") {
45 return Ok(ColumnType::I64);
46 }
47 if formated.starts_with("numeric") || formated.starts_with("decimal") {
48 return Ok(ColumnType::Decimal);
49 }
50 return match formated.as_str() {
51 "float" => Ok(ColumnType::F32),
52 "double" | "real" | "double precision" => Ok(ColumnType::F64),
53 "bool" | "boolean" => Ok(ColumnType::Bool),
54 "tinytext" | "text" | "mediumtext" | "longtext" => Ok(ColumnType::String),
55 "tinyblob" | "blob" | "mediumblob" | "longblob" => Ok(ColumnType::Bytes),
56 "timestamp" => Ok(ColumnType::Timestamptz),
57 "datetime" => Ok(ColumnType::Timestamp),
58 "date" => Ok(ColumnType::Date),
59 "time" => Ok(ColumnType::Time),
60 "json" => Ok(ColumnType::Json),
61 _ => Err(anyhow::anyhow!("Unknown column type {type_name}")),
62 };
63 }
64}
65
66impl TryFrom<(&Column, mysql::Value)> for Value {
67 type Error = anyhow::Error;
68
69 fn try_from(value: (&Column, mysql::Value)) -> Result<Self, Self::Error> {
70 let (column, val) = value;
71 if val == mysql::Value::NULL {
72 return Ok(Value::Null);
73 }
74 let parsed = match column.column_type {
75 ColumnType::I64 => Value::I64(mysql::from_value_opt(val)?),
76 ColumnType::I32 => Value::I32(mysql::from_value_opt(val)?),
77 ColumnType::I16 => Value::I16(mysql::from_value_opt(val)?),
78 ColumnType::F64 => Value::F64(mysql::from_value_opt(val)?),
79 ColumnType::F32 => Value::F32(mysql::from_value_opt(val)?),
80 ColumnType::Decimal => Value::Decimal(mysql::from_value_opt(val)?),
81 ColumnType::Bool => Value::Bool(mysql::from_value_opt(val)?),
82 ColumnType::String => Value::String(mysql::from_value_opt(val)?),
83 ColumnType::Bytes => {
84 Value::Bytes(bytes::Bytes::from(mysql::from_value_opt::<Vec<u8>>(val)?))
85 }
86 ColumnType::Timestamp => Value::Timestamp(mysql::from_value_opt(val)?),
87 ColumnType::Timestamptz => {
88 let dt: NaiveDateTime = mysql::from_value_opt(val)?;
89 Value::Timestamptz(Utc.from_utc_datetime(&dt)) }
91 ColumnType::Date => Value::Date(mysql::from_value_opt(val)?),
92 ColumnType::Time => Value::Time(mysql::from_value_opt(val)?),
93 ColumnType::Json => Value::Json(mysql::from_value_opt(val)?),
94 ColumnType::Uuid => Value::Uuid(mysql::from_value_opt(val)?),
95 };
96 return Ok(parsed);
97 }
98}
99
100impl From<&Value> for mysql::Value {
101 fn from(value: &Value) -> Self {
102 match value {
103 Value::Null => mysql::Value::NULL,
104 Value::I64(val) => val.into(),
105 Value::I32(val) => val.into(),
106 Value::I16(val) => val.into(),
107 Value::F64(val) => val.into(),
108 Value::F32(val) => val.into(),
109 Value::Decimal(val) => val.into(),
110 Value::Bool(val) => val.into(),
111 Value::String(val) => val.into(),
112 Value::Bytes(val) => val.as_ref().into(),
113 Value::Timestamptz(val) => val.naive_utc().into(),
114 Value::Timestamp(val) => val.into(),
115 Value::Date(val) => val.into(),
116 Value::Time(val) => val.into(),
117 Value::Json(val) => val.into(),
118 Value::Uuid(val) => val.into(),
119 }
120 }
121}