Allow PATCH clauses to accept parameters

This commit is contained in:
Tobie Morgan Hitchcock 2022-07-07 10:58:35 +01:00
parent 63d099e305
commit 4ab552a8e3
9 changed files with 55 additions and 47 deletions

View file

@ -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!(),

View file

@ -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

View file

@ -105,17 +105,6 @@ impl Array {
_ => [self.0.remove(0).as_float(), self.0.remove(0).as_float()],
}
}
pub fn to_operations(&self) -> Result<Vec<Operation>, 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::<Result<Vec<_>, Error>>()
}
}
impl Array {

View file

@ -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)))
}

View file

@ -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()));

View file

@ -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?;

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -708,6 +708,23 @@ impl Value {
}
}
pub fn to_operations(&self) -> Result<Vec<Operation>, 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::<Result<Vec<_>, Error>>(),
_ => Err(Error::InvalidPatch {
message: String::from("Operations must be an array"),
}),
}
}
// -----------------------------------
// Simple conversion of value
// -----------------------------------