parent
4dba9fc675
commit
1102a2c6da
7 changed files with 192 additions and 11 deletions
|
@ -56,6 +56,9 @@ impl<'a> Document<'a> {
|
||||||
Operator::Dec => {
|
Operator::Dec => {
|
||||||
self.current.to_mut().decrement(ctx, opt, txn, &x.0, v).await?
|
self.current.to_mut().decrement(ctx, opt, txn, &x.0, v).await?
|
||||||
}
|
}
|
||||||
|
Operator::Ext => {
|
||||||
|
self.current.to_mut().extend(ctx, opt, txn, &x.0, v).await?
|
||||||
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ pub enum Operator {
|
||||||
Pow, // **
|
Pow, // **
|
||||||
Inc, // +=
|
Inc, // +=
|
||||||
Dec, // -=
|
Dec, // -=
|
||||||
|
Ext, // +?=
|
||||||
//
|
//
|
||||||
Equal, // =
|
Equal, // =
|
||||||
Exact, // ==
|
Exact, // ==
|
||||||
|
@ -93,6 +94,7 @@ impl fmt::Display for Operator {
|
||||||
Self::Pow => "**",
|
Self::Pow => "**",
|
||||||
Self::Inc => "+=",
|
Self::Inc => "+=",
|
||||||
Self::Dec => "-=",
|
Self::Dec => "-=",
|
||||||
|
Self::Ext => "+=?",
|
||||||
Self::Equal => "=",
|
Self::Equal => "=",
|
||||||
Self::Exact => "==",
|
Self::Exact => "==",
|
||||||
Self::NotEqual => "!=",
|
Self::NotEqual => "!=",
|
||||||
|
@ -127,6 +129,7 @@ pub fn assigner(i: &str) -> IResult<&str, Operator> {
|
||||||
map(char('='), |_| Operator::Equal),
|
map(char('='), |_| Operator::Equal),
|
||||||
map(tag("+="), |_| Operator::Inc),
|
map(tag("+="), |_| Operator::Inc),
|
||||||
map(tag("-="), |_| Operator::Dec),
|
map(tag("-="), |_| Operator::Dec),
|
||||||
|
map(tag("+?="), |_| Operator::Ext),
|
||||||
))(i)
|
))(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ mod tests {
|
||||||
use crate::sql::test::Parse;
|
use crate::sql::test::Parse;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn dec_none() {
|
async fn decrement_none() {
|
||||||
let (ctx, opt, txn) = mock().await;
|
let (ctx, opt, txn) = mock().await;
|
||||||
let idi = Idiom::parse("other");
|
let idi = Idiom::parse("other");
|
||||||
let mut val = Value::parse("{ test: 100 }");
|
let mut val = Value::parse("{ test: 100 }");
|
||||||
|
@ -54,7 +54,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn dec_number() {
|
async fn decrement_number() {
|
||||||
let (ctx, opt, txn) = mock().await;
|
let (ctx, opt, txn) = mock().await;
|
||||||
let idi = Idiom::parse("test");
|
let idi = Idiom::parse("test");
|
||||||
let mut val = Value::parse("{ test: 100 }");
|
let mut val = Value::parse("{ test: 100 }");
|
||||||
|
@ -64,7 +64,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn dec_array_number() {
|
async fn decrement_array_number() {
|
||||||
let (ctx, opt, txn) = mock().await;
|
let (ctx, opt, txn) = mock().await;
|
||||||
let idi = Idiom::parse("test[1]");
|
let idi = Idiom::parse("test[1]");
|
||||||
let mut val = Value::parse("{ test: [100, 200, 300] }");
|
let mut val = Value::parse("{ test: [100, 200, 300] }");
|
||||||
|
@ -74,7 +74,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn dec_array_value() {
|
async fn decrement_array_value() {
|
||||||
let (ctx, opt, txn) = mock().await;
|
let (ctx, opt, txn) = mock().await;
|
||||||
let idi = Idiom::parse("test");
|
let idi = Idiom::parse("test");
|
||||||
let mut val = Value::parse("{ test: [100, 200, 300] }");
|
let mut val = Value::parse("{ test: [100, 200, 300] }");
|
||||||
|
@ -84,7 +84,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn dec_array_array() {
|
async fn decrement_array_array() {
|
||||||
let (ctx, opt, txn) = mock().await;
|
let (ctx, opt, txn) = mock().await;
|
||||||
let idi = Idiom::parse("test");
|
let idi = Idiom::parse("test");
|
||||||
let mut val = Value::parse("{ test: [100, 200, 300] }");
|
let mut val = Value::parse("{ test: [100, 200, 300] }");
|
||||||
|
|
59
lib/src/sql/value/extend.rs
Normal file
59
lib/src/sql/value/extend.rs
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
use crate::ctx::Context;
|
||||||
|
use crate::dbs::Options;
|
||||||
|
use crate::dbs::Transaction;
|
||||||
|
use crate::err::Error;
|
||||||
|
use crate::sql::array::Uniq;
|
||||||
|
use crate::sql::part::Part;
|
||||||
|
use crate::sql::value::Value;
|
||||||
|
|
||||||
|
impl Value {
|
||||||
|
pub async fn extend(
|
||||||
|
&mut self,
|
||||||
|
ctx: &Context<'_>,
|
||||||
|
opt: &Options,
|
||||||
|
txn: &Transaction,
|
||||||
|
path: &[Part],
|
||||||
|
val: Value,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
match self.get(ctx, opt, txn, path).await? {
|
||||||
|
Value::Array(v) => match val {
|
||||||
|
Value::Array(x) => self.set(ctx, opt, txn, path, Value::from((v + x).uniq())).await,
|
||||||
|
x => self.set(ctx, opt, txn, path, Value::from((v + x).uniq())).await,
|
||||||
|
},
|
||||||
|
Value::None => match val {
|
||||||
|
Value::Array(x) => self.set(ctx, opt, txn, path, Value::from(x)).await,
|
||||||
|
x => self.set(ctx, opt, txn, path, Value::from(vec![x])).await,
|
||||||
|
},
|
||||||
|
_ => Ok(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use crate::dbs::test::mock;
|
||||||
|
use crate::sql::idiom::Idiom;
|
||||||
|
use crate::sql::test::Parse;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn extend_array_value() {
|
||||||
|
let (ctx, opt, txn) = mock().await;
|
||||||
|
let idi = Idiom::parse("test");
|
||||||
|
let mut val = Value::parse("{ test: [100, 200, 300] }");
|
||||||
|
let res = Value::parse("{ test: [100, 200, 300] }");
|
||||||
|
val.extend(&ctx, &opt, &txn, &idi, Value::from(200)).await.unwrap();
|
||||||
|
assert_eq!(res, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn extend_array_array() {
|
||||||
|
let (ctx, opt, txn) = mock().await;
|
||||||
|
let idi = Idiom::parse("test");
|
||||||
|
let mut val = Value::parse("{ test: [100, 200, 300] }");
|
||||||
|
let res = Value::parse("{ test: [100, 200, 300, 400, 500] }");
|
||||||
|
val.extend(&ctx, &opt, &txn, &idi, Value::parse("[100, 300, 400, 500]")).await.unwrap();
|
||||||
|
assert_eq!(res, val);
|
||||||
|
}
|
||||||
|
}
|
|
@ -45,7 +45,7 @@ mod tests {
|
||||||
use crate::sql::test::Parse;
|
use crate::sql::test::Parse;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn inc_none() {
|
async fn increment_none() {
|
||||||
let (ctx, opt, txn) = mock().await;
|
let (ctx, opt, txn) = mock().await;
|
||||||
let idi = Idiom::parse("other");
|
let idi = Idiom::parse("other");
|
||||||
let mut val = Value::parse("{ test: 100 }");
|
let mut val = Value::parse("{ test: 100 }");
|
||||||
|
@ -55,7 +55,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn inc_number() {
|
async fn increment_number() {
|
||||||
let (ctx, opt, txn) = mock().await;
|
let (ctx, opt, txn) = mock().await;
|
||||||
let idi = Idiom::parse("test");
|
let idi = Idiom::parse("test");
|
||||||
let mut val = Value::parse("{ test: 100 }");
|
let mut val = Value::parse("{ test: 100 }");
|
||||||
|
@ -65,7 +65,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn inc_array_number() {
|
async fn increment_array_number() {
|
||||||
let (ctx, opt, txn) = mock().await;
|
let (ctx, opt, txn) = mock().await;
|
||||||
let idi = Idiom::parse("test[1]");
|
let idi = Idiom::parse("test[1]");
|
||||||
let mut val = Value::parse("{ test: [100, 200, 300] }");
|
let mut val = Value::parse("{ test: [100, 200, 300] }");
|
||||||
|
@ -75,7 +75,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn inc_array_value() {
|
async fn increment_array_value() {
|
||||||
let (ctx, opt, txn) = mock().await;
|
let (ctx, opt, txn) = mock().await;
|
||||||
let idi = Idiom::parse("test");
|
let idi = Idiom::parse("test");
|
||||||
let mut val = Value::parse("{ test: [100, 200, 300] }");
|
let mut val = Value::parse("{ test: [100, 200, 300] }");
|
||||||
|
@ -85,7 +85,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn inc_array_array() {
|
async fn increment_array_array() {
|
||||||
let (ctx, opt, txn) = mock().await;
|
let (ctx, opt, txn) = mock().await;
|
||||||
let idi = Idiom::parse("test");
|
let idi = Idiom::parse("test");
|
||||||
let mut val = Value::parse("{ test: [100, 200, 300] }");
|
let mut val = Value::parse("{ test: [100, 200, 300] }");
|
||||||
|
|
|
@ -13,6 +13,7 @@ mod del;
|
||||||
mod diff;
|
mod diff;
|
||||||
mod each;
|
mod each;
|
||||||
mod every;
|
mod every;
|
||||||
|
mod extend;
|
||||||
mod fetch;
|
mod fetch;
|
||||||
mod first;
|
mod first;
|
||||||
mod flatten;
|
mod flatten;
|
||||||
|
|
|
@ -102,7 +102,122 @@ async fn subquery_select() -> Result<(), Error> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn subquery_ifelse() -> Result<(), Error> {
|
async fn subquery_ifelse_set() -> Result<(), Error> {
|
||||||
|
let sql = "
|
||||||
|
-- Check if the record exists
|
||||||
|
LET $record = (SELECT *, count() AS count FROM person:test);
|
||||||
|
-- Return the specified record
|
||||||
|
RETURN $record;
|
||||||
|
-- Update the record field if it exists
|
||||||
|
IF $record.count THEN
|
||||||
|
(UPDATE person:test SET sport +?= 'football' RETURN sport)
|
||||||
|
ELSE
|
||||||
|
(UPDATE person:test SET sport = ['basketball'] RETURN sport)
|
||||||
|
END;
|
||||||
|
-- Check if the record exists
|
||||||
|
LET $record = SELECT *, count() AS count FROM person:test;
|
||||||
|
-- Return the specified record
|
||||||
|
RETURN $record;
|
||||||
|
-- Update the record field if it exists
|
||||||
|
IF $record.count THEN
|
||||||
|
UPDATE person:test SET sport +?= 'football' RETURN sport
|
||||||
|
ELSE
|
||||||
|
UPDATE person:test SET sport = ['basketball'] RETURN sport
|
||||||
|
END;
|
||||||
|
-- Check if the record exists
|
||||||
|
LET $record = SELECT *, count() AS count FROM person:test;
|
||||||
|
-- Return the specified record
|
||||||
|
RETURN $record;
|
||||||
|
-- Update the record field if it exists
|
||||||
|
IF $record.count THEN
|
||||||
|
UPDATE person:test SET sport +?= 'football' RETURN sport;
|
||||||
|
ELSE
|
||||||
|
UPDATE person:test SET sport = ['basketball'] RETURN sport;
|
||||||
|
END;
|
||||||
|
";
|
||||||
|
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(), 9);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::None;
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::None;
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse(
|
||||||
|
"{
|
||||||
|
sport: [
|
||||||
|
'basketball',
|
||||||
|
]
|
||||||
|
}",
|
||||||
|
);
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::None;
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse(
|
||||||
|
"{
|
||||||
|
count: 1,
|
||||||
|
id: person:test,
|
||||||
|
sport: [
|
||||||
|
'basketball',
|
||||||
|
]
|
||||||
|
}",
|
||||||
|
);
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse(
|
||||||
|
"{
|
||||||
|
sport: [
|
||||||
|
'basketball',
|
||||||
|
'football',
|
||||||
|
]
|
||||||
|
}",
|
||||||
|
);
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::None;
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse(
|
||||||
|
"{
|
||||||
|
count: 1,
|
||||||
|
id: person:test,
|
||||||
|
sport: [
|
||||||
|
'basketball',
|
||||||
|
'football',
|
||||||
|
]
|
||||||
|
}",
|
||||||
|
);
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result?;
|
||||||
|
let val = Value::parse(
|
||||||
|
"{
|
||||||
|
sport: [
|
||||||
|
'basketball',
|
||||||
|
'football',
|
||||||
|
]
|
||||||
|
}",
|
||||||
|
);
|
||||||
|
assert_eq!(tmp, val);
|
||||||
|
//
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn subquery_ifelse_array() -> Result<(), Error> {
|
||||||
let sql = "
|
let sql = "
|
||||||
-- Check if the record exists
|
-- Check if the record exists
|
||||||
LET $record = (SELECT *, count() AS count FROM person:test);
|
LET $record = (SELECT *, count() AS count FROM person:test);
|
||||||
|
|
Loading…
Reference in a new issue