Ensure user $input
is accessible with all Data
pattern types (#1945)
This commit is contained in:
parent
f0e5753cd1
commit
bfbadc6fdd
4 changed files with 138 additions and 13 deletions
|
@ -4,7 +4,6 @@ use crate::dbs::Statement;
|
||||||
use crate::dbs::Transaction;
|
use crate::dbs::Transaction;
|
||||||
use crate::doc::Document;
|
use crate::doc::Document;
|
||||||
use crate::err::Error;
|
use crate::err::Error;
|
||||||
use crate::sql::data::Data;
|
|
||||||
use crate::sql::permission::Permission;
|
use crate::sql::permission::Permission;
|
||||||
use crate::sql::value::Value;
|
use crate::sql::value::Value;
|
||||||
|
|
||||||
|
@ -14,7 +13,7 @@ impl<'a> Document<'a> {
|
||||||
ctx: &Context<'_>,
|
ctx: &Context<'_>,
|
||||||
opt: &Options,
|
opt: &Options,
|
||||||
txn: &Transaction,
|
txn: &Transaction,
|
||||||
stm: &Statement<'_>,
|
_stm: &Statement<'_>,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
// Check fields
|
// Check fields
|
||||||
if !opt.fields {
|
if !opt.fields {
|
||||||
|
@ -22,6 +21,8 @@ impl<'a> Document<'a> {
|
||||||
}
|
}
|
||||||
// Get the record id
|
// Get the record id
|
||||||
let rid = self.id.as_ref().unwrap();
|
let rid = self.id.as_ref().unwrap();
|
||||||
|
// Get the user applied input
|
||||||
|
let inp = self.initial.changed(self.current.as_ref());
|
||||||
// Loop through all field statements
|
// Loop through all field statements
|
||||||
for fd in self.fd(opt, txn).await?.iter() {
|
for fd in self.fd(opt, txn).await?.iter() {
|
||||||
// Loop over each field in document
|
// Loop over each field in document
|
||||||
|
@ -29,16 +30,7 @@ impl<'a> Document<'a> {
|
||||||
// Get the initial value
|
// Get the initial value
|
||||||
let old = self.initial.pick(&k);
|
let old = self.initial.pick(&k);
|
||||||
// Get the input value
|
// Get the input value
|
||||||
let inp = match stm.data() {
|
let inp = inp.pick(&k);
|
||||||
Some(Data::MergeExpression(v)) => v.pick(&k),
|
|
||||||
Some(Data::ContentExpression(v)) => v.pick(&k),
|
|
||||||
Some(Data::ReplaceExpression(v)) => v.pick(&k),
|
|
||||||
Some(Data::SetExpression(v)) => match v.iter().find(|f| f.0.eq(&k)) {
|
|
||||||
Some((_, _, v)) => v.to_owned(),
|
|
||||||
_ => Value::None,
|
|
||||||
},
|
|
||||||
_ => Value::None,
|
|
||||||
};
|
|
||||||
// Check for a TYPE clause
|
// Check for a TYPE clause
|
||||||
if let Some(kind) = &fd.kind {
|
if let Some(kind) = &fd.kind {
|
||||||
if !val.is_none() {
|
if !val.is_none() {
|
||||||
|
|
94
lib/src/sql/value/changed.rs
Normal file
94
lib/src/sql/value/changed.rs
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
use crate::sql::idiom::Idiom;
|
||||||
|
use crate::sql::value::Value;
|
||||||
|
|
||||||
|
impl Value {
|
||||||
|
pub(crate) fn changed(&self, val: &Value) -> Value {
|
||||||
|
match (self, val) {
|
||||||
|
(Value::Object(a), Value::Object(b)) => {
|
||||||
|
// Create an object
|
||||||
|
let mut chg = Value::base();
|
||||||
|
// Loop over old keys
|
||||||
|
for (key, _) in a.iter() {
|
||||||
|
if !b.contains_key(key) {
|
||||||
|
let path = Idiom::from(key.clone());
|
||||||
|
chg.put(&path, Value::None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Loop over new keys
|
||||||
|
for (key, val) in b.iter() {
|
||||||
|
match a.get(key) {
|
||||||
|
// Key did not exist
|
||||||
|
None => {
|
||||||
|
let path = Idiom::from(key.clone());
|
||||||
|
chg.put(&path, val.clone());
|
||||||
|
}
|
||||||
|
Some(old) => {
|
||||||
|
if old != val {
|
||||||
|
let path = Idiom::from(key.clone());
|
||||||
|
chg.put(&path, old.changed(val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//
|
||||||
|
chg
|
||||||
|
}
|
||||||
|
(_, _) => val.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use crate::sql::test::Parse;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn changed_none() {
|
||||||
|
let old = Value::parse("{ test: true, text: 'text', other: { something: true } }");
|
||||||
|
let now = Value::parse("{ test: true, text: 'text', other: { something: true } }");
|
||||||
|
let res = Value::parse("{}");
|
||||||
|
assert_eq!(res, old.changed(&now));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn changed_add() {
|
||||||
|
let old = Value::parse("{ test: true }");
|
||||||
|
let now = Value::parse("{ test: true, other: 'test' }");
|
||||||
|
let res = Value::parse("{ other: 'test' }");
|
||||||
|
assert_eq!(res, old.changed(&now));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn changed_remove() {
|
||||||
|
let old = Value::parse("{ test: true, other: 'test' }");
|
||||||
|
let now = Value::parse("{ test: true }");
|
||||||
|
let res = Value::parse("{ other: NONE }]");
|
||||||
|
assert_eq!(res, old.changed(&now));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn changed_add_array() {
|
||||||
|
let old = Value::parse("{ test: [1,2,3] }");
|
||||||
|
let now = Value::parse("{ test: [1,2,3,4] }");
|
||||||
|
let res = Value::parse("{ test: [1,2,3,4] }");
|
||||||
|
assert_eq!(res, old.changed(&now));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn changed_replace_embedded() {
|
||||||
|
let old = Value::parse("{ test: { other: 'test' } }");
|
||||||
|
let now = Value::parse("{ test: { other: false } }");
|
||||||
|
let res = Value::parse("{ test: { other: false } }");
|
||||||
|
assert_eq!(res, old.changed(&now));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn changed_change_text() {
|
||||||
|
let old = Value::parse("{ test: { other: 'test' } }");
|
||||||
|
let now = Value::parse("{ test: { other: 'text' } }");
|
||||||
|
let res = Value::parse("{ test: { other: 'text' } }");
|
||||||
|
assert_eq!(res, old.changed(&now));
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ pub(super) mod serde;
|
||||||
mod value;
|
mod value;
|
||||||
|
|
||||||
mod all;
|
mod all;
|
||||||
|
mod changed;
|
||||||
mod clear;
|
mod clear;
|
||||||
mod compare;
|
mod compare;
|
||||||
mod cut;
|
mod cut;
|
||||||
|
|
|
@ -6,7 +6,7 @@ use surrealdb::kvs::Datastore;
|
||||||
use surrealdb::sql::Value;
|
use surrealdb::sql::Value;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn update_with_input() -> Result<(), Error> {
|
async fn update_simple_with_input() -> Result<(), Error> {
|
||||||
let sql = "
|
let sql = "
|
||||||
DEFINE FIELD name ON TABLE person
|
DEFINE FIELD name ON TABLE person
|
||||||
ASSERT
|
ASSERT
|
||||||
|
@ -95,3 +95,41 @@ async fn update_with_input() -> Result<(), Error> {
|
||||||
//
|
//
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn update_complex_with_input() -> Result<(), Error> {
|
||||||
|
let sql = "
|
||||||
|
DEFINE FIELD images ON product
|
||||||
|
TYPE array
|
||||||
|
ASSERT array::len($value) > 0
|
||||||
|
;
|
||||||
|
DEFINE FIELD images.* ON product TYPE string
|
||||||
|
VALUE string::trim($input)
|
||||||
|
ASSERT $input AND string::len($value) > 0
|
||||||
|
;
|
||||||
|
CREATE product:test SET images = [' test.png '];
|
||||||
|
";
|
||||||
|
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(), 3);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result;
|
||||||
|
assert!(tmp.is_ok());
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result;
|
||||||
|
assert!(tmp.is_ok());
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse(
|
||||||
|
"[
|
||||||
|
{
|
||||||
|
id: product:test,
|
||||||
|
images: ['test.png'],
|
||||||
|
}
|
||||||
|
]",
|
||||||
|
);
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue