Allow generating ulid and uuid based on a timestamp (#4384)

This commit is contained in:
Micha de Vries 2024-07-19 13:11:55 +02:00 committed by GitHub
parent c471dc0317
commit 70c682987c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 131 additions and 6 deletions

View file

@ -2,6 +2,7 @@ use crate::cnf::ID_CHARS;
use crate::err::Error; use crate::err::Error;
use crate::sql::uuid::Uuid; use crate::sql::uuid::Uuid;
use crate::sql::value::Value; use crate::sql::value::Value;
use crate::sql::Datetime;
use chrono::{TimeZone, Utc}; use chrono::{TimeZone, Utc};
use nanoid::nanoid; use nanoid::nanoid;
use rand::distributions::{Alphanumeric, DistString}; use rand::distributions::{Alphanumeric, DistString};
@ -150,12 +151,21 @@ pub fn time((range,): (Option<(i64, i64)>,)) -> Result<Value, Error> {
Ok(Utc.timestamp_opt(val, 0).earliest().unwrap().into()) Ok(Utc.timestamp_opt(val, 0).earliest().unwrap().into())
} }
pub fn ulid(_: ()) -> Result<Value, Error> { pub fn ulid((timestamp,): (Option<Datetime>,)) -> Result<Value, Error> {
Ok(Ulid::new().to_string().into()) let ulid = match timestamp {
Some(timestamp) => Ulid::from_datetime(timestamp.0.into()),
None => Ulid::new(),
};
Ok(ulid.to_string().into())
} }
pub fn uuid(_: ()) -> Result<Value, Error> { pub fn uuid((timestamp,): (Option<Datetime>,)) -> Result<Value, Error> {
Ok(Uuid::new().into()) let uuid = match timestamp {
Some(timestamp) => Uuid::new_v7_from_datetime(timestamp),
None => Uuid::new(),
};
Ok(uuid.into())
} }
pub mod uuid { pub mod uuid {
@ -163,12 +173,17 @@ pub mod uuid {
use crate::err::Error; use crate::err::Error;
use crate::sql::uuid::Uuid; use crate::sql::uuid::Uuid;
use crate::sql::value::Value; use crate::sql::value::Value;
use crate::sql::Datetime;
pub fn v4(_: ()) -> Result<Value, Error> { pub fn v4(_: ()) -> Result<Value, Error> {
Ok(Uuid::new_v4().into()) Ok(Uuid::new_v4().into())
} }
pub fn v7(_: ()) -> Result<Value, Error> { pub fn v7((timestamp,): (Option<Datetime>,)) -> Result<Value, Error> {
Ok(Uuid::new_v7().into()) let uuid = match timestamp {
Some(timestamp) => Uuid::new_v7_from_datetime(timestamp),
None => Uuid::new(),
};
Ok(uuid.into())
} }
} }

View file

@ -6,6 +6,8 @@ use std::ops::Deref;
use std::str; use std::str;
use std::str::FromStr; use std::str::FromStr;
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)]
@ -78,6 +80,15 @@ impl Uuid {
pub fn new_v7() -> Self { pub fn new_v7() -> Self {
Self(uuid::Uuid::now_v7()) Self(uuid::Uuid::now_v7())
} }
/// Generate a new V7 UUID
pub fn new_v7_from_datetime(timestamp: Datetime) -> Self {
let ts = uuid::Timestamp::from_unix(
uuid::NoContext,
timestamp.0.timestamp() as u64,
timestamp.0.timestamp_subsec_nanos(),
);
Self(uuid::Uuid::new_v7(ts))
}
/// Convert the Uuid to a raw String /// Convert the Uuid to a raw String
pub fn to_raw(&self) -> String { pub fn to_raw(&self) -> String {
self.0.to_string() self.0.to_string()

View file

@ -2953,6 +2953,39 @@ async fn function_rand_ulid() -> Result<(), Error> {
Ok(()) Ok(())
} }
#[tokio::test]
async fn function_rand_ulid_from_datetime() -> Result<(), Error> {
let sql = r#"
CREATE ONLY test:[rand::ulid()] SET created = time::now(), num = 1;
SLEEP 1ms;
LET $rec = CREATE ONLY test:[rand::ulid()] SET created = time::now(), num = 2;
SLEEP 1ms;
CREATE ONLY test:[rand::ulid()] SET created = time::now(), num = 3;
SELECT VALUE num FROM test:[rand::ulid($rec.created - 1ms)]..;
"#;
let mut test = Test::new(sql).await?;
//
let tmp = test.next()?.result?;
assert!(tmp.is_object());
//
let tmp = test.next()?.result?;
assert!(tmp.is_none());
//
let tmp = test.next()?.result?;
assert!(tmp.is_none());
//
let tmp = test.next()?.result?;
assert!(tmp.is_none());
//
let tmp = test.next()?.result?;
assert!(tmp.is_object());
//
let tmp = test.next()?.result?;
assert_eq!(tmp, Value::parse("[2, 3]"));
//
Ok(())
}
#[tokio::test] #[tokio::test]
async fn function_rand_uuid() -> Result<(), Error> { async fn function_rand_uuid() -> Result<(), Error> {
let sql = r#" let sql = r#"
@ -2966,6 +2999,39 @@ async fn function_rand_uuid() -> Result<(), Error> {
Ok(()) Ok(())
} }
#[tokio::test]
async fn function_rand_uuid_from_datetime() -> Result<(), Error> {
let sql = r#"
CREATE ONLY test:[rand::uuid()] SET created = time::now(), num = 1;
SLEEP 1ms;
LET $rec = CREATE ONLY test:[rand::uuid()] SET created = time::now(), num = 2;
SLEEP 1ms;
CREATE ONLY test:[rand::uuid()] SET created = time::now(), num = 3;
SELECT VALUE num FROM test:[rand::uuid($rec.created - 1ms)]..;
"#;
let mut test = Test::new(sql).await?;
//
let tmp = test.next()?.result?;
assert!(tmp.is_object());
//
let tmp = test.next()?.result?;
assert!(tmp.is_none());
//
let tmp = test.next()?.result?;
assert!(tmp.is_none());
//
let tmp = test.next()?.result?;
assert!(tmp.is_none());
//
let tmp = test.next()?.result?;
assert!(tmp.is_object());
//
let tmp = test.next()?.result?;
assert_eq!(tmp, Value::parse("[2, 3]"));
//
Ok(())
}
#[tokio::test] #[tokio::test]
async fn function_rand_uuid_v4() -> Result<(), Error> { async fn function_rand_uuid_v4() -> Result<(), Error> {
let sql = r#" let sql = r#"
@ -2992,6 +3058,39 @@ async fn function_rand_uuid_v7() -> Result<(), Error> {
Ok(()) Ok(())
} }
#[tokio::test]
async fn function_rand_uuid_v7_from_datetime() -> Result<(), Error> {
let sql = r#"
CREATE ONLY test:[rand::uuid::v7()] SET created = time::now(), num = 1;
SLEEP 1ms;
LET $rec = CREATE ONLY test:[rand::uuid::v7()] SET created = time::now(), num = 2;
SLEEP 1ms;
CREATE ONLY test:[rand::uuid::v7()] SET created = time::now(), num = 3;
SELECT VALUE num FROM test:[rand::uuid::v7($rec.created - 1ms)]..;
"#;
let mut test = Test::new(sql).await?;
//
let tmp = test.next()?.result?;
assert!(tmp.is_object());
//
let tmp = test.next()?.result?;
assert!(tmp.is_none());
//
let tmp = test.next()?.result?;
assert!(tmp.is_none());
//
let tmp = test.next()?.result?;
assert!(tmp.is_none());
//
let tmp = test.next()?.result?;
assert!(tmp.is_object());
//
let tmp = test.next()?.result?;
assert_eq!(tmp, Value::parse("[2, 3]"));
//
Ok(())
}
// -------------------------------------------------- // --------------------------------------------------
// string // string
// -------------------------------------------------- // --------------------------------------------------