From 4ab552a8e36cfd1cc05ec27d9763389d711d9f0f Mon Sep 17 00:00:00 2001 From: Tobie Morgan Hitchcock Date: Thu, 7 Jul 2022 10:58:35 +0100 Subject: [PATCH] Allow PATCH clauses to accept parameters --- lib/src/doc/alter.rs | 4 ++++ lib/src/doc/merge.rs | 1 + lib/src/sql/array.rs | 11 ----------- lib/src/sql/data.rs | 5 ++--- lib/src/sql/value/diff.rs | 13 ++++++------- lib/src/sql/value/merge.rs | 4 ++-- lib/src/sql/value/patch.rs | 35 +++++++++++++++++------------------ lib/src/sql/value/replace.rs | 12 ++++++------ lib/src/sql/value/value.rs | 17 +++++++++++++++++ 9 files changed, 55 insertions(+), 47 deletions(-) diff --git a/lib/src/doc/alter.rs b/lib/src/doc/alter.rs index 6cb145e6..2b9e528a 100644 --- a/lib/src/doc/alter.rs +++ b/lib/src/doc/alter.rs @@ -64,15 +64,19 @@ impl<'a> Document<'a> { } } Data::PatchExpression(data) => { + let data = data.compute(ctx, opt, txn, Some(&self.current)).await?; self.current.to_mut().patch(ctx, opt, txn, data).await? } Data::MergeExpression(data) => { + let data = data.compute(ctx, opt, txn, Some(&self.current)).await?; self.current.to_mut().merge(ctx, opt, txn, data).await? } Data::ReplaceExpression(data) => { + let data = data.compute(ctx, opt, txn, Some(&self.current)).await?; self.current.to_mut().replace(ctx, opt, txn, data).await? } Data::ContentExpression(data) => { + let data = data.compute(ctx, opt, txn, Some(&self.current)).await?; self.current.to_mut().replace(ctx, opt, txn, data).await? } _ => unreachable!(), diff --git a/lib/src/doc/merge.rs b/lib/src/doc/merge.rs index 712aa45b..17384b74 100644 --- a/lib/src/doc/merge.rs +++ b/lib/src/doc/merge.rs @@ -20,6 +20,7 @@ impl<'a> Document<'a> { self.current.to_mut().def(ctx, opt, txn, rid).await?; // This is an INSERT statement if let Workable::Insert(v) = &self.extras { + let v = v.compute(ctx, opt, txn, Some(&self.current)).await?; self.current.to_mut().merge(ctx, opt, txn, v).await?; } // Set default field values diff --git a/lib/src/sql/array.rs b/lib/src/sql/array.rs index 5149c78c..cf0b5b19 100644 --- a/lib/src/sql/array.rs +++ b/lib/src/sql/array.rs @@ -105,17 +105,6 @@ impl Array { _ => [self.0.remove(0).as_float(), self.0.remove(0).as_float()], } } - - pub fn to_operations(&self) -> Result, Error> { - self.iter() - .map(|v| match v { - Value::Object(v) => v.to_operation(), - _ => Err(Error::InvalidPatch { - message: String::from("Operation must be an object"), - }), - }) - .collect::, Error>>() - } } impl Array { diff --git a/lib/src/sql/data.rs b/lib/src/sql/data.rs index c600f783..aa362a2a 100644 --- a/lib/src/sql/data.rs +++ b/lib/src/sql/data.rs @@ -1,4 +1,3 @@ -use crate::sql::array::{array, Array}; use crate::sql::comment::mightbespace; use crate::sql::comment::shouldbespace; use crate::sql::common::commas; @@ -16,7 +15,7 @@ use std::fmt; pub enum Data { EmptyExpression, SetExpression(Vec<(Idiom, Operator, Value)>), - PatchExpression(Array), + PatchExpression(Value), MergeExpression(Value), ReplaceExpression(Value), ContentExpression(Value), @@ -98,7 +97,7 @@ fn set(i: &str) -> IResult<&str, Data> { fn patch(i: &str) -> IResult<&str, Data> { let (i, _) = tag_no_case("PATCH")(i)?; let (i, _) = shouldbespace(i)?; - let (i, v) = array(i)?; + let (i, v) = value(i)?; Ok((i, Data::PatchExpression(v))) } diff --git a/lib/src/sql/value/diff.rs b/lib/src/sql/value/diff.rs index b9ab197d..3481122a 100644 --- a/lib/src/sql/value/diff.rs +++ b/lib/src/sql/value/diff.rs @@ -86,14 +86,13 @@ impl Value { mod tests { use super::*; - use crate::sql::array::Array; use crate::sql::test::Parse; #[test] fn diff_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 = Array::parse("[]"); + let res = Value::parse("[]"); assert_eq!(res.to_operations().unwrap(), old.diff(&now, Idiom::default())); } @@ -101,7 +100,7 @@ mod tests { fn diff_add() { let old = Value::parse("{ test: true }"); let now = Value::parse("{ test: true, other: 'test' }"); - let res = Array::parse("[{ op: 'add', path: '/other', value: 'test' }]"); + let res = Value::parse("[{ op: 'add', path: '/other', value: 'test' }]"); assert_eq!(res.to_operations().unwrap(), old.diff(&now, Idiom::default())); } @@ -109,7 +108,7 @@ mod tests { fn diff_remove() { let old = Value::parse("{ test: true, other: 'test' }"); let now = Value::parse("{ test: true }"); - let res = Array::parse("[{ op: 'remove', path: '/other' }]"); + let res = Value::parse("[{ op: 'remove', path: '/other' }]"); assert_eq!(res.to_operations().unwrap(), old.diff(&now, Idiom::default())); } @@ -117,7 +116,7 @@ mod tests { fn diff_add_array() { let old = Value::parse("{ test: [1,2,3] }"); let now = Value::parse("{ test: [1,2,3,4] }"); - let res = Array::parse("[{ op: 'add', path: '/test/3', value: 4 }]"); + let res = Value::parse("[{ op: 'add', path: '/test/3', value: 4 }]"); assert_eq!(res.to_operations().unwrap(), old.diff(&now, Idiom::default())); } @@ -125,7 +124,7 @@ mod tests { fn diff_replace_embedded() { let old = Value::parse("{ test: { other: 'test' } }"); let now = Value::parse("{ test: { other: false } }"); - let res = Array::parse("[{ op: 'replace', path: '/test/other', value: false }]"); + let res = Value::parse("[{ op: 'replace', path: '/test/other', value: false }]"); assert_eq!(res.to_operations().unwrap(), old.diff(&now, Idiom::default())); } @@ -133,7 +132,7 @@ mod tests { fn diff_change_text() { let old = Value::parse("{ test: { other: 'test' } }"); let now = Value::parse("{ test: { other: 'text' } }"); - let res = Array::parse( + let res = Value::parse( "[{ op: 'change', path: '/test/other', value: '@@ -1,4 +1,4 @@\n te\n-s\n+x\n t\n' }]", ); assert_eq!(res.to_operations().unwrap(), old.diff(&now, Idiom::default())); diff --git a/lib/src/sql/value/merge.rs b/lib/src/sql/value/merge.rs index fbb7af88..d32865f8 100644 --- a/lib/src/sql/value/merge.rs +++ b/lib/src/sql/value/merge.rs @@ -11,9 +11,9 @@ impl Value { ctx: &Context<'_>, opt: &Options, txn: &Transaction, - val: &Value, + val: Value, ) -> Result<(), Error> { - match val.compute(ctx, opt, txn, Some(self)).await? { + match val { Value::Object(v) => { for (k, v) in v { self.set(ctx, opt, txn, &[Part::from(k)], v).await?; diff --git a/lib/src/sql/value/patch.rs b/lib/src/sql/value/patch.rs index a43e9ed3..0b4bd37c 100644 --- a/lib/src/sql/value/patch.rs +++ b/lib/src/sql/value/patch.rs @@ -2,7 +2,6 @@ use crate::ctx::Context; use crate::dbs::Options; use crate::dbs::Transaction; use crate::err::Error; -use crate::sql::array::Array; use crate::sql::operation::Op; use crate::sql::value::Value; @@ -12,7 +11,7 @@ impl Value { ctx: &Context<'_>, opt: &Options, txn: &Transaction, - val: &Array, + val: Value, ) -> Result<(), Error> { for o in val.to_operations()?.into_iter() { match o.op { @@ -51,9 +50,9 @@ mod tests { async fn patch_add_simple() { let (ctx, opt, txn) = mock().await; let mut val = Value::parse("{ test: { other: null, something: 123 } }"); - let ops = Array::parse("[{ op: 'add', path: '/temp', value: true }]"); + let ops = Value::parse("[{ op: 'add', path: '/temp', value: true }]"); let res = Value::parse("{ test: { other: null, something: 123 }, temp: true }"); - val.patch(&ctx, &opt, &txn, &ops).await.unwrap(); + val.patch(&ctx, &opt, &txn, ops).await.unwrap(); assert_eq!(res, val); } @@ -61,9 +60,9 @@ mod tests { async fn patch_remove_simple() { let (ctx, opt, txn) = mock().await; let mut val = Value::parse("{ test: { other: null, something: 123 }, temp: true }"); - let ops = Array::parse("[{ op: 'remove', path: '/temp' }]"); + let ops = Value::parse("[{ op: 'remove', path: '/temp' }]"); let res = Value::parse("{ test: { other: null, something: 123 } }"); - val.patch(&ctx, &opt, &txn, &ops).await.unwrap(); + val.patch(&ctx, &opt, &txn, ops).await.unwrap(); assert_eq!(res, val); } @@ -71,9 +70,9 @@ mod tests { async fn patch_replace_simple() { let (ctx, opt, txn) = mock().await; let mut val = Value::parse("{ test: { other: null, something: 123 }, temp: true }"); - let ops = Array::parse("[{ op: 'replace', path: '/temp', value: 'text' }]"); + let ops = Value::parse("[{ op: 'replace', path: '/temp', value: 'text' }]"); let res = Value::parse("{ test: { other: null, something: 123 }, temp: 'text' }"); - val.patch(&ctx, &opt, &txn, &ops).await.unwrap(); + val.patch(&ctx, &opt, &txn, ops).await.unwrap(); assert_eq!(res, val); } @@ -81,11 +80,11 @@ mod tests { async fn patch_change_simple() { let (ctx, opt, txn) = mock().await; let mut val = Value::parse("{ test: { other: null, something: 123 }, temp: 'test' }"); - let ops = Array::parse( + let ops = Value::parse( "[{ op: 'change', path: '/temp', value: '@@ -1,4 +1,4 @@\n te\n-s\n+x\n t\n' }]", ); let res = Value::parse("{ test: { other: null, something: 123 }, temp: 'text' }"); - val.patch(&ctx, &opt, &txn, &ops).await.unwrap(); + val.patch(&ctx, &opt, &txn, ops).await.unwrap(); assert_eq!(res, val); } @@ -93,9 +92,9 @@ mod tests { async fn patch_add_embedded() { let (ctx, opt, txn) = mock().await; let mut val = Value::parse("{ test: { other: null, something: 123 } }"); - let ops = Array::parse("[{ op: 'add', path: '/temp/test', value: true }]"); + let ops = Value::parse("[{ op: 'add', path: '/temp/test', value: true }]"); let res = Value::parse("{ test: { other: null, something: 123 }, temp: { test: true } }"); - val.patch(&ctx, &opt, &txn, &ops).await.unwrap(); + val.patch(&ctx, &opt, &txn, ops).await.unwrap(); assert_eq!(res, val); } @@ -103,9 +102,9 @@ mod tests { async fn patch_remove_embedded() { let (ctx, opt, txn) = mock().await; let mut val = Value::parse("{ test: { other: null, something: 123 }, temp: true }"); - let ops = Array::parse("[{ op: 'remove', path: '/test/other' }]"); + let ops = Value::parse("[{ op: 'remove', path: '/test/other' }]"); let res = Value::parse("{ test: { something: 123 }, temp: true }"); - val.patch(&ctx, &opt, &txn, &ops).await.unwrap(); + val.patch(&ctx, &opt, &txn, ops).await.unwrap(); assert_eq!(res, val); } @@ -113,9 +112,9 @@ mod tests { async fn patch_replace_embedded() { let (ctx, opt, txn) = mock().await; let mut val = Value::parse("{ test: { other: null, something: 123 }, temp: true }"); - let ops = Array::parse("[{ op: 'replace', path: '/test/other', value: 'text' }]"); + let ops = Value::parse("[{ op: 'replace', path: '/test/other', value: 'text' }]"); let res = Value::parse("{ test: { other: 'text', something: 123 }, temp: true }"); - val.patch(&ctx, &opt, &txn, &ops).await.unwrap(); + val.patch(&ctx, &opt, &txn, ops).await.unwrap(); assert_eq!(res, val); } @@ -123,11 +122,11 @@ mod tests { async fn patch_change_embedded() { let (ctx, opt, txn) = mock().await; let mut val = Value::parse("{ test: { other: 'test', something: 123 }, temp: true }"); - let ops = Array::parse( + let ops = Value::parse( "[{ op: 'change', path: '/test/other', value: '@@ -1,4 +1,4 @@\n te\n-s\n+x\n t\n' }]", ); let res = Value::parse("{ test: { other: 'text', something: 123 }, temp: true }"); - val.patch(&ctx, &opt, &txn, &ops).await.unwrap(); + val.patch(&ctx, &opt, &txn, ops).await.unwrap(); assert_eq!(res, val); } } diff --git a/lib/src/sql/value/replace.rs b/lib/src/sql/value/replace.rs index ba2fb2eb..8111025b 100644 --- a/lib/src/sql/value/replace.rs +++ b/lib/src/sql/value/replace.rs @@ -7,13 +7,13 @@ use crate::sql::value::Value; impl Value { pub async fn replace( &mut self, - ctx: &Context<'_>, - opt: &Options, - txn: &Transaction, - val: &Value, + _ctx: &Context<'_>, + _opt: &Options, + _txn: &Transaction, + val: Value, ) -> Result<(), Error> { // Clear all entries - match val.compute(ctx, opt, txn, Some(self)).await? { + match val { Value::Object(v) => { *self = Value::from(v); Ok(()) @@ -36,7 +36,7 @@ mod tests { let mut val = Value::parse("{ test: { other: null, something: 123 } }"); let res = Value::parse("{ other: true }"); let obj = Value::parse("{ other: true }"); - val.replace(&ctx, &opt, &txn, &obj).await.unwrap(); + val.replace(&ctx, &opt, &txn, obj).await.unwrap(); assert_eq!(res, val); } } diff --git a/lib/src/sql/value/value.rs b/lib/src/sql/value/value.rs index 7d54184a..e0dccda4 100644 --- a/lib/src/sql/value/value.rs +++ b/lib/src/sql/value/value.rs @@ -708,6 +708,23 @@ impl Value { } } + pub fn to_operations(&self) -> Result, Error> { + match self { + Value::Array(v) => v + .iter() + .map(|v| match v { + Value::Object(v) => v.to_operation(), + _ => Err(Error::InvalidPatch { + message: String::from("Operation must be an object"), + }), + }) + .collect::, Error>>(), + _ => Err(Error::InvalidPatch { + message: String::from("Operations must be an array"), + }), + } + } + // ----------------------------------- // Simple conversion of value // -----------------------------------