diff --git a/edgedb-protocol/src/codec.rs b/edgedb-protocol/src/codec.rs index 7eddbe10..e2c3154e 100644 --- a/edgedb-protocol/src/codec.rs +++ b/edgedb-protocol/src/codec.rs @@ -49,6 +49,10 @@ pub const STD_PG_TIMESTAMPTZ: UuidVal = UuidVal::from_u128(0x1000002); pub const STD_PG_TIMESTAMP: UuidVal = UuidVal::from_u128(0x1000003); pub const STD_PG_DATE: UuidVal = UuidVal::from_u128(0x1000004); pub const STD_PG_INTERVAL: UuidVal = UuidVal::from_u128(0x1000005); +pub const POSTGIS_GEOMETRY: UuidVal = UuidVal::from_u128(0x44c901c0_d922_4894_83c8_061bd05e4840); +pub const POSTGIS_GEOGRAPHY: UuidVal = UuidVal::from_u128(0x4d738878_3a5f_4821_ab76_9d8e7d6b32c4); +pub const POSTGIS_BOX_2D: UuidVal = UuidVal::from_u128(0x7fae5536_6311_4f60_8eb9_096a5d972f48); +pub const POSTGIS_BOX_3D: UuidVal = UuidVal::from_u128(0xc1a50ff8_fded_48b0_85c2_4905a8481433); pub(crate) fn uuid_to_known_name(uuid: &UuidVal) -> Option<&'static str> { match *uuid { @@ -78,6 +82,10 @@ pub(crate) fn uuid_to_known_name(uuid: &UuidVal) -> Option<&'static str> { STD_PG_TIMESTAMP => Some("BaseScalar(std::pg::timestamp)"), STD_PG_DATE => Some("BaseScalar(std::pg::date)"), STD_PG_INTERVAL => Some("BaseScalar(std::pg::interval)"), + POSTGIS_GEOMETRY => Some("BaseScalar(ext::postgis::geometry)"), + POSTGIS_GEOGRAPHY => Some("BaseScalar(ext::postgis::geography)"), + POSTGIS_BOX_2D => Some("BaseScalar(ext::postgis::box2d)"), + POSTGIS_BOX_3D => Some("BaseScalar(ext::postgis::box3d)"), _ => None, } } @@ -257,6 +265,18 @@ pub struct Enum { members: HashSet>, } +#[derive(Debug)] +pub struct PostGisGeometry {} + +#[derive(Debug)] +pub struct PostGisGeography {} + +#[derive(Debug)] +pub struct PostGisBox2d {} + +#[derive(Debug)] +pub struct PostGisBox3d {} + struct CodecBuilder<'a> { descriptors: &'a [Descriptor], } @@ -376,6 +396,10 @@ pub fn scalar_codec(uuid: &UuidVal) -> Result, CodecError> { STD_PG_TIMESTAMP => Ok(Arc::new(LocalDatetime {})), STD_PG_DATE => Ok(Arc::new(LocalDate {})), STD_PG_INTERVAL => Ok(Arc::new(RelativeDuration {})), + POSTGIS_GEOMETRY => Ok(Arc::new(PostGisGeometry {})), + POSTGIS_GEOGRAPHY => Ok(Arc::new(PostGisGeography {})), + POSTGIS_BOX_2D => Ok(Arc::new(PostGisBox2d {})), + POSTGIS_BOX_3D => Ok(Arc::new(PostGisBox3d {})), _ => errors::UndefinedBaseScalar { uuid: uuid.clone() }.fail()?, } } @@ -1484,3 +1508,59 @@ impl Codec for Enum { Ok(()) } } + +impl Codec for PostGisGeometry { + fn decode(&self, buf: &[u8]) -> Result { + RawCodec::decode(buf).map(Value::PostGisGeometry) + } + fn encode(&self, buf: &mut BytesMut, val: &Value) -> Result<(), EncodeError> { + let val = match val { + Value::PostGisGeometry(val) => val, + _ => Err(errors::invalid_value(type_name::(), val))?, + }; + buf.extend(val); + Ok(()) + } +} + +impl Codec for PostGisGeography { + fn decode(&self, buf: &[u8]) -> Result { + RawCodec::decode(buf).map(Value::PostGisGeography) + } + fn encode(&self, buf: &mut BytesMut, val: &Value) -> Result<(), EncodeError> { + let val = match val { + Value::PostGisGeography(val) => val, + _ => Err(errors::invalid_value(type_name::(), val))?, + }; + buf.extend(val); + Ok(()) + } +} + +impl Codec for PostGisBox2d { + fn decode(&self, buf: &[u8]) -> Result { + RawCodec::decode(buf).map(Value::PostGisBox2d) + } + fn encode(&self, buf: &mut BytesMut, val: &Value) -> Result<(), EncodeError> { + let val = match val { + Value::PostGisBox2d(val) => val, + _ => Err(errors::invalid_value(type_name::(), val))?, + }; + buf.extend(val); + Ok(()) + } +} + +impl Codec for PostGisBox3d { + fn decode(&self, buf: &[u8]) -> Result { + RawCodec::decode(buf).map(Value::PostGisBox3d) + } + fn encode(&self, buf: &mut BytesMut, val: &Value) -> Result<(), EncodeError> { + let val = match val { + Value::PostGisBox3d(val) => val, + _ => Err(errors::invalid_value(type_name::(), val))?, + }; + buf.extend(val); + Ok(()) + } +} diff --git a/edgedb-protocol/src/query_arg.rs b/edgedb-protocol/src/query_arg.rs index 1ed5fb3c..6869e694 100644 --- a/edgedb-protocol/src/query_arg.rs +++ b/edgedb-protocol/src/query_arg.rs @@ -203,6 +203,10 @@ impl QueryArg for Value { Enum(v) => v.encode_slot(enc)?, Range(v) => v.encode_slot(enc)?, Vector(v) => v.encode_slot(enc)?, + PostGisGeometry(v) => v.encode_slot(enc)?, + PostGisGeography(v) => v.encode_slot(enc)?, + PostGisBox2d(v) => v.encode_slot(enc)?, + PostGisBox3d(v) => v.encode_slot(enc)?, } Ok(()) @@ -238,6 +242,10 @@ impl QueryArg for Value { let val = val.deref(); check_enum(val, &members) } + (PostGisGeometry(_), BaseScalar(d)) if d.id == codec::POSTGIS_GEOMETRY => Ok(()), + (PostGisGeography(_), BaseScalar(d)) if d.id == codec::POSTGIS_GEOGRAPHY => Ok(()), + (PostGisBox2d(_), BaseScalar(d)) if d.id == codec::POSTGIS_BOX_2D => Ok(()), + (PostGisBox3d(_), BaseScalar(d)) if d.id == codec::POSTGIS_BOX_3D => Ok(()), // TODO(tailhook) all types (_, desc) => Err(ctx.wrong_type(&desc, self.kind())), } diff --git a/edgedb-protocol/src/value.rs b/edgedb-protocol/src/value.rs index b1548412..2501cc53 100644 --- a/edgedb-protocol/src/value.rs +++ b/edgedb-protocol/src/value.rs @@ -46,6 +46,10 @@ pub enum Value { Vector(Vec), Enum(EnumValue), Range(Range>), + PostGisGeometry(bytes::Bytes), + PostGisGeography(bytes::Bytes), + PostGisBox2d(bytes::Bytes), + PostGisBox3d(bytes::Bytes), } #[derive(Clone, Debug)] @@ -88,6 +92,10 @@ impl Value { Tuple(..) => "tuple", Uuid(..) => "uuid", Vector(..) => "ext::pgvector::vector", + PostGisGeometry(..) => "ext::postgis::geometry", + PostGisGeography(..) => "ext::postgis::geography", + PostGisBox2d(..) => "ext::postgis::box2d", + PostGisBox3d(..) => "ext::postgis::box3d", } } pub fn empty_tuple() -> Value { diff --git a/edgedb-protocol/tests/codecs.rs b/edgedb-protocol/tests/codecs.rs index a3f9bf92..53a2e975 100644 --- a/edgedb-protocol/tests/codecs.rs +++ b/edgedb-protocol/tests/codecs.rs @@ -4,7 +4,7 @@ extern crate pretty_assertions; use std::error::Error; use std::sync::Arc; -use bytes::Bytes; +use bytes::{Buf, Bytes}; use edgedb_protocol::codec::build_codec; use edgedb_protocol::codec::{Codec, ObjectShape}; @@ -1201,3 +1201,230 @@ fn multi_range() -> Result<(), Box> { ); Ok(()) } + +#[test] +fn postgis_geometry() -> Result<(), Box> { + let codec = build_codec( + Some(TypePos(0)), + &[Descriptor::BaseScalar(BaseScalarTypeDescriptor { + id: "44c901c0-d922-4894-83c8-061bd05e4840" + .parse::()? + .into(), + })], + )?; + + encoding_eq!( + &codec, + /* + * Point + * 01 - byteOrder, Little Endian + * 01000000 - wkbType, WKBPoint + * 0000000000000040 - x, 2.0 + * 000000000000F03F - y, 1.0 + */ + b"\ + \x01\ + \x01\x00\x00\x00\ + \x00\x00\x00\x00\x00\x00\x00\x40\ + \x00\x00\x00\x00\x00\x00\xF0\x3F\ + ", + Value::PostGisGeometry( + b"\ + \x01\ + \x01\x00\x00\x00\ + \x00\x00\x00\x00\x00\x00\x00\x40\ + \x00\x00\x00\x00\x00\x00\xF0\x3F\ + "[..] + .into() + ) + ); + Ok(()) +} + +#[test] +fn postgis_geography() -> Result<(), Box> { + let codec = build_codec( + Some(TypePos(0)), + &[Descriptor::BaseScalar(BaseScalarTypeDescriptor { + id: "4d738878-3a5f-4821-ab76-9d8e7d6b32c4" + .parse::()? + .into(), + })], + )?; + encoding_eq!( + &codec, + /* + * Point + * 01 - byteOrder, Little Endian + * 01000000 - wkbType, WKBPoint + * 0000000000000040 - x, 2.0 + * 000000000000F03F - y, 1.0 + */ + b"\ + \x01\ + \x01\x00\x00\x00\ + \x00\x00\x00\x00\x00\x00\x00\x40\ + \x00\x00\x00\x00\x00\x00\xF0\x3F\ + ", + Value::PostGisGeometry( + b"\ + \x01\ + \x01\x00\x00\x00\ + \x00\x00\x00\x00\x00\x00\x00\x40\ + \x00\x00\x00\x00\x00\x00\xF0\x3F\ + "[..] + .into() + ) + ); + Ok(()) +} + +#[test] +fn postgis_box_2d() -> Result<(), Box> { + let codec = build_codec( + Some(TypePos(0)), + &[Descriptor::BaseScalar(BaseScalarTypeDescriptor { + id: "7fae5536-6311-4f60-8eb9-096a5d972f48" + .parse::()? + .into(), + })], + )?; + encoding_eq!( + &codec, + /* + * Polygon + * 01 - byteOrder, Little Endian + * 03000000 - wkbType, wkbPolygon + * 01000000 - numRings, 1 + * 05000000 - numPoints, 5 + * 000000000000F03F - x, 1.0 + * 000000000000F03F - y, 1.0 + * 0000000000000040 - x, 2.0 + * 000000000000F03F - y, 1.0 + * 0000000000000040 - x, 2.0 + * 0000000000000040 - y, 2.0 + * 000000000000F03F - x, 1.0 + * 0000000000000040 - y, 2.0 + * 000000000000F03F - x, 1.0 + * 000000000000F03F - y, 1.0 + */ + b"\ + \x01\ + \x03\x00\x00\x00\ + \x01\x00\x00\x00\ + \x05\x00\x00\x00\ + \x00\x00\x00\x00\x00\x00\xF0\x3F\ + \x00\x00\x00\x00\x00\x00\xF0\x3F\ + \x00\x00\x00\x00\x00\x00\x00\x40\ + \x00\x00\x00\x00\x00\x00\xF0\x3F\ + \x00\x00\x00\x00\x00\x00\x00\x40\ + \x00\x00\x00\x00\x00\x00\x00\x40\ + \x00\x00\x00\x00\x00\x00\xF0\x3F\ + \x00\x00\x00\x00\x00\x00\x00\x40\ + \x00\x00\x00\x00\x00\x00\xF0\x3F\ + \x00\x00\x00\x00\x00\x00\xF0\x3F\ + ", + Value::PostGisBox2d( + b"\ + \x01\ + \x03\x00\x00\x00\ + \x01\x00\x00\x00\ + \x05\x00\x00\x00\ + \x00\x00\x00\x00\x00\x00\xF0\x3F\ + \x00\x00\x00\x00\x00\x00\xF0\x3F\ + \x00\x00\x00\x00\x00\x00\x00\x40\ + \x00\x00\x00\x00\x00\x00\xF0\x3F\ + \x00\x00\x00\x00\x00\x00\x00\x40\ + \x00\x00\x00\x00\x00\x00\x00\x40\ + \x00\x00\x00\x00\x00\x00\xF0\x3F\ + \x00\x00\x00\x00\x00\x00\x00\x40\ + \x00\x00\x00\x00\x00\x00\xF0\x3F\ + \x00\x00\x00\x00\x00\x00\xF0\x3F\ + "[..] + .into() + ) + ); + Ok(()) +} + +#[test] +fn postgis_box_3d() -> Result<(), Box> { + let codec = build_codec( + Some(TypePos(0)), + &[Descriptor::BaseScalar(BaseScalarTypeDescriptor { + id: "c1a50ff8-fded-48b0-85c2-4905a8481433" + .parse::()? + .into(), + })], + )?; + encoding_eq!( + &codec, + /* + * Polygon + * 01 - byteOrder, Little Endian + * 03000080 - wkbType, wkbPolygonZ + * 01000000 - numRings, 1 + * 05000000 - numPoints, 5 + * 000000000000F03F - x, 1.0 + * 000000000000F03F - y, 1.0 + * 0000000000000000 - z, 0.0 + * 0000000000000040 - x, 2.0 + * 000000000000F03F - y, 1.0 + * 0000000000000000 - z, 0.0 + * 0000000000000040 - x, 2.0 + * 0000000000000040 - y, 2.0 + * 000000000000F03F - x, 1.0 + * 0000000000000000 - z, 0.0 + * 0000000000000040 - y, 2.0 + * 000000000000F03F - x, 1.0 + * 000000000000F03F - y, 1.0 + * 0000000000000000 - z, 0.0 + */ + b"\ + \x01\ + \x03\x00\x00\x80\ + \x01\x00\x00\x00\ + \x05\x00\x00\x00\ + \x00\x00\x00\x00\x00\x00\xF0\x3F\ + \x00\x00\x00\x00\x00\x00\xF0\x3F\ + \x00\x00\x00\x00\x00\x00\x00\x00\ + \x00\x00\x00\x00\x00\x00\x00\x40\ + \x00\x00\x00\x00\x00\x00\xF0\x3F\ + \x00\x00\x00\x00\x00\x00\x00\x00\ + \x00\x00\x00\x00\x00\x00\x00\x40\ + \x00\x00\x00\x00\x00\x00\x00\x40\ + \x00\x00\x00\x00\x00\x00\x00\x00\ + \x00\x00\x00\x00\x00\x00\xF0\x3F\ + \x00\x00\x00\x00\x00\x00\x00\x40\ + \x00\x00\x00\x00\x00\x00\x00\x00\ + \x00\x00\x00\x00\x00\x00\xF0\x3F\ + \x00\x00\x00\x00\x00\x00\xF0\x3F\ + \x00\x00\x00\x00\x00\x00\x00\x00\ + ", + Value::PostGisBox3d( + b"\ + \x01\ + \x03\x00\x00\x80\ + \x01\x00\x00\x00\ + \x05\x00\x00\x00\ + \x00\x00\x00\x00\x00\x00\xF0\x3F\ + \x00\x00\x00\x00\x00\x00\xF0\x3F\ + \x00\x00\x00\x00\x00\x00\x00\x00\ + \x00\x00\x00\x00\x00\x00\x00\x40\ + \x00\x00\x00\x00\x00\x00\xF0\x3F\ + \x00\x00\x00\x00\x00\x00\x00\x00\ + \x00\x00\x00\x00\x00\x00\x00\x40\ + \x00\x00\x00\x00\x00\x00\x00\x40\ + \x00\x00\x00\x00\x00\x00\x00\x00\ + \x00\x00\x00\x00\x00\x00\xF0\x3F\ + \x00\x00\x00\x00\x00\x00\x00\x40\ + \x00\x00\x00\x00\x00\x00\x00\x00\ + \x00\x00\x00\x00\x00\x00\xF0\x3F\ + \x00\x00\x00\x00\x00\x00\xF0\x3F\ + \x00\x00\x00\x00\x00\x00\x00\x00\ + "[..] + .into() + ) + ); + Ok(()) +}