Skip to content

Commit

Permalink
Add support for geo::Coordinate<f64> for Postgres
Browse files Browse the repository at this point in the history
  • Loading branch information
qtbeee committed Apr 17, 2020
1 parent 2fb38dd commit 76b07a8
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 1 deletion.
38 changes: 38 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ tls = [ "sqlx-core/tls" ]
# intended mainly for CI and docs
all = [ "tls", "all-database", "all-type" ]
all-database = [ "mysql", "sqlite", "postgres" ]
all-type = [ "bigdecimal", "json", "time", "chrono", "ipnetwork", "uuid" ]
all-type = [ "bigdecimal", "json", "time", "chrono", "ipnetwork", "uuid", "geo" ]

# runtime
runtime-async-std = [ "sqlx-core/runtime-async-std", "sqlx-macros/runtime-async-std" ]
Expand All @@ -60,6 +60,7 @@ ipnetwork = [ "sqlx-core/ipnetwork", "sqlx-macros/ipnetwork" ]
uuid = [ "sqlx-core/uuid", "sqlx-macros/uuid" ]
json = [ "sqlx-core/json", "sqlx-macros/json" ]
time = [ "sqlx-core/time", "sqlx-macros/time" ]
geo = [ "sqlx-core/geo", "sqlx-macros/geo" ]

[dependencies]
sqlx-core = { version = "0.3.4", path = "sqlx-core", default-features = false }
Expand Down
1 change: 1 addition & 0 deletions sqlx-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ futures-channel = { version = "0.3.4", default-features = false, features = [ "a
futures-core = { version = "0.3.4", default-features = false }
futures-util = { version = "0.3.4", default-features = false }
generic-array = { version = "0.12.3", default-features = false, optional = true }
geo = { version = "0.13.0", optional = true }
hex = "0.4.2"
hmac = { version = "0.7.1", default-features = false, optional = true }
ipnetwork = { version = "0.16.0", default-features = false, optional = true }
Expand Down
18 changes: 18 additions & 0 deletions sqlx-core/src/io/buf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,12 @@ pub trait Buf<'a> {

fn get_u32<T: ByteOrder>(&mut self) -> io::Result<u32>;

fn get_f32<T: ByteOrder>(&mut self) -> io::Result<f32>;

fn get_u64<T: ByteOrder>(&mut self) -> io::Result<u64>;

fn get_f64<T: ByteOrder>(&mut self) -> io::Result<f64>;

fn get_str(&mut self, len: usize) -> io::Result<&'a str>;

fn get_str_nul(&mut self) -> io::Result<&'a str>;
Expand Down Expand Up @@ -100,13 +104,27 @@ impl<'a> Buf<'a> for &'a [u8] {
Ok(val)
}

fn get_f32<T: ByteOrder>(&mut self) -> io::Result<f32> {
let val = T::read_f32(*self);
self.advance(4);

Ok(val)
}

fn get_u64<T: ByteOrder>(&mut self) -> io::Result<u64> {
let val = T::read_u64(*self);
self.advance(8);

Ok(val)
}

fn get_f64<T: ByteOrder>(&mut self) -> io::Result<f64> {
let val = T::read_f64(*self);
self.advance(8);

Ok(val)
}

fn get_str(&mut self, len: usize) -> io::Result<&'a str> {
str::from_utf8(self.get_bytes(len)?)
.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))
Expand Down
16 changes: 16 additions & 0 deletions sqlx-core/src/io/buf_mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@ pub trait BufMut {

fn put_u32<T: ByteOrder>(&mut self, val: u32);

fn put_f32<T: ByteOrder>(&mut self, val: f32);

fn put_u64<T: ByteOrder>(&mut self, val: u64);

fn put_f64<T: ByteOrder>(&mut self, val: f64);

fn put_bytes(&mut self, val: &[u8]);

fn put_str(&mut self, val: &str);
Expand Down Expand Up @@ -64,12 +68,24 @@ impl BufMut for Vec<u8> {
self.extend_from_slice(&buf);
}

fn put_f32<T: ByteOrder>(&mut self, val: f32) {
let mut buf = [0; 4];
T::write_f32(&mut buf, val);
self.extend_from_slice(&buf);
}

fn put_u64<T: ByteOrder>(&mut self, val: u64) {
let mut buf = [0; 8];
T::write_u64(&mut buf, val);
self.extend_from_slice(&buf);
}

fn put_f64<T: ByteOrder>(&mut self, val: f64) {
let mut buf = [0; 8];
T::write_f64(&mut buf, val);
self.extend_from_slice(&buf);
}

fn put_bytes(&mut self, val: &[u8]) {
self.extend_from_slice(val);
}
Expand Down
4 changes: 4 additions & 0 deletions sqlx-core/src/postgres/protocol/type_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ impl TypeId {
pub(crate) const CIDR: TypeId = TypeId(650);
pub(crate) const INET: TypeId = TypeId(869);

pub(crate) const POINT: TypeId = TypeId(600);

// Arrays

pub(crate) const ARRAY_BOOL: TypeId = TypeId(1000);
Expand Down Expand Up @@ -80,6 +82,8 @@ impl TypeId {

pub(crate) const ARRAY_CIDR: TypeId = TypeId(651);
pub(crate) const ARRAY_INET: TypeId = TypeId(1041);

pub(crate) const ARRAY_POINT: TypeId = TypeId(1017);

// JSON

Expand Down
60 changes: 60 additions & 0 deletions sqlx-core/src/postgres/types/geo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use crate::decode::Decode;
use crate::encode::Encode;
use crate::types::Type;
use crate::postgres::protocol::TypeId;
use crate::postgres::{ PgData, PgValue, PgRawBuffer, PgTypeInfo, Postgres };
use crate::io::Buf;
use std::mem;
use geo::Coordinate;
use byteorder::BigEndian;

// <https://www.postgresql.org/docs/12/datatype-geometric.html>

impl Type<Postgres> for Coordinate<f64> {
fn type_info() -> PgTypeInfo {
PgTypeInfo::new(TypeId::POINT, "POINT")
}
}

impl<'de> Decode<'de, Postgres> for Coordinate<f64> {
fn decode(value: PgValue<'de>) -> crate::Result<Self> {
match value.try_get()? {
PgData::Binary(mut buf) => {
let x = buf.get_f64::<BigEndian>()?;
println!("we have what is hopefully x: {}", x);

let y = buf.get_f64::<BigEndian>()?;
println!("is this a y? {}", y);

Ok((x, y).into())
}

PgData::Text(s) => {
let parens: &[_] = &['(', ')'];
let mut s = s.trim_matches(parens).split(',');

match (s.next(), s.next()) {
(Some(x), Some(y)) => {
let x = x.parse().map_err(crate::Error::decode)?;
let y = y.parse().map_err(crate::Error::decode)?;

Ok((x, y).into())
}

_ => Err(crate::Error::Decode(format!("expecting a value with the format \"(x,y)\"").into()))
}
}
}
}
}

impl Encode<Postgres> for Coordinate<f64> {
fn encode(&self, buf: &mut PgRawBuffer) {
Encode::<Postgres>::encode(&self.x, buf);
Encode::<Postgres>::encode(&self.y, buf);
}

fn size_hint(&self) -> usize {
2 * mem::size_of::<f64>()
}
}
15 changes: 15 additions & 0 deletions sqlx-core/src/postgres/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,18 @@
//! [`Json<T>`] can be used for structured JSON data with Postgres.
//!
//! [`Json<T>`]: crate::types::Json
//!
//! ### [`geo`](https://crates.io/crates/geo)
//!
//! Requires the `geo` Cargo feature flag.
//!
//! | Rust type | Postgres type(s) |
//! |---------------------------------------|------------------------------------------------------|
//! | `geo::Coordinate<f64>` | POINT |
//! | `geo::Line<f64>` | LINE, LSEG |
//! | `geo::Rect<f64>` | BOX |
//! | `geo::LineString<f64>` | PATH |
//! | `geo::Polygon<f64>` | POLYGON |
//!
//! # [Composite types](https://www.postgresql.org/docs/current/rowtypes.html)
//!
Expand Down Expand Up @@ -162,6 +174,9 @@ mod json;
#[cfg(feature = "ipnetwork")]
mod ipnetwork;

#[cfg(feature = "geo")]
mod geo;

// Implement `Decode` for all postgres types
// The concept of a nullable `RawValue` is db-specific
// `Type` is implemented generically at src/types.rs
Expand Down
1 change: 1 addition & 0 deletions sqlx-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ time = [ "sqlx/time" ]
ipnetwork = [ "sqlx/ipnetwork" ]
uuid = [ "sqlx/uuid" ]
json = [ "sqlx/json", "serde_json" ]
geo = [ "sqlx/geo" ]

[dependencies]
async-std = { version = "1.5.0", default-features = false, optional = true }
Expand Down

0 comments on commit 76b07a8

Please sign in to comment.