From 6ae05ef16cb578f136bd9983812b583c7a1379d7 Mon Sep 17 00:00:00 2001 From: Huliiiiii <308013446a@gmail.com> Date: Tue, 21 Oct 2025 20:43:28 +0800 Subject: [PATCH 01/19] Add postgres enum value support --- src/backend/query_builder.rs | 16 ++++++++++++++++ src/types/iden/core.rs | 10 ++++++++++ src/value.rs | 34 +++++++++++++++++++++++++++++++++- 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/backend/query_builder.rs b/src/backend/query_builder.rs index 43fe575df..9f90130eb 100644 --- a/src/backend/query_builder.rs +++ b/src/backend/query_builder.rs @@ -1111,6 +1111,16 @@ pub trait QueryBuilder: #[doc(hidden)] fn write_value_common(&self, buf: &mut impl Write, value: &Value) -> fmt::Result { + #[cfg(feature = "backend-postgres")] + fn prepare_enum(this: &impl QueryBuilder, r#enum: &Enum, sql: &mut impl Write) { + this.write_string_quoted(r#enum.value.as_str(), sql); + + if let Some(type_name) = &r#enum.type_name { + sql.write_str("::").unwrap(); + sql.write_str(&type_name).unwrap(); + } + } + match value { Value::Bool(None) | Value::TinyInt(None) @@ -1126,6 +1136,8 @@ pub trait QueryBuilder: | Value::String(None) | Value::Char(None) | Value::Bytes(None) => buf.write_str("NULL")?, + #[cfg(feature = "backend-postgres")] + Value::Enum(None) => buf.write_str("NULL")?, #[cfg(feature = "with-json")] Value::Json(None) => buf.write_str("NULL")?, #[cfg(feature = "with-chrono")] @@ -1203,6 +1215,10 @@ pub trait QueryBuilder: Value::Char(Some(v)) => { self.write_string_quoted(std::str::from_utf8(&[*v as u8]).unwrap(), buf) } + #[cfg(feature = "backend-postgres")] + Value::Enum(Some(enum_value)) => { + prepare_enum(self, enum_value, buf); + } Value::Bytes(Some(v)) => self.write_bytes(v, buf), #[cfg(feature = "with-json")] Value::Json(Some(v)) => self.write_string_quoted(&v.to_string(), buf), diff --git a/src/types/iden/core.rs b/src/types/iden/core.rs index 11214d65b..dec047386 100644 --- a/src/types/iden/core.rs +++ b/src/types/iden/core.rs @@ -77,6 +77,10 @@ impl DynIden { pub fn inner(&self) -> Cow<'static, str> { self.0.clone() } + + pub fn as_str(&self) -> &str { + &self.0 + } } impl std::fmt::Display for DynIden { @@ -85,6 +89,12 @@ impl std::fmt::Display for DynIden { } } +impl AsRef for DynIden { + fn as_ref(&self) -> &str { + &self.0 + } +} + pub trait IntoIden: Into { fn into_iden(self) -> DynIden; } diff --git a/src/value.rs b/src/value.rs index 42c82141e..610da2b91 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,6 +1,8 @@ //! Container for all SQL value types. use std::borrow::Cow; +#[cfg(feature = "backend-postgres")] +use std::sync::Arc; #[cfg(feature = "with-json")] use serde_json::Value as Json; @@ -34,7 +36,7 @@ use std::net::IpAddr; #[cfg(feature = "with-mac_address")] use mac_address::MacAddress; -use crate::{ColumnType, CommonSqlQueryBuilder, QueryBuilder, StringLen}; +use crate::{ColumnType, CommonSqlQueryBuilder, DynIden, QueryBuilder, StringLen}; #[cfg(test)] mod tests; @@ -118,6 +120,8 @@ pub enum ArrayType { String, Char, Bytes, + #[cfg(feature = "backend-postgres")] + Enum(Arc), #[cfg(feature = "with-json")] #[cfg_attr(docsrs, doc(cfg(feature = "with-json")))] @@ -227,6 +231,10 @@ pub enum Value { Double(Option), String(Option), Char(Option), + /// In most cases, the values of enums are staticly known, + /// so we use Arc to save space + #[cfg(feature = "backend-postgres")] + Enum(Option>), #[allow(clippy::box_collection)] Bytes(Option>), @@ -324,6 +332,28 @@ pub enum Value { MacAddress(Option), } +#[cfg(feature = "backend-postgres")] +#[derive(Debug, Clone, PartialEq)] +pub struct Enum { + /// The type_name is only used for the Postgres backend + /// + /// In most cases, the enum type name is staticly known, + /// we wrap it in an [`Arc`] to save space. + pub(crate) type_name: Option>, + pub(crate) value: DynIden, +} + +#[cfg(feature = "backend-postgres")] +impl Enum { + /// Create a new [`EnumValue`] + pub fn new(type_name: impl Into>>, value: DynIden) -> Self { + Self { + type_name: type_name.into(), + value, + } + } +} + /// This test is to check if the size of [`Value`] exceeds the limit. /// /// If the size exceeds the limit, you should box the variant. @@ -399,6 +429,7 @@ impl Value { Self::Double(_) => Self::Double(None), Self::String(_) => Self::String(None), Self::Char(_) => Self::Char(None), + Self::Enum(_) => Self::Enum(None), Self::Bytes(_) => Self::Bytes(None), #[cfg(feature = "with-json")] @@ -519,6 +550,7 @@ impl Value { Self::Double(_) => Self::Double(Some(Default::default())), Self::String(_) => Self::String(Some(Default::default())), Self::Char(_) => Self::Char(Some(Default::default())), + Self::Enum(value) => Self::Enum(value.clone()), Self::Bytes(_) => Self::Bytes(Some(Default::default())), #[cfg(feature = "with-json")] From 52ab44b8b873f92435151659222fc69e35543c5b Mon Sep 17 00:00:00 2001 From: Huliiiiii <308013446a@gmail.com> Date: Wed, 29 Oct 2025 00:25:29 +0800 Subject: [PATCH 02/19] Refactor Value::Array --- Cargo.toml | 4 +- src/value.rs | 25 +++++++---- src/value/array.rs | 86 +++++++++++++++++++++++++++++++++++++ src/value/hashable_value.rs | 67 ++++++++++++++++++----------- 4 files changed, 147 insertions(+), 35 deletions(-) create mode 100644 src/value/array.rs diff --git a/Cargo.toml b/Cargo.toml index ee2551d8a..eb820317e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,8 +29,8 @@ path = "src/lib.rs" [dependencies] inherent = "1.0" -sea-query-derive = { version = "1.0.0-rc", path = "sea-query-derive", optional = true } -serde = { version = "1", default-features = false, optional = true, features = ["std", "derive"] } +sea-query-derive = { version = "1.0.0-rc", path = "sea-query-derive", default-features = false, optional = true } +serde = { version = "1", default-features = false, optional = true, features = ["std", "derive", "rc"] } serde_json = { version = "1", default-features = false, optional = true, features = ["std"] } chrono = { version = "0.4.27", default-features = false, optional = true, features = ["clock"] } postgres-types = { version = "0", default-features = false, optional = true } diff --git a/src/value.rs b/src/value.rs index 610da2b91..75f18833a 100644 --- a/src/value.rs +++ b/src/value.rs @@ -4,13 +4,10 @@ use std::borrow::Cow; #[cfg(feature = "backend-postgres")] use std::sync::Arc; -#[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-json")] +use serde_json::Value as Json; #[cfg(feature = "with-time")] use time::{OffsetDateTime, PrimitiveDateTime}; @@ -36,7 +33,13 @@ use std::net::IpAddr; #[cfg(feature = "with-mac_address")] use mac_address::MacAddress; +#[cfg(feature = "postgres-array")] +#[cfg_attr(docsrs, doc(cfg(feature = "postgres-array")))] +mod array; + use crate::{ColumnType, CommonSqlQueryBuilder, DynIden, QueryBuilder, StringLen}; +#[cfg(feature = "postgres-array")] +pub use array::Array; #[cfg(test)] mod tests; @@ -120,6 +123,7 @@ pub enum ArrayType { String, Char, Bytes, + /// The type name of the enum #[cfg(feature = "backend-postgres")] Enum(Arc), @@ -317,7 +321,7 @@ pub enum Value { #[cfg(feature = "postgres-array")] #[cfg_attr(docsrs, doc(cfg(feature = "postgres-array")))] - Array(ArrayType, Option>>), + Array(Option>), #[cfg(feature = "postgres-vector")] #[cfg_attr(docsrs, doc(cfg(feature = "postgres-vector")))] @@ -333,9 +337,10 @@ pub enum Value { } #[cfg(feature = "backend-postgres")] -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Enum { - /// The type_name is only used for the Postgres backend + /// The type_name is only used for the Postgres /// /// In most cases, the enum type name is staticly known, /// we wrap it in an [`Arc`] to save space. @@ -429,8 +434,9 @@ impl Value { Self::Double(_) => Self::Double(None), Self::String(_) => Self::String(None), Self::Char(_) => Self::Char(None), - Self::Enum(_) => Self::Enum(None), Self::Bytes(_) => Self::Bytes(None), + #[cfg(feature = "backend-postgres")] + Self::Enum(_) => Self::Enum(None), #[cfg(feature = "with-json")] #[cfg_attr(docsrs, doc(cfg(feature = "with-json")))] @@ -550,6 +556,7 @@ impl Value { Self::Double(_) => Self::Double(Some(Default::default())), Self::String(_) => Self::String(Some(Default::default())), Self::Char(_) => Self::Char(Some(Default::default())), + #[cfg(feature = "backend-postgres")] Self::Enum(value) => Self::Enum(value.clone()), Self::Bytes(_) => Self::Bytes(Some(Default::default())), diff --git a/src/value/array.rs b/src/value/array.rs new file mode 100644 index 000000000..698947e3d --- /dev/null +++ b/src/value/array.rs @@ -0,0 +1,86 @@ +use super::*; + +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[non_exhaustive] +pub enum Array { + Bool(Box<[Option]>), + TinyInt(Box<[Option]>), + SmallInt(Box<[Option]>), + Int(Box<[Option]>), + BigInt(Box<[Option]>), + TinyUnsigned(Box<[Option]>), + SmallUnsigned(Box<[Option]>), + Unsigned(Box<[Option]>), + BigUnsigned(Box<[Option]>), + Float(Box<[Option]>), + Double(Box<[Option]>), + String(Box<[Option]>), + Char(Box<[Option]>), + Bytes(Box<[Option>]>), + Enum(Arc, Box<[Option>]>), + Array(Box<[Option]>), + #[cfg(feature = "with-json")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-json")))] + Json(Box<[Option]>), + #[cfg(feature = "with-chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] + ChronoDate(Box<[Option]>), + #[cfg(feature = "with-chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] + ChronoTime(Box<[Option]>), + #[cfg(feature = "with-chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] + ChronoDateTime(Box<[Option]>), + #[cfg(feature = "with-chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] + ChronoDateTimeUtc(Box<[Option>]>), + #[cfg(feature = "with-chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] + ChronoDateTimeLocal(Box<[Option>]>), + #[cfg(feature = "with-chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-chrono")))] + ChronoDateTimeWithTimeZone(Box<[Option>]>), + #[cfg(feature = "with-time")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))] + TimeDate(Box<[Option]>), + #[cfg(feature = "with-time")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))] + TimeTime(Box<[Option]>), + #[cfg(feature = "with-time")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))] + TimeDateTime(Box<[Option]>), + #[cfg(feature = "with-time")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-time")))] + TimeDateTimeWithTimeZone(Box<[Option]>), + #[cfg(feature = "with-jiff")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))] + JiffDate(Box<[Option]>), + #[cfg(feature = "with-jiff")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))] + JiffTime(Box<[Option]>), + #[cfg(feature = "with-jiff")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))] + JiffDateTime(Box<[Option>]>), + #[cfg(feature = "with-jiff")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))] + JiffTimestamp(Box<[Option>]>), + #[cfg(feature = "with-jiff")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))] + JiffZoned(Box<[Option>]>), + #[cfg(feature = "with-uuid")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-uuid")))] + Uuid(Box<[Option]>), + #[cfg(feature = "with-rust_decimal")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-rust_decimal")))] + Decimal(Box<[Option]>), + #[cfg(feature = "with-bigdecimal")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-bigdecimal")))] + BigDecimal(Box<[Option>]>), + #[cfg(feature = "with-ipnetwork")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-ipnetwork")))] + IpNetwork(Box<[Option]>), + #[cfg(feature = "with-mac_address")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-mac_address")))] + MacAddress(Box<[Option]>), +} diff --git a/src/value/hashable_value.rs b/src/value/hashable_value.rs index e51a9234d..5dda93cae 100644 --- a/src/value/hashable_value.rs +++ b/src/value/hashable_value.rs @@ -22,6 +22,8 @@ impl PartialEq for Value { (Self::String(l), Self::String(r)) => l == r, (Self::Char(l), Self::Char(r)) => l == r, (Self::Bytes(l), Self::Bytes(r)) => l == r, + #[cfg(feature = "backend-postgres")] + (Self::Enum(l), Self::Enum(r)) => l == r, #[cfg(feature = "with-json")] (Self::Json(l), Self::Json(r)) => cmp_json(l, r), @@ -69,9 +71,7 @@ impl PartialEq for Value { (Self::BigDecimal(l), Self::BigDecimal(r)) => l == r, #[cfg(feature = "postgres-array")] - (Self::Array(ty_l, values_l), Self::Array(ty_r, values_r)) => { - ty_l == ty_r && values_l == values_r - } + (Self::Array(l), Self::Array(r)) => l == r, #[cfg(feature = "postgres-vector")] (Self::Vector(l), Self::Vector(r)) => cmp_vector(l, r), @@ -107,6 +107,8 @@ impl Hash for Value { Value::String(v) => v.hash(state), Value::Char(v) => v.hash(state), Value::Bytes(v) => v.hash(state), + #[cfg(feature = "backend-postgres")] + Value::Enum(v) => v.hash(state), #[cfg(feature = "with-json")] Value::Json(value) => hash_json(value, state), @@ -154,10 +156,21 @@ impl Hash for Value { Value::BigDecimal(big_decimal) => big_decimal.hash(state), #[cfg(feature = "postgres-array")] - Value::Array(array_type, vec) => { - array_type.hash(state); - vec.hash(state); - } + Value::Array(array) => match array { + Some(array) => { + if array.as_ref().is_null() { + 2u8.hash(state); + array.as_ref().array_type().hash(state); + } else { + 1u8.hash(state); + array.as_ref().array_type().hash(state); + for value in array.as_ref().to_value_vec() { + value.hash(state); + } + } + } + None => 0u8.hash(state), + }, #[cfg(feature = "postgres-vector")] Value::Vector(vector) => hash_vector(vector, state), @@ -322,30 +335,36 @@ mod tests { #[cfg(feature = "postgres-array")] #[test] fn test_hash_value_array() { - use crate::ArrayType; + use crate::{value::Array, ArrayType}; assert_eq!( Into::::into(vec![0i32, 1, 2]), - Value::Array( - ArrayType::Int, - Some(Box::new(vec![ - Value::Int(Some(0)), - Value::Int(Some(1)), - Value::Int(Some(2)) - ])) - ) + Value::Array(Some(Box::new( + Array::try_from_parts( + ArrayType::Int, + vec![ + Value::Int(Some(0)), + Value::Int(Some(1)), + Value::Int(Some(2)), + ], + ) + .expect("array construction"), + ))) ); assert_eq!( Into::::into(vec![0f32, 1.0, 2.0]), - Value::Array( - ArrayType::Float, - Some(Box::new(vec![ - Value::Float(Some(0f32)), - Value::Float(Some(1.0)), - Value::Float(Some(2.0)) - ])) - ) + Value::Array(Some(Box::new( + Array::try_from_parts( + ArrayType::Float, + vec![ + Value::Float(Some(0f32)), + Value::Float(Some(1.0)), + Value::Float(Some(2.0)), + ], + ) + .expect("array construction"), + ))) ); let hash_set: std::collections::HashSet = [ From fea232c85656939fcf6c68aaf5d19a2b80605a1f Mon Sep 17 00:00:00 2001 From: Huliiiiii <308013446a@gmail.com> Date: Thu, 23 Oct 2025 21:58:51 +0800 Subject: [PATCH 03/19] WIP --- src/backend/query_builder.rs | 17 +- src/types/iden/core.rs | 1 + src/value.rs | 50 ++- src/value/array.rs | 774 ++++++++++++++++++++++++++++++++++- src/value/hashable_value.rs | 10 +- src/value/with_array.rs | 168 ++++++-- src/value/with_bigdecimal.rs | 4 +- src/value/with_jiff.rs | 12 +- src/value/with_json.rs | 7 +- 9 files changed, 957 insertions(+), 86 deletions(-) diff --git a/src/backend/query_builder.rs b/src/backend/query_builder.rs index 9f90130eb..e194ce263 100644 --- a/src/backend/query_builder.rs +++ b/src/backend/query_builder.rs @@ -1121,6 +1121,9 @@ pub trait QueryBuilder: } } + #[cfg(feature = "postgres-array")] + fn prepare_array(this: &impl QueryBuilder, array: &Array, sql: &mut impl Write) {} + match value { Value::Bool(None) | Value::TinyInt(None) @@ -1181,7 +1184,7 @@ pub trait QueryBuilder: #[cfg(feature = "with-mac_address")] Value::MacAddress(None) => buf.write_str("NULL")?, #[cfg(feature = "postgres-array")] - Value::Array(_, None) => buf.write_str("NULL")?, + Value::Array(None) => buf.write_str("NULL")?, #[cfg(feature = "postgres-vector")] Value::Vector(None) => buf.write_str("NULL")?, Value::Bool(Some(b)) => buf.write_str(if *b { "TRUE" } else { "FALSE" })?, @@ -1330,22 +1333,14 @@ pub trait QueryBuilder: buf.write_str("'")?; } #[cfg(feature = "postgres-array")] - Value::Array(_, Some(v)) => { + Value::Array(Some(v)) => { if v.is_empty() { buf.write_str("'{}'")?; } else { buf.write_str("ARRAY [")?; - let mut viter = v.iter(); - - if let Some(element) = viter.next() { - self.write_value(buf, element)?; - } + prepare_array(self, v, buf); - for element in viter { - buf.write_str(",")?; - self.write_value(buf, element)?; - } buf.write_str("]")?; } } diff --git a/src/types/iden/core.rs b/src/types/iden/core.rs index dec047386..7db6eb527 100644 --- a/src/types/iden/core.rs +++ b/src/types/iden/core.rs @@ -71,6 +71,7 @@ pub trait IdenStatic: Iden + Copy + 'static { /// Nowadays, it's an eagerly-rendered string. /// Most identifiers are static strings that aren't "rendered" at runtime anyway. #[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct DynIden(pub(crate) Cow<'static, str>); impl DynIden { diff --git a/src/value.rs b/src/value.rs index 75f18833a..3d8cf8268 100644 --- a/src/value.rs +++ b/src/value.rs @@ -297,15 +297,15 @@ pub enum Value { #[cfg(feature = "with-jiff")] #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))] - JiffDateTime(Option>), + JiffDateTime(Option), #[cfg(feature = "with-jiff")] #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))] - JiffTimestamp(Option>), + JiffTimestamp(Option), #[cfg(feature = "with-jiff")] #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))] - JiffZoned(Option>), + JiffZoned(Option), #[cfg(feature = "with-uuid")] #[cfg_attr(docsrs, doc(cfg(feature = "with-uuid")))] @@ -317,11 +317,11 @@ pub enum Value { #[cfg(feature = "with-bigdecimal")] #[cfg_attr(docsrs, doc(cfg(feature = "with-bigdecimal")))] - BigDecimal(Option>), + BigDecimal(Option), #[cfg(feature = "postgres-array")] #[cfg_attr(docsrs, doc(cfg(feature = "postgres-array")))] - Array(Option>), + Array(Option), #[cfg(feature = "postgres-vector")] #[cfg_attr(docsrs, doc(cfg(feature = "postgres-vector")))] @@ -371,16 +371,35 @@ impl Enum { pub const VALUE_SIZE: usize = check_value_size(); const MAX_VALUE_SIZE: usize = 32; -#[cfg(feature = "with-json")] const EXPECTED_VALUE_SIZE: usize = { - if size_of::>() > MAX_VALUE_SIZE { - size_of::>() - } else { - MAX_VALUE_SIZE + let mut max = MAX_VALUE_SIZE; + // If some crate enabled indexmap feature, the size of Json will be 72 or larger. + #[cfg(feature = "with-json")] + { + if size_of::>() > max { + max = size_of::>(); + } + } + + // If bigdecimal is enabled and its size is larger, we make the limit to be bigdecimal's size + #[cfg(feature = "with-bigdecimal")] + { + if size_of::>() > MAX_VALUE_SIZE { + max = size_of::>(); + } } + + // Jiff has extra size in debug mode. Skip size check in that case. + #[cfg(feature = "with-jiff")] + { + let zoned_size = size_of::>(); + if zoned_size > max && cfg!(debug_assertions) { + max = zoned_size; + } + } + + max }; -#[cfg(not(feature = "with-json"))] -const EXPECTED_VALUE_SIZE: usize = MAX_VALUE_SIZE; const fn check_value_size() -> usize { if std::mem::size_of::() > EXPECTED_VALUE_SIZE { @@ -516,7 +535,7 @@ impl Value { #[cfg(feature = "postgres-array")] #[cfg_attr(docsrs, doc(cfg(feature = "postgres-array")))] - Self::Array(ty, _) => Self::Array(ty.clone(), None), + Self::Array(_) => Self::Array(None), #[cfg(feature = "postgres-vector")] #[cfg_attr(docsrs, doc(cfg(feature = "postgres-vector")))] @@ -648,7 +667,10 @@ impl Value { #[cfg(feature = "postgres-array")] #[cfg_attr(docsrs, doc(cfg(feature = "postgres-array")))] - Self::Array(ty, _) => Self::Array(ty.clone(), Some(Default::default())), + Self::Array(Some(arr)) => Self::Array(Some(arr.dummy_value())), + #[cfg(feature = "postgres-array")] + #[cfg_attr(docsrs, doc(cfg(feature = "postgres-array")))] + Self::Array(None) => Self::Array(None), #[cfg(feature = "postgres-vector")] #[cfg_attr(docsrs, doc(cfg(feature = "postgres-vector")))] diff --git a/src/value/array.rs b/src/value/array.rs index 698947e3d..635a095ed 100644 --- a/src/value/array.rs +++ b/src/value/array.rs @@ -1,6 +1,7 @@ use super::*; -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone)] +#[cfg_attr(not(feature = "hashable-value"), derive(PartialEq))] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[non_exhaustive] pub enum Array { @@ -18,8 +19,8 @@ pub enum Array { String(Box<[Option]>), Char(Box<[Option]>), Bytes(Box<[Option>]>), - Enum(Arc, Box<[Option>]>), - Array(Box<[Option]>), + Enum(Box<(Arc, Box<[Option>]>)>), + Array(Box<(ArrayType, Box<[Option]>)>), #[cfg(feature = "with-json")] #[cfg_attr(docsrs, doc(cfg(feature = "with-json")))] Json(Box<[Option]>), @@ -61,13 +62,13 @@ pub enum Array { JiffTime(Box<[Option]>), #[cfg(feature = "with-jiff")] #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))] - JiffDateTime(Box<[Option>]>), + JiffDateTime(Box<[Option]>), #[cfg(feature = "with-jiff")] #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))] - JiffTimestamp(Box<[Option>]>), + JiffTimestamp(Box<[Option]>), #[cfg(feature = "with-jiff")] #[cfg_attr(docsrs, doc(cfg(feature = "with-jiff")))] - JiffZoned(Box<[Option>]>), + JiffZoned(Box<[Option]>), #[cfg(feature = "with-uuid")] #[cfg_attr(docsrs, doc(cfg(feature = "with-uuid")))] Uuid(Box<[Option]>), @@ -76,7 +77,7 @@ pub enum Array { Decimal(Box<[Option]>), #[cfg(feature = "with-bigdecimal")] #[cfg_attr(docsrs, doc(cfg(feature = "with-bigdecimal")))] - BigDecimal(Box<[Option>]>), + BigDecimal(Box<[Option]>), #[cfg(feature = "with-ipnetwork")] #[cfg_attr(docsrs, doc(cfg(feature = "with-ipnetwork")))] IpNetwork(Box<[Option]>), @@ -84,3 +85,762 @@ pub enum Array { #[cfg_attr(docsrs, doc(cfg(feature = "with-mac_address")))] MacAddress(Box<[Option]>), } + +impl Array { + pub fn array_type(&self) -> ArrayType { + match self { + Array::Bool(_) => ArrayType::Bool, + Array::TinyInt(_) => ArrayType::TinyInt, + Array::SmallInt(_) => ArrayType::SmallInt, + Array::Int(_) => ArrayType::Int, + Array::BigInt(_) => ArrayType::BigInt, + Array::TinyUnsigned(_) => ArrayType::TinyUnsigned, + Array::SmallUnsigned(_) => ArrayType::SmallUnsigned, + Array::Unsigned(_) => ArrayType::Unsigned, + Array::BigUnsigned(_) => ArrayType::BigUnsigned, + Array::Float(_) => ArrayType::Float, + Array::Double(_) => ArrayType::Double, + Array::String(_) => ArrayType::String, + Array::Char(_) => ArrayType::Char, + Array::Bytes(_) => ArrayType::Bytes, + Array::Enum(boxed) => ArrayType::Enum(boxed.as_ref().0.clone()), + Array::Array(arr) => arr.as_ref().0.clone(), + #[cfg(feature = "with-json")] + Array::Json(_) => ArrayType::Json, + #[cfg(feature = "with-chrono")] + Array::ChronoDate(_) => ArrayType::ChronoDate, + #[cfg(feature = "with-chrono")] + Array::ChronoTime(_) => ArrayType::ChronoTime, + #[cfg(feature = "with-chrono")] + Array::ChronoDateTime(_) => ArrayType::ChronoDateTime, + #[cfg(feature = "with-chrono")] + Array::ChronoDateTimeUtc(_) => ArrayType::ChronoDateTimeUtc, + #[cfg(feature = "with-chrono")] + Array::ChronoDateTimeLocal(_) => ArrayType::ChronoDateTimeLocal, + #[cfg(feature = "with-chrono")] + Array::ChronoDateTimeWithTimeZone(_) => ArrayType::ChronoDateTimeWithTimeZone, + #[cfg(feature = "with-time")] + Array::TimeDate(_) => ArrayType::TimeDate, + #[cfg(feature = "with-time")] + Array::TimeTime(_) => ArrayType::TimeTime, + #[cfg(feature = "with-time")] + Array::TimeDateTime(_) => ArrayType::TimeDateTime, + #[cfg(feature = "with-time")] + Array::TimeDateTimeWithTimeZone(_) => ArrayType::TimeDateTimeWithTimeZone, + #[cfg(feature = "with-jiff")] + Array::JiffDate(_) => ArrayType::JiffDate, + #[cfg(feature = "with-jiff")] + Array::JiffTime(_) => ArrayType::JiffTime, + #[cfg(feature = "with-jiff")] + Array::JiffDateTime(_) => ArrayType::JiffDateTime, + #[cfg(feature = "with-jiff")] + Array::JiffTimestamp(_) => ArrayType::JiffTimestamp, + #[cfg(feature = "with-jiff")] + Array::JiffZoned(_) => ArrayType::JiffZoned, + #[cfg(feature = "with-uuid")] + Array::Uuid(_) => ArrayType::Uuid, + #[cfg(feature = "with-rust_decimal")] + Array::Decimal(_) => ArrayType::Decimal, + #[cfg(feature = "with-bigdecimal")] + Array::BigDecimal(_) => ArrayType::BigDecimal, + #[cfg(feature = "with-ipnetwork")] + Array::IpNetwork(_) => ArrayType::IpNetwork, + #[cfg(feature = "with-mac_address")] + Array::MacAddress(_) => ArrayType::MacAddress, + } + } + + pub fn is_empty(&self) -> bool { + match self { + Array::Bool(v) => v.is_empty(), + Array::TinyInt(v) => v.is_empty(), + Array::SmallInt(v) => v.is_empty(), + Array::Int(v) => v.is_empty(), + Array::BigInt(v) => v.is_empty(), + Array::TinyUnsigned(v) => v.is_empty(), + Array::SmallUnsigned(v) => v.is_empty(), + Array::Unsigned(v) => v.is_empty(), + Array::BigUnsigned(v) => v.is_empty(), + Array::Float(v) => v.is_empty(), + Array::Double(v) => v.is_empty(), + Array::String(v) => v.is_empty(), + Array::Char(v) => v.is_empty(), + Array::Bytes(v) => v.is_empty(), + Array::Enum(boxed) => boxed.as_ref().1.is_empty(), + Array::Array(v) => v.as_ref().1.is_empty(), + #[cfg(feature = "with-json")] + Array::Json(v) => v.is_empty(), + #[cfg(feature = "with-chrono")] + Array::ChronoDate(v) => v.is_empty(), + #[cfg(feature = "with-chrono")] + Array::ChronoTime(v) => v.is_empty(), + #[cfg(feature = "with-chrono")] + Array::ChronoDateTime(v) => v.is_empty(), + #[cfg(feature = "with-chrono")] + Array::ChronoDateTimeUtc(v) => v.is_empty(), + #[cfg(feature = "with-chrono")] + Array::ChronoDateTimeLocal(v) => v.is_empty(), + #[cfg(feature = "with-chrono")] + Array::ChronoDateTimeWithTimeZone(v) => v.is_empty(), + #[cfg(feature = "with-time")] + Array::TimeDate(v) => v.is_empty(), + #[cfg(feature = "with-time")] + Array::TimeTime(v) => v.is_empty(), + #[cfg(feature = "with-time")] + Array::TimeDateTime(v) => v.is_empty(), + #[cfg(feature = "with-time")] + Array::TimeDateTimeWithTimeZone(v) => v.is_empty(), + #[cfg(feature = "with-jiff")] + Array::JiffDate(v) => v.is_empty(), + #[cfg(feature = "with-jiff")] + Array::JiffTime(v) => v.is_empty(), + #[cfg(feature = "with-jiff")] + Array::JiffDateTime(v) => v.is_empty(), + #[cfg(feature = "with-jiff")] + Array::JiffTimestamp(v) => v.is_empty(), + #[cfg(feature = "with-jiff")] + Array::JiffZoned(v) => v.is_empty(), + #[cfg(feature = "with-uuid")] + Array::Uuid(v) => v.is_empty(), + #[cfg(feature = "with-rust_decimal")] + Array::Decimal(v) => v.is_empty(), + #[cfg(feature = "with-bigdecimal")] + Array::BigDecimal(v) => v.is_empty(), + #[cfg(feature = "with-ipnetwork")] + Array::IpNetwork(v) => v.is_empty(), + #[cfg(feature = "with-mac_address")] + Array::MacAddress(v) => v.is_empty(), + } + } + + pub fn try_from_parts(ty: ArrayType, vals: Vec) -> Result { + match ty { + ArrayType::Bool => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::Bool(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::Bool(v.into_boxed_slice())) + } + ArrayType::TinyInt => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::TinyInt(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::TinyInt(v.into_boxed_slice())) + } + ArrayType::SmallInt => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::SmallInt(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::SmallInt(v.into_boxed_slice())) + } + ArrayType::Int => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::Int(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::Int(v.into_boxed_slice())) + } + ArrayType::BigInt => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::BigInt(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::BigInt(v.into_boxed_slice())) + } + ArrayType::TinyUnsigned => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::TinyUnsigned(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::TinyUnsigned(v.into_boxed_slice())) + } + ArrayType::SmallUnsigned => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::SmallUnsigned(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::SmallUnsigned(v.into_boxed_slice())) + } + ArrayType::Unsigned => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::Unsigned(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::Unsigned(v.into_boxed_slice())) + } + ArrayType::BigUnsigned => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::BigUnsigned(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::BigUnsigned(v.into_boxed_slice())) + } + ArrayType::Float => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::Float(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::Float(v.into_boxed_slice())) + } + ArrayType::Double => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::Double(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::Double(v.into_boxed_slice())) + } + ArrayType::String => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::String(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::String(v.into_boxed_slice())) + } + ArrayType::Char => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::Char(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::Char(v.into_boxed_slice())) + } + ArrayType::Bytes => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::Bytes(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::Bytes(v.into_boxed_slice())) + } + #[cfg(feature = "backend-postgres")] + ArrayType::Enum(name) => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::Enum(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::Enum(Box::new((name, v.into_boxed_slice())))) + } + #[cfg(feature = "with-json")] + ArrayType::Json => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::Json(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::Json(v.into_boxed_slice())) + } + #[cfg(feature = "with-chrono")] + ArrayType::ChronoDate => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::ChronoDate(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::ChronoDate(v.into_boxed_slice())) + } + #[cfg(feature = "with-chrono")] + ArrayType::ChronoTime => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::ChronoTime(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::ChronoTime(v.into_boxed_slice())) + } + #[cfg(feature = "with-chrono")] + ArrayType::ChronoDateTime => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::ChronoDateTime(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::ChronoDateTime(v.into_boxed_slice())) + } + #[cfg(feature = "with-chrono")] + ArrayType::ChronoDateTimeUtc => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::ChronoDateTimeUtc(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::ChronoDateTimeUtc(v.into_boxed_slice())) + } + #[cfg(feature = "with-chrono")] + ArrayType::ChronoDateTimeLocal => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::ChronoDateTimeLocal(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::ChronoDateTimeLocal(v.into_boxed_slice())) + } + #[cfg(feature = "with-chrono")] + ArrayType::ChronoDateTimeWithTimeZone => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::ChronoDateTimeWithTimeZone(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::ChronoDateTimeWithTimeZone(v.into_boxed_slice())) + } + #[cfg(feature = "with-time")] + ArrayType::TimeDate => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::TimeDate(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::TimeDate(v.into_boxed_slice())) + } + #[cfg(feature = "with-time")] + ArrayType::TimeTime => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::TimeTime(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::TimeTime(v.into_boxed_slice())) + } + #[cfg(feature = "with-time")] + ArrayType::TimeDateTime => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::TimeDateTime(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::TimeDateTime(v.into_boxed_slice())) + } + #[cfg(feature = "with-time")] + ArrayType::TimeDateTimeWithTimeZone => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::TimeDateTimeWithTimeZone(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::TimeDateTimeWithTimeZone(v.into_boxed_slice())) + } + #[cfg(feature = "with-jiff")] + ArrayType::JiffDate => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::JiffDate(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::JiffDate(v.into_boxed_slice())) + } + #[cfg(feature = "with-jiff")] + ArrayType::JiffTime => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::JiffTime(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::JiffTime(v.into_boxed_slice())) + } + #[cfg(feature = "with-jiff")] + ArrayType::JiffDateTime => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::JiffDateTime(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::JiffDateTime(v.into_boxed_slice())) + } + #[cfg(feature = "with-jiff")] + ArrayType::JiffTimestamp => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::JiffTimestamp(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::JiffTimestamp(v.into_boxed_slice())) + } + #[cfg(feature = "with-jiff")] + ArrayType::JiffZoned => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::JiffZoned(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::JiffZoned(v.into_boxed_slice())) + } + #[cfg(feature = "with-uuid")] + ArrayType::Uuid => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::Uuid(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::Uuid(v.into_boxed_slice())) + } + #[cfg(feature = "with-rust_decimal")] + ArrayType::Decimal => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::Decimal(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::Decimal(v.into_boxed_slice())) + } + #[cfg(feature = "with-bigdecimal")] + ArrayType::BigDecimal => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::BigDecimal(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::BigDecimal(v.into_boxed_slice())) + } + #[cfg(feature = "with-ipnetwork")] + ArrayType::IpNetwork => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::IpNetwork(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::IpNetwork(v.into_boxed_slice())) + } + #[cfg(feature = "with-mac_address")] + ArrayType::MacAddress => { + let mut v = Vec::with_capacity(vals.len()); + for e in vals { + match e { + Value::MacAddress(x) => v.push(x), + _ => return Err(ValueTypeErr), + } + } + Ok(Array::MacAddress(v.into_boxed_slice())) + } + } + } + + pub fn dummy_value(&self) -> Self { + match self { + Array::Bool(_) => Array::Bool(Box::new([])), + Array::TinyInt(_) => Array::TinyInt(Box::new([])), + Array::SmallInt(_) => Array::SmallInt(Box::new([])), + Array::Int(_) => Array::Int(Box::new([])), + Array::BigInt(_) => Array::BigInt(Box::new([])), + Array::TinyUnsigned(_) => Array::TinyUnsigned(Box::new([])), + Array::SmallUnsigned(_) => Array::SmallUnsigned(Box::new([])), + Array::Unsigned(_) => Array::Unsigned(Box::new([])), + Array::BigUnsigned(_) => Array::BigUnsigned(Box::new([])), + Array::Float(_) => Array::Float(Box::new([])), + Array::Double(_) => Array::Double(Box::new([])), + Array::String(_) => Array::String(Box::new([])), + Array::Char(_) => Array::Char(Box::new([])), + Array::Bytes(_) => Array::Bytes(Box::new([])), + Array::Enum(val) => { + let val = val.as_ref(); + Array::Enum(Box::new((val.0.clone(), Box::new([])))) + } + Array::Array(val) => { + let val = val.as_ref(); + Array::Array(Box::new((val.0.clone(), Box::new([])))) + } + #[cfg(feature = "with-json")] + Array::Json(_) => Array::Json(Box::new([])), + #[cfg(feature = "with-chrono")] + Array::ChronoDate(_) => Array::ChronoDate(Box::new([])), + #[cfg(feature = "with-chrono")] + Array::ChronoTime(_) => Array::ChronoTime(Box::new([])), + #[cfg(feature = "with-chrono")] + Array::ChronoDateTime(_) => Array::ChronoDateTime(Box::new([])), + #[cfg(feature = "with-chrono")] + Array::ChronoDateTimeUtc(_) => Array::ChronoDateTimeUtc(Box::new([])), + #[cfg(feature = "with-chrono")] + Array::ChronoDateTimeLocal(_) => Array::ChronoDateTimeLocal(Box::new([])), + #[cfg(feature = "with-chrono")] + Array::ChronoDateTimeWithTimeZone(_) => Array::ChronoDateTimeWithTimeZone(Box::new([])), + #[cfg(feature = "with-time")] + Array::TimeDate(_) => Array::TimeDate(Box::new([])), + #[cfg(feature = "with-time")] + Array::TimeTime(_) => Array::TimeTime(Box::new([])), + #[cfg(feature = "with-time")] + Array::TimeDateTime(_) => Array::TimeDateTime(Box::new([])), + #[cfg(feature = "with-time")] + Array::TimeDateTimeWithTimeZone(_) => Array::TimeDateTimeWithTimeZone(Box::new([])), + #[cfg(feature = "with-jiff")] + Array::JiffDate(_) => Array::JiffDate(Box::new([])), + #[cfg(feature = "with-jiff")] + Array::JiffTime(_) => Array::JiffTime(Box::new([])), + #[cfg(feature = "with-jiff")] + Array::JiffDateTime(_) => Array::JiffDateTime(Box::new([])), + #[cfg(feature = "with-jiff")] + Array::JiffTimestamp(_) => Array::JiffTimestamp(Box::new([])), + #[cfg(feature = "with-jiff")] + Array::JiffZoned(_) => Array::JiffZoned(Box::new([])), + #[cfg(feature = "with-uuid")] + Array::Uuid(_) => Array::Uuid(Box::new([])), + #[cfg(feature = "with-rust_decimal")] + Array::Decimal(_) => Array::Decimal(Box::new([])), + #[cfg(feature = "with-bigdecimal")] + Array::BigDecimal(_) => Array::BigDecimal(Box::new([])), + #[cfg(feature = "with-ipnetwork")] + Array::IpNetwork(_) => Array::IpNetwork(Box::new([])), + #[cfg(feature = "with-mac_address")] + Array::MacAddress(_) => Array::MacAddress(Box::new([])), + } + } + + #[cfg(feature = "with-json")] + #[allow(unused)] + pub(crate) fn to_json_values(&self) -> Vec { + match self { + Array::Bool(items) => todo!(), + Array::TinyInt(items) => todo!(), + Array::SmallInt(items) => todo!(), + Array::Int(items) => todo!(), + Array::BigInt(items) => todo!(), + Array::TinyUnsigned(items) => todo!(), + Array::SmallUnsigned(items) => todo!(), + Array::Unsigned(items) => todo!(), + Array::BigUnsigned(items) => todo!(), + Array::Float(items) => todo!(), + Array::Double(items) => todo!(), + Array::String(items) => todo!(), + Array::Char(items) => todo!(), + Array::Bytes(items) => todo!(), + Array::Enum(_) => todo!(), + Array::Array(items) => todo!(), + Array::Json(values) => todo!(), + Array::ChronoDate(naive_dates) => todo!(), + Array::ChronoTime(naive_times) => todo!(), + Array::ChronoDateTime(naive_date_times) => todo!(), + Array::ChronoDateTimeUtc(date_times) => todo!(), + Array::ChronoDateTimeLocal(date_times) => todo!(), + Array::ChronoDateTimeWithTimeZone(date_times) => todo!(), + Array::TimeDate(dates) => todo!(), + Array::TimeTime(times) => todo!(), + Array::TimeDateTime(primitive_date_times) => todo!(), + Array::TimeDateTimeWithTimeZone(offset_date_times) => todo!(), + Array::JiffDate(dates) => todo!(), + Array::JiffTime(times) => todo!(), + Array::JiffDateTime(date_times) => todo!(), + Array::JiffTimestamp(timestamps) => todo!(), + Array::JiffZoned(zoneds) => todo!(), + Array::Uuid(uuids) => todo!(), + Array::Decimal(decimals) => todo!(), + Array::BigDecimal(big_decimals) => todo!(), + Array::IpNetwork(ip_networks) => todo!(), + Array::MacAddress(items) => todo!(), + } + } +} + +#[cfg(feature = "hashable-value")] +mod hash { + use ordered_float::{FloatCore, OrderedFloat}; + + use super::Array; + + #[inline] + fn map_option_ordered_float_vec( + vec: &[Option], + ) -> impl Iterator>> + '_ + where + T: FloatCore, + { + vec.iter().copied().map(|x| x.map(OrderedFloat)) + } + + #[inline] + fn cmp_option_ordered_float_vec(left: &[Option], right: &[Option]) -> bool + where + T: FloatCore, + { + map_option_ordered_float_vec(left).eq(map_option_ordered_float_vec(right)) + } + + impl PartialEq for Array { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Bool(l0), Self::Bool(r0)) => l0 == r0, + (Self::TinyInt(l0), Self::TinyInt(r0)) => l0 == r0, + (Self::SmallInt(l0), Self::SmallInt(r0)) => l0 == r0, + (Self::Int(l0), Self::Int(r0)) => l0 == r0, + (Self::BigInt(l0), Self::BigInt(r0)) => l0 == r0, + (Self::TinyUnsigned(l0), Self::TinyUnsigned(r0)) => l0 == r0, + (Self::SmallUnsigned(l0), Self::SmallUnsigned(r0)) => l0 == r0, + (Self::Unsigned(l0), Self::Unsigned(r0)) => l0 == r0, + (Self::BigUnsigned(l0), Self::BigUnsigned(r0)) => l0 == r0, + (Self::Float(l0), Self::Float(r0)) => cmp_option_ordered_float_vec(l0, r0), + (Self::Double(l0), Self::Double(r0)) => cmp_option_ordered_float_vec(l0, r0), + (Self::String(l0), Self::String(r0)) => l0 == r0, + (Self::Char(l0), Self::Char(r0)) => l0 == r0, + (Self::Bytes(l0), Self::Bytes(r0)) => l0 == r0, + (Self::Enum(l0), Self::Enum(r0)) => l0 == r0, + (Self::Array(l0), Self::Array(r0)) => l0 == r0, + (Self::Json(l0), Self::Json(r0)) => l0 == r0, + (Self::ChronoDate(l0), Self::ChronoDate(r0)) => l0 == r0, + (Self::ChronoTime(l0), Self::ChronoTime(r0)) => l0 == r0, + (Self::ChronoDateTime(l0), Self::ChronoDateTime(r0)) => l0 == r0, + (Self::ChronoDateTimeUtc(l0), Self::ChronoDateTimeUtc(r0)) => l0 == r0, + (Self::ChronoDateTimeLocal(l0), Self::ChronoDateTimeLocal(r0)) => l0 == r0, + (Self::ChronoDateTimeWithTimeZone(l0), Self::ChronoDateTimeWithTimeZone(r0)) => { + l0 == r0 + } + (Self::TimeDate(l0), Self::TimeDate(r0)) => l0 == r0, + (Self::TimeTime(l0), Self::TimeTime(r0)) => l0 == r0, + (Self::TimeDateTime(l0), Self::TimeDateTime(r0)) => l0 == r0, + (Self::TimeDateTimeWithTimeZone(l0), Self::TimeDateTimeWithTimeZone(r0)) => { + l0 == r0 + } + (Self::JiffDate(l0), Self::JiffDate(r0)) => l0 == r0, + (Self::JiffTime(l0), Self::JiffTime(r0)) => l0 == r0, + (Self::JiffDateTime(l0), Self::JiffDateTime(r0)) => l0 == r0, + (Self::JiffTimestamp(l0), Self::JiffTimestamp(r0)) => l0 == r0, + (Self::JiffZoned(l0), Self::JiffZoned(r0)) => l0 == r0, + (Self::Uuid(l0), Self::Uuid(r0)) => l0 == r0, + (Self::Decimal(l0), Self::Decimal(r0)) => l0 == r0, + (Self::BigDecimal(l0), Self::BigDecimal(r0)) => l0 == r0, + (Self::IpNetwork(l0), Self::IpNetwork(r0)) => l0 == r0, + (Self::MacAddress(l0), Self::MacAddress(r0)) => l0 == r0, + _ => false, + } + } + } + + impl Eq for Array {} + + impl std::hash::Hash for Array { + fn hash(&self, state: &mut H) { + std::mem::discriminant(self).hash(state); + match self { + Array::Bool(items) => items.hash(state), + Array::TinyInt(items) => items.hash(state), + Array::SmallInt(items) => items.hash(state), + Array::Int(items) => items.hash(state), + Array::BigInt(items) => items.hash(state), + Array::TinyUnsigned(items) => items.hash(state), + Array::SmallUnsigned(items) => items.hash(state), + Array::Unsigned(items) => items.hash(state), + Array::BigUnsigned(items) => items.hash(state), + Array::Float(items) => items + .iter() + .copied() + .map(|x| x.map(OrderedFloat)) + .collect::>() + .hash(state), + Array::Double(items) => items + .iter() + .copied() + .map(|x| x.map(OrderedFloat)) + .collect::>() + .hash(state), + Array::String(items) => items.hash(state), + Array::Char(items) => items.hash(state), + Array::Bytes(items) => items.hash(state), + Array::Enum(items) => items.hash(state), + Array::Array(items) => items.hash(state), + Array::Json(items) => items.hash(state), + Array::ChronoDate(items) => items.hash(state), + Array::ChronoTime(items) => items.hash(state), + Array::ChronoDateTime(items) => items.hash(state), + Array::ChronoDateTimeUtc(items) => items.hash(state), + Array::ChronoDateTimeLocal(items) => items.hash(state), + Array::ChronoDateTimeWithTimeZone(items) => items.hash(state), + Array::TimeDate(items) => items.hash(state), + Array::TimeTime(items) => items.hash(state), + Array::TimeDateTime(items) => items.hash(state), + Array::TimeDateTimeWithTimeZone(items) => items.hash(state), + Array::JiffDate(items) => items.hash(state), + Array::JiffTime(items) => items.hash(state), + Array::JiffDateTime(items) => items.hash(state), + Array::JiffTimestamp(items) => items.hash(state), + Array::JiffZoned(items) => items.hash(state), + Array::Uuid(items) => items.hash(state), + Array::Decimal(items) => items.hash(state), + Array::BigDecimal(items) => items.hash(state), + Array::IpNetwork(items) => items.hash(state), + Array::MacAddress(items) => items.hash(state), + } + } + } +} diff --git a/src/value/hashable_value.rs b/src/value/hashable_value.rs index 5dda93cae..4acbadcf8 100644 --- a/src/value/hashable_value.rs +++ b/src/value/hashable_value.rs @@ -158,15 +158,13 @@ impl Hash for Value { #[cfg(feature = "postgres-array")] Value::Array(array) => match array { Some(array) => { - if array.as_ref().is_null() { + if array.is_empty() { 2u8.hash(state); - array.as_ref().array_type().hash(state); + array.array_type().hash(state); } else { 1u8.hash(state); - array.as_ref().array_type().hash(state); - for value in array.as_ref().to_value_vec() { - value.hash(state); - } + array.array_type().hash(state); + array.hash(state); } } None => 0u8.hash(state), diff --git a/src/value/with_array.rs b/src/value/with_array.rs index ef198974f..f1a81de67 100644 --- a/src/value/with_array.rs +++ b/src/value/with_array.rs @@ -76,62 +76,152 @@ impl NotU8 for IpNetwork {} #[cfg(feature = "with-mac_address")] impl NotU8 for MacAddress {} -impl From> for Value -where - T: Into + NotU8 + ValueType, -{ - fn from(x: Vec) -> Value { - Value::Array( - T::array_type(), - Some(Box::new(x.into_iter().map(|e| e.into()).collect())), - ) +macro_rules! impl_value_vec { + ($($ty:ty => $vari:ident)*) => { + $( + impl From> for Value { + fn from(x: Vec<$ty>) -> Value { + let values: Vec> = x + .into_iter() + .map(Some) + .collect(); + + Value::Array( + Some(Array::$vari(values.into_boxed_slice())) + ) + } + } + + impl From>> for Value { + fn from(x: Vec>) -> Value { + Value::Array(Some(Array::$vari(x.into()))) + } + } + + impl ValueType for Vec> + { + fn try_from(v: Value) -> Result { + match v { + Value::Array(Some(Array::$vari(inner))) => { + Ok(inner.into_vec()) + } + _ => Err(ValueTypeErr), + } + } + + fn type_name() -> String { + stringify!(Vec<$ty>).to_owned() + } + + fn array_type() -> ArrayType { + <$ty>::array_type() + } + + fn column_type() -> ColumnType { + use ColumnType::*; + Array(RcOrArc::new(<$ty>::column_type())) + } + } + )* } } -impl Nullable for Vec -where - T: Into + NotU8 + ValueType, -{ - fn null() -> Value { - Value::Array(T::array_type(), None) - } +impl_value_vec! { + bool => Bool + i8 => TinyInt + i16 => SmallInt + i32 => Int + i64 => BigInt + u16 => SmallUnsigned + u32 => Unsigned + u64 => BigUnsigned + f32 => Float + f64 => Double + std::string::String => String + char => Char + Vec => Bytes } -impl ValueType for Vec -where - T: NotU8 + ValueType, -{ - fn try_from(v: Value) -> Result { - match v { - Value::Array(ty, Some(v)) if T::array_type() == ty => { - Ok(v.into_iter().map(|e| e.unwrap()).collect()) - } - _ => Err(ValueTypeErr), - } - } +#[cfg(feature = "with-json")] +impl_value_vec! { + serde_json::Value => Json +} - fn type_name() -> String { - stringify!(Vec).to_owned() +#[cfg(feature = "backend-postgres")] +impl From<(Arc, Vec>>)> for Value { + fn from(x: (Arc, Vec>>)) -> Value { + Value::Array(Some(Array::Enum(Box::new((x.0, x.1.into_boxed_slice()))))) } - fn array_type() -> ArrayType { - T::array_type() - } +#[cfg(feature = "with-chrono")] +impl_value_vec! { + NaiveDate => ChronoDate + NaiveTime => ChronoTime + NaiveDateTime => ChronoDateTime + chrono::DateTime => ChronoDateTimeUtc + chrono::DateTime => ChronoDateTimeLocal + chrono::DateTime => ChronoDateTimeWithTimeZone +} - fn column_type() -> ColumnType { - use ColumnType::*; - Array(RcOrArc::new(T::column_type())) +#[cfg(feature = "with-time")] +impl_value_vec! { + time::Date => TimeDate + time::Time => TimeTime + PrimitiveDateTime => TimeDateTime + OffsetDateTime => TimeDateTimeWithTimeZone +} + +#[cfg(feature = "with-jiff")] +impl_value_vec! { + jiff::civil::Date => JiffDate + jiff::civil::Time => JiffTime + jiff::civil::DateTime => JiffDateTime + jiff::Timestamp => JiffTimestamp + jiff::Zoned => JiffZoned +} + +#[cfg(feature = "with-rust_decimal")] +impl_value_vec! { + rust_decimal::Decimal => Decimal +} + +#[cfg(feature = "with-bigdecimal")] +impl_value_vec! { + bigdecimal::BigDecimal => BigDecimal +} + +#[cfg(feature = "with-uuid")] +impl_value_vec! { + uuid::Uuid => Uuid +} + +#[cfg(feature = "with-ipnetwork")] +impl_value_vec! { + IpNetwork => IpNetwork +} + +#[cfg(feature = "with-mac_address")] +impl_value_vec! { + MacAddress => MacAddress +} + +impl Nullable for Vec +where + T: Into + NotU8 + ValueType, +{ + fn null() -> Value { + Value::Array(None) } } impl Value { pub fn is_array(&self) -> bool { - matches!(self, Self::Array(_, _)) + matches!(self, Self::Array(_)) } - pub fn as_ref_array(&self) -> Option<&Vec> { + pub fn as_ref_array(&self) -> Option<&Array> { match self { - Self::Array(_, v) => v.as_ref().map(|v| v.as_ref()), + Self::Array(v) => v.as_ref(), _ => panic!("not Value::Array"), } } diff --git a/src/value/with_bigdecimal.rs b/src/value/with_bigdecimal.rs index 9a7ee4264..e3570fe5a 100644 --- a/src/value/with_bigdecimal.rs +++ b/src/value/with_bigdecimal.rs @@ -1,6 +1,6 @@ use super::*; -type_to_box_value!(BigDecimal, BigDecimal, Decimal(None)); +type_to_value!(BigDecimal, BigDecimal, Decimal(None)); impl Value { pub fn is_big_decimal(&self) -> bool { @@ -9,7 +9,7 @@ impl Value { pub fn as_ref_big_decimal(&self) -> Option<&BigDecimal> { match self { - Self::BigDecimal(v) => v.as_ref().map(|x| x.as_ref()), + Self::BigDecimal(v) => v.as_ref(), _ => panic!("not Value::BigDecimal"), } } diff --git a/src/value/with_jiff.rs b/src/value/with_jiff.rs index c072f8f07..7a2eb5b2c 100644 --- a/src/value/with_jiff.rs +++ b/src/value/with_jiff.rs @@ -3,9 +3,9 @@ use jiff::{Timestamp, Zoned, civil}; type_to_value!(civil::Date, JiffDate, Date); type_to_value!(civil::Time, JiffTime, Time); -type_to_box_value!(civil::DateTime, JiffDateTime, DateTime); -type_to_box_value!(Timestamp, JiffTimestamp, Timestamp); -type_to_box_value!(Zoned, JiffZoned, TimestampWithTimeZone); +type_to_value!(civil::DateTime, JiffDateTime, DateTime); +type_to_value!(Timestamp, JiffTimestamp, Timestamp); +type_to_value!(Zoned, JiffZoned, TimestampWithTimeZone); impl Value { #[inline] @@ -71,21 +71,21 @@ impl Value { pub fn as_ref_jiff_date_time(&self) -> Option<&civil::DateTime> { match self { - Self::JiffDateTime(v) => v.as_deref(), + Self::JiffDateTime(v) => v.as_ref(), _ => panic!("not Value::JiffDateTime"), } } pub fn as_ref_jiff_timestamp(&self) -> Option<&Timestamp> { match self { - Self::JiffTimestamp(v) => v.as_deref(), + Self::JiffTimestamp(v) => v.as_ref(), _ => panic!("not Value::JiffTimestamp"), } } pub fn as_ref_jiff_zoned(&self) -> Option<&Zoned> { match self { - Self::JiffZoned(v) => v.as_deref(), + Self::JiffZoned(v) => v.as_ref(), _ => panic!("not Value::JiffZoned"), } } diff --git a/src/value/with_json.rs b/src/value/with_json.rs index 7b4cf4ba6..7b368f66c 100644 --- a/src/value/with_json.rs +++ b/src/value/with_json.rs @@ -1,4 +1,5 @@ use super::*; +use std::str::from_utf8; type_to_value!(Json, Json, Json); @@ -34,6 +35,8 @@ pub fn sea_value_to_json_value(value: &Value) -> Json { | Value::Char(None) | Value::Bytes(None) | Value::Json(None) => Json::Null, + #[cfg(feature = "backend-postgres")] + Value::Enum(None) => Json::Null, #[cfg(feature = "with-rust_decimal")] Value::Decimal(None) => Json::Null, #[cfg(feature = "with-bigdecimal")] @@ -41,7 +44,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 = "postgres-vector")] Value::Vector(None) => Json::Null, #[cfg(feature = "with-ipnetwork")] @@ -105,6 +108,8 @@ 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 = "backend-postgres")] + Value::Enum(Some(v)) => Json::String(v.value.to_string()), #[cfg(feature = "postgres-array")] Value::Array(_, Some(v)) => Json::Array(v.iter().map(sea_value_to_json_value).collect()), #[cfg(feature = "postgres-vector")] From 114a16a5e23c316b0f34afc62e4d8489ea0dbae2 Mon Sep 17 00:00:00 2001 From: Huliiiiii <308013446a@gmail.com> Date: Mon, 27 Oct 2025 13:20:57 +0800 Subject: [PATCH 04/19] Update --- src/backend/mod.rs | 3 + src/backend/mysql/mod.rs | 2 + src/backend/postgres/query.rs | 13 + src/backend/query_builder.rs | 312 ++++++----------------- src/backend/sqlite/mod.rs | 2 + src/backend/value_encoder.rs | 451 ++++++++++++++++++++++++++++++++++ src/value/array.rs | 424 ++++++++++++++++++++++---------- src/value/hashable_value.rs | 10 +- src/value/tests.rs | 8 +- src/value/with_array.rs | 63 +++++ src/value/with_json.rs | 2 +- 11 files changed, 908 insertions(+), 382 deletions(-) create mode 100644 src/backend/value_encoder.rs diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 21d91030b..8d9971e54 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -2,6 +2,7 @@ use crate::*; use std::borrow::Cow; +use std::fmt::Write as _; #[cfg(feature = "backend-mysql")] #[cfg_attr(docsrs, doc(cfg(feature = "backend-mysql")))] @@ -25,12 +26,14 @@ mod index_builder; mod query_builder; mod table_builder; mod table_ref_builder; +mod value_encoder; pub use self::foreign_key_builder::*; pub use self::index_builder::*; pub use self::query_builder::*; pub use self::table_builder::*; pub use self::table_ref_builder::*; +pub use self::value_encoder::*; pub trait GenericBuilder: QueryBuilder + SchemaBuilder {} diff --git a/src/backend/mysql/mod.rs b/src/backend/mysql/mod.rs index dc699f1f0..3e926b687 100644 --- a/src/backend/mysql/mod.rs +++ b/src/backend/mysql/mod.rs @@ -15,6 +15,8 @@ pub type MySqlQueryBuilder = MysqlQueryBuilder; impl GenericBuilder for MysqlQueryBuilder {} +impl ValueEncoder for MysqlQueryBuilder {} + impl SchemaBuilder for MysqlQueryBuilder {} impl QuotedBuilder for MysqlQueryBuilder { diff --git a/src/backend/postgres/query.rs b/src/backend/postgres/query.rs index a2e5c12d7..374e9f3ca 100644 --- a/src/backend/postgres/query.rs +++ b/src/backend/postgres/query.rs @@ -29,6 +29,19 @@ impl PrecedenceDecider for PostgresQueryBuilder { } } +impl ValueEncoder for PostgresQueryBuilder { + fn write_enum(&self, buf: &mut impl Write, value: &crate::value::Enum) { + // Write the enum value as a quoted string + self.write_str(buf, value.value.as_str()); + + // If a type name is provided, append type cast using ::Type + if let Some(type_name) = &value.type_name { + buf.write_str("::").unwrap(); + buf.write_str(type_name).unwrap(); + } + } +} + impl QueryBuilder for PostgresQueryBuilder { fn placeholder(&self) -> (&'static str, bool) { ("$", true) diff --git a/src/backend/query_builder.rs b/src/backend/query_builder.rs index e194ce263..46c6000fa 100644 --- a/src/backend/query_builder.rs +++ b/src/backend/query_builder.rs @@ -5,7 +5,13 @@ use crate::*; const QUOTE: Quote = Quote(b'"', b'"'); pub trait QueryBuilder: - QuotedBuilder + EscapeBuilder + TableRefBuilder + OperLeftAssocDecider + PrecedenceDecider + Sized + ValueEncoder + + QuotedBuilder + + EscapeBuilder + + TableRefBuilder + + OperLeftAssocDecider + + PrecedenceDecider + + Sized { /// The type of placeholder the builder uses for values, and whether it is numbered. fn placeholder(&self) -> (&'static str, bool) { @@ -1106,272 +1112,102 @@ pub trait QueryBuilder: #[doc(hidden)] fn write_value(&self, buf: &mut impl Write, value: &Value) -> fmt::Result { - self.write_value_common(buf, value) - } - - #[doc(hidden)] - fn write_value_common(&self, buf: &mut impl Write, value: &Value) -> fmt::Result { - #[cfg(feature = "backend-postgres")] - fn prepare_enum(this: &impl QueryBuilder, r#enum: &Enum, sql: &mut impl Write) { - this.write_string_quoted(r#enum.value.as_str(), sql); - - if let Some(type_name) = &r#enum.type_name { - sql.write_str("::").unwrap(); - sql.write_str(&type_name).unwrap(); - } + macro_rules! write_opt { + ($opt:expr, $val:ident => $body:expr) => { + match $opt { + Some($val) => $body, + None => buf.write_str("NULL").unwrap(), + } + }; } - #[cfg(feature = "postgres-array")] - fn prepare_array(this: &impl QueryBuilder, array: &Array, sql: &mut impl Write) {} - 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) => buf.write_str("NULL")?, + Value::Bool(v) => write_opt!(v, val => self.write_bool(buf, *val)), + Value::TinyInt(v) => write_opt!(v, val => self.write_i8(buf, *val)), + Value::SmallInt(v) => write_opt!(v, val => self.write_i16(buf, *val)), + Value::Int(v) => write_opt!(v, val => self.write_i32(buf, *val)), + Value::BigInt(v) => write_opt!(v, val => self.write_i64(buf, *val)), + Value::TinyUnsigned(v) => write_opt!(v, val => self.write_u8(buf, *val)), + Value::SmallUnsigned(v) => write_opt!(v, val => self.write_u16(buf, *val)), + Value::Unsigned(v) => write_opt!(v, val => self.write_u32(buf, *val)), + Value::BigUnsigned(v) => write_opt!(v, val => self.write_u64(buf, *val)), + Value::Float(v) => write_opt!(v, val => self.write_f32(buf, *val)), + Value::Double(v) => write_opt!(v, val => self.write_f64(buf, *val)), + Value::String(v) => write_opt!(v, val => self.write_str(buf, val)), + Value::Char(v) => write_opt!(v, val => self.write_char(buf, *val)), #[cfg(feature = "backend-postgres")] - Value::Enum(None) => buf.write_str("NULL")?, - #[cfg(feature = "with-json")] - Value::Json(None) => buf.write_str("NULL")?, - #[cfg(feature = "with-chrono")] - Value::ChronoDate(None) => buf.write_str("NULL")?, - #[cfg(feature = "with-chrono")] - Value::ChronoTime(None) => buf.write_str("NULL")?, - #[cfg(feature = "with-chrono")] - Value::ChronoDateTime(None) => buf.write_str("NULL")?, - #[cfg(feature = "with-chrono")] - Value::ChronoDateTimeUtc(None) => buf.write_str("NULL")?, - #[cfg(feature = "with-chrono")] - Value::ChronoDateTimeLocal(None) => buf.write_str("NULL")?, - #[cfg(feature = "with-chrono")] - Value::ChronoDateTimeWithTimeZone(None) => buf.write_str("NULL")?, - #[cfg(feature = "with-time")] - Value::TimeDate(None) => buf.write_str("NULL")?, - #[cfg(feature = "with-time")] - Value::TimeTime(None) => buf.write_str("NULL")?, - #[cfg(feature = "with-time")] - Value::TimeDateTime(None) => buf.write_str("NULL")?, - #[cfg(feature = "with-time")] - Value::TimeDateTimeWithTimeZone(None) => buf.write_str("NULL")?, - #[cfg(feature = "with-jiff")] - Value::JiffDate(None) => buf.write_str("NULL")?, - #[cfg(feature = "with-jiff")] - Value::JiffTime(None) => buf.write_str("NULL")?, - #[cfg(feature = "with-jiff")] - Value::JiffDateTime(None) => buf.write_str("NULL")?, - #[cfg(feature = "with-jiff")] - Value::JiffTimestamp(None) => buf.write_str("NULL")?, - #[cfg(feature = "with-jiff")] - Value::JiffZoned(None) => buf.write_str("NULL")?, - #[cfg(feature = "with-rust_decimal")] - Value::Decimal(None) => buf.write_str("NULL")?, - #[cfg(feature = "with-bigdecimal")] - Value::BigDecimal(None) => buf.write_str("NULL")?, - #[cfg(feature = "with-uuid")] - Value::Uuid(None) => buf.write_str("NULL")?, - #[cfg(feature = "with-ipnetwork")] - Value::IpNetwork(None) => buf.write_str("NULL")?, - #[cfg(feature = "with-mac_address")] - Value::MacAddress(None) => buf.write_str("NULL")?, - #[cfg(feature = "postgres-array")] - Value::Array(None) => buf.write_str("NULL")?, - #[cfg(feature = "postgres-vector")] - Value::Vector(None) => buf.write_str("NULL")?, - Value::Bool(Some(b)) => buf.write_str(if *b { "TRUE" } else { "FALSE" })?, - Value::TinyInt(Some(v)) => { - write_int(buf, *v); - } - Value::SmallInt(Some(v)) => { - write_int(buf, *v); - } - Value::Int(Some(v)) => { - write_int(buf, *v); - } - Value::BigInt(Some(v)) => { - write_int(buf, *v); - } - Value::TinyUnsigned(Some(v)) => { - write_int(buf, *v); - } - Value::SmallUnsigned(Some(v)) => { - write_int(buf, *v); + Value::Enum(v) => write_opt!(v, val => self.write_enum(buf, val.as_ref())), + Value::Bytes(v) => { + write_opt!(v, val => ValueEncoder::write_bytes(self, buf, val)) } - Value::Unsigned(Some(v)) => { - write_int(buf, *v); - } - Value::BigUnsigned(Some(v)) => { - write_int(buf, *v); - } - Value::Float(Some(v)) => write!(buf, "{v}")?, - Value::Double(Some(v)) => write!(buf, "{v}")?, - Value::String(Some(v)) => self.write_string_quoted(v, buf), - Value::Char(Some(v)) => { - self.write_string_quoted(std::str::from_utf8(&[*v as u8]).unwrap(), buf) - } - #[cfg(feature = "backend-postgres")] - Value::Enum(Some(enum_value)) => { - prepare_enum(self, enum_value, buf); - } - Value::Bytes(Some(v)) => self.write_bytes(v, buf), #[cfg(feature = "with-json")] - Value::Json(Some(v)) => self.write_string_quoted(&v.to_string(), buf), + Value::Json(v) => write_opt!(v, val => self.write_json(buf, val)), #[cfg(feature = "with-chrono")] - Value::ChronoDate(Some(v)) => { - buf.write_str("'")?; - write!(buf, "{}", v.format("%Y-%m-%d"))?; - buf.write_str("'")?; + Value::ChronoDate(v) => { + write_opt!(v, val => self.write_naive_date(buf, val)) } #[cfg(feature = "with-chrono")] - Value::ChronoTime(Some(v)) => { - buf.write_str("'")?; - write!(buf, "{}", v.format("%H:%M:%S%.6f"))?; - buf.write_str("'")?; + Value::ChronoTime(v) => { + write_opt!(v, val => self.write_naive_time(buf, val)) } #[cfg(feature = "with-chrono")] - Value::ChronoDateTime(Some(v)) => { - buf.write_str("'")?; - write!(buf, "{}", v.format("%Y-%m-%d %H:%M:%S%.6f"))?; - buf.write_str("'")?; + Value::ChronoDateTime(v) => { + write_opt!(v, val => self.write_naive_datetime(buf, val)) } #[cfg(feature = "with-chrono")] - Value::ChronoDateTimeUtc(Some(v)) => { - buf.write_str("'")?; - write!(buf, "{}", v.format("%Y-%m-%d %H:%M:%S%.6f %:z"))?; - buf.write_str("'")?; + Value::ChronoDateTimeUtc(v) => { + write_opt!(v, val => self.write_datetime_utc(buf, val)) } #[cfg(feature = "with-chrono")] - Value::ChronoDateTimeLocal(Some(v)) => { - buf.write_str("'")?; - write!(buf, "{}", v.format("%Y-%m-%d %H:%M:%S%.6f %:z"))?; - buf.write_str("'")?; - } + Value::ChronoDateTimeLocal(v) => write_opt!(v, val => self + .write_datetime_local(buf, val)), #[cfg(feature = "with-chrono")] - Value::ChronoDateTimeWithTimeZone(Some(v)) => { - buf.write_str("'")?; - write!(buf, "{}", v.format("%Y-%m-%d %H:%M:%S%.6f %:z"))?; - buf.write_str("'")?; + Value::ChronoDateTimeWithTimeZone(v) => { + write_opt!(v, val => self.write_datetime_fixed(buf, val)) } #[cfg(feature = "with-time")] - Value::TimeDate(Some(v)) => { - buf.write_str("'")?; - buf.write_str(&v.format(time_format::FORMAT_DATE).unwrap())?; - buf.write_str("'")?; - } + Value::TimeDate(v) => write_opt!(v, val => self.write_time_date(buf, val)), #[cfg(feature = "with-time")] - Value::TimeTime(Some(v)) => { - buf.write_str("'")?; - buf.write_str(&v.format(time_format::FORMAT_TIME).unwrap())?; - buf.write_str("'")?; - } + Value::TimeTime(v) => write_opt!(v, val => self.write_time_time(buf, val)), #[cfg(feature = "with-time")] - Value::TimeDateTime(Some(v)) => { - buf.write_str("'")?; - buf.write_str(&v.format(time_format::FORMAT_DATETIME).unwrap())?; - buf.write_str("'")?; - } + Value::TimeDateTime(v) => write_opt!(v, val => self.write_time_datetime(buf, val)), #[cfg(feature = "with-time")] - Value::TimeDateTimeWithTimeZone(Some(v)) => { - buf.write_str("'")?; - buf.write_str(&v.format(time_format::FORMAT_DATETIME_TZ).unwrap())?; - buf.write_str("'")?; - } - // Jiff date and time dosen't need format string - // The default behavior is what we want + Value::TimeDateTimeWithTimeZone(v) => write_opt!(v, val => self + .write_time_datetime_tz(buf, val)), #[cfg(feature = "with-jiff")] - Value::JiffDate(Some(v)) => { - buf.write_str("'")?; - write!(buf, "{v}")?; - buf.write_str("'")?; + Value::JiffDate(v) => { + write_opt!(v, val => self.write_jiff_date(buf, val)) } #[cfg(feature = "with-jiff")] - Value::JiffTime(Some(v)) => { - buf.write_str("'")?; - write!(buf, "{v}")?; - buf.write_str("'")?; + Value::JiffTime(v) => { + write_opt!(v, val => self.write_jiff_time(buf, val)) } - // Both JiffDateTime and JiffTimestamp map to timestamp #[cfg(feature = "with-jiff")] - Value::JiffDateTime(Some(v)) => { - use crate::with_jiff::JIFF_DATE_TIME_FMT_STR; - buf.write_str("'")?; - write!(buf, "{}", v.strftime(JIFF_DATE_TIME_FMT_STR))?; - buf.write_str("'")?; - } + Value::JiffDateTime(v) => write_opt!(v, val => self + .write_jiff_datetime(buf, val)), #[cfg(feature = "with-jiff")] - Value::JiffTimestamp(Some(v)) => { - use crate::with_jiff::JIFF_TIMESTAMP_FMT_STR; - buf.write_str("'")?; - write!(buf, "{}", v.strftime(JIFF_TIMESTAMP_FMT_STR))?; - buf.write_str("'")?; + Value::JiffTimestamp(v) => { + write_opt!(v, val => self.write_jiff_timestamp(buf, val)) } #[cfg(feature = "with-jiff")] - // Zoned map to timestamp with timezone - Value::JiffZoned(Some(v)) => { - use crate::with_jiff::JIFF_ZONE_FMT_STR; - buf.write_str("'")?; - write!(buf, "{}", v.strftime(JIFF_ZONE_FMT_STR))?; - buf.write_str("'")?; - } + Value::JiffZoned(v) => write_opt!(v, val => self.write_jiff_zoned(buf, val)), #[cfg(feature = "with-rust_decimal")] - Value::Decimal(Some(v)) => write!(buf, "{v}")?, + Value::Decimal(v) => write_opt!(v, val => self.write_decimal(buf, val)), #[cfg(feature = "with-bigdecimal")] - Value::BigDecimal(Some(v)) => write!(buf, "{v}")?, - #[cfg(feature = "with-uuid")] - Value::Uuid(Some(v)) => { - buf.write_str("'")?; - write!(buf, "{v}")?; - buf.write_str("'")?; - } - #[cfg(feature = "postgres-array")] - Value::Array(Some(v)) => { - if v.is_empty() { - buf.write_str("'{}'")?; - } else { - buf.write_str("ARRAY [")?; - - prepare_array(self, v, buf); - - buf.write_str("]")?; - } + Value::BigDecimal(v) => { + write_opt!(v, val => self.write_bigdecimal(buf, val)) } + #[cfg(feature = "with-uuid")] + Value::Uuid(v) => write_opt!(v, val => self.write_uuid(buf, val)), #[cfg(feature = "postgres-vector")] - Value::Vector(Some(v)) => { - buf.write_str("'[")?; - let mut viter = v.as_slice().iter(); - - if let Some(element) = viter.next() { - write!(buf, "{element}")?; - } - - for element in viter { - buf.write_str(",")?; - write!(buf, "{element}")?; - } - buf.write_str("]'")?; - } + Value::Vector(v) => write_opt!(v, val => self.write_vector(buf, val)), #[cfg(feature = "with-ipnetwork")] - Value::IpNetwork(Some(v)) => { - buf.write_str("'")?; - write!(buf, "{v}")?; - buf.write_str("'")?; - } + Value::IpNetwork(v) => write_opt!(v, val => self.write_ipnetwork(buf, val)), #[cfg(feature = "with-mac_address")] - Value::MacAddress(Some(v)) => { - buf.write_str("'")?; - write!(buf, "{v}")?; - buf.write_str("'")?; - } - }; + Value::MacAddress(v) => write_opt!(v, val => self.write_mac(buf, val)), + #[cfg(feature = "postgres-array")] + Value::Array(v) => write_opt!(v, val => self.write_array(buf, val)), + } Ok(()) } @@ -1706,16 +1542,6 @@ pub trait QueryBuilder: buffer.write_str("'").unwrap(); } - #[doc(hidden)] - /// Write bytes enclosed with engine specific byte syntax - fn write_bytes(&self, bytes: &[u8], buffer: &mut impl Write) { - buffer.write_str("x'").unwrap(); - for b in bytes { - write!(buffer, "{b:02X}").unwrap() - } - buffer.write_str("'").unwrap(); - } - #[doc(hidden)] /// The name of the function that represents the "if null" condition. fn if_null_function(&self) -> &str { @@ -1818,6 +1644,8 @@ impl PrecedenceDecider for CommonSqlQueryBuilder { } } +impl ValueEncoder for CommonSqlQueryBuilder {} + impl QueryBuilder for CommonSqlQueryBuilder { fn prepare_query_statement(&self, query: &SubQueryStatement, sql: &mut impl SqlWriter) { query.prepare_statement(self, sql); diff --git a/src/backend/sqlite/mod.rs b/src/backend/sqlite/mod.rs index 71dbba1b8..a78170458 100644 --- a/src/backend/sqlite/mod.rs +++ b/src/backend/sqlite/mod.rs @@ -11,6 +11,8 @@ pub struct SqliteQueryBuilder; const QUOTE: Quote = Quote(b'"', b'"'); +impl ValueEncoder for SqliteQueryBuilder {} + impl GenericBuilder for SqliteQueryBuilder {} impl SchemaBuilder for SqliteQueryBuilder {} diff --git a/src/backend/value_encoder.rs b/src/backend/value_encoder.rs new file mode 100644 index 000000000..79f2c24bd --- /dev/null +++ b/src/backend/value_encoder.rs @@ -0,0 +1,451 @@ +use crate::{prepare::write_int, *}; +use std::fmt::Write; + +#[allow(unused_variables)] +pub trait ValueEncoder: EscapeBuilder { + fn write_bool(&self, buf: &mut impl Write, value: bool) { + buf.write_str(if value { "TRUE" } else { "FALSE" }).unwrap(); + } + + fn write_i8(&self, buf: &mut impl Write, value: i8) { + write_int(buf, value); + } + + fn write_i16(&self, buf: &mut impl Write, value: i16) { + write_int(buf, value); + } + + fn write_i32(&self, buf: &mut impl Write, value: i32) { + write_int(buf, value); + } + + fn write_i64(&self, buf: &mut impl Write, value: i64) { + write_int(buf, value); + } + + fn write_u8(&self, buf: &mut impl Write, value: u8) { + write_int(buf, value); + } + + fn write_u16(&self, buf: &mut impl Write, value: u16) { + write_int(buf, value); + } + + fn write_u32(&self, buf: &mut impl Write, value: u32) { + write_int(buf, value); + } + + fn write_u64(&self, buf: &mut impl Write, value: u64) { + write_int(buf, value); + } + + fn write_f32(&self, buf: &mut impl Write, value: f32) { + write!(buf, "{value}").unwrap(); + } + + fn write_f64(&self, buf: &mut impl Write, value: f64) { + write!(buf, "{value}").unwrap(); + } + + fn write_str(&self, buf: &mut impl Write, value: &str) { + buf.write_str("'").unwrap(); + self.write_escaped(buf, value); + buf.write_str("'").unwrap(); + } + + fn write_char(&self, buf: &mut impl Write, value: char) { + let mut tmp = [0u8; 4]; + let s = value.encode_utf8(&mut tmp); + self.write_str(buf, s); + } + + fn write_bytes(&self, buf: &mut impl Write, value: &[u8]) { + buf.write_str("x'").unwrap(); + for b in value { + write!(buf, "{b:02X}").unwrap() + } + buf.write_str("'").unwrap(); + } + + #[cfg(feature = "with-json")] + fn write_json(&self, buf: &mut impl Write, value: &serde_json::Value) { + self.write_str(buf, &value.to_string()); + } + + #[cfg(feature = "with-chrono")] + fn write_naive_date(&self, buf: &mut impl Write, value: &chrono::NaiveDate) { + buf.write_str("'").unwrap(); + value.format("%Y-%m-%d").write_to(buf).unwrap(); + buf.write_str("'").unwrap(); + } + + #[cfg(feature = "with-chrono")] + fn write_naive_time(&self, buf: &mut impl Write, value: &chrono::NaiveTime) { + buf.write_str("'").unwrap(); + value.format("%H:%M:%S%.6f").write_to(buf).unwrap(); + buf.write_str("'").unwrap(); + } + + #[cfg(feature = "with-chrono")] + fn write_naive_datetime(&self, buf: &mut impl Write, value: &chrono::NaiveDateTime) { + buf.write_str("'").unwrap(); + value.format("%Y-%m-%d %H:%M:%S%.6f").write_to(buf).unwrap(); + buf.write_str("'").unwrap(); + } + + #[cfg(feature = "with-chrono")] + fn write_datetime_utc(&self, buf: &mut impl Write, value: &chrono::DateTime) { + buf.write_str("'").unwrap(); + value + .format("%Y-%m-%d %H:%M:%S%.6f %:z") + .write_to(buf) + .unwrap(); + buf.write_str("'").unwrap(); + } + + #[cfg(feature = "with-chrono")] + fn write_datetime_local(&self, buf: &mut impl Write, value: &chrono::DateTime) { + buf.write_str("'").unwrap(); + value + .format("%Y-%m-%d %H:%M:%S%.6f %:z") + .write_to(buf) + .unwrap(); + buf.write_str("'").unwrap(); + } + + #[cfg(feature = "with-chrono")] + fn write_datetime_fixed( + &self, + buf: &mut impl Write, + value: &chrono::DateTime, + ) { + buf.write_str("'").unwrap(); + value + .format("%Y-%m-%d %H:%M:%S%.6f %:z") + .write_to(buf) + .unwrap(); + buf.write_str("'").unwrap(); + } + + // TODO: https://github.com/time-rs/time/issues/375 + // Currently, time crate dosen't support formatting into impl fmt::Write + // So this solution must allocate a temporary String + // Fix it when the issue is resolved + #[cfg(feature = "with-time")] + fn write_time_date(&self, buf: &mut impl Write, value: &time::Date) { + buf.write_str("'").unwrap(); + let s = value + .format(crate::value::time_format::FORMAT_DATE) + .unwrap(); + buf.write_str(&s).unwrap(); + buf.write_str("'").unwrap(); + } + + #[cfg(feature = "with-time")] + fn write_time_time(&self, buf: &mut impl Write, value: &time::Time) { + buf.write_str("'").unwrap(); + let s = value + .format(crate::value::time_format::FORMAT_TIME) + .unwrap(); + buf.write_str(&s).unwrap(); + buf.write_str("'").unwrap(); + } + + #[cfg(feature = "with-time")] + fn write_time_datetime(&self, buf: &mut impl Write, value: &time::PrimitiveDateTime) { + buf.write_str("'").unwrap(); + let s = value + .format(crate::value::time_format::FORMAT_DATETIME) + .unwrap(); + buf.write_str(&s).unwrap(); + buf.write_str("'").unwrap(); + } + + #[cfg(feature = "with-time")] + fn write_time_datetime_tz(&self, buf: &mut impl Write, value: &time::OffsetDateTime) { + buf.write_str("'").unwrap(); + let s = value + .format(crate::value::time_format::FORMAT_DATETIME_TZ) + .unwrap(); + buf.write_str(&s).unwrap(); + buf.write_str("'").unwrap(); + } + + #[cfg(feature = "with-jiff")] + fn write_jiff_date(&self, buf: &mut impl Write, value: &jiff::civil::Date) { + buf.write_str("'").unwrap(); + write!(buf, "{value}").unwrap(); + buf.write_str("'").unwrap(); + } + + #[cfg(feature = "with-jiff")] + fn write_jiff_time(&self, buf: &mut impl Write, value: &jiff::civil::Time) { + buf.write_str("'").unwrap(); + write!(buf, "{value}").unwrap(); + buf.write_str("'").unwrap(); + } + + #[cfg(feature = "with-jiff")] + fn write_jiff_datetime(&self, buf: &mut impl Write, value: &jiff::civil::DateTime) { + use crate::value::with_jiff::JIFF_DATE_TIME_FMT_STR; + buf.write_str("'").unwrap(); + write!(buf, "{}", value.strftime(JIFF_DATE_TIME_FMT_STR)).unwrap(); + buf.write_str("'").unwrap(); + } + + #[cfg(feature = "with-jiff")] + fn write_jiff_timestamp(&self, buf: &mut impl Write, value: &jiff::Timestamp) { + use crate::value::with_jiff::JIFF_TIMESTAMP_FMT_STR; + buf.write_str("'").unwrap(); + write!(buf, "{}", value.strftime(JIFF_TIMESTAMP_FMT_STR)).unwrap(); + buf.write_str("'").unwrap(); + } + + #[cfg(feature = "with-jiff")] + fn write_jiff_zoned(&self, buf: &mut impl Write, value: &jiff::Zoned) { + use crate::value::with_jiff::JIFF_ZONE_FMT_STR; + buf.write_str("'").unwrap(); + write!(buf, "{}", value.strftime(JIFF_ZONE_FMT_STR)).unwrap(); + buf.write_str("'").unwrap(); + } + + #[cfg(feature = "postgres-vector")] + fn write_vector(&self, buf: &mut impl Write, value: &pgvector::Vector) { + buf.write_str("'[").unwrap(); + let mut iter = value.as_slice().iter(); + if let Some(first) = iter.next() { + write!(buf, "{first}").unwrap(); + } + for v in iter { + buf.write_char(',').unwrap(); + write!(buf, "{v}").unwrap(); + } + buf.write_str("]'").unwrap(); + } + + #[cfg(feature = "with-rust_decimal")] + fn write_decimal(&self, buf: &mut impl Write, value: &rust_decimal::Decimal) { + write!(buf, "{value}").unwrap(); + } + + #[cfg(feature = "with-bigdecimal")] + fn write_bigdecimal(&self, buf: &mut impl Write, value: &bigdecimal::BigDecimal) { + write!(buf, "{value}").unwrap(); + } + + #[cfg(feature = "with-uuid")] + fn write_uuid(&self, buf: &mut impl Write, value: &uuid::Uuid) { + self.write_str(buf, &value.to_string()); + } + + #[cfg(feature = "with-ipnetwork")] + fn write_ipnetwork(&self, buf: &mut impl Write, value: &ipnetwork::IpNetwork) { + self.write_str(buf, &value.to_string()); + } + + #[cfg(feature = "with-mac_address")] + fn write_mac(&self, buf: &mut impl Write, value: &mac_address::MacAddress) { + self.write_str(buf, &value.to_string()); + } + + #[cfg(feature = "backend-postgres")] + fn write_enum(&self, buf: &mut impl Write, value: &crate::value::Enum) { + buf.write_str(value.value.as_str()); + } + + #[cfg(feature = "postgres-array")] + fn write_array(&self, buf: &mut impl Write, array: &crate::value::Array) { + use crate::value::Array; + + fn write_items(encoder: &QB, buf: &mut W, items: &[Option], mut f: F) + where + QB: ValueEncoder + ?Sized, + W: Write, + F: FnMut(&QB, &mut W, &T), + { + let mut first = true; + for item in items { + if !first { + buf.write_char(',').unwrap(); + } + first = false; + match item { + Some(value) => f(encoder, buf, value), + None => buf.write_str("NULL").unwrap(), + } + } + } + + fn write_array_recursive(encoder: &QB, buf: &mut W, array: &Array) + where + QB: ValueEncoder + ?Sized, + W: Write, + { + if array.is_empty() { + buf.write_str("'{}'").unwrap(); + return; + } + + buf.write_str("'ARRAY[").unwrap(); + match array { + Array::Bool(items) => { + write_items(encoder, buf, items, |qb, buf, val| qb.write_bool(buf, *val)) + } + Array::TinyInt(items) => { + write_items(encoder, buf, items, |qb, buf, val| qb.write_i8(buf, *val)) + } + Array::SmallInt(items) => { + write_items(encoder, buf, items, |qb, buf, val| qb.write_i16(buf, *val)) + } + Array::Int(items) => { + write_items(encoder, buf, items, |qb, buf, val| qb.write_i32(buf, *val)) + } + Array::BigInt(items) => { + write_items(encoder, buf, items, |qb, buf, val| qb.write_i64(buf, *val)) + } + Array::TinyUnsigned(items) => { + write_items(encoder, buf, items, |qb, buf, val| qb.write_u8(buf, *val)) + } + Array::SmallUnsigned(items) => { + write_items(encoder, buf, items, |qb, buf, val| qb.write_u16(buf, *val)) + } + Array::Unsigned(items) => { + write_items(encoder, buf, items, |qb, buf, val| qb.write_u32(buf, *val)) + } + Array::BigUnsigned(items) => { + write_items(encoder, buf, items, |qb, buf, val| qb.write_u64(buf, *val)) + } + Array::Float(items) => { + write_items(encoder, buf, items, |qb, buf, val| qb.write_f32(buf, *val)) + } + Array::Double(items) => { + write_items(encoder, buf, items, |qb, buf, val| qb.write_f64(buf, *val)) + } + Array::String(items) => { + write_items(encoder, buf, items, |qb, buf, val| qb.write_str(buf, val)) + } + Array::Char(items) => { + write_items(encoder, buf, items, |qb, buf, val| qb.write_char(buf, *val)) + } + Array::Bytes(items) => { + write_items(encoder, buf, items, |qb, buf, val| qb.write_bytes(buf, val)) + } + #[cfg(feature = "backend-postgres")] + Array::Enum(boxed) => { + write_items(encoder, buf, &boxed.as_ref().1, |qb, buf, val| { + qb.write_enum(buf, val.as_ref()) + }) + } + Array::Array(boxed) => { + let (_, inner) = boxed.as_ref(); + let mut first = true; + for item in inner.iter() { + if !first { + buf.write_char(',').unwrap(); + } + first = false; + match item { + Some(array) => write_array_recursive(encoder, buf, array), + None => buf.write_str("NULL").unwrap(), + } + } + } + #[cfg(feature = "with-json")] + Array::Json(items) => { + write_items(encoder, buf, items, |qb, buf, val| qb.write_json(buf, val)) + } + #[cfg(feature = "with-chrono")] + Array::ChronoDate(items) => write_items(encoder, buf, items, |qb, buf, val| { + qb.write_naive_date(buf, val) + }), + #[cfg(feature = "with-chrono")] + Array::ChronoTime(items) => write_items(encoder, buf, items, |qb, buf, val| { + qb.write_naive_time(buf, val) + }), + #[cfg(feature = "with-chrono")] + Array::ChronoDateTime(items) => write_items(encoder, buf, items, |qb, buf, val| { + qb.write_naive_datetime(buf, val) + }), + #[cfg(feature = "with-chrono")] + Array::ChronoDateTimeUtc(items) => { + write_items(encoder, buf, items, |qb, buf, val| { + qb.write_datetime_utc(buf, val) + }) + } + #[cfg(feature = "with-chrono")] + Array::ChronoDateTimeLocal(items) => { + write_items(encoder, buf, items, |qb, buf, val| { + qb.write_datetime_local(buf, val) + }) + } + #[cfg(feature = "with-chrono")] + Array::ChronoDateTimeWithTimeZone(items) => { + write_items(encoder, buf, items, |qb, buf, val| { + qb.write_datetime_fixed(buf, val) + }) + } + #[cfg(feature = "with-time")] + Array::TimeDate(items) => write_items(encoder, buf, items, |qb, buf, val| { + qb.write_time_date(buf, val) + }), + #[cfg(feature = "with-time")] + Array::TimeTime(items) => write_items(encoder, buf, items, |qb, buf, val| { + qb.write_time_time(buf, val) + }), + #[cfg(feature = "with-time")] + Array::TimeDateTime(items) => write_items(encoder, buf, items, |qb, buf, val| { + qb.write_time_datetime(buf, val) + }), + #[cfg(feature = "with-time")] + Array::TimeDateTimeWithTimeZone(items) => { + write_items(encoder, buf, items, |qb, buf, val| { + qb.write_time_datetime_tz(buf, val) + }) + } + #[cfg(feature = "with-jiff")] + Array::JiffDate(items) => write_items(encoder, buf, items, |qb, buf, val| { + qb.write_jiff_date(buf, val) + }), + #[cfg(feature = "with-jiff")] + Array::JiffTime(items) => write_items(encoder, buf, items, |qb, buf, val| { + qb.write_jiff_time(buf, val) + }), + #[cfg(feature = "with-jiff")] + Array::JiffDateTime(items) => write_items(encoder, buf, items, |qb, buf, val| { + qb.write_jiff_datetime(buf, val) + }), + #[cfg(feature = "with-jiff")] + Array::JiffTimestamp(items) => write_items(encoder, buf, items, |qb, buf, val| { + qb.write_jiff_timestamp(buf, val) + }), + #[cfg(feature = "with-jiff")] + Array::JiffZoned(items) => write_items(encoder, buf, items, |qb, buf, val| { + qb.write_jiff_zoned(buf, val) + }), + #[cfg(feature = "with-uuid")] + Array::Uuid(items) => { + write_items(encoder, buf, items, |qb, buf, val| qb.write_uuid(buf, val)) + } + #[cfg(feature = "with-rust_decimal")] + Array::Decimal(items) => write_items(encoder, buf, items, |qb, buf, val| { + qb.write_decimal(buf, val) + }), + #[cfg(feature = "with-bigdecimal")] + Array::BigDecimal(items) => write_items(encoder, buf, items, |qb, buf, val| { + qb.write_bigdecimal(buf, val) + }), + #[cfg(feature = "with-ipnetwork")] + Array::IpNetwork(items) => write_items(encoder, buf, items, |qb, buf, val| { + qb.write_ipnetwork(buf, val) + }), + #[cfg(feature = "with-mac_address")] + Array::MacAddress(items) => { + write_items(encoder, buf, items, |qb, buf, val| qb.write_mac(buf, val)) + } + } + buf.write_str("]'").unwrap(); + } + + write_array_recursive(self, buf, array); + } +} diff --git a/src/value/array.rs b/src/value/array.rs index 635a095ed..32b859b78 100644 --- a/src/value/array.rs +++ b/src/value/array.rs @@ -166,8 +166,8 @@ impl Array { Array::String(v) => v.is_empty(), Array::Char(v) => v.is_empty(), Array::Bytes(v) => v.is_empty(), - Array::Enum(boxed) => boxed.as_ref().1.is_empty(), - Array::Array(v) => v.as_ref().1.is_empty(), + Array::Enum(b) => b.as_ref().1.is_empty(), + Array::Array(b) => b.as_ref().1.is_empty(), #[cfg(feature = "with-json")] Array::Json(v) => v.is_empty(), #[cfg(feature = "with-chrono")] @@ -213,8 +213,265 @@ impl Array { } } - pub fn try_from_parts(ty: ArrayType, vals: Vec) -> Result { - match ty { + // TODO: optimize performance to avoid intermediate Value allocations + pub fn to_json_values(&self) -> Vec { + match self { + Array::Bool(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::Bool(x.clone()))) + .collect(), + Array::TinyInt(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::TinyInt(x.clone()))) + .collect(), + Array::SmallInt(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::SmallInt(x.clone()))) + .collect(), + Array::Int(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::Int(x.clone()))) + .collect(), + Array::BigInt(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::BigInt(x.clone()))) + .collect(), + Array::TinyUnsigned(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::TinyUnsigned(x.clone()))) + .collect(), + Array::SmallUnsigned(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::SmallUnsigned(x.clone()))) + .collect(), + Array::Unsigned(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::Unsigned(x.clone()))) + .collect(), + Array::BigUnsigned(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::BigUnsigned(x.clone()))) + .collect(), + Array::Float(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::Float(x.clone()))) + .collect(), + Array::Double(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::Double(x.clone()))) + .collect(), + Array::String(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::String(x.clone()))) + .collect(), + Array::Char(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::Char(x.clone()))) + .collect(), + Array::Bytes(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::Bytes(x.clone()))) + .collect(), + #[cfg(feature = "backend-postgres")] + Array::Enum(v) => { + let (_, arr) = v.as_ref(); + arr.iter() + .map(|x| super::sea_value_to_json_value(&Value::Enum(x.clone()))) + .collect() + } + Array::Array(v) => { + let (t, arrs) = v.as_ref(); + // Represent nested arrays as arrays of json values from inner arrays + arrs.iter() + .map(|opt_a| match opt_a { + Some(a) => Json::Array(a.to_json_values()), + None => Json::Null, + }) + .collect() + } + #[cfg(feature = "with-json")] + Array::Json(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::Json(x.clone()))) + .collect(), + #[cfg(feature = "with-chrono")] + Array::ChronoDate(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::ChronoDate(x.clone()))) + .collect(), + #[cfg(feature = "with-chrono")] + Array::ChronoTime(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::ChronoTime(x.clone()))) + .collect(), + #[cfg(feature = "with-chrono")] + Array::ChronoDateTime(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::ChronoDateTime(x.clone()))) + .collect(), + #[cfg(feature = "with-chrono")] + Array::ChronoDateTimeUtc(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::ChronoDateTimeUtc(x.clone()))) + .collect(), + #[cfg(feature = "with-chrono")] + Array::ChronoDateTimeLocal(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::ChronoDateTimeLocal(x.clone()))) + .collect(), + #[cfg(feature = "with-chrono")] + Array::ChronoDateTimeWithTimeZone(v) => v + .iter() + .map(|x| { + super::sea_value_to_json_value(&Value::ChronoDateTimeWithTimeZone(x.clone())) + }) + .collect(), + #[cfg(feature = "with-time")] + Array::TimeDate(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::TimeDate(x.clone()))) + .collect(), + #[cfg(feature = "with-time")] + Array::TimeTime(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::TimeTime(x.clone()))) + .collect(), + #[cfg(feature = "with-time")] + Array::TimeDateTime(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::TimeDateTime(x.clone()))) + .collect(), + #[cfg(feature = "with-time")] + Array::TimeDateTimeWithTimeZone(v) => v + .iter() + .map(|x| { + super::sea_value_to_json_value(&Value::TimeDateTimeWithTimeZone(x.clone())) + }) + .collect(), + #[cfg(feature = "with-jiff")] + Array::JiffDate(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::JiffDate(x.clone()))) + .collect(), + #[cfg(feature = "with-jiff")] + Array::JiffTime(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::JiffTime(x.clone()))) + .collect(), + #[cfg(feature = "with-jiff")] + Array::JiffDateTime(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::JiffDateTime(x.clone()))) + .collect(), + #[cfg(feature = "with-jiff")] + Array::JiffTimestamp(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::JiffTimestamp(x.clone()))) + .collect(), + #[cfg(feature = "with-jiff")] + Array::JiffZoned(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::JiffZoned(x.clone()))) + .collect(), + #[cfg(feature = "with-uuid")] + Array::Uuid(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::Uuid(x.clone()))) + .collect(), + #[cfg(feature = "with-rust_decimal")] + Array::Decimal(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::Decimal(x.clone()))) + .collect(), + #[cfg(feature = "with-bigdecimal")] + Array::BigDecimal(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::BigDecimal(x.clone()))) + .collect(), + #[cfg(feature = "with-ipnetwork")] + Array::IpNetwork(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::IpNetwork(x.clone()))) + .collect(), + #[cfg(feature = "with-mac_address")] + Array::MacAddress(v) => v + .iter() + .map(|x| super::sea_value_to_json_value(&Value::MacAddress(x.clone()))) + .collect(), + } + } + + pub fn dummy_value(&self) -> Self { + match self { + Array::Bool(_) => Array::Bool(Box::new([])), + Array::TinyInt(_) => Array::TinyInt(Box::new([])), + Array::SmallInt(_) => Array::SmallInt(Box::new([])), + Array::Int(_) => Array::Int(Box::new([])), + Array::BigInt(_) => Array::BigInt(Box::new([])), + Array::TinyUnsigned(_) => Array::TinyUnsigned(Box::new([])), + Array::SmallUnsigned(_) => Array::SmallUnsigned(Box::new([])), + Array::Unsigned(_) => Array::Unsigned(Box::new([])), + Array::BigUnsigned(_) => Array::BigUnsigned(Box::new([])), + Array::Float(_) => Array::Float(Box::new([])), + Array::Double(_) => Array::Double(Box::new([])), + Array::String(_) => Array::String(Box::new([])), + Array::Char(_) => Array::Char(Box::new([])), + Array::Bytes(_) => Array::Bytes(Box::new([])), + Array::Enum(val) => { + let val = val.as_ref(); + Array::Enum(Box::new((val.0.clone(), Box::new([])))) + } + Array::Array(val) => { + let val = val.as_ref(); + Array::Array(Box::new((val.0.clone(), Box::new([])))) + } + #[cfg(feature = "with-json")] + Array::Json(_) => Array::Json(Box::new([])), + #[cfg(feature = "with-chrono")] + Array::ChronoDate(_) => Array::ChronoDate(Box::new([])), + #[cfg(feature = "with-chrono")] + Array::ChronoTime(_) => Array::ChronoTime(Box::new([])), + #[cfg(feature = "with-chrono")] + Array::ChronoDateTime(_) => Array::ChronoDateTime(Box::new([])), + #[cfg(feature = "with-chrono")] + Array::ChronoDateTimeUtc(_) => Array::ChronoDateTimeUtc(Box::new([])), + #[cfg(feature = "with-chrono")] + Array::ChronoDateTimeLocal(_) => Array::ChronoDateTimeLocal(Box::new([])), + #[cfg(feature = "with-chrono")] + Array::ChronoDateTimeWithTimeZone(_) => Array::ChronoDateTimeWithTimeZone(Box::new([])), + #[cfg(feature = "with-time")] + Array::TimeDate(_) => Array::TimeDate(Box::new([])), + #[cfg(feature = "with-time")] + Array::TimeTime(_) => Array::TimeTime(Box::new([])), + #[cfg(feature = "with-time")] + Array::TimeDateTime(_) => Array::TimeDateTime(Box::new([])), + #[cfg(feature = "with-time")] + Array::TimeDateTimeWithTimeZone(_) => Array::TimeDateTimeWithTimeZone(Box::new([])), + #[cfg(feature = "with-jiff")] + Array::JiffDate(_) => Array::JiffDate(Box::new([])), + #[cfg(feature = "with-jiff")] + Array::JiffTime(_) => Array::JiffTime(Box::new([])), + #[cfg(feature = "with-jiff")] + Array::JiffDateTime(_) => Array::JiffDateTime(Box::new([])), + #[cfg(feature = "with-jiff")] + Array::JiffTimestamp(_) => Array::JiffTimestamp(Box::new([])), + #[cfg(feature = "with-jiff")] + Array::JiffZoned(_) => Array::JiffZoned(Box::new([])), + #[cfg(feature = "with-uuid")] + Array::Uuid(_) => Array::Uuid(Box::new([])), + #[cfg(feature = "with-rust_decimal")] + Array::Decimal(_) => Array::Decimal(Box::new([])), + #[cfg(feature = "with-bigdecimal")] + Array::BigDecimal(_) => Array::BigDecimal(Box::new([])), + #[cfg(feature = "with-ipnetwork")] + Array::IpNetwork(_) => Array::IpNetwork(Box::new([])), + #[cfg(feature = "with-mac_address")] + Array::MacAddress(_) => Array::MacAddress(Box::new([])), + } + } + + pub fn try_from_parts(kind: ArrayType, elems: Vec) -> Result { + match kind { ArrayType::Bool => { let mut v = Vec::with_capacity(vals.len()); for e in vals { @@ -599,121 +856,7 @@ impl Array { } } } - - pub fn dummy_value(&self) -> Self { - match self { - Array::Bool(_) => Array::Bool(Box::new([])), - Array::TinyInt(_) => Array::TinyInt(Box::new([])), - Array::SmallInt(_) => Array::SmallInt(Box::new([])), - Array::Int(_) => Array::Int(Box::new([])), - Array::BigInt(_) => Array::BigInt(Box::new([])), - Array::TinyUnsigned(_) => Array::TinyUnsigned(Box::new([])), - Array::SmallUnsigned(_) => Array::SmallUnsigned(Box::new([])), - Array::Unsigned(_) => Array::Unsigned(Box::new([])), - Array::BigUnsigned(_) => Array::BigUnsigned(Box::new([])), - Array::Float(_) => Array::Float(Box::new([])), - Array::Double(_) => Array::Double(Box::new([])), - Array::String(_) => Array::String(Box::new([])), - Array::Char(_) => Array::Char(Box::new([])), - Array::Bytes(_) => Array::Bytes(Box::new([])), - Array::Enum(val) => { - let val = val.as_ref(); - Array::Enum(Box::new((val.0.clone(), Box::new([])))) - } - Array::Array(val) => { - let val = val.as_ref(); - Array::Array(Box::new((val.0.clone(), Box::new([])))) - } - #[cfg(feature = "with-json")] - Array::Json(_) => Array::Json(Box::new([])), - #[cfg(feature = "with-chrono")] - Array::ChronoDate(_) => Array::ChronoDate(Box::new([])), - #[cfg(feature = "with-chrono")] - Array::ChronoTime(_) => Array::ChronoTime(Box::new([])), - #[cfg(feature = "with-chrono")] - Array::ChronoDateTime(_) => Array::ChronoDateTime(Box::new([])), - #[cfg(feature = "with-chrono")] - Array::ChronoDateTimeUtc(_) => Array::ChronoDateTimeUtc(Box::new([])), - #[cfg(feature = "with-chrono")] - Array::ChronoDateTimeLocal(_) => Array::ChronoDateTimeLocal(Box::new([])), - #[cfg(feature = "with-chrono")] - Array::ChronoDateTimeWithTimeZone(_) => Array::ChronoDateTimeWithTimeZone(Box::new([])), - #[cfg(feature = "with-time")] - Array::TimeDate(_) => Array::TimeDate(Box::new([])), - #[cfg(feature = "with-time")] - Array::TimeTime(_) => Array::TimeTime(Box::new([])), - #[cfg(feature = "with-time")] - Array::TimeDateTime(_) => Array::TimeDateTime(Box::new([])), - #[cfg(feature = "with-time")] - Array::TimeDateTimeWithTimeZone(_) => Array::TimeDateTimeWithTimeZone(Box::new([])), - #[cfg(feature = "with-jiff")] - Array::JiffDate(_) => Array::JiffDate(Box::new([])), - #[cfg(feature = "with-jiff")] - Array::JiffTime(_) => Array::JiffTime(Box::new([])), - #[cfg(feature = "with-jiff")] - Array::JiffDateTime(_) => Array::JiffDateTime(Box::new([])), - #[cfg(feature = "with-jiff")] - Array::JiffTimestamp(_) => Array::JiffTimestamp(Box::new([])), - #[cfg(feature = "with-jiff")] - Array::JiffZoned(_) => Array::JiffZoned(Box::new([])), - #[cfg(feature = "with-uuid")] - Array::Uuid(_) => Array::Uuid(Box::new([])), - #[cfg(feature = "with-rust_decimal")] - Array::Decimal(_) => Array::Decimal(Box::new([])), - #[cfg(feature = "with-bigdecimal")] - Array::BigDecimal(_) => Array::BigDecimal(Box::new([])), - #[cfg(feature = "with-ipnetwork")] - Array::IpNetwork(_) => Array::IpNetwork(Box::new([])), - #[cfg(feature = "with-mac_address")] - Array::MacAddress(_) => Array::MacAddress(Box::new([])), - } - } - - #[cfg(feature = "with-json")] - #[allow(unused)] - pub(crate) fn to_json_values(&self) -> Vec { - match self { - Array::Bool(items) => todo!(), - Array::TinyInt(items) => todo!(), - Array::SmallInt(items) => todo!(), - Array::Int(items) => todo!(), - Array::BigInt(items) => todo!(), - Array::TinyUnsigned(items) => todo!(), - Array::SmallUnsigned(items) => todo!(), - Array::Unsigned(items) => todo!(), - Array::BigUnsigned(items) => todo!(), - Array::Float(items) => todo!(), - Array::Double(items) => todo!(), - Array::String(items) => todo!(), - Array::Char(items) => todo!(), - Array::Bytes(items) => todo!(), - Array::Enum(_) => todo!(), - Array::Array(items) => todo!(), - Array::Json(values) => todo!(), - Array::ChronoDate(naive_dates) => todo!(), - Array::ChronoTime(naive_times) => todo!(), - Array::ChronoDateTime(naive_date_times) => todo!(), - Array::ChronoDateTimeUtc(date_times) => todo!(), - Array::ChronoDateTimeLocal(date_times) => todo!(), - Array::ChronoDateTimeWithTimeZone(date_times) => todo!(), - Array::TimeDate(dates) => todo!(), - Array::TimeTime(times) => todo!(), - Array::TimeDateTime(primitive_date_times) => todo!(), - Array::TimeDateTimeWithTimeZone(offset_date_times) => todo!(), - Array::JiffDate(dates) => todo!(), - Array::JiffTime(times) => todo!(), - Array::JiffDateTime(date_times) => todo!(), - Array::JiffTimestamp(timestamps) => todo!(), - Array::JiffZoned(zoneds) => todo!(), - Array::Uuid(uuids) => todo!(), - Array::Decimal(decimals) => todo!(), - Array::BigDecimal(big_decimals) => todo!(), - Array::IpNetwork(ip_networks) => todo!(), - Array::MacAddress(items) => todo!(), - } - } } - #[cfg(feature = "hashable-value")] mod hash { use ordered_float::{FloatCore, OrderedFloat}; @@ -791,6 +934,8 @@ mod hash { impl std::hash::Hash for Array { fn hash(&self, state: &mut H) { + use ordered_float::OrderedFloat; + std::mem::discriminant(self).hash(state); match self { Array::Bool(items) => items.hash(state), @@ -802,43 +947,62 @@ mod hash { Array::SmallUnsigned(items) => items.hash(state), Array::Unsigned(items) => items.hash(state), Array::BigUnsigned(items) => items.hash(state), - Array::Float(items) => items - .iter() - .copied() - .map(|x| x.map(OrderedFloat)) - .collect::>() - .hash(state), - Array::Double(items) => items - .iter() - .copied() - .map(|x| x.map(OrderedFloat)) - .collect::>() - .hash(state), + Array::Float(items) => { + for x in items.iter() { + x.map(OrderedFloat).hash(state) + } + } + Array::Double(items) => { + for x in items.iter() { + x.map(OrderedFloat).hash(state) + } + } Array::String(items) => items.hash(state), Array::Char(items) => items.hash(state), Array::Bytes(items) => items.hash(state), Array::Enum(items) => items.hash(state), Array::Array(items) => items.hash(state), + #[cfg(feature = "with-json")] Array::Json(items) => items.hash(state), + #[cfg(feature = "with-chrono")] Array::ChronoDate(items) => items.hash(state), + #[cfg(feature = "with-chrono")] Array::ChronoTime(items) => items.hash(state), + #[cfg(feature = "with-chrono")] Array::ChronoDateTime(items) => items.hash(state), + #[cfg(feature = "with-chrono")] Array::ChronoDateTimeUtc(items) => items.hash(state), + #[cfg(feature = "with-chrono")] Array::ChronoDateTimeLocal(items) => items.hash(state), + #[cfg(feature = "with-chrono")] Array::ChronoDateTimeWithTimeZone(items) => items.hash(state), + #[cfg(feature = "with-time")] Array::TimeDate(items) => items.hash(state), + #[cfg(feature = "with-time")] Array::TimeTime(items) => items.hash(state), + #[cfg(feature = "with-time")] Array::TimeDateTime(items) => items.hash(state), + #[cfg(feature = "with-time")] Array::TimeDateTimeWithTimeZone(items) => items.hash(state), + #[cfg(feature = "with-jiff")] Array::JiffDate(items) => items.hash(state), + #[cfg(feature = "with-jiff")] Array::JiffTime(items) => items.hash(state), + #[cfg(feature = "with-jiff")] Array::JiffDateTime(items) => items.hash(state), + #[cfg(feature = "with-jiff")] Array::JiffTimestamp(items) => items.hash(state), + #[cfg(feature = "with-jiff")] Array::JiffZoned(items) => items.hash(state), + #[cfg(feature = "with-uuid")] Array::Uuid(items) => items.hash(state), + #[cfg(feature = "with-rust_decimal")] Array::Decimal(items) => items.hash(state), + #[cfg(feature = "with-bigdecimal")] Array::BigDecimal(items) => items.hash(state), + #[cfg(feature = "with-ipnetwork")] Array::IpNetwork(items) => items.hash(state), + #[cfg(feature = "with-mac_address")] Array::MacAddress(items) => items.hash(state), } } diff --git a/src/value/hashable_value.rs b/src/value/hashable_value.rs index 4acbadcf8..958b128ff 100644 --- a/src/value/hashable_value.rs +++ b/src/value/hashable_value.rs @@ -333,11 +333,11 @@ mod tests { #[cfg(feature = "postgres-array")] #[test] fn test_hash_value_array() { - use crate::{value::Array, ArrayType}; + use crate::{ArrayType, value::Array}; assert_eq!( Into::::into(vec![0i32, 1, 2]), - Value::Array(Some(Box::new( + Value::Array(Some( Array::try_from_parts( ArrayType::Int, vec![ @@ -347,12 +347,12 @@ mod tests { ], ) .expect("array construction"), - ))) + )) ); assert_eq!( Into::::into(vec![0f32, 1.0, 2.0]), - Value::Array(Some(Box::new( + Value::Array(Some( Array::try_from_parts( ArrayType::Float, vec![ @@ -362,7 +362,7 @@ mod tests { ], ) .expect("array construction"), - ))) + )) ); let hash_set: std::collections::HashSet = [ diff --git a/src/value/tests.rs b/src/value/tests.rs index 91c78db59..6c56b2f5a 100644 --- a/src/value/tests.rs +++ b/src/value/tests.rs @@ -413,14 +413,14 @@ fn test_decimal_value() { 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]); + let out: Vec> = v.unwrap(); + assert_eq!(out, vec![Some(1), Some(2), Some(3), Some(4), Some(5)]); } #[test] #[cfg(feature = "postgres-array")] fn test_option_array_value() { - let v: Value = Value::Array(ArrayType::Int, None); - let out: Option> = v.unwrap(); + let v: Value = Value::Array(None); + let out: Option>> = v.unwrap(); assert_eq!(out, None); } diff --git a/src/value/with_array.rs b/src/value/with_array.rs index f1a81de67..bf7709798 100644 --- a/src/value/with_array.rs +++ b/src/value/with_array.rs @@ -79,6 +79,24 @@ impl NotU8 for MacAddress {} macro_rules! impl_value_vec { ($($ty:ty => $vari:ident)*) => { $( + impl From> for Array { + fn from(x: Vec<$ty>) -> Array { + let values: Vec> = x + .into_iter() + .map(Some) + .collect(); + + Array::$vari(values.into_boxed_slice()) + } + } + + + impl From>> for Array { + fn from(x: Vec>) -> Array { + Array::$vari(x.into_boxed_slice()) + } + } + impl From> for Value { fn from(x: Vec<$ty>) -> Value { let values: Vec> = x @@ -142,6 +160,50 @@ impl_value_vec! { Vec => Bytes } +// Impls for u8 +// because Vec is already defined as Bytes +impl From> for Array { + fn from(x: Vec) -> Array { + let values: Vec> = x.into_iter().map(Some).collect(); + + Array::TinyUnsigned(values.into_boxed_slice()) + } +} + +impl From>> for Array { + fn from(x: Vec>) -> Array { + Array::TinyUnsigned(x.into_boxed_slice()) + } +} + +impl From>> for Value { + fn from(x: Vec>) -> Value { + Value::Array(Some(Array::TinyUnsigned(x.into_boxed_slice()))) + } +} + +impl ValueType for Vec> { + fn try_from(v: Value) -> Result { + match v { + Value::Array(Some(Array::TinyUnsigned(inner))) => Ok(inner.into_vec()), + _ => Err(ValueTypeErr), + } + } + + fn type_name() -> String { + stringify!(Vec).to_owned() + } + + fn array_type() -> ArrayType { + ::array_type() + } + + fn column_type() -> ColumnType { + use ColumnType::*; + Array(RcOrArc::new(::column_type())) + } +} + #[cfg(feature = "with-json")] impl_value_vec! { serde_json::Value => Json @@ -152,6 +214,7 @@ impl From<(Arc, Vec>>)> for Value { fn from(x: (Arc, Vec>>)) -> Value { Value::Array(Some(Array::Enum(Box::new((x.0, x.1.into_boxed_slice()))))) } +} #[cfg(feature = "with-chrono")] impl_value_vec! { diff --git a/src/value/with_json.rs b/src/value/with_json.rs index 7b368f66c..7732144c3 100644 --- a/src/value/with_json.rs +++ b/src/value/with_json.rs @@ -111,7 +111,7 @@ pub fn sea_value_to_json_value(value: &Value) -> Json { #[cfg(feature = "backend-postgres")] Value::Enum(Some(v)) => Json::String(v.value.to_string()), #[cfg(feature = "postgres-array")] - Value::Array(_, Some(v)) => Json::Array(v.iter().map(sea_value_to_json_value).collect()), + Value::Array(Some(v)) => Json::Array(v.to_json_values()), #[cfg(feature = "postgres-vector")] Value::Vector(Some(v)) => Json::Array(v.as_slice().iter().map(|&v| v.into()).collect()), #[cfg(feature = "with-ipnetwork")] From 6e3869b42f910e2128f4449590ef23637ea3a8e0 Mon Sep 17 00:00:00 2001 From: Huliiiiii <308013446a@gmail.com> Date: Tue, 28 Oct 2025 21:29:17 +0800 Subject: [PATCH 05/19] Update --- src/backend/postgres/query.rs | 26 +++- src/backend/value_encoder.rs | 275 +++++++++++++++++++++------------- src/lib.rs | 1 + src/utils.rs | 20 +++ src/value/array.rs | 144 +++++++++--------- 5 files changed, 285 insertions(+), 181 deletions(-) create mode 100644 src/utils.rs diff --git a/src/backend/postgres/query.rs b/src/backend/postgres/query.rs index 374e9f3ca..b6a5e85a3 100644 --- a/src/backend/postgres/query.rs +++ b/src/backend/postgres/query.rs @@ -30,6 +30,16 @@ impl PrecedenceDecider for PostgresQueryBuilder { } impl ValueEncoder for PostgresQueryBuilder { + fn write_str(&self, buf: &mut impl Write, value: &str) { + if self.needs_escape(value) { + buf.write_str("E'").unwrap(); + } else { + buf.write_str("'").unwrap(); + } + self.write_escaped(buf, value); + buf.write_str("'").unwrap(); + } + fn write_enum(&self, buf: &mut impl Write, value: &crate::value::Enum) { // Write the enum value as a quoted string self.write_str(buf, value.value.as_str()); @@ -40,6 +50,14 @@ impl ValueEncoder for PostgresQueryBuilder { buf.write_str(type_name).unwrap(); } } + + fn write_bytes(&self, buffer: &mut impl Write, bytes: &[u8]) { + buffer.write_str("'\\x").unwrap(); + for b in bytes { + write!(buffer, "{b:02X}").unwrap(); + } + buffer.write_str("'").unwrap(); + } } impl QueryBuilder for PostgresQueryBuilder { @@ -206,14 +224,6 @@ impl QueryBuilder for PostgresQueryBuilder { buffer.write_str("'").unwrap(); } - fn write_bytes(&self, bytes: &[u8], buffer: &mut impl Write) { - buffer.write_str("'\\x").unwrap(); - for b in bytes { - write!(buffer, "{b:02X}").unwrap(); - } - buffer.write_str("'").unwrap(); - } - fn if_null_function(&self) -> &str { "COALESCE" } diff --git a/src/backend/value_encoder.rs b/src/backend/value_encoder.rs index 79f2c24bd..9f09ecfae 100644 --- a/src/backend/value_encoder.rs +++ b/src/backend/value_encoder.rs @@ -250,202 +250,275 @@ pub trait ValueEncoder: EscapeBuilder { #[cfg(feature = "backend-postgres")] fn write_enum(&self, buf: &mut impl Write, value: &crate::value::Enum) { - buf.write_str(value.value.as_str()); + self.write_str(buf, value.value.as_str()); } #[cfg(feature = "postgres-array")] fn write_array(&self, buf: &mut impl Write, array: &crate::value::Array) { + use std::fmt; + use crate::value::Array; - fn write_items(encoder: &QB, buf: &mut W, items: &[Option], mut f: F) + fn write_array_values( + encoder: &VE, + buf: &mut W, + items: &[Option], + mut f: F, + ) -> fmt::Result where - QB: ValueEncoder + ?Sized, + VE: ValueEncoder + ?Sized, W: Write, - F: FnMut(&QB, &mut W, &T), + F: FnMut(&VE, &mut W, &T), { - let mut first = true; - for item in items { - if !first { - buf.write_char(',').unwrap(); - } - first = false; - match item { - Some(value) => f(encoder, buf, value), - None => buf.write_str("NULL").unwrap(), - } - } + use crate::utils::join_write; + + join_write( + buf, + items, + |buf| buf.write_char(','), + |buf, item| { + match item.as_ref() { + Some(val) => f(encoder, buf, val), + None => buf.write_str("NULL")?, + } + + Ok(()) + }, + ) } - fn write_array_recursive(encoder: &QB, buf: &mut W, array: &Array) + fn write_array_recursive(encoder: &VE, buf: &mut W, array: &Array) -> fmt::Result where - QB: ValueEncoder + ?Sized, + VE: ValueEncoder + ?Sized, W: Write, { if array.is_empty() { - buf.write_str("'{}'").unwrap(); - return; + return buf.write_str("'{}'"); } - buf.write_str("'ARRAY[").unwrap(); + buf.write_str("'ARRAY[")?; match array { Array::Bool(items) => { - write_items(encoder, buf, items, |qb, buf, val| qb.write_bool(buf, *val)) + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_bool(buf, *val) + }) } Array::TinyInt(items) => { - write_items(encoder, buf, items, |qb, buf, val| qb.write_i8(buf, *val)) + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_i8(buf, *val) + }) } Array::SmallInt(items) => { - write_items(encoder, buf, items, |qb, buf, val| qb.write_i16(buf, *val)) + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_i16(buf, *val) + }) } Array::Int(items) => { - write_items(encoder, buf, items, |qb, buf, val| qb.write_i32(buf, *val)) + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_i32(buf, *val) + }) } Array::BigInt(items) => { - write_items(encoder, buf, items, |qb, buf, val| qb.write_i64(buf, *val)) + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_i64(buf, *val) + }) } Array::TinyUnsigned(items) => { - write_items(encoder, buf, items, |qb, buf, val| qb.write_u8(buf, *val)) + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_u8(buf, *val) + }) } Array::SmallUnsigned(items) => { - write_items(encoder, buf, items, |qb, buf, val| qb.write_u16(buf, *val)) + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_u16(buf, *val) + }) } Array::Unsigned(items) => { - write_items(encoder, buf, items, |qb, buf, val| qb.write_u32(buf, *val)) + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_u32(buf, *val) + }) } Array::BigUnsigned(items) => { - write_items(encoder, buf, items, |qb, buf, val| qb.write_u64(buf, *val)) + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_u64(buf, *val) + }) } Array::Float(items) => { - write_items(encoder, buf, items, |qb, buf, val| qb.write_f32(buf, *val)) + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_f32(buf, *val) + }) } Array::Double(items) => { - write_items(encoder, buf, items, |qb, buf, val| qb.write_f64(buf, *val)) + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_f64(buf, *val) + }) } Array::String(items) => { - write_items(encoder, buf, items, |qb, buf, val| qb.write_str(buf, val)) + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_str(buf, val) + }) } Array::Char(items) => { - write_items(encoder, buf, items, |qb, buf, val| qb.write_char(buf, *val)) + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_char(buf, *val) + }) } Array::Bytes(items) => { - write_items(encoder, buf, items, |qb, buf, val| qb.write_bytes(buf, val)) + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_bytes(buf, val) + }) } #[cfg(feature = "backend-postgres")] Array::Enum(boxed) => { - write_items(encoder, buf, &boxed.as_ref().1, |qb, buf, val| { - qb.write_enum(buf, val.as_ref()) + write_array_values(encoder, buf, &boxed.as_ref().1, |encoder, buf, val| { + encoder.write_enum(buf, val.as_ref()) }) } Array::Array(boxed) => { + use crate::utils::join_write; + let (_, inner) = boxed.as_ref(); - let mut first = true; - for item in inner.iter() { - if !first { - buf.write_char(',').unwrap(); - } - first = false; - match item { + join_write( + buf, + inner, + |buf| buf.write_char(','), + |buf, item| match item { Some(array) => write_array_recursive(encoder, buf, array), - None => buf.write_str("NULL").unwrap(), - } - } + None => buf.write_str("NULL"), + }, + ) } #[cfg(feature = "with-json")] Array::Json(items) => { - write_items(encoder, buf, items, |qb, buf, val| qb.write_json(buf, val)) + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_json(buf, val) + }) } #[cfg(feature = "with-chrono")] - Array::ChronoDate(items) => write_items(encoder, buf, items, |qb, buf, val| { - qb.write_naive_date(buf, val) - }), + Array::ChronoDate(items) => { + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_naive_date(buf, val) + }) + } #[cfg(feature = "with-chrono")] - Array::ChronoTime(items) => write_items(encoder, buf, items, |qb, buf, val| { - qb.write_naive_time(buf, val) - }), + Array::ChronoTime(items) => { + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_naive_time(buf, val) + }) + } #[cfg(feature = "with-chrono")] - Array::ChronoDateTime(items) => write_items(encoder, buf, items, |qb, buf, val| { - qb.write_naive_datetime(buf, val) - }), + Array::ChronoDateTime(items) => { + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_naive_datetime(buf, val) + }) + } #[cfg(feature = "with-chrono")] Array::ChronoDateTimeUtc(items) => { - write_items(encoder, buf, items, |qb, buf, val| { - qb.write_datetime_utc(buf, val) + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_datetime_utc(buf, val) }) } #[cfg(feature = "with-chrono")] Array::ChronoDateTimeLocal(items) => { - write_items(encoder, buf, items, |qb, buf, val| { - qb.write_datetime_local(buf, val) + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_datetime_local(buf, val) }) } #[cfg(feature = "with-chrono")] Array::ChronoDateTimeWithTimeZone(items) => { - write_items(encoder, buf, items, |qb, buf, val| { - qb.write_datetime_fixed(buf, val) + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_datetime_fixed(buf, val) }) } #[cfg(feature = "with-time")] - Array::TimeDate(items) => write_items(encoder, buf, items, |qb, buf, val| { - qb.write_time_date(buf, val) - }), + Array::TimeDate(items) => { + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_time_date(buf, val) + }) + } #[cfg(feature = "with-time")] - Array::TimeTime(items) => write_items(encoder, buf, items, |qb, buf, val| { - qb.write_time_time(buf, val) - }), + Array::TimeTime(items) => { + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_time_time(buf, val) + }) + } #[cfg(feature = "with-time")] - Array::TimeDateTime(items) => write_items(encoder, buf, items, |qb, buf, val| { - qb.write_time_datetime(buf, val) - }), + Array::TimeDateTime(items) => { + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_time_datetime(buf, val) + }) + } #[cfg(feature = "with-time")] Array::TimeDateTimeWithTimeZone(items) => { - write_items(encoder, buf, items, |qb, buf, val| { - qb.write_time_datetime_tz(buf, val) + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_time_datetime_tz(buf, val) }) } #[cfg(feature = "with-jiff")] - Array::JiffDate(items) => write_items(encoder, buf, items, |qb, buf, val| { - qb.write_jiff_date(buf, val) - }), + Array::JiffDate(items) => { + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_jiff_date(buf, val) + }) + } #[cfg(feature = "with-jiff")] - Array::JiffTime(items) => write_items(encoder, buf, items, |qb, buf, val| { - qb.write_jiff_time(buf, val) - }), + Array::JiffTime(items) => { + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_jiff_time(buf, val) + }) + } #[cfg(feature = "with-jiff")] - Array::JiffDateTime(items) => write_items(encoder, buf, items, |qb, buf, val| { - qb.write_jiff_datetime(buf, val) - }), + Array::JiffDateTime(items) => { + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_jiff_datetime(buf, val) + }) + } #[cfg(feature = "with-jiff")] - Array::JiffTimestamp(items) => write_items(encoder, buf, items, |qb, buf, val| { - qb.write_jiff_timestamp(buf, val) - }), + Array::JiffTimestamp(items) => { + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_jiff_timestamp(buf, val) + }) + } #[cfg(feature = "with-jiff")] - Array::JiffZoned(items) => write_items(encoder, buf, items, |qb, buf, val| { - qb.write_jiff_zoned(buf, val) - }), + Array::JiffZoned(items) => { + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_jiff_zoned(buf, val) + }) + } #[cfg(feature = "with-uuid")] Array::Uuid(items) => { - write_items(encoder, buf, items, |qb, buf, val| qb.write_uuid(buf, val)) + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_uuid(buf, val) + }) } #[cfg(feature = "with-rust_decimal")] - Array::Decimal(items) => write_items(encoder, buf, items, |qb, buf, val| { - qb.write_decimal(buf, val) - }), + Array::Decimal(items) => { + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_decimal(buf, val) + }) + } #[cfg(feature = "with-bigdecimal")] - Array::BigDecimal(items) => write_items(encoder, buf, items, |qb, buf, val| { - qb.write_bigdecimal(buf, val) - }), + Array::BigDecimal(items) => { + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_bigdecimal(buf, val) + }) + } #[cfg(feature = "with-ipnetwork")] - Array::IpNetwork(items) => write_items(encoder, buf, items, |qb, buf, val| { - qb.write_ipnetwork(buf, val) - }), + Array::IpNetwork(items) => { + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_ipnetwork(buf, val) + }) + } #[cfg(feature = "with-mac_address")] Array::MacAddress(items) => { - write_items(encoder, buf, items, |qb, buf, val| qb.write_mac(buf, val)) + write_array_values(encoder, buf, items, |encoder, buf, val| { + encoder.write_mac(buf, val) + }) } } - buf.write_str("]'").unwrap(); + .unwrap(); + buf.write_str("]'") } - write_array_recursive(self, buf, array); + write_array_recursive(self, buf, array).unwrap() } } diff --git a/src/lib.rs b/src/lib.rs index a0f87bd1f..a7d68ffd8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1022,6 +1022,7 @@ pub mod sqlx; pub mod table; pub mod token; pub mod types; +mod utils; pub mod value; #[doc(hidden)] diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 000000000..02d3abfd8 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,20 @@ +use std::fmt; + +// Make write a sperater a bit faster +pub(crate) fn join_write( + buf: &mut B, + items: impl IntoIterator, + mut join: impl (FnMut(&mut B) -> fmt::Result), + mut r#do: impl (FnMut(&mut B, T) -> fmt::Result), +) -> fmt::Result { + let mut iter = items.into_iter(); + if let Some(first) = iter.next() { + r#do(buf, first)?; + for item in iter { + join(buf)?; + r#do(buf, item)?; + } + } + + Ok(()) +} diff --git a/src/value/array.rs b/src/value/array.rs index 32b859b78..8403cccad 100644 --- a/src/value/array.rs +++ b/src/value/array.rs @@ -473,8 +473,8 @@ impl Array { pub fn try_from_parts(kind: ArrayType, elems: Vec) -> Result { match kind { ArrayType::Bool => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::Bool(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -483,8 +483,8 @@ impl Array { Ok(Array::Bool(v.into_boxed_slice())) } ArrayType::TinyInt => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::TinyInt(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -493,8 +493,8 @@ impl Array { Ok(Array::TinyInt(v.into_boxed_slice())) } ArrayType::SmallInt => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::SmallInt(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -503,8 +503,8 @@ impl Array { Ok(Array::SmallInt(v.into_boxed_slice())) } ArrayType::Int => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::Int(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -513,8 +513,8 @@ impl Array { Ok(Array::Int(v.into_boxed_slice())) } ArrayType::BigInt => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::BigInt(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -523,8 +523,8 @@ impl Array { Ok(Array::BigInt(v.into_boxed_slice())) } ArrayType::TinyUnsigned => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::TinyUnsigned(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -533,8 +533,8 @@ impl Array { Ok(Array::TinyUnsigned(v.into_boxed_slice())) } ArrayType::SmallUnsigned => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::SmallUnsigned(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -543,8 +543,8 @@ impl Array { Ok(Array::SmallUnsigned(v.into_boxed_slice())) } ArrayType::Unsigned => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::Unsigned(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -553,8 +553,8 @@ impl Array { Ok(Array::Unsigned(v.into_boxed_slice())) } ArrayType::BigUnsigned => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::BigUnsigned(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -563,8 +563,8 @@ impl Array { Ok(Array::BigUnsigned(v.into_boxed_slice())) } ArrayType::Float => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::Float(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -573,8 +573,8 @@ impl Array { Ok(Array::Float(v.into_boxed_slice())) } ArrayType::Double => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::Double(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -583,8 +583,8 @@ impl Array { Ok(Array::Double(v.into_boxed_slice())) } ArrayType::String => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::String(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -593,8 +593,8 @@ impl Array { Ok(Array::String(v.into_boxed_slice())) } ArrayType::Char => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::Char(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -603,8 +603,8 @@ impl Array { Ok(Array::Char(v.into_boxed_slice())) } ArrayType::Bytes => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::Bytes(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -614,8 +614,8 @@ impl Array { } #[cfg(feature = "backend-postgres")] ArrayType::Enum(name) => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::Enum(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -625,8 +625,8 @@ impl Array { } #[cfg(feature = "with-json")] ArrayType::Json => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::Json(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -636,8 +636,8 @@ impl Array { } #[cfg(feature = "with-chrono")] ArrayType::ChronoDate => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::ChronoDate(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -647,8 +647,8 @@ impl Array { } #[cfg(feature = "with-chrono")] ArrayType::ChronoTime => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::ChronoTime(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -658,8 +658,8 @@ impl Array { } #[cfg(feature = "with-chrono")] ArrayType::ChronoDateTime => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::ChronoDateTime(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -669,8 +669,8 @@ impl Array { } #[cfg(feature = "with-chrono")] ArrayType::ChronoDateTimeUtc => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::ChronoDateTimeUtc(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -680,8 +680,8 @@ impl Array { } #[cfg(feature = "with-chrono")] ArrayType::ChronoDateTimeLocal => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::ChronoDateTimeLocal(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -691,8 +691,8 @@ impl Array { } #[cfg(feature = "with-chrono")] ArrayType::ChronoDateTimeWithTimeZone => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::ChronoDateTimeWithTimeZone(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -702,8 +702,8 @@ impl Array { } #[cfg(feature = "with-time")] ArrayType::TimeDate => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::TimeDate(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -713,8 +713,8 @@ impl Array { } #[cfg(feature = "with-time")] ArrayType::TimeTime => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::TimeTime(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -724,8 +724,8 @@ impl Array { } #[cfg(feature = "with-time")] ArrayType::TimeDateTime => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::TimeDateTime(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -735,8 +735,8 @@ impl Array { } #[cfg(feature = "with-time")] ArrayType::TimeDateTimeWithTimeZone => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::TimeDateTimeWithTimeZone(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -746,8 +746,8 @@ impl Array { } #[cfg(feature = "with-jiff")] ArrayType::JiffDate => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::JiffDate(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -757,8 +757,8 @@ impl Array { } #[cfg(feature = "with-jiff")] ArrayType::JiffTime => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::JiffTime(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -768,8 +768,8 @@ impl Array { } #[cfg(feature = "with-jiff")] ArrayType::JiffDateTime => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::JiffDateTime(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -779,8 +779,8 @@ impl Array { } #[cfg(feature = "with-jiff")] ArrayType::JiffTimestamp => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::JiffTimestamp(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -790,8 +790,8 @@ impl Array { } #[cfg(feature = "with-jiff")] ArrayType::JiffZoned => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::JiffZoned(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -801,8 +801,8 @@ impl Array { } #[cfg(feature = "with-uuid")] ArrayType::Uuid => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::Uuid(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -812,8 +812,8 @@ impl Array { } #[cfg(feature = "with-rust_decimal")] ArrayType::Decimal => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::Decimal(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -823,8 +823,8 @@ impl Array { } #[cfg(feature = "with-bigdecimal")] ArrayType::BigDecimal => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::BigDecimal(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -834,8 +834,8 @@ impl Array { } #[cfg(feature = "with-ipnetwork")] ArrayType::IpNetwork => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::IpNetwork(x) => v.push(x), _ => return Err(ValueTypeErr), @@ -845,8 +845,8 @@ impl Array { } #[cfg(feature = "with-mac_address")] ArrayType::MacAddress => { - let mut v = Vec::with_capacity(vals.len()); - for e in vals { + let mut v = Vec::with_capacity(elems.len()); + for e in elems { match e { Value::MacAddress(x) => v.push(x), _ => return Err(ValueTypeErr), From 929dc476eb5731faaaf032b8874f1226cfd4bccb Mon Sep 17 00:00:00 2001 From: Huliiiiii <308013446a@gmail.com> Date: Tue, 28 Oct 2025 22:19:58 +0800 Subject: [PATCH 06/19] Rename --- src/backend/postgres/query.rs | 8 +- src/backend/query_builder.rs | 76 ++++++++-------- src/backend/value_encoder.rs | 164 +++++++++++++++++----------------- 3 files changed, 126 insertions(+), 122 deletions(-) diff --git a/src/backend/postgres/query.rs b/src/backend/postgres/query.rs index b6a5e85a3..74ac4d631 100644 --- a/src/backend/postgres/query.rs +++ b/src/backend/postgres/query.rs @@ -30,7 +30,7 @@ impl PrecedenceDecider for PostgresQueryBuilder { } impl ValueEncoder for PostgresQueryBuilder { - fn write_str(&self, buf: &mut impl Write, value: &str) { + fn write_str_to(&self, buf: &mut impl Write, value: &str) { if self.needs_escape(value) { buf.write_str("E'").unwrap(); } else { @@ -40,9 +40,9 @@ impl ValueEncoder for PostgresQueryBuilder { buf.write_str("'").unwrap(); } - fn write_enum(&self, buf: &mut impl Write, value: &crate::value::Enum) { + fn write_enum_to(&self, buf: &mut impl Write, value: &crate::value::Enum) { // Write the enum value as a quoted string - self.write_str(buf, value.value.as_str()); + self.write_str_to(buf, value.value.as_str()); // If a type name is provided, append type cast using ::Type if let Some(type_name) = &value.type_name { @@ -51,7 +51,7 @@ impl ValueEncoder for PostgresQueryBuilder { } } - fn write_bytes(&self, buffer: &mut impl Write, bytes: &[u8]) { + fn write_bytes_to(&self, buffer: &mut impl Write, bytes: &[u8]) { buffer.write_str("'\\x").unwrap(); for b in bytes { write!(buffer, "{b:02X}").unwrap(); diff --git a/src/backend/query_builder.rs b/src/backend/query_builder.rs index 46c6000fa..c2aa1bc39 100644 --- a/src/backend/query_builder.rs +++ b/src/backend/query_builder.rs @@ -1122,91 +1122,91 @@ pub trait QueryBuilder: } match value { - Value::Bool(v) => write_opt!(v, val => self.write_bool(buf, *val)), - Value::TinyInt(v) => write_opt!(v, val => self.write_i8(buf, *val)), - Value::SmallInt(v) => write_opt!(v, val => self.write_i16(buf, *val)), - Value::Int(v) => write_opt!(v, val => self.write_i32(buf, *val)), - Value::BigInt(v) => write_opt!(v, val => self.write_i64(buf, *val)), - Value::TinyUnsigned(v) => write_opt!(v, val => self.write_u8(buf, *val)), - Value::SmallUnsigned(v) => write_opt!(v, val => self.write_u16(buf, *val)), - Value::Unsigned(v) => write_opt!(v, val => self.write_u32(buf, *val)), - Value::BigUnsigned(v) => write_opt!(v, val => self.write_u64(buf, *val)), - Value::Float(v) => write_opt!(v, val => self.write_f32(buf, *val)), - Value::Double(v) => write_opt!(v, val => self.write_f64(buf, *val)), - Value::String(v) => write_opt!(v, val => self.write_str(buf, val)), - Value::Char(v) => write_opt!(v, val => self.write_char(buf, *val)), + Value::Bool(v) => write_opt!(v, val => self.write_bool_to(buf, *val)), + Value::TinyInt(v) => write_opt!(v, val => self.write_i8_to(buf, *val)), + Value::SmallInt(v) => write_opt!(v, val => self.write_i16_to(buf, *val)), + Value::Int(v) => write_opt!(v, val => self.write_i32_to(buf, *val)), + Value::BigInt(v) => write_opt!(v, val => self.write_i64_to(buf, *val)), + Value::TinyUnsigned(v) => write_opt!(v, val => self.write_u8_to(buf, *val)), + Value::SmallUnsigned(v) => write_opt!(v, val => self.write_u16_to(buf, *val)), + Value::Unsigned(v) => write_opt!(v, val => self.write_u32_to(buf, *val)), + Value::BigUnsigned(v) => write_opt!(v, val => self.write_u64_to(buf, *val)), + Value::Float(v) => write_opt!(v, val => self.write_f32_to(buf, *val)), + Value::Double(v) => write_opt!(v, val => self.write_f64_to(buf, *val)), + Value::String(v) => write_opt!(v, val => self.write_str_to(buf, val)), + Value::Char(v) => write_opt!(v, val => self.write_char_to(buf, *val)), #[cfg(feature = "backend-postgres")] - Value::Enum(v) => write_opt!(v, val => self.write_enum(buf, val.as_ref())), + Value::Enum(v) => write_opt!(v, val => self.write_enum_to(buf, val.as_ref())), Value::Bytes(v) => { - write_opt!(v, val => ValueEncoder::write_bytes(self, buf, val)) + write_opt!(v, val => ValueEncoder::write_bytes_to(self, buf, val)) } #[cfg(feature = "with-json")] - Value::Json(v) => write_opt!(v, val => self.write_json(buf, val)), + Value::Json(v) => write_opt!(v, val => self.write_json_to(buf, val)), #[cfg(feature = "with-chrono")] Value::ChronoDate(v) => { - write_opt!(v, val => self.write_naive_date(buf, val)) + write_opt!(v, val => self.write_naive_date_to(buf, val)) } #[cfg(feature = "with-chrono")] Value::ChronoTime(v) => { - write_opt!(v, val => self.write_naive_time(buf, val)) + write_opt!(v, val => self.write_naive_time_to(buf, val)) } #[cfg(feature = "with-chrono")] Value::ChronoDateTime(v) => { - write_opt!(v, val => self.write_naive_datetime(buf, val)) + write_opt!(v, val => self.write_naive_datetime_to(buf, val)) } #[cfg(feature = "with-chrono")] Value::ChronoDateTimeUtc(v) => { - write_opt!(v, val => self.write_datetime_utc(buf, val)) + write_opt!(v, val => self.write_datetime_utc_to(buf, val)) } #[cfg(feature = "with-chrono")] Value::ChronoDateTimeLocal(v) => write_opt!(v, val => self - .write_datetime_local(buf, val)), + .write_datetime_local_to(buf, val)), #[cfg(feature = "with-chrono")] Value::ChronoDateTimeWithTimeZone(v) => { - write_opt!(v, val => self.write_datetime_fixed(buf, val)) + write_opt!(v, val => self.write_datetime_fixed_to(buf, val)) } #[cfg(feature = "with-time")] - Value::TimeDate(v) => write_opt!(v, val => self.write_time_date(buf, val)), + Value::TimeDate(v) => write_opt!(v, val => self.write_time_date_to(buf, val)), #[cfg(feature = "with-time")] - Value::TimeTime(v) => write_opt!(v, val => self.write_time_time(buf, val)), + Value::TimeTime(v) => write_opt!(v, val => self.write_time_time_to(buf, val)), #[cfg(feature = "with-time")] - Value::TimeDateTime(v) => write_opt!(v, val => self.write_time_datetime(buf, val)), + Value::TimeDateTime(v) => write_opt!(v, val => self.write_time_datetime_to(buf, val)), #[cfg(feature = "with-time")] Value::TimeDateTimeWithTimeZone(v) => write_opt!(v, val => self - .write_time_datetime_tz(buf, val)), + .write_time_datetime_tz_to(buf, val)), #[cfg(feature = "with-jiff")] Value::JiffDate(v) => { - write_opt!(v, val => self.write_jiff_date(buf, val)) + write_opt!(v, val => self.write_jiff_date_to(buf, val)) } #[cfg(feature = "with-jiff")] Value::JiffTime(v) => { - write_opt!(v, val => self.write_jiff_time(buf, val)) + write_opt!(v, val => self.write_jiff_time_to(buf, val)) } #[cfg(feature = "with-jiff")] Value::JiffDateTime(v) => write_opt!(v, val => self - .write_jiff_datetime(buf, val)), + .write_jiff_datetime_to(buf, val)), #[cfg(feature = "with-jiff")] Value::JiffTimestamp(v) => { - write_opt!(v, val => self.write_jiff_timestamp(buf, val)) + write_opt!(v, val => self.write_jiff_timestamp_to(buf, val)) } #[cfg(feature = "with-jiff")] - Value::JiffZoned(v) => write_opt!(v, val => self.write_jiff_zoned(buf, val)), + Value::JiffZoned(v) => write_opt!(v, val => self.write_jiff_zoned_to(buf, val)), #[cfg(feature = "with-rust_decimal")] - Value::Decimal(v) => write_opt!(v, val => self.write_decimal(buf, val)), + Value::Decimal(v) => write_opt!(v, val => self.write_decimal_to(buf, val)), #[cfg(feature = "with-bigdecimal")] Value::BigDecimal(v) => { - write_opt!(v, val => self.write_bigdecimal(buf, val)) + write_opt!(v, val => self.write_bigdecimal_to(buf, val)) } #[cfg(feature = "with-uuid")] - Value::Uuid(v) => write_opt!(v, val => self.write_uuid(buf, val)), + Value::Uuid(v) => write_opt!(v, val => self.write_uuid_to(buf, val)), #[cfg(feature = "postgres-vector")] - Value::Vector(v) => write_opt!(v, val => self.write_vector(buf, val)), + Value::Vector(v) => write_opt!(v, val => self.write_vector_to(buf, val)), #[cfg(feature = "with-ipnetwork")] - Value::IpNetwork(v) => write_opt!(v, val => self.write_ipnetwork(buf, val)), + Value::IpNetwork(v) => write_opt!(v, val => self.write_ipnetwork_to(buf, val)), #[cfg(feature = "with-mac_address")] - Value::MacAddress(v) => write_opt!(v, val => self.write_mac(buf, val)), + Value::MacAddress(v) => write_opt!(v, val => self.write_mac_to(buf, val)), #[cfg(feature = "postgres-array")] - Value::Array(v) => write_opt!(v, val => self.write_array(buf, val)), + Value::Array(v) => write_opt!(v, val => self.write_array_to(buf, val)), } Ok(()) diff --git a/src/backend/value_encoder.rs b/src/backend/value_encoder.rs index 9f09ecfae..4dda09ebe 100644 --- a/src/backend/value_encoder.rs +++ b/src/backend/value_encoder.rs @@ -3,63 +3,63 @@ use std::fmt::Write; #[allow(unused_variables)] pub trait ValueEncoder: EscapeBuilder { - fn write_bool(&self, buf: &mut impl Write, value: bool) { + fn write_bool_to(&self, buf: &mut impl Write, value: bool) { buf.write_str(if value { "TRUE" } else { "FALSE" }).unwrap(); } - fn write_i8(&self, buf: &mut impl Write, value: i8) { + fn write_i8_to(&self, buf: &mut impl Write, value: i8) { write_int(buf, value); } - fn write_i16(&self, buf: &mut impl Write, value: i16) { + fn write_i16_to(&self, buf: &mut impl Write, value: i16) { write_int(buf, value); } - fn write_i32(&self, buf: &mut impl Write, value: i32) { + fn write_i32_to(&self, buf: &mut impl Write, value: i32) { write_int(buf, value); } - fn write_i64(&self, buf: &mut impl Write, value: i64) { + fn write_i64_to(&self, buf: &mut impl Write, value: i64) { write_int(buf, value); } - fn write_u8(&self, buf: &mut impl Write, value: u8) { + fn write_u8_to(&self, buf: &mut impl Write, value: u8) { write_int(buf, value); } - fn write_u16(&self, buf: &mut impl Write, value: u16) { + fn write_u16_to(&self, buf: &mut impl Write, value: u16) { write_int(buf, value); } - fn write_u32(&self, buf: &mut impl Write, value: u32) { + fn write_u32_to(&self, buf: &mut impl Write, value: u32) { write_int(buf, value); } - fn write_u64(&self, buf: &mut impl Write, value: u64) { + fn write_u64_to(&self, buf: &mut impl Write, value: u64) { write_int(buf, value); } - fn write_f32(&self, buf: &mut impl Write, value: f32) { + fn write_f32_to(&self, buf: &mut impl Write, value: f32) { write!(buf, "{value}").unwrap(); } - fn write_f64(&self, buf: &mut impl Write, value: f64) { + fn write_f64_to(&self, buf: &mut impl Write, value: f64) { write!(buf, "{value}").unwrap(); } - fn write_str(&self, buf: &mut impl Write, value: &str) { + fn write_str_to(&self, buf: &mut impl Write, value: &str) { buf.write_str("'").unwrap(); self.write_escaped(buf, value); buf.write_str("'").unwrap(); } - fn write_char(&self, buf: &mut impl Write, value: char) { + fn write_char_to(&self, buf: &mut impl Write, value: char) { let mut tmp = [0u8; 4]; let s = value.encode_utf8(&mut tmp); - self.write_str(buf, s); + self.write_str_to(buf, s); } - fn write_bytes(&self, buf: &mut impl Write, value: &[u8]) { + fn write_bytes_to(&self, buf: &mut impl Write, value: &[u8]) { buf.write_str("x'").unwrap(); for b in value { write!(buf, "{b:02X}").unwrap() @@ -68,33 +68,33 @@ pub trait ValueEncoder: EscapeBuilder { } #[cfg(feature = "with-json")] - fn write_json(&self, buf: &mut impl Write, value: &serde_json::Value) { - self.write_str(buf, &value.to_string()); + fn write_json_to(&self, buf: &mut impl Write, value: &serde_json::Value) { + self.write_str_to(buf, &value.to_string()); } #[cfg(feature = "with-chrono")] - fn write_naive_date(&self, buf: &mut impl Write, value: &chrono::NaiveDate) { + fn write_naive_date_to(&self, buf: &mut impl Write, value: chrono::NaiveDate) { buf.write_str("'").unwrap(); value.format("%Y-%m-%d").write_to(buf).unwrap(); buf.write_str("'").unwrap(); } #[cfg(feature = "with-chrono")] - fn write_naive_time(&self, buf: &mut impl Write, value: &chrono::NaiveTime) { + fn write_naive_time_to(&self, buf: &mut impl Write, value: chrono::NaiveTime) { buf.write_str("'").unwrap(); value.format("%H:%M:%S%.6f").write_to(buf).unwrap(); buf.write_str("'").unwrap(); } #[cfg(feature = "with-chrono")] - fn write_naive_datetime(&self, buf: &mut impl Write, value: &chrono::NaiveDateTime) { + fn write_naive_datetime_to(&self, buf: &mut impl Write, value: &chrono::NaiveDateTime) { buf.write_str("'").unwrap(); value.format("%Y-%m-%d %H:%M:%S%.6f").write_to(buf).unwrap(); buf.write_str("'").unwrap(); } #[cfg(feature = "with-chrono")] - fn write_datetime_utc(&self, buf: &mut impl Write, value: &chrono::DateTime) { + fn write_datetime_utc_to(&self, buf: &mut impl Write, value: &chrono::DateTime) { buf.write_str("'").unwrap(); value .format("%Y-%m-%d %H:%M:%S%.6f %:z") @@ -104,7 +104,11 @@ pub trait ValueEncoder: EscapeBuilder { } #[cfg(feature = "with-chrono")] - fn write_datetime_local(&self, buf: &mut impl Write, value: &chrono::DateTime) { + fn write_datetime_local_to( + &self, + buf: &mut impl Write, + value: &chrono::DateTime, + ) { buf.write_str("'").unwrap(); value .format("%Y-%m-%d %H:%M:%S%.6f %:z") @@ -114,7 +118,7 @@ pub trait ValueEncoder: EscapeBuilder { } #[cfg(feature = "with-chrono")] - fn write_datetime_fixed( + fn write_datetime_fixed_to( &self, buf: &mut impl Write, value: &chrono::DateTime, @@ -132,7 +136,7 @@ pub trait ValueEncoder: EscapeBuilder { // So this solution must allocate a temporary String // Fix it when the issue is resolved #[cfg(feature = "with-time")] - fn write_time_date(&self, buf: &mut impl Write, value: &time::Date) { + fn write_time_date_to(&self, buf: &mut impl Write, value: &time::Date) { buf.write_str("'").unwrap(); let s = value .format(crate::value::time_format::FORMAT_DATE) @@ -142,7 +146,7 @@ pub trait ValueEncoder: EscapeBuilder { } #[cfg(feature = "with-time")] - fn write_time_time(&self, buf: &mut impl Write, value: &time::Time) { + fn write_time_time_to(&self, buf: &mut impl Write, value: &time::Time) { buf.write_str("'").unwrap(); let s = value .format(crate::value::time_format::FORMAT_TIME) @@ -152,7 +156,7 @@ pub trait ValueEncoder: EscapeBuilder { } #[cfg(feature = "with-time")] - fn write_time_datetime(&self, buf: &mut impl Write, value: &time::PrimitiveDateTime) { + fn write_time_datetime_to(&self, buf: &mut impl Write, value: &time::PrimitiveDateTime) { buf.write_str("'").unwrap(); let s = value .format(crate::value::time_format::FORMAT_DATETIME) @@ -162,7 +166,7 @@ pub trait ValueEncoder: EscapeBuilder { } #[cfg(feature = "with-time")] - fn write_time_datetime_tz(&self, buf: &mut impl Write, value: &time::OffsetDateTime) { + fn write_time_datetime_tz_to(&self, buf: &mut impl Write, value: &time::OffsetDateTime) { buf.write_str("'").unwrap(); let s = value .format(crate::value::time_format::FORMAT_DATETIME_TZ) @@ -172,21 +176,21 @@ pub trait ValueEncoder: EscapeBuilder { } #[cfg(feature = "with-jiff")] - fn write_jiff_date(&self, buf: &mut impl Write, value: &jiff::civil::Date) { + fn write_jiff_date_to(&self, buf: &mut impl Write, value: &jiff::civil::Date) { buf.write_str("'").unwrap(); write!(buf, "{value}").unwrap(); buf.write_str("'").unwrap(); } #[cfg(feature = "with-jiff")] - fn write_jiff_time(&self, buf: &mut impl Write, value: &jiff::civil::Time) { + fn write_jiff_time_to(&self, buf: &mut impl Write, value: &jiff::civil::Time) { buf.write_str("'").unwrap(); write!(buf, "{value}").unwrap(); buf.write_str("'").unwrap(); } #[cfg(feature = "with-jiff")] - fn write_jiff_datetime(&self, buf: &mut impl Write, value: &jiff::civil::DateTime) { + fn write_jiff_datetime_to(&self, buf: &mut impl Write, value: &jiff::civil::DateTime) { use crate::value::with_jiff::JIFF_DATE_TIME_FMT_STR; buf.write_str("'").unwrap(); write!(buf, "{}", value.strftime(JIFF_DATE_TIME_FMT_STR)).unwrap(); @@ -194,7 +198,7 @@ pub trait ValueEncoder: EscapeBuilder { } #[cfg(feature = "with-jiff")] - fn write_jiff_timestamp(&self, buf: &mut impl Write, value: &jiff::Timestamp) { + fn write_jiff_timestamp_to(&self, buf: &mut impl Write, value: &jiff::Timestamp) { use crate::value::with_jiff::JIFF_TIMESTAMP_FMT_STR; buf.write_str("'").unwrap(); write!(buf, "{}", value.strftime(JIFF_TIMESTAMP_FMT_STR)).unwrap(); @@ -202,7 +206,7 @@ pub trait ValueEncoder: EscapeBuilder { } #[cfg(feature = "with-jiff")] - fn write_jiff_zoned(&self, buf: &mut impl Write, value: &jiff::Zoned) { + fn write_jiff_zoned_to(&self, buf: &mut impl Write, value: &jiff::Zoned) { use crate::value::with_jiff::JIFF_ZONE_FMT_STR; buf.write_str("'").unwrap(); write!(buf, "{}", value.strftime(JIFF_ZONE_FMT_STR)).unwrap(); @@ -210,7 +214,7 @@ pub trait ValueEncoder: EscapeBuilder { } #[cfg(feature = "postgres-vector")] - fn write_vector(&self, buf: &mut impl Write, value: &pgvector::Vector) { + fn write_vector_to(&self, buf: &mut impl Write, value: &pgvector::Vector) { buf.write_str("'[").unwrap(); let mut iter = value.as_slice().iter(); if let Some(first) = iter.next() { @@ -224,37 +228,37 @@ pub trait ValueEncoder: EscapeBuilder { } #[cfg(feature = "with-rust_decimal")] - fn write_decimal(&self, buf: &mut impl Write, value: &rust_decimal::Decimal) { + fn write_decimal_to(&self, buf: &mut impl Write, value: &rust_decimal::Decimal) { write!(buf, "{value}").unwrap(); } #[cfg(feature = "with-bigdecimal")] - fn write_bigdecimal(&self, buf: &mut impl Write, value: &bigdecimal::BigDecimal) { + fn write_bigdecimal_to(&self, buf: &mut impl Write, value: &bigdecimal::BigDecimal) { write!(buf, "{value}").unwrap(); } #[cfg(feature = "with-uuid")] - fn write_uuid(&self, buf: &mut impl Write, value: &uuid::Uuid) { - self.write_str(buf, &value.to_string()); + fn write_uuid_to(&self, buf: &mut impl Write, value: &uuid::Uuid) { + self.write_str_to(buf, &value.to_string()); } #[cfg(feature = "with-ipnetwork")] - fn write_ipnetwork(&self, buf: &mut impl Write, value: &ipnetwork::IpNetwork) { - self.write_str(buf, &value.to_string()); + fn write_ipnetwork_to(&self, buf: &mut impl Write, value: &ipnetwork::IpNetwork) { + self.write_str_to(buf, &value.to_string()); } #[cfg(feature = "with-mac_address")] - fn write_mac(&self, buf: &mut impl Write, value: &mac_address::MacAddress) { - self.write_str(buf, &value.to_string()); + fn write_mac_to(&self, buf: &mut impl Write, value: &mac_address::MacAddress) { + self.write_str_to(buf, &value.to_string()); } #[cfg(feature = "backend-postgres")] - fn write_enum(&self, buf: &mut impl Write, value: &crate::value::Enum) { - self.write_str(buf, value.value.as_str()); + fn write_enum_to(&self, buf: &mut impl Write, value: &crate::value::Enum) { + self.write_str_to(buf, value.value.as_str()); } #[cfg(feature = "postgres-array")] - fn write_array(&self, buf: &mut impl Write, array: &crate::value::Array) { + fn write_array_to(&self, buf: &mut impl Write, array: &crate::value::Array) { use std::fmt; use crate::value::Array; @@ -300,78 +304,78 @@ pub trait ValueEncoder: EscapeBuilder { match array { Array::Bool(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_bool(buf, *val) + encoder.write_bool_to(buf, *val) }) } Array::TinyInt(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_i8(buf, *val) + encoder.write_i8_to(buf, *val) }) } Array::SmallInt(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_i16(buf, *val) + encoder.write_i16_to(buf, *val) }) } Array::Int(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_i32(buf, *val) + encoder.write_i32_to(buf, *val) }) } Array::BigInt(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_i64(buf, *val) + encoder.write_i64_to(buf, *val) }) } Array::TinyUnsigned(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_u8(buf, *val) + encoder.write_u8_to(buf, *val) }) } Array::SmallUnsigned(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_u16(buf, *val) + encoder.write_u16_to(buf, *val) }) } Array::Unsigned(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_u32(buf, *val) + encoder.write_u32_to(buf, *val) }) } Array::BigUnsigned(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_u64(buf, *val) + encoder.write_u64_to(buf, *val) }) } Array::Float(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_f32(buf, *val) + encoder.write_f32_to(buf, *val) }) } Array::Double(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_f64(buf, *val) + encoder.write_f64_to(buf, *val) }) } Array::String(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_str(buf, val) + encoder.write_str_to(buf, val) }) } Array::Char(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_char(buf, *val) + encoder.write_char_to(buf, *val) }) } Array::Bytes(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_bytes(buf, val) + encoder.write_bytes_to(buf, val) }) } #[cfg(feature = "backend-postgres")] Array::Enum(boxed) => { write_array_values(encoder, buf, &boxed.as_ref().1, |encoder, buf, val| { - encoder.write_enum(buf, val.as_ref()) + encoder.write_enum_to(buf, val.as_ref()) }) } Array::Array(boxed) => { @@ -391,127 +395,127 @@ pub trait ValueEncoder: EscapeBuilder { #[cfg(feature = "with-json")] Array::Json(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_json(buf, val) + encoder.write_json_to(buf, val) }) } #[cfg(feature = "with-chrono")] Array::ChronoDate(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_naive_date(buf, val) + encoder.write_naive_date_to(buf, val) }) } #[cfg(feature = "with-chrono")] Array::ChronoTime(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_naive_time(buf, val) + encoder.write_naive_time_to(buf, val) }) } #[cfg(feature = "with-chrono")] Array::ChronoDateTime(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_naive_datetime(buf, val) + encoder.write_naive_datetime_to(buf, val) }) } #[cfg(feature = "with-chrono")] Array::ChronoDateTimeUtc(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_datetime_utc(buf, val) + encoder.write_datetime_utc_to(buf, val) }) } #[cfg(feature = "with-chrono")] Array::ChronoDateTimeLocal(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_datetime_local(buf, val) + encoder.write_datetime_local_to(buf, val) }) } #[cfg(feature = "with-chrono")] Array::ChronoDateTimeWithTimeZone(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_datetime_fixed(buf, val) + encoder.write_datetime_fixed_to(buf, val) }) } #[cfg(feature = "with-time")] Array::TimeDate(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_time_date(buf, val) + encoder.write_time_date_to(buf, val) }) } #[cfg(feature = "with-time")] Array::TimeTime(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_time_time(buf, val) + encoder.write_time_time_to(buf, val) }) } #[cfg(feature = "with-time")] Array::TimeDateTime(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_time_datetime(buf, val) + encoder.write_time_datetime_to(buf, val) }) } #[cfg(feature = "with-time")] Array::TimeDateTimeWithTimeZone(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_time_datetime_tz(buf, val) + encoder.write_time_datetime_tz_to(buf, val) }) } #[cfg(feature = "with-jiff")] Array::JiffDate(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_jiff_date(buf, val) + encoder.write_jiff_date_to(buf, val) }) } #[cfg(feature = "with-jiff")] Array::JiffTime(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_jiff_time(buf, val) + encoder.write_jiff_time_to(buf, val) }) } #[cfg(feature = "with-jiff")] Array::JiffDateTime(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_jiff_datetime(buf, val) + encoder.write_jiff_datetime_to(buf, val) }) } #[cfg(feature = "with-jiff")] Array::JiffTimestamp(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_jiff_timestamp(buf, val) + encoder.write_jiff_timestamp_to(buf, val) }) } #[cfg(feature = "with-jiff")] Array::JiffZoned(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_jiff_zoned(buf, val) + encoder.write_jiff_zoned_to(buf, val) }) } #[cfg(feature = "with-uuid")] Array::Uuid(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_uuid(buf, val) + encoder.write_uuid_to(buf, val) }) } #[cfg(feature = "with-rust_decimal")] Array::Decimal(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_decimal(buf, val) + encoder.write_decimal_to(buf, val) }) } #[cfg(feature = "with-bigdecimal")] Array::BigDecimal(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_bigdecimal(buf, val) + encoder.write_bigdecimal_to(buf, val) }) } #[cfg(feature = "with-ipnetwork")] Array::IpNetwork(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_ipnetwork(buf, val) + encoder.write_ipnetwork_to(buf, val) }) } #[cfg(feature = "with-mac_address")] Array::MacAddress(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_mac(buf, val) + encoder.write_mac_to(buf, val) }) } } From 582d676a0b63ff54689ba711eb439ea7f2f6d919 Mon Sep 17 00:00:00 2001 From: Huliiiiii <308013446a@gmail.com> Date: Tue, 28 Oct 2025 23:45:54 +0800 Subject: [PATCH 07/19] Pass value instead of reference --- src/backend/query_builder.rs | 30 +++++++++---------- src/backend/value_encoder.rs | 56 ++++++++++++++++++------------------ 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/backend/query_builder.rs b/src/backend/query_builder.rs index c2aa1bc39..f7b96294c 100644 --- a/src/backend/query_builder.rs +++ b/src/backend/query_builder.rs @@ -1144,15 +1144,15 @@ pub trait QueryBuilder: Value::Json(v) => write_opt!(v, val => self.write_json_to(buf, val)), #[cfg(feature = "with-chrono")] Value::ChronoDate(v) => { - write_opt!(v, val => self.write_naive_date_to(buf, val)) + write_opt!(v, val => self.write_naive_date_to(buf, *val)) } #[cfg(feature = "with-chrono")] Value::ChronoTime(v) => { - write_opt!(v, val => self.write_naive_time_to(buf, val)) + write_opt!(v, val => self.write_naive_time_to(buf, *val)) } #[cfg(feature = "with-chrono")] Value::ChronoDateTime(v) => { - write_opt!(v, val => self.write_naive_datetime_to(buf, val)) + write_opt!(v, val => self.write_naive_datetime_to(buf, *val)) } #[cfg(feature = "with-chrono")] Value::ChronoDateTimeUtc(v) => { @@ -1166,45 +1166,45 @@ pub trait QueryBuilder: write_opt!(v, val => self.write_datetime_fixed_to(buf, val)) } #[cfg(feature = "with-time")] - Value::TimeDate(v) => write_opt!(v, val => self.write_time_date_to(buf, val)), + Value::TimeDate(v) => write_opt!(v, val => self.write_time_date_to(buf, *val)), #[cfg(feature = "with-time")] - Value::TimeTime(v) => write_opt!(v, val => self.write_time_time_to(buf, val)), + Value::TimeTime(v) => write_opt!(v, val => self.write_time_time_to(buf, *val)), #[cfg(feature = "with-time")] - Value::TimeDateTime(v) => write_opt!(v, val => self.write_time_datetime_to(buf, val)), + Value::TimeDateTime(v) => write_opt!(v, val => self.write_time_datetime_to(buf, *val)), #[cfg(feature = "with-time")] Value::TimeDateTimeWithTimeZone(v) => write_opt!(v, val => self - .write_time_datetime_tz_to(buf, val)), + .write_time_datetime_tz_to(buf, *val)), #[cfg(feature = "with-jiff")] Value::JiffDate(v) => { - write_opt!(v, val => self.write_jiff_date_to(buf, val)) + write_opt!(v, val => self.write_jiff_date_to(buf, *val)) } #[cfg(feature = "with-jiff")] Value::JiffTime(v) => { - write_opt!(v, val => self.write_jiff_time_to(buf, val)) + write_opt!(v, val => self.write_jiff_time_to(buf, *val)) } #[cfg(feature = "with-jiff")] Value::JiffDateTime(v) => write_opt!(v, val => self - .write_jiff_datetime_to(buf, val)), + .write_jiff_datetime_to(buf, *val)), #[cfg(feature = "with-jiff")] Value::JiffTimestamp(v) => { - write_opt!(v, val => self.write_jiff_timestamp_to(buf, val)) + write_opt!(v, val => self.write_jiff_timestamp_to(buf, *val)) } #[cfg(feature = "with-jiff")] Value::JiffZoned(v) => write_opt!(v, val => self.write_jiff_zoned_to(buf, val)), #[cfg(feature = "with-rust_decimal")] - Value::Decimal(v) => write_opt!(v, val => self.write_decimal_to(buf, val)), + Value::Decimal(v) => write_opt!(v, val => self.write_decimal_to(buf, *val)), #[cfg(feature = "with-bigdecimal")] Value::BigDecimal(v) => { write_opt!(v, val => self.write_bigdecimal_to(buf, val)) } #[cfg(feature = "with-uuid")] - Value::Uuid(v) => write_opt!(v, val => self.write_uuid_to(buf, val)), + Value::Uuid(v) => write_opt!(v, val => self.write_uuid_to(buf, *val)), #[cfg(feature = "postgres-vector")] Value::Vector(v) => write_opt!(v, val => self.write_vector_to(buf, val)), #[cfg(feature = "with-ipnetwork")] - Value::IpNetwork(v) => write_opt!(v, val => self.write_ipnetwork_to(buf, val)), + Value::IpNetwork(v) => write_opt!(v, val => self.write_ipnetwork_to(buf, *val)), #[cfg(feature = "with-mac_address")] - Value::MacAddress(v) => write_opt!(v, val => self.write_mac_to(buf, val)), + Value::MacAddress(v) => write_opt!(v, val => self.write_mac_to(buf, *val)), #[cfg(feature = "postgres-array")] Value::Array(v) => write_opt!(v, val => self.write_array_to(buf, val)), } diff --git a/src/backend/value_encoder.rs b/src/backend/value_encoder.rs index 4dda09ebe..9f3caf100 100644 --- a/src/backend/value_encoder.rs +++ b/src/backend/value_encoder.rs @@ -87,7 +87,7 @@ pub trait ValueEncoder: EscapeBuilder { } #[cfg(feature = "with-chrono")] - fn write_naive_datetime_to(&self, buf: &mut impl Write, value: &chrono::NaiveDateTime) { + fn write_naive_datetime_to(&self, buf: &mut impl Write, value: chrono::NaiveDateTime) { buf.write_str("'").unwrap(); value.format("%Y-%m-%d %H:%M:%S%.6f").write_to(buf).unwrap(); buf.write_str("'").unwrap(); @@ -136,7 +136,7 @@ pub trait ValueEncoder: EscapeBuilder { // So this solution must allocate a temporary String // Fix it when the issue is resolved #[cfg(feature = "with-time")] - fn write_time_date_to(&self, buf: &mut impl Write, value: &time::Date) { + fn write_time_date_to(&self, buf: &mut impl Write, value: time::Date) { buf.write_str("'").unwrap(); let s = value .format(crate::value::time_format::FORMAT_DATE) @@ -146,7 +146,7 @@ pub trait ValueEncoder: EscapeBuilder { } #[cfg(feature = "with-time")] - fn write_time_time_to(&self, buf: &mut impl Write, value: &time::Time) { + fn write_time_time_to(&self, buf: &mut impl Write, value: time::Time) { buf.write_str("'").unwrap(); let s = value .format(crate::value::time_format::FORMAT_TIME) @@ -156,7 +156,7 @@ pub trait ValueEncoder: EscapeBuilder { } #[cfg(feature = "with-time")] - fn write_time_datetime_to(&self, buf: &mut impl Write, value: &time::PrimitiveDateTime) { + fn write_time_datetime_to(&self, buf: &mut impl Write, value: time::PrimitiveDateTime) { buf.write_str("'").unwrap(); let s = value .format(crate::value::time_format::FORMAT_DATETIME) @@ -166,7 +166,7 @@ pub trait ValueEncoder: EscapeBuilder { } #[cfg(feature = "with-time")] - fn write_time_datetime_tz_to(&self, buf: &mut impl Write, value: &time::OffsetDateTime) { + fn write_time_datetime_tz_to(&self, buf: &mut impl Write, value: time::OffsetDateTime) { buf.write_str("'").unwrap(); let s = value .format(crate::value::time_format::FORMAT_DATETIME_TZ) @@ -176,21 +176,21 @@ pub trait ValueEncoder: EscapeBuilder { } #[cfg(feature = "with-jiff")] - fn write_jiff_date_to(&self, buf: &mut impl Write, value: &jiff::civil::Date) { + fn write_jiff_date_to(&self, buf: &mut impl Write, value: jiff::civil::Date) { buf.write_str("'").unwrap(); write!(buf, "{value}").unwrap(); buf.write_str("'").unwrap(); } #[cfg(feature = "with-jiff")] - fn write_jiff_time_to(&self, buf: &mut impl Write, value: &jiff::civil::Time) { + fn write_jiff_time_to(&self, buf: &mut impl Write, value: jiff::civil::Time) { buf.write_str("'").unwrap(); write!(buf, "{value}").unwrap(); buf.write_str("'").unwrap(); } #[cfg(feature = "with-jiff")] - fn write_jiff_datetime_to(&self, buf: &mut impl Write, value: &jiff::civil::DateTime) { + fn write_jiff_datetime_to(&self, buf: &mut impl Write, value: jiff::civil::DateTime) { use crate::value::with_jiff::JIFF_DATE_TIME_FMT_STR; buf.write_str("'").unwrap(); write!(buf, "{}", value.strftime(JIFF_DATE_TIME_FMT_STR)).unwrap(); @@ -198,7 +198,7 @@ pub trait ValueEncoder: EscapeBuilder { } #[cfg(feature = "with-jiff")] - fn write_jiff_timestamp_to(&self, buf: &mut impl Write, value: &jiff::Timestamp) { + fn write_jiff_timestamp_to(&self, buf: &mut impl Write, value: jiff::Timestamp) { use crate::value::with_jiff::JIFF_TIMESTAMP_FMT_STR; buf.write_str("'").unwrap(); write!(buf, "{}", value.strftime(JIFF_TIMESTAMP_FMT_STR)).unwrap(); @@ -228,7 +228,7 @@ pub trait ValueEncoder: EscapeBuilder { } #[cfg(feature = "with-rust_decimal")] - fn write_decimal_to(&self, buf: &mut impl Write, value: &rust_decimal::Decimal) { + fn write_decimal_to(&self, buf: &mut impl Write, value: rust_decimal::Decimal) { write!(buf, "{value}").unwrap(); } @@ -238,17 +238,17 @@ pub trait ValueEncoder: EscapeBuilder { } #[cfg(feature = "with-uuid")] - fn write_uuid_to(&self, buf: &mut impl Write, value: &uuid::Uuid) { + fn write_uuid_to(&self, buf: &mut impl Write, value: uuid::Uuid) { self.write_str_to(buf, &value.to_string()); } #[cfg(feature = "with-ipnetwork")] - fn write_ipnetwork_to(&self, buf: &mut impl Write, value: &ipnetwork::IpNetwork) { + fn write_ipnetwork_to(&self, buf: &mut impl Write, value: ipnetwork::IpNetwork) { self.write_str_to(buf, &value.to_string()); } #[cfg(feature = "with-mac_address")] - fn write_mac_to(&self, buf: &mut impl Write, value: &mac_address::MacAddress) { + fn write_mac_to(&self, buf: &mut impl Write, value: mac_address::MacAddress) { self.write_str_to(buf, &value.to_string()); } @@ -401,19 +401,19 @@ pub trait ValueEncoder: EscapeBuilder { #[cfg(feature = "with-chrono")] Array::ChronoDate(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_naive_date_to(buf, val) + encoder.write_naive_date_to(buf, *val) }) } #[cfg(feature = "with-chrono")] Array::ChronoTime(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_naive_time_to(buf, val) + encoder.write_naive_time_to(buf, *val) }) } #[cfg(feature = "with-chrono")] Array::ChronoDateTime(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_naive_datetime_to(buf, val) + encoder.write_naive_datetime_to(buf, *val) }) } #[cfg(feature = "with-chrono")] @@ -437,49 +437,49 @@ pub trait ValueEncoder: EscapeBuilder { #[cfg(feature = "with-time")] Array::TimeDate(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_time_date_to(buf, val) + encoder.write_time_date_to(buf, *val) }) } #[cfg(feature = "with-time")] Array::TimeTime(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_time_time_to(buf, val) + encoder.write_time_time_to(buf, *val) }) } #[cfg(feature = "with-time")] Array::TimeDateTime(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_time_datetime_to(buf, val) + encoder.write_time_datetime_to(buf, *val) }) } #[cfg(feature = "with-time")] Array::TimeDateTimeWithTimeZone(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_time_datetime_tz_to(buf, val) + encoder.write_time_datetime_tz_to(buf, *val) }) } #[cfg(feature = "with-jiff")] Array::JiffDate(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_jiff_date_to(buf, val) + encoder.write_jiff_date_to(buf, *val) }) } #[cfg(feature = "with-jiff")] Array::JiffTime(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_jiff_time_to(buf, val) + encoder.write_jiff_time_to(buf, *val) }) } #[cfg(feature = "with-jiff")] Array::JiffDateTime(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_jiff_datetime_to(buf, val) + encoder.write_jiff_datetime_to(buf, *val) }) } #[cfg(feature = "with-jiff")] Array::JiffTimestamp(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_jiff_timestamp_to(buf, val) + encoder.write_jiff_timestamp_to(buf, *val) }) } #[cfg(feature = "with-jiff")] @@ -491,13 +491,13 @@ pub trait ValueEncoder: EscapeBuilder { #[cfg(feature = "with-uuid")] Array::Uuid(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_uuid_to(buf, val) + encoder.write_uuid_to(buf, *val) }) } #[cfg(feature = "with-rust_decimal")] Array::Decimal(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_decimal_to(buf, val) + encoder.write_decimal_to(buf, *val) }) } #[cfg(feature = "with-bigdecimal")] @@ -509,13 +509,13 @@ pub trait ValueEncoder: EscapeBuilder { #[cfg(feature = "with-ipnetwork")] Array::IpNetwork(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_ipnetwork_to(buf, val) + encoder.write_ipnetwork_to(buf, *val) }) } #[cfg(feature = "with-mac_address")] Array::MacAddress(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { - encoder.write_mac_to(buf, val) + encoder.write_mac_to(buf, *val) }) } } From 33a8ef8657caca244c71063100714a2bebebedc2 Mon Sep 17 00:00:00 2001 From: Huliiiiii <308013446a@gmail.com> Date: Tue, 28 Oct 2025 23:52:18 +0800 Subject: [PATCH 08/19] Tweak --- src/backend/value_encoder.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/backend/value_encoder.rs b/src/backend/value_encoder.rs index 9f3caf100..4db3573c1 100644 --- a/src/backend/value_encoder.rs +++ b/src/backend/value_encoder.rs @@ -239,17 +239,23 @@ pub trait ValueEncoder: EscapeBuilder { #[cfg(feature = "with-uuid")] fn write_uuid_to(&self, buf: &mut impl Write, value: uuid::Uuid) { - self.write_str_to(buf, &value.to_string()); + buf.write_str("'").unwrap(); + write!(buf, "{value}").unwrap(); + buf.write_str("'").unwrap(); } #[cfg(feature = "with-ipnetwork")] fn write_ipnetwork_to(&self, buf: &mut impl Write, value: ipnetwork::IpNetwork) { - self.write_str_to(buf, &value.to_string()); + buf.write_str("'").unwrap(); + write!(buf, "{value}").unwrap(); + buf.write_str("'").unwrap(); } #[cfg(feature = "with-mac_address")] fn write_mac_to(&self, buf: &mut impl Write, value: mac_address::MacAddress) { - self.write_str_to(buf, &value.to_string()); + buf.write_str("'").unwrap(); + write!(buf, "{value}").unwrap(); + buf.write_str("'").unwrap(); } #[cfg(feature = "backend-postgres")] From 4ec2b45a18792ce69022d07422b9184681a847ac Mon Sep 17 00:00:00 2001 From: Huliiiiii <308013446a@gmail.com> Date: Wed, 29 Oct 2025 00:23:46 +0800 Subject: [PATCH 09/19] Refactor --- src/utils.rs | 1 + src/value/array.rs | 274 ++++++++++++++++++----------------------- src/value/with_json.rs | 2 +- 3 files changed, 124 insertions(+), 153 deletions(-) diff --git a/src/utils.rs b/src/utils.rs index 02d3abfd8..94c545f18 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,5 +1,6 @@ use std::fmt; +// TODO: replace join_io with this function // Make write a sperater a bit faster pub(crate) fn join_write( buf: &mut B, diff --git a/src/value/array.rs b/src/value/array.rs index 8403cccad..c5904cc63 100644 --- a/src/value/array.rs +++ b/src/value/array.rs @@ -1,4 +1,5 @@ use super::*; +use crate::backend::ValueEncoder; #[derive(Debug, Clone)] #[cfg_attr(not(feature = "hashable-value"), derive(PartialEq))] @@ -213,191 +214,160 @@ impl Array { } } - // TODO: optimize performance to avoid intermediate Value allocations - pub fn to_json_values(&self) -> Vec { + #[cfg(feature = "with-json")] + pub(crate) fn to_json_value(&self) -> Json { + fn map_slice_of_opts(slice: &[Option], mut f: F) -> Json + where + F: FnMut(&T) -> Json, + { + slice + .iter() + .map(|o| match o.as_ref() { + Some(v) => f(v), + None => Json::Null, + }) + .collect() + } + + fn encode_to_string(f: F) -> String + where + F: FnOnce(&CommonSqlQueryBuilder, &mut String), + { + let mut s = String::new(); + let enc = CommonSqlQueryBuilder; + f(&enc, &mut s); + s + } + match self { - Array::Bool(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::Bool(x.clone()))) - .collect(), - Array::TinyInt(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::TinyInt(x.clone()))) - .collect(), - Array::SmallInt(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::SmallInt(x.clone()))) - .collect(), - Array::Int(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::Int(x.clone()))) - .collect(), - Array::BigInt(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::BigInt(x.clone()))) - .collect(), - Array::TinyUnsigned(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::TinyUnsigned(x.clone()))) - .collect(), - Array::SmallUnsigned(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::SmallUnsigned(x.clone()))) - .collect(), - Array::Unsigned(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::Unsigned(x.clone()))) - .collect(), - Array::BigUnsigned(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::BigUnsigned(x.clone()))) - .collect(), - Array::Float(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::Float(x.clone()))) - .collect(), - Array::Double(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::Double(x.clone()))) - .collect(), - Array::String(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::String(x.clone()))) - .collect(), - Array::Char(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::Char(x.clone()))) - .collect(), - Array::Bytes(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::Bytes(x.clone()))) - .collect(), + Array::Bool(v) => map_slice_of_opts(v, |&b| Json::Bool(b)), + Array::TinyInt(v) => map_slice_of_opts(v, |&x| x.into()), + Array::SmallInt(v) => map_slice_of_opts(v, |&x| x.into()), + Array::Int(v) => map_slice_of_opts(v, |&x| x.into()), + Array::BigInt(v) => map_slice_of_opts(v, |&x| x.into()), + Array::TinyUnsigned(v) => map_slice_of_opts(v, |&x| x.into()), + Array::SmallUnsigned(v) => map_slice_of_opts(v, |&x| x.into()), + Array::Unsigned(v) => map_slice_of_opts(v, |&x| x.into()), + Array::BigUnsigned(v) => map_slice_of_opts(v, |&x| x.into()), + Array::Float(v) => map_slice_of_opts(v, |&x| x.into()), + Array::Double(v) => map_slice_of_opts(v, |&x| x.into()), + Array::String(v) => map_slice_of_opts(v, |s| Json::String(s.clone())), + Array::Char(v) => map_slice_of_opts(v, |&c| Json::String(c.to_string())), + Array::Bytes(v) => map_slice_of_opts(v, |bytes| { + Json::String(std::str::from_utf8(bytes).unwrap().to_string()) + }), #[cfg(feature = "backend-postgres")] Array::Enum(v) => { let (_, arr) = v.as_ref(); - arr.iter() - .map(|x| super::sea_value_to_json_value(&Value::Enum(x.clone()))) - .collect() + map_slice_of_opts(arr, |e| Json::String(e.value.to_string())) } Array::Array(v) => { - let (t, arrs) = v.as_ref(); - // Represent nested arrays as arrays of json values from inner arrays + let (_, arrs) = v.as_ref(); arrs.iter() .map(|opt_a| match opt_a { - Some(a) => Json::Array(a.to_json_values()), + Some(a) => a.to_json_value(), None => Json::Null, }) .collect() } #[cfg(feature = "with-json")] - Array::Json(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::Json(x.clone()))) - .collect(), + Array::Json(v) => map_slice_of_opts(v, |j| j.clone()), #[cfg(feature = "with-chrono")] - Array::ChronoDate(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::ChronoDate(x.clone()))) - .collect(), + Array::ChronoDate(v) => map_slice_of_opts(v, |&d| { + Json::String(encode_to_string(|enc, buf| enc.write_naive_date_to(buf, d))) + }), #[cfg(feature = "with-chrono")] - Array::ChronoTime(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::ChronoTime(x.clone()))) - .collect(), + Array::ChronoTime(v) => map_slice_of_opts(v, |&t| { + Json::String(encode_to_string(|enc, buf| enc.write_naive_time_to(buf, t))) + }), #[cfg(feature = "with-chrono")] - Array::ChronoDateTime(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::ChronoDateTime(x.clone()))) - .collect(), + Array::ChronoDateTime(v) => map_slice_of_opts(v, |&dt| { + Json::String(encode_to_string(|enc, buf| { + enc.write_naive_datetime_to(buf, dt) + })) + }), #[cfg(feature = "with-chrono")] - Array::ChronoDateTimeUtc(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::ChronoDateTimeUtc(x.clone()))) - .collect(), + Array::ChronoDateTimeUtc(v) => map_slice_of_opts(v, |dt| { + Json::String(encode_to_string(|enc, buf| { + enc.write_datetime_utc_to(buf, dt) + })) + }), #[cfg(feature = "with-chrono")] - Array::ChronoDateTimeLocal(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::ChronoDateTimeLocal(x.clone()))) - .collect(), + Array::ChronoDateTimeLocal(v) => map_slice_of_opts(v, |dt| { + Json::String(encode_to_string(|enc, buf| { + enc.write_datetime_local_to(buf, dt) + })) + }), #[cfg(feature = "with-chrono")] - Array::ChronoDateTimeWithTimeZone(v) => v - .iter() - .map(|x| { - super::sea_value_to_json_value(&Value::ChronoDateTimeWithTimeZone(x.clone())) - }) - .collect(), + Array::ChronoDateTimeWithTimeZone(v) => map_slice_of_opts(v, |dt| { + Json::String(encode_to_string(|enc, buf| { + enc.write_datetime_fixed_to(buf, dt) + })) + }), #[cfg(feature = "with-time")] - Array::TimeDate(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::TimeDate(x.clone()))) - .collect(), + Array::TimeDate(v) => map_slice_of_opts(v, |&d| { + Json::String(encode_to_string(|enc, buf| enc.write_time_date_to(buf, d))) + }), #[cfg(feature = "with-time")] - Array::TimeTime(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::TimeTime(x.clone()))) - .collect(), + Array::TimeTime(v) => map_slice_of_opts(v, |&t| { + Json::String(encode_to_string(|enc, buf| enc.write_time_time_to(buf, t))) + }), #[cfg(feature = "with-time")] - Array::TimeDateTime(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::TimeDateTime(x.clone()))) - .collect(), + Array::TimeDateTime(v) => map_slice_of_opts(v, |&dt| { + Json::String(encode_to_string(|enc, buf| { + enc.write_time_datetime_to(buf, dt) + })) + }), #[cfg(feature = "with-time")] - Array::TimeDateTimeWithTimeZone(v) => v - .iter() - .map(|x| { - super::sea_value_to_json_value(&Value::TimeDateTimeWithTimeZone(x.clone())) - }) - .collect(), + Array::TimeDateTimeWithTimeZone(v) => map_slice_of_opts(v, |&dt| { + Json::String(encode_to_string(|enc, buf| { + enc.write_time_datetime_tz_to(buf, dt) + })) + }), #[cfg(feature = "with-jiff")] - Array::JiffDate(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::JiffDate(x.clone()))) - .collect(), + Array::JiffDate(v) => map_slice_of_opts(v, |&d| { + Json::String(encode_to_string(|enc, buf| enc.write_jiff_date_to(buf, d))) + }), #[cfg(feature = "with-jiff")] - Array::JiffTime(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::JiffTime(x.clone()))) - .collect(), + Array::JiffTime(v) => map_slice_of_opts(v, |&t| { + Json::String(encode_to_string(|enc, buf| enc.write_jiff_time_to(buf, t))) + }), #[cfg(feature = "with-jiff")] - Array::JiffDateTime(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::JiffDateTime(x.clone()))) - .collect(), + Array::JiffDateTime(v) => map_slice_of_opts(v, |&dt| { + Json::String(encode_to_string(|enc, buf| { + enc.write_jiff_datetime_to(buf, dt) + })) + }), #[cfg(feature = "with-jiff")] - Array::JiffTimestamp(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::JiffTimestamp(x.clone()))) - .collect(), + Array::JiffTimestamp(v) => map_slice_of_opts(v, |&ts| { + Json::String(encode_to_string(|enc, buf| { + enc.write_jiff_timestamp_to(buf, ts) + })) + }), #[cfg(feature = "with-jiff")] - Array::JiffZoned(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::JiffZoned(x.clone()))) - .collect(), + Array::JiffZoned(v) => map_slice_of_opts(v, |z| { + Json::String(encode_to_string(|enc, buf| enc.write_jiff_zoned_to(buf, z))) + }), #[cfg(feature = "with-uuid")] - Array::Uuid(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::Uuid(x.clone()))) - .collect(), + Array::Uuid(v) => map_slice_of_opts(v, |&u| Json::String(u.to_string())), #[cfg(feature = "with-rust_decimal")] - Array::Decimal(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::Decimal(x.clone()))) - .collect(), + Array::Decimal(v) => map_slice_of_opts(v, |&d| { + use rust_decimal::prelude::ToPrimitive; + Json::Number(serde_json::Number::from_f64(d.to_f64().unwrap()).unwrap()) + }), #[cfg(feature = "with-bigdecimal")] - Array::BigDecimal(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::BigDecimal(x.clone()))) - .collect(), + Array::BigDecimal(v) => map_slice_of_opts(v, |bd| { + use bigdecimal::ToPrimitive; + Json::Number(serde_json::Number::from_f64(bd.to_f64().unwrap()).unwrap()) + }), #[cfg(feature = "with-ipnetwork")] - Array::IpNetwork(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::IpNetwork(x.clone()))) - .collect(), + Array::IpNetwork(v) => map_slice_of_opts(v, |&ip| { + Json::String(encode_to_string(|enc, buf| enc.write_ipnetwork_to(buf, ip))) + }), #[cfg(feature = "with-mac_address")] - Array::MacAddress(v) => v - .iter() - .map(|x| super::sea_value_to_json_value(&Value::MacAddress(x.clone()))) - .collect(), + Array::MacAddress(v) => map_slice_of_opts(v, |&mac| { + Json::String(encode_to_string(|enc, buf| enc.write_mac_to(buf, mac))) + }), } } diff --git a/src/value/with_json.rs b/src/value/with_json.rs index 7732144c3..e70a394e0 100644 --- a/src/value/with_json.rs +++ b/src/value/with_json.rs @@ -111,7 +111,7 @@ pub fn sea_value_to_json_value(value: &Value) -> Json { #[cfg(feature = "backend-postgres")] Value::Enum(Some(v)) => Json::String(v.value.to_string()), #[cfg(feature = "postgres-array")] - Value::Array(Some(v)) => Json::Array(v.to_json_values()), + Value::Array(Some(v)) => v.to_json_value(), #[cfg(feature = "postgres-vector")] Value::Vector(Some(v)) => Json::Array(v.as_slice().iter().map(|&v| v.into()).collect()), #[cfg(feature = "with-ipnetwork")] From b7abc213c1d28319ddc21442c8cd69fb7b410695 Mon Sep 17 00:00:00 2001 From: Huliiiiii <308013446a@gmail.com> Date: Wed, 29 Oct 2025 00:26:41 +0800 Subject: [PATCH 10/19] Fix --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index eb820317e..0c73b446b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,7 @@ path = "src/lib.rs" [dependencies] inherent = "1.0" -sea-query-derive = { version = "1.0.0-rc", path = "sea-query-derive", default-features = false, optional = true } +sea-query-derive = { version = "1.0.0-rc", path = "sea-query-derive", optional = true } serde = { version = "1", default-features = false, optional = true, features = ["std", "derive", "rc"] } serde_json = { version = "1", default-features = false, optional = true, features = ["std"] } chrono = { version = "0.4.27", default-features = false, optional = true, features = ["clock"] } From 087356c299c44e067d2d166613af7ed36683e457 Mon Sep 17 00:00:00 2001 From: Huliiiiii <308013446a@gmail.com> Date: Wed, 29 Oct 2025 01:45:22 +0800 Subject: [PATCH 11/19] Replace `ARRAY [` with `ARRAY[` --- src/backend/value_encoder.rs | 4 ++-- src/extension/postgres/expr.rs | 2 +- src/extension/postgres/func.rs | 6 +++--- src/value/with_array.rs | 2 +- tests/postgres/query.rs | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/backend/value_encoder.rs b/src/backend/value_encoder.rs index 4db3573c1..9f3d1b611 100644 --- a/src/backend/value_encoder.rs +++ b/src/backend/value_encoder.rs @@ -306,7 +306,7 @@ pub trait ValueEncoder: EscapeBuilder { return buf.write_str("'{}'"); } - buf.write_str("'ARRAY[")?; + buf.write_str("ARRAY[")?; match array { Array::Bool(items) => { write_array_values(encoder, buf, items, |encoder, buf, val| { @@ -526,7 +526,7 @@ pub trait ValueEncoder: EscapeBuilder { } } .unwrap(); - buf.write_str("]'") + buf.write_str("]") } write_array_recursive(self, buf, array).unwrap() diff --git a/src/extension/postgres/expr.rs b/src/extension/postgres/expr.rs index 9a1fcaa99..ac22ff57f 100644 --- a/src/extension/postgres/expr.rs +++ b/src/extension/postgres/expr.rs @@ -25,7 +25,7 @@ pub trait PgExpr: ExprTrait { /// /// assert_eq!( /// query.to_string(PostgresQueryBuilder), - /// r#"SELECT ARRAY ['a'] || ARRAY ['b']"# + /// r#"SELECT ARRAY['a'] || ARRAY['b']"# /// ); /// } /// ``` diff --git a/src/extension/postgres/func.rs b/src/extension/postgres/func.rs index a7768b172..7fca4b1f6 100644 --- a/src/extension/postgres/func.rs +++ b/src/extension/postgres/func.rs @@ -263,7 +263,7 @@ impl PgFunc { /// /// assert_eq!( /// query.to_string(PostgresQueryBuilder), - /// r#"SELECT ANY(ARRAY [0,1])"# + /// r#"SELECT ANY(ARRAY[0,1])"# /// ); /// ``` #[cfg(feature = "postgres-array")] @@ -285,7 +285,7 @@ impl PgFunc { /// /// assert_eq!( /// query.to_string(PostgresQueryBuilder), - /// r#"SELECT SOME(ARRAY [0,1])"# + /// r#"SELECT SOME(ARRAY[0,1])"# /// ); /// ``` #[cfg(feature = "postgres-array")] @@ -307,7 +307,7 @@ impl PgFunc { /// /// assert_eq!( /// query.to_string(PostgresQueryBuilder), - /// r#"SELECT ALL(ARRAY [0,1])"# + /// r#"SELECT ALL(ARRAY[0,1])"# /// ); /// ``` #[cfg(feature = "postgres-array")] diff --git a/src/value/with_array.rs b/src/value/with_array.rs index bf7709798..6902ba4f4 100644 --- a/src/value/with_array.rs +++ b/src/value/with_array.rs @@ -20,7 +20,7 @@ impl NotU8 for char {} impl NotU8 for String {} impl NotU8 for Vec {} -// TODO impl NotU8 for Option {} +impl NotU8 for Option {} #[cfg(feature = "with-json")] impl NotU8 for Json {} diff --git a/tests/postgres/query.rs b/tests/postgres/query.rs index 575530154..feda518fb 100644 --- a/tests/postgres/query.rs +++ b/tests/postgres/query.rs @@ -1443,7 +1443,7 @@ fn insert_10() { .into() ]) .to_string(PostgresQueryBuilder), - r#"INSERT INTO "glyph" ("aspect", "tokens") VALUES (3.1415, ARRAY ['Token1','Token2','Token3'])"# + r#"INSERT INTO "glyph" ("aspect", "tokens") VALUES (3.1415, ARRAY['Token1','Token2','Token3'])"# ); } From 6c2a59823146256642248ddd60301ca9d390977a Mon Sep 17 00:00:00 2001 From: Huliiiiii <308013446a@gmail.com> Date: Wed, 29 Oct 2025 06:38:39 +0800 Subject: [PATCH 12/19] Fix rust postgres binder --- sea-query-postgres/src/lib.rs | 121 ++++++++++++++++++++++++++++------ src/value/array.rs | 10 ++- 2 files changed, 111 insertions(+), 20 deletions(-) diff --git a/sea-query-postgres/src/lib.rs b/sea-query-postgres/src/lib.rs index a98660c6a..167a127bc 100644 --- a/sea-query-postgres/src/lib.rs +++ b/sea-query-postgres/src/lib.rs @@ -5,6 +5,8 @@ use std::error::Error; use bytes::BytesMut; use postgres_types::{IsNull, ToSql, Type, to_sql_checked}; +#[cfg(feature = "postgres-array")] +use sea_query::Array; use sea_query::{QueryBuilder, Value, query::*}; #[derive(Clone, Debug, PartialEq)] @@ -104,38 +106,107 @@ impl ToSql for PostgresValue { #[cfg(feature = "with-bigdecimal")] Value::BigDecimal(v) => { use bigdecimal::ToPrimitive; - v.as_deref() + v.as_ref() .map(|v| v.to_f64().expect("Fail to convert bigdecimal as f64")) .to_sql(ty, out) } #[cfg(feature = "with-uuid")] Value::Uuid(v) => v.to_sql(ty, out), #[cfg(feature = "postgres-array")] - Value::Array(_, Some(v)) => v - .iter() - .map(|v| PostgresValue(v.clone())) - .collect::>() - .to_sql(ty, out), + Value::Array(Some(arr)) => match arr { + Array::Bool(inner) => inner.to_sql(ty, out), + Array::TinyInt(inner) => inner.to_sql(ty, out), + Array::SmallInt(inner) => inner.to_sql(ty, out), + Array::Int(inner) => inner.to_sql(ty, out), + Array::BigInt(inner) => inner.to_sql(ty, out), + Array::TinyUnsigned(inner) => inner + .iter() + .map(|v| v.map(|x| x as u32)) + .collect::>>() + .to_sql(ty, out), + Array::SmallUnsigned(inner) => inner + .iter() + .map(|v| v.map(|x| x as u32)) + .collect::>>() + .to_sql(ty, out), + Array::Unsigned(inner) => inner.to_sql(ty, out), + Array::BigUnsigned(inner) => inner + .into_iter() + .map(|v| v.map(|x| x as i64)) + .collect::>>() + .to_sql(ty, out), + Array::Float(inner) => inner.to_sql(ty, out), + Array::Double(inner) => inner.to_sql(ty, out), + Array::String(inner) => inner.to_sql(ty, out), + Array::Char(inner) => inner + .into_iter() + .map(|v| v.map(|c| c.to_string())) + .collect::>>() + .to_sql(ty, out), + Array::Bytes(inner) => inner.to_sql(ty, out), + #[cfg(feature = "with-json")] + Array::Json(inner) => inner.to_sql(ty, out), + #[cfg(feature = "with-chrono")] + Array::ChronoDate(inner) => inner.to_sql(ty, out), + #[cfg(feature = "with-chrono")] + Array::ChronoTime(inner) => inner.to_sql(ty, out), + #[cfg(feature = "with-chrono")] + Array::ChronoDateTime(inner) => inner.to_sql(ty, out), + #[cfg(feature = "with-chrono")] + Array::ChronoDateTimeUtc(inner) => inner.to_sql(ty, out), + #[cfg(feature = "with-chrono")] + Array::ChronoDateTimeLocal(inner) => inner.to_sql(ty, out), + #[cfg(feature = "with-chrono")] + Array::ChronoDateTimeWithTimeZone(inner) => inner.to_sql(ty, out), + #[cfg(feature = "with-time")] + Array::TimeDate(inner) => inner.to_sql(ty, out), + #[cfg(feature = "with-time")] + Array::TimeTime(inner) => inner.to_sql(ty, out), + #[cfg(feature = "with-time")] + Array::TimeDateTime(inner) => inner.to_sql(ty, out), + #[cfg(feature = "with-time")] + Array::TimeDateTimeWithTimeZone(inner) => inner.to_sql(ty, out), + #[cfg(feature = "with-uuid")] + Array::Uuid(inner) => inner.to_sql(ty, out), + #[cfg(feature = "with-rust_decimal")] + Array::Decimal(inner) => inner.to_sql(ty, out), + #[cfg(feature = "with-bigdecimal")] + Array::BigDecimal(inner) => { + use bigdecimal::ToPrimitive; + inner + .iter() + .cloned() + .map(|v| { + v.map(|bd| bd.to_f64().expect("Fail to convert bigdecimal as f64")) + }) + .collect::>>() + .to_sql(ty, out) + } + #[cfg(feature = "with-ipnetwork")] + Array::IpNetwork(inner) => inner + .iter() + .cloned() + .map(|v| v.map(conv_ip_network)) + .collect::>() + .to_sql(ty, out), + #[cfg(feature = "with-mac_address")] + Array::MacAddress(inner) => inner + .into_iter() + .map(|v| v.map(conv_mac_address)) + .collect::>() + .to_sql(ty, out), + _ => unimplemented!("Unsupported array variant"), + }, #[cfg(feature = "postgres-array")] - Value::Array(_, None) => Ok(IsNull::Yes), + Value::Array(None) => Ok(IsNull::Yes), #[cfg(feature = "postgres-vector")] Value::Vector(Some(v)) => v.to_sql(ty, out), #[cfg(feature = "postgres-vector")] Value::Vector(None) => Ok(IsNull::Yes), #[cfg(feature = "with-ipnetwork")] - Value::IpNetwork(v) => { - use cidr::IpCidr; - v.map(|v| { - IpCidr::new(v.network(), v.prefix()) - .expect("Fail to convert IpNetwork to IpCidr") - }) - .to_sql(ty, out) - } + Value::IpNetwork(v) => v.map(conv_ip_network).to_sql(ty, out), #[cfg(feature = "with-mac_address")] - Value::MacAddress(v) => { - use eui48::MacAddress; - v.map(|v| MacAddress::new(v.bytes())).to_sql(ty, out) - } + Value::MacAddress(v) => v.map(conv_mac_address).to_sql(ty, out), } } @@ -145,3 +216,15 @@ impl ToSql for PostgresValue { to_sql_checked!(); } + +#[cfg(feature = "with-mac_address")] +fn conv_mac_address(input: mac_address::MacAddress) -> eui48::MacAddress { + use eui48::MacAddress; + MacAddress::new(input.bytes()) +} + +#[cfg(feature = "with-ipnetwork")] +fn conv_ip_network(input: ipnetwork::IpNetwork) -> cidr::IpCidr { + use cidr::IpCidr; + IpCidr::new(input.network(), input.prefix()).expect("Fail to convert IpNetwork to IpCidr") +} diff --git a/src/value/array.rs b/src/value/array.rs index c5904cc63..fd6b04ff4 100644 --- a/src/value/array.rs +++ b/src/value/array.rs @@ -1,5 +1,7 @@ use super::*; use crate::backend::ValueEncoder; +#[cfg(feature = "backend-postgres")] +use std::sync::Arc; #[derive(Debug, Clone)] #[cfg_attr(not(feature = "hashable-value"), derive(PartialEq))] @@ -20,6 +22,7 @@ pub enum Array { String(Box<[Option]>), Char(Box<[Option]>), Bytes(Box<[Option>]>), + #[cfg(feature = "backend-postgres")] Enum(Box<(Arc, Box<[Option>]>)>), Array(Box<(ArrayType, Box<[Option]>)>), #[cfg(feature = "with-json")] @@ -104,6 +107,7 @@ impl Array { Array::String(_) => ArrayType::String, Array::Char(_) => ArrayType::Char, Array::Bytes(_) => ArrayType::Bytes, + #[cfg(feature = "backend-postgres")] Array::Enum(boxed) => ArrayType::Enum(boxed.as_ref().0.clone()), Array::Array(arr) => arr.as_ref().0.clone(), #[cfg(feature = "with-json")] @@ -167,6 +171,7 @@ impl Array { Array::String(v) => v.is_empty(), Array::Char(v) => v.is_empty(), Array::Bytes(v) => v.is_empty(), + #[cfg(feature = "backend-postgres")] Array::Enum(b) => b.as_ref().1.is_empty(), Array::Array(b) => b.as_ref().1.is_empty(), #[cfg(feature = "with-json")] @@ -387,6 +392,7 @@ impl Array { Array::String(_) => Array::String(Box::new([])), Array::Char(_) => Array::Char(Box::new([])), Array::Bytes(_) => Array::Bytes(Box::new([])), + #[cfg(feature = "backend-postgres")] Array::Enum(val) => { let val = val.as_ref(); Array::Enum(Box::new((val.0.clone(), Box::new([])))) @@ -868,7 +874,6 @@ mod hash { (Self::String(l0), Self::String(r0)) => l0 == r0, (Self::Char(l0), Self::Char(r0)) => l0 == r0, (Self::Bytes(l0), Self::Bytes(r0)) => l0 == r0, - (Self::Enum(l0), Self::Enum(r0)) => l0 == r0, (Self::Array(l0), Self::Array(r0)) => l0 == r0, (Self::Json(l0), Self::Json(r0)) => l0 == r0, (Self::ChronoDate(l0), Self::ChronoDate(r0)) => l0 == r0, @@ -892,6 +897,8 @@ mod hash { (Self::JiffZoned(l0), Self::JiffZoned(r0)) => l0 == r0, (Self::Uuid(l0), Self::Uuid(r0)) => l0 == r0, (Self::Decimal(l0), Self::Decimal(r0)) => l0 == r0, + #[cfg(feature = "backend-postgres")] + (Self::Enum(l0), Self::Enum(r0)) => l0 == r0, (Self::BigDecimal(l0), Self::BigDecimal(r0)) => l0 == r0, (Self::IpNetwork(l0), Self::IpNetwork(r0)) => l0 == r0, (Self::MacAddress(l0), Self::MacAddress(r0)) => l0 == r0, @@ -930,6 +937,7 @@ mod hash { Array::String(items) => items.hash(state), Array::Char(items) => items.hash(state), Array::Bytes(items) => items.hash(state), + #[cfg(feature = "backend-postgres")] Array::Enum(items) => items.hash(state), Array::Array(items) => items.hash(state), #[cfg(feature = "with-json")] From e3449a2b33c313cc304ef81c10e8bef73b561853 Mon Sep 17 00:00:00 2001 From: Huliiiiii <308013446a@gmail.com> Date: Wed, 29 Oct 2025 06:44:47 +0800 Subject: [PATCH 13/19] Fix sqlx except pg --- sea-query-rusqlite/src/lib.rs | 2 +- sea-query-sqlx/src/sqlx_any.rs | 2 +- sea-query-sqlx/src/sqlx_mysql.rs | 4 ++-- sea-query-sqlx/src/sqlx_postgres.rs | 2 +- sea-query-sqlx/src/sqlx_sqlite.rs | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sea-query-rusqlite/src/lib.rs b/sea-query-rusqlite/src/lib.rs index 4660b12a4..62cef5a2a 100644 --- a/sea-query-rusqlite/src/lib.rs +++ b/sea-query-rusqlite/src/lib.rs @@ -135,7 +135,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"); } #[cfg(feature = "postgres-vector")] diff --git a/sea-query-sqlx/src/sqlx_any.rs b/sea-query-sqlx/src/sqlx_any.rs index 466f7b187..22259d25a 100644 --- a/sea-query-sqlx/src/sqlx_any.rs +++ b/sea-query-sqlx/src/sqlx_any.rs @@ -117,7 +117,7 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::any::Any> for SqlxValues { panic!("SQLx doesn't support MacAddress arguments for Any"); } #[cfg(feature = "postgres-array")] - Value::Array(_, _) => { + Value::Array(_) => { panic!("SQLx doesn't support array arguments for Any"); } #[cfg(feature = "postgres-vector")] diff --git a/sea-query-sqlx/src/sqlx_mysql.rs b/sea-query-sqlx/src/sqlx_mysql.rs index 5275f65f2..4b79c9ab1 100644 --- a/sea-query-sqlx/src/sqlx_mysql.rs +++ b/sea-query-sqlx/src/sqlx_mysql.rs @@ -100,14 +100,14 @@ impl sqlx::IntoArguments<'_, sqlx::mysql::MySql> for SqlxValues { } #[cfg(feature = "with-bigdecimal")] Value::BigDecimal(d) => { - let _ = args.add(d.as_deref()); + let _ = args.add(d.as_ref()); } #[cfg(feature = "with-json")] Value::Json(j) => { let _ = args.add(j); } #[cfg(feature = "postgres-array")] - Value::Array(_, _) => { + Value::Array(_) => { panic!("Mysql doesn't support array arguments"); } #[cfg(feature = "postgres-vector")] diff --git a/sea-query-sqlx/src/sqlx_postgres.rs b/sea-query-sqlx/src/sqlx_postgres.rs index 6402de556..6c7cd5c92 100644 --- a/sea-query-sqlx/src/sqlx_postgres.rs +++ b/sea-query-sqlx/src/sqlx_postgres.rs @@ -115,7 +115,7 @@ impl sqlx::IntoArguments<'_, sqlx::postgres::Postgres> for SqlxValues { } #[cfg(feature = "with-bigdecimal")] Value::BigDecimal(d) => { - let _ = args.add(d.as_deref()); + let _ = args.add(d.as_ref()); } #[cfg(feature = "with-json")] Value::Json(j) => { diff --git a/sea-query-sqlx/src/sqlx_sqlite.rs b/sea-query-sqlx/src/sqlx_sqlite.rs index 888e55778..8a6921f3c 100644 --- a/sea-query-sqlx/src/sqlx_sqlite.rs +++ b/sea-query-sqlx/src/sqlx_sqlite.rs @@ -117,7 +117,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"); } #[cfg(feature = "postgres-vector")] From 36e4b10a4555aacda95157c41413f2aeff94d61f Mon Sep 17 00:00:00 2001 From: Huliiiiii <308013446a@gmail.com> Date: Wed, 29 Oct 2025 17:05:17 +0800 Subject: [PATCH 14/19] Fix --- sea-query-postgres/src/lib.rs | 2 +- sea-query-sqlx/src/sqlx_postgres.rs | 337 +++++++++++++--------------- 2 files changed, 152 insertions(+), 187 deletions(-) diff --git a/sea-query-postgres/src/lib.rs b/sea-query-postgres/src/lib.rs index 167a127bc..d9c167464 100644 --- a/sea-query-postgres/src/lib.rs +++ b/sea-query-postgres/src/lib.rs @@ -132,7 +132,7 @@ impl ToSql for PostgresValue { Array::Unsigned(inner) => inner.to_sql(ty, out), Array::BigUnsigned(inner) => inner .into_iter() - .map(|v| v.map(|x| x as i64)) + .map(|v| v.map(|x| i64::try_from(x).expect("Fail to convert u64 to i64"))) .collect::>>() .to_sql(ty, out), Array::Float(inner) => inner.to_sql(ty, out), diff --git a/sea-query-sqlx/src/sqlx_postgres.rs b/sea-query-sqlx/src/sqlx_postgres.rs index 6c7cd5c92..0ed8dd087 100644 --- a/sea-query-sqlx/src/sqlx_postgres.rs +++ b/sea-query-sqlx/src/sqlx_postgres.rs @@ -10,10 +10,13 @@ use mac_address::MacAddress; use rust_decimal::Decimal; #[cfg(feature = "with-json")] use serde_json::Value as Json; +use sqlx::Arguments; #[cfg(feature = "with-uuid")] use uuid::Uuid; -use sea_query::{ArrayType, Value}; +#[cfg(feature = "postgres-array")] +use sea_query::Array; +use sea_query::Value; use crate::SqlxValues; @@ -21,7 +24,6 @@ impl sqlx::IntoArguments<'_, sqlx::postgres::Postgres> for SqlxValues { fn into_arguments(self) -> sqlx::postgres::PgArguments { let mut args = sqlx::postgres::PgArguments::default(); for arg in self.0.into_iter() { - use sqlx::Arguments; match arg { Value::Bool(b) => { let _ = args.add(b); @@ -130,190 +132,15 @@ impl sqlx::IntoArguments<'_, sqlx::postgres::Postgres> for SqlxValues { let _ = args.add(mac); } #[cfg(feature = "postgres-array")] - Value::Array(ty, v) => match ty { - ArrayType::Bool => { - let value: Option> = Value::Array(ty, v) - .expect("This Value::Array should consist of Value::Bool"); - let _ = args.add(value); - } - ArrayType::TinyInt => { - let value: Option> = Value::Array(ty, v) - .expect("This Value::Array should consist of Value::TinyInt"); - let _ = args.add(value); - } - ArrayType::SmallInt => { - let value: Option> = Value::Array(ty, v) - .expect("This Value::Array should consist of Value::SmallInt"); - let _ = args.add(value); - } - ArrayType::Int => { - let value: Option> = Value::Array(ty, v) - .expect("This Value::Array should consist of Value::Int"); - let _ = args.add(value); - } - ArrayType::BigInt => { - let value: Option> = Value::Array(ty, v) - .expect("This Value::Array should consist of Value::BigInt"); - let _ = args.add(value); - } - ArrayType::TinyUnsigned => { - let value: Option> = Value::Array(ty, v) - .expect("This Value::Array should consist of Value::TinyUnsigned"); - let value: Option> = - value.map(|vec| vec.into_iter().map(|i| i as i16).collect()); - let _ = args.add(value); - } - ArrayType::SmallUnsigned => { - let value: Option> = Value::Array(ty, v) - .expect("This Value::Array should consist of Value::SmallUnsigned"); - let value: Option> = - value.map(|vec| vec.into_iter().map(|i| i as i32).collect()); - let _ = args.add(value); - } - ArrayType::Unsigned => { - let value: Option> = Value::Array(ty, v) - .expect("This Value::Array should consist of Value::Unsigned"); - let value: Option> = - value.map(|vec| vec.into_iter().map(|i| i as i64).collect()); - let _ = args.add(value); - } - ArrayType::BigUnsigned => { - let value: Option> = Value::Array(ty, v) - .expect("This Value::Array should consist of Value::BigUnsigned"); - let value: Option> = value.map(|vec| { - vec.into_iter() - .map(|i| >::try_from(i).unwrap()) - .collect() - }); - let _ = args.add(value); - } - ArrayType::Float => { - let value: Option> = Value::Array(ty, v) - .expect("This Value::Array should consist of Value::Float"); - let _ = args.add(value); - } - ArrayType::Double => { - let value: Option> = Value::Array(ty, v) - .expect("This Value::Array should consist of Value::Double"); - let _ = args.add(value); - } - ArrayType::String => { - let value: Option> = Value::Array(ty, v) - .expect("This Value::Array should consist of Value::String"); - let _ = args.add(value); - } - ArrayType::Char => { - let value: Option> = Value::Array(ty, v) - .expect("This Value::Array should consist of Value::Char"); - let value: Option> = - value.map(|vec| vec.into_iter().map(|c| c.to_string()).collect()); - let _ = args.add(value); - } - ArrayType::Bytes => { - let value: Option>> = Value::Array(ty, v) - .expect("This Value::Array should consist of Value::Bytes"); - let _ = args.add(value); - } - #[cfg(feature = "with-chrono")] - ArrayType::ChronoDate => { - let value: Option> = Value::Array(ty, v) - .expect("This Value::Array should consist of Value::ChronoDate"); - let _ = args.add(value); - } - #[cfg(feature = "with-chrono")] - ArrayType::ChronoTime => { - let value: Option> = Value::Array(ty, v) - .expect("This Value::Array should consist of Value::ChronoTime"); - let _ = args.add(value); - } - #[cfg(feature = "with-chrono")] - ArrayType::ChronoDateTime => { - let value: Option> = Value::Array(ty, v) - .expect("This Value::Array should consist of Value::ChronoDateTime"); - let _ = args.add(value); - } - #[cfg(feature = "with-chrono")] - ArrayType::ChronoDateTimeUtc => { - let value: Option>> = Value::Array(ty, v) - .expect("This Value::Array should consist of Value::ChronoDateTimeUtc"); - let _ = args.add(value); - } - #[cfg(feature = "with-chrono")] - ArrayType::ChronoDateTimeLocal => { - let value: Option>> = Value::Array(ty, v).expect( - "This Value::Array should consist of Value::ChronoDateTimeLocal", - ); - let _ = args.add(value); - } - #[cfg(feature = "with-chrono")] - ArrayType::ChronoDateTimeWithTimeZone => { - let value: Option>> = Value::Array(ty, v).expect( - "This Value::Array should consist of Value::ChronoDateTimeWithTimeZone", - ); - let _ = args.add(value); - } - #[cfg(feature = "with-time")] - ArrayType::TimeDate => { - let value: Option> = Value::Array(ty, v) - .expect("This Value::Array should consist of Value::TimeDate"); - let _ = args.add(value); - } - #[cfg(feature = "with-time")] - ArrayType::TimeTime => { - let value: Option> = Value::Array(ty, v) - .expect("This Value::Array should consist of Value::TimeTime"); - let _ = args.add(value); - } - #[cfg(feature = "with-time")] - ArrayType::TimeDateTime => { - let value: Option> = Value::Array(ty, v) - .expect("This Value::Array should consist of Value::TimeDateTime"); - let _ = args.add(value); - } - #[cfg(feature = "with-time")] - ArrayType::TimeDateTimeWithTimeZone => { - let value: Option> = Value::Array(ty, v).expect( - "This Value::Array should consist of Value::TimeDateTimeWithTimeZone", - ); - let _ = args.add(value); - } - #[cfg(feature = "with-uuid")] - ArrayType::Uuid => { - let value: Option> = Value::Array(ty, v) - .expect("This Value::Array should consist of Value::Uuid"); - let _ = args.add(value); - } - #[cfg(feature = "with-rust_decimal")] - ArrayType::Decimal => { - let value: Option> = Value::Array(ty, v) - .expect("This Value::Array should consist of Value::Decimal"); - let _ = args.add(value); - } - #[cfg(feature = "with-bigdecimal")] - ArrayType::BigDecimal => { - let value: Option> = Value::Array(ty, v) - .expect("This Value::Array should consist of Value::BigDecimal"); - let _ = args.add(value); - } - #[cfg(feature = "with-json")] - ArrayType::Json => { - let value: Option> = Value::Array(ty, v) - .expect("This Value::Array should consist of Value::Json"); - let _ = args.add(value); - } - #[cfg(feature = "with-ipnetwork")] - ArrayType::IpNetwork => { - let value: Option> = Value::Array(ty, v) - .expect("This Value::Array should consist of Value::IpNetwork"); - let _ = args.add(value); - } - #[cfg(feature = "with-mac_address")] - ArrayType::MacAddress => { - let value: Option> = Value::Array(ty, v) - .expect("This Value::Array should consist of Value::MacAddress"); - let _ = args.add(value); - } - }, + Value::Array(arr) => { + match arr { + Some(a) => match_some_array(a, &mut args), + None => { + // TODO: Add Array::Null? + panic!("Sqlx does not support binding null arrays"); + } + }; + } #[cfg(feature = "postgres-vector")] Value::Vector(v) => { let _ = args.add(v); @@ -323,3 +150,141 @@ impl sqlx::IntoArguments<'_, sqlx::postgres::Postgres> for SqlxValues { args } } + +#[cfg(feature = "postgres-array")] +fn match_some_array(arr: Array, args: &mut sqlx::postgres::PgArguments) { + match arr { + Array::Bool(inner) => { + let _ = args.add(inner.into_vec()); + } + Array::TinyInt(inner) => { + let _ = args.add(inner.into_vec()); + } + Array::SmallInt(inner) => { + let _ = args.add(inner.into_vec()); + } + Array::Int(inner) => { + let _ = args.add(inner.into_vec()); + } + Array::BigInt(inner) => { + let _ = args.add(inner.into_vec()); + } + Array::TinyUnsigned(inner) => { + let v: Vec> = inner + .into_vec() + .into_iter() + .map(|x| x.map(|y| y as i16)) + .collect(); + let _ = args.add(v); + } + Array::SmallUnsigned(inner) => { + let v: Vec> = inner + .into_vec() + .into_iter() + .map(|x| x.map(|y| y as i32)) + .collect(); + let _ = args.add(v); + } + Array::Unsigned(inner) => { + let v: Vec> = inner + .into_vec() + .into_iter() + .map(|x| x.map(|y| y as i64)) + .collect(); + let _ = args.add(v); + } + Array::BigUnsigned(inner) => { + let v: Vec> = inner + .into_vec() + .into_iter() + .map(|x| x.map(|y| >::try_from(y).unwrap())) + .collect(); + let _ = args.add(v); + } + Array::Float(inner) => { + let _ = args.add(inner.into_vec()); + } + Array::Double(inner) => { + let _ = args.add(inner.into_vec()); + } + Array::String(inner) => { + let _ = args.add(inner.into_vec()); + } + Array::Char(inner) => { + let v: Vec> = inner + .into_vec() + .into_iter() + .map(|c| c.map(|x| x.to_string())) + .collect(); + let _ = args.add(v); + } + Array::Bytes(inner) => { + let _ = args.add(inner.into_vec()); + } + #[cfg(feature = "with-json")] + Array::Json(inner) => { + let _ = args.add(inner.into_vec()); + } + #[cfg(feature = "with-chrono")] + Array::ChronoDate(inner) => { + let _ = args.add(inner.into_vec()); + } + #[cfg(feature = "with-chrono")] + Array::ChronoTime(inner) => { + let _ = args.add(inner.into_vec()); + } + #[cfg(feature = "with-chrono")] + Array::ChronoDateTime(inner) => { + let _ = args.add(inner.into_vec()); + } + #[cfg(feature = "with-chrono")] + Array::ChronoDateTimeUtc(inner) => { + let _ = args.add(inner.into_vec()); + } + #[cfg(feature = "with-chrono")] + Array::ChronoDateTimeLocal(inner) => { + let _ = args.add(inner.into_vec()); + } + #[cfg(feature = "with-chrono")] + Array::ChronoDateTimeWithTimeZone(inner) => { + let _ = args.add(inner.into_vec()); + } + #[cfg(feature = "with-time")] + Array::TimeDate(inner) => { + let _ = args.add(inner.into_vec()); + } + #[cfg(feature = "with-time")] + Array::TimeTime(inner) => { + let _ = args.add(inner.into_vec()); + } + #[cfg(feature = "with-time")] + Array::TimeDateTime(inner) => { + let _ = args.add(inner.into_vec()); + } + #[cfg(feature = "with-time")] + Array::TimeDateTimeWithTimeZone(inner) => { + let _ = args.add(inner.into_vec()); + } + #[cfg(feature = "with-uuid")] + Array::Uuid(inner) => { + let _ = args.add(inner.into_vec()); + } + #[cfg(feature = "with-rust_decimal")] + Array::Decimal(inner) => { + let _ = args.add(inner.into_vec()); + } + #[cfg(feature = "with-bigdecimal")] + Array::BigDecimal(inner) => { + let _ = args.add(inner.into_vec()); + } + #[cfg(feature = "with-ipnetwork")] + Array::IpNetwork(inner) => { + let _ = args.add(inner.into_vec()); + } + #[cfg(feature = "with-mac_address")] + Array::MacAddress(inner) => { + let _ = args.add(inner.into_vec()); + } + _ => {} + } +} From 4e1b37ab0a7e05e91e4b5f43563abf9777124e7e Mon Sep 17 00:00:00 2001 From: Huliiiiii <308013446a@gmail.com> Date: Wed, 29 Oct 2025 17:57:20 +0800 Subject: [PATCH 15/19] Tweak --- sea-query-sqlx/src/sqlx_postgres.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sea-query-sqlx/src/sqlx_postgres.rs b/sea-query-sqlx/src/sqlx_postgres.rs index 0ed8dd087..e2b117383 100644 --- a/sea-query-sqlx/src/sqlx_postgres.rs +++ b/sea-query-sqlx/src/sqlx_postgres.rs @@ -137,7 +137,7 @@ impl sqlx::IntoArguments<'_, sqlx::postgres::Postgres> for SqlxValues { Some(a) => match_some_array(a, &mut args), None => { // TODO: Add Array::Null? - panic!("Sqlx does not support binding null arrays"); + panic!("Postgres does not support binding null to arrays"); } }; } From b7505dc9cccf19f025b179761c2e018a04936633 Mon Sep 17 00:00:00 2001 From: Huliiiiii <308013446a@gmail.com> Date: Wed, 29 Oct 2025 21:37:31 +0800 Subject: [PATCH 16/19] Refactor Postgres binder error handling --- sea-query-postgres/Cargo.toml | 2 +- sea-query-postgres/src/lib.rs | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/sea-query-postgres/Cargo.toml b/sea-query-postgres/Cargo.toml index 4618b25d4..3290d38a3 100644 --- a/sea-query-postgres/Cargo.toml +++ b/sea-query-postgres/Cargo.toml @@ -17,7 +17,7 @@ rust-version = "1.85.0" [lib] [dependencies] -sea-query = { version = "1.0.0-rc.1", path = "..", default-features = false } +sea-query = { version = "1.0.0-rc.1", path = "..", default-features = false, features = ["backend-postgres"] } postgres-types = { version = "0.2", default-features = false } pgvector = { version = "~0.4", default-features = false, optional = true } bytes = { version = "1", default-features = false } diff --git a/sea-query-postgres/src/lib.rs b/sea-query-postgres/src/lib.rs index d9c167464..d316a95d0 100644 --- a/sea-query-postgres/src/lib.rs +++ b/sea-query-postgres/src/lib.rs @@ -79,6 +79,10 @@ impl ToSql for PostgresValue { 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), + Value::Enum(_) => Err(PostgresBindError::new( + "Binding Enum is not supported by sea-query-postgres binder", + ) + .into()), #[cfg(feature = "with-json")] Value::Json(v) => v.to_sql(ty, out), #[cfg(feature = "with-chrono")] @@ -195,7 +199,18 @@ impl ToSql for PostgresValue { .map(|v| v.map(conv_mac_address)) .collect::>() .to_sql(ty, out), - _ => unimplemented!("Unsupported array variant"), + Array::Array(_) => Err(PostgresBindError::new( + "Nested arrays (Array::Array) are not supported by sea-query-postgres binder", + ) + .into()), + Array::Enum(_) => Err(PostgresBindError::new( + "Array of Enum is not supported by sea-query-postgres binder; consider casting in SQL", + ) + .into()), + _ => Err(PostgresBindError::new( + "Unsupported array variant for sea-query-postgres binder", + ) + .into()), }, #[cfg(feature = "postgres-array")] Value::Array(None) => Ok(IsNull::Yes), @@ -217,6 +232,23 @@ impl ToSql for PostgresValue { to_sql_checked!(); } +#[derive(Debug, Clone)] +struct PostgresBindError(&'static str); + +impl PostgresBindError { + fn new(msg: &'static str) -> Self { + Self(msg) + } +} + +impl std::fmt::Display for PostgresBindError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(self.0) + } +} + +impl Error for PostgresBindError {} + #[cfg(feature = "with-mac_address")] fn conv_mac_address(input: mac_address::MacAddress) -> eui48::MacAddress { use eui48::MacAddress; From 17e2e60997c7ea0de5161307862f9b43643e7de3 Mon Sep 17 00:00:00 2001 From: Huliiiiii <308013446a@gmail.com> Date: Wed, 29 Oct 2025 21:37:31 +0800 Subject: [PATCH 17/19] Refactor Postgres binder error handling --- sea-query-postgres/src/lib.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/sea-query-postgres/src/lib.rs b/sea-query-postgres/src/lib.rs index d316a95d0..c266e4b99 100644 --- a/sea-query-postgres/src/lib.rs +++ b/sea-query-postgres/src/lib.rs @@ -110,9 +110,12 @@ impl ToSql for PostgresValue { #[cfg(feature = "with-bigdecimal")] Value::BigDecimal(v) => { use bigdecimal::ToPrimitive; - v.as_ref() - .map(|v| v.to_f64().expect("Fail to convert bigdecimal as f64")) - .to_sql(ty, out) + v.as_ref().map(|x| x.to_f64().ok_or( + PostgresBindError::new( + "Fail to convert bigdecimal as f64 for sea-query-postgres binder", + ) + )).transpose()?.to_sql(ty, out) + } #[cfg(feature = "with-uuid")] Value::Uuid(v) => v.to_sql(ty, out), @@ -136,8 +139,8 @@ impl ToSql for PostgresValue { Array::Unsigned(inner) => inner.to_sql(ty, out), Array::BigUnsigned(inner) => inner .into_iter() - .map(|v| v.map(|x| i64::try_from(x).expect("Fail to convert u64 to i64"))) - .collect::>>() + .map(|v| v.map(|x| i64::try_from(x)).transpose()) + .collect::>,_>>()? .to_sql(ty, out), Array::Float(inner) => inner.to_sql(ty, out), Array::Double(inner) => inner.to_sql(ty, out), From 533758b5e67c925029a7bee3aa984ef79847f921 Mon Sep 17 00:00:00 2001 From: Huliiiiii <308013446a@gmail.com> Date: Wed, 29 Oct 2025 22:22:01 +0800 Subject: [PATCH 18/19] Remove try_from_parts --- src/value/array.rs | 387 ------------------------------------ src/value/hashable_value.rs | 24 +-- 2 files changed, 2 insertions(+), 409 deletions(-) diff --git a/src/value/array.rs b/src/value/array.rs index fd6b04ff4..5b80aff19 100644 --- a/src/value/array.rs +++ b/src/value/array.rs @@ -445,393 +445,6 @@ impl Array { Array::MacAddress(_) => Array::MacAddress(Box::new([])), } } - - pub fn try_from_parts(kind: ArrayType, elems: Vec) -> Result { - match kind { - ArrayType::Bool => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::Bool(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::Bool(v.into_boxed_slice())) - } - ArrayType::TinyInt => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::TinyInt(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::TinyInt(v.into_boxed_slice())) - } - ArrayType::SmallInt => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::SmallInt(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::SmallInt(v.into_boxed_slice())) - } - ArrayType::Int => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::Int(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::Int(v.into_boxed_slice())) - } - ArrayType::BigInt => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::BigInt(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::BigInt(v.into_boxed_slice())) - } - ArrayType::TinyUnsigned => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::TinyUnsigned(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::TinyUnsigned(v.into_boxed_slice())) - } - ArrayType::SmallUnsigned => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::SmallUnsigned(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::SmallUnsigned(v.into_boxed_slice())) - } - ArrayType::Unsigned => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::Unsigned(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::Unsigned(v.into_boxed_slice())) - } - ArrayType::BigUnsigned => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::BigUnsigned(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::BigUnsigned(v.into_boxed_slice())) - } - ArrayType::Float => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::Float(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::Float(v.into_boxed_slice())) - } - ArrayType::Double => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::Double(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::Double(v.into_boxed_slice())) - } - ArrayType::String => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::String(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::String(v.into_boxed_slice())) - } - ArrayType::Char => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::Char(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::Char(v.into_boxed_slice())) - } - ArrayType::Bytes => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::Bytes(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::Bytes(v.into_boxed_slice())) - } - #[cfg(feature = "backend-postgres")] - ArrayType::Enum(name) => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::Enum(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::Enum(Box::new((name, v.into_boxed_slice())))) - } - #[cfg(feature = "with-json")] - ArrayType::Json => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::Json(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::Json(v.into_boxed_slice())) - } - #[cfg(feature = "with-chrono")] - ArrayType::ChronoDate => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::ChronoDate(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::ChronoDate(v.into_boxed_slice())) - } - #[cfg(feature = "with-chrono")] - ArrayType::ChronoTime => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::ChronoTime(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::ChronoTime(v.into_boxed_slice())) - } - #[cfg(feature = "with-chrono")] - ArrayType::ChronoDateTime => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::ChronoDateTime(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::ChronoDateTime(v.into_boxed_slice())) - } - #[cfg(feature = "with-chrono")] - ArrayType::ChronoDateTimeUtc => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::ChronoDateTimeUtc(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::ChronoDateTimeUtc(v.into_boxed_slice())) - } - #[cfg(feature = "with-chrono")] - ArrayType::ChronoDateTimeLocal => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::ChronoDateTimeLocal(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::ChronoDateTimeLocal(v.into_boxed_slice())) - } - #[cfg(feature = "with-chrono")] - ArrayType::ChronoDateTimeWithTimeZone => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::ChronoDateTimeWithTimeZone(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::ChronoDateTimeWithTimeZone(v.into_boxed_slice())) - } - #[cfg(feature = "with-time")] - ArrayType::TimeDate => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::TimeDate(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::TimeDate(v.into_boxed_slice())) - } - #[cfg(feature = "with-time")] - ArrayType::TimeTime => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::TimeTime(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::TimeTime(v.into_boxed_slice())) - } - #[cfg(feature = "with-time")] - ArrayType::TimeDateTime => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::TimeDateTime(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::TimeDateTime(v.into_boxed_slice())) - } - #[cfg(feature = "with-time")] - ArrayType::TimeDateTimeWithTimeZone => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::TimeDateTimeWithTimeZone(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::TimeDateTimeWithTimeZone(v.into_boxed_slice())) - } - #[cfg(feature = "with-jiff")] - ArrayType::JiffDate => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::JiffDate(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::JiffDate(v.into_boxed_slice())) - } - #[cfg(feature = "with-jiff")] - ArrayType::JiffTime => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::JiffTime(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::JiffTime(v.into_boxed_slice())) - } - #[cfg(feature = "with-jiff")] - ArrayType::JiffDateTime => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::JiffDateTime(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::JiffDateTime(v.into_boxed_slice())) - } - #[cfg(feature = "with-jiff")] - ArrayType::JiffTimestamp => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::JiffTimestamp(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::JiffTimestamp(v.into_boxed_slice())) - } - #[cfg(feature = "with-jiff")] - ArrayType::JiffZoned => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::JiffZoned(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::JiffZoned(v.into_boxed_slice())) - } - #[cfg(feature = "with-uuid")] - ArrayType::Uuid => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::Uuid(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::Uuid(v.into_boxed_slice())) - } - #[cfg(feature = "with-rust_decimal")] - ArrayType::Decimal => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::Decimal(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::Decimal(v.into_boxed_slice())) - } - #[cfg(feature = "with-bigdecimal")] - ArrayType::BigDecimal => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::BigDecimal(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::BigDecimal(v.into_boxed_slice())) - } - #[cfg(feature = "with-ipnetwork")] - ArrayType::IpNetwork => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::IpNetwork(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::IpNetwork(v.into_boxed_slice())) - } - #[cfg(feature = "with-mac_address")] - ArrayType::MacAddress => { - let mut v = Vec::with_capacity(elems.len()); - for e in elems { - match e { - Value::MacAddress(x) => v.push(x), - _ => return Err(ValueTypeErr), - } - } - Ok(Array::MacAddress(v.into_boxed_slice())) - } - } - } } #[cfg(feature = "hashable-value")] mod hash { diff --git a/src/value/hashable_value.rs b/src/value/hashable_value.rs index 958b128ff..299589953 100644 --- a/src/value/hashable_value.rs +++ b/src/value/hashable_value.rs @@ -337,32 +337,12 @@ mod tests { assert_eq!( Into::::into(vec![0i32, 1, 2]), - Value::Array(Some( - Array::try_from_parts( - ArrayType::Int, - vec![ - Value::Int(Some(0)), - Value::Int(Some(1)), - Value::Int(Some(2)), - ], - ) - .expect("array construction"), - )) + Value::Array(Some(Array::from(vec![Some(0), Some(1), Some(2)]))) ); assert_eq!( Into::::into(vec![0f32, 1.0, 2.0]), - Value::Array(Some( - Array::try_from_parts( - ArrayType::Float, - vec![ - Value::Float(Some(0f32)), - Value::Float(Some(1.0)), - Value::Float(Some(2.0)), - ], - ) - .expect("array construction"), - )) + Value::Array(Some(Array::from(vec![Some(0f32), Some(1.0), Some(2.0)]))) ); let hash_set: std::collections::HashSet = [ From ba70b69a585915372d818c6886cf2f6a70199b1f Mon Sep 17 00:00:00 2001 From: Huliiiiii <308013446a@gmail.com> Date: Wed, 29 Oct 2025 22:31:25 +0800 Subject: [PATCH 19/19] Remove panics --- sea-query-postgres/src/lib.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/sea-query-postgres/src/lib.rs b/sea-query-postgres/src/lib.rs index c266e4b99..dc7107435 100644 --- a/sea-query-postgres/src/lib.rs +++ b/sea-query-postgres/src/lib.rs @@ -182,11 +182,16 @@ impl ToSql for PostgresValue { use bigdecimal::ToPrimitive; inner .iter() - .cloned() .map(|v| { - v.map(|bd| bd.to_f64().expect("Fail to convert bigdecimal as f64")) + v.as_ref() + .map(|bd| { + bd.to_f64().ok_or(PostgresBindError::new( + "Fail to convert bigdecimal as f64 for sea-query-postgres binder", + )) + }) + .transpose() }) - .collect::>>() + .collect::>, _>>()? .to_sql(ty, out) } #[cfg(feature = "with-ipnetwork")]