Only allow simple fields for SPLIT, ORDER, and GROUP clauses
This commit is contained in:
parent
652195032c
commit
3370b20c38
8 changed files with 161 additions and 12 deletions
|
@ -161,7 +161,7 @@ impl Iterator {
|
|||
// Loop over each value
|
||||
for obj in &res {
|
||||
// Get the value at the path
|
||||
let val = obj.get(ctx, opt, txn, &split.split).await?;
|
||||
let val = obj.pick(&split.split);
|
||||
// Set the value at the path
|
||||
match val {
|
||||
Value::Array(v) => {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::common::commas;
|
||||
use crate::sql::error::IResult;
|
||||
use crate::sql::idiom::{idiom, Idiom};
|
||||
use crate::sql::idiom::{basic, Idiom};
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::combinator::opt;
|
||||
use nom::multi::separated_list1;
|
||||
|
@ -42,7 +42,7 @@ pub fn group(i: &str) -> IResult<&str, Groups> {
|
|||
}
|
||||
|
||||
fn group_raw(i: &str) -> IResult<&str, Group> {
|
||||
let (i, v) = idiom(i)?;
|
||||
let (i, v) = basic(i)?;
|
||||
Ok((
|
||||
i,
|
||||
Group {
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::dbs::Transaction;
|
|||
use crate::err::Error;
|
||||
use crate::sql::common::commas;
|
||||
use crate::sql::error::IResult;
|
||||
use crate::sql::part::{all, field, first, graph, index, part, Part};
|
||||
use crate::sql::part::{all, field, first, graph, index, last, part, Part};
|
||||
use crate::sql::value::Value;
|
||||
use nom::branch::alt;
|
||||
use nom::multi::many0;
|
||||
|
@ -103,7 +103,7 @@ impl fmt::Display for Idiom {
|
|||
}
|
||||
}
|
||||
|
||||
// Used in a DEFINE FIELD and DEFINE INDEX clause
|
||||
// Used in a DEFINE FIELD and DEFINE INDEX clauses
|
||||
pub fn local(i: &str) -> IResult<&str, Idiom> {
|
||||
let (i, p) = first(i)?;
|
||||
let (i, mut v) = many0(alt((all, index, field)))(i)?;
|
||||
|
@ -111,6 +111,14 @@ pub fn local(i: &str) -> IResult<&str, Idiom> {
|
|||
Ok((i, Idiom::from(v)))
|
||||
}
|
||||
|
||||
// Used in a SPLIT, ORDER, and GROUP clauses
|
||||
pub fn basic(i: &str) -> IResult<&str, Idiom> {
|
||||
let (i, p) = first(i)?;
|
||||
let (i, mut v) = many0(alt((all, last, index, field)))(i)?;
|
||||
v.insert(0, p);
|
||||
Ok((i, Idiom::from(v)))
|
||||
}
|
||||
|
||||
// Used in a $param definition
|
||||
pub fn param(i: &str) -> IResult<&str, Idiom> {
|
||||
let (i, p) = first(i)?;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::common::commas;
|
||||
use crate::sql::error::IResult;
|
||||
use crate::sql::idiom::{idiom, Idiom};
|
||||
use crate::sql::idiom::{basic, Idiom};
|
||||
use nom::branch::alt;
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::combinator::{map, opt};
|
||||
|
@ -75,7 +75,7 @@ fn order_rand(i: &str) -> IResult<&str, Vec<Order>> {
|
|||
}
|
||||
|
||||
fn order_raw(i: &str) -> IResult<&str, Order> {
|
||||
let (i, v) = idiom(i)?;
|
||||
let (i, v) = basic(i)?;
|
||||
let (i, c) = opt(tuple((shouldbespace, tag_no_case("COLLATE"))))(i)?;
|
||||
let (i, n) = opt(tuple((shouldbespace, tag_no_case("NUMERIC"))))(i)?;
|
||||
let (i, d) = opt(alt((
|
||||
|
|
|
@ -115,9 +115,19 @@ pub fn first(i: &str) -> IResult<&str, Part> {
|
|||
}
|
||||
|
||||
pub fn all(i: &str) -> IResult<&str, Part> {
|
||||
let (i, _) = alt((
|
||||
|i| {
|
||||
let (i, _) = char('.')(i)?;
|
||||
let (i, _) = char('*')(i)?;
|
||||
Ok((i, ()))
|
||||
},
|
||||
|i| {
|
||||
let (i, _) = char('[')(i)?;
|
||||
let (i, _) = char('*')(i)?;
|
||||
let (i, _) = char(']')(i)?;
|
||||
Ok((i, ()))
|
||||
},
|
||||
))(i)?;
|
||||
Ok((i, Part::All))
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::sql::comment::shouldbespace;
|
||||
use crate::sql::common::commas;
|
||||
use crate::sql::error::IResult;
|
||||
use crate::sql::idiom::{idiom, Idiom};
|
||||
use crate::sql::idiom::{basic, Idiom};
|
||||
use nom::bytes::complete::tag_no_case;
|
||||
use nom::combinator::opt;
|
||||
use nom::multi::separated_list1;
|
||||
|
@ -42,7 +42,7 @@ pub fn split(i: &str) -> IResult<&str, Splits> {
|
|||
}
|
||||
|
||||
fn split_raw(i: &str) -> IResult<&str, Split> {
|
||||
let (i, v) = idiom(i)?;
|
||||
let (i, v) = basic(i)?;
|
||||
Ok((
|
||||
i,
|
||||
Split {
|
||||
|
|
|
@ -13,6 +13,7 @@ mod last;
|
|||
mod merge;
|
||||
mod object;
|
||||
mod patch;
|
||||
mod pick;
|
||||
mod replace;
|
||||
mod set;
|
||||
mod single;
|
||||
|
|
130
lib/src/sql/value/pick.rs
Normal file
130
lib/src/sql/value/pick.rs
Normal file
|
@ -0,0 +1,130 @@
|
|||
use crate::sql::part::Next;
|
||||
use crate::sql::part::Part;
|
||||
use crate::sql::value::Value;
|
||||
|
||||
impl Value {
|
||||
pub fn pick(&self, path: &[Part]) -> Self {
|
||||
match path.first() {
|
||||
// Get the current path part
|
||||
Some(p) => match self {
|
||||
// Current path part is an object
|
||||
Value::Object(v) => match p {
|
||||
Part::Field(f) => match v.value.get(&f.name) {
|
||||
Some(v) => v.pick(path.next()),
|
||||
None => Value::None,
|
||||
},
|
||||
_ => Value::None,
|
||||
},
|
||||
// Current path part is an array
|
||||
Value::Array(v) => match p {
|
||||
Part::All => {
|
||||
v.value.iter().map(|v| v.pick(path.next())).collect::<Vec<_>>().into()
|
||||
}
|
||||
Part::First => match v.value.first() {
|
||||
Some(v) => v.pick(path.next()),
|
||||
None => Value::None,
|
||||
},
|
||||
Part::Last => match v.value.last() {
|
||||
Some(v) => v.pick(path.next()),
|
||||
None => Value::None,
|
||||
},
|
||||
Part::Index(i) => match v.value.get(i.to_usize()) {
|
||||
Some(v) => v.pick(path.next()),
|
||||
None => Value::None,
|
||||
},
|
||||
_ => v.value.iter().map(|v| v.pick(path)).collect::<Vec<_>>().into(),
|
||||
},
|
||||
// Ignore everything else
|
||||
_ => Value::None,
|
||||
},
|
||||
// No more parts so get the value
|
||||
None => self.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::sql::id::Id;
|
||||
use crate::sql::idiom::Idiom;
|
||||
use crate::sql::test::Parse;
|
||||
use crate::sql::thing::Thing;
|
||||
|
||||
#[test]
|
||||
fn pick_none() {
|
||||
let idi = Idiom::default();
|
||||
let val = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
let res = val.pick(&idi);
|
||||
assert_eq!(res, val);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pick_basic() {
|
||||
let idi = Idiom::parse("test.something");
|
||||
let val = Value::parse("{ test: { other: null, something: 123 } }");
|
||||
let res = val.pick(&idi);
|
||||
assert_eq!(res, Value::from(123));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pick_thing() {
|
||||
let idi = Idiom::parse("test.other");
|
||||
let val = Value::parse("{ test: { other: test:tobie, something: 123 } }");
|
||||
let res = val.pick(&idi);
|
||||
assert_eq!(
|
||||
res,
|
||||
Value::from(Thing {
|
||||
tb: String::from("test"),
|
||||
id: Id::from("tobie")
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pick_array() {
|
||||
let idi = Idiom::parse("test.something[1]");
|
||||
let val = Value::parse("{ test: { something: [123, 456, 789] } }");
|
||||
let res = val.pick(&idi);
|
||||
assert_eq!(res, Value::from(456));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pick_array_thing() {
|
||||
let idi = Idiom::parse("test.something[1]");
|
||||
let val = Value::parse("{ test: { something: [test:tobie, test:jaime] } }");
|
||||
let res = val.pick(&idi);
|
||||
assert_eq!(
|
||||
res,
|
||||
Value::from(Thing {
|
||||
tb: String::from("test"),
|
||||
id: Id::from("jaime")
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pick_array_field() {
|
||||
let idi = Idiom::parse("test.something[1].age");
|
||||
let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
|
||||
let res = val.pick(&idi);
|
||||
assert_eq!(res, Value::from(36));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pick_array_fields() {
|
||||
let idi = Idiom::parse("test.something[*].age");
|
||||
let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
|
||||
let res = val.pick(&idi);
|
||||
assert_eq!(res, Value::from(vec![34, 36]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pick_array_fields_flat() {
|
||||
let idi = Idiom::parse("test.something.age");
|
||||
let val = Value::parse("{ test: { something: [{ age: 34 }, { age: 36 }] } }");
|
||||
let res = val.pick(&idi);
|
||||
assert_eq!(res, Value::from(vec![34, 36]));
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue