From aadf0b64e787c9e895b9cee08a701421cfdb39b2 Mon Sep 17 00:00:00 2001 From: Elvis Pranskevichus Date: Wed, 30 Oct 2024 15:42:53 -0700 Subject: [PATCH] Add support for protocol v2.0 (#354) This is mostly about adding support for the v2.0 type descriptor layout with some minor cleanups thrown in. --- edgedb-protocol/Cargo.toml | 2 +- edgedb-protocol/src/codec.rs | 59 ++- edgedb-protocol/src/descriptors.rs | 495 +++++++++++++++--- edgedb-protocol/src/encoding.rs | 22 + edgedb-protocol/src/errors.rs | 8 +- edgedb-protocol/src/features.rs | 5 +- edgedb-protocol/src/query_arg.rs | 13 +- .../serialization/decode/queryable/scalars.rs | 7 +- .../src/serialization/decode/raw_scalar.rs | 9 +- edgedb-protocol/src/value.rs | 13 +- edgedb-protocol/tests/codecs.rs | 48 +- edgedb-protocol/tests/server_messages.rs | 8 +- edgedb-protocol/tests/type_descriptors.rs | 222 +++++++- flake.lock | 18 +- 14 files changed, 789 insertions(+), 140 deletions(-) diff --git a/edgedb-protocol/Cargo.toml b/edgedb-protocol/Cargo.toml index 2d910893..a462a4ee 100644 --- a/edgedb-protocol/Cargo.toml +++ b/edgedb-protocol/Cargo.toml @@ -13,7 +13,7 @@ rust-version.workspace = true [dependencies] bytes = "1.5.0" -snafu = {version="0.8.0"} +snafu = {version="0.8.0", features=["backtrace"]} uuid = "1.1.2" num-bigint = {version="0.4.3", optional=true} num-traits = {version="0.2.10", optional=true} diff --git a/edgedb-protocol/src/codec.rs b/edgedb-protocol/src/codec.rs index d7107138..6180e506 100644 --- a/edgedb-protocol/src/codec.rs +++ b/edgedb-protocol/src/codec.rs @@ -98,6 +98,20 @@ pub struct ShapeElement { pub name: String, } +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct InputObjectShape(pub(crate) Arc); + +#[derive(Debug, PartialEq, Eq)] +pub struct InputObjectShapeInfo { + pub elements: Vec, +} + +#[derive(Debug, PartialEq, Eq)] +pub struct InputShapeElement { + pub cardinality: Option, + pub name: String, +} + #[derive(Debug, PartialEq, Eq)] pub struct NamedTupleShapeInfo { pub elements: Vec, @@ -179,7 +193,7 @@ pub struct Object { #[derive(Debug)] pub struct Input { - shape: ObjectShape, + shape: InputObjectShape, codecs: Vec>, } @@ -247,6 +261,19 @@ impl Deref for ObjectShape { } } +impl InputObjectShape { + pub fn new(elements: Vec) -> Self { + InputObjectShape(Arc::new(InputObjectShapeInfo { elements })) + } +} + +impl Deref for InputObjectShape { + type Target = InputObjectShapeInfo; + fn deref(&self) -> &InputObjectShapeInfo { + &self.0 + } +} + impl Deref for NamedTupleShape { type Target = NamedTupleShapeInfo; fn deref(&self) -> &NamedTupleShapeInfo { @@ -263,7 +290,10 @@ impl<'a> CodecBuilder<'a> { D::Set(d) => Ok(Arc::new(Set::build(d, self)?)), D::ObjectShape(d) => Ok(Arc::new(Object::build(d, self)?)), D::Scalar(d) => Ok(Arc::new(Scalar { - inner: self.build(d.base_type_pos)?, + inner: match d.base_type_pos { + Some(type_pos) => self.build(type_pos)?, + None => scalar_codec(&d.id)?, + }, })), D::Tuple(d) => Ok(Arc::new(Tuple::build(d, self)?)), D::NamedTuple(d) => Ok(Arc::new(NamedTuple::build(d, self)?)), @@ -281,6 +311,8 @@ impl<'a> CodecBuilder<'a> { D::Enumeration(d) => Ok(Arc::new(Enum { members: d.members.iter().map(|x| x[..].into()).collect(), })), + D::Object(_) => Ok(Arc::new(Nothing {})), + D::Compound(_) => Ok(Arc::new(Nothing {})), D::InputShape(d) => Ok(Arc::new(Input::build(d, self)?)), // type annotations are stripped from codecs array before // building a codec @@ -815,6 +847,7 @@ impl<'a> From<&'a descriptors::ShapeElement> for ShapeElement { cardinality, name, type_pos: _, + source_type_pos: _, } = e; ShapeElement { flag_implicit: *flag_implicit, @@ -826,6 +859,28 @@ impl<'a> From<&'a descriptors::ShapeElement> for ShapeElement { } } +impl<'a> From<&'a [descriptors::InputShapeElement]> for InputObjectShape { + fn from(shape: &'a [descriptors::InputShapeElement]) -> InputObjectShape { + InputObjectShape(Arc::new(InputObjectShapeInfo { + elements: shape.iter().map(InputShapeElement::from).collect(), + })) + } +} + +impl<'a> From<&'a descriptors::InputShapeElement> for InputShapeElement { + fn from(e: &'a descriptors::InputShapeElement) -> InputShapeElement { + let descriptors::InputShapeElement { + cardinality, + name, + type_pos: _, + } = e; + InputShapeElement { + cardinality: *cardinality, + name: name.clone(), + } + } +} + impl<'a> From<&'a [descriptors::TupleElement]> for NamedTupleShape { fn from(shape: &'a [descriptors::TupleElement]) -> NamedTupleShape { NamedTupleShape(Arc::new(NamedTupleShapeInfo { diff --git a/edgedb-protocol/src/descriptors.rs b/edgedb-protocol/src/descriptors.rs index 98705da5..460941ca 100644 --- a/edgedb-protocol/src/descriptors.rs +++ b/edgedb-protocol/src/descriptors.rs @@ -70,6 +70,8 @@ pub enum Descriptor { InputShape(InputShapeTypeDescriptor), Range(RangeTypeDescriptor), MultiRange(MultiRangeTypeDescriptor), + Object(ObjectTypeDescriptor), + Compound(CompoundTypeDescriptor), TypeAnnotation(TypeAnnotationDescriptor), } @@ -122,13 +124,15 @@ pub struct SetDescriptor { #[derive(Clone, Debug, PartialEq, Eq)] pub struct ObjectShapeDescriptor { pub id: DescriptorUuid, + pub ephemeral_free_shape: bool, + pub type_pos: Option, pub elements: Vec, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct InputShapeTypeDescriptor { pub id: DescriptorUuid, - pub elements: Vec, + pub elements: Vec, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -139,6 +143,14 @@ pub struct ShapeElement { pub cardinality: Option, pub name: String, pub type_pos: TypePos, + pub source_type_pos: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct InputShapeElement { + pub cardinality: Option, + pub name: String, + pub type_pos: TypePos, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -149,19 +161,51 @@ pub struct BaseScalarTypeDescriptor { #[derive(Clone, Debug, PartialEq, Eq)] pub struct ScalarTypeDescriptor { pub id: DescriptorUuid, - pub base_type_pos: TypePos, + pub base_type_pos: Option, + pub name: Option, + pub schema_defined: Option, + pub ancestors: Vec, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct TupleTypeDescriptor { pub id: DescriptorUuid, pub element_types: Vec, + pub name: Option, + pub schema_defined: Option, + pub ancestors: Vec, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct NamedTupleTypeDescriptor { pub id: DescriptorUuid, pub elements: Vec, + pub name: Option, + pub schema_defined: Option, + pub ancestors: Vec, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ObjectTypeDescriptor { + pub id: DescriptorUuid, + pub name: Option, + pub schema_defined: Option, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +#[repr(u8)] +pub enum TypeOperation { + UNION = 1, + INTERSECTION = 2, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct CompoundTypeDescriptor { + pub id: DescriptorUuid, + pub name: Option, + pub schema_defined: Option, + pub op: TypeOperation, + pub components: Vec, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -175,24 +219,36 @@ pub struct ArrayTypeDescriptor { pub id: DescriptorUuid, pub type_pos: TypePos, pub dimensions: Vec>, + pub name: Option, + pub schema_defined: Option, + pub ancestors: Vec, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct RangeTypeDescriptor { pub id: DescriptorUuid, pub type_pos: TypePos, + pub name: Option, + pub schema_defined: Option, + pub ancestors: Vec, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct MultiRangeTypeDescriptor { pub id: DescriptorUuid, pub type_pos: TypePos, + pub name: Option, + pub schema_defined: Option, + pub ancestors: Vec, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct EnumerationTypeDescriptor { pub id: DescriptorUuid, pub members: Vec, + pub name: Option, + pub schema_defined: Option, + pub ancestors: Vec, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -490,29 +546,96 @@ impl Descriptor { MultiRange(i) => &i.id, Enumeration(i) => &i.id, InputShape(i) => &i.id, + Object(i) => &i.id, + Compound(i) => &i.id, TypeAnnotation(i) => &i.id, } } pub fn decode(buf: &mut Input) -> Result { ::decode(buf) } + pub fn normalize_to_base( + &self, + ctx: &query_arg::DescriptorContext, + ) -> Result { + let norm = match self { + Descriptor::Scalar(d) if d.base_type_pos.is_some() => { + match ctx.get(d.base_type_pos.unwrap())? { + Descriptor::Scalar(d) => { + Descriptor::BaseScalar(BaseScalarTypeDescriptor { id: d.id.clone() }) + } + desc => desc.clone(), + } + } + Descriptor::Scalar(d) => { + if ctx.proto.is_2() { + Descriptor::BaseScalar(BaseScalarTypeDescriptor { id: d.id.clone() }) + } else { + unreachable!("scalar dereference to a non-base type") + } + } + desc => desc.clone(), + }; + + Ok(norm) + } +} + +impl Decode for Vec { + fn decode(buf: &mut Input) -> Result { + ensure!(buf.remaining() >= 2, errors::Underflow); + let element_count = buf.get_u16(); + let mut elements = Vec::with_capacity(element_count as usize); + for _ in 0..element_count { + elements.push(T::decode(buf)?); + } + Ok(elements) + } +} + +impl Decode for Option { + fn decode(buf: &mut Input) -> Result { + ensure!(buf.remaining() >= 4, errors::Underflow); + + let val = match buf.get_i32() { + -1 => None, + n if n > 0 => Some(n as u32), + _ => errors::InvalidOptionU32.fail()?, + }; + + Ok(val) + } +} + +impl Decode for TypePos { + fn decode(buf: &mut Input) -> Result { + ensure!(buf.remaining() >= 2, errors::Underflow); + Ok(Self(buf.get_u16())) + } } impl Decode for Descriptor { fn decode(buf: &mut Input) -> Result { use Descriptor as D; + if buf.proto().is_2() { + ensure!(buf.remaining() >= 4, errors::Underflow); + let desc_len = buf.get_u32() as u64; + ensure!((buf.remaining() as u64) >= desc_len, errors::Underflow); + } ensure!(buf.remaining() >= 1, errors::Underflow); match buf.chunk()[0] { - 0 => SetDescriptor::decode(buf).map(D::Set), - 1 => ObjectShapeDescriptor::decode(buf).map(D::ObjectShape), - 2 => BaseScalarTypeDescriptor::decode(buf).map(D::BaseScalar), - 3 => ScalarTypeDescriptor::decode(buf).map(D::Scalar), - 4 => TupleTypeDescriptor::decode(buf).map(D::Tuple), - 5 => NamedTupleTypeDescriptor::decode(buf).map(D::NamedTuple), - 6 => ArrayTypeDescriptor::decode(buf).map(D::Array), - 7 => EnumerationTypeDescriptor::decode(buf).map(D::Enumeration), - 8 => InputShapeTypeDescriptor::decode(buf).map(D::InputShape), - 9 => RangeTypeDescriptor::decode(buf).map(D::Range), + 0x00 => SetDescriptor::decode(buf).map(D::Set), + 0x01 => ObjectShapeDescriptor::decode(buf).map(D::ObjectShape), + 0x02 => BaseScalarTypeDescriptor::decode(buf).map(D::BaseScalar), + 0x03 => ScalarTypeDescriptor::decode(buf).map(D::Scalar), + 0x04 => TupleTypeDescriptor::decode(buf).map(D::Tuple), + 0x05 => NamedTupleTypeDescriptor::decode(buf).map(D::NamedTuple), + 0x06 => ArrayTypeDescriptor::decode(buf).map(D::Array), + 0x07 => EnumerationTypeDescriptor::decode(buf).map(D::Enumeration), + 0x08 => InputShapeTypeDescriptor::decode(buf).map(D::InputShape), + 0x09 => RangeTypeDescriptor::decode(buf).map(D::Range), + 0x0A => ObjectTypeDescriptor::decode(buf).map(D::Object), + 0x0B => CompoundTypeDescriptor::decode(buf).map(D::Compound), 0x0C => MultiRangeTypeDescriptor::decode(buf).map(D::MultiRange), 0x7F..=0xFF => TypeAnnotationDescriptor::decode(buf).map(D::TypeAnnotation), descriptor => InvalidTypeDescriptor { descriptor }.fail()?, @@ -535,26 +658,26 @@ impl Decode for ObjectShapeDescriptor { ensure!(buf.remaining() >= 19, errors::Underflow); assert!(buf.get_u8() == 1); let id = Uuid::decode(buf)?.into(); - let element_count = buf.get_u16(); - let mut elements = Vec::with_capacity(element_count as usize); - for _ in 0..element_count { - elements.push(ShapeElement::decode(buf)?); - } - Ok(ObjectShapeDescriptor { id, elements }) - } -} - -impl Decode for InputShapeTypeDescriptor { - fn decode(buf: &mut Input) -> Result { - ensure!(buf.remaining() >= 19, errors::Underflow); - assert!(buf.get_u8() == 8); - let id = Uuid::decode(buf)?.into(); - let element_count = buf.get_u16(); - let mut elements = Vec::with_capacity(element_count as usize); - for _ in 0..element_count { - elements.push(ShapeElement::decode(buf)?); - } - Ok(InputShapeTypeDescriptor { id, elements }) + let type_desc = if buf.proto().is_2() { + let ephemeral_free_shape = bool::decode(buf)?; + let type_pos = Some(TypePos::decode(buf)?); + let elements = Vec::::decode(buf)?; + ObjectShapeDescriptor { + id, + elements, + ephemeral_free_shape, + type_pos, + } + } else { + let elements = Vec::::decode(buf)?; + ObjectShapeDescriptor { + id, + elements, + ephemeral_free_shape: false, + type_pos: None, + } + }; + Ok(type_desc) } } @@ -569,8 +692,12 @@ impl Decode for ShapeElement { (buf.get_u8() as u32, None) }; let name = String::decode(buf)?; - ensure!(buf.remaining() >= 2, errors::Underflow); - let type_pos = TypePos(buf.get_u16()); + let type_pos = TypePos::decode(buf)?; + let source_type_pos = if buf.proto().is_2() { + Some(TypePos::decode(buf)?) + } else { + None + }; Ok(ShapeElement { flag_implicit: flags & 0b001 != 0, flag_link_property: flags & 0b010 != 0, @@ -578,25 +705,128 @@ impl Decode for ShapeElement { cardinality, name, type_pos, + source_type_pos, + }) + } +} + +impl Decode for InputShapeTypeDescriptor { + fn decode(buf: &mut Input) -> Result { + ensure!(buf.remaining() >= 19, errors::Underflow); + assert!(buf.get_u8() == 8); + let id = Uuid::decode(buf)?.into(); + let elements = Vec::::decode(buf)?; + Ok(InputShapeTypeDescriptor { id, elements }) + } +} + +impl Decode for InputShapeElement { + fn decode(buf: &mut Input) -> Result { + ensure!(buf.remaining() >= 7, errors::Underflow); + let _flags = buf.get_u32(); + let cardinality = Some(TryFrom::try_from(buf.get_u8())?); + let name = String::decode(buf)?; + let type_pos = TypePos::decode(buf)?; + Ok(InputShapeElement { + cardinality, + name, + type_pos, }) } } impl Decode for BaseScalarTypeDescriptor { fn decode(buf: &mut Input) -> Result { - assert!(buf.get_u8() == 2); + let desc_byte = buf.get_u8(); + assert!(desc_byte == 2); + ensure!( + !buf.proto().is_2(), + InvalidTypeDescriptor { + descriptor: desc_byte + } + ); let id = Uuid::decode(buf)?.into(); Ok(BaseScalarTypeDescriptor { id }) } } +impl Decode for ObjectTypeDescriptor { + fn decode(buf: &mut Input) -> Result { + ensure!(buf.remaining() >= 19, errors::Underflow); + assert!(buf.get_u8() == 0x0A); + let id = Uuid::decode(buf)?.into(); + let name = Some(String::decode(buf)?); + let schema_defined = Some(bool::decode(buf)?); + let type_desc = ObjectTypeDescriptor { + id, + name, + schema_defined, + }; + Ok(type_desc) + } +} + +impl Decode for TypeOperation { + fn decode(buf: &mut Input) -> Result { + ensure!(buf.remaining() >= 1, errors::Underflow); + let val = match buf.get_u8() { + 0x00 => TypeOperation::UNION, + 0x01 => TypeOperation::INTERSECTION, + _ => errors::InvalidTypeOperation.fail()?, + }; + Ok(val) + } +} + +impl Decode for CompoundTypeDescriptor { + fn decode(buf: &mut Input) -> Result { + ensure!(buf.remaining() >= 19, errors::Underflow); + assert!(buf.get_u8() == 0x0A); + let id = Uuid::decode(buf)?.into(); + let name = Some(String::decode(buf)?); + let schema_defined = Some(bool::decode(buf)?); + ensure!(buf.remaining() >= 1, errors::Underflow); + let op = TypeOperation::decode(buf)?; + let components = Vec::::decode(buf)?; + let type_desc = CompoundTypeDescriptor { + id, + name, + schema_defined, + op, + components, + }; + Ok(type_desc) + } +} + impl Decode for ScalarTypeDescriptor { fn decode(buf: &mut Input) -> Result { ensure!(buf.remaining() >= 19, errors::Underflow); assert!(buf.get_u8() == 3); let id = Uuid::decode(buf)?.into(); - let base_type_pos = TypePos(buf.get_u16()); - Ok(ScalarTypeDescriptor { id, base_type_pos }) + let type_desc = if buf.proto().is_2() { + let name = Some(String::decode(buf)?); + let schema_defined = Some(bool::decode(buf)?); + let ancestors = Vec::::decode(buf)?; + let base_type_pos = ancestors.last().copied(); + ScalarTypeDescriptor { + id, + base_type_pos, + name, + schema_defined, + ancestors, + } + } else { + let base_type_pos = Some(TypePos(buf.get_u16())); + ScalarTypeDescriptor { + id, + base_type_pos, + name: None, + schema_defined: None, + ancestors: vec![], + } + }; + Ok(type_desc) } } @@ -605,13 +835,30 @@ impl Decode for TupleTypeDescriptor { ensure!(buf.remaining() >= 19, errors::Underflow); assert!(buf.get_u8() == 4); let id = Uuid::decode(buf)?.into(); - let el_count = buf.get_u16(); - ensure!(buf.remaining() >= 2 * el_count as usize, errors::Underflow); - let mut element_types = Vec::with_capacity(el_count as usize); - for _ in 0..el_count { - element_types.push(TypePos(buf.get_u16())); - } - Ok(TupleTypeDescriptor { id, element_types }) + + let type_desc = if buf.proto().is_2() { + let name = Some(String::decode(buf)?); + let schema_defined = Some(bool::decode(buf)?); + let ancestors = Vec::::decode(buf)?; + let element_types = Vec::::decode(buf)?; + TupleTypeDescriptor { + id, + element_types, + name, + schema_defined, + ancestors, + } + } else { + let element_types = Vec::::decode(buf)?; + TupleTypeDescriptor { + id, + element_types, + name: None, + schema_defined: None, + ancestors: vec![], + } + }; + Ok(type_desc) } } @@ -620,20 +867,38 @@ impl Decode for NamedTupleTypeDescriptor { ensure!(buf.remaining() >= 19, errors::Underflow); assert!(buf.get_u8() == 5); let id = Uuid::decode(buf)?.into(); - let element_count = buf.get_u16(); - let mut elements = Vec::with_capacity(element_count as usize); - for _ in 0..element_count { - elements.push(TupleElement::decode(buf)?); - } - Ok(NamedTupleTypeDescriptor { id, elements }) + + let type_desc = if buf.proto().is_2() { + let name = Some(String::decode(buf)?); + let schema_defined = Some(bool::decode(buf)?); + let ancestors = Vec::::decode(buf)?; + let elements = Vec::::decode(buf)?; + NamedTupleTypeDescriptor { + id, + elements, + name, + schema_defined, + ancestors, + } + } else { + let elements = Vec::::decode(buf)?; + NamedTupleTypeDescriptor { + id, + elements, + name: None, + schema_defined: None, + ancestors: vec![], + } + }; + + Ok(type_desc) } } impl Decode for TupleElement { fn decode(buf: &mut Input) -> Result { let name = String::decode(buf)?; - ensure!(buf.remaining() >= 2, errors::Underflow); - let type_pos = TypePos(buf.get_u16()); + let type_pos = TypePos::decode(buf)?; Ok(TupleElement { name, type_pos }) } } @@ -643,22 +908,34 @@ impl Decode for ArrayTypeDescriptor { ensure!(buf.remaining() >= 21, errors::Underflow); assert!(buf.get_u8() == 6); let id = Uuid::decode(buf)?.into(); - let type_pos = TypePos(buf.get_u16()); - let dim_count = buf.get_u16(); - ensure!(buf.remaining() >= 4 * dim_count as usize, errors::Underflow); - let mut dimensions = Vec::with_capacity(dim_count as usize); - for _ in 0..dim_count { - dimensions.push(match buf.get_i32() { - -1 => None, - n if n > 0 => Some(n as u32), - _ => errors::InvalidArrayShape.fail()?, - }); - } - Ok(ArrayTypeDescriptor { - id, - type_pos, - dimensions, - }) + let type_desc = if buf.proto().is_2() { + let name = Some(String::decode(buf)?); + let schema_defined = Some(bool::decode(buf)?); + let ancestors = Vec::::decode(buf)?; + let type_pos = TypePos::decode(buf)?; + let dimensions = Vec::>::decode(buf)?; + ArrayTypeDescriptor { + id, + type_pos, + dimensions, + name, + schema_defined, + ancestors, + } + } else { + let type_pos = TypePos::decode(buf)?; + let dimensions = Vec::>::decode(buf)?; + ArrayTypeDescriptor { + id, + type_pos, + dimensions, + name: None, + schema_defined: None, + ancestors: vec![], + } + }; + + Ok(type_desc) } } @@ -667,8 +944,30 @@ impl Decode for RangeTypeDescriptor { ensure!(buf.remaining() >= 19, errors::Underflow); assert!(buf.get_u8() == 9); let id = Uuid::decode(buf)?.into(); - let type_pos = TypePos(buf.get_u16()); - Ok(RangeTypeDescriptor { id, type_pos }) + let type_desc = if buf.proto().is_2() { + let name = Some(String::decode(buf)?); + let schema_defined = Some(bool::decode(buf)?); + let ancestors = Vec::::decode(buf)?; + let type_pos = TypePos::decode(buf)?; + RangeTypeDescriptor { + id, + type_pos, + name, + schema_defined, + ancestors, + } + } else { + let type_pos = TypePos::decode(buf)?; + RangeTypeDescriptor { + id, + type_pos, + name: None, + schema_defined: None, + ancestors: vec![], + } + }; + + Ok(type_desc) } } @@ -677,8 +976,30 @@ impl Decode for MultiRangeTypeDescriptor { ensure!(buf.remaining() >= 19, errors::Underflow); assert!(buf.get_u8() == 0x0C); let id = Uuid::decode(buf)?.into(); - let type_pos = TypePos(buf.get_u16()); - Ok(MultiRangeTypeDescriptor { id, type_pos }) + let type_desc = if buf.proto().is_2() { + let name = Some(String::decode(buf)?); + let schema_defined = Some(bool::decode(buf)?); + let ancestors = Vec::::decode(buf)?; + let type_pos = TypePos::decode(buf)?; + MultiRangeTypeDescriptor { + id, + type_pos, + name, + schema_defined, + ancestors, + } + } else { + let type_pos = TypePos::decode(buf)?; + MultiRangeTypeDescriptor { + id, + type_pos, + name: None, + schema_defined: None, + ancestors: vec![], + } + }; + + Ok(type_desc) } } @@ -687,12 +1008,30 @@ impl Decode for EnumerationTypeDescriptor { ensure!(buf.remaining() >= 19, errors::Underflow); assert!(buf.get_u8() == 7); let id = Uuid::decode(buf)?.into(); - let member_count = buf.get_u16(); - let mut members = Vec::with_capacity(member_count as usize); - for _ in 0..member_count { - members.push(String::decode(buf)?); - } - Ok(EnumerationTypeDescriptor { id, members }) + let type_desc = if buf.proto().is_2() { + let name = Some(String::decode(buf)?); + let schema_defined = Some(bool::decode(buf)?); + let ancestors = Vec::::decode(buf)?; + let members = Vec::::decode(buf)?; + EnumerationTypeDescriptor { + id, + members, + name, + schema_defined, + ancestors, + } + } else { + let members = Vec::::decode(buf)?; + EnumerationTypeDescriptor { + id, + members, + name: None, + schema_defined: None, + ancestors: vec![], + } + }; + + Ok(type_desc) } } diff --git a/edgedb-protocol/src/encoding.rs b/edgedb-protocol/src/encoding.rs index 815ba9e3..7c59d415 100644 --- a/edgedb-protocol/src/encoding.rs +++ b/edgedb-protocol/src/encoding.rs @@ -194,3 +194,25 @@ impl Encode for Uuid { Ok(()) } } + +impl Decode for bool { + fn decode(buf: &mut Input) -> Result { + ensure!(buf.remaining() >= 1, errors::Underflow); + let res = match buf.get_u8() { + 0x00 => false, + 0x01 => true, + v => errors::InvalidBool { val: v }.fail()?, + }; + Ok(res) + } +} + +impl Encode for bool { + fn encode(&self, buf: &mut Output) -> Result<(), EncodeError> { + buf.extend(match self { + true => &[0x01], + false => &[0x00], + }); + Ok(()) + } +} diff --git a/edgedb-protocol/src/errors.rs b/edgedb-protocol/src/errors.rs index 6d4a08f1..a81d8f12 100644 --- a/edgedb-protocol/src/errors.rs +++ b/edgedb-protocol/src/errors.rs @@ -73,8 +73,10 @@ pub enum DecodeError { InvalidArrayOrSetShape { backtrace: Backtrace }, #[snafu(display("decimal or bigint sign bytes have invalid value"))] BadSign { backtrace: Backtrace }, - #[snafu(display("invalid boolean value"))] - InvalidBool { backtrace: Backtrace }, + #[snafu(display("invalid boolean value: {val:?}"))] + InvalidBool { backtrace: Backtrace, val: u8 }, + #[snafu(display("invalid optional u32 value"))] + InvalidOptionU32 { backtrace: Backtrace }, #[snafu(display("datetime is out of range"))] InvalidDate { backtrace: Backtrace }, #[snafu(display("json format is invalid"))] @@ -102,6 +104,8 @@ pub enum DecodeError { backtrace: Backtrace, annotation: &'static str, }, + #[snafu(display("invalid type operation value"))] + InvalidTypeOperation { backtrace: Backtrace }, } #[derive(Snafu, Debug)] diff --git a/edgedb-protocol/src/features.rs b/edgedb-protocol/src/features.rs index 095fb551..c23a2b31 100644 --- a/edgedb-protocol/src/features.rs +++ b/edgedb-protocol/src/features.rs @@ -7,7 +7,7 @@ pub struct ProtocolVersion { impl ProtocolVersion { pub fn current() -> ProtocolVersion { ProtocolVersion { - major_ver: 1, + major_ver: 2, minor_ver: 0, } } @@ -23,6 +23,9 @@ impl ProtocolVersion { pub fn is_1(&self) -> bool { self.major_ver >= 1 } + pub fn is_2(&self) -> bool { + self.major_ver >= 2 + } pub fn supports_inline_typenames(&self) -> bool { self.version_tuple() >= (0, 9) } diff --git a/edgedb-protocol/src/query_arg.rs b/edgedb-protocol/src/query_arg.rs index 1a8c6109..1ed5fb3c 100644 --- a/edgedb-protocol/src/query_arg.rs +++ b/edgedb-protocol/src/query_arg.rs @@ -93,7 +93,7 @@ impl DescriptorContext<'_> { } pub fn wrong_type(&self, descriptor: &Descriptor, expected: &str) -> Error { DescriptorMismatch::with_message(format!( - "\nEdgeDB returned unexpected type {descriptor:?}\nClient expected {expected}" + "server returned unexpected type {descriptor:?} when client expected {expected}" )) } pub fn field_number(&self, expected: usize, unexpected: usize) -> Error { @@ -210,13 +210,10 @@ impl QueryArg for Value { fn check_descriptor(&self, ctx: &DescriptorContext, pos: TypePos) -> Result<(), Error> { use Descriptor::*; use Value::*; - let mut desc = ctx.get(pos)?; - if let Scalar(d) = desc { - desc = ctx.get(d.base_type_pos)?; - } + let desc = ctx.get(pos)?.normalize_to_base(ctx)?; + match (self, desc) { (Nothing, _) => Ok(()), // any descriptor works - (_, Scalar(_)) => unreachable!("scalar dereference to a non-base type"), (BigInt(_), BaseScalar(d)) if d.id == codec::STD_BIGINT => Ok(()), (Bool(_), BaseScalar(d)) if d.id == codec::STD_BOOL => Ok(()), (Bytes(_), BaseScalar(d)) if d.id == codec::STD_BYTES => Ok(()), @@ -239,10 +236,10 @@ impl QueryArg for Value { (Uuid(_), BaseScalar(d)) if d.id == codec::STD_UUID => Ok(()), (Enum(val), Enumeration(EnumerationTypeDescriptor { members, .. })) => { let val = val.deref(); - check_enum(val, members) + check_enum(val, &members) } // TODO(tailhook) all types - (_, desc) => Err(ctx.wrong_type(desc, self.kind())), + (_, desc) => Err(ctx.wrong_type(&desc, self.kind())), } } fn to_value(&self) -> Result { diff --git a/edgedb-protocol/src/serialization/decode/queryable/scalars.rs b/edgedb-protocol/src/serialization/decode/queryable/scalars.rs index 56ecfb82..85d30bdc 100644 --- a/edgedb-protocol/src/serialization/decode/queryable/scalars.rs +++ b/edgedb-protocol/src/serialization/decode/queryable/scalars.rs @@ -21,8 +21,11 @@ pub(crate) fn check_scalar( use crate::descriptors::Descriptor::{BaseScalar, Scalar}; let desc = ctx.get(type_pos)?; match desc { - Scalar(scalar) => { - return check_scalar(ctx, scalar.base_type_pos, type_id, name); + Scalar(scalar) if scalar.base_type_pos.is_some() => { + return check_scalar(ctx, scalar.base_type_pos.unwrap(), type_id, name); + } + Scalar(scalar) if *scalar.id == type_id => { + return Ok(()); } BaseScalar(base) if *base.id == type_id => { return Ok(()); diff --git a/edgedb-protocol/src/serialization/decode/raw_scalar.rs b/edgedb-protocol/src/serialization/decode/raw_scalar.rs index 97906e36..52abc6b2 100644 --- a/edgedb-protocol/src/serialization/decode/raw_scalar.rs +++ b/edgedb-protocol/src/serialization/decode/raw_scalar.rs @@ -50,8 +50,11 @@ fn check_scalar( use crate::descriptors::Descriptor::{BaseScalar, Scalar}; let desc = ctx.get(type_pos)?; match desc { - Scalar(scalar) => { - return check_scalar(ctx, scalar.base_type_pos, type_id, name); + Scalar(scalar) if scalar.base_type_pos.is_some() => { + return check_scalar(ctx, scalar.base_type_pos.unwrap(), type_id, name); + } + Scalar(scalar) if ctx.proto.is_2() && *scalar.id == type_id => { + return Ok(()); } BaseScalar(base) if *base.id == type_id => { return Ok(()); @@ -152,7 +155,7 @@ impl<'t> RawCodec<'t> for bool { let res = match buf[0] { 0x00 => false, 0x01 => true, - _ => errors::InvalidBool.fail()?, + v => errors::InvalidBool { val: v }.fail()?, }; Ok(res) } diff --git a/edgedb-protocol/src/value.rs b/edgedb-protocol/src/value.rs index 7b691e1f..b1548412 100644 --- a/edgedb-protocol/src/value.rs +++ b/edgedb-protocol/src/value.rs @@ -2,7 +2,7 @@ Contains the [Value] enum. */ pub use crate::codec::EnumValue; -use crate::codec::{NamedTupleShape, ObjectShape, ShapeElement}; +use crate::codec::{InputObjectShape, InputShapeElement, NamedTupleShape, ObjectShape}; use crate::common::Cardinality; use crate::model::{BigInt, ConfigMemory, Decimal, Range, Uuid}; use crate::model::{DateDuration, Json, RelativeDuration}; @@ -50,7 +50,7 @@ pub enum Value { #[derive(Clone, Debug)] pub struct SparseObject { - pub(crate) shape: ObjectShape, + pub(crate) shape: InputObjectShape, pub(crate) fields: Vec>>, } @@ -113,24 +113,21 @@ impl SparseObject { let mut elements = Vec::new(); let mut fields = Vec::new(); for (key, val) in iter.into_iter() { - elements.push(ShapeElement { - flag_implicit: false, - flag_link_property: false, - flag_link: false, + elements.push(InputShapeElement { cardinality: Some(Cardinality::AtMostOne), name: key.to_string(), }); fields.push(Some(val.into())); } SparseObject { - shape: ObjectShape::new(elements), + shape: InputObjectShape::new(elements), fields, } } /// Create an empty sparse object pub fn empty() -> SparseObject { SparseObject { - shape: ObjectShape::new(Vec::new()), + shape: InputObjectShape::new(Vec::new()), fields: Vec::new(), } } diff --git a/edgedb-protocol/tests/codecs.rs b/edgedb-protocol/tests/codecs.rs index 00426b2c..a3f9bf92 100644 --- a/edgedb-protocol/tests/codecs.rs +++ b/edgedb-protocol/tests/codecs.rs @@ -333,6 +333,7 @@ fn object_codec() -> Result<(), Box> { cardinality: None, name: String::from("__tid__"), type_pos: TypePos(0), + source_type_pos: None, }, ShapeElement { flag_implicit: false, @@ -341,6 +342,7 @@ fn object_codec() -> Result<(), Box> { cardinality: None, name: String::from("id"), type_pos: TypePos(0), + source_type_pos: None, }, ]; let shape = elements.as_slice().into(); @@ -356,6 +358,8 @@ fn object_codec() -> Result<(), Box> { id: "5d5ebe41-eac8-eab7-a24e-cc3a8cd2766c" .parse::()? .into(), + ephemeral_free_shape: false, + type_pos: None, elements, }), ], @@ -383,7 +387,7 @@ fn object_codec() -> Result<(), Box> { fn input_codec() -> Result<(), Box> { let sdd = StateDataDescription { typedesc: RawTypedesc { - proto: ProtocolVersion::current(), + proto: ProtocolVersion::new(1, 0), id: "fd6c3b17504a714858ec2282431ce72c".parse()?, data: Bytes::from_static( b"\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\ @@ -459,6 +463,7 @@ fn set_codec() -> Result<(), Box> { cardinality: None, name: "__tid__".into(), type_pos: TypePos(0), + source_type_pos: None, }, ShapeElement { flag_implicit: true, @@ -467,6 +472,7 @@ fn set_codec() -> Result<(), Box> { cardinality: None, name: "id".into(), type_pos: TypePos(0), + source_type_pos: None, }, ShapeElement { flag_implicit: false, @@ -475,6 +481,7 @@ fn set_codec() -> Result<(), Box> { cardinality: None, name: "first_name".into(), type_pos: TypePos(1), + source_type_pos: None, }, ]; let outer_elements = vec![ @@ -485,6 +492,7 @@ fn set_codec() -> Result<(), Box> { cardinality: None, name: "__tid__".into(), type_pos: TypePos(0), + source_type_pos: None, }, ShapeElement { flag_implicit: true, @@ -493,6 +501,7 @@ fn set_codec() -> Result<(), Box> { cardinality: None, name: "id".into(), type_pos: TypePos(0), + source_type_pos: None, }, ShapeElement { flag_implicit: false, @@ -501,6 +510,7 @@ fn set_codec() -> Result<(), Box> { cardinality: None, name: "first_name".into(), type_pos: TypePos(1), + source_type_pos: None, }, ShapeElement { flag_implicit: false, @@ -509,6 +519,7 @@ fn set_codec() -> Result<(), Box> { cardinality: None, name: "collegues".into(), type_pos: TypePos(3), + source_type_pos: None, }, ]; let inner_shape = ObjectShape::from(&inner_elements[..]); @@ -531,6 +542,8 @@ fn set_codec() -> Result<(), Box> { .parse::()? .into(), elements: inner_elements, + ephemeral_free_shape: false, + type_pos: None, }), Descriptor::Set(SetDescriptor { id: "afbb389d-aa73-2aae-9310-84a9163cb5ed" @@ -543,6 +556,8 @@ fn set_codec() -> Result<(), Box> { .parse::()? .into(), elements: outer_elements, + ephemeral_free_shape: false, + type_pos: None, }), ], )?; @@ -854,7 +869,10 @@ fn custom_scalar() -> Result<(), Box> { id: "234dc787-2646-11ea-bebd-010d530c06ca" .parse::()? .into(), - base_type_pos: TypePos(0), + base_type_pos: Some(TypePos(0)), + name: None, + schema_defined: None, + ancestors: vec![], }), ], )?; @@ -883,6 +901,9 @@ fn tuple() -> Result<(), Box> { .parse::()? .into(), element_types: vec![TypePos(0), TypePos(1)], + name: None, + schema_defined: None, + ancestors: vec![], }), ], )?; @@ -928,6 +949,9 @@ fn named_tuple() -> Result<(), Box> { .parse::()? .into(), elements, + name: None, + schema_defined: None, + ancestors: vec![], }), ], )?; @@ -961,6 +985,9 @@ fn array() -> Result<(), Box> { .into(), type_pos: TypePos(0), dimensions: vec![None], + name: None, + schema_defined: None, + ancestors: vec![], }), ], )?; @@ -991,6 +1018,9 @@ fn enums() -> Result<(), Box> { .parse::()? .into(), members: vec!["x".into(), "y".into()], + name: None, + schema_defined: None, + ancestors: vec![], })], )?; encoding_eq!(&codec, bconcat!(b"x"), Value::Enum("x".into())); @@ -1007,6 +1037,7 @@ fn set_of_arrays() -> Result<(), Box> { cardinality: None, name: String::from("__tname__"), type_pos: TypePos(0), + source_type_pos: None, }, ShapeElement { flag_implicit: true, @@ -1015,6 +1046,7 @@ fn set_of_arrays() -> Result<(), Box> { cardinality: None, name: String::from("id"), type_pos: TypePos(1), + source_type_pos: None, }, ShapeElement { flag_implicit: false, @@ -1023,6 +1055,7 @@ fn set_of_arrays() -> Result<(), Box> { cardinality: None, name: String::from("sets"), type_pos: TypePos(4), + source_type_pos: None, }, ]; let shape = ObjectShape::from(&elements[..]); @@ -1051,6 +1084,9 @@ fn set_of_arrays() -> Result<(), Box> { .into(), type_pos: TypePos(2), dimensions: vec![None], + name: None, + schema_defined: None, + ancestors: vec![], }), Descriptor::Set(SetDescriptor { id: "499ffd5c-f21b-574d-af8a-1c094c9d6fb0" @@ -1063,6 +1099,8 @@ fn set_of_arrays() -> Result<(), Box> { .parse::()? .into(), elements, + ephemeral_free_shape: false, + type_pos: None, }), ], )?; @@ -1110,6 +1148,9 @@ fn range() -> Result<(), Box> { .unwrap() .into(), type_pos: TypePos(0), + name: None, + schema_defined: None, + ancestors: vec![], }), ], )?; @@ -1142,6 +1183,9 @@ fn multi_range() -> Result<(), Box> { .unwrap() .into(), type_pos: TypePos(0), + name: None, + schema_defined: None, + ancestors: vec![], }), ], )?; diff --git a/edgedb-protocol/tests/server_messages.rs b/edgedb-protocol/tests/server_messages.rs index e79c280e..5329b504 100644 --- a/edgedb-protocol/tests/server_messages.rs +++ b/edgedb-protocol/tests/server_messages.rs @@ -244,12 +244,12 @@ fn command_data_description1() -> Result<(), Box> { capabilities: Capabilities::MODIFICATIONS, result_cardinality: Cardinality::AtMostOne, input: RawTypedesc { - proto: ProtocolVersion::current(), + proto: ProtocolVersion::new(1, 0), id: Uuid::from_u128(0xFF), data: Bytes::from_static(b"\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\0\0"), }, output: RawTypedesc { - proto: ProtocolVersion::current(), + proto: ProtocolVersion::new(1, 0), id: Uuid::from_u128(0x105), data: Bytes::from_static(b"\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x05"), }, @@ -270,12 +270,12 @@ fn command_data_description1() -> Result<(), Box> { capabilities: Capabilities::MODIFICATIONS, result_cardinality: Cardinality::NoResult, input: RawTypedesc { - proto: ProtocolVersion::current(), + proto: ProtocolVersion::new(1, 0), id: Uuid::from_u128(0xFF), data: Bytes::from_static(b"\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\0\0"), }, output: RawTypedesc { - proto: ProtocolVersion::current(), + proto: ProtocolVersion::new(1, 0), id: Uuid::from_u128(0), data: Bytes::from_static(b""), }, diff --git a/edgedb-protocol/tests/type_descriptors.rs b/edgedb-protocol/tests/type_descriptors.rs index 0c2042a8..92a20517 100644 --- a/edgedb-protocol/tests/type_descriptors.rs +++ b/edgedb-protocol/tests/type_descriptors.rs @@ -2,6 +2,8 @@ use bytes::{Buf, Bytes}; use std::error::Error; use edgedb_protocol::descriptors::BaseScalarTypeDescriptor; +use edgedb_protocol::descriptors::ObjectTypeDescriptor; +use edgedb_protocol::descriptors::ScalarTypeDescriptor; use edgedb_protocol::descriptors::TupleTypeDescriptor; use edgedb_protocol::descriptors::{Descriptor, TypePos}; use edgedb_protocol::descriptors::{ObjectShapeDescriptor, ShapeElement}; @@ -12,9 +14,9 @@ use uuid::Uuid; mod base; -fn decode(bytes: &[u8]) -> Result, DecodeError> { +fn decode(pv: ProtocolVersion, bytes: &[u8]) -> Result, DecodeError> { let bytes = Bytes::copy_from_slice(bytes); - let mut input = Input::new(ProtocolVersion::current(), bytes); + let mut input = Input::new(pv, bytes); let mut result = Vec::new(); while input.remaining() > 0 { result.push(Descriptor::decode(&mut input)?); @@ -23,27 +25,31 @@ fn decode(bytes: &[u8]) -> Result, DecodeError> { Ok(result) } -fn decode_10(bytes: &[u8]) -> Result, DecodeError> { - let bytes = Bytes::copy_from_slice(bytes); - let mut input = Input::new(ProtocolVersion::new(0, 10), bytes); - let mut result = Vec::new(); - while input.remaining() > 0 { - result.push(Descriptor::decode(&mut input)?); - } - assert!(input.remaining() == 0); - Ok(result) +fn decode_2_0(bytes: &[u8]) -> Result, DecodeError> { + decode(ProtocolVersion::new(2, 0), bytes) +} + +fn decode_1_0(bytes: &[u8]) -> Result, DecodeError> { + decode(ProtocolVersion::new(1, 0), bytes) +} + +fn decode_0_10(bytes: &[u8]) -> Result, DecodeError> { + decode(ProtocolVersion::new(0, 10), bytes) } #[test] fn empty_tuple() -> Result<(), Box> { // `SELECT ()` assert_eq!( - decode(b"\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\0\0")?, + decode_1_0(b"\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xff\0\0")?, vec![Descriptor::Tuple(TupleTypeDescriptor { id: "00000000-0000-0000-0000-0000000000FF" .parse::()? .into(), element_types: Vec::new(), + name: None, + schema_defined: None, + ancestors: vec![], }),] ); Ok(()) @@ -53,7 +59,7 @@ fn empty_tuple() -> Result<(), Box> { fn one_tuple() -> Result<(), Box> { // `SELECT (1,)` assert_eq!( - decode(bconcat!( + decode_1_0(bconcat!( b"\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x05" b"\x04\x1cyGes%\x89Sa\x03\xe7\x87vE\xad9\0\x01\0\0"))?, vec![ @@ -67,6 +73,9 @@ fn one_tuple() -> Result<(), Box> { .parse::()? .into(), element_types: vec![TypePos(0)], + name: None, + schema_defined: None, + ancestors: vec![], }), ] ); @@ -74,9 +83,9 @@ fn one_tuple() -> Result<(), Box> { } #[test] -fn single_int() -> Result<(), Box> { +fn single_int_1_0() -> Result<(), Box> { assert_eq!( - decode(b"\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x05")?, + decode_1_0(b"\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x05")?, vec![Descriptor::BaseScalar(BaseScalarTypeDescriptor { id: "00000000-0000-0000-0000-000000000105" .parse::()? @@ -86,10 +95,70 @@ fn single_int() -> Result<(), Box> { Ok(()) } +#[test] +fn single_int_2_0() -> Result<(), Box> { + assert_eq!( + decode_2_0(b"\0\0\0\"\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x05\0\0\0\nstd::int64\x01\0\0")?, + vec![Descriptor::Scalar(ScalarTypeDescriptor { + id: "00000000-0000-0000-0000-000000000105" + .parse::()? + .into(), + name: Some(String::from("std::int64")), + schema_defined: Some(true), + ancestors: vec![], + base_type_pos: None, + })] + ); + Ok(()) +} + +#[test] +fn single_derived_int_2_0() -> Result<(), Box> { + assert_eq!( + decode_2_0(bconcat!( + b"\0\0\0\"\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x05\0\0\0\n" + b"std::int64\x01\0\0\0\0\0)\x03\x91v\xff\x8c\x95\xb6\x11\xef\x9c" + b" [\x0e\x8c=\xaa\xc8\0\0\0\x0fdefault::my_int\x01\0\x01\0\0\0\0\0" + b"-\x03J\xa0\x08{\x95\xb7\x11\xef\xbd\xe2?\xfa\xe3\r\x13\xe9\0\0\0" + b"\x11default::my_int_2\x01\0\x02\0\x01\0\0" + ))?, + vec![ + Descriptor::Scalar(ScalarTypeDescriptor { + id: "00000000-0000-0000-0000-000000000105" + .parse::()? + .into(), + name: Some(String::from("std::int64")), + schema_defined: Some(true), + ancestors: vec![], + base_type_pos: None, + }), + Descriptor::Scalar(ScalarTypeDescriptor { + id: "9176ff8c-95b6-11ef-9c20-5b0e8c3daac8" + .parse::()? + .into(), + name: Some(String::from("default::my_int")), + schema_defined: Some(true), + ancestors: vec![TypePos(0)], + base_type_pos: Some(TypePos(0)), + }), + Descriptor::Scalar(ScalarTypeDescriptor { + id: "4aa0087b-95b7-11ef-bde2-3ffae30d13e9" + .parse::()? + .into(), + name: Some(String::from("default::my_int_2")), + schema_defined: Some(true), + ancestors: vec![TypePos(1), TypePos(0)], + base_type_pos: Some(TypePos(0)), + }), + ] + ); + Ok(()) +} + #[test] fn duration() -> Result<(), Box> { assert_eq!( - decode(b"\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x0e")?, + decode_1_0(b"\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x0e")?, vec![Descriptor::BaseScalar(BaseScalarTypeDescriptor { id: "00000000-0000-0000-0000-00000000010e" .parse::()? @@ -100,9 +169,9 @@ fn duration() -> Result<(), Box> { } #[test] -fn object_10() -> Result<(), Box> { +fn object_0_10() -> Result<(), Box> { assert_eq!( - decode_10(bconcat!( + decode_0_10(bconcat!( b"\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\x02" b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x01\x01n" b"\xbb\xbe\xda\0P\x14\xfe\x84\xbc\x82\x15@\xb1" @@ -123,6 +192,8 @@ fn object_10() -> Result<(), Box> { id: "6ebbbeda-0050-14fe-84bc-821540b152cd" .parse::()? .into(), + ephemeral_free_shape: false, + type_pos: None, elements: vec![ ShapeElement { flag_implicit: true, @@ -131,6 +202,7 @@ fn object_10() -> Result<(), Box> { cardinality: None, name: String::from("__tid__"), type_pos: TypePos(0), + source_type_pos: None, }, ShapeElement { flag_implicit: true, @@ -139,6 +211,7 @@ fn object_10() -> Result<(), Box> { cardinality: None, name: String::from("id"), type_pos: TypePos(0), + source_type_pos: None, }, ShapeElement { flag_implicit: false, @@ -147,6 +220,7 @@ fn object_10() -> Result<(), Box> { cardinality: None, name: String::from("title"), type_pos: TypePos(1), + source_type_pos: None, } ] }) @@ -156,10 +230,10 @@ fn object_10() -> Result<(), Box> { } #[test] -fn object() -> Result<(), Box> { +fn object_1_0() -> Result<(), Box> { use edgedb_protocol::common::Cardinality::*; assert_eq!( - decode(bconcat!( + decode_1_0(bconcat!( // equivalent of 0.10 //b"\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x01\x02" //b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\x01,sT" @@ -186,6 +260,8 @@ fn object() -> Result<(), Box> { id: "6e27dba0-7861-24c2-86a9-15a6f2e3faf5" .parse::()? .into(), + ephemeral_free_shape: false, + type_pos: None, elements: vec![ ShapeElement { flag_implicit: true, @@ -194,6 +270,7 @@ fn object() -> Result<(), Box> { cardinality: Some(One), name: String::from("__tname__"), type_pos: TypePos(0), + source_type_pos: None, }, ShapeElement { flag_implicit: true, @@ -202,6 +279,7 @@ fn object() -> Result<(), Box> { cardinality: Some(One), name: String::from("id"), type_pos: TypePos(1), + source_type_pos: None, }, ShapeElement { flag_implicit: false, @@ -210,6 +288,7 @@ fn object() -> Result<(), Box> { cardinality: Some(AtMostOne), name: String::from("title"), type_pos: TypePos(0), + source_type_pos: None, } ] }) @@ -217,3 +296,106 @@ fn object() -> Result<(), Box> { ); Ok(()) } + +#[test] +fn object_2_0() -> Result<(), Box> { + use edgedb_protocol::common::Cardinality::*; + // SELECT Foo { + // id, + // title, + // [IS Bar].body, + // } + assert_eq!( + decode_2_0(bconcat!( + b"\0\0\0 \x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x01\0\0\0\x08" + b"std::str\x01\0\0\0\0\0!\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01" + b"\0\0\0\0\tstd::uuid\x01\0\0\0\0\0\"\n\xc3\xcc\xa7R\x95\xb7" + b"\x11\xef\xb4\x87\x1d\x1b\x9f\xa20\x03\0\0\0\x0cdefault::Foo" + b"\x01\0\0\0\"\n\r\xdc\xd7\x1e\x95\xb8\x11\xef\x82M!7\x80\\^4" + b"\0\0\0\x0cdefault::Bar\x01\0\0\0^\x01\x1dMg\xe7{\xdd]9\x90\x97" + b"O\x82\xfa\xd8\xaf7\0\0\x02\0\x04\0\0\0\x01A\0\0\0\t__tname__" + b"\0\0\0\x02\0\0\0\0A\0\0\0\x02id\0\x01\0\x02\0\0\0\0o\0\0\0\x05" + b"title\0\0\0\x02\0\0\0\0o\0\0\0\x04body\0\0\0\x03" + ))?, + vec![ + Descriptor::Scalar(ScalarTypeDescriptor { + id: "00000000-0000-0000-0000-000000000101" + .parse::()? + .into(), + name: Some(String::from("std::str")), + schema_defined: Some(true), + ancestors: vec![], + base_type_pos: None, + }), + Descriptor::Scalar(ScalarTypeDescriptor { + id: "00000000-0000-0000-0000-000000000100" + .parse::()? + .into(), + name: Some(String::from("std::uuid")), + schema_defined: Some(true), + ancestors: vec![], + base_type_pos: None, + }), + Descriptor::Object(ObjectTypeDescriptor { + id: "c3cca752-95b7-11ef-b487-1d1b9fa23003" + .parse::()? + .into(), + name: Some(String::from("default::Foo")), + schema_defined: Some(true), + }), + Descriptor::Object(ObjectTypeDescriptor { + id: "0ddcd71e-95b8-11ef-824d-2137805c5e34" + .parse::()? + .into(), + name: Some(String::from("default::Bar")), + schema_defined: Some(true), + }), + Descriptor::ObjectShape(ObjectShapeDescriptor { + id: "1d4d67e7-7bdd-5d39-9097-4f82fad8af37" + .parse::()? + .into(), + ephemeral_free_shape: false, + type_pos: Some(TypePos(2)), + elements: vec![ + ShapeElement { + flag_implicit: true, + flag_link_property: false, + flag_link: false, + cardinality: Some(One), + name: String::from("__tname__"), + type_pos: TypePos(0), + source_type_pos: Some(TypePos(2)), + }, + ShapeElement { + flag_implicit: false, + flag_link_property: false, + flag_link: false, + cardinality: Some(One), + name: String::from("id"), + type_pos: TypePos(1), + source_type_pos: Some(TypePos(2)), + }, + ShapeElement { + flag_implicit: false, + flag_link_property: false, + flag_link: false, + cardinality: Some(AtMostOne), + name: String::from("title"), + type_pos: TypePos(0), + source_type_pos: Some(TypePos(2)), + }, + ShapeElement { + flag_implicit: false, + flag_link_property: false, + flag_link: false, + cardinality: Some(AtMostOne), + name: String::from("body"), + type_pos: TypePos(0), + source_type_pos: Some(TypePos(3)), + }, + ] + }) + ] + ); + Ok(()) +} diff --git a/flake.lock b/flake.lock index c99756ff..40bc9fe2 100644 --- a/flake.lock +++ b/flake.lock @@ -35,11 +35,11 @@ ] }, "locked": { - "lastModified": 1728494477, - "narHash": "sha256-cbDdK/BlNmvqUXnUi2zWpR9bgHAWIjLr9en2ujLHMM0=", + "lastModified": 1730270645, + "narHash": "sha256-/ShKBKso+DEFM2AYqmiJNT1ngP9/hIesnJlQmq+I6jk=", "owner": "edgedb", "repo": "packages-nix", - "rev": "eb1a1ac4f0f41b1b918baa3ce88baac7647b6a5b", + "rev": "8b84b61569b0bd7389db6f4956c0067ccc18b92e", "type": "github" }, "original": { @@ -56,11 +56,11 @@ "rust-analyzer-src": [] }, "locked": { - "lastModified": 1728455642, - "narHash": "sha256-abYGwrL6ak5sBRqwPh+V3CPJ6Pa89p378t51b7BO1lE=", + "lastModified": 1730183653, + "narHash": "sha256-dcJGcoDgNBxTagW8kECwBKsRBA1ZITtQ+p3N6KHg5ps=", "owner": "nix-community", "repo": "fenix", - "rev": "3b47535a5c782e4f4ad59cd4bdb23636b6926e03", + "rev": "dc19afc39af5f5e69fca78ebae59170e61017df8", "type": "github" }, "original": { @@ -89,11 +89,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1728493385, - "narHash": "sha256-ryNG62K/XVIKt9/bBQxcavi3NOD/KHU2QRR+N4Z5xQA=", + "lastModified": 1730263663, + "narHash": "sha256-6qkh67ScZi1Twp8s1e6df2TinQb0QQGwkDZBQZhSKW8=", "owner": "nixos", "repo": "nixpkgs", - "rev": "516e84444d8aaaca070aba846b9c1015452f1a00", + "rev": "bc6857069763f5721a53462eaf72016fafe002ff", "type": "github" }, "original": {