From e98f4eb738cc4727a5955a8cbdb4b1f90c98fc6d Mon Sep 17 00:00:00 2001 From: Ivan Krivosheev Date: Sun, 7 Aug 2022 23:47:22 +0300 Subject: [PATCH 1/2] issues-336 Init new Value type --- Cargo.toml | 7 +- examples/postgres/Cargo.toml | 1 - sea-query-postgres/Cargo.toml | 3 +- sea-query-postgres/src/lib.rs | 66 +- sea-query-rusqlite/Cargo.toml | 5 +- sea-query-rusqlite/src/lib.rs | 81 +- src/backend/query_builder.rs | 152 +-- src/expr.rs | 4 +- src/prepare.rs | 8 +- src/query/delete.rs | 2 +- src/query/select.rs | 4 +- src/query/update.rs | 2 +- src/types.rs | 2 +- src/value.rs | 1645 --------------------------------- src/value/mod.rs | 23 + src/value/tuple.rs | 125 +++ src/value/types.rs | 151 +++ 17 files changed, 330 insertions(+), 1951 deletions(-) delete mode 100644 src/value.rs create mode 100644 src/value/mod.rs create mode 100644 src/value/tuple.rs create mode 100644 src/value/types.rs diff --git a/Cargo.toml b/Cargo.toml index 618b0c66c..28440840b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,6 @@ sea-query-attr = { version = "^0.1.1", path = "sea-query-attr", optional = true sea-query-derive = { version = "^0.2.0", path = "sea-query-derive", optional = true } serde_json = { version = "^1", optional = true } chrono = { version = "^0.4", default-features = false, features = ["clock"], optional = true } -postgres-types = { version = "^0", optional = true } rust_decimal = { version = "^1", optional = true } bigdecimal = { version = "^0.3", optional = true } uuid = { version = "^1", optional = true } @@ -40,6 +39,9 @@ quote = { version = "^1", optional = true } time = { version = "^0.3", optional = true, features = ["macros", "formatting"] } ipnetwork = { version = "^0.19", optional = true } mac_address = { version = "^1.1", optional = true } +bytes = { version = "^1", optional = true } +postgres-types = { version = "^0", optional = true } +rusqlite = { version = "^0.28", features = ["bundled"], optional = true } [dev-dependencies] criterion = { version = "0.3", features = ["html_reports"] } @@ -52,7 +54,8 @@ backend-sqlite = [] default = ["derive", "backend-mysql", "backend-postgres", "backend-sqlite"] derive = ["sea-query-derive"] attr = ["sea-query-attr"] -postgres-array = [] +with-postgres = ["postgres-types", "bytes"] +with-rusqlite = ["rusqlite"] postgres-interval = ["proc-macro2", "quote"] thread-safe = [] with-chrono = ["chrono"] diff --git a/examples/postgres/Cargo.toml b/examples/postgres/Cargo.toml index 43aff5efe..ed437dc9c 100644 --- a/examples/postgres/Cargo.toml +++ b/examples/postgres/Cargo.toml @@ -19,7 +19,6 @@ sea-query-postgres = { path = "../../sea-query-postgres", features = [ "with-chrono", "with-json", "with-time", - "postgres-array", "with-rust_decimal" ] } # NOTE: if you are copying this example into your own project, use the following line instead: diff --git a/sea-query-postgres/Cargo.toml b/sea-query-postgres/Cargo.toml index 4afd7aa49..ffb5f6f28 100644 --- a/sea-query-postgres/Cargo.toml +++ b/sea-query-postgres/Cargo.toml @@ -17,7 +17,7 @@ rust-version = "1.60" [lib] [dependencies] -sea-query = { version = "^0", path = ".." } +sea-query = { version = "^0", path = "..", features = ["thread-safe", "with-postgres"] } postgres-types = { version = "^0.2" } bytes = { version = "^1" } rust_decimal = { version = "^1", optional = true } @@ -29,6 +29,5 @@ with-rust_decimal = ["sea-query/with-rust_decimal", "rust_decimal/db-postgres"] with-bigdecimal = ["sea-query/with-bigdecimal"] with-uuid = ["postgres-types/with-uuid-1", "sea-query/with-uuid"] with-time = ["postgres-types/with-time-0_3", "sea-query/with-time"] -postgres-array = ["postgres-types/array-impls", "sea-query/postgres-array"] with-ipnetwork = ["sea-query/with-ipnetwork"] with-mac_address = ["sea-query/with-mac_address"] diff --git a/sea-query-postgres/src/lib.rs b/sea-query-postgres/src/lib.rs index 4ebffe656..9ee269d1a 100644 --- a/sea-query-postgres/src/lib.rs +++ b/sea-query-postgres/src/lib.rs @@ -3,11 +3,11 @@ use std::error::Error; use bytes::BytesMut; use postgres_types::{to_sql_checked, IsNull, ToSql, Type}; -use sea_query::{query::*, QueryBuilder, Value}; +use sea_query::{query::*, QueryBuilder, Value, ValueTrait}; -#[derive(Clone, Debug, PartialEq)] -pub struct PostgresValue(pub sea_query::Value); -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug)] +pub struct PostgresValue(pub Value); +#[derive(Clone, Debug)] pub struct PostgresValues(pub Vec); impl<'a> PostgresValues { @@ -54,63 +54,7 @@ impl ToSql for PostgresValue { ty: &Type, out: &mut BytesMut, ) -> Result> { - macro_rules! to_sql { - ( $v: expr, $ty: ty ) => { - $v.map(|v| v as $ty).as_ref().to_sql(ty, out) - }; - } - match &self.0 { - Value::Bool(v) => to_sql!(v, bool), - Value::TinyInt(v) => to_sql!(v, i8), - Value::SmallInt(v) => to_sql!(v, i16), - Value::Int(v) => to_sql!(v, i32), - Value::BigInt(v) => to_sql!(v, i64), - Value::TinyUnsigned(v) => to_sql!(v, u32), - Value::SmallUnsigned(v) => to_sql!(v, u32), - Value::Unsigned(v) => to_sql!(v, u32), - Value::BigUnsigned(v) => to_sql!(v, i64), - Value::Float(v) => to_sql!(v, f32), - Value::Double(v) => to_sql!(v, f64), - Value::String(v) => v.as_deref().to_sql(ty, out), - Value::Char(v) => v.map(|v| v.to_string()).to_sql(ty, out), - Value::Bytes(v) => v.as_deref().to_sql(ty, out), - #[cfg(feature = "with-json")] - Value::Json(v) => v.as_deref().to_sql(ty, out), - #[cfg(feature = "with-chrono")] - Value::ChronoDate(v) => v.as_deref().to_sql(ty, out), - #[cfg(feature = "with-chrono")] - Value::ChronoTime(v) => v.as_deref().to_sql(ty, out), - #[cfg(feature = "with-chrono")] - Value::ChronoDateTime(v) => v.as_deref().to_sql(ty, out), - #[cfg(feature = "with-chrono")] - Value::ChronoDateTimeUtc(v) => v.as_deref().to_sql(ty, out), - #[cfg(feature = "with-chrono")] - Value::ChronoDateTimeLocal(v) => v.as_deref().to_sql(ty, out), - #[cfg(feature = "with-chrono")] - Value::ChronoDateTimeWithTimeZone(v) => v.as_deref().to_sql(ty, out), - #[cfg(feature = "with-time")] - Value::TimeDate(v) => v.as_deref().to_sql(ty, out), - #[cfg(feature = "with-time")] - Value::TimeTime(v) => v.as_deref().to_sql(ty, out), - #[cfg(feature = "with-time")] - Value::TimeDateTime(v) => v.as_deref().to_sql(ty, out), - #[cfg(feature = "with-time")] - Value::TimeDateTimeWithTimeZone(v) => v.as_deref().to_sql(ty, out), - #[cfg(feature = "with-rust_decimal")] - Value::Decimal(v) => v.as_deref().to_sql(ty, out), - #[cfg(feature = "with-uuid")] - Value::Uuid(v) => v.as_deref().to_sql(ty, out), - #[cfg(feature = "postgres-array")] - Value::Array(Some(v)) => v - .iter() - .map(|v| PostgresValue(v.clone())) - .collect::>() - .to_sql(ty, out), - #[cfg(feature = "postgres-array")] - Value::Array(None) => Ok(IsNull::Yes), - #[allow(unreachable_patterns)] - _ => unimplemented!(), - } + self.0.to_sql(ty, out) } fn accepts(_ty: &Type) -> bool { diff --git a/sea-query-rusqlite/Cargo.toml b/sea-query-rusqlite/Cargo.toml index 6a6f57fa2..6e15a12ea 100644 --- a/sea-query-rusqlite/Cargo.toml +++ b/sea-query-rusqlite/Cargo.toml @@ -17,8 +17,8 @@ rust-version = "1.60" [lib] [dependencies] -sea-query = { version = "^0", path = ".." } -rusqlite = { package = "rusqlite", version = "^0.28", features = ["bundled"] } +sea-query = { version = "^0", path = "..", feature = ["with-rusqlite"] } +rusqlite = { version = "^0.28", features = ["bundled"] } [features] with-chrono = ["rusqlite/chrono", "sea-query/with-chrono"] @@ -29,5 +29,4 @@ with-uuid = ["rusqlite/uuid", "sea-query/with-uuid"] with-time = ["rusqlite/time", "sea-query/with-time"] with-ipnetwork = ["sea-query/with-ipnetwork"] with-mac_address = ["sea-query/with-mac_address"] -postgres-array = ["sea-query/postgres-array"] diff --git a/sea-query-rusqlite/src/lib.rs b/sea-query-rusqlite/src/lib.rs index 86f18f571..347836e58 100644 --- a/sea-query-rusqlite/src/lib.rs +++ b/sea-query-rusqlite/src/lib.rs @@ -50,85 +50,6 @@ impl_rusqlite_binder!(DeleteStatement); impl ToSql for RusqliteValue { fn to_sql(&self) -> Result> { - macro_rules! box_to_sql { - ( $v: expr ) => { - match $v { - Some(v) => v.as_ref().to_sql(), - None => Null.to_sql(), - } - }; - } - - macro_rules! opt_string_to_sql { - ( $v: expr ) => { - match $v { - Some(v) => Ok(ToSqlOutput::from(v)), - None => Null.to_sql(), - } - }; - } - - match &self.0 { - Value::Bool(v) => v.to_sql(), - Value::TinyInt(v) => v.to_sql(), - Value::SmallInt(v) => v.to_sql(), - Value::Int(v) => v.to_sql(), - Value::BigInt(v) => v.to_sql(), - Value::TinyUnsigned(v) => v.to_sql(), - Value::SmallUnsigned(v) => v.to_sql(), - Value::Unsigned(v) => v.to_sql(), - Value::BigUnsigned(v) => v.to_sql(), - Value::Float(v) => v.to_sql(), - Value::Double(v) => v.to_sql(), - Value::String(v) => box_to_sql!(v), - Value::Char(v) => opt_string_to_sql!(v.map(|v| v.to_string())), - Value::Bytes(v) => box_to_sql!(v), - #[cfg(feature = "with-chrono")] - Value::ChronoDate(v) => box_to_sql!(v), - #[cfg(feature = "with-chrono")] - Value::ChronoTime(v) => box_to_sql!(v), - #[cfg(feature = "with-chrono")] - Value::ChronoDateTime(v) => box_to_sql!(v), - #[cfg(feature = "with-chrono")] - Value::ChronoDateTimeUtc(v) => box_to_sql!(v), - #[cfg(feature = "with-chrono")] - Value::ChronoDateTimeLocal(v) => box_to_sql!(v), - #[cfg(feature = "with-chrono")] - Value::ChronoDateTimeWithTimeZone(v) => box_to_sql!(v), - #[cfg(feature = "with-time")] - v @ Value::TimeDate(_) => opt_string_to_sql!(v.time_as_naive_utc_in_string()), - #[cfg(feature = "with-time")] - v @ Value::TimeTime(_) => opt_string_to_sql!(v.time_as_naive_utc_in_string()), - #[cfg(feature = "with-time")] - v @ Value::TimeDateTime(_) => opt_string_to_sql!(v.time_as_naive_utc_in_string()), - #[cfg(feature = "with-time")] - v @ Value::TimeDateTimeWithTimeZone(_) => { - opt_string_to_sql!(v.time_as_naive_utc_in_string()) - } - #[cfg(feature = "with-uuid")] - Value::Uuid(v) => box_to_sql!(v), - #[cfg(feature = "with-json")] - Value::Json(j) => box_to_sql!(j), - #[cfg(feature = "with-rust_decimal")] - Value::Decimal(_) => { - panic!("Rusqlite doesn't support rust_decimal arguments"); - } - #[cfg(feature = "with-bigdecimal")] - Value::BigDecimal(_) => { - panic!("Rusqlite doesn't support bigdecimal arguments"); - } - #[cfg(feature = "with-ipnetwork")] - Value::IpNetwork(_) => { - panic!("Rusqlite doesn't support IpNetwork arguments"); - } - #[cfg(feature = "with-mac_address")] - Value::MacAddress(_) => { - panic!("Rusqlite doesn't support MacAddress arguments"); - } - #[cfg(feature = "postgres-array")] - Value::Array(_) => { - panic!("Rusqlite doesn't support Array arguments"); - } - } + self.0.to_sql() } } diff --git a/src/backend/query_builder.rs b/src/backend/query_builder.rs index abdda289e..e688952d5 100644 --- a/src/backend/query_builder.rs +++ b/src/backend/query_builder.rs @@ -852,8 +852,7 @@ pub trait QueryBuilder: QuotedBuilder + EscapeBuilder + TableRefBuilder { write!(sql, "WHEN ").unwrap(); self.prepare_simple_expr(&order_expr.expr, sql); write!(sql, "=").unwrap(); - let value = self.value_to_string(value); - write!(sql, "{}", value).unwrap(); + write!(sql, "{}", value.to_sql_string()).unwrap(); write!(sql, " THEN {} ", i).unwrap(); i += 1; } @@ -865,8 +864,7 @@ pub trait QueryBuilder: QuotedBuilder + EscapeBuilder + TableRefBuilder { /// Write [`Value`] inline. fn prepare_constant(&self, value: &Value, sql: &mut dyn SqlWriter) { - let string = self.value_to_string(value); - write!(sql, "{}", string).unwrap(); + write!(sql, "{}", value.to_sql_string()).unwrap(); } /// Translate a `&[ValueTuple]` into a VALUES list. @@ -915,144 +913,6 @@ pub trait QueryBuilder: QuotedBuilder + EscapeBuilder + TableRefBuilder { } } - /// Convert a SQL value into syntax-specific string - fn value_to_string(&self, v: &Value) -> String { - let mut s = String::new(); - match v { - Value::Bool(None) - | Value::TinyInt(None) - | Value::SmallInt(None) - | Value::Int(None) - | Value::BigInt(None) - | Value::TinyUnsigned(None) - | Value::SmallUnsigned(None) - | Value::Unsigned(None) - | Value::BigUnsigned(None) - | Value::Float(None) - | Value::Double(None) - | Value::String(None) - | Value::Char(None) - | Value::Bytes(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-json")] - Value::Json(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-chrono")] - Value::ChronoDate(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-chrono")] - Value::ChronoTime(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-chrono")] - Value::ChronoDateTime(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-chrono")] - Value::ChronoDateTimeUtc(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-chrono")] - Value::ChronoDateTimeLocal(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-chrono")] - Value::ChronoDateTimeWithTimeZone(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-time")] - Value::TimeDate(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-time")] - Value::TimeTime(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-time")] - Value::TimeDateTime(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-time")] - Value::TimeDateTimeWithTimeZone(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-rust_decimal")] - Value::Decimal(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-bigdecimal")] - Value::BigDecimal(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-uuid")] - Value::Uuid(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-ipnetwork")] - Value::IpNetwork(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "with-mac_address")] - Value::MacAddress(None) => write!(s, "NULL").unwrap(), - #[cfg(feature = "postgres-array")] - Value::Array(None) => write!(s, "NULL").unwrap(), - Value::Bool(Some(b)) => write!(s, "{}", if *b { "TRUE" } else { "FALSE" }).unwrap(), - Value::TinyInt(Some(v)) => write!(s, "{}", v).unwrap(), - Value::SmallInt(Some(v)) => write!(s, "{}", v).unwrap(), - Value::Int(Some(v)) => write!(s, "{}", v).unwrap(), - Value::BigInt(Some(v)) => write!(s, "{}", v).unwrap(), - Value::TinyUnsigned(Some(v)) => write!(s, "{}", v).unwrap(), - Value::SmallUnsigned(Some(v)) => write!(s, "{}", v).unwrap(), - Value::Unsigned(Some(v)) => write!(s, "{}", v).unwrap(), - Value::BigUnsigned(Some(v)) => write!(s, "{}", v).unwrap(), - Value::Float(Some(v)) => write!(s, "{}", v).unwrap(), - Value::Double(Some(v)) => write!(s, "{}", v).unwrap(), - Value::String(Some(v)) => self.write_string_quoted(v, &mut s), - Value::Char(Some(v)) => { - self.write_string_quoted(std::str::from_utf8(&[*v as u8]).unwrap(), &mut s) - } - Value::Bytes(Some(v)) => write!( - s, - "x'{}'", - v.iter().map(|b| format!("{:02X}", b)).collect::() - ) - .unwrap(), - #[cfg(feature = "with-json")] - Value::Json(Some(v)) => self.write_string_quoted(&v.to_string(), &mut s), - #[cfg(feature = "with-chrono")] - Value::ChronoDate(Some(v)) => write!(s, "'{}'", v.format("%Y-%m-%d")).unwrap(), - #[cfg(feature = "with-chrono")] - Value::ChronoTime(Some(v)) => write!(s, "'{}'", v.format("%H:%M:%S")).unwrap(), - #[cfg(feature = "with-chrono")] - Value::ChronoDateTime(Some(v)) => { - write!(s, "'{}'", v.format("%Y-%m-%d %H:%M:%S")).unwrap() - } - #[cfg(feature = "with-chrono")] - Value::ChronoDateTimeUtc(Some(v)) => { - write!(s, "'{}'", v.format("%Y-%m-%d %H:%M:%S %:z")).unwrap() - } - #[cfg(feature = "with-chrono")] - Value::ChronoDateTimeLocal(Some(v)) => { - write!(s, "'{}'", v.format("%Y-%m-%d %H:%M:%S %:z")).unwrap() - } - #[cfg(feature = "with-chrono")] - Value::ChronoDateTimeWithTimeZone(Some(v)) => { - write!(s, "'{}'", v.format("%Y-%m-%d %H:%M:%S %:z")).unwrap() - } - #[cfg(feature = "with-time")] - Value::TimeDate(Some(v)) => { - write!(s, "'{}'", v.format(time_format::FORMAT_DATE).unwrap()).unwrap() - } - #[cfg(feature = "with-time")] - Value::TimeTime(Some(v)) => { - write!(s, "'{}'", v.format(time_format::FORMAT_TIME).unwrap()).unwrap() - } - #[cfg(feature = "with-time")] - Value::TimeDateTime(Some(v)) => { - write!(s, "'{}'", v.format(time_format::FORMAT_DATETIME).unwrap()).unwrap() - } - #[cfg(feature = "with-time")] - Value::TimeDateTimeWithTimeZone(Some(v)) => write!( - s, - "'{}'", - v.format(time_format::FORMAT_DATETIME_TZ).unwrap() - ) - .unwrap(), - #[cfg(feature = "with-rust_decimal")] - Value::Decimal(Some(v)) => write!(s, "{}", v).unwrap(), - #[cfg(feature = "with-bigdecimal")] - Value::BigDecimal(Some(v)) => write!(s, "{}", v).unwrap(), - #[cfg(feature = "with-uuid")] - Value::Uuid(Some(v)) => write!(s, "'{}'", v).unwrap(), - #[cfg(feature = "postgres-array")] - Value::Array(Some(v)) => write!( - s, - "'{{{}}}'", - v.iter() - .map(|element| self.value_to_string(element)) - .collect::>() - .join(",") - ) - .unwrap(), - #[cfg(feature = "with-ipnetwork")] - Value::IpNetwork(Some(v)) => write!(s, "'{}'", v).unwrap(), - #[cfg(feature = "with-mac_address")] - Value::MacAddress(Some(v)) => write!(s, "'{}'", v).unwrap(), - }; - s - } - #[doc(hidden)] /// Write ON CONFLICT expression fn prepare_on_conflict(&self, on_conflict: &Option, sql: &mut dyn SqlWriter) { @@ -1260,13 +1120,13 @@ pub trait QueryBuilder: QuotedBuilder + EscapeBuilder + TableRefBuilder { match *frame { Frame::UnboundedPreceding => write!(sql, "UNBOUNDED PRECEDING").unwrap(), Frame::Preceding(v) => { - self.prepare_value(&Some(v).into(), sql); - write!(sql, "PRECEDING").unwrap(); + self.prepare_value(&v.into(), sql); + write!(sql, " PRECEDING ").unwrap(); } Frame::CurrentRow => write!(sql, "CURRENT ROW").unwrap(), Frame::Following(v) => { - self.prepare_value(&Some(v).into(), sql); - write!(sql, "FOLLOWING").unwrap(); + self.prepare_value(&v.into(), sql); + write!(sql, " FOLLOWING ").unwrap(); } Frame::UnboundedFollowing => write!(sql, "UNBOUNDED FOLLOWING").unwrap(), } diff --git a/src/expr.rs b/src/expr.rs index 90d003ffa..eaa3eaf7b 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -1260,14 +1260,14 @@ impl Expr { } fn like_like(self, op: BinOper, like: LikeExpr) -> SimpleExpr { - let value = SimpleExpr::Value(Value::String(Some(Box::new(like.pattern)))); + let value = SimpleExpr::Value(like.pattern.into()); self.bin_oper( op, match like.escape { Some(escape) => SimpleExpr::Binary( Box::new(value), BinOper::Escape, - Box::new(SimpleExpr::Constant(Value::Char(Some(escape)))), + Box::new(SimpleExpr::Constant(escape.into())), ), None => value, }, diff --git a/src/prepare.rs b/src/prepare.rs index 78aa6fa19..585f5abc0 100644 --- a/src/prepare.rs +++ b/src/prepare.rs @@ -11,7 +11,7 @@ pub trait SqlWriter: Write + ToString { impl SqlWriter for String { fn push_param(&mut self, value: Value, query_builder: &dyn QueryBuilder) { - self.push_str(&query_builder.value_to_string(&value)) + self.push_str(&value.to_sql_string()) } fn as_writer(&mut self) -> &mut dyn Write { @@ -89,7 +89,7 @@ where match token { Token::Punctuation(mark) => { if (mark.as_ref(), false) == query_builder.placeholder() { - output.push(query_builder.value_to_string(¶ms[counter])); + output.push(params[counter].to_sql_string()); counter += 1; i += 1; continue; @@ -98,7 +98,7 @@ where { if let Token::Unquoted(next) = &tokens[i + 1] { if let Ok(num) = next.parse::() { - output.push(query_builder.value_to_string(¶ms[num - 1])); + output.push(params[num - 1].to_sql_string()); i += 2; continue; } @@ -116,7 +116,7 @@ where #[cfg(test)] #[cfg(feature = "backend-mysql")] mod tests { - use super::*; + use crate::*; #[test] fn inject_parameters_1() { diff --git a/src/query/delete.rs b/src/query/delete.rs index 2baf9d3ab..b8a49244f 100644 --- a/src/query/delete.rs +++ b/src/query/delete.rs @@ -99,7 +99,7 @@ impl DeleteStatement { /// Limit number of updated rows. pub fn limit(&mut self, limit: u64) -> &mut Self { - self.limit = Some(Value::BigUnsigned(Some(limit))); + self.limit = Some(limit.into()); self } diff --git a/src/query/select.rs b/src/query/select.rs index 41b1fd07b..257ff844d 100644 --- a/src/query/select.rs +++ b/src/query/select.rs @@ -1745,7 +1745,7 @@ impl SelectStatement { /// ); /// ``` pub fn limit(&mut self, limit: u64) -> &mut Self { - self.limit = Some(Value::BigUnsigned(Some(limit))); + self.limit = Some(limit.into()); self } @@ -1783,7 +1783,7 @@ impl SelectStatement { /// ); /// ``` pub fn offset(&mut self, offset: u64) -> &mut Self { - self.offset = Some(Value::BigUnsigned(Some(offset))); + self.offset = Some(offset.into()); self } diff --git a/src/query/update.rs b/src/query/update.rs index 3527e22bc..5d9c59cfc 100644 --- a/src/query/update.rs +++ b/src/query/update.rs @@ -186,7 +186,7 @@ impl UpdateStatement { /// Limit number of updated rows. pub fn limit(&mut self, limit: u64) -> &mut Self { - self.limit = Some(Value::BigUnsigned(Some(limit))); + self.limit = Some(limit.into()); self } diff --git a/src/types.rs b/src/types.rs index 00ebe7b83..5a2bf8c8a 100644 --- a/src/types.rs +++ b/src/types.rs @@ -181,7 +181,7 @@ pub enum JoinOn { } /// Ordering options -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone)] pub enum Order { Asc, Desc, diff --git a/src/value.rs b/src/value.rs deleted file mode 100644 index 5f3192a50..000000000 --- a/src/value.rs +++ /dev/null @@ -1,1645 +0,0 @@ -//! Container for all SQL value types. - -#[cfg(feature = "with-json")] -use serde_json::Value as Json; -#[cfg(feature = "with-json")] -use std::str::from_utf8; - -#[cfg(feature = "with-chrono")] -use chrono::{DateTime, FixedOffset, Local, NaiveDate, NaiveDateTime, NaiveTime, Utc}; - -#[cfg(feature = "with-time")] -use time::{OffsetDateTime, PrimitiveDateTime}; - -#[cfg(feature = "with-rust_decimal")] -use rust_decimal::Decimal; - -#[cfg(feature = "with-bigdecimal")] -use bigdecimal::BigDecimal; - -#[cfg(feature = "with-uuid")] -use uuid::Uuid; - -#[cfg(feature = "with-ipnetwork")] -use ipnetwork::IpNetwork; - -#[cfg(feature = "with-ipnetwork")] -use std::net::IpAddr; - -#[cfg(feature = "with-mac_address")] -use mac_address::MacAddress; - -use crate::{BlobSize, ColumnType, CommonSqlQueryBuilder, QueryBuilder}; - -/// Value variants -/// -/// We want Value to be exactly 1 pointer sized, so anything larger should be boxed. -#[derive(Clone, Debug, PartialEq)] -pub enum Value { - Bool(Option), - TinyInt(Option), - SmallInt(Option), - Int(Option), - BigInt(Option), - TinyUnsigned(Option), - SmallUnsigned(Option), - Unsigned(Option), - BigUnsigned(Option), - Float(Option), - Double(Option), - String(Option>), - Char(Option), - - #[allow(clippy::box_collection)] - Bytes(Option>>), - - #[cfg(feature = "with-json")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-json")))] - Json(Option>), - - #[cfg(feature = "with-chrono")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] - ChronoDate(Option>), - - #[cfg(feature = "with-chrono")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] - ChronoTime(Option>), - - #[cfg(feature = "with-chrono")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] - ChronoDateTime(Option>), - - #[cfg(feature = "with-chrono")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] - ChronoDateTimeUtc(Option>>), - - #[cfg(feature = "with-chrono")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] - ChronoDateTimeLocal(Option>>), - - #[cfg(feature = "with-chrono")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] - ChronoDateTimeWithTimeZone(Option>>), - - #[cfg(feature = "with-time")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))] - TimeDate(Option>), - - #[cfg(feature = "with-time")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))] - TimeTime(Option>), - - #[cfg(feature = "with-time")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))] - TimeDateTime(Option>), - - #[cfg(feature = "with-time")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))] - TimeDateTimeWithTimeZone(Option>), - - #[cfg(feature = "with-uuid")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-uuid")))] - Uuid(Option>), - - #[cfg(feature = "with-rust_decimal")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-rust_decimal")))] - Decimal(Option>), - - #[cfg(feature = "with-bigdecimal")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-bigdecimal")))] - BigDecimal(Option>), - - #[cfg(feature = "postgres-array")] - #[cfg_attr(docsrs, doc(cfg(feature = "postgres-array")))] - Array(Option>>), - - #[cfg(feature = "with-ipnetwork")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-ipnetwork")))] - IpNetwork(Option>), - - #[cfg(feature = "with-mac_address")] - #[cfg_attr(docsrs, doc(cfg(feature = "with-mac_address")))] - MacAddress(Option>), -} - -impl std::fmt::Display for Value { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", CommonSqlQueryBuilder.value_to_string(self)) - } -} - -pub trait ValueType: Sized { - fn try_from(v: Value) -> Result; - - fn unwrap(v: Value) -> Self { - Self::try_from(v).unwrap() - } - - fn type_name() -> String; - - fn column_type() -> ColumnType; -} - -#[derive(Debug)] -pub struct ValueTypeErr; - -impl std::error::Error for ValueTypeErr {} - -impl std::fmt::Display for ValueTypeErr { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "Value type mismatch") - } -} - -#[derive(Clone, Debug, PartialEq)] -pub struct Values(pub Vec); - -#[derive(Clone, Debug, PartialEq)] -pub enum ValueTuple { - One(Value), - Two(Value, Value), - Three(Value, Value, Value), - Four(Value, Value, Value, Value), - Five(Value, Value, Value, Value, Value), - Six(Value, Value, Value, Value, Value, Value), -} - -pub trait IntoValueTuple { - fn into_value_tuple(self) -> ValueTuple; -} - -pub trait FromValueTuple: Sized { - fn from_value_tuple(i: I) -> Self - where - I: IntoValueTuple; -} - -pub trait Nullable { - fn null() -> Value; -} - -impl Value { - pub fn unwrap(self) -> T - where - T: ValueType, - { - T::unwrap(self) - } -} - -macro_rules! type_to_value { - ( $type: ty, $name: ident, $col_type: expr ) => { - impl From<$type> for Value { - fn from(x: $type) -> Value { - Value::$name(Some(x)) - } - } - - impl Nullable for $type { - fn null() -> Value { - Value::$name(None) - } - } - - impl ValueType for $type { - fn try_from(v: Value) -> Result { - match v { - Value::$name(Some(x)) => Ok(x), - _ => Err(ValueTypeErr), - } - } - - fn type_name() -> String { - stringify!($type).to_owned() - } - - fn column_type() -> ColumnType { - use ColumnType::*; - $col_type - } - } - }; -} - -macro_rules! type_to_box_value { - ( $type: ty, $name: ident, $col_type: expr ) => { - impl From<$type> for Value { - fn from(x: $type) -> Value { - Value::$name(Some(Box::new(x))) - } - } - - impl Nullable for $type { - fn null() -> Value { - Value::$name(None) - } - } - - impl ValueType for $type { - fn try_from(v: Value) -> Result { - match v { - Value::$name(Some(x)) => Ok(*x), - _ => Err(ValueTypeErr), - } - } - - fn type_name() -> String { - stringify!($type).to_owned() - } - - fn column_type() -> ColumnType { - use ColumnType::*; - $col_type - } - } - }; -} - -type_to_value!(bool, Bool, Boolean); -type_to_value!(i8, TinyInt, TinyInteger(None)); -type_to_value!(i16, SmallInt, SmallInteger(None)); -type_to_value!(i32, Int, Integer(None)); -type_to_value!(i64, BigInt, BigInteger(None)); -type_to_value!(u8, TinyUnsigned, TinyUnsigned(None)); -type_to_value!(u16, SmallUnsigned, SmallUnsigned(None)); -type_to_value!(u32, Unsigned, Unsigned(None)); -type_to_value!(u64, BigUnsigned, BigUnsigned(None)); -type_to_value!(f32, Float, Float(None)); -type_to_value!(f64, Double, Double(None)); -type_to_value!(char, Char, Char(None)); - -impl<'a> From<&'a [u8]> for Value { - fn from(x: &'a [u8]) -> Value { - Value::Bytes(Some(Box::>::new(x.into()))) - } -} - -impl<'a> From<&'a str> for Value { - fn from(x: &'a str) -> Value { - let string: String = x.into(); - Value::String(Some(Box::new(string))) - } -} - -impl<'a> Nullable for &'a str { - fn null() -> Value { - Value::String(None) - } -} - -impl From> for Value -where - T: Into + Nullable, -{ - fn from(x: Option) -> Value { - match x { - Some(v) => v.into(), - None => T::null(), - } - } -} - -impl ValueType for Option -where - T: ValueType + Nullable, -{ - fn try_from(v: Value) -> Result { - if v == T::null() { - Ok(None) - } else { - Ok(Some(T::try_from(v)?)) - } - } - - fn type_name() -> String { - format!("Option<{}>", T::type_name()) - } - - fn column_type() -> ColumnType { - T::column_type() - } -} - -type_to_box_value!(Vec, Bytes, Binary(BlobSize::Blob(None))); -type_to_box_value!(String, String, String(None)); - -#[cfg(feature = "with-json")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-json")))] -mod with_json { - use super::*; - - type_to_box_value!(Json, Json, Json); -} - -#[cfg(feature = "with-chrono")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] -mod with_chrono { - use super::*; - use chrono::{Local, Offset, Utc}; - - type_to_box_value!(NaiveDate, ChronoDate, Date); - type_to_box_value!(NaiveTime, ChronoTime, Time(None)); - type_to_box_value!(NaiveDateTime, ChronoDateTime, DateTime(None)); - - impl From> for Value { - fn from(v: DateTime) -> Value { - Value::ChronoDateTimeUtc(Some(Box::new(v))) - } - } - - impl From> for Value { - fn from(v: DateTime) -> Value { - Value::ChronoDateTimeLocal(Some(Box::new(v))) - } - } - - impl From> for Value { - fn from(x: DateTime) -> Value { - let v = DateTime::::from_utc(x.naive_utc(), x.offset().fix()); - Value::ChronoDateTimeWithTimeZone(Some(Box::new(v))) - } - } - - impl Nullable for DateTime { - fn null() -> Value { - Value::ChronoDateTimeUtc(None) - } - } - - impl ValueType for DateTime { - fn try_from(v: Value) -> Result { - match v { - Value::ChronoDateTimeUtc(Some(x)) => Ok(*x), - _ => Err(ValueTypeErr), - } - } - - fn type_name() -> String { - stringify!(DateTime).to_owned() - } - - fn column_type() -> ColumnType { - ColumnType::TimestampWithTimeZone(None) - } - } - - impl Nullable for DateTime { - fn null() -> Value { - Value::ChronoDateTimeLocal(None) - } - } - - impl ValueType for DateTime { - fn try_from(v: Value) -> Result { - match v { - Value::ChronoDateTimeLocal(Some(x)) => Ok(*x), - _ => Err(ValueTypeErr), - } - } - - fn type_name() -> String { - stringify!(DateTime).to_owned() - } - - fn column_type() -> ColumnType { - ColumnType::TimestampWithTimeZone(None) - } - } - - impl Nullable for DateTime { - fn null() -> Value { - Value::ChronoDateTimeWithTimeZone(None) - } - } - - impl ValueType for DateTime { - fn try_from(v: Value) -> Result { - match v { - Value::ChronoDateTimeWithTimeZone(Some(x)) => Ok(*x), - _ => Err(ValueTypeErr), - } - } - - fn type_name() -> String { - stringify!(DateTime).to_owned() - } - - fn column_type() -> ColumnType { - ColumnType::TimestampWithTimeZone(None) - } - } -} - -#[cfg(feature = "with-time")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-time")))] -pub mod time_format { - use time::format_description::FormatItem; - use time::macros::format_description; - - pub static FORMAT_DATE: &[FormatItem<'static>] = format_description!("[year]-[month]-[day]"); - pub static FORMAT_TIME: &[FormatItem<'static>] = - format_description!("[hour]:[minute]:[second]"); - pub static FORMAT_DATETIME: &[FormatItem<'static>] = - format_description!("[year]-[month]-[day] [hour]:[minute]:[second]"); - pub static FORMAT_DATETIME_TZ: &[FormatItem<'static>] = format_description!( - "[year]-[month]-[day] [hour]:[minute]:[second] [offset_hour sign:mandatory][offset_minute]" - ); -} - -#[cfg(feature = "with-time")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-time")))] -mod with_time { - use super::*; - - type_to_box_value!(time::Date, TimeDate, Date); - type_to_box_value!(time::Time, TimeTime, Time(None)); - type_to_box_value!(PrimitiveDateTime, TimeDateTime, DateTime(None)); - - impl From for Value { - fn from(v: OffsetDateTime) -> Value { - Value::TimeDateTimeWithTimeZone(Some(Box::new(v))) - } - } - - impl Nullable for OffsetDateTime { - fn null() -> Value { - Value::TimeDateTimeWithTimeZone(None) - } - } - - impl ValueType for OffsetDateTime { - fn try_from(v: Value) -> Result { - match v { - Value::TimeDateTimeWithTimeZone(Some(x)) => Ok(*x), - _ => Err(ValueTypeErr), - } - } - - fn type_name() -> String { - stringify!(OffsetDateTime).to_owned() - } - - fn column_type() -> ColumnType { - ColumnType::TimestampWithTimeZone(None) - } - } -} - -#[cfg(feature = "with-rust_decimal")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-rust_decimal")))] -mod with_rust_decimal { - use super::*; - - type_to_box_value!(Decimal, Decimal, Decimal(None)); -} - -#[cfg(feature = "with-bigdecimal")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-bigdecimal")))] -mod with_bigdecimal { - use super::*; - - type_to_box_value!(BigDecimal, BigDecimal, Decimal(None)); -} - -#[cfg(feature = "with-uuid")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-uuid")))] -mod with_uuid { - use super::*; - - type_to_box_value!(Uuid, Uuid, Uuid); -} - -#[cfg(feature = "with-ipnetwork")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-ipnetwork")))] -mod with_ipnetwork { - use super::*; - - type_to_box_value!(IpNetwork, IpNetwork, Inet); -} - -#[cfg(feature = "with-mac_address")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-mac_address")))] -mod with_mac_address { - use super::*; - - type_to_box_value!(MacAddress, MacAddress, MacAddr); -} - -#[cfg(feature = "postgres-array")] -#[cfg_attr(docsrs, doc(cfg(feature = "postgres-array")))] -mod with_array { - use super::*; - - // We only imlement conversion from Vec to Array when T is not u8. - // This is because for u8's case, there is already conversion to Byte defined above. - // TODO When negative trait becomes a stable feature, following code can be much shorter. - pub trait NotU8 {} - - impl NotU8 for bool {} - impl NotU8 for i8 {} - impl NotU8 for i16 {} - impl NotU8 for i32 {} - impl NotU8 for i64 {} - impl NotU8 for u16 {} - impl NotU8 for u32 {} - impl NotU8 for u64 {} - impl NotU8 for f32 {} - impl NotU8 for f64 {} - impl NotU8 for String {} - - #[cfg(feature = "with-json")] - impl NotU8 for Json {} - - #[cfg(feature = "with-chrono")] - impl NotU8 for NaiveDate {} - - #[cfg(feature = "with-chrono")] - impl NotU8 for NaiveTime {} - - #[cfg(feature = "with-chrono")] - impl NotU8 for NaiveDateTime {} - - #[cfg(feature = "with-chrono")] - impl NotU8 for DateTime where Tz: chrono::TimeZone {} - - #[cfg(feature = "with-time")] - impl NotU8 for time::Date {} - - #[cfg(feature = "with-time")] - impl NotU8 for time::Time {} - - #[cfg(feature = "with-time")] - impl NotU8 for PrimitiveDateTime {} - - #[cfg(feature = "with-time")] - impl NotU8 for OffsetDateTime {} - - #[cfg(feature = "with-rust_decimal")] - impl NotU8 for Decimal {} - - #[cfg(feature = "with-bigdecimal")] - impl NotU8 for BigDecimal {} - - #[cfg(feature = "with-uuid")] - impl NotU8 for Uuid {} - - impl From> for Value - where - T: Into + NotU8, - { - fn from(x: Vec) -> Value { - Value::Array(Some(Box::new(x.into_iter().map(|e| e.into()).collect()))) - } - } - - impl Nullable for Vec - where - T: Into + NotU8, - { - fn null() -> Value { - Value::Array(None) - } - } - - impl ValueType for Vec - where - T: NotU8 + ValueType, - { - fn try_from(v: Value) -> Result { - match v { - Value::Array(Some(v)) => Ok(v.into_iter().map(|e| e.unwrap()).collect()), - _ => Err(ValueTypeErr), - } - } - - fn type_name() -> String { - stringify!(Vec).to_owned() - } - - fn column_type() -> ColumnType { - use ColumnType::*; - Array(None) - } - } -} - -#[allow(unused_macros)] -macro_rules! box_to_opt_ref { - ( $v: expr ) => { - match $v { - Some(v) => Some(v.as_ref()), - None => None, - } - }; -} - -#[cfg(feature = "with-json")] -impl Value { - pub fn is_json(&self) -> bool { - matches!(self, Self::Json(_)) - } - - pub fn as_ref_json(&self) -> Option<&Json> { - match self { - Self::Json(v) => box_to_opt_ref!(v), - _ => panic!("not Value::Json"), - } - } -} - -#[cfg(feature = "with-chrono")] -impl Value { - pub fn is_chrono_date(&self) -> bool { - matches!(self, Self::ChronoDate(_)) - } - - pub fn as_ref_chrono_date(&self) -> Option<&NaiveDate> { - match self { - Self::ChronoDate(v) => box_to_opt_ref!(v), - _ => panic!("not Value::ChronoDate"), - } - } -} - -#[cfg(feature = "with-time")] -impl Value { - pub fn is_time_date(&self) -> bool { - matches!(self, Self::TimeDate(_)) - } - - pub fn as_ref_time_date(&self) -> Option<&time::Date> { - match self { - Self::TimeDate(v) => box_to_opt_ref!(v), - _ => panic!("not Value::TimeDate"), - } - } -} - -#[cfg(feature = "with-chrono")] -impl Value { - pub fn is_chrono_time(&self) -> bool { - matches!(self, Self::ChronoTime(_)) - } - - pub fn as_ref_chrono_time(&self) -> Option<&NaiveTime> { - match self { - Self::ChronoTime(v) => box_to_opt_ref!(v), - _ => panic!("not Value::ChronoTime"), - } - } -} - -#[cfg(feature = "with-time")] -impl Value { - pub fn is_time_time(&self) -> bool { - matches!(self, Self::TimeTime(_)) - } - - pub fn as_ref_time_time(&self) -> Option<&time::Time> { - match self { - Self::TimeTime(v) => box_to_opt_ref!(v), - _ => panic!("not Value::TimeTime"), - } - } -} - -#[cfg(feature = "with-chrono")] -impl Value { - pub fn is_chrono_date_time(&self) -> bool { - matches!(self, Self::ChronoDateTime(_)) - } - - pub fn as_ref_chrono_date_time(&self) -> Option<&NaiveDateTime> { - match self { - Self::ChronoDateTime(v) => box_to_opt_ref!(v), - _ => panic!("not Value::ChronoDateTime"), - } - } -} - -#[cfg(feature = "with-time")] -impl Value { - pub fn is_time_date_time(&self) -> bool { - matches!(self, Self::TimeDateTime(_)) - } - - pub fn as_ref_time_date_time(&self) -> Option<&PrimitiveDateTime> { - match self { - Self::TimeDateTime(v) => box_to_opt_ref!(v), - _ => panic!("not Value::TimeDateTime"), - } - } -} - -#[cfg(feature = "with-chrono")] -impl Value { - pub fn is_chrono_date_time_utc(&self) -> bool { - matches!(self, Self::ChronoDateTimeUtc(_)) - } - - pub fn as_ref_chrono_date_time_utc(&self) -> Option<&DateTime> { - match self { - Self::ChronoDateTimeUtc(v) => box_to_opt_ref!(v), - _ => panic!("not Value::ChronoDateTimeUtc"), - } - } -} - -#[cfg(feature = "with-chrono")] -impl Value { - pub fn is_chrono_date_time_local(&self) -> bool { - matches!(self, Self::ChronoDateTimeLocal(_)) - } - - pub fn as_ref_chrono_date_time_local(&self) -> Option<&DateTime> { - match self { - Self::ChronoDateTimeLocal(v) => box_to_opt_ref!(v), - _ => panic!("not Value::ChronoDateTimeLocal"), - } - } -} - -#[cfg(feature = "with-chrono")] -impl Value { - pub fn is_chrono_date_time_with_time_zone(&self) -> bool { - matches!(self, Self::ChronoDateTimeWithTimeZone(_)) - } - - pub fn as_ref_chrono_date_time_with_time_zone(&self) -> Option<&DateTime> { - match self { - Self::ChronoDateTimeWithTimeZone(v) => box_to_opt_ref!(v), - _ => panic!("not Value::ChronoDateTimeWithTimeZone"), - } - } -} - -#[cfg(feature = "with-time")] -impl Value { - pub fn is_time_date_time_with_time_zone(&self) -> bool { - matches!(self, Self::TimeDateTimeWithTimeZone(_)) - } - - pub fn as_ref_time_date_time_with_time_zone(&self) -> Option<&OffsetDateTime> { - match self { - Self::TimeDateTimeWithTimeZone(v) => box_to_opt_ref!(v), - _ => panic!("not Value::TimeDateTimeWithTimeZone"), - } - } -} - -#[cfg(feature = "with-chrono")] -impl Value { - pub fn chrono_as_naive_utc_in_string(&self) -> Option { - match self { - Self::ChronoDate(v) => v.as_ref().map(|v| v.to_string()), - Self::ChronoTime(v) => v.as_ref().map(|v| v.to_string()), - Self::ChronoDateTime(v) => v.as_ref().map(|v| v.to_string()), - Self::ChronoDateTimeUtc(v) => v.as_ref().map(|v| v.naive_utc().to_string()), - Self::ChronoDateTimeLocal(v) => v.as_ref().map(|v| v.naive_utc().to_string()), - Self::ChronoDateTimeWithTimeZone(v) => v.as_ref().map(|v| v.naive_utc().to_string()), - _ => panic!("not chrono Value"), - } - } -} - -#[cfg(feature = "with-time")] -impl Value { - pub fn time_as_naive_utc_in_string(&self) -> Option { - match self { - Self::TimeDate(v) => v - .as_ref() - .and_then(|v| v.format(time_format::FORMAT_DATE).ok()), - Self::TimeTime(v) => v - .as_ref() - .and_then(|v| v.format(time_format::FORMAT_TIME).ok()), - Self::TimeDateTime(v) => v - .as_ref() - .and_then(|v| v.format(time_format::FORMAT_DATETIME).ok()), - Self::TimeDateTimeWithTimeZone(v) => v.as_ref().and_then(|v| { - v.to_offset(time::macros::offset!(UTC)) - .format(time_format::FORMAT_DATETIME_TZ) - .ok() - }), - _ => panic!("not time Value"), - } - } -} - -#[cfg(feature = "with-rust_decimal")] -impl Value { - pub fn is_decimal(&self) -> bool { - matches!(self, Self::Decimal(_)) - } - - pub fn as_ref_decimal(&self) -> Option<&Decimal> { - match self { - Self::Decimal(v) => box_to_opt_ref!(v), - _ => panic!("not Value::Decimal"), - } - } - - pub fn decimal_to_f64(&self) -> Option { - use rust_decimal::prelude::ToPrimitive; - - self.as_ref_decimal().map(|d| d.to_f64().unwrap()) - } -} - -#[cfg(feature = "with-bigdecimal")] -impl Value { - pub fn is_big_decimal(&self) -> bool { - matches!(self, Self::BigDecimal(_)) - } - - pub fn as_ref_big_decimal(&self) -> Option<&BigDecimal> { - match self { - Self::BigDecimal(v) => box_to_opt_ref!(v), - _ => panic!("not Value::BigDecimal"), - } - } - - pub fn big_decimal_to_f64(&self) -> Option { - use bigdecimal::ToPrimitive; - self.as_ref_big_decimal().map(|d| d.to_f64().unwrap()) - } -} - -#[cfg(feature = "with-uuid")] -impl Value { - pub fn is_uuid(&self) -> bool { - matches!(self, Self::Uuid(_)) - } - pub fn as_ref_uuid(&self) -> Option<&Uuid> { - match self { - Self::Uuid(v) => box_to_opt_ref!(v), - _ => panic!("not Value::Uuid"), - } - } -} - -#[cfg(feature = "postgres-array")] -impl Value { - pub fn is_array(&self) -> bool { - matches!(self, Self::Array(_)) - } - - pub fn as_ref_array(&self) -> Option<&Vec> { - match self { - Self::Array(v) => box_to_opt_ref!(v), - _ => panic!("not Value::Array"), - } - } -} - -#[cfg(feature = "with-ipnetwork")] -impl Value { - pub fn is_ipnetwork(&self) -> bool { - matches!(self, Self::IpNetwork(_)) - } - - pub fn as_ref_ipnetwork(&self) -> Option<&IpNetwork> { - match self { - Self::IpNetwork(v) => box_to_opt_ref!(v), - _ => panic!("not Value::IpNetwork"), - } - } - - pub fn as_ipaddr(&self) -> Option { - match self { - Self::IpNetwork(v) => v.clone().map(|v| v.network()), - _ => panic!("not Value::IpNetwork"), - } - } -} - -#[cfg(feature = "with-mac_address")] -impl Value { - pub fn is_mac_address(&self) -> bool { - matches!(self, Self::MacAddress(_)) - } - - pub fn as_ref_mac_address(&self) -> Option<&MacAddress> { - match self { - Self::MacAddress(v) => box_to_opt_ref!(v), - _ => panic!("not Value::MacAddress"), - } - } -} - -impl IntoIterator for ValueTuple { - type Item = Value; - type IntoIter = std::vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - match self { - ValueTuple::One(v) => vec![v].into_iter(), - ValueTuple::Two(v, w) => vec![v, w].into_iter(), - ValueTuple::Three(u, v, w) => vec![u, v, w].into_iter(), - ValueTuple::Four(u, v, w, x) => vec![u, v, w, x].into_iter(), - ValueTuple::Five(u, v, w, x, y) => vec![u, v, w, x, y].into_iter(), - ValueTuple::Six(u, v, w, x, y, z) => vec![u, v, w, x, y, z].into_iter(), - } - } -} - -impl IntoValueTuple for ValueTuple { - fn into_value_tuple(self) -> ValueTuple { - self - } -} - -impl IntoValueTuple for V -where - V: Into, -{ - fn into_value_tuple(self) -> ValueTuple { - ValueTuple::One(self.into()) - } -} - -impl IntoValueTuple for (V, W) -where - V: Into, - W: Into, -{ - fn into_value_tuple(self) -> ValueTuple { - ValueTuple::Two(self.0.into(), self.1.into()) - } -} - -impl IntoValueTuple for (U, V, W) -where - U: Into, - V: Into, - W: Into, -{ - fn into_value_tuple(self) -> ValueTuple { - ValueTuple::Three(self.0.into(), self.1.into(), self.2.into()) - } -} - -impl IntoValueTuple for (U, V, W, X) -where - U: Into, - V: Into, - W: Into, - X: Into, -{ - fn into_value_tuple(self) -> ValueTuple { - ValueTuple::Four(self.0.into(), self.1.into(), self.2.into(), self.3.into()) - } -} - -impl IntoValueTuple for (U, V, W, X, Y) -where - U: Into, - V: Into, - W: Into, - X: Into, - Y: Into, -{ - fn into_value_tuple(self) -> ValueTuple { - ValueTuple::Five( - self.0.into(), - self.1.into(), - self.2.into(), - self.3.into(), - self.4.into(), - ) - } -} - -impl IntoValueTuple for (U, V, W, X, Y, Z) -where - U: Into, - V: Into, - W: Into, - X: Into, - Y: Into, - Z: Into, -{ - fn into_value_tuple(self) -> ValueTuple { - ValueTuple::Six( - self.0.into(), - self.1.into(), - self.2.into(), - self.3.into(), - self.4.into(), - self.5.into(), - ) - } -} - -impl FromValueTuple for V -where - V: Into + ValueType, -{ - fn from_value_tuple(i: I) -> Self - where - I: IntoValueTuple, - { - match i.into_value_tuple() { - ValueTuple::One(u) => u.unwrap(), - _ => panic!("not ValueTuple::One"), - } - } -} - -impl FromValueTuple for (V, W) -where - V: Into + ValueType, - W: Into + ValueType, -{ - fn from_value_tuple(i: I) -> Self - where - I: IntoValueTuple, - { - match i.into_value_tuple() { - ValueTuple::Two(v, w) => (v.unwrap(), w.unwrap()), - _ => panic!("not ValueTuple::Two"), - } - } -} - -impl FromValueTuple for (U, V, W) -where - U: Into + ValueType, - V: Into + ValueType, - W: Into + ValueType, -{ - fn from_value_tuple(i: I) -> Self - where - I: IntoValueTuple, - { - match i.into_value_tuple() { - ValueTuple::Three(u, v, w) => (u.unwrap(), v.unwrap(), w.unwrap()), - _ => panic!("not ValueTuple::Three"), - } - } -} - -impl FromValueTuple for (U, V, W, X) -where - U: Into + ValueType, - V: Into + ValueType, - W: Into + ValueType, - X: Into + ValueType, -{ - fn from_value_tuple(i: I) -> Self - where - I: IntoValueTuple, - { - match i.into_value_tuple() { - ValueTuple::Four(u, v, w, x) => (u.unwrap(), v.unwrap(), w.unwrap(), x.unwrap()), - _ => panic!("not ValueTuple::Four"), - } - } -} - -impl FromValueTuple for (U, V, W, X, Y) -where - U: Into + ValueType, - V: Into + ValueType, - W: Into + ValueType, - X: Into + ValueType, - Y: Into + ValueType, -{ - fn from_value_tuple(i: I) -> Self - where - I: IntoValueTuple, - { - match i.into_value_tuple() { - ValueTuple::Five(u, v, w, x, y) => { - (u.unwrap(), v.unwrap(), w.unwrap(), x.unwrap(), y.unwrap()) - } - _ => panic!("not ValueTuple::Five"), - } - } -} - -impl FromValueTuple for (U, V, W, X, Y, Z) -where - U: Into + ValueType, - V: Into + ValueType, - W: Into + ValueType, - X: Into + ValueType, - Y: Into + ValueType, - Z: Into + ValueType, -{ - fn from_value_tuple(i: I) -> Self - where - I: IntoValueTuple, - { - match i.into_value_tuple() { - ValueTuple::Six(u, v, w, x, y, z) => ( - u.unwrap(), - v.unwrap(), - w.unwrap(), - x.unwrap(), - y.unwrap(), - z.unwrap(), - ), - _ => panic!("not ValueTuple::Six"), - } - } -} - -/// Convert value to json value -#[allow(clippy::many_single_char_names)] -#[cfg(feature = "with-json")] -#[cfg_attr(docsrs, doc(cfg(feature = "with-json")))] -pub fn sea_value_to_json_value(value: &Value) -> Json { - match value { - Value::Bool(None) - | Value::TinyInt(None) - | Value::SmallInt(None) - | Value::Int(None) - | Value::BigInt(None) - | Value::TinyUnsigned(None) - | Value::SmallUnsigned(None) - | Value::Unsigned(None) - | Value::BigUnsigned(None) - | Value::Float(None) - | Value::Double(None) - | Value::String(None) - | Value::Char(None) - | Value::Bytes(None) - | Value::Json(None) => Json::Null, - #[cfg(feature = "with-rust_decimal")] - Value::Decimal(None) => Json::Null, - #[cfg(feature = "with-bigdecimal")] - Value::BigDecimal(None) => Json::Null, - #[cfg(feature = "with-uuid")] - Value::Uuid(None) => Json::Null, - #[cfg(feature = "postgres-array")] - Value::Array(None) => Json::Null, - #[cfg(feature = "with-ipnetwork")] - Value::IpNetwork(None) => Json::Null, - #[cfg(feature = "with-mac_address")] - Value::MacAddress(None) => Json::Null, - Value::Bool(Some(b)) => Json::Bool(*b), - Value::TinyInt(Some(v)) => (*v).into(), - Value::SmallInt(Some(v)) => (*v).into(), - Value::Int(Some(v)) => (*v).into(), - Value::BigInt(Some(v)) => (*v).into(), - Value::TinyUnsigned(Some(v)) => (*v).into(), - Value::SmallUnsigned(Some(v)) => (*v).into(), - Value::Unsigned(Some(v)) => (*v).into(), - Value::BigUnsigned(Some(v)) => (*v).into(), - Value::Float(Some(v)) => (*v).into(), - Value::Double(Some(v)) => (*v).into(), - Value::String(Some(s)) => Json::String(s.as_ref().clone()), - Value::Char(Some(v)) => Json::String(v.to_string()), - Value::Bytes(Some(s)) => Json::String(from_utf8(s).unwrap().to_string()), - Value::Json(Some(v)) => v.as_ref().clone(), - #[cfg(feature = "with-chrono")] - Value::ChronoDate(_) => CommonSqlQueryBuilder.value_to_string(value).into(), - #[cfg(feature = "with-chrono")] - Value::ChronoTime(_) => CommonSqlQueryBuilder.value_to_string(value).into(), - #[cfg(feature = "with-chrono")] - Value::ChronoDateTime(_) => CommonSqlQueryBuilder.value_to_string(value).into(), - #[cfg(feature = "with-chrono")] - Value::ChronoDateTimeWithTimeZone(_) => CommonSqlQueryBuilder.value_to_string(value).into(), - #[cfg(feature = "with-chrono")] - Value::ChronoDateTimeUtc(_) => CommonSqlQueryBuilder.value_to_string(value).into(), - #[cfg(feature = "with-chrono")] - Value::ChronoDateTimeLocal(_) => CommonSqlQueryBuilder.value_to_string(value).into(), - #[cfg(feature = "with-time")] - Value::TimeDate(_) => CommonSqlQueryBuilder.value_to_string(value).into(), - #[cfg(feature = "with-time")] - Value::TimeTime(_) => CommonSqlQueryBuilder.value_to_string(value).into(), - #[cfg(feature = "with-time")] - Value::TimeDateTime(_) => CommonSqlQueryBuilder.value_to_string(value).into(), - #[cfg(feature = "with-time")] - Value::TimeDateTimeWithTimeZone(_) => CommonSqlQueryBuilder.value_to_string(value).into(), - #[cfg(feature = "with-rust_decimal")] - Value::Decimal(Some(v)) => { - use rust_decimal::prelude::ToPrimitive; - v.as_ref().to_f64().unwrap().into() - } - #[cfg(feature = "with-bigdecimal")] - Value::BigDecimal(Some(v)) => { - use bigdecimal::ToPrimitive; - v.as_ref().to_f64().unwrap().into() - } - #[cfg(feature = "with-uuid")] - Value::Uuid(Some(v)) => Json::String(v.to_string()), - #[cfg(feature = "postgres-array")] - Value::Array(Some(v)) => { - Json::Array(v.as_ref().iter().map(sea_value_to_json_value).collect()) - } - #[cfg(feature = "with-ipnetwork")] - Value::IpNetwork(Some(_)) => CommonSqlQueryBuilder.value_to_string(value).into(), - #[cfg(feature = "with-mac_address")] - Value::MacAddress(Some(_)) => CommonSqlQueryBuilder.value_to_string(value).into(), - } -} - -impl Values { - pub fn iter(&self) -> impl Iterator { - self.0.iter() - } -} - -impl IntoIterator for Values { - type Item = Value; - type IntoIter = std::vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_value() { - macro_rules! test_value { - ( $type: ty, $val: literal ) => { - let val: $type = $val; - let v: Value = val.into(); - let out: $type = v.unwrap(); - assert_eq!(out, val); - }; - } - - test_value!(u8, 255); - test_value!(u16, 65535); - test_value!(i8, 127); - test_value!(i16, 32767); - test_value!(i32, 1073741824); - test_value!(i64, 8589934592); - } - - #[test] - fn test_option_value() { - macro_rules! test_some_value { - ( $type: ty, $val: literal ) => { - let val: Option<$type> = Some($val); - let v: Value = val.into(); - let out: $type = v.unwrap(); - assert_eq!(out, val.unwrap()); - }; - } - - macro_rules! test_none { - ( $type: ty, $name: ident ) => { - let val: Option<$type> = None; - let v: Value = val.into(); - assert_eq!(v, Value::$name(None)); - }; - } - - test_some_value!(u8, 255); - test_some_value!(u16, 65535); - test_some_value!(i8, 127); - test_some_value!(i16, 32767); - test_some_value!(i32, 1073741824); - test_some_value!(i64, 8589934592); - - test_none!(u8, TinyUnsigned); - test_none!(u16, SmallUnsigned); - test_none!(i8, TinyInt); - test_none!(i16, SmallInt); - test_none!(i32, Int); - test_none!(i64, BigInt); - } - - #[test] - fn test_box_value() { - let val: String = "hello".to_owned(); - let v: Value = val.clone().into(); - let out: String = v.unwrap(); - assert_eq!(out, val); - } - - #[test] - fn test_value_tuple() { - assert_eq!( - 1i32.into_value_tuple(), - ValueTuple::One(Value::Int(Some(1))) - ); - assert_eq!( - "b".into_value_tuple(), - ValueTuple::One(Value::String(Some(Box::new("b".to_owned())))) - ); - assert_eq!( - (1i32, "b").into_value_tuple(), - ValueTuple::Two( - Value::Int(Some(1)), - Value::String(Some(Box::new("b".to_owned()))) - ) - ); - assert_eq!( - (1i32, 2.4f64, "b").into_value_tuple(), - ValueTuple::Three( - Value::Int(Some(1)), - Value::Double(Some(2.4)), - Value::String(Some(Box::new("b".to_owned()))) - ) - ); - assert_eq!( - (1i32, 2.4f64, "b", 123u8).into_value_tuple(), - ValueTuple::Four( - Value::Int(Some(1)), - Value::Double(Some(2.4)), - Value::String(Some(Box::new("b".to_owned()))), - Value::TinyUnsigned(Some(123)) - ) - ); - assert_eq!( - (1i32, 2.4f64, "b", 123u8, 456u16).into_value_tuple(), - ValueTuple::Five( - Value::Int(Some(1)), - Value::Double(Some(2.4)), - Value::String(Some(Box::new("b".to_owned()))), - Value::TinyUnsigned(Some(123)), - Value::SmallUnsigned(Some(456)) - ) - ); - assert_eq!( - (1i32, 2.4f64, "b", 123u8, 456u16, 789u32).into_value_tuple(), - ValueTuple::Six( - Value::Int(Some(1)), - Value::Double(Some(2.4)), - Value::String(Some(Box::new("b".to_owned()))), - Value::TinyUnsigned(Some(123)), - Value::SmallUnsigned(Some(456)), - Value::Unsigned(Some(789)) - ) - ); - } - - #[test] - #[allow(clippy::clone_on_copy)] - fn test_from_value_tuple() { - let mut val = 1i32; - let original = val.clone(); - val = FromValueTuple::from_value_tuple(val); - assert_eq!(val, original); - - let mut val = "b".to_owned(); - let original = val.clone(); - val = FromValueTuple::from_value_tuple(val); - assert_eq!(val, original); - - let mut val = (1i32, "b".to_owned()); - let original = val.clone(); - val = FromValueTuple::from_value_tuple(val); - assert_eq!(val, original); - - let mut val = (1i32, 2.4f64, "b".to_owned()); - let original = val.clone(); - val = FromValueTuple::from_value_tuple(val); - assert_eq!(val, original); - - let mut val = (1i32, 2.4f64, "b".to_owned(), 123u8); - let original = val.clone(); - val = FromValueTuple::from_value_tuple(val); - assert_eq!(val, original); - - let mut val = (1i32, 2.4f64, "b".to_owned(), 123u8, 456u16); - let original = val.clone(); - val = FromValueTuple::from_value_tuple(val); - assert_eq!(val, original); - - let mut val = (1i32, 2.4f64, "b".to_owned(), 123u8, 456u16, 789u32); - let original = val.clone(); - val = FromValueTuple::from_value_tuple(val); - assert_eq!(val, original); - } - - #[test] - fn test_value_tuple_iter() { - let mut iter = (1i32).into_value_tuple().into_iter(); - assert_eq!(iter.next().unwrap(), Value::Int(Some(1))); - assert_eq!(iter.next(), None); - - let mut iter = (1i32, 2.4f64).into_value_tuple().into_iter(); - assert_eq!(iter.next().unwrap(), Value::Int(Some(1))); - assert_eq!(iter.next().unwrap(), Value::Double(Some(2.4))); - assert_eq!(iter.next(), None); - - let mut iter = (1i32, 2.4f64, "b").into_value_tuple().into_iter(); - assert_eq!(iter.next().unwrap(), Value::Int(Some(1))); - assert_eq!(iter.next().unwrap(), Value::Double(Some(2.4))); - assert_eq!( - iter.next().unwrap(), - Value::String(Some(Box::new("b".to_owned()))) - ); - assert_eq!(iter.next(), None); - - let mut iter = (1i32, 2.4f64, "b", 123u8).into_value_tuple().into_iter(); - assert_eq!(iter.next().unwrap(), Value::Int(Some(1))); - assert_eq!(iter.next().unwrap(), Value::Double(Some(2.4))); - assert_eq!( - iter.next().unwrap(), - Value::String(Some(Box::new("b".to_owned()))) - ); - assert_eq!(iter.next().unwrap(), Value::TinyUnsigned(Some(123))); - assert_eq!(iter.next(), None); - - let mut iter = (1i32, 2.4f64, "b", 123u8, 456u16) - .into_value_tuple() - .into_iter(); - assert_eq!(iter.next().unwrap(), Value::Int(Some(1))); - assert_eq!(iter.next().unwrap(), Value::Double(Some(2.4))); - assert_eq!( - iter.next().unwrap(), - Value::String(Some(Box::new("b".to_owned()))) - ); - assert_eq!(iter.next().unwrap(), Value::TinyUnsigned(Some(123))); - assert_eq!(iter.next().unwrap(), Value::SmallUnsigned(Some(456))); - assert_eq!(iter.next(), None); - - let mut iter = (1i32, 2.4f64, "b", 123u8, 456u16, 789u32) - .into_value_tuple() - .into_iter(); - assert_eq!(iter.next().unwrap(), Value::Int(Some(1))); - assert_eq!(iter.next().unwrap(), Value::Double(Some(2.4))); - assert_eq!( - iter.next().unwrap(), - Value::String(Some(Box::new("b".to_owned()))) - ); - assert_eq!(iter.next().unwrap(), Value::TinyUnsigned(Some(123))); - assert_eq!(iter.next().unwrap(), Value::SmallUnsigned(Some(456))); - assert_eq!(iter.next().unwrap(), Value::Unsigned(Some(789))); - assert_eq!(iter.next(), None); - } - - #[test] - #[cfg(feature = "with-json")] - fn test_json_value() { - let json = serde_json::json! {{ - "a": 25.0, - "b": "hello", - }}; - let value: Value = json.clone().into(); - let out: Json = value.unwrap(); - assert_eq!(out, json); - } - - #[test] - #[cfg(feature = "with-chrono")] - fn test_chrono_value() { - let timestamp = chrono::NaiveDate::from_ymd(2020, 1, 1).and_hms(2, 2, 2); - let value: Value = timestamp.into(); - let out: NaiveDateTime = value.unwrap(); - assert_eq!(out, timestamp); - } - - #[test] - #[cfg(feature = "with-chrono")] - fn test_chrono_utc_value() { - let timestamp = - DateTime::::from_utc(NaiveDate::from_ymd(2022, 1, 2).and_hms(3, 4, 5), Utc); - let value: Value = timestamp.into(); - let out: DateTime = value.unwrap(); - assert_eq!(out, timestamp); - } - - #[test] - #[cfg(feature = "with-chrono")] - fn test_chrono_local_value() { - let timestamp_utc = - DateTime::::from_utc(NaiveDate::from_ymd(2022, 1, 2).and_hms(3, 4, 5), Utc); - let timestamp_local: DateTime = timestamp_utc.into(); - let value: Value = timestamp_local.into(); - let out: DateTime = value.unwrap(); - assert_eq!(out, timestamp_local); - } - - #[test] - #[cfg(feature = "with-chrono")] - fn test_chrono_timezone_value() { - let timestamp = DateTime::parse_from_rfc3339("2020-01-01T02:02:02+08:00").unwrap(); - let value: Value = timestamp.into(); - let out: DateTime = value.unwrap(); - assert_eq!(out, timestamp); - } - - #[test] - #[cfg(feature = "with-chrono")] - fn test_chrono_query() { - use crate::*; - - let string = "2020-01-01T02:02:02+08:00"; - let timestamp = DateTime::parse_from_rfc3339(string).unwrap(); - - let query = Query::select().expr(Expr::val(timestamp)).to_owned(); - - let formatted = "2020-01-01 02:02:02 +08:00"; - - assert_eq!( - query.to_string(MysqlQueryBuilder), - format!("SELECT '{}'", formatted) - ); - assert_eq!( - query.to_string(PostgresQueryBuilder), - format!("SELECT '{}'", formatted) - ); - assert_eq!( - query.to_string(SqliteQueryBuilder), - format!("SELECT '{}'", formatted) - ); - } - - #[test] - #[cfg(feature = "with-time")] - fn test_time_value() { - use time::macros::{date, time}; - let timestamp = date!(2020 - 01 - 01).with_time(time!(2:2:2)); - let value: Value = timestamp.into(); - let out: PrimitiveDateTime = value.unwrap(); - assert_eq!(out, timestamp); - } - - #[test] - #[cfg(feature = "with-time")] - fn test_time_utc_value() { - use time::macros::{date, time}; - let timestamp = date!(2022 - 01 - 02).with_time(time!(3:04:05)).assume_utc(); - let value: Value = timestamp.into(); - let out: OffsetDateTime = value.unwrap(); - assert_eq!(out, timestamp); - } - - #[test] - #[cfg(feature = "with-time")] - fn test_time_local_value() { - use time::macros::{date, offset, time}; - let timestamp_utc = date!(2022 - 01 - 02).with_time(time!(3:04:05)).assume_utc(); - let timestamp_local: OffsetDateTime = timestamp_utc.to_offset(offset!(+3)); - let value: Value = timestamp_local.into(); - let out: OffsetDateTime = value.unwrap(); - assert_eq!(out, timestamp_local); - } - - #[test] - #[cfg(feature = "with-time")] - fn test_time_timezone_value() { - use time::macros::{date, offset, time}; - let timestamp = date!(2022 - 01 - 02) - .with_time(time!(3:04:05)) - .assume_offset(offset!(+8)); - let value: Value = timestamp.into(); - let out: OffsetDateTime = value.unwrap(); - assert_eq!(out, timestamp); - } - - #[test] - #[cfg(feature = "with-time")] - fn test_time_query() { - use crate::*; - use time::macros::datetime; - - let timestamp = datetime!(2020-01-01 02:02:02 +8); - let query = Query::select().expr(Expr::val(timestamp)).to_owned(); - let formatted = "2020-01-01 02:02:02 +0800"; - - assert_eq!( - query.to_string(MysqlQueryBuilder), - format!("SELECT '{}'", formatted) - ); - assert_eq!( - query.to_string(PostgresQueryBuilder), - format!("SELECT '{}'", formatted) - ); - assert_eq!( - query.to_string(SqliteQueryBuilder), - format!("SELECT '{}'", formatted) - ); - } - - #[test] - #[cfg(feature = "with-uuid")] - fn test_uuid_value() { - let uuid = uuid::Uuid::parse_str("936DA01F9ABD4d9d80C702AF85C822A8").unwrap(); - let value: Value = uuid.into(); - let out: uuid::Uuid = value.unwrap(); - assert_eq!(out, uuid); - } - - #[test] - #[cfg(feature = "with-rust_decimal")] - fn test_decimal_value() { - use std::str::FromStr; - - let num = "2.02"; - let val = Decimal::from_str(num).unwrap(); - let v: Value = val.into(); - let out: Decimal = v.unwrap(); - assert_eq!(out.to_string(), num); - } - - #[test] - #[cfg(feature = "postgres-array")] - fn test_array_value() { - let array = vec![1, 2, 3, 4, 5]; - let v: Value = array.into(); - let out: Vec = v.unwrap(); - assert_eq!(out, vec![1, 2, 3, 4, 5]); - } -} diff --git a/src/value/mod.rs b/src/value/mod.rs new file mode 100644 index 000000000..3be547c5a --- /dev/null +++ b/src/value/mod.rs @@ -0,0 +1,23 @@ +pub use tuple::*; +pub use types::*; + +mod tuple; +mod types; + +#[derive(Debug, Clone)] +pub struct Values(pub Vec); + +impl Values { + pub fn iter(&self) -> impl Iterator { + self.0.iter() + } +} + +impl IntoIterator for Values { + type Item = Value; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} diff --git a/src/value/tuple.rs b/src/value/tuple.rs new file mode 100644 index 000000000..3620466ea --- /dev/null +++ b/src/value/tuple.rs @@ -0,0 +1,125 @@ +use crate::value::Value; + +#[derive(Debug, Clone)] +pub enum ValueTuple { + One(Value), + Two(Value, Value), + Three(Value, Value, Value), + Four(Value, Value, Value, Value), + Five(Value, Value, Value, Value, Value), + Six(Value, Value, Value, Value, Value, Value), +} + +pub trait IntoValueTuple { + fn into_value_tuple(self) -> ValueTuple; +} + +pub trait FromValueTuple: Sized { + fn from_value_tuple(i: I) -> Self + where + I: IntoValueTuple; +} + +impl IntoIterator for ValueTuple { + type Item = Value; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + match self { + ValueTuple::One(v) => vec![v].into_iter(), + ValueTuple::Two(v, w) => vec![v, w].into_iter(), + ValueTuple::Three(u, v, w) => vec![u, v, w].into_iter(), + ValueTuple::Four(u, v, w, x) => vec![u, v, w, x].into_iter(), + ValueTuple::Five(u, v, w, x, y) => vec![u, v, w, x, y].into_iter(), + ValueTuple::Six(u, v, w, x, y, z) => vec![u, v, w, x, y, z].into_iter(), + } + } +} + +impl IntoValueTuple for ValueTuple { + fn into_value_tuple(self) -> ValueTuple { + self + } +} + +impl IntoValueTuple for V +where + V: Into, +{ + fn into_value_tuple(self) -> ValueTuple { + ValueTuple::One(self.into()) + } +} + +impl IntoValueTuple for (V, W) +where + V: Into, + W: Into, +{ + fn into_value_tuple(self) -> ValueTuple { + ValueTuple::Two(self.0.into(), self.1.into()) + } +} + +impl IntoValueTuple for (U, V, W) +where + U: Into, + V: Into, + W: Into, +{ + fn into_value_tuple(self) -> ValueTuple { + ValueTuple::Three(self.0.into(), self.1.into(), self.2.into()) + } +} + +impl IntoValueTuple for (U, V, W, X) +where + U: Into, + V: Into, + W: Into, + X: Into, +{ + fn into_value_tuple(self) -> ValueTuple { + ValueTuple::Four(self.0.into(), self.1.into(), self.2.into(), self.3.into()) + } +} + +impl IntoValueTuple for (U, V, W, X, Y) +where + U: Into, + V: Into, + W: Into, + X: Into, + Y: Into, +{ + fn into_value_tuple(self) -> ValueTuple { + ValueTuple::Five( + self.0.into(), + self.1.into(), + self.2.into(), + self.3.into(), + self.4.into(), + ) + } +} + +impl IntoValueTuple for (U, V, W, X, Y, Z) +where + U: Into, + V: Into, + W: Into, + X: Into, + Y: Into, + Z: Into, +{ + fn into_value_tuple(self) -> ValueTuple { + ValueTuple::Six( + self.0.into(), + self.1.into(), + self.2.into(), + self.3.into(), + self.4.into(), + self.5.into(), + ) + } +} diff --git a/src/value/types.rs b/src/value/types.rs new file mode 100644 index 000000000..ef891a4f3 --- /dev/null +++ b/src/value/types.rs @@ -0,0 +1,151 @@ +use std::error::Error; +use std::fmt::{Debug, Formatter}; + +use crate::SeaRc; +#[cfg(feature = "with-postgres")] +use bytes::BytesMut; +#[cfg(feature = "with-postgres")] +use postgres_types::{to_sql_checked, IsNull, ToSql as PostgresToSql, Type}; +#[cfg(feature = "with-rusqlite")] +use rusqlite::{types::ToSqlOutput, Result, ToSql as RuSqliteToSql}; + +pub trait ValueTrait: PostgresToSql + RuSqliteToSql +where + Self: Debug, +{ + fn to_sql_string(&self) -> String; +} + +#[derive(Debug, Clone)] +pub enum Value { + Bool(bool), + TinyInt(i8), + SmallInt(i16), + Int(i32), + BigInt(i64), + TinyUnsigned(u8), + SmallUnsigned(u16), + Unsigned(u32), + BigUnsigned(u64), + Float(f32), + Double(f64), + #[cfg(feature = "thread-safe")] + Object(SeaRc>), + #[cfg(not(feature = "thread-safe"))] + Object(SeaRc>), +} + +impl PostgresToSql for Value { + fn to_sql( + &self, + ty: &Type, + out: &mut BytesMut, + ) -> Result> { + match self { + Value::Bool(v) => ::to_sql(v, ty, out), + Value::TinyInt(v) => ::to_sql(v, ty, out), + Value::SmallInt(v) => ::to_sql(v, ty, out), + Value::Int(v) => ::to_sql(v, ty, out), + Value::BigInt(v) => ::to_sql(v, ty, out), + Value::TinyUnsigned(v) => ::to_sql(&(*v as u32), ty, out), + Value::SmallUnsigned(v) => ::to_sql(&(*v as u32), ty, out), + Value::Unsigned(v) => ::to_sql(v, ty, out), + Value::BigUnsigned(v) => ::to_sql(&(*v as i64), ty, out), + Value::Float(v) => ::to_sql(v, ty, out), + Value::Double(v) => ::to_sql(v, ty, out), + Value::Object(o) => (o as &dyn PostgresToSql).to_sql(), + } + } + + fn accepts(_ty: &Type) -> bool { + true + } + + to_sql_checked!(); +} + +impl RuSqliteToSql for Value { + fn to_sql(&self) -> Result> { + match self { + Value::Bool(v) => ::to_sql(v), + Value::TinyInt(v) => ::to_sql(v), + Value::SmallInt(v) => ::to_sql(v), + Value::Int(v) => ::to_sql(v), + Value::BigInt(v) => ::to_sql(v), + Value::TinyUnsigned(v) => ::to_sql(v), + Value::SmallUnsigned(v) => ::to_sql(v), + Value::Unsigned(v) => ::to_sql(v), + Value::BigUnsigned(v) => ::to_sql(v), + Value::Float(v) => ::to_sql(v), + Value::Double(v) => ::to_sql(v), + Value::Object(o) => (o as &dyn RuSqliteToSql).to_sql(), + } + } +} + +impl ValueTrait for Value { + fn to_sql_string(&self) -> String { + todo!() + } +} + +macro_rules! simple_to { + ( $type: ty, $name: expr ) => { + impl From<$type> for Value { + fn from(v: $type) -> Value { + use Value::*; + $name(v.to_owned()) + } + } + }; +} + +macro_rules! object_to { + ( $type: ty ) => { + impl From<$type> for Value { + fn from(v: $type) -> Value { + let object = Box::new(v) as _; + Value::Object(SeaRc::new(object)) + } + } + }; +} + +simple_to!(i8, TinyInt); +simple_to!(i16, SmallInt); +simple_to!(i32, Int); +simple_to!(i64, BigInt); +simple_to!(u8, TinyUnsigned); +simple_to!(u16, SmallUnsigned); +simple_to!(u32, Unsigned); +simple_to!(u64, BigUnsigned); +simple_to!(f32, Float); +simple_to!(f64, Double); + +impl ValueTrait for String { + fn to_sql_string(&self) -> String { + todo!() + } +} + +object_to!(String); + +impl ValueTrait for &str { + fn to_sql_string(&self) -> String { + todo!() + } +} + +impl From<&str> for Value { + fn from(v: &str) -> Self { + let object = Box::new(v.to_string()) as _; + Value::Object(SeaRc::new(object)) + } +} + +impl From for Value { + fn from(v: char) -> Self { + let object = Box::new(v.to_string()) as _; + Value::Object(SeaRc::new(object)) + } +} From 5625838fb13cc1684aea48ccc0f6fd3dd96022a8 Mon Sep 17 00:00:00 2001 From: Ivan Krivosheev Date: Mon, 10 Oct 2022 22:31:51 +0300 Subject: [PATCH 2/2] issues-336 Some thought --- Cargo.toml | 4 - sea-query-binder/Cargo.toml | 1 - sea-query-postgres/Cargo.toml | 2 +- sea-query-postgres/src/lib.rs | 59 ++++++- sea-query-rusqlite/Cargo.toml | 3 +- src/value/mod.rs | 2 + src/value/string.rs | 49 ++++++ src/value/types.rs | 300 ++++++++++++++++++++++------------ 8 files changed, 307 insertions(+), 113 deletions(-) create mode 100644 src/value/string.rs diff --git a/Cargo.toml b/Cargo.toml index 28440840b..6c5fb4e6d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,8 +40,6 @@ time = { version = "^0.3", optional = true, features = ["macros", "formatting"] ipnetwork = { version = "^0.19", optional = true } mac_address = { version = "^1.1", optional = true } bytes = { version = "^1", optional = true } -postgres-types = { version = "^0", optional = true } -rusqlite = { version = "^0.28", features = ["bundled"], optional = true } [dev-dependencies] criterion = { version = "0.3", features = ["html_reports"] } @@ -54,8 +52,6 @@ backend-sqlite = [] default = ["derive", "backend-mysql", "backend-postgres", "backend-sqlite"] derive = ["sea-query-derive"] attr = ["sea-query-attr"] -with-postgres = ["postgres-types", "bytes"] -with-rusqlite = ["rusqlite"] postgres-interval = ["proc-macro2", "quote"] thread-safe = [] with-chrono = ["chrono"] diff --git a/sea-query-binder/Cargo.toml b/sea-query-binder/Cargo.toml index 27e301278..889dd7f49 100644 --- a/sea-query-binder/Cargo.toml +++ b/sea-query-binder/Cargo.toml @@ -33,7 +33,6 @@ with-uuid = ["sqlx/uuid", "sea-query/with-uuid"] with-time = ["sqlx/time", "sea-query/with-time"] with-ipnetwork = ["sqlx/ipnetwork", "sea-query/with-ipnetwork"] with-mac_address = ["sqlx/mac_address", "sea-query/with-mac_address"] -postgres-array = ["sea-query/postgres-array"] runtime-async-std-native-tls = ["sqlx/runtime-async-std-native-tls"] runtime-async-std-rustls = ["sqlx/runtime-async-std-rustls", ] runtime-actix-native-tls = ["sqlx/runtime-actix-native-tls"] diff --git a/sea-query-postgres/Cargo.toml b/sea-query-postgres/Cargo.toml index ffb5f6f28..e83d47791 100644 --- a/sea-query-postgres/Cargo.toml +++ b/sea-query-postgres/Cargo.toml @@ -17,7 +17,7 @@ rust-version = "1.60" [lib] [dependencies] -sea-query = { version = "^0", path = "..", features = ["thread-safe", "with-postgres"] } +sea-query = { version = "^0", path = "..", features = ["thread-safe"] } postgres-types = { version = "^0.2" } bytes = { version = "^1" } rust_decimal = { version = "^1", optional = true } diff --git a/sea-query-postgres/src/lib.rs b/sea-query-postgres/src/lib.rs index 9ee269d1a..1a547bd23 100644 --- a/sea-query-postgres/src/lib.rs +++ b/sea-query-postgres/src/lib.rs @@ -3,7 +3,8 @@ use std::error::Error; use bytes::BytesMut; use postgres_types::{to_sql_checked, IsNull, ToSql, Type}; -use sea_query::{query::*, QueryBuilder, Value, ValueTrait}; +use sea_query::BinOper::Is; +use sea_query::{query::*, QueryBuilder, Value, ValueType}; #[derive(Clone, Debug)] pub struct PostgresValue(pub Value); @@ -54,7 +55,61 @@ impl ToSql for PostgresValue { ty: &Type, out: &mut BytesMut, ) -> Result> { - self.0.to_sql(ty, out) + if self.0.is_null() { + return Ok(IsNull::Yes); + } + + match &self.0.ty() { + ValueType::Bool => { + let v = self.0.value::().unwrap(); + (*v).to_sql(ty, out) + } + ValueType::TinyInt => { + let v = self.0.value::().unwrap(); + (*v).to_sql(ty, out) + } + ValueType::SmallInt => { + let v = self.0.value::().unwrap(); + (*v).to_sql(ty, out) + } + ValueType::Int => { + let v = self.0.value::().unwrap(); + (*v).to_sql(ty, out) + } + ValueType::BigInt => { + let v = self.0.value::().unwrap(); + (*v).to_sql(ty, out) + } + ValueType::TinyUnsigned => { + let v = self.0.value::().unwrap(); + (*v as i16).to_sql(ty, out) + } + ValueType::SmallUnsigned => { + let v = self.0.value::().unwrap(); + (*v as i32).to_sql(ty, out) + } + ValueType::Unsigned => { + let v = self.0.value::().unwrap(); + (*v as i64).to_sql(ty, out) + } + ValueType::BigUnsigned => { + let v = self.0.value::().unwrap(); + (*v as i64).to_sql(ty, out) + } + ValueType::Float => { + let v = self.0.value::().unwrap(); + (*v).to_sql(ty, out) + } + ValueType::Double => { + let v = self.0.value::().unwrap(); + (*v).to_sql(ty, out) + } + ValueType::Char => { + let v = self.0.value::().unwrap(); + v.to_string().to_sql(ty, out) + } + _ => panic!(), + } } fn accepts(_ty: &Type) -> bool { diff --git a/sea-query-rusqlite/Cargo.toml b/sea-query-rusqlite/Cargo.toml index 6e15a12ea..947cd35e1 100644 --- a/sea-query-rusqlite/Cargo.toml +++ b/sea-query-rusqlite/Cargo.toml @@ -17,7 +17,7 @@ rust-version = "1.60" [lib] [dependencies] -sea-query = { version = "^0", path = "..", feature = ["with-rusqlite"] } +sea-query = { version = "^0", path = ".." } rusqlite = { version = "^0.28", features = ["bundled"] } [features] @@ -29,4 +29,3 @@ with-uuid = ["rusqlite/uuid", "sea-query/with-uuid"] with-time = ["rusqlite/time", "sea-query/with-time"] with-ipnetwork = ["sea-query/with-ipnetwork"] with-mac_address = ["sea-query/with-mac_address"] - diff --git a/src/value/mod.rs b/src/value/mod.rs index 3be547c5a..42062dc89 100644 --- a/src/value/mod.rs +++ b/src/value/mod.rs @@ -1,6 +1,8 @@ +pub use string::*; pub use tuple::*; pub use types::*; +mod string; mod tuple; mod types; diff --git a/src/value/string.rs b/src/value/string.rs new file mode 100644 index 000000000..cd1ed3eac --- /dev/null +++ b/src/value/string.rs @@ -0,0 +1,49 @@ +use crate::{IsNull, SeaRc, Value, ValueTrait, ValueType}; + +impl From for Value { + fn from(v: String) -> Value { + let object = Box::new(v) as _; + Value { + ty: String::value_type(), + object: SeaRc::new(object), + } + } +} + +impl ValueTrait for String { + fn to_sql_string(&self) -> String { + todo!() + } + + fn value_type() -> ValueType { + ValueType::String + } + + fn value_is_null(&self) -> IsNull { + IsNull::No + } +} + +impl ValueTrait for &str { + fn to_sql_string(&self) -> String { + todo!() + } + + fn value_type() -> ValueType { + ValueType::String + } + + fn value_is_null(&self) -> IsNull { + IsNull::No + } +} + +impl From<&str> for Value { + fn from(v: &str) -> Self { + let object = Box::new(v.to_string()) as _; + Value { + ty: <&str>::value_type(), + object: SeaRc::new(object), + } + } +} diff --git a/src/value/types.rs b/src/value/types.rs index ef891a4f3..ede4151d8 100644 --- a/src/value/types.rs +++ b/src/value/types.rs @@ -1,151 +1,245 @@ -use std::error::Error; -use std::fmt::{Debug, Formatter}; +use std::fmt::Debug; use crate::SeaRc; -#[cfg(feature = "with-postgres")] -use bytes::BytesMut; -#[cfg(feature = "with-postgres")] -use postgres_types::{to_sql_checked, IsNull, ToSql as PostgresToSql, Type}; -#[cfg(feature = "with-rusqlite")] -use rusqlite::{types::ToSqlOutput, Result, ToSql as RuSqliteToSql}; - -pub trait ValueTrait: PostgresToSql + RuSqliteToSql + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum ValueType { + Bool, + TinyInt, + SmallInt, + Int, + BigInt, + TinyUnsigned, + SmallUnsigned, + Unsigned, + BigUnsigned, + Float, + Double, + String, + Char, + Bytes, + + #[cfg(feature = "with-json")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-json")))] + Json, + + #[cfg(feature = "with-chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] + ChronoDate, + + #[cfg(feature = "with-chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] + ChronoTime, + + #[cfg(feature = "with-chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] + ChronoDateTime, + + #[cfg(feature = "with-chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] + ChronoDateTimeUtc, + + #[cfg(feature = "with-chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] + ChronoDateTimeLocal, + + #[cfg(feature = "with-chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] + ChronoDateTimeWithTimeZone, + + #[cfg(feature = "with-time")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))] + TimeDate, + + #[cfg(feature = "with-time")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))] + TimeTime, + + #[cfg(feature = "with-time")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))] + TimeDateTime, + + #[cfg(feature = "with-time")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))] + TimeDateTimeWithTimeZone, + + #[cfg(feature = "with-uuid")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-uuid")))] + Uuid, + + #[cfg(feature = "with-rust_decimal")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-rust_decimal")))] + Decimal, + + #[cfg(feature = "with-bigdecimal")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-bigdecimal")))] + BigDecimal, + + #[cfg(feature = "with-ipnetwork")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-ipnetwork")))] + IpNetwork, + + #[cfg(feature = "with-mac_address")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-mac_address")))] + MacAddress, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum IsNull { + /// The value is NULL. + Yes, + /// The value is not NULL. + No, +} + +#[cfg(feature = "thread-safe")] +pub trait ValueTrait: Sync + Send +where + Self: Debug, +{ + fn to_sql_string(&self) -> String; + + fn value_type() -> ValueType + where + Self: Sized; + + fn value_is_null(&self) -> IsNull; +} + +#[cfg(not(feature = "thread-safe"))] +pub trait ValueTrait where Self: Debug, { fn to_sql_string(&self) -> String; + + fn value_type() -> ValueType + where + Self: Sized; + + fn value_is_null(&self) -> IsNull; } #[derive(Debug, Clone)] -pub enum Value { - Bool(bool), - TinyInt(i8), - SmallInt(i16), - Int(i32), - BigInt(i64), - TinyUnsigned(u8), - SmallUnsigned(u16), - Unsigned(u32), - BigUnsigned(u64), - Float(f32), - Double(f64), +pub struct Value { + pub(crate) ty: ValueType, #[cfg(feature = "thread-safe")] - Object(SeaRc>), + pub(crate) object: SeaRc>, #[cfg(not(feature = "thread-safe"))] - Object(SeaRc>), + pub(crate) object: SeaRc>, } -impl PostgresToSql for Value { - fn to_sql( - &self, - ty: &Type, - out: &mut BytesMut, - ) -> Result> { - match self { - Value::Bool(v) => ::to_sql(v, ty, out), - Value::TinyInt(v) => ::to_sql(v, ty, out), - Value::SmallInt(v) => ::to_sql(v, ty, out), - Value::Int(v) => ::to_sql(v, ty, out), - Value::BigInt(v) => ::to_sql(v, ty, out), - Value::TinyUnsigned(v) => ::to_sql(&(*v as u32), ty, out), - Value::SmallUnsigned(v) => ::to_sql(&(*v as u32), ty, out), - Value::Unsigned(v) => ::to_sql(v, ty, out), - Value::BigUnsigned(v) => ::to_sql(&(*v as i64), ty, out), - Value::Float(v) => ::to_sql(v, ty, out), - Value::Double(v) => ::to_sql(v, ty, out), - Value::Object(o) => (o as &dyn PostgresToSql).to_sql(), - } +impl Value { + pub fn to_sql_string(&self) -> String { + todo!() } - fn accepts(_ty: &Type) -> bool { - true + pub fn ty(&self) -> &ValueType { + &self.ty } - to_sql_checked!(); -} - -impl RuSqliteToSql for Value { - fn to_sql(&self) -> Result> { - match self { - Value::Bool(v) => ::to_sql(v), - Value::TinyInt(v) => ::to_sql(v), - Value::SmallInt(v) => ::to_sql(v), - Value::Int(v) => ::to_sql(v), - Value::BigInt(v) => ::to_sql(v), - Value::TinyUnsigned(v) => ::to_sql(v), - Value::SmallUnsigned(v) => ::to_sql(v), - Value::Unsigned(v) => ::to_sql(v), - Value::BigUnsigned(v) => ::to_sql(v), - Value::Float(v) => ::to_sql(v), - Value::Double(v) => ::to_sql(v), - Value::Object(o) => (o as &dyn RuSqliteToSql).to_sql(), - } + pub fn is_null(&self) -> bool { + matches!(self.object.value_is_null(), IsNull::Yes) } -} -impl ValueTrait for Value { - fn to_sql_string(&self) -> String { - todo!() + pub fn value(&self) -> Result<&T, ()> { + if T::value_type() == self.ty { + let (v, _): (&T, *const ()) = + unsafe { std::mem::transmute(self.object.as_ref().as_ref()) }; + Ok(v) + } else { + Err(()) + } } } macro_rules! simple_to { - ( $type: ty, $name: expr ) => { + ( $type: ty, $value_type: expr ) => { impl From<$type> for Value { fn from(v: $type) -> Value { - use Value::*; - $name(v.to_owned()) + Value { + ty: <$type>::value_type(), + object: SeaRc::new(Box::new(v)), + } } } - }; -} -macro_rules! object_to { - ( $type: ty ) => { - impl From<$type> for Value { - fn from(v: $type) -> Value { - let object = Box::new(v) as _; - Value::Object(SeaRc::new(object)) + impl ValueTrait for $type { + fn to_sql_string(&self) -> String { + todo!() + } + + fn value_type() -> ValueType { + $value_type + } + + fn value_is_null(&self) -> IsNull { + IsNull::No } } }; } -simple_to!(i8, TinyInt); -simple_to!(i16, SmallInt); -simple_to!(i32, Int); -simple_to!(i64, BigInt); -simple_to!(u8, TinyUnsigned); -simple_to!(u16, SmallUnsigned); -simple_to!(u32, Unsigned); -simple_to!(u64, BigUnsigned); -simple_to!(f32, Float); -simple_to!(f64, Double); - -impl ValueTrait for String { +simple_to!(bool, ValueType::Bool); +simple_to!(i8, ValueType::TinyInt); +simple_to!(i16, ValueType::SmallInt); +simple_to!(i32, ValueType::Int); +simple_to!(i64, ValueType::BigInt); +simple_to!(u8, ValueType::TinyUnsigned); +simple_to!(u16, ValueType::SmallUnsigned); +simple_to!(u32, ValueType::Unsigned); +simple_to!(u64, ValueType::BigUnsigned); +simple_to!(f32, ValueType::Float); +simple_to!(f64, ValueType::Double); +simple_to!(char, ValueType::Char); + +impl ValueTrait for Option { fn to_sql_string(&self) -> String { todo!() } + + fn value_type() -> ValueType { + T::value_type() + } + + fn value_is_null(&self) -> IsNull { + match self { + Some(_) => IsNull::Yes, + None => IsNull::No, + } + } } -object_to!(String); +impl From> for Value { + fn from(v: Option) -> Self { + let object = Box::new(v) as _; + Value { + ty: T::value_type(), + object: SeaRc::new(object), + } + } +} -impl ValueTrait for &str { +impl ValueTrait for Vec { fn to_sql_string(&self) -> String { todo!() } -} -impl From<&str> for Value { - fn from(v: &str) -> Self { - let object = Box::new(v.to_string()) as _; - Value::Object(SeaRc::new(object)) + fn value_type() -> ValueType { + T::value_type() + } + + fn value_is_null(&self) -> IsNull { + IsNull::No } } -impl From for Value { - fn from(v: char) -> Self { - let object = Box::new(v.to_string()) as _; - Value::Object(SeaRc::new(object)) +impl From> for Value { + fn from(v: Vec) -> Self { + let object = Box::new(v) as _; + Value { + ty: T::value_type(), + object: SeaRc::new(object), + } } }