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> {
|
fn eval_id(&self, id: &Id) -> Option<Id> {
|
||||||
match 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::Array(a) => self.eval_array(a).map(Id::Array),
|
||||||
Id::Object(o) => self.eval_object(o).map(Id::Object),
|
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))),
|
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 id1 = thing.id;
|
||||||
let val = Thing::new("testns", "testdb", "testtb", id1);
|
let val = Thing::new("testns", "testdb", "testtb", id1);
|
||||||
let enc = Thing::encode(&val).unwrap();
|
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();
|
let dec = Thing::decode(&enc).unwrap();
|
||||||
assert_eq!(val, dec);
|
assert_eq!(val, dec);
|
||||||
|
@ -96,7 +96,7 @@ mod tests {
|
||||||
let id2 = thing.id;
|
let id2 = thing.id;
|
||||||
let val = Thing::new("testns", "testdb", "testtb", id2);
|
let val = Thing::new("testns", "testdb", "testtb", id2);
|
||||||
let enc = Thing::encode(&val).unwrap();
|
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();
|
let dec = Thing::decode(&enc).unwrap();
|
||||||
assert_eq!(val, dec);
|
assert_eq!(val, dec);
|
||||||
|
|
|
@ -116,13 +116,7 @@ impl TryFrom<Cbor> for Value {
|
||||||
_ => Err("Expected a CBOR text data type"),
|
_ => Err("Expected a CBOR text data type"),
|
||||||
},
|
},
|
||||||
// A byte string uuid
|
// A byte string uuid
|
||||||
TAG_SPEC_UUID => match *v {
|
TAG_SPEC_UUID => v.deref().to_owned().try_into().map(Value::Uuid),
|
||||||
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"),
|
|
||||||
},
|
|
||||||
// A literal decimal
|
// A literal decimal
|
||||||
TAG_STRING_DECIMAL => match *v {
|
TAG_STRING_DECIMAL => match *v {
|
||||||
Data::Text(v) => match Decimal::from_str(v.as_str()) {
|
Data::Text(v) => match Decimal::from_str(v.as_str()) {
|
||||||
|
@ -393,6 +387,7 @@ impl TryFrom<Value> for Cbor {
|
||||||
match v.id {
|
match v.id {
|
||||||
Id::Number(v) => Data::Integer(v.into()),
|
Id::Number(v) => Data::Integer(v.into()),
|
||||||
Id::String(v) => Data::Text(v),
|
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::Array(v) => Cbor::try_from(Value::from(v))?.0,
|
||||||
Id::Object(v) => Cbor::try_from(Value::from(v))?.0,
|
Id::Object(v) => Cbor::try_from(Value::from(v))?.0,
|
||||||
Id::Generate(_) => {
|
Id::Generate(_) => {
|
||||||
|
@ -564,6 +559,8 @@ impl TryFrom<Data> for Id {
|
||||||
Data::Array(v) => Ok(Id::Array(v.try_into()?)),
|
Data::Array(v) => Ok(Id::Array(v.try_into()?)),
|
||||||
Data::Map(v) => Ok(Id::Object(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_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"),
|
_ => 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::Array(v) => Ok(Cbor::try_from(Value::from(v))?.0),
|
||||||
Id::Object(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::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"),
|
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 {
|
pub enum Id {
|
||||||
Number(i64),
|
Number(i64),
|
||||||
String(String),
|
String(String),
|
||||||
|
Uuid(Uuid),
|
||||||
Array(Array),
|
Array(Array),
|
||||||
Object(Object),
|
Object(Object),
|
||||||
Generate(Gen),
|
Generate(Gen),
|
||||||
|
@ -79,7 +80,7 @@ impl From<Object> for Id {
|
||||||
|
|
||||||
impl From<Uuid> for Id {
|
impl From<Uuid> for Id {
|
||||||
fn from(v: Uuid) -> Self {
|
fn from(v: Uuid) -> Self {
|
||||||
Self::String(v.to_raw())
|
Self::Uuid(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,13 +189,14 @@ impl Id {
|
||||||
}
|
}
|
||||||
/// Generate a new random UUID
|
/// Generate a new random UUID
|
||||||
pub fn uuid() -> Self {
|
pub fn uuid() -> Self {
|
||||||
Self::String(Uuid::new_v7().to_raw())
|
Self::Uuid(Uuid::new_v7())
|
||||||
}
|
}
|
||||||
/// Convert the Id to a raw String
|
/// Convert the Id to a raw String
|
||||||
pub fn to_raw(&self) -> String {
|
pub fn to_raw(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
Self::Number(v) => v.to_string(),
|
Self::Number(v) => v.to_string(),
|
||||||
Self::String(v) => v.to_string(),
|
Self::String(v) => v.to_string(),
|
||||||
|
Self::Uuid(v) => v.to_string(),
|
||||||
Self::Array(v) => v.to_string(),
|
Self::Array(v) => v.to_string(),
|
||||||
Self::Object(v) => v.to_string(),
|
Self::Object(v) => v.to_string(),
|
||||||
Self::Generate(v) => match v {
|
Self::Generate(v) => match v {
|
||||||
|
@ -212,6 +214,7 @@ impl Display for Id {
|
||||||
match self {
|
match self {
|
||||||
Self::Number(v) => Display::fmt(v, f),
|
Self::Number(v) => Display::fmt(v, f),
|
||||||
Self::String(v) => Display::fmt(&escape_rid(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::Array(v) => Display::fmt(v, f),
|
||||||
Self::Object(v) => Display::fmt(v, f),
|
Self::Object(v) => Display::fmt(v, f),
|
||||||
Self::Generate(v) => match v {
|
Self::Generate(v) => match v {
|
||||||
|
@ -236,6 +239,7 @@ impl Id {
|
||||||
match self {
|
match self {
|
||||||
Id::Number(v) => Ok(Id::Number(*v)),
|
Id::Number(v) => Ok(Id::Number(*v)),
|
||||||
Id::String(v) => Ok(Id::String(v.clone())),
|
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? {
|
Id::Array(v) => match v.compute(stk, ctx, opt, doc).await? {
|
||||||
Value::Array(v) => Ok(Id::Array(v)),
|
Value::Array(v) => Ok(Id::Array(v)),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
|
|
|
@ -11,7 +11,9 @@ use super::Datetime;
|
||||||
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Uuid";
|
pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Uuid";
|
||||||
|
|
||||||
#[revisioned(revision = 1)]
|
#[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")]
|
#[serde(rename = "$surrealdb::private::sql::Uuid")]
|
||||||
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
|
|
|
@ -600,6 +600,7 @@ impl From<Id> for Value {
|
||||||
match v {
|
match v {
|
||||||
Id::Number(v) => v.into(),
|
Id::Number(v) => v.into(),
|
||||||
Id::String(v) => v.into(),
|
Id::String(v) => v.into(),
|
||||||
|
Id::Uuid(v) => v.into(),
|
||||||
Id::Array(v) => v.into(),
|
Id::Array(v) => v.into(),
|
||||||
Id::Object(v) => v.into(),
|
Id::Object(v) => v.into(),
|
||||||
Id::Generate(v) => match v {
|
Id::Generate(v) => match v {
|
||||||
|
|
|
@ -32,7 +32,10 @@ impl Parser<'_> {
|
||||||
|
|
||||||
fn kind_cast_start_id(kind: TokenKind) -> bool {
|
fn kind_cast_start_id(kind: TokenKind) -> bool {
|
||||||
Self::tokenkind_can_start_ident(kind)
|
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(
|
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> {
|
pub async fn parse_id(&mut self, stk: &mut Stk) -> ParseResult<Id> {
|
||||||
let token = self.peek_whitespace();
|
let token = self.peek_whitespace();
|
||||||
match token.kind {
|
match token.kind {
|
||||||
|
t!("u'") | t!("u\"") => Ok(Id::Uuid(self.next_token_value()?)),
|
||||||
t!("{") => {
|
t!("{") => {
|
||||||
self.pop_peek();
|
self.pop_peek();
|
||||||
// object record id
|
// object record id
|
||||||
|
|
|
@ -94,7 +94,7 @@ async fn create_with_id() -> Result<(), Error> {
|
||||||
let val = Value::parse(
|
let val = Value::parse(
|
||||||
"[
|
"[
|
||||||
{
|
{
|
||||||
id: city:⟨8e60244d-95f6-4f95-9e30-09a98977efb0⟩,
|
id: city:u'8e60244d-95f6-4f95-9e30-09a98977efb0',
|
||||||
name: 'London'
|
name: 'London'
|
||||||
}
|
}
|
||||||
]",
|
]",
|
||||||
|
|
Loading…
Reference in a new issue