feat: allow cbor uuid serialization for record id part (#4491)
Co-authored-by: Micha de Vries <micha@devrie.sh> Co-authored-by: Tobie Morgan Hitchcock <tobie@surrealdb.com> Co-authored-by: Mees Delzenne <DelSkayn@users.noreply.github.com>
This commit is contained in:
parent
f795434345
commit
c7bcd4f3a6
8 changed files with 39 additions and 15 deletions
|
@ -125,7 +125,7 @@ impl<'a> KnnConditionRewriter<'a> {
|
|||
|
||||
fn eval_id(&self, id: &Id) -> Option<Id> {
|
||||
match id {
|
||||
Id::Number(_) | Id::String(_) | Id::Generate(_) => Some(id.clone()),
|
||||
Id::Number(_) | Id::String(_) | Id::Generate(_) | Id::Uuid(_) => Some(id.clone()),
|
||||
Id::Array(a) => self.eval_array(a).map(Id::Array),
|
||||
Id::Object(o) => self.eval_object(o).map(Id::Object),
|
||||
Id::Range(r) => self.eval_id_range(r).map(|v| Id::Range(Box::new(v))),
|
||||
|
|
|
@ -86,7 +86,7 @@ mod tests {
|
|||
let id1 = thing.id;
|
||||
let val = Thing::new("testns", "testdb", "testtb", id1);
|
||||
let enc = Thing::encode(&val).unwrap();
|
||||
assert_eq!(enc, b"/*testns\0*testdb\0*testtb\0*\0\0\0\x02\0\0\0\x04test\0\x01");
|
||||
assert_eq!(enc, b"/*testns\0*testdb\0*testtb\0*\0\0\0\x03\0\0\0\x04test\0\x01");
|
||||
|
||||
let dec = Thing::decode(&enc).unwrap();
|
||||
assert_eq!(val, dec);
|
||||
|
@ -96,7 +96,7 @@ mod tests {
|
|||
let id2 = thing.id;
|
||||
let val = Thing::new("testns", "testdb", "testtb", id2);
|
||||
let enc = Thing::encode(&val).unwrap();
|
||||
assert_eq!(enc, b"/*testns\0*testdb\0*testtb\0*\0\0\0\x02\0\0\0\x07\0\0\0\0\0\0\0\x10\xf8\xe2\x38\xf2\xe7\x34\x47\xb8\x9a\x16\x47\x6b\x29\x1b\xd7\x8a\x01");
|
||||
assert_eq!(enc, b"/*testns\0*testdb\0*testtb\0*\0\0\0\x03\0\0\0\x07\0\0\0\0\0\0\0\x10\xf8\xe2\x38\xf2\xe7\x34\x47\xb8\x9a\x16\x47\x6b\x29\x1b\xd7\x8a\x01");
|
||||
|
||||
let dec = Thing::decode(&enc).unwrap();
|
||||
assert_eq!(val, dec);
|
||||
|
|
|
@ -116,13 +116,7 @@ impl TryFrom<Cbor> for Value {
|
|||
_ => Err("Expected a CBOR text data type"),
|
||||
},
|
||||
// A byte string uuid
|
||||
TAG_SPEC_UUID => match *v {
|
||||
Data::Bytes(v) if v.len() == 16 => match v.as_slice().try_into() {
|
||||
Ok(v) => Ok(Value::Uuid(Uuid::from(uuid::Uuid::from_bytes(v)))),
|
||||
Err(_) => Err("Expected a CBOR byte array with 16 elements"),
|
||||
},
|
||||
_ => Err("Expected a CBOR byte array with 16 elements"),
|
||||
},
|
||||
TAG_SPEC_UUID => v.deref().to_owned().try_into().map(Value::Uuid),
|
||||
// A literal decimal
|
||||
TAG_STRING_DECIMAL => match *v {
|
||||
Data::Text(v) => match Decimal::from_str(v.as_str()) {
|
||||
|
@ -393,6 +387,7 @@ impl TryFrom<Value> for Cbor {
|
|||
match v.id {
|
||||
Id::Number(v) => Data::Integer(v.into()),
|
||||
Id::String(v) => Data::Text(v),
|
||||
Id::Uuid(v) => Cbor::try_from(Value::from(v))?.0,
|
||||
Id::Array(v) => Cbor::try_from(Value::from(v))?.0,
|
||||
Id::Object(v) => Cbor::try_from(Value::from(v))?.0,
|
||||
Id::Generate(_) => {
|
||||
|
@ -564,6 +559,8 @@ impl TryFrom<Data> for Id {
|
|||
Data::Array(v) => Ok(Id::Array(v.try_into()?)),
|
||||
Data::Map(v) => Ok(Id::Object(v.try_into()?)),
|
||||
Data::Tag(TAG_RANGE, v) => Ok(Id::Range(Box::new(IdRange::try_from(*v)?))),
|
||||
Data::Tag(TAG_STRING_UUID, v) => v.deref().to_owned().try_into().map(Id::Uuid),
|
||||
Data::Tag(TAG_SPEC_UUID, v) => v.deref().to_owned().try_into().map(Id::Uuid),
|
||||
_ => Err("Expected a CBOR integer, text, array or map"),
|
||||
}
|
||||
}
|
||||
|
@ -578,6 +575,9 @@ impl TryFrom<Id> for Data {
|
|||
Id::Array(v) => Ok(Cbor::try_from(Value::from(v))?.0),
|
||||
Id::Object(v) => Ok(Cbor::try_from(Value::from(v))?.0),
|
||||
Id::Range(v) => Ok(Data::Tag(TAG_RANGE, Box::new(v.deref().to_owned().try_into()?))),
|
||||
Id::Uuid(v) => {
|
||||
Ok(Data::Tag(TAG_SPEC_UUID, Box::new(Data::Bytes(v.into_bytes().into()))))
|
||||
}
|
||||
Id::Generate(_) => Err("Cannot encode an ungenerated Record ID into CBOR"),
|
||||
}
|
||||
}
|
||||
|
@ -604,3 +604,16 @@ impl TryFrom<Vec<(Data, Data)>> for Object {
|
|||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Data> for Uuid {
|
||||
type Error = &'static str;
|
||||
fn try_from(val: Data) -> Result<Self, &'static str> {
|
||||
match val {
|
||||
Data::Bytes(v) if v.len() == 16 => match v.as_slice().try_into() {
|
||||
Ok(v) => Ok(Uuid::from(uuid::Uuid::from_bytes(v))),
|
||||
Err(_) => Err("Expected a CBOR byte array with 16 elements"),
|
||||
},
|
||||
_ => Err("Expected a CBOR byte array with 16 elements"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ pub enum Gen {
|
|||
pub enum Id {
|
||||
Number(i64),
|
||||
String(String),
|
||||
Uuid(Uuid),
|
||||
Array(Array),
|
||||
Object(Object),
|
||||
Generate(Gen),
|
||||
|
@ -79,7 +80,7 @@ impl From<Object> for Id {
|
|||
|
||||
impl From<Uuid> for Id {
|
||||
fn from(v: Uuid) -> Self {
|
||||
Self::String(v.to_raw())
|
||||
Self::Uuid(v)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,13 +189,14 @@ impl Id {
|
|||
}
|
||||
/// Generate a new random UUID
|
||||
pub fn uuid() -> Self {
|
||||
Self::String(Uuid::new_v7().to_raw())
|
||||
Self::Uuid(Uuid::new_v7())
|
||||
}
|
||||
/// Convert the Id to a raw String
|
||||
pub fn to_raw(&self) -> String {
|
||||
match self {
|
||||
Self::Number(v) => v.to_string(),
|
||||
Self::String(v) => v.to_string(),
|
||||
Self::Uuid(v) => v.to_string(),
|
||||
Self::Array(v) => v.to_string(),
|
||||
Self::Object(v) => v.to_string(),
|
||||
Self::Generate(v) => match v {
|
||||
|
@ -212,6 +214,7 @@ impl Display for Id {
|
|||
match self {
|
||||
Self::Number(v) => Display::fmt(v, f),
|
||||
Self::String(v) => Display::fmt(&escape_rid(v), f),
|
||||
Self::Uuid(v) => Display::fmt(v, f),
|
||||
Self::Array(v) => Display::fmt(v, f),
|
||||
Self::Object(v) => Display::fmt(v, f),
|
||||
Self::Generate(v) => match v {
|
||||
|
@ -236,6 +239,7 @@ impl Id {
|
|||
match self {
|
||||
Id::Number(v) => Ok(Id::Number(*v)),
|
||||
Id::String(v) => Ok(Id::String(v.clone())),
|
||||
Id::Uuid(v) => Ok(Id::Uuid(*v)),
|
||||
Id::Array(v) => match v.compute(stk, ctx, opt, doc).await? {
|
||||
Value::Array(v) => Ok(Id::Array(v)),
|
||||
_ => unreachable!(),
|
||||
|
|
|
@ -11,7 +11,9 @@ use super::Datetime;
|
|||
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Uuid";
|
||||
|
||||
#[revisioned(revision = 1)]
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
|
||||
#[derive(
|
||||
Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd, Serialize, Deserialize, Hash,
|
||||
)]
|
||||
#[serde(rename = "$surrealdb::private::sql::Uuid")]
|
||||
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||
#[non_exhaustive]
|
||||
|
|
|
@ -600,6 +600,7 @@ impl From<Id> for Value {
|
|||
match v {
|
||||
Id::Number(v) => v.into(),
|
||||
Id::String(v) => v.into(),
|
||||
Id::Uuid(v) => v.into(),
|
||||
Id::Array(v) => v.into(),
|
||||
Id::Object(v) => v.into(),
|
||||
Id::Generate(v) => match v {
|
||||
|
|
|
@ -32,7 +32,10 @@ impl Parser<'_> {
|
|||
|
||||
fn kind_cast_start_id(kind: TokenKind) -> bool {
|
||||
Self::tokenkind_can_start_ident(kind)
|
||||
|| matches!(kind, TokenKind::Digits | t!("{") | t!("[") | t!("+") | t!("-"))
|
||||
|| matches!(
|
||||
kind,
|
||||
TokenKind::Digits | t!("{") | t!("[") | t!("+") | t!("-") | t!("u'") | t!("u\"")
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn parse_thing_or_range(
|
||||
|
@ -187,6 +190,7 @@ impl Parser<'_> {
|
|||
pub async fn parse_id(&mut self, stk: &mut Stk) -> ParseResult<Id> {
|
||||
let token = self.peek_whitespace();
|
||||
match token.kind {
|
||||
t!("u'") | t!("u\"") => Ok(Id::Uuid(self.next_token_value()?)),
|
||||
t!("{") => {
|
||||
self.pop_peek();
|
||||
// object record id
|
||||
|
|
|
@ -94,7 +94,7 @@ async fn create_with_id() -> Result<(), Error> {
|
|||
let val = Value::parse(
|
||||
"[
|
||||
{
|
||||
id: city:⟨8e60244d-95f6-4f95-9e30-09a98977efb0⟩,
|
||||
id: city:u'8e60244d-95f6-4f95-9e30-09a98977efb0',
|
||||
name: 'London'
|
||||
}
|
||||
]",
|
||||
|
|
Loading…
Reference in a new issue