-
Notifications
You must be signed in to change notification settings - Fork 1.5k
feat: support geometry #3449
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
jayy-lmao
wants to merge
64
commits into
launchbadge:main
from
jayy-lmao:feat/support-geometry-postgres
Closed
feat: support geometry #3449
Changes from 48 commits
Commits
Show all changes
64 commits
Select commit
Hold shift + click to select a range
4248c94
fix: clean branch
jayy-lmao d16c98e
feat: points and boxes
jayy-lmao 1c5387c
test: geo array test support
jayy-lmao d13fc3c
test: path tests
jayy-lmao 01ea1fa
fix: imports
jayy-lmao 1a64ebb
fix: impl point
jayy-lmao 98dfbdf
fix: update point
jayy-lmao a69378c
fix: line and line seg
jayy-lmao 845cadd
fix: box
jayy-lmao 0993ef0
fix: path
jayy-lmao 4e58b17
fix: path header length
jayy-lmao ffe6103
fix: line segment
jayy-lmao 1154397
fix: split not just 4 for path
jayy-lmao 409a50b
fix: test for boxes
jayy-lmao a5c7c00
fix: path link
jayy-lmao 91ecaa1
fix: pg box name in tests
jayy-lmao 2494a00
fix: syntax for box tests
jayy-lmao 28b4a99
fix: polygon tests
jayy-lmao d67ddf5
fix: tests for paths and integration tests for polygons
jayy-lmao a9b0d57
docs: copy in some postgres docs
jayy-lmao 4315174
docs: remove duplicate docs
jayy-lmao 6e535e9
fix: polygon in docs
jayy-lmao afb00b5
docs: align md table
jayy-lmao c2f652f
docs: remove unused comments
jayy-lmao 0a34651
test: make box ordering test explicit
jayy-lmao a1c8a64
feat: add circle
jayy-lmao b9ea489
test: circle tests
jayy-lmao 5def297
test: import pgpoint to avoid repeat nesting
jayy-lmao 9be8dd6
tests: polygon integ and circle unit
jayy-lmao 1ddb3e9
fix: update sqlx-postgres/src/types/geometry/circle.rs
jayy-lmao 2ea7d16
fix: circle
jayy-lmao c538aa4
test: fix type tests for polygon
jayy-lmao 92a4bdb
fix: circle from bytes
jayy-lmao 305e95c
fix: remove double decodes
jayy-lmao 2d28514
fix: circle test
jayy-lmao fc6f3f3
test: boxes sort
jayy-lmao 0662628
docs: update doc headerws
jayy-lmao 65184f7
test: invalid box syntax
jayy-lmao 5a165df
fix: circle syntax
jayy-lmao 95ec809
test: box ordering in array test
jayy-lmao 24486e2
docs: remove output comments
jayy-lmao d3082c8
fix: usize max in max lengths
jayy-lmao 8207d89
fix: handle uneven number of points
jayy-lmao 4dd55c3
fix: capacity
jayy-lmao 8ec8142
fix: order of operations
jayy-lmao 99d8368
fix: uneven pairs in polygons
jayy-lmao 2ebb0fb
fix: throw on uneven points
jayy-lmao cffcec9
fix: unmatched pair errors
jayy-lmao 341650c
fix: box dyn errors for from strings
jayy-lmao d559b07
fix: remove unused error imports
jayy-lmao a3c25f3
fix: radius variable circle
jayy-lmao 5a9a6f6
fix: path import
jayy-lmao 815c22c
fix: splitn str
jayy-lmao 0b49de3
test: more circle tests
jayy-lmao bf0d599
test: lines
jayy-lmao 782005e
fix: splits
jayy-lmao e343924
fix: splits
jayy-lmao 79f18f1
fix: lower upper with box
jayy-lmao e67b195
fix: circle too many points
jayy-lmao bbda7d0
fix: line too many points
jayy-lmao 89fd0b0
fix: too many
jayy-lmao 896f44a
fix: variable names for box and lseg
jayy-lmao 5e36b65
test: circle test radius
jayy-lmao 963d1d4
test: fix upper right y
jayy-lmao File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,277 @@ | ||
use crate::decode::Decode; | ||
use crate::encode::{Encode, IsNull}; | ||
use crate::error::BoxDynError; | ||
use crate::types::Type; | ||
use crate::{PgArgumentBuffer, PgHasArrayType, PgTypeInfo, PgValueFormat, PgValueRef, Postgres}; | ||
use sqlx_core::bytes::Buf; | ||
use sqlx_core::Error; | ||
use std::str::FromStr; | ||
|
||
const ERROR: &str = "error decoding BOX"; | ||
|
||
/// ## Postgres Geometric Box type | ||
/// | ||
/// Description: Rectangular box | ||
/// Representation: `((x1,y1),(x2,y2))` | ||
/// | ||
/// Boxes are represented by pairs of points that are opposite corners of the box. Values of type box are specified using any of the following syntaxes: | ||
/// | ||
/// ```text | ||
/// ( ( x1 , y1 ) , ( x2 , y2 ) ) | ||
/// ( x1 , y1 ) , ( x2 , y2 ) | ||
/// x1 , y1 , x2 , y2 | ||
/// ``` | ||
/// where `(x1,y1) and (x2,y2)` are any two opposite corners of the box. | ||
/// Any two opposite corners can be supplied on input, but the values will be reordered as needed to store the upper right and lower left corners, in that order. | ||
/// | ||
/// See https://www.postgresql.org/docs/16/datatype-geometric.html#DATATYPE-GEOMETRIC-BOXES | ||
#[derive(Debug, Clone, PartialEq)] | ||
pub struct PgBox { | ||
pub x1: f64, | ||
pub y1: f64, | ||
pub x2: f64, | ||
pub y2: f64, | ||
} | ||
|
||
impl Type<Postgres> for PgBox { | ||
fn type_info() -> PgTypeInfo { | ||
PgTypeInfo::with_name("box") | ||
} | ||
} | ||
|
||
impl PgHasArrayType for PgBox { | ||
fn array_type_info() -> PgTypeInfo { | ||
PgTypeInfo::with_name("_box") | ||
} | ||
} | ||
|
||
impl<'r> Decode<'r, Postgres> for PgBox { | ||
fn decode(value: PgValueRef<'r>) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> { | ||
match value.format() { | ||
PgValueFormat::Text => Ok(PgBox::from_str(value.as_str()?)?), | ||
PgValueFormat::Binary => Ok(PgBox::from_bytes(value.as_bytes()?)?), | ||
} | ||
} | ||
} | ||
|
||
impl<'q> Encode<'q, Postgres> for PgBox { | ||
fn produces(&self) -> Option<PgTypeInfo> { | ||
Some(PgTypeInfo::with_name("box")) | ||
} | ||
|
||
fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> Result<IsNull, BoxDynError> { | ||
self.serialize(buf)?; | ||
Ok(IsNull::No) | ||
} | ||
} | ||
|
||
impl FromStr for PgBox { | ||
type Err = Error; | ||
|
||
fn from_str(s: &str) -> Result<Self, Self::Err> { | ||
let sanitised = s.replace(['(', ')', '[', ']', ' '], ""); | ||
let mut parts = sanitised.splitn(4, ","); | ||
|
||
let x1 = parts | ||
.next() | ||
.and_then(|s| s.parse::<f64>().ok()) | ||
.ok_or(Error::Decode( | ||
format!("{}: could not get x1 from {}", ERROR, s).into(), | ||
))?; | ||
|
||
let y1 = parts | ||
.next() | ||
.and_then(|s| s.parse::<f64>().ok()) | ||
.ok_or(Error::Decode( | ||
format!("{}: could not get y1 from {}", ERROR, s).into(), | ||
))?; | ||
|
||
let x2 = parts | ||
.next() | ||
.and_then(|s| s.parse::<f64>().ok()) | ||
.ok_or(Error::Decode( | ||
format!("{}: could not get x2 from {}", ERROR, s).into(), | ||
))?; | ||
|
||
let y2 = parts | ||
.next() | ||
.and_then(|s| s.parse::<f64>().ok()) | ||
.ok_or(Error::Decode( | ||
format!("{}: could not get y2 from {}", ERROR, s).into(), | ||
))?; | ||
|
||
Ok(PgBox { x1, y1, x2, y2 }) | ||
} | ||
} | ||
|
||
impl PgBox { | ||
fn from_bytes(mut bytes: &[u8]) -> Result<PgBox, BoxDynError> { | ||
let x1 = bytes.get_f64(); | ||
let y1 = bytes.get_f64(); | ||
let x2 = bytes.get_f64(); | ||
let y2 = bytes.get_f64(); | ||
|
||
Ok(PgBox { x1, y1, x2, y2 }) | ||
} | ||
|
||
fn serialize(&self, buff: &mut PgArgumentBuffer) -> Result<(), String> { | ||
let min_x = &self.x1.min(self.x2); | ||
let min_y = &self.y1.min(self.y2); | ||
let max_x = &self.x1.max(self.x2); | ||
let max_y = &self.y1.max(self.y2); | ||
|
||
buff.extend_from_slice(&max_x.to_be_bytes()); | ||
buff.extend_from_slice(&max_y.to_be_bytes()); | ||
buff.extend_from_slice(&min_x.to_be_bytes()); | ||
buff.extend_from_slice(&min_y.to_be_bytes()); | ||
|
||
Ok(()) | ||
} | ||
|
||
#[cfg(test)] | ||
fn serialize_to_vec(&self) -> Vec<u8> { | ||
let mut buff = PgArgumentBuffer::default(); | ||
self.serialize(&mut buff).unwrap(); | ||
buff.to_vec() | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod box_tests { | ||
|
||
use std::str::FromStr; | ||
|
||
use super::PgBox; | ||
|
||
const BOX_BYTES: &[u8] = &[ | ||
64, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 192, 0, 0, 0, | ||
0, 0, 0, 0, | ||
]; | ||
|
||
#[test] | ||
fn can_deserialise_box_type_bytes_in_order() { | ||
let pg_box = PgBox::from_bytes(BOX_BYTES).unwrap(); | ||
assert_eq!( | ||
pg_box, | ||
PgBox { | ||
x1: 2., | ||
y1: 2., | ||
x2: -2., | ||
y2: -2. | ||
} | ||
) | ||
} | ||
|
||
#[test] | ||
fn can_deserialise_box_type_str_first_syntax() { | ||
let pg_box = PgBox::from_str("[( 1, 2), (3, 4 )]").unwrap(); | ||
assert_eq!( | ||
pg_box, | ||
PgBox { | ||
x1: 1., | ||
y1: 2., | ||
x2: 3., | ||
y2: 4. | ||
} | ||
); | ||
} | ||
#[test] | ||
fn can_deserialise_box_type_str_second_syntax() { | ||
let pg_box = PgBox::from_str("(( 1, 2), (3, 4 ))").unwrap(); | ||
assert_eq!( | ||
pg_box, | ||
PgBox { | ||
x1: 1., | ||
y1: 2., | ||
x2: 3., | ||
y2: 4. | ||
} | ||
); | ||
} | ||
|
||
#[test] | ||
fn can_deserialise_box_type_str_third_syntax() { | ||
let pg_box = PgBox::from_str("(1, 2), (3, 4 )").unwrap(); | ||
assert_eq!( | ||
pg_box, | ||
PgBox { | ||
x1: 1., | ||
y1: 2., | ||
x2: 3., | ||
y2: 4. | ||
} | ||
); | ||
} | ||
|
||
#[test] | ||
fn can_deserialise_box_type_str_fourth_syntax() { | ||
let pg_box = PgBox::from_str("1, 2, 3, 4").unwrap(); | ||
assert_eq!( | ||
pg_box, | ||
PgBox { | ||
x1: 1., | ||
y1: 2., | ||
x2: 3., | ||
y2: 4. | ||
} | ||
); | ||
} | ||
|
||
#[test] | ||
fn can_deserialise_box_type_str_float() { | ||
let pg_box = PgBox::from_str("(1.1, 2.2), (3.3, 4.4)").unwrap(); | ||
assert_eq!( | ||
pg_box, | ||
PgBox { | ||
x1: 1.1, | ||
y1: 2.2, | ||
x2: 3.3, | ||
y2: 4.4 | ||
} | ||
); | ||
} | ||
|
||
#[test] | ||
fn can_serialise_box_type_in_order() { | ||
let pg_box = PgBox { | ||
x1: 2., | ||
x2: -2., | ||
y1: -2., | ||
y2: 2., | ||
}; | ||
assert_eq!(pg_box.serialize_to_vec(), BOX_BYTES,) | ||
} | ||
|
||
#[test] | ||
fn can_serialise_box_type_out_of_order() { | ||
let pg_box = PgBox { | ||
x1: -2., | ||
x2: 2., | ||
y1: 2., | ||
y2: -2., | ||
}; | ||
assert_eq!(pg_box.serialize_to_vec(), BOX_BYTES,) | ||
} | ||
|
||
#[test] | ||
fn can_order_box() { | ||
let pg_box = PgBox { | ||
x1: -2., | ||
x2: 2., | ||
y1: 2., | ||
y2: -2., | ||
}; | ||
let bytes = pg_box.serialize_to_vec(); | ||
|
||
let pg_box = PgBox::from_bytes(&bytes).unwrap(); | ||
assert_eq!( | ||
pg_box, | ||
PgBox { | ||
x1: 2., | ||
y1: 2., | ||
x2: -2., | ||
y2: -2. | ||
} | ||
) | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.