Add UUID data type to SQL for efficient storage of UUIDs

This commit is contained in:
Tobie Morgan Hitchcock 2022-06-25 23:37:45 +01:00
parent d04db02ee0
commit ccdce709f2
7 changed files with 164 additions and 18 deletions

4
Cargo.lock generated
View file

@ -2623,9 +2623,9 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]] [[package]]
name = "storekey" name = "storekey"
version = "0.2.0" version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c486bc18d702687cb3265288657cfe89df0f2f1a88900f104a2cc86889bddd7" checksum = "3c05ac79ed7dcd90b469801242eb9f348cd5749404a567bf36d2d6c3c5ff6698"
dependencies = [ dependencies = [
"byteorder", "byteorder",
"serde", "serde",

View file

@ -43,7 +43,7 @@ regex = "1.5.6"
msgpack = { version = "1.1.0", package = "rmp-serde" } msgpack = { version = "1.1.0", package = "rmp-serde" }
scrypt = "0.10.0" scrypt = "0.10.0"
serde = { version = "1.0.137", features = ["derive"] } serde = { version = "1.0.137", features = ["derive"] }
storekey = "0.2.0" storekey = "0.3.0"
sha-1 = "0.10.0" sha-1 = "0.10.0"
sha2 = "0.10.2" sha2 = "0.10.2"
slug = "0.1.4" slug = "0.1.4"

View file

@ -2,11 +2,11 @@ use crate::cnf::ID_CHARS;
use crate::ctx::Context; use crate::ctx::Context;
use crate::err::Error; use crate::err::Error;
use crate::sql::datetime::Datetime; use crate::sql::datetime::Datetime;
use crate::sql::uuid::Uuid;
use crate::sql::value::Value; use crate::sql::value::Value;
use nanoid::nanoid; use nanoid::nanoid;
use rand::distributions::Alphanumeric; use rand::distributions::Alphanumeric;
use rand::Rng; use rand::Rng;
use uuid::Uuid;
pub fn rand(_: &Context, _: Vec<Value>) -> Result<Value, Error> { pub fn rand(_: &Context, _: Vec<Value>) -> Result<Value, Error> {
Ok(rand::random::<f64>().into()) Ok(rand::random::<f64>().into())
@ -147,5 +147,5 @@ pub fn time(_: &Context, mut args: Vec<Value>) -> Result<Value, Error> {
} }
pub fn uuid(_: &Context, _: Vec<Value>) -> Result<Value, Error> { pub fn uuid(_: &Context, _: Vec<Value>) -> Result<Value, Error> {
Ok(Uuid::new_v4().to_string().into()) Ok(Uuid::new().into())
} }

View file

@ -23,6 +23,11 @@ pub fn commas(i: &str) -> IResult<&str, ()> {
Ok((i, ())) Ok((i, ()))
} }
#[inline]
pub fn is_hex(chr: char) -> bool {
chr.is_ascii_hexdigit()
}
#[inline] #[inline]
pub fn is_digit(chr: char) -> bool { pub fn is_digit(chr: char) -> bool {
(0x30..=0x39).contains(&(chr as u8)) (0x30..=0x39).contains(&(chr as u8))

View file

@ -48,6 +48,7 @@ pub(crate) mod subquery;
pub(crate) mod table; pub(crate) mod table;
pub(crate) mod thing; pub(crate) mod thing;
pub(crate) mod timeout; pub(crate) mod timeout;
pub(crate) mod uuid;
pub(crate) mod value; pub(crate) mod value;
pub(crate) mod version; pub(crate) mod version;
pub(crate) mod view; pub(crate) mod view;
@ -112,6 +113,7 @@ pub use self::table::Table;
pub use self::table::Tables; pub use self::table::Tables;
pub use self::thing::Thing; pub use self::thing::Thing;
pub use self::timeout::Timeout; pub use self::timeout::Timeout;
pub use self::uuid::Uuid;
pub use self::value::Value; pub use self::value::Value;
pub use self::value::Values; pub use self::value::Values;
pub use self::version::Version; pub use self::version::Version;

121
lib/src/sql/uuid.rs Normal file
View file

@ -0,0 +1,121 @@
use crate::sql::common::is_hex;
use crate::sql::error::IResult;
use crate::sql::serde::is_internal_serialization;
use nom::branch::alt;
use nom::bytes::complete::take_while_m_n;
use nom::character::complete::char;
use nom::combinator::recognize;
use nom::sequence::delimited;
use nom::sequence::tuple;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::ops::Deref;
use std::str;
const SINGLE: char = '\'';
const DOUBLE: char = '"';
#[derive(Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd, Deserialize)]
pub struct Uuid(pub uuid::Uuid);
impl From<&str> for Uuid {
fn from(s: &str) -> Self {
match uuid::Uuid::try_parse(s) {
Ok(v) => Uuid(v),
_ => Uuid::default(),
}
}
}
impl From<String> for Uuid {
fn from(s: String) -> Self {
match uuid::Uuid::try_parse(&s) {
Ok(v) => Uuid(v),
_ => Uuid::default(),
}
}
}
impl Deref for Uuid {
type Target = uuid::Uuid;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Uuid {
pub fn new() -> Self {
Uuid(uuid::Uuid::new_v4())
}
pub fn to_raw(&self) -> String {
self.0.to_string()
}
}
impl fmt::Display for Uuid {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "\"{}\"", self.0)
}
}
impl Serialize for Uuid {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
if is_internal_serialization() {
serializer.serialize_newtype_struct("Uuid", &self.0)
} else {
serializer.serialize_some(&self.0)
}
}
}
pub fn uuid(i: &str) -> IResult<&str, Uuid> {
alt((
delimited(char(DOUBLE), uuid_raw, char(DOUBLE)),
delimited(char(SINGLE), uuid_raw, char(SINGLE)),
))(i)
}
fn uuid_raw(i: &str) -> IResult<&str, Uuid> {
let (i, v) = recognize(tuple((
take_while_m_n(8, 8, is_hex),
char('-'),
take_while_m_n(4, 4, is_hex),
char('-'),
alt((char('1'), char('2'), char('3'), char('4'))),
take_while_m_n(3, 3, is_hex),
char('-'),
take_while_m_n(4, 4, is_hex),
char('-'),
take_while_m_n(12, 12, is_hex),
)))(i)?;
Ok((i, Uuid::from(v)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn uuid_v1() {
let sql = "e72bee20-f49b-11ec-b939-0242ac120002";
let res = uuid_raw(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("\"e72bee20-f49b-11ec-b939-0242ac120002\"", format!("{}", out));
assert_eq!(out, Uuid::from("e72bee20-f49b-11ec-b939-0242ac120002"));
}
#[test]
fn uuid_v4() {
let sql = "b19bc00b-aa98-486c-ae37-c8e1c54295b1";
let res = uuid_raw(sql);
assert!(res.is_ok());
let out = res.unwrap().1;
assert_eq!("\"b19bc00b-aa98-486c-ae37-c8e1c54295b1\"", format!("{}", out));
assert_eq!(out, Uuid::from("b19bc00b-aa98-486c-ae37-c8e1c54295b1"));
}
}

View file

@ -28,6 +28,7 @@ use crate::sql::strand::{strand, Strand};
use crate::sql::subquery::{subquery, Subquery}; use crate::sql::subquery::{subquery, Subquery};
use crate::sql::table::{table, Table}; use crate::sql::table::{table, Table};
use crate::sql::thing::{thing, Thing}; use crate::sql::thing::{thing, Thing};
use crate::sql::uuid::{uuid as unique, Uuid};
use async_recursion::async_recursion; use async_recursion::async_recursion;
use bigdecimal::BigDecimal; use bigdecimal::BigDecimal;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
@ -101,6 +102,7 @@ pub enum Value {
Strand(Strand), Strand(Strand),
Duration(Duration), Duration(Duration),
Datetime(Datetime), Datetime(Datetime),
Uuid(Uuid),
Array(Array), Array(Array),
Object(Object), Object(Object),
Geometry(Geometry), Geometry(Geometry),
@ -140,6 +142,12 @@ impl From<bool> for Value {
} }
} }
impl From<Uuid> for Value {
fn from(v: Uuid) -> Self {
Value::Uuid(v)
}
}
impl From<Param> for Value { impl From<Param> for Value {
fn from(v: Param) -> Self { fn from(v: Param) -> Self {
Value::Param(v) Value::Param(v)
@ -823,6 +831,10 @@ impl Value {
}, },
_ => false, _ => false,
}, },
Value::Uuid(v) => match other {
Value::Uuid(w) => v == w,
_ => false,
},
Value::Array(v) => match other { Value::Array(v) => match other {
Value::Array(w) => v == w, Value::Array(w) => v == w,
_ => false, _ => false,
@ -989,6 +1001,7 @@ impl fmt::Display for Value {
Value::Strand(v) => write!(f, "{}", v), Value::Strand(v) => write!(f, "{}", v),
Value::Duration(v) => write!(f, "{}", v), Value::Duration(v) => write!(f, "{}", v),
Value::Datetime(v) => write!(f, "{}", v), Value::Datetime(v) => write!(f, "{}", v),
Value::Uuid(v) => write!(f, "{}", v),
Value::Array(v) => write!(f, "{}", v), Value::Array(v) => write!(f, "{}", v),
Value::Object(v) => write!(f, "{}", v), Value::Object(v) => write!(f, "{}", v),
Value::Geometry(v) => write!(f, "{}", v), Value::Geometry(v) => write!(f, "{}", v),
@ -1061,19 +1074,20 @@ impl Serialize for Value {
Value::Strand(v) => s.serialize_newtype_variant("Value", 6, "Strand", v), Value::Strand(v) => s.serialize_newtype_variant("Value", 6, "Strand", v),
Value::Duration(v) => s.serialize_newtype_variant("Value", 7, "Duration", v), Value::Duration(v) => s.serialize_newtype_variant("Value", 7, "Duration", v),
Value::Datetime(v) => s.serialize_newtype_variant("Value", 8, "Datetime", v), Value::Datetime(v) => s.serialize_newtype_variant("Value", 8, "Datetime", v),
Value::Array(v) => s.serialize_newtype_variant("Value", 9, "Array", v), Value::Uuid(v) => s.serialize_newtype_variant("Value", 9, "Uuid", v),
Value::Object(v) => s.serialize_newtype_variant("Value", 10, "Object", v), Value::Array(v) => s.serialize_newtype_variant("Value", 10, "Array", v),
Value::Geometry(v) => s.serialize_newtype_variant("Value", 11, "Geometry", v), Value::Object(v) => s.serialize_newtype_variant("Value", 11, "Object", v),
Value::Param(v) => s.serialize_newtype_variant("Value", 12, "Param", v), Value::Geometry(v) => s.serialize_newtype_variant("Value", 12, "Geometry", v),
Value::Idiom(v) => s.serialize_newtype_variant("Value", 13, "Idiom", v), Value::Param(v) => s.serialize_newtype_variant("Value", 13, "Param", v),
Value::Table(v) => s.serialize_newtype_variant("Value", 14, "Table", v), Value::Idiom(v) => s.serialize_newtype_variant("Value", 14, "Idiom", v),
Value::Thing(v) => s.serialize_newtype_variant("Value", 15, "Thing", v), Value::Table(v) => s.serialize_newtype_variant("Value", 15, "Table", v),
Value::Model(v) => s.serialize_newtype_variant("Value", 16, "Model", v), Value::Thing(v) => s.serialize_newtype_variant("Value", 16, "Thing", v),
Value::Regex(v) => s.serialize_newtype_variant("Value", 17, "Regex", v), Value::Model(v) => s.serialize_newtype_variant("Value", 17, "Model", v),
Value::Edges(v) => s.serialize_newtype_variant("Value", 18, "Edges", v), Value::Regex(v) => s.serialize_newtype_variant("Value", 18, "Regex", v),
Value::Function(v) => s.serialize_newtype_variant("Value", 19, "Function", v), Value::Edges(v) => s.serialize_newtype_variant("Value", 19, "Edges", v),
Value::Subquery(v) => s.serialize_newtype_variant("Value", 20, "Subquery", v), Value::Function(v) => s.serialize_newtype_variant("Value", 20, "Function", v),
Value::Expression(v) => s.serialize_newtype_variant("Value", 21, "Expression", v), Value::Subquery(v) => s.serialize_newtype_variant("Value", 21, "Subquery", v),
Value::Expression(v) => s.serialize_newtype_variant("Value", 22, "Expression", v),
} }
} else { } else {
match self { match self {
@ -1083,6 +1097,7 @@ impl Serialize for Value {
Value::True => s.serialize_bool(true), Value::True => s.serialize_bool(true),
Value::False => s.serialize_bool(false), Value::False => s.serialize_bool(false),
Value::Thing(v) => s.serialize_some(v), Value::Thing(v) => s.serialize_some(v),
Value::Uuid(v) => s.serialize_some(v),
Value::Array(v) => s.serialize_some(v), Value::Array(v) => s.serialize_some(v),
Value::Object(v) => s.serialize_some(v), Value::Object(v) => s.serialize_some(v),
Value::Number(v) => s.serialize_some(v), Value::Number(v) => s.serialize_some(v),
@ -1164,6 +1179,7 @@ pub fn single(i: &str) -> IResult<&str, Value> {
map(datetime, Value::from), map(datetime, Value::from),
map(duration, Value::from), map(duration, Value::from),
map(geometry, Value::from), map(geometry, Value::from),
map(unique, Value::from),
map(number, Value::from), map(number, Value::from),
map(strand, Value::from), map(strand, Value::from),
map(object, Value::from), map(object, Value::from),
@ -1188,6 +1204,7 @@ pub fn select(i: &str) -> IResult<&str, Value> {
map(datetime, Value::from), map(datetime, Value::from),
map(duration, Value::from), map(duration, Value::from),
map(geometry, Value::from), map(geometry, Value::from),
map(unique, Value::from),
map(number, Value::from), map(number, Value::from),
map(strand, Value::from), map(strand, Value::from),
map(object, Value::from), map(object, Value::from),
@ -1221,6 +1238,7 @@ pub fn json(i: &str) -> IResult<&str, Value> {
map(datetime, Value::from), map(datetime, Value::from),
map(duration, Value::from), map(duration, Value::from),
map(geometry, Value::from), map(geometry, Value::from),
map(unique, Value::from),
map(number, Value::from), map(number, Value::from),
map(object, Value::from), map(object, Value::from),
map(array, Value::from), map(array, Value::from),