Allow custom Record IDs in RELATE statements

This commit is contained in:
Tobie Morgan Hitchcock 2022-09-30 21:33:33 +01:00
parent 2bd2c3ee0b
commit cb7c159d71
8 changed files with 179 additions and 12 deletions

View file

@ -37,11 +37,11 @@ impl Data {
// Fetch
pub(crate) fn rid(&self, tb: &Table) -> Result<Thing, Error> {
match self {
Data::MergeExpression(v) => v.generate(tb, false),
Data::ReplaceExpression(v) => v.generate(tb, false),
Data::ContentExpression(v) => v.generate(tb, false),
Data::MergeExpression(v) => v.rid().generate(tb, false),
Data::ReplaceExpression(v) => v.rid().generate(tb, false),
Data::ContentExpression(v) => v.rid().generate(tb, false),
Data::SetExpression(v) => match v.iter().find(|f| f.0.is_id()) {
Some((_, _, v)) => v.generate(tb, false),
Some((_, _, v)) => v.clone().generate(tb, false),
_ => Ok(tb.generate()),
},
_ => Ok(tb.generate()),

View file

@ -65,7 +65,7 @@ impl InsertStatement {
o.set(ctx, opt, txn, k, v).await?;
}
// Specify the new table record id
let id = o.generate(&self.into, true)?;
let id = o.rid().generate(&self.into, true)?;
// Pass the mergeable to the iterator
i.ingest(Iterable::Mergeable(id, o));
}
@ -77,14 +77,14 @@ impl InsertStatement {
Value::Array(v) => {
for v in v {
// Specify the new table record id
let id = v.generate(&self.into, true)?;
let id = v.rid().generate(&self.into, true)?;
// Pass the mergeable to the iterator
i.ingest(Iterable::Mergeable(id, v));
}
}
Value::Object(_) => {
// Specify the new table record id
let id = v.generate(&self.into, true)?;
let id = v.rid().generate(&self.into, true)?;
// Pass the mergeable to the iterator
i.ingest(Iterable::Mergeable(id, v));
}

View file

@ -148,8 +148,17 @@ impl RelateStatement {
for w in with.iter() {
let f = f.clone();
let w = w.clone();
let t = self.kind.generate();
i.ingest(Iterable::Relatable(f, t, w));
match &self.data {
// There is a data clause so check for a record id
Some(data) => match data.rid(&self.kind) {
// There was a problem creating the record id
Err(e) => return Err(e),
// There is an id field so use the record id
Ok(t) => i.ingest(Iterable::Relatable(f, t, w)),
},
// There is no data clause so create a record id
None => i.ingest(Iterable::Relatable(f, self.kind.generate(), w)),
};
}
}
// Assign the statement

View file

@ -1,13 +1,12 @@
use crate::err::Error;
use crate::sql::id::Id;
use crate::sql::paths::ID;
use crate::sql::table::Table;
use crate::sql::thing::Thing;
use crate::sql::value::Value;
impl Value {
pub fn generate(&self, tb: &Table, retable: bool) -> Result<Thing, Error> {
match self.pick(&*ID) {
pub fn generate(self, tb: &Table, retable: bool) -> Result<Thing, Error> {
match self {
// There is a floating point number for the id field
Value::Number(id) if id.is_float() => Ok(Thing {
tb: tb.to_string(),

View file

@ -25,6 +25,7 @@ mod patch;
mod pick;
mod put;
mod replace;
mod rid;
mod set;
mod single;
mod walk;

34
lib/src/sql/value/rid.rs Normal file
View file

@ -0,0 +1,34 @@
use crate::sql::paths::ID;
use crate::sql::value::Value;
impl Value {
pub fn rid(&self) -> Value {
self.pick(&*ID)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sql::id::Id;
use crate::sql::test::Parse;
use crate::sql::thing::Thing;
#[tokio::test]
async fn rid_none() {
let val = Value::parse("{ test: { other: null, something: 123 } }");
let res = Value::None;
assert_eq!(res, val.rid());
}
#[tokio::test]
async fn rid_some() {
let val = Value::parse("{ id: test:id, test: { other: null, something: 123 } }");
let res = Value::Thing(Thing {
tb: String::from("test"),
id: Id::from("id"),
});
assert_eq!(res, val.rid());
}
}

102
lib/tests/create.rs Normal file
View file

@ -0,0 +1,102 @@
mod parse;
use parse::Parse;
use surrealdb::sql::Value;
use surrealdb::Datastore;
use surrealdb::Error;
use surrealdb::Session;
#[tokio::test]
async fn create_with_id() -> Result<(), Error> {
let sql = "
CREATE person:test SET name = 'Tester';
CREATE person SET id = person:tobie, name = 'Tobie';
CREATE person CONTENT { id: person:jaime, name: 'Jaime' };
CREATE user CONTENT { id: 1, name: 'Robert' };
CREATE city CONTENT { id: 'london', name: 'London' };
CREATE city CONTENT { id: '8e60244d-95f6-4f95-9e30-09a98977efb0', name: 'London' };
CREATE temperature CONTENT { id: ['London', '2022-09-30T20:25:01.406828Z'], name: 'London' };
";
let dbs = Datastore::new("memory").await?;
let ses = Session::for_kv().with_ns("test").with_db("test");
let res = &mut dbs.execute(&sql, &ses, None, false).await?;
assert_eq!(res.len(), 7);
//
let tmp = res.remove(0).result?;
let val = Value::parse(
"[
{
id: person:test,
name: 'Tester'
}
]",
);
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result?;
let val = Value::parse(
"[
{
id: person:tobie,
name: 'Tobie'
}
]",
);
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result?;
let val = Value::parse(
"[
{
id: person:jaime,
name: 'Jaime'
}
]",
);
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result?;
let val = Value::parse(
"[
{
id: user:1,
name: 'Robert'
}
]",
);
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result?;
let val = Value::parse(
"[
{
id: city:london,
name: 'London'
}
]",
);
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result?;
let val = Value::parse(
"[
{
id: city:8e60244d-95f6-4f95-9e30-09a98977efb0,
name: 'London'
}
]",
);
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result?;
let val = Value::parse(
"[
{
id: temperature:['London', '2022-09-30T20:25:01.406828Z'],
name: 'London'
}
]",
);
assert_eq!(tmp, val);
//
Ok(())
}

View file

@ -98,6 +98,28 @@ async fn insert_statement_values_multiple() -> Result<(), Error> {
Ok(())
}
#[tokio::test]
async fn insert_statement_values_retable_id() -> Result<(), Error> {
let sql = "
INSERT INTO test (id, test, something) VALUES (person:1, true, 'other'), (person:2, false, 'else');
";
let dbs = Datastore::new("memory").await?;
let ses = Session::for_kv().with_ns("test").with_db("test");
let res = &mut dbs.execute(&sql, &ses, None, false).await?;
assert_eq!(res.len(), 1);
//
let tmp = res.remove(0).result?;
let val = Value::parse(
"[
{ id: test:1, test: true, something: 'other' },
{ id: test:2, test: false, something: 'else' }
]",
);
assert_eq!(tmp, val);
//
Ok(())
}
#[tokio::test]
async fn insert_statement_on_duplicate_key() -> Result<(), Error> {
let sql = "