diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 2c7fe6e3..352c1d15 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -17,7 +17,7 @@ resolver = "2" [features] # Public features -default = ["protocol-ws", "rustls"] +default = ["protocol-ws", "rustls", "kv-mem"] protocol-http = ["dep:reqwest", "dep:tokio-util"] protocol-ws = ["dep:tokio-tungstenite", "tokio/time"] kv-mem = ["dep:echodb", "tokio/time"] diff --git a/lib/src/api/opt/mod.rs b/lib/src/api/opt/mod.rs index 1e5e11ce..8542effa 100644 --- a/lib/src/api/opt/mod.rs +++ b/lib/src/api/opt/mod.rs @@ -11,6 +11,7 @@ mod tls; use crate::api::err::Error; use crate::sql::constant::ConstantValue; +use crate::sql::id::Gen; use crate::sql::to_value; use crate::sql::Thing; use crate::sql::Value; @@ -325,6 +326,11 @@ fn into_json(value: Value, simplify: bool) -> JsonValue { sql::Id::String(s) => Id::String(s), sql::Id::Array(arr) => Id::Array((arr, simplify).into()), sql::Id::Object(obj) => Id::Object((obj, simplify).into()), + sql::Id::Generate(v) => match v { + Gen::Rand => Id::from((sql::Id::rand(), simplify)), + Gen::Ulid => Id::from((sql::Id::ulid(), simplify)), + Gen::Uuid => Id::from((sql::Id::uuid(), simplify)), + }, } } } diff --git a/lib/src/sql/id.rs b/lib/src/sql/id.rs index bf176db1..2209ca6b 100644 --- a/lib/src/sql/id.rs +++ b/lib/src/sql/id.rs @@ -22,6 +22,14 @@ use serde::{Deserialize, Serialize}; use std::fmt::{self, Display, Formatter}; use ulid::Ulid; +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash)] +#[revisioned(revision = 1)] +pub enum Gen { + Rand, + Ulid, + Uuid, +} + #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash)] #[revisioned(revision = 1)] pub enum Id { @@ -29,6 +37,7 @@ pub enum Id { String(String), Array(Array), Object(Object), + Generate(Gen), } impl From for Id { @@ -149,8 +158,13 @@ impl Id { match self { Self::Number(v) => v.to_string(), Self::String(v) => v.to_string(), - Self::Object(v) => v.to_string(), Self::Array(v) => v.to_string(), + Self::Object(v) => v.to_string(), + Self::Generate(v) => match v { + Gen::Rand => "rand()".to_string(), + Gen::Ulid => "ulid()".to_string(), + Gen::Uuid => "uuid()".to_string(), + }, } } } @@ -160,8 +174,13 @@ impl Display for Id { match self { Self::Number(v) => Display::fmt(v, f), Self::String(v) => Display::fmt(&escape_rid(v), f), - Self::Object(v) => Display::fmt(v, f), Self::Array(v) => Display::fmt(v, f), + Self::Object(v) => Display::fmt(v, f), + Self::Generate(v) => match v { + Gen::Rand => Display::fmt("rand()", f), + Gen::Ulid => Display::fmt("ulid()", f), + Gen::Uuid => Display::fmt("uuid()", f), + }, } } } @@ -178,13 +197,18 @@ impl Id { match self { Id::Number(v) => Ok(Id::Number(*v)), Id::String(v) => Ok(Id::String(v.clone())), + Id::Array(v) => match v.compute(ctx, opt, txn, doc).await? { + Value::Array(v) => Ok(Id::Array(v)), + _ => unreachable!(), + }, Id::Object(v) => match v.compute(ctx, opt, txn, doc).await? { Value::Object(v) => Ok(Id::Object(v)), _ => unreachable!(), }, - Id::Array(v) => match v.compute(ctx, opt, txn, doc).await? { - Value::Array(v) => Ok(Id::Array(v)), - _ => unreachable!(), + Id::Generate(v) => match v { + Gen::Rand => Ok(Self::rand()), + Gen::Ulid => Ok(Self::ulid()), + Gen::Uuid => Ok(Self::uuid()), }, } } diff --git a/lib/src/sql/thing.rs b/lib/src/sql/thing.rs index a1054d76..ccf0f0f8 100644 --- a/lib/src/sql/thing.rs +++ b/lib/src/sql/thing.rs @@ -4,7 +4,7 @@ use crate::doc::CursorDoc; use crate::err::Error; use crate::sql::error::IResult; use crate::sql::escape::escape_rid; -use crate::sql::id::{id, Id}; +use crate::sql::id::{id, Gen, Id}; use crate::sql::ident::ident_raw; use crate::sql::strand::Strand; use crate::sql::value::Value; @@ -135,9 +135,9 @@ fn thing_raw(i: &str) -> IResult<&str, Thing> { let (i, t) = ident_raw(i)?; let (i, _) = char(':')(i)?; let (i, v) = alt(( - map(tag("rand()"), |_| Id::rand()), - map(tag("ulid()"), |_| Id::ulid()), - map(tag("uuid()"), |_| Id::uuid()), + map(tag("rand()"), |_| Id::Generate(Gen::Rand)), + map(tag("ulid()"), |_| Id::Generate(Gen::Ulid)), + map(tag("uuid()"), |_| Id::Generate(Gen::Uuid)), id, ))(i)?; Ok(( diff --git a/lib/src/sql/value/value.rs b/lib/src/sql/value/value.rs index fb95714e..5f596c92 100644 --- a/lib/src/sql/value/value.rs +++ b/lib/src/sql/value/value.rs @@ -23,7 +23,7 @@ use crate::sql::fmt::{Fmt, Pretty}; use crate::sql::function::{self, function, Function}; use crate::sql::future::{future, Future}; use crate::sql::geometry::{geometry, Geometry}; -use crate::sql::id::Id; +use crate::sql::id::{Gen, Id}; use crate::sql::idiom::{self, Idiom}; use crate::sql::kind::Kind; use crate::sql::model::{model, Model}; @@ -531,8 +531,13 @@ impl From for Value { match v { Id::Number(v) => v.into(), Id::String(v) => v.into(), - Id::Object(v) => v.into(), Id::Array(v) => v.into(), + Id::Object(v) => v.into(), + Id::Generate(v) => match v { + Gen::Rand => Id::rand().into(), + Gen::Ulid => Id::ulid().into(), + Gen::Uuid => Id::uuid().into(), + }, } } } diff --git a/lib/tests/create.rs b/lib/tests/create.rs index f72e9d20..df5a8aa7 100644 --- a/lib/tests/create.rs +++ b/lib/tests/create.rs @@ -4,6 +4,7 @@ use surrealdb::dbs::Session; use surrealdb::err::Error; use surrealdb::iam::Role; use surrealdb::kvs::Datastore; +use surrealdb::sql::Part; use surrealdb::sql::Thing; use surrealdb::sql::Value; @@ -136,6 +137,44 @@ async fn create_with_id() -> Result<(), Error> { Ok(()) } +#[tokio::test] +async fn create_with_custom_function() -> Result<(), Error> { + let sql = " + DEFINE FUNCTION fn::record::create($data: any) { + RETURN CREATE person:ulid() CONTENT { data: $data } RETURN AFTER; + }; + RETURN fn::record::create({ test: true, name: 'Tobie' }); + RETURN fn::record::create({ test: true, name: 'Jaime' }); + "; + let dbs = Datastore::new("memory").await?; + let ses = Session::owner().with_ns("test").with_db("test"); + let res = &mut dbs.execute(sql, &ses, None).await?; + assert_eq!(res.len(), 3); + // + let tmp = res.remove(0).result; + assert!(tmp.is_ok()); + // + let tmp = res.remove(0).result?.pick(&[Part::from("data")]); + let val = Value::parse( + "{ + test: true, + name: 'Tobie' + }", + ); + assert_eq!(tmp, val); + // + let tmp = res.remove(0).result?.pick(&[Part::from("data")]); + let val = Value::parse( + "{ + test: true, + name: 'Jaime' + }", + ); + assert_eq!(tmp, val); + // + Ok(()) +} + #[tokio::test] async fn create_or_insert_with_permissions() -> Result<(), Error> { let sql = "