diff --git a/Cargo.toml b/Cargo.toml index 7c44f442d..184e7fcda 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ time = { version = "0.3.36", default-features = false, optional = true, features 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 } +geo-types = { version = "0.7", default-features = false, optional = true } [dev-dependencies] sea-query = { path = ".", features = ["tests-cfg"] } @@ -68,6 +69,7 @@ with-uuid = ["uuid"] with-time = ["time"] with-ipnetwork = ["ipnetwork"] with-mac_address = ["mac_address"] +with-postgres-point = ["dep:geo-types", "dep:postgres-types", "postgres-types/with-geo-types-0_7"] tests-cfg = [] all-features = [ "backend-mysql", @@ -91,6 +93,7 @@ all-types = [ "with-time", "with-ipnetwork", "with-mac_address", + "with-postgres-point", ] option-more-parentheses = [] option-sqlite-exact-column-type = [] diff --git a/sea-query-binder/Cargo.toml b/sea-query-binder/Cargo.toml index 44ed4d853..93b94c038 100644 --- a/sea-query-binder/Cargo.toml +++ b/sea-query-binder/Cargo.toml @@ -42,6 +42,7 @@ with-uuid = ["sqlx?/uuid", "sea-query/with-uuid", "uuid"] with-time = ["sqlx?/time", "sea-query/with-time", "time"] with-ipnetwork = ["sqlx?/ipnetwork", "sea-query/with-ipnetwork", "ipnetwork"] with-mac_address = ["sqlx?/mac_address", "sea-query/with-mac_address", "mac_address"] +with-postgres-point = ["sea-query/with-postgres-point"] postgres-array = ["sea-query/postgres-array"] postgres-vector = ["sea-query/postgres-vector", "pgvector/sqlx"] runtime-async-std = ["sqlx?/runtime-async-std"] diff --git a/sea-query-binder/src/sqlx_postgres.rs b/sea-query-binder/src/sqlx_postgres.rs index 1e91ccad5..bd46d3680 100644 --- a/sea-query-binder/src/sqlx_postgres.rs +++ b/sea-query-binder/src/sqlx_postgres.rs @@ -129,6 +129,10 @@ impl sqlx::IntoArguments<'_, sqlx::postgres::Postgres> for SqlxValues { Value::MacAddress(mac) => { let _ = args.add(mac.as_deref()); } + #[cfg(feature = "with-postgres-point")] + Value::Point(point) => { + let _ = args.add(point.as_deref()); + } #[cfg(feature = "postgres-array")] Value::Array(ty, v) => match ty { ArrayType::Bool => { diff --git a/sea-query-postgres/Cargo.toml b/sea-query-postgres/Cargo.toml index 5cb41a7ce..987e5bc15 100644 --- a/sea-query-postgres/Cargo.toml +++ b/sea-query-postgres/Cargo.toml @@ -39,3 +39,4 @@ postgres-array = ["postgres-types/array-impls", "sea-query/postgres-array"] 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-postgres-point = ["sea-query/with-postgres-point"] diff --git a/sea-query-postgres/src/lib.rs b/sea-query-postgres/src/lib.rs index 130567457..a1ec71a59 100644 --- a/sea-query-postgres/src/lib.rs +++ b/sea-query-postgres/src/lib.rs @@ -137,6 +137,8 @@ impl ToSql for PostgresValue { .map(|v| MacAddress::new(v.bytes())) .to_sql(ty, out) } + #[cfg(feature = "with-postgres-point")] + Value::Point(v) => v.as_deref().to_sql(ty, out), } } diff --git a/src/backend/mysql/table.rs b/src/backend/mysql/table.rs index 0da5ef304..ca1661f03 100644 --- a/src/backend/mysql/table.rs +++ b/src/backend/mysql/table.rs @@ -95,6 +95,7 @@ impl TableBuilder for MysqlQueryBuilder { ColumnType::Inet => unimplemented!("Inet is not available in MySQL."), ColumnType::MacAddr => unimplemented!("MacAddr is not available in MySQL."), ColumnType::LTree => unimplemented!("LTree is not available in MySQL."), + ColumnType::Point => unimplemented!("Point is not available in MySQL."), } ) .unwrap(); diff --git a/src/backend/postgres/table.rs b/src/backend/postgres/table.rs index bf2f5c22f..1a9b13d05 100644 --- a/src/backend/postgres/table.rs +++ b/src/backend/postgres/table.rs @@ -82,6 +82,7 @@ impl TableBuilder for PostgresQueryBuilder { ColumnType::MacAddr => "macaddr".into(), ColumnType::Year => unimplemented!("Year is not available in Postgres."), ColumnType::LTree => "ltree".into(), + ColumnType::Point => "point".into(), } ) .unwrap() diff --git a/src/backend/query_builder.rs b/src/backend/query_builder.rs index 7709f4456..ef0463011 100644 --- a/src/backend/query_builder.rs +++ b/src/backend/query_builder.rs @@ -1110,6 +1110,8 @@ pub trait QueryBuilder: Value::Array(_, None) => write!(s, "NULL").unwrap(), #[cfg(feature = "postgres-vector")] Value::Vector(None) => write!(s, "NULL").unwrap(), + #[cfg(feature = "with-postgres-point")] + Value::Point(None) => s.write_str("NULL").unwrap(), Value::Bool(Some(b)) => write!(s, "{}", if *b { "TRUE" } else { "FALSE" }).unwrap(), Value::TinyInt(Some(v)) => write!(s, "{v}").unwrap(), Value::SmallInt(Some(v)) => write!(s, "{v}").unwrap(), @@ -1204,6 +1206,14 @@ pub trait QueryBuilder: Value::IpNetwork(Some(v)) => write!(s, "'{v}'").unwrap(), #[cfg(feature = "with-mac_address")] Value::MacAddress(Some(v)) => write!(s, "'{v}'").unwrap(), + #[cfg(feature = "with-postgres-point")] + Value::Point(Some(v)) => { + s.write_str("'(").unwrap(); + write!(s, "{}", v.x()).unwrap(); + s.write_str(",").unwrap(); + write!(s, "{}", v.y()).unwrap(); + s.write_str(")'").unwrap(); + } }; s } diff --git a/src/backend/sqlite/table.rs b/src/backend/sqlite/table.rs index 429a4572e..f942a99a4 100644 --- a/src/backend/sqlite/table.rs +++ b/src/backend/sqlite/table.rs @@ -192,6 +192,7 @@ impl SqliteQueryBuilder { ColumnType::Bit(_) => unimplemented!("Bit is not available in Sqlite."), ColumnType::VarBit(_) => unimplemented!("VarBit is not available in Sqlite."), ColumnType::LTree => unimplemented!("LTree is not available in Sqlite."), + ColumnType::Point => unimplemented!("Point is not available in Sqlite."), } ) .unwrap() diff --git a/src/table/column.rs b/src/table/column.rs index 97677c524..05d833d86 100644 --- a/src/table/column.rs +++ b/src/table/column.rs @@ -57,6 +57,7 @@ pub trait IntoColumnDef { /// | Inet | N/A | inet | N/A | /// | MacAddr | N/A | macaddr | N/A | /// | LTree | N/A | ltree | N/A | +/// | Point | N/A | point | N/A | #[non_exhaustive] #[derive(Debug, Clone)] pub enum ColumnType { @@ -102,6 +103,7 @@ pub enum ColumnType { Inet, MacAddr, LTree, + Point, } /// Length for var-char/binary; default to 255 diff --git a/src/value.rs b/src/value.rs index f4ba0b8cb..d35271a4e 100644 --- a/src/value.rs +++ b/src/value.rs @@ -31,8 +31,15 @@ use std::net::IpAddr; #[cfg(feature = "with-mac_address")] use mac_address::MacAddress; +#[cfg(feature = "with-postgres-point")] +use geo_types::Point; + use crate::{ColumnType, CommonSqlQueryBuilder, QueryBuilder, StringLen}; +#[cfg(feature = "with-postgres-point")] +#[cfg_attr(docsrs, doc(cfg(feature = "with-postgres-point")))] +mod with_postgres_point; + /// [`Value`] types variant for Postgres array #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub enum ArrayType { @@ -213,6 +220,9 @@ pub enum Value { #[cfg(feature = "with-mac_address")] #[cfg_attr(docsrs, doc(cfg(feature = "with-mac_address")))] MacAddress(Option>), + #[cfg(feature = "with-postgres-point")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-postgres-point")))] + Point(Option>), } impl std::fmt::Display for Value { @@ -400,6 +410,10 @@ impl Value { #[cfg(feature = "with-mac_address")] #[cfg_attr(docsrs, doc(cfg(feature = "with-mac_address")))] Self::MacAddress(_) => Self::MacAddress(None), + + #[cfg(feature = "with-postgres-point")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-postgres-point")))] + Self::Point(_) => Self::Point(None), } } @@ -504,6 +518,10 @@ impl Value { #[cfg(feature = "with-mac_address")] #[cfg_attr(docsrs, doc(cfg(feature = "with-mac_address")))] Self::MacAddress(_) => Self::MacAddress(Some(Default::default())), + + #[cfg(feature = "with-postgres-point")] + #[cfg_attr(docsrs, doc(cfg(feature = "with-postgres-point")))] + Self::Point(_) => Self::Point(Some(Box::new(Point::default()))), } } } diff --git a/src/value/with_postgres_point.rs b/src/value/with_postgres_point.rs new file mode 100644 index 000000000..e29b4a36f --- /dev/null +++ b/src/value/with_postgres_point.rs @@ -0,0 +1,36 @@ +use super::*; + +pub use geo_types::Point; + +impl From for Value { + fn from(x: Point) -> Value { + Value::Point(Some(Box::new(x))) + } +} + +impl Nullable for Point { + fn null() -> Value { + Value::Point(None) + } +} + +impl ValueType for Point { + fn try_from(v: Value) -> Result { + match v { + Value::Point(Some(x)) => Ok(*x), + _ => Err(ValueTypeErr), + } + } + + fn type_name() -> String { + stringify!(Point).to_owned() + } + + fn array_type() -> ArrayType { + unimplemented!() + } + + fn column_type() -> ColumnType { + ColumnType::Point + } +}