Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 28 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,30 @@ 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"] }
serde_json = { version = "1", default-features = false, optional = true, features = ["std"] }
chrono = { version = "0.4.27", default-features = false, optional = true, features = ["clock"] }
sea-query-postgres-types = { version = "0.8.0-rc.9", path = "sea-query-postgres-types", optional = true }
serde = { version = "1", default-features = false, optional = true, features = [
"std",
"derive",
] }
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 }
pgvector = { version = "~0.4", default-features = false, optional = true }
rust_decimal = { version = "1", default-features = false, optional = true }
bigdecimal = { version = "0.4", default-features = false, optional = true }
uuid = { version = "1", default-features = false, optional = true }
time = { version = "0.3.36", default-features = false, optional = true, features = ["macros", "formatting"] }
jiff = { version = "0.2.15", default-features = false, optional = true, features = ["std", "perf-inline"] }
time = { version = "0.3.36", default-features = false, optional = true, features = [
"macros",
"formatting",
] }
jiff = { version = "0.2.15", default-features = false, optional = true, features = [
"std",
"perf-inline",
] }
ipnetwork = { version = "0.20", default-features = false, optional = true }
mac_address = { version = "1.1", default-features = false, optional = true }
ordered-float = { version = "4.6", default-features = false, optional = true }
Expand All @@ -56,13 +70,21 @@ audit = []
backend-mysql = []
backend-postgres = []
backend-sqlite = []
default = ["derive", "audit", "backend-mysql", "backend-postgres", "backend-sqlite", "itoa"]
default = [
"derive",
"audit",
"backend-mysql",
"backend-postgres",
"backend-sqlite",
"itoa",
]
derive = ["sea-query-derive"]
attr = ["sea-query-derive"]
hashable-value = ["ordered-float"]
postgres-array = []
postgres-vector = ["pgvector"]
postgres-interval = []
postgres-range = ["sea-query-postgres-types"]
serde = [
"dep:serde",
"chrono?/serde",
Expand Down
Empty file modified build-tools/rustclippy.sh
100644 → 100755
Empty file.
Empty file modified build-tools/rustfmt.sh
100644 → 100755
Empty file.
41 changes: 41 additions & 0 deletions sea-query-postgres-types/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
[package]
name = "sea-query-postgres-types"
version = "0.8.0-rc.9"
authors = ["Richard Jonas <[email protected]>"]
edition = "2024"
description = "Postgres types for using SeaQuery with SQLx"
license = "MIT OR Apache-2.0"
documentation = "https://docs.rs/sea-query"
repository = "https://github.com/SeaQL/sea-query"
categories = ["database"]
keywords = ["database", "sql", "mysql", "postgres", "sqlite"]
rust-version = "1.85.0"

[lib]

[dependencies]
bytes = { version = "1", default-features = false }
chrono = { version = "0.4", default-features = false, optional = true, features = [
"clock",
] }
ordered-float = { version = "4.6", default-features = false, optional = false }
postgres-protocol = { version = "0.6", default-features = false }
postgres-types = { version = "0.2", default-features = false }
serde = { version = "1", default-features = false, optional = false, features = [
"std",
"derive",
] }
serde_json = { version = "1", default-features = false, optional = false, features = [
"std",
] }
time = { version = "0.3.36", default-features = false, optional = true, features = [
"macros",
"formatting",
] }

[features]
hashable-value = []
postgres-range = []
serde = ["chrono?/serde", "time?/serde"]
with-chrono = ["chrono"]
with-time = ["time"]
1 change: 1 addition & 0 deletions sea-query-postgres-types/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod range;
190 changes: 190 additions & 0 deletions sea-query-postgres-types/src/range.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
//#[cfg(feature = "hashable-value")]
use std::hash::{Hash, Hasher};
use std::{
error::Error,
fmt::{Debug, Display},
};

use bytes::BytesMut;
use postgres_protocol::types;
use postgres_types::{IsNull, Kind, ToSql, Type, to_sql_checked};

#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
//#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum RangeBound<T: Clone + Display + ToSql> {
Exclusive(T),
Inclusive(T),
Unbounded,
}

#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
//#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum RangeType {
Int4Range(RangeBound<i32>, RangeBound<i32>),
Int8Range(RangeBound<i64>, RangeBound<i64>),
NumRange(RangeBound<f64>, RangeBound<f64>),
}

impl RangeType {
pub fn empty(&self) -> bool {
matches!(
self,
&RangeType::Int4Range(RangeBound::Unbounded, RangeBound::Unbounded)
| &RangeType::Int8Range(RangeBound::Unbounded, RangeBound::Unbounded)
| &RangeType::NumRange(RangeBound::Unbounded, RangeBound::Unbounded)
)
}
}

impl Default for RangeType {
fn default() -> Self {
Self::Int4Range(RangeBound::Unbounded, RangeBound::Unbounded)
}
}

impl Display for RangeType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
RangeType::Int4Range(a, b) => display_range(a, b, f),
RangeType::Int8Range(a, b) => display_range(a, b, f),
RangeType::NumRange(a, b) => display_range(a, b, f),
}
}
}

fn display_range<T: Clone + Display + ToSql>(
a: &RangeBound<T>,
b: &RangeBound<T>,
f: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
match a {
RangeBound::Exclusive(v) => {
f.write_fmt(format_args!("[{v},"))?;
}
RangeBound::Inclusive(v) => {
f.write_fmt(format_args!("({v},"))?;
}
RangeBound::Unbounded => {
f.write_str("(,")?;
}
}

match b {
RangeBound::Exclusive(v) => {
f.write_fmt(format_args!("{v}]"))?;
}
RangeBound::Inclusive(v) => {
f.write_fmt(format_args!("{v})"))?;
}
RangeBound::Unbounded => {
f.write_str(")")?;
}
}

Ok(())
}

// TODO even if I put Hash impl behind feature gate, compilation fails
//#[cfg(feature = "hashable-value")]
impl Hash for RangeType {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
core::mem::discriminant(self).hash(state);
match self {
RangeType::Int4Range(a, b) => {
a.hash(state);
b.hash(state);
}
RangeType::Int8Range(a, b) => {
a.hash(state);
b.hash(state);
}
RangeType::NumRange(a, b) => {
hash_range_bound(a, state);
hash_range_bound(b, state);
}
}
}
}

//#[cfg(feature = "hashable-value")]
fn hash_range_bound<H: Hasher>(rb: &RangeBound<f64>, state: &mut H) {
match rb {
RangeBound::Exclusive(v) => ordered_float::OrderedFloat(*v).hash(state),
RangeBound::Inclusive(v) => ordered_float::OrderedFloat(*v).hash(state),
RangeBound::Unbounded => (),
}
}

impl ToSql for RangeType {
fn to_sql(
&self,
ty: &postgres_types::Type,
buf: &mut BytesMut,
) -> Result<postgres_types::IsNull, Box<dyn std::error::Error + Sync + Send>>
where
Self: Sized,
{
let element_type = match *ty.kind() {
Kind::Range(ref ty) => ty,
_ => return Err(format!("unexpected type {:?}", ty).into()),
};

if self.empty() {
types::empty_range_to_sql(buf);
} else {
types::range_to_sql(
|buf| match self {
RangeType::Int4Range(lower, _) => bound_to_sql(lower, element_type, buf),
RangeType::Int8Range(lower, _) => bound_to_sql(lower, element_type, buf),
RangeType::NumRange(lower, _) => bound_to_sql(lower, element_type, buf),
},
|buf| match self {
RangeType::Int4Range(_, upper) => bound_to_sql(upper, element_type, buf),
RangeType::Int8Range(_, upper) => bound_to_sql(upper, element_type, buf),
RangeType::NumRange(_, upper) => bound_to_sql(upper, element_type, buf),
},
buf,
)?;
}

Ok(postgres_types::IsNull::No)
}

fn accepts(ty: &postgres_types::Type) -> bool
where
Self: Sized,
{
matches!(ty.kind(), &Kind::Range(_))
}

to_sql_checked!();
}

fn bound_to_sql<T>(
bound: &RangeBound<T>,
ty: &Type,
buf: &mut BytesMut,
) -> Result<types::RangeBound<postgres_protocol::IsNull>, Box<dyn Error + Sync + Send>>
where
T: Clone + Display + ToSql,
{
match bound {
RangeBound::Exclusive(v) => {
let is_null = match v.to_sql(ty, buf)? {
IsNull::Yes => postgres_protocol::IsNull::Yes,
IsNull::No => postgres_protocol::IsNull::No,
};

Ok(types::RangeBound::Exclusive(is_null))
}
RangeBound::Inclusive(v) => {
let is_null = match v.to_sql(ty, buf)? {
IsNull::Yes => postgres_protocol::IsNull::Yes,
IsNull::No => postgres_protocol::IsNull::No,
};

Ok(types::RangeBound::Inclusive(is_null))
}
RangeBound::Unbounded => Ok(types::RangeBound::Unbounded),
}
}
22 changes: 17 additions & 5 deletions sea-query-postgres/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,21 @@
[package]
name = "sea-query-postgres"
version = "0.6.0-rc.1"
authors = [ "Ivan Krivosheev <[email protected]>" ]
authors = ["Ivan Krivosheev <[email protected]>"]
edition = "2024"
description = "Binder traits for connecting sea-query with postgres driver"
license = "MIT OR Apache-2.0"
documentation = "https://docs.rs/sea-query"
repository = "https://github.com/SeaQL/sea-query"
categories = [ "database" ]
keywords = [ "database", "sql", "postgres" ]
categories = ["database"]
keywords = ["database", "sql", "postgres"]
rust-version = "1.85.0"

[lib]

[dependencies]
sea-query = { version = "1.0.0-rc.1", path = "..", default-features = false }
sea-query-postgres-types = { version = "0.8.0-rc.9", path = "../sea-query-postgres-types", default-features = false }
postgres-types = { version = "0.2", default-features = false }
pgvector = { version = "~0.4", default-features = false, optional = true }
bytes = { version = "1", default-features = false }
Expand All @@ -36,6 +37,17 @@ with-bigdecimal = ["sea-query/with-bigdecimal", "bigdecimal"]
with-uuid = ["postgres-types/with-uuid-1", "sea-query/with-uuid"]
with-time = ["postgres-types/with-time-0_3", "sea-query/with-time"]
postgres-array = ["postgres-types/array-impls", "sea-query/postgres-array"]
postgres-range = ["sea-query/postgres-range"]
postgres-vector = ["sea-query/postgres-vector", "pgvector/postgres"]
with-ipnetwork = ["postgres-types/with-cidr-0_2", "sea-query/with-ipnetwork", "ipnetwork", "cidr"]
with-mac_address = ["postgres-types/with-eui48-1", "sea-query/with-mac_address", "mac_address", "eui48"]
with-ipnetwork = [
"postgres-types/with-cidr-0_2",
"sea-query/with-ipnetwork",
"ipnetwork",
"cidr",
]
with-mac_address = [
"postgres-types/with-eui48-1",
"sea-query/with-mac_address",
"mac_address",
"eui48",
]
4 changes: 4 additions & 0 deletions sea-query-postgres/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ impl ToSql for PostgresValue {
use eui48::MacAddress;
v.map(|v| MacAddress::new(v.bytes())).to_sql(ty, out)
}
#[cfg(feature = "postgres-range")]
Value::Range(None) => Ok(IsNull::Yes),
#[cfg(feature = "postgres-range")]
Value::Range(Some(v)) => v.to_sql(ty, out),
}
}

Expand Down
Loading
Loading