From ac92bd1fb272d87898e1338c0f596fb618337886 Mon Sep 17 00:00:00 2001 From: Ivan Krivosheev Date: Sun, 9 Oct 2022 19:39:51 +0300 Subject: [PATCH 01/10] issues-336 Third try impl postgres-array --- sea-query-binder/Cargo.toml | 29 +++- sea-query-binder/src/sqlx_postgres.rs | 219 +++++++++++++++++++++++- sea-query-postgres/Cargo.toml | 2 +- src/backend/postgres/table.rs | 6 +- src/backend/query_builder.rs | 4 +- src/query/insert.rs | 5 +- src/table/column.rs | 8 +- src/types.rs | 6 +- src/value.rs | 237 +++++++++++++++++++++----- 9 files changed, 445 insertions(+), 71 deletions(-) diff --git a/sea-query-binder/Cargo.toml b/sea-query-binder/Cargo.toml index 27e301278..104f6e17b 100644 --- a/sea-query-binder/Cargo.toml +++ b/sea-query-binder/Cargo.toml @@ -17,22 +17,33 @@ rust-version = "1.60" [lib] [dependencies] -sea-query = { version = "^0", path = ".." } +sea-query = { version = "^0", path = "..", features = ["thread-safe"] } sqlx = { version = "^0.6", 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 } +proc-macro2 = { version = "1", optional = true } +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 } [features] sqlx-mysql = ["sqlx/mysql"] sqlx-postgres = ["sqlx/postgres"] sqlx-sqlite = ["sqlx/sqlite"] sqlx-any = ["sqlx/any"] -with-chrono = ["sqlx/chrono", "sea-query/with-chrono"] -with-json = ["sqlx/json", "sea-query/with-json"] -with-rust_decimal = ["sqlx/decimal", "sea-query/with-rust_decimal"] -with-bigdecimal = ["sqlx/bigdecimal", "sea-query/with-bigdecimal"] -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"] +with-chrono = ["sqlx/chrono", "sea-query/with-chrono", "chrono"] +with-json = ["sqlx/json", "sea-query/with-json", "serde_json"] +with-rust_decimal = ["sqlx/decimal", "sea-query/with-rust_decimal", "rust_decimal"] +with-bigdecimal = ["sqlx/bigdecimal", "sea-query/with-bigdecimal", "bigdecimal"] +with-uuid = ["sqlx/uuid", "sea-query/with-uuid", "uuid"] +with-time = ["sqlx/time", "sea-query/with-time", "time"] +with-ipnetwork = ["sqlx/ipnetwork", "sea-query/with-ipnetwork", "ipnetwork"] +with-mac_address = ["sqlx/mac_address", "sea-query/with-mac_address", "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", ] diff --git a/sea-query-binder/src/sqlx_postgres.rs b/sea-query-binder/src/sqlx_postgres.rs index f25c9806c..160de1836 100644 --- a/sea-query-binder/src/sqlx_postgres.rs +++ b/sea-query-binder/src/sqlx_postgres.rs @@ -1,5 +1,32 @@ +use sea_query::{ArrayType, Value}; + +#[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-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::SqlxValues; -use sea_query::Value; impl<'q> sqlx::IntoArguments<'q, sqlx::postgres::Postgres> for SqlxValues { fn into_arguments(self) -> sqlx::postgres::PgArguments { @@ -26,13 +53,13 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::postgres::Postgres> for SqlxValues { args.add(i.map(|i| i as i16)); } Value::SmallUnsigned(i) => { - args.add(i.map(|i| i as i32)); + args.add(i.map(|i| i as i16)); } Value::Unsigned(i) => { args.add(i.map(|i| i as i64)); } Value::BigUnsigned(i) => { - args.add(i.map(|i| >::try_from(i).unwrap())); + args.add(i.map(|i| >::try_from(i).unwrap())); } Value::Float(f) => { args.add(f); @@ -105,10 +132,6 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::postgres::Postgres> for SqlxValues { Value::Json(j) => { args.add(j.as_deref()); } - #[cfg(feature = "postgres-array")] - Value::Array(_) => { - panic!("SeaQuery doesn't support array arguments for Postgresql"); - } #[cfg(feature = "with-ipnetwork")] Value::IpNetwork(ip) => { args.add(ip.as_deref()); @@ -117,6 +140,188 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::postgres::Postgres> for SqlxValues { Value::MacAddress(mac) => { args.add(mac.as_deref()); } + #[cfg(feature = "postgres-array")] + Value::Array(ty, v) => match ty { + ArrayType::Bool => { + let value: Option> = + Value::Array(ty, v).except("Invalid type for array value"); + args.add(value) + } + ArrayType::TinyInt => { + let value: Option> = + Value::Array(ty, v).except("Invalid type for array value"); + args.add(value) + } + ArrayType::SmallInt => { + let value: Option> = + Value::Array(ty, v).except("Invalid type for array value"); + args.add(value) + } + ArrayType::Int => { + let value: Option> = + Value::Array(ty, v).except("Invalid type for array value"); + args.add(value) + } + ArrayType::BigInt => { + let value: Option> = + Value::Array(ty, v).except("Invalid type for array value"); + args.add(value) + } + ArrayType::TinyUnsigned => { + let value: Option> = + Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = + value.map(|vec| vec.into_iter().map(|i| i as i16).collect()); + args.add(value) + } + ArrayType::SmallUnsigned => { + let value: Option> = + Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = + value.map(|vec| vec.into_iter().map(|i| i as i32).collect()); + args.add(value) + } + ArrayType::Unsigned => { + let value: Option> = + Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = + value.map(|vec| vec.into_iter().map(|i| i as i64).collect()); + args.add(value) + } + ArrayType::BigUnsigned => { + let value: Option> = + Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = value.map(|vec| { + vec.into_iter() + .map(|i| >::try_from(i).unwrap()) + .collect() + }); + args.add(value) + } + ArrayType::Float => { + let value: Option> = + Value::Array(ty, v).except("Invalid type for array value"); + args.add(value) + } + ArrayType::Double => { + let value: Option> = + Value::Array(ty, v).except("Invalid type for array value"); + args.add(value) + } + ArrayType::String => { + let value: Option> = + Value::Array(ty, v).except("Invalid type for array value"); + args.add(value) + } + ArrayType::Char => { + let value: Option> = + Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = + value.map(|vec| vec.into_iter().map(|c| c.to_string()).collect()); + args.add(value) + } + ArrayType::Bytes => { + let value: Option>> = + Value::Array(ty, v).except("Invalid type for array value"); + args.add(value) + } + #[cfg(feature = "with-chrono")] + ArrayType::ChronoDate => { + let value: Option> = + Value::Array(ty, v).except("Invalid type for array value"); + args.add(value); + } + #[cfg(feature = "with-chrono")] + ArrayType::ChronoTime => { + let value: Option> = + Value::Array(ty, v).except("Invalid type for array value"); + args.add(value); + } + #[cfg(feature = "with-chrono")] + ArrayType::ChronoDateTime => { + let value: Option> = + Value::Array(ty, v).except("Invalid type for array value"); + args.add(value); + } + #[cfg(feature = "with-chrono")] + ArrayType::ChronoDateTimeUtc => { + let value: Option>> = + Value::Array(ty, v).except("Invalid type for array value"); + args.add(value); + } + #[cfg(feature = "with-chrono")] + ArrayType::ChronoDateTimeLocal => { + let value: Option>> = + Value::Array(ty, v).except("Invalid type for array value"); + args.add(value); + } + #[cfg(feature = "with-chrono")] + ArrayType::ChronoDateTimeWithTimeZone => { + let value: Option>> = + Value::Array(ty, v).except("Invalid type for array value"); + args.add(value); + } + #[cfg(feature = "with-time")] + ArrayType::TimeDate => { + let value: Option> = + Value::Array(ty, v).except("Invalid type for array value"); + args.add(value); + } + #[cfg(feature = "with-time")] + ArrayType::TimeTime => { + let value: Option> = + Value::Array(ty, v).except("Invalid type for array value"); + args.add(value); + } + #[cfg(feature = "with-time")] + ArrayType::TimeDateTime => { + let value: Option> = + Value::Array(ty, v).except("Invalid type for array value"); + args.add(value); + } + #[cfg(feature = "with-time")] + ArrayType::TimeDateTimeWithTimeZone => { + let value: Option> = + Value::Array(ty, v).except("Invalid type for array value"); + args.add(value); + } + #[cfg(feature = "with-uuid")] + ArrayType::Uuid => { + let value: Option> = + Value::Array(ty, v).except("Invalid type for array value"); + args.add(value); + } + #[cfg(feature = "with-rust_decimal")] + ArrayType::Decimal => { + let value: Option> = + Value::Array(ty, v).except("Invalid type for array value"); + args.add(value); + } + #[cfg(feature = "with-bigdecimal")] + ArrayType::BigDecimal => { + let value: Option> = + Value::Array(ty, v).except("Invalid type for array value"); + args.add(value); + } + #[cfg(feature = "with-json")] + ArrayType::Json => { + let value: Option> = + Value::Array(ty, v).except("Invalid type for array value"); + args.add(value); + } + #[cfg(feature = "with-ipnetwork")] + ArrayType::IpNetwork => { + let value: Option> = + Value::Array(ty, v).except("Invalid type for array value"); + args.add(value); + } + #[cfg(feature = "with-mac_address")] + ArrayType::MacAddress => { + let value: Option> = + Value::Array(ty, v).except("Invalid type for array value"); + args.add(value); + } + }, } } args diff --git a/sea-query-postgres/Cargo.toml b/sea-query-postgres/Cargo.toml index 4afd7aa49..462d0b066 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 = "..", feature = ["thread-safe"] } postgres-types = { version = "^0.2" } bytes = { version = "^1" } rust_decimal = { version = "^1", optional = true } diff --git a/src/backend/postgres/table.rs b/src/backend/postgres/table.rs index a175528c1..c6334f742 100644 --- a/src/backend/postgres/table.rs +++ b/src/backend/postgres/table.rs @@ -96,7 +96,11 @@ impl TableBuilder for PostgresQueryBuilder { ColumnType::Json => "json".into(), ColumnType::JsonBinary => "jsonb".into(), ColumnType::Uuid => "uuid".into(), - ColumnType::Array(elem_type) => format!("{}[]", elem_type.as_ref().unwrap()), + ColumnType::Array(elem_type) => { + let mut sql = String::new(); + self.prepare_column_type(elem_type, &mut sql); + format!("{}[]", sql) + } ColumnType::Custom(iden) => iden.to_string(), ColumnType::Enum { name, .. } => name.to_string(), ColumnType::Cidr => "cidr".into(), diff --git a/src/backend/query_builder.rs b/src/backend/query_builder.rs index bf7b8859a..ec62c9ac4 100644 --- a/src/backend/query_builder.rs +++ b/src/backend/query_builder.rs @@ -966,7 +966,7 @@ pub trait QueryBuilder: QuotedBuilder + EscapeBuilder + TableRefBuilder { #[cfg(feature = "with-mac_address")] Value::MacAddress(None) => write!(s, "NULL").unwrap(), #[cfg(feature = "postgres-array")] - Value::Array(None) => write!(s, "NULL").unwrap(), + 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(), @@ -1036,7 +1036,7 @@ pub trait QueryBuilder: QuotedBuilder + EscapeBuilder + TableRefBuilder { #[cfg(feature = "with-uuid")] Value::Uuid(Some(v)) => write!(s, "'{}'", v).unwrap(), #[cfg(feature = "postgres-array")] - Value::Array(Some(v)) => write!( + Value::Array(_, Some(v)) => write!( s, "'{{{}}}'", v.iter() diff --git a/src/query/insert.rs b/src/query/insert.rs index dd1a6bab7..069bbc63b 100644 --- a/src/query/insert.rs +++ b/src/query/insert.rs @@ -198,10 +198,7 @@ impl InsertStatement { where I: IntoIterator, { - let values = values - .into_iter() - .map(|v| v.into()) - .collect::>(); + let values = values.into_iter().collect::>(); if self.columns.len() != values.len() { return Err(Error::ColValNumMismatch { col_len: self.columns.len(), diff --git a/src/table/column.rs b/src/table/column.rs index db4bc8fd5..78b1940ec 100644 --- a/src/table/column.rs +++ b/src/table/column.rs @@ -45,7 +45,7 @@ pub enum ColumnType { name: DynIden, variants: Vec, }, - Array(Option), + Array(SeaRc>), Cidr, Inet, MacAddr, @@ -64,7 +64,7 @@ pub enum ColumnSpec { } // All interval fields -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq)] pub enum PgInterval { Year, Month, @@ -524,8 +524,8 @@ impl ColumnDef { /// Set column type as an array with a specified element type. /// This is only supported on Postgres. - pub fn array(&mut self, elem_type: String) -> &mut Self { - self.types = Some(ColumnType::Array(Some(elem_type))); + pub fn array(&mut self, elem_type: ColumnType) -> &mut Self { + self.types = Some(ColumnType::Array(SeaRc::new(Box::new(elem_type)))); self } diff --git a/src/types.rs b/src/types.rs index 00ebe7b83..3c147c303 100644 --- a/src/types.rs +++ b/src/types.rs @@ -23,9 +23,9 @@ macro_rules! iden_trait { } fn to_string(&self) -> String { - let s = &mut String::new(); - self.unquoted(s); - s.to_owned() + let mut s = String::new(); + self.unquoted(&mut s); + s } fn unquoted(&self, s: &mut dyn fmt::Write); diff --git a/src/value.rs b/src/value.rs index 5f3192a50..423d151fa 100644 --- a/src/value.rs +++ b/src/value.rs @@ -31,6 +31,88 @@ use mac_address::MacAddress; use crate::{BlobSize, ColumnType, CommonSqlQueryBuilder, QueryBuilder}; +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum ArrayType { + 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, +} + /// Value variants /// /// We want Value to be exactly 1 pointer sized, so anything larger should be boxed. @@ -111,7 +193,7 @@ pub enum Value { #[cfg(feature = "postgres-array")] #[cfg_attr(docsrs, doc(cfg(feature = "postgres-array")))] - Array(Option>>), + Array(ArrayType, Option>>), #[cfg(feature = "with-ipnetwork")] #[cfg_attr(docsrs, doc(cfg(feature = "with-ipnetwork")))] @@ -135,8 +217,14 @@ pub trait ValueType: Sized { Self::try_from(v).unwrap() } + fn except(v: Value, msg: &str) -> Self { + Self::try_from(v).expect(msg) + } + fn type_name() -> String; + fn array_type() -> ArrayType; + fn column_type() -> ColumnType; } @@ -185,10 +273,17 @@ impl Value { { T::unwrap(self) } + + pub fn except(self, msg: &str) -> T + where + T: ValueType, + { + T::except(self, msg) + } } macro_rules! type_to_value { - ( $type: ty, $name: ident, $col_type: expr ) => { + ( $type: ty, $name: ident, $col_type: expr, $array_type: expr ) => { impl From<$type> for Value { fn from(x: $type) -> Value { Value::$name(Some(x)) @@ -213,6 +308,11 @@ macro_rules! type_to_value { stringify!($type).to_owned() } + fn array_type() -> ArrayType { + use ArrayType::*; + $array_type + } + fn column_type() -> ColumnType { use ColumnType::*; $col_type @@ -222,7 +322,7 @@ macro_rules! type_to_value { } macro_rules! type_to_box_value { - ( $type: ty, $name: ident, $col_type: expr ) => { + ( $type: ty, $name: ident, $col_type: expr, $array_type: expr ) => { impl From<$type> for Value { fn from(x: $type) -> Value { Value::$name(Some(Box::new(x))) @@ -247,6 +347,11 @@ macro_rules! type_to_box_value { stringify!($type).to_owned() } + fn array_type() -> ArrayType { + use ArrayType::*; + $array_type + } + fn column_type() -> ColumnType { use ColumnType::*; $col_type @@ -255,18 +360,18 @@ macro_rules! type_to_box_value { }; } -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)); +type_to_value!(bool, Bool, Boolean, Bool); +type_to_value!(i8, TinyInt, TinyInteger(None), TinyInt); +type_to_value!(i16, SmallInt, SmallInteger(None), SmallInt); +type_to_value!(i32, Int, Integer(None), Int); +type_to_value!(i64, BigInt, BigInteger(None), BigInt); +type_to_value!(u8, TinyUnsigned, TinyUnsigned(None), TinyUnsigned); +type_to_value!(u16, SmallUnsigned, SmallUnsigned(None), SmallUnsigned); +type_to_value!(u32, Unsigned, Unsigned(None), Unsigned); +type_to_value!(u64, BigUnsigned, BigUnsigned(None), BigUnsigned); +type_to_value!(f32, Float, Float(None), Float); +type_to_value!(f64, Double, Double(None), Double); +type_to_value!(char, Char, Char(None), Char); impl<'a> From<&'a [u8]> for Value { fn from(x: &'a [u8]) -> Value { @@ -315,20 +420,24 @@ where format!("Option<{}>", T::type_name()) } + fn array_type() -> ArrayType { + T::array_type() + } + 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)); +type_to_box_value!(Vec, Bytes, Binary(BlobSize::Blob(None)), Bytes); +type_to_box_value!(String, String, String(None), String); #[cfg(feature = "with-json")] #[cfg_attr(docsrs, doc(cfg(feature = "with-json")))] mod with_json { use super::*; - type_to_box_value!(Json, Json, Json); + type_to_box_value!(Json, Json, Json, Json); } #[cfg(feature = "with-chrono")] @@ -337,9 +446,14 @@ 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)); + type_to_box_value!(NaiveDate, ChronoDate, Date, ChronoDate); + type_to_box_value!(NaiveTime, ChronoTime, Time(None), ChronoTime); + type_to_box_value!( + NaiveDateTime, + ChronoDateTime, + DateTime(None), + ChronoDateTime + ); impl From> for Value { fn from(v: DateTime) -> Value { @@ -378,6 +492,10 @@ mod with_chrono { stringify!(DateTime).to_owned() } + fn array_type() -> ArrayType { + ArrayType::ChronoDateTimeUtc + } + fn column_type() -> ColumnType { ColumnType::TimestampWithTimeZone(None) } @@ -401,6 +519,10 @@ mod with_chrono { stringify!(DateTime).to_owned() } + fn array_type() -> ArrayType { + ArrayType::ChronoDateTimeLocal + } + fn column_type() -> ColumnType { ColumnType::TimestampWithTimeZone(None) } @@ -424,6 +546,10 @@ mod with_chrono { stringify!(DateTime).to_owned() } + fn array_type() -> ArrayType { + ArrayType::ChronoDateTimeWithTimeZone + } + fn column_type() -> ColumnType { ColumnType::TimestampWithTimeZone(None) } @@ -451,9 +577,14 @@ pub mod time_format { 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)); + type_to_box_value!(time::Date, TimeDate, Date, TimeDate); + type_to_box_value!(time::Time, TimeTime, Time(None), TimeTime); + type_to_box_value!( + PrimitiveDateTime, + TimeDateTime, + DateTime(None), + TimeDateTime + ); impl From for Value { fn from(v: OffsetDateTime) -> Value { @@ -479,6 +610,10 @@ mod with_time { stringify!(OffsetDateTime).to_owned() } + fn array_type() -> ArrayType { + ArrayType::TimeDateTimeWithTimeZone + } + fn column_type() -> ColumnType { ColumnType::TimestampWithTimeZone(None) } @@ -490,7 +625,7 @@ mod with_time { mod with_rust_decimal { use super::*; - type_to_box_value!(Decimal, Decimal, Decimal(None)); + type_to_box_value!(Decimal, Decimal, Decimal(None), Decimal); } #[cfg(feature = "with-bigdecimal")] @@ -498,7 +633,7 @@ mod with_rust_decimal { mod with_bigdecimal { use super::*; - type_to_box_value!(BigDecimal, BigDecimal, Decimal(None)); + type_to_box_value!(BigDecimal, BigDecimal, Decimal(None), BigDecimal); } #[cfg(feature = "with-uuid")] @@ -506,7 +641,7 @@ mod with_bigdecimal { mod with_uuid { use super::*; - type_to_box_value!(Uuid, Uuid, Uuid); + type_to_box_value!(Uuid, Uuid, Uuid, Uuid); } #[cfg(feature = "with-ipnetwork")] @@ -514,7 +649,7 @@ mod with_uuid { mod with_ipnetwork { use super::*; - type_to_box_value!(IpNetwork, IpNetwork, Inet); + type_to_box_value!(IpNetwork, IpNetwork, Inet, IpNetwork); } #[cfg(feature = "with-mac_address")] @@ -522,13 +657,14 @@ mod with_ipnetwork { mod with_mac_address { use super::*; - type_to_box_value!(MacAddress, MacAddress, MacAddr); + type_to_box_value!(MacAddress, MacAddress, MacAddr, MacAddress); } #[cfg(feature = "postgres-array")] #[cfg_attr(docsrs, doc(cfg(feature = "postgres-array")))] mod with_array { use super::*; + use crate::SeaRc; // 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. @@ -545,7 +681,11 @@ mod with_array { impl NotU8 for u64 {} impl NotU8 for f32 {} impl NotU8 for f64 {} + impl NotU8 for char {} impl NotU8 for String {} + impl NotU8 for Vec {} + + // TODO impl NotU8 for Option {} #[cfg(feature = "with-json")] impl NotU8 for Json {} @@ -585,19 +725,22 @@ mod with_array { impl From> for Value where - T: Into + NotU8, + T: Into + NotU8 + ValueType, { fn from(x: Vec) -> Value { - Value::Array(Some(Box::new(x.into_iter().map(|e| e.into()).collect()))) + Value::Array( + T::array_type(), + Some(Box::new(x.into_iter().map(|e| e.into()).collect())), + ) } } impl Nullable for Vec where - T: Into + NotU8, + T: Into + NotU8 + ValueType, { fn null() -> Value { - Value::Array(None) + Value::Array(T::array_type(), None) } } @@ -607,7 +750,9 @@ mod with_array { { fn try_from(v: Value) -> Result { match v { - Value::Array(Some(v)) => Ok(v.into_iter().map(|e| e.unwrap()).collect()), + Value::Array(ty, Some(v)) if T::array_type() == ty => { + Ok(v.into_iter().map(|e| e.unwrap()).collect()) + } _ => Err(ValueTypeErr), } } @@ -616,9 +761,13 @@ mod with_array { stringify!(Vec).to_owned() } + fn array_type() -> ArrayType { + T::array_type() + } + fn column_type() -> ColumnType { use ColumnType::*; - Array(None) + Array(SeaRc::new(Box::new(T::column_type()))) } } } @@ -880,12 +1029,12 @@ impl Value { #[cfg(feature = "postgres-array")] impl Value { pub fn is_array(&self) -> bool { - matches!(self, Self::Array(_)) + matches!(self, Self::Array(_, _)) } pub fn as_ref_array(&self) -> Option<&Vec> { match self { - Self::Array(v) => box_to_opt_ref!(v), + Self::Array(_, v) => box_to_opt_ref!(v), _ => panic!("not Value::Array"), } } @@ -1172,7 +1321,7 @@ pub fn sea_value_to_json_value(value: &Value) -> Json { #[cfg(feature = "with-uuid")] Value::Uuid(None) => Json::Null, #[cfg(feature = "postgres-array")] - Value::Array(None) => Json::Null, + Value::Array(_, None) => Json::Null, #[cfg(feature = "with-ipnetwork")] Value::IpNetwork(None) => Json::Null, #[cfg(feature = "with-mac_address")] @@ -1225,7 +1374,7 @@ pub fn sea_value_to_json_value(value: &Value) -> Json { #[cfg(feature = "with-uuid")] Value::Uuid(Some(v)) => Json::String(v.to_string()), #[cfg(feature = "postgres-array")] - Value::Array(Some(v)) => { + Value::Array(_, Some(v)) => { Json::Array(v.as_ref().iter().map(sea_value_to_json_value).collect()) } #[cfg(feature = "with-ipnetwork")] @@ -1616,7 +1765,7 @@ mod tests { #[test] #[cfg(feature = "with-uuid")] fn test_uuid_value() { - let uuid = uuid::Uuid::parse_str("936DA01F9ABD4d9d80C702AF85C822A8").unwrap(); + let uuid = Uuid::parse_str("936DA01F9ABD4d9d80C702AF85C822A8").unwrap(); let value: Value = uuid.into(); let out: uuid::Uuid = value.unwrap(); assert_eq!(out, uuid); @@ -1642,4 +1791,12 @@ mod tests { let out: Vec = v.unwrap(); assert_eq!(out, vec![1, 2, 3, 4, 5]); } + + #[test] + #[cfg(feature = "postgres-array")] + fn test_option_array_value() { + let v: Value = Value::Array(ArrayType::Int, None); + let out: Option> = v.unwrap(); + assert_eq!(out, None); + } } From a2931d89f30758b594f50d57406c2eb4af699ac9 Mon Sep 17 00:00:00 2001 From: Ivan Krivosheev Date: Mon, 10 Oct 2022 20:36:38 +0300 Subject: [PATCH 02/10] issues-336 Fix compile errors --- sea-query-binder/src/sqlx_any.rs | 2 +- sea-query-binder/src/sqlx_mysql.rs | 2 +- sea-query-binder/src/sqlx_sqlite.rs | 2 +- sea-query-postgres/Cargo.toml | 2 +- sea-query-rusqlite/src/lib.rs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/sea-query-binder/src/sqlx_any.rs b/sea-query-binder/src/sqlx_any.rs index 5c8fd3b65..bc03ec57d 100644 --- a/sea-query-binder/src/sqlx_any.rs +++ b/sea-query-binder/src/sqlx_any.rs @@ -114,7 +114,7 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::any::Any> for SqlxValues { panic!("SeaQuery doesn't support MacAddress arguments for Any"); } #[cfg(feature = "postgres-array")] - Value::Array(_) => { + Value::Array(_, _) => { panic!("SeaQuery doesn't support array arguments for Any"); } } diff --git a/sea-query-binder/src/sqlx_mysql.rs b/sea-query-binder/src/sqlx_mysql.rs index 991ce0133..460d7392b 100644 --- a/sea-query-binder/src/sqlx_mysql.rs +++ b/sea-query-binder/src/sqlx_mysql.rs @@ -106,7 +106,7 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::mysql::MySql> for SqlxValues { args.add(j.as_deref()); } #[cfg(feature = "postgres-array")] - Value::Array(_) => { + Value::Array(_, _) => { panic!("Mysql doesn't support array arguments"); } #[cfg(feature = "with-ipnetwork")] diff --git a/sea-query-binder/src/sqlx_sqlite.rs b/sea-query-binder/src/sqlx_sqlite.rs index c8af436a1..6ff6998e8 100644 --- a/sea-query-binder/src/sqlx_sqlite.rs +++ b/sea-query-binder/src/sqlx_sqlite.rs @@ -114,7 +114,7 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::sqlite::Sqlite> for SqlxValues { panic!("Sqlite doesn't support MacAddress arguments"); } #[cfg(feature = "postgres-array")] - Value::Array(_) => { + Value::Array(_, _) => { panic!("Sqlite doesn't support array arguments"); } } diff --git a/sea-query-postgres/Cargo.toml b/sea-query-postgres/Cargo.toml index 462d0b066..4afd7aa49 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 = "..", feature = ["thread-safe"] } +sea-query = { version = "^0", path = ".." } postgres-types = { version = "^0.2" } bytes = { version = "^1" } rust_decimal = { version = "^1", optional = true } diff --git a/sea-query-rusqlite/src/lib.rs b/sea-query-rusqlite/src/lib.rs index 86f18f571..6299bb183 100644 --- a/sea-query-rusqlite/src/lib.rs +++ b/sea-query-rusqlite/src/lib.rs @@ -126,7 +126,7 @@ impl ToSql for RusqliteValue { panic!("Rusqlite doesn't support MacAddress arguments"); } #[cfg(feature = "postgres-array")] - Value::Array(_) => { + Value::Array(_, _) => { panic!("Rusqlite doesn't support Array arguments"); } } From f554714c7d81308a17b1d243326afb8cff3aad41 Mon Sep 17 00:00:00 2001 From: Ivan Krivosheev Date: Mon, 10 Oct 2022 20:40:30 +0300 Subject: [PATCH 03/10] issues-336 Fix sqlx_postgres.rs --- sea-query-binder/src/sqlx_postgres.rs | 30 +++++++++++---------------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/sea-query-binder/src/sqlx_postgres.rs b/sea-query-binder/src/sqlx_postgres.rs index 160de1836..af71e696e 100644 --- a/sea-query-binder/src/sqlx_postgres.rs +++ b/sea-query-binder/src/sqlx_postgres.rs @@ -1,30 +1,24 @@ -use sea_query::{ArrayType, Value}; - -#[cfg(feature = "with-json")] -use serde_json::Value as Json; +#[cfg(feature = "with-ipnetwork")] +use std::net::IpAddr; #[cfg(feature = "with-json")] use std::str::from_utf8; +#[cfg(feature = "with-bigdecimal")] +use bigdecimal::BigDecimal; #[cfg(feature = "with-chrono")] use chrono::{DateTime, FixedOffset, Local, NaiveDate, NaiveDateTime, NaiveTime, Utc}; - +#[cfg(feature = "with-ipnetwork")] +use ipnetwork::IpNetwork; +#[cfg(feature = "with-mac_address")] +use mac_address::MacAddress; #[cfg(feature = "with-rust_decimal")] use rust_decimal::Decimal; - -#[cfg(feature = "with-bigdecimal")] -use bigdecimal::BigDecimal; - +#[cfg(feature = "with-json")] +use serde_json::Value as Json; #[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 sea_query::{ArrayType, Value}; use crate::SqlxValues; @@ -53,7 +47,7 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::postgres::Postgres> for SqlxValues { args.add(i.map(|i| i as i16)); } Value::SmallUnsigned(i) => { - args.add(i.map(|i| i as i16)); + args.add(i.map(|i| i as i32)); } Value::Unsigned(i) => { args.add(i.map(|i| i as i64)); From f32f6c86ab604ca52f888a984ccc9750d4cbaa67 Mon Sep 17 00:00:00 2001 From: Ivan Krivosheev Date: Mon, 10 Oct 2022 20:49:39 +0300 Subject: [PATCH 04/10] issues-336 Fix compile error --- sea-query-binder/src/sqlx_postgres.rs | 7 +------ sea-query-postgres/src/lib.rs | 6 +++--- src/value.rs | 6 ++++++ 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/sea-query-binder/src/sqlx_postgres.rs b/sea-query-binder/src/sqlx_postgres.rs index af71e696e..aaed5a8f5 100644 --- a/sea-query-binder/src/sqlx_postgres.rs +++ b/sea-query-binder/src/sqlx_postgres.rs @@ -1,12 +1,7 @@ -#[cfg(feature = "with-ipnetwork")] -use std::net::IpAddr; -#[cfg(feature = "with-json")] -use std::str::from_utf8; - #[cfg(feature = "with-bigdecimal")] use bigdecimal::BigDecimal; #[cfg(feature = "with-chrono")] -use chrono::{DateTime, FixedOffset, Local, NaiveDate, NaiveDateTime, NaiveTime, Utc}; +use chrono::{DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime, Utc}; #[cfg(feature = "with-ipnetwork")] use ipnetwork::IpNetwork; #[cfg(feature = "with-mac_address")] diff --git a/sea-query-postgres/src/lib.rs b/sea-query-postgres/src/lib.rs index 4ebffe656..782a29987 100644 --- a/sea-query-postgres/src/lib.rs +++ b/sea-query-postgres/src/lib.rs @@ -6,7 +6,7 @@ use postgres_types::{to_sql_checked, IsNull, ToSql, Type}; use sea_query::{query::*, QueryBuilder, Value}; #[derive(Clone, Debug, PartialEq)] -pub struct PostgresValue(pub sea_query::Value); +pub struct PostgresValue(pub Value); #[derive(Clone, Debug, PartialEq)] pub struct PostgresValues(pub Vec); @@ -101,13 +101,13 @@ impl ToSql for PostgresValue { #[cfg(feature = "with-uuid")] Value::Uuid(v) => v.as_deref().to_sql(ty, out), #[cfg(feature = "postgres-array")] - Value::Array(Some(v)) => v + 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), + Value::Array(_, None) => Ok(IsNull::Yes), #[allow(unreachable_patterns)] _ => unimplemented!(), } diff --git a/src/value.rs b/src/value.rs index 423d151fa..bc25ffb0d 100644 --- a/src/value.rs +++ b/src/value.rs @@ -723,6 +723,12 @@ mod with_array { #[cfg(feature = "with-uuid")] impl NotU8 for Uuid {} + #[cfg(feature = "with-ipnetwork")] + impl NotU8 for IpNetwork {} + + #[cfg(feature = "with-mac_address")] + impl NotU8 for MacAddress {} + impl From> for Value where T: Into + NotU8 + ValueType, From 8154a5a9544056f24cb6a43cfd591ab46c992c2b Mon Sep 17 00:00:00 2001 From: Ivan Krivosheev Date: Mon, 10 Oct 2022 21:03:40 +0300 Subject: [PATCH 05/10] issues-336 Fix example --- examples/postgres/src/main.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/postgres/src/main.rs b/examples/postgres/src/main.rs index 7d8799c99..a48a80e5d 100644 --- a/examples/postgres/src/main.rs +++ b/examples/postgres/src/main.rs @@ -1,7 +1,8 @@ use chrono::{DateTime, FixedOffset, NaiveDate, NaiveDateTime}; +use postgres::types::IsNull::No; use postgres::{Client, NoTls, Row}; use rust_decimal::Decimal; -use sea_query::{ColumnDef, Iden, Order, PostgresQueryBuilder, Query, Table}; +use sea_query::{ColumnDef, ColumnType, Iden, Order, PostgresQueryBuilder, Query, Table}; use sea_query_postgres::PostgresBinder; use time::{ macros::{date, offset, time}, @@ -34,7 +35,7 @@ fn main() { .col(ColumnDef::new(Document::Timestamp).timestamp()) .col(ColumnDef::new(Document::TimestampWithTimeZone).timestamp_with_time_zone()) .col(ColumnDef::new(Document::Decimal).decimal()) - .col(ColumnDef::new(Document::Array).array("integer".into())) + .col(ColumnDef::new(Document::Array).array(ColumnType::Integer(None))) .build(PostgresQueryBuilder), ] .join("; "); From 4cd974b2b134db5021e5461c11e3c79c84c204c3 Mon Sep 17 00:00:00 2001 From: Ivan Krivosheev Date: Wed, 12 Oct 2022 20:45:40 +0300 Subject: [PATCH 06/10] issues-336 Add small documentation --- src/value.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/value.rs b/src/value.rs index bc25ffb0d..a253bddf1 100644 --- a/src/value.rs +++ b/src/value.rs @@ -31,6 +31,7 @@ use mac_address::MacAddress; use crate::{BlobSize, ColumnType, CommonSqlQueryBuilder, QueryBuilder}; +/// [`Value`] types variant for Postgres array #[derive(Clone, Debug, Eq, PartialEq)] pub enum ArrayType { Bool, From 9335b75f836821340e1cfbfc56388c0117b32f6f Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Fri, 14 Oct 2022 18:07:58 +0800 Subject: [PATCH 07/10] Reuse $name ident in type_to_box_value macro for ArrayType variant --- src/value.rs | 43 ++++++++++++++++--------------------------- 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/src/value.rs b/src/value.rs index a253bddf1..84675e991 100644 --- a/src/value.rs +++ b/src/value.rs @@ -323,7 +323,7 @@ macro_rules! type_to_value { } macro_rules! type_to_box_value { - ( $type: ty, $name: ident, $col_type: expr, $array_type: expr ) => { + ( $type: ty, $name: ident, $col_type: expr ) => { impl From<$type> for Value { fn from(x: $type) -> Value { Value::$name(Some(Box::new(x))) @@ -349,8 +349,7 @@ macro_rules! type_to_box_value { } fn array_type() -> ArrayType { - use ArrayType::*; - $array_type + ArrayType::$name } fn column_type() -> ColumnType { @@ -430,15 +429,15 @@ where } } -type_to_box_value!(Vec, Bytes, Binary(BlobSize::Blob(None)), Bytes); -type_to_box_value!(String, String, String(None), String); +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, Json); + type_to_box_value!(Json, Json, Json); } #[cfg(feature = "with-chrono")] @@ -447,14 +446,9 @@ mod with_chrono { use super::*; use chrono::{Local, Offset, Utc}; - type_to_box_value!(NaiveDate, ChronoDate, Date, ChronoDate); - type_to_box_value!(NaiveTime, ChronoTime, Time(None), ChronoTime); - type_to_box_value!( - NaiveDateTime, - ChronoDateTime, - DateTime(None), - ChronoDateTime - ); + 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 { @@ -578,14 +572,9 @@ pub mod time_format { mod with_time { use super::*; - type_to_box_value!(time::Date, TimeDate, Date, TimeDate); - type_to_box_value!(time::Time, TimeTime, Time(None), TimeTime); - type_to_box_value!( - PrimitiveDateTime, - TimeDateTime, - DateTime(None), - TimeDateTime - ); + 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 { @@ -626,7 +615,7 @@ mod with_time { mod with_rust_decimal { use super::*; - type_to_box_value!(Decimal, Decimal, Decimal(None), Decimal); + type_to_box_value!(Decimal, Decimal, Decimal(None)); } #[cfg(feature = "with-bigdecimal")] @@ -634,7 +623,7 @@ mod with_rust_decimal { mod with_bigdecimal { use super::*; - type_to_box_value!(BigDecimal, BigDecimal, Decimal(None), BigDecimal); + type_to_box_value!(BigDecimal, BigDecimal, Decimal(None)); } #[cfg(feature = "with-uuid")] @@ -642,7 +631,7 @@ mod with_bigdecimal { mod with_uuid { use super::*; - type_to_box_value!(Uuid, Uuid, Uuid, Uuid); + type_to_box_value!(Uuid, Uuid, Uuid); } #[cfg(feature = "with-ipnetwork")] @@ -650,7 +639,7 @@ mod with_uuid { mod with_ipnetwork { use super::*; - type_to_box_value!(IpNetwork, IpNetwork, Inet, IpNetwork); + type_to_box_value!(IpNetwork, IpNetwork, Inet); } #[cfg(feature = "with-mac_address")] @@ -658,7 +647,7 @@ mod with_ipnetwork { mod with_mac_address { use super::*; - type_to_box_value!(MacAddress, MacAddress, MacAddr, MacAddress); + type_to_box_value!(MacAddress, MacAddress, MacAddr); } #[cfg(feature = "postgres-array")] From d5424aa99e9c3c245691141e92dd78ea53afeb85 Mon Sep 17 00:00:00 2001 From: Ivan Krivosheev Date: Fri, 14 Oct 2022 14:15:19 +0300 Subject: [PATCH 08/10] issues-336 Fix after review --- sea-query-binder/src/sqlx_postgres.rs | 123 +++++++++++++------------- src/value.rs | 4 +- 2 files changed, 65 insertions(+), 62 deletions(-) diff --git a/sea-query-binder/src/sqlx_postgres.rs b/sea-query-binder/src/sqlx_postgres.rs index aaed5a8f5..53258dd26 100644 --- a/sea-query-binder/src/sqlx_postgres.rs +++ b/sea-query-binder/src/sqlx_postgres.rs @@ -132,54 +132,54 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::postgres::Postgres> for SqlxValues { #[cfg(feature = "postgres-array")] Value::Array(ty, v) => match ty { ArrayType::Bool => { - let value: Option> = - Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = Value::Array(ty, v) + .except("This Value::Array should consist of Value::Bool"); args.add(value) } ArrayType::TinyInt => { - let value: Option> = - Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = Value::Array(ty, v) + .except("This Value::Array should consist of Value::TinyInt"); args.add(value) } ArrayType::SmallInt => { - let value: Option> = - Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = Value::Array(ty, v) + .except("This Value::Array should consist of Value::SmallInt"); args.add(value) } ArrayType::Int => { - let value: Option> = - Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = Value::Array(ty, v) + .except("This Value::Array should consist of Value::Int"); args.add(value) } ArrayType::BigInt => { - let value: Option> = - Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = Value::Array(ty, v) + .except("This Value::Array should consist of Value::BigInt"); args.add(value) } ArrayType::TinyUnsigned => { - let value: Option> = - Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = Value::Array(ty, v) + .except("This Value::Array should consist of Value::TinyUnsigned"); let value: Option> = value.map(|vec| vec.into_iter().map(|i| i as i16).collect()); args.add(value) } ArrayType::SmallUnsigned => { - let value: Option> = - Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = Value::Array(ty, v) + .except("This Value::Array should consist of Value::SmallUnsigned"); let value: Option> = value.map(|vec| vec.into_iter().map(|i| i as i32).collect()); args.add(value) } ArrayType::Unsigned => { - let value: Option> = - Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = Value::Array(ty, v) + .except("This Value::Array should consist of Value::Unsigned"); let value: Option> = value.map(|vec| vec.into_iter().map(|i| i as i64).collect()); args.add(value) } ArrayType::BigUnsigned => { - let value: Option> = - Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = Value::Array(ty, v) + .except("This Value::Array should consist of Value::BigUnsigned"); let value: Option> = value.map(|vec| { vec.into_iter() .map(|i| >::try_from(i).unwrap()) @@ -188,126 +188,129 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::postgres::Postgres> for SqlxValues { args.add(value) } ArrayType::Float => { - let value: Option> = - Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = Value::Array(ty, v) + .except("This Value::Array should consist of Value::Float"); args.add(value) } ArrayType::Double => { - let value: Option> = - Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = Value::Array(ty, v) + .except("This Value::Array should consist of Value::Double"); args.add(value) } ArrayType::String => { - let value: Option> = - Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = Value::Array(ty, v) + .except("This Value::Array should consist of Value::String"); args.add(value) } ArrayType::Char => { - let value: Option> = - Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = Value::Array(ty, v) + .except("This Value::Array should consist of Value::Char"); let value: Option> = value.map(|vec| vec.into_iter().map(|c| c.to_string()).collect()); args.add(value) } ArrayType::Bytes => { - let value: Option>> = - Value::Array(ty, v).except("Invalid type for array value"); + let value: Option>> = Value::Array(ty, v) + .except("This Value::Array should consist of Value::Bytes"); args.add(value) } #[cfg(feature = "with-chrono")] ArrayType::ChronoDate => { - let value: Option> = - Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = Value::Array(ty, v) + .except("This Value::Array should consist of Value::ChronoDate"); args.add(value); } #[cfg(feature = "with-chrono")] ArrayType::ChronoTime => { - let value: Option> = - Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = Value::Array(ty, v) + .except("This Value::Array should consist of Value::ChronoTime"); args.add(value); } #[cfg(feature = "with-chrono")] ArrayType::ChronoDateTime => { - let value: Option> = - Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = Value::Array(ty, v) + .except("This Value::Array should consist of Value::ChronoDateTime"); args.add(value); } #[cfg(feature = "with-chrono")] ArrayType::ChronoDateTimeUtc => { - let value: Option>> = - Value::Array(ty, v).except("Invalid type for array value"); + let value: Option>> = Value::Array(ty, v) + .except("This Value::Array should consist of Value::ChronoDateTimeUtc"); args.add(value); } #[cfg(feature = "with-chrono")] ArrayType::ChronoDateTimeLocal => { - let value: Option>> = - Value::Array(ty, v).except("Invalid type for array value"); + let value: Option>> = Value::Array(ty, v).except( + "This Value::Array should consist of Value::ChronoDateTimeLocal", + ); args.add(value); } #[cfg(feature = "with-chrono")] ArrayType::ChronoDateTimeWithTimeZone => { - let value: Option>> = - Value::Array(ty, v).except("Invalid type for array value"); + let value: Option>> = Value::Array(ty, v).except( + "This Value::Array should consist of Value::ChronoDateTimeWithTimeZone", + ); args.add(value); } #[cfg(feature = "with-time")] ArrayType::TimeDate => { - let value: Option> = - Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = Value::Array(ty, v) + .except("This Value::Array should consist of Value::TimeDate"); args.add(value); } #[cfg(feature = "with-time")] ArrayType::TimeTime => { - let value: Option> = - Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = Value::Array(ty, v) + .except("This Value::Array should consist of Value::TimeTime"); args.add(value); } #[cfg(feature = "with-time")] ArrayType::TimeDateTime => { - let value: Option> = - Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = Value::Array(ty, v) + .except("This Value::Array should consist of Value::TimeDateTime"); args.add(value); } #[cfg(feature = "with-time")] ArrayType::TimeDateTimeWithTimeZone => { - let value: Option> = - Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = Value::Array(ty, v).except( + "This Value::Array should consist of Value::TimeDateTimeWithTimeZone", + ); args.add(value); } #[cfg(feature = "with-uuid")] ArrayType::Uuid => { - let value: Option> = - Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = Value::Array(ty, v) + .except("This Value::Array should consist of Value::Uuid"); args.add(value); } #[cfg(feature = "with-rust_decimal")] ArrayType::Decimal => { - let value: Option> = - Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = Value::Array(ty, v) + .except("This Value::Array should consist of Value::Decimal"); args.add(value); } #[cfg(feature = "with-bigdecimal")] ArrayType::BigDecimal => { - let value: Option> = - Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = Value::Array(ty, v) + .except("This Value::Array should consist of Value::BigDecimal"); args.add(value); } #[cfg(feature = "with-json")] ArrayType::Json => { - let value: Option> = - Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = Value::Array(ty, v) + .except("This Value::Array should consist of Value::Json"); args.add(value); } #[cfg(feature = "with-ipnetwork")] ArrayType::IpNetwork => { - let value: Option> = - Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = Value::Array(ty, v) + .except("This Value::Array should consist of Value::IpNetwork"); args.add(value); } #[cfg(feature = "with-mac_address")] ArrayType::MacAddress => { - let value: Option> = - Value::Array(ty, v).except("Invalid type for array value"); + let value: Option> = Value::Array(ty, v) + .except("This Value::Array should consist of Value::MacAddress"); args.add(value); } }, diff --git a/src/value.rs b/src/value.rs index 84675e991..b491afb69 100644 --- a/src/value.rs +++ b/src/value.rs @@ -218,7 +218,7 @@ pub trait ValueType: Sized { Self::try_from(v).unwrap() } - fn except(v: Value, msg: &str) -> Self { + fn expect(v: Value, msg: &str) -> Self { Self::try_from(v).expect(msg) } @@ -279,7 +279,7 @@ impl Value { where T: ValueType, { - T::except(self, msg) + T::expect(self, msg) } } From b882d86e32bf232c619f079fc5e22051ea68839e Mon Sep 17 00:00:00 2001 From: Ivan Krivosheev Date: Fri, 14 Oct 2022 14:20:32 +0300 Subject: [PATCH 09/10] issues-336 Fix macros --- src/value.rs | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/value.rs b/src/value.rs index b491afb69..6d36f4ebf 100644 --- a/src/value.rs +++ b/src/value.rs @@ -284,7 +284,7 @@ impl Value { } macro_rules! type_to_value { - ( $type: ty, $name: ident, $col_type: expr, $array_type: expr ) => { + ( $type: ty, $name: ident, $col_type: expr ) => { impl From<$type> for Value { fn from(x: $type) -> Value { Value::$name(Some(x)) @@ -310,8 +310,7 @@ macro_rules! type_to_value { } fn array_type() -> ArrayType { - use ArrayType::*; - $array_type + ArrayType::$name } fn column_type() -> ColumnType { @@ -360,18 +359,18 @@ macro_rules! type_to_box_value { }; } -type_to_value!(bool, Bool, Boolean, Bool); -type_to_value!(i8, TinyInt, TinyInteger(None), TinyInt); -type_to_value!(i16, SmallInt, SmallInteger(None), SmallInt); -type_to_value!(i32, Int, Integer(None), Int); -type_to_value!(i64, BigInt, BigInteger(None), BigInt); -type_to_value!(u8, TinyUnsigned, TinyUnsigned(None), TinyUnsigned); -type_to_value!(u16, SmallUnsigned, SmallUnsigned(None), SmallUnsigned); -type_to_value!(u32, Unsigned, Unsigned(None), Unsigned); -type_to_value!(u64, BigUnsigned, BigUnsigned(None), BigUnsigned); -type_to_value!(f32, Float, Float(None), Float); -type_to_value!(f64, Double, Double(None), Double); -type_to_value!(char, Char, Char(None), Char); +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 { From 7bba3e2f73f4b30caacedd9baa6d3a226f6e5762 Mon Sep 17 00:00:00 2001 From: Ivan Krivosheev Date: Sat, 15 Oct 2022 18:06:21 +0300 Subject: [PATCH 10/10] issues-336 Rename function --- sea-query-binder/src/sqlx_postgres.rs | 60 +++++++++++++-------------- src/value.rs | 2 +- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/sea-query-binder/src/sqlx_postgres.rs b/sea-query-binder/src/sqlx_postgres.rs index 53258dd26..176ede156 100644 --- a/sea-query-binder/src/sqlx_postgres.rs +++ b/sea-query-binder/src/sqlx_postgres.rs @@ -133,53 +133,53 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::postgres::Postgres> for SqlxValues { Value::Array(ty, v) => match ty { ArrayType::Bool => { let value: Option> = Value::Array(ty, v) - .except("This Value::Array should consist of Value::Bool"); + .expect("This Value::Array should consist of Value::Bool"); args.add(value) } ArrayType::TinyInt => { let value: Option> = Value::Array(ty, v) - .except("This Value::Array should consist of Value::TinyInt"); + .expect("This Value::Array should consist of Value::TinyInt"); args.add(value) } ArrayType::SmallInt => { let value: Option> = Value::Array(ty, v) - .except("This Value::Array should consist of Value::SmallInt"); + .expect("This Value::Array should consist of Value::SmallInt"); args.add(value) } ArrayType::Int => { let value: Option> = Value::Array(ty, v) - .except("This Value::Array should consist of Value::Int"); + .expect("This Value::Array should consist of Value::Int"); args.add(value) } ArrayType::BigInt => { let value: Option> = Value::Array(ty, v) - .except("This Value::Array should consist of Value::BigInt"); + .expect("This Value::Array should consist of Value::BigInt"); args.add(value) } ArrayType::TinyUnsigned => { let value: Option> = Value::Array(ty, v) - .except("This Value::Array should consist of Value::TinyUnsigned"); + .expect("This Value::Array should consist of Value::TinyUnsigned"); let value: Option> = value.map(|vec| vec.into_iter().map(|i| i as i16).collect()); args.add(value) } ArrayType::SmallUnsigned => { let value: Option> = Value::Array(ty, v) - .except("This Value::Array should consist of Value::SmallUnsigned"); + .expect("This Value::Array should consist of Value::SmallUnsigned"); let value: Option> = value.map(|vec| vec.into_iter().map(|i| i as i32).collect()); args.add(value) } ArrayType::Unsigned => { let value: Option> = Value::Array(ty, v) - .except("This Value::Array should consist of Value::Unsigned"); + .expect("This Value::Array should consist of Value::Unsigned"); let value: Option> = value.map(|vec| vec.into_iter().map(|i| i as i64).collect()); args.add(value) } ArrayType::BigUnsigned => { let value: Option> = Value::Array(ty, v) - .except("This Value::Array should consist of Value::BigUnsigned"); + .expect("This Value::Array should consist of Value::BigUnsigned"); let value: Option> = value.map(|vec| { vec.into_iter() .map(|i| >::try_from(i).unwrap()) @@ -189,65 +189,65 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::postgres::Postgres> for SqlxValues { } ArrayType::Float => { let value: Option> = Value::Array(ty, v) - .except("This Value::Array should consist of Value::Float"); + .expect("This Value::Array should consist of Value::Float"); args.add(value) } ArrayType::Double => { let value: Option> = Value::Array(ty, v) - .except("This Value::Array should consist of Value::Double"); + .expect("This Value::Array should consist of Value::Double"); args.add(value) } ArrayType::String => { let value: Option> = Value::Array(ty, v) - .except("This Value::Array should consist of Value::String"); + .expect("This Value::Array should consist of Value::String"); args.add(value) } ArrayType::Char => { let value: Option> = Value::Array(ty, v) - .except("This Value::Array should consist of Value::Char"); + .expect("This Value::Array should consist of Value::Char"); let value: Option> = value.map(|vec| vec.into_iter().map(|c| c.to_string()).collect()); args.add(value) } ArrayType::Bytes => { let value: Option>> = Value::Array(ty, v) - .except("This Value::Array should consist of Value::Bytes"); + .expect("This Value::Array should consist of Value::Bytes"); args.add(value) } #[cfg(feature = "with-chrono")] ArrayType::ChronoDate => { let value: Option> = Value::Array(ty, v) - .except("This Value::Array should consist of Value::ChronoDate"); + .expect("This Value::Array should consist of Value::ChronoDate"); args.add(value); } #[cfg(feature = "with-chrono")] ArrayType::ChronoTime => { let value: Option> = Value::Array(ty, v) - .except("This Value::Array should consist of Value::ChronoTime"); + .expect("This Value::Array should consist of Value::ChronoTime"); args.add(value); } #[cfg(feature = "with-chrono")] ArrayType::ChronoDateTime => { let value: Option> = Value::Array(ty, v) - .except("This Value::Array should consist of Value::ChronoDateTime"); + .expect("This Value::Array should consist of Value::ChronoDateTime"); args.add(value); } #[cfg(feature = "with-chrono")] ArrayType::ChronoDateTimeUtc => { let value: Option>> = Value::Array(ty, v) - .except("This Value::Array should consist of Value::ChronoDateTimeUtc"); + .expect("This Value::Array should consist of Value::ChronoDateTimeUtc"); args.add(value); } #[cfg(feature = "with-chrono")] ArrayType::ChronoDateTimeLocal => { - let value: Option>> = Value::Array(ty, v).except( + let value: Option>> = Value::Array(ty, v).expect( "This Value::Array should consist of Value::ChronoDateTimeLocal", ); args.add(value); } #[cfg(feature = "with-chrono")] ArrayType::ChronoDateTimeWithTimeZone => { - let value: Option>> = Value::Array(ty, v).except( + let value: Option>> = Value::Array(ty, v).expect( "This Value::Array should consist of Value::ChronoDateTimeWithTimeZone", ); args.add(value); @@ -255,24 +255,24 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::postgres::Postgres> for SqlxValues { #[cfg(feature = "with-time")] ArrayType::TimeDate => { let value: Option> = Value::Array(ty, v) - .except("This Value::Array should consist of Value::TimeDate"); + .expect("This Value::Array should consist of Value::TimeDate"); args.add(value); } #[cfg(feature = "with-time")] ArrayType::TimeTime => { let value: Option> = Value::Array(ty, v) - .except("This Value::Array should consist of Value::TimeTime"); + .expect("This Value::Array should consist of Value::TimeTime"); args.add(value); } #[cfg(feature = "with-time")] ArrayType::TimeDateTime => { let value: Option> = Value::Array(ty, v) - .except("This Value::Array should consist of Value::TimeDateTime"); + .expect("This Value::Array should consist of Value::TimeDateTime"); args.add(value); } #[cfg(feature = "with-time")] ArrayType::TimeDateTimeWithTimeZone => { - let value: Option> = Value::Array(ty, v).except( + let value: Option> = Value::Array(ty, v).expect( "This Value::Array should consist of Value::TimeDateTimeWithTimeZone", ); args.add(value); @@ -280,37 +280,37 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::postgres::Postgres> for SqlxValues { #[cfg(feature = "with-uuid")] ArrayType::Uuid => { let value: Option> = Value::Array(ty, v) - .except("This Value::Array should consist of Value::Uuid"); + .expect("This Value::Array should consist of Value::Uuid"); args.add(value); } #[cfg(feature = "with-rust_decimal")] ArrayType::Decimal => { let value: Option> = Value::Array(ty, v) - .except("This Value::Array should consist of Value::Decimal"); + .expect("This Value::Array should consist of Value::Decimal"); args.add(value); } #[cfg(feature = "with-bigdecimal")] ArrayType::BigDecimal => { let value: Option> = Value::Array(ty, v) - .except("This Value::Array should consist of Value::BigDecimal"); + .expect("This Value::Array should consist of Value::BigDecimal"); args.add(value); } #[cfg(feature = "with-json")] ArrayType::Json => { let value: Option> = Value::Array(ty, v) - .except("This Value::Array should consist of Value::Json"); + .expect("This Value::Array should consist of Value::Json"); args.add(value); } #[cfg(feature = "with-ipnetwork")] ArrayType::IpNetwork => { let value: Option> = Value::Array(ty, v) - .except("This Value::Array should consist of Value::IpNetwork"); + .expect("This Value::Array should consist of Value::IpNetwork"); args.add(value); } #[cfg(feature = "with-mac_address")] ArrayType::MacAddress => { let value: Option> = Value::Array(ty, v) - .except("This Value::Array should consist of Value::MacAddress"); + .expect("This Value::Array should consist of Value::MacAddress"); args.add(value); } }, diff --git a/src/value.rs b/src/value.rs index 6d36f4ebf..323e29254 100644 --- a/src/value.rs +++ b/src/value.rs @@ -275,7 +275,7 @@ impl Value { T::unwrap(self) } - pub fn except(self, msg: &str) -> T + pub fn expect(self, msg: &str) -> T where T: ValueType, {