Improve id
field handling in the statement data clause (#2487)
This commit is contained in:
parent
27cc21876d
commit
afdd0b3c85
12 changed files with 312 additions and 218 deletions
|
@ -77,11 +77,189 @@ impl Iterator {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Prepares a value for processing
|
/// Ingests an iterable for processing
|
||||||
pub fn ingest(&mut self, val: Iterable) {
|
pub fn ingest(&mut self, val: Iterable) {
|
||||||
self.entries.push(val)
|
self.entries.push(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Prepares a value for processing
|
||||||
|
pub async fn prepare(
|
||||||
|
&mut self,
|
||||||
|
ctx: &Context<'_>,
|
||||||
|
opt: &Options,
|
||||||
|
txn: &Transaction,
|
||||||
|
stm: &Statement<'_>,
|
||||||
|
val: Value,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
// Match the values
|
||||||
|
match val {
|
||||||
|
Value::Table(v) => match stm.data() {
|
||||||
|
// There is a data clause so fetch a record id
|
||||||
|
Some(data) => match stm {
|
||||||
|
Statement::Create(_) => {
|
||||||
|
let id = match data.rid(ctx, opt, txn).await? {
|
||||||
|
// Generate a new id from the id field
|
||||||
|
Some(id) => id.generate(&v, false)?,
|
||||||
|
// Generate a new random table id
|
||||||
|
None => v.generate(),
|
||||||
|
};
|
||||||
|
self.ingest(Iterable::Thing(id))
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Ingest the table for scanning
|
||||||
|
self.ingest(Iterable::Table(v))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// There is no data clause so create a record id
|
||||||
|
None => match stm {
|
||||||
|
Statement::Create(_) => {
|
||||||
|
// Generate a new random table id
|
||||||
|
self.ingest(Iterable::Thing(v.generate()))
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Ingest the table for scanning
|
||||||
|
self.ingest(Iterable::Table(v))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Value::Thing(v) => {
|
||||||
|
// Check if there is a data clause
|
||||||
|
if let Some(data) = stm.data() {
|
||||||
|
// Check if there is an id field specified
|
||||||
|
if let Some(id) = data.rid(ctx, opt, txn).await? {
|
||||||
|
// Check to see the type of the id
|
||||||
|
match id {
|
||||||
|
// The id is a match, so don't error
|
||||||
|
Value::Thing(id) if id == v => (),
|
||||||
|
// The id does not match
|
||||||
|
id => {
|
||||||
|
return Err(Error::IdMismatch {
|
||||||
|
value: id.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add the record to the iterator
|
||||||
|
self.ingest(Iterable::Thing(v));
|
||||||
|
}
|
||||||
|
Value::Model(v) => {
|
||||||
|
// Check if there is a data clause
|
||||||
|
if let Some(data) = stm.data() {
|
||||||
|
// Check if there is an id field specified
|
||||||
|
if let Some(id) = data.rid(ctx, opt, txn).await? {
|
||||||
|
return Err(Error::IdMismatch {
|
||||||
|
value: id.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add the records to the iterator
|
||||||
|
for v in v {
|
||||||
|
self.ingest(Iterable::Thing(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Value::Range(v) => {
|
||||||
|
// Check if this is a create statement
|
||||||
|
if let Statement::Create(_) = stm {
|
||||||
|
return Err(Error::InvalidStatementTarget {
|
||||||
|
value: v.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Check if there is a data clause
|
||||||
|
if let Some(data) = stm.data() {
|
||||||
|
// Check if there is an id field specified
|
||||||
|
if let Some(id) = data.rid(ctx, opt, txn).await? {
|
||||||
|
return Err(Error::IdMismatch {
|
||||||
|
value: id.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add the record to the iterator
|
||||||
|
self.ingest(Iterable::Range(*v));
|
||||||
|
}
|
||||||
|
Value::Edges(v) => {
|
||||||
|
// Check if this is a create statement
|
||||||
|
if let Statement::Create(_) = stm {
|
||||||
|
return Err(Error::InvalidStatementTarget {
|
||||||
|
value: v.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Check if there is a data clause
|
||||||
|
if let Some(data) = stm.data() {
|
||||||
|
// Check if there is an id field specified
|
||||||
|
if let Some(id) = data.rid(ctx, opt, txn).await? {
|
||||||
|
return Err(Error::IdMismatch {
|
||||||
|
value: id.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add the record to the iterator
|
||||||
|
self.ingest(Iterable::Edges(*v));
|
||||||
|
}
|
||||||
|
Value::Object(v) => {
|
||||||
|
// Check if there is a data clause
|
||||||
|
if let Some(data) = stm.data() {
|
||||||
|
// Check if there is an id field specified
|
||||||
|
if let Some(id) = data.rid(ctx, opt, txn).await? {
|
||||||
|
return Err(Error::IdMismatch {
|
||||||
|
value: id.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check if the object has an id field
|
||||||
|
match v.rid() {
|
||||||
|
Some(id) => {
|
||||||
|
// Add the record to the iterator
|
||||||
|
self.ingest(Iterable::Thing(id))
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
return Err(Error::InvalidStatementTarget {
|
||||||
|
value: v.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Value::Array(v) => {
|
||||||
|
// Check if there is a data clause
|
||||||
|
if let Some(data) = stm.data() {
|
||||||
|
// Check if there is an id field specified
|
||||||
|
if let Some(id) = data.rid(ctx, opt, txn).await? {
|
||||||
|
return Err(Error::IdMismatch {
|
||||||
|
value: id.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add the records to the iterator
|
||||||
|
for v in v {
|
||||||
|
match v {
|
||||||
|
Value::Thing(v) => self.ingest(Iterable::Thing(v)),
|
||||||
|
Value::Edges(v) => self.ingest(Iterable::Edges(*v)),
|
||||||
|
Value::Object(v) => match v.rid() {
|
||||||
|
Some(v) => self.ingest(Iterable::Thing(v)),
|
||||||
|
None => {
|
||||||
|
return Err(Error::InvalidStatementTarget {
|
||||||
|
value: v.to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
return Err(Error::InvalidStatementTarget {
|
||||||
|
value: v.to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v => {
|
||||||
|
return Err(Error::InvalidStatementTarget {
|
||||||
|
value: v.to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// All ingested ok
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Process the records and output
|
/// Process the records and output
|
||||||
pub async fn output(
|
pub async fn output(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
|
@ -10,6 +10,7 @@ use crate::sql::idiom::Idiom;
|
||||||
use crate::sql::number::Number;
|
use crate::sql::number::Number;
|
||||||
use crate::sql::operator::Operator;
|
use crate::sql::operator::Operator;
|
||||||
use crate::sql::part::Part;
|
use crate::sql::part::Part;
|
||||||
|
use crate::sql::paths::ID;
|
||||||
use crate::sql::statement::Statement as Query;
|
use crate::sql::statement::Statement as Query;
|
||||||
use crate::sql::statements::delete::DeleteStatement;
|
use crate::sql::statements::delete::DeleteStatement;
|
||||||
use crate::sql::statements::ifelse::IfelseStatement;
|
use crate::sql::statements::ifelse::IfelseStatement;
|
||||||
|
@ -175,7 +176,6 @@ impl<'a> Document<'a> {
|
||||||
tb: ft.name.to_raw(),
|
tb: ft.name.to_raw(),
|
||||||
id: rid.id.clone(),
|
id: rid.id.clone(),
|
||||||
};
|
};
|
||||||
// Use the current record data
|
|
||||||
// Check if a WHERE clause is specified
|
// Check if a WHERE clause is specified
|
||||||
match &tb.cond {
|
match &tb.cond {
|
||||||
// There is a WHERE clause specified
|
// There is a WHERE clause specified
|
||||||
|
@ -192,17 +192,7 @@ impl<'a> Document<'a> {
|
||||||
// Update the value in the table
|
// Update the value in the table
|
||||||
_ => Query::Update(UpdateStatement {
|
_ => Query::Update(UpdateStatement {
|
||||||
what: Values(vec![Value::from(rid)]),
|
what: Values(vec![Value::from(rid)]),
|
||||||
data: Some(Data::ReplaceExpression(
|
data: Some(self.full(ctx, opt, txn, &tb.expr).await?),
|
||||||
tb.expr
|
|
||||||
.compute(
|
|
||||||
ctx,
|
|
||||||
opt,
|
|
||||||
txn,
|
|
||||||
Some(&self.current),
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.await?,
|
|
||||||
)),
|
|
||||||
..UpdateStatement::default()
|
..UpdateStatement::default()
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
@ -232,11 +222,7 @@ impl<'a> Document<'a> {
|
||||||
// Update the value in the table
|
// Update the value in the table
|
||||||
_ => Query::Update(UpdateStatement {
|
_ => Query::Update(UpdateStatement {
|
||||||
what: Values(vec![Value::from(rid)]),
|
what: Values(vec![Value::from(rid)]),
|
||||||
data: Some(Data::ReplaceExpression(
|
data: Some(self.full(ctx, opt, txn, &tb.expr).await?),
|
||||||
tb.expr
|
|
||||||
.compute(ctx, opt, txn, Some(&self.current), false)
|
|
||||||
.await?,
|
|
||||||
)),
|
|
||||||
..UpdateStatement::default()
|
..UpdateStatement::default()
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
@ -251,6 +237,18 @@ impl<'a> Document<'a> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
|
async fn full(
|
||||||
|
&self,
|
||||||
|
ctx: &Context<'_>,
|
||||||
|
opt: &Options,
|
||||||
|
txn: &Transaction,
|
||||||
|
exp: &Fields,
|
||||||
|
) -> Result<Data, Error> {
|
||||||
|
let mut data = exp.compute(ctx, opt, txn, Some(&self.current), false).await?;
|
||||||
|
data.cut(ID.as_ref());
|
||||||
|
Ok(Data::ReplaceExpression(data))
|
||||||
|
}
|
||||||
|
//
|
||||||
async fn data(
|
async fn data(
|
||||||
&self,
|
&self,
|
||||||
ctx: &Context<'_>,
|
ctx: &Context<'_>,
|
||||||
|
@ -275,7 +273,13 @@ impl<'a> Document<'a> {
|
||||||
alias,
|
alias,
|
||||||
} = field
|
} = field
|
||||||
{
|
{
|
||||||
|
// Get the name of the field
|
||||||
let idiom = alias.clone().unwrap_or_else(|| expr.to_idiom());
|
let idiom = alias.clone().unwrap_or_else(|| expr.to_idiom());
|
||||||
|
// Ignore any id field
|
||||||
|
if idiom.is_id() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Process the field projection
|
||||||
match expr {
|
match expr {
|
||||||
Value::Function(f) if f.is_rolling() => match f.name() {
|
Value::Function(f) if f.is_rolling() => match f.name() {
|
||||||
"count" => {
|
"count" => {
|
||||||
|
|
|
@ -344,44 +344,50 @@ pub enum Error {
|
||||||
#[error("Reached excessive computation depth due to functions, subqueries, or futures")]
|
#[error("Reached excessive computation depth due to functions, subqueries, or futures")]
|
||||||
ComputationDepthExceeded,
|
ComputationDepthExceeded,
|
||||||
|
|
||||||
/// Can not execute CREATE query using the specified value
|
/// Can not execute statement using the specified value
|
||||||
#[error("Can not execute CREATE query using value '{value}'")]
|
#[error("Can not execute statement using value '{value}'")]
|
||||||
|
InvalidStatementTarget {
|
||||||
|
value: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Can not execute CREATE statement using the specified value
|
||||||
|
#[error("Can not execute CREATE statement using value '{value}'")]
|
||||||
CreateStatement {
|
CreateStatement {
|
||||||
value: String,
|
value: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Can not execute UPDATE query using the specified value
|
/// Can not execute UPDATE statement using the specified value
|
||||||
#[error("Can not execute UPDATE query using value '{value}'")]
|
#[error("Can not execute UPDATE statement using value '{value}'")]
|
||||||
UpdateStatement {
|
UpdateStatement {
|
||||||
value: String,
|
value: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Can not execute RELATE query using the specified value
|
/// Can not execute RELATE statement using the specified value
|
||||||
#[error("Can not execute RELATE query using value '{value}'")]
|
#[error("Can not execute RELATE statement using value '{value}'")]
|
||||||
RelateStatement {
|
RelateStatement {
|
||||||
value: String,
|
value: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Can not execute DELETE query using the specified value
|
/// Can not execute DELETE statement using the specified value
|
||||||
#[error("Can not execute DELETE query using value '{value}'")]
|
#[error("Can not execute DELETE statement using value '{value}'")]
|
||||||
DeleteStatement {
|
DeleteStatement {
|
||||||
value: String,
|
value: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Can not execute INSERT query using the specified value
|
/// Can not execute INSERT statement using the specified value
|
||||||
#[error("Can not execute INSERT query using value '{value}'")]
|
#[error("Can not execute INSERT statement using value '{value}'")]
|
||||||
InsertStatement {
|
InsertStatement {
|
||||||
value: String,
|
value: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Can not execute LIVE query using the specified value
|
/// Can not execute LIVE statement using the specified value
|
||||||
#[error("Can not execute LIVE query using value '{value}'")]
|
#[error("Can not execute LIVE statement using value '{value}'")]
|
||||||
LiveStatement {
|
LiveStatement {
|
||||||
value: String,
|
value: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Can not execute KILL query using the specified id
|
/// Can not execute KILL statement using the specified id
|
||||||
#[error("Can not execute KILL query using id '{value}'")]
|
#[error("Can not execute KILL statement using id '{value}'")]
|
||||||
KillStatement {
|
KillStatement {
|
||||||
value: String,
|
value: String,
|
||||||
},
|
},
|
||||||
|
@ -430,8 +436,14 @@ pub enum Error {
|
||||||
check: String,
|
check: String,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Found a record id for the record but we are creating a specific record
|
||||||
|
#[error("Found {value} for the id field, but a specific record has been specified")]
|
||||||
|
IdMismatch {
|
||||||
|
value: String,
|
||||||
|
},
|
||||||
|
|
||||||
/// Found a record id for the record but this is not a valid id
|
/// Found a record id for the record but this is not a valid id
|
||||||
#[error("Found '{value}' for the record ID but this is not a valid id")]
|
#[error("Found {value} for the Record ID but this is not a valid id")]
|
||||||
IdInvalid {
|
IdInvalid {
|
||||||
value: String,
|
value: String,
|
||||||
},
|
},
|
||||||
|
|
|
@ -8,8 +8,6 @@ use crate::sql::error::IResult;
|
||||||
use crate::sql::fmt::Fmt;
|
use crate::sql::fmt::Fmt;
|
||||||
use crate::sql::idiom::{plain as idiom, Idiom};
|
use crate::sql::idiom::{plain as idiom, Idiom};
|
||||||
use crate::sql::operator::{assigner, Operator};
|
use crate::sql::operator::{assigner, Operator};
|
||||||
use crate::sql::table::Table;
|
|
||||||
use crate::sql::thing::Thing;
|
|
||||||
use crate::sql::value::{value, Value};
|
use crate::sql::value::{value, Value};
|
||||||
use nom::branch::alt;
|
use nom::branch::alt;
|
||||||
use nom::bytes::complete::tag_no_case;
|
use nom::bytes::complete::tag_no_case;
|
||||||
|
@ -46,31 +44,30 @@ impl Data {
|
||||||
ctx: &Context<'_>,
|
ctx: &Context<'_>,
|
||||||
opt: &Options,
|
opt: &Options,
|
||||||
txn: &Transaction,
|
txn: &Transaction,
|
||||||
tb: &Table,
|
) -> Result<Option<Value>, Error> {
|
||||||
) -> Result<Thing, Error> {
|
|
||||||
match self {
|
match self {
|
||||||
Self::MergeExpression(v) => {
|
Self::MergeExpression(v) => {
|
||||||
// This MERGE expression has an 'id' field
|
// This MERGE expression has an 'id' field
|
||||||
v.compute(ctx, opt, txn, None).await?.rid().generate(tb, false)
|
Ok(v.compute(ctx, opt, txn, None).await?.rid().some())
|
||||||
}
|
}
|
||||||
Self::ReplaceExpression(v) => {
|
Self::ReplaceExpression(v) => {
|
||||||
// This REPLACE expression has an 'id' field
|
// This REPLACE expression has an 'id' field
|
||||||
v.compute(ctx, opt, txn, None).await?.rid().generate(tb, false)
|
Ok(v.compute(ctx, opt, txn, None).await?.rid().some())
|
||||||
}
|
}
|
||||||
Self::ContentExpression(v) => {
|
Self::ContentExpression(v) => {
|
||||||
// This CONTENT expression has an 'id' field
|
// This CONTENT expression has an 'id' field
|
||||||
v.compute(ctx, opt, txn, None).await?.rid().generate(tb, false)
|
Ok(v.compute(ctx, opt, txn, None).await?.rid().some())
|
||||||
}
|
}
|
||||||
Self::SetExpression(v) => match v.iter().find(|f| f.0.is_id()) {
|
Self::SetExpression(v) => match v.iter().find(|f| f.0.is_id()) {
|
||||||
Some((_, _, v)) => {
|
Some((_, _, v)) => {
|
||||||
// This SET expression has an 'id' field
|
// This SET expression has an 'id' field
|
||||||
v.compute(ctx, opt, txn, None).await?.generate(tb, false)
|
Ok(v.compute(ctx, opt, txn, None).await?.some())
|
||||||
}
|
}
|
||||||
// This SET expression had no 'id' field
|
// This SET expression had no 'id' field
|
||||||
_ => Ok(tb.generate()),
|
_ => Ok(None),
|
||||||
},
|
},
|
||||||
// Generate a random id for all other data clauses
|
// Generate a random id for all other data clauses
|
||||||
_ => Ok(tb.generate()),
|
_ => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::ctx::Context;
|
||||||
use crate::dbs::Iterator;
|
use crate::dbs::Iterator;
|
||||||
use crate::dbs::Options;
|
use crate::dbs::Options;
|
||||||
use crate::dbs::Statement;
|
use crate::dbs::Statement;
|
||||||
use crate::dbs::{Iterable, Transaction};
|
use crate::dbs::Transaction;
|
||||||
use crate::doc::CursorDoc;
|
use crate::doc::CursorDoc;
|
||||||
use crate::err::Error;
|
use crate::err::Error;
|
||||||
use crate::sql::comment::shouldbespace;
|
use crate::sql::comment::shouldbespace;
|
||||||
|
@ -55,72 +55,22 @@ impl CreateStatement {
|
||||||
opt.valid_for_db()?;
|
opt.valid_for_db()?;
|
||||||
// Create a new iterator
|
// Create a new iterator
|
||||||
let mut i = Iterator::new();
|
let mut i = Iterator::new();
|
||||||
|
// Assign the statement
|
||||||
|
let stm = Statement::from(self);
|
||||||
// Ensure futures are stored
|
// Ensure futures are stored
|
||||||
let opt = &opt.new_with_futures(false);
|
let opt = &opt.new_with_futures(false);
|
||||||
// Loop over the create targets
|
// Loop over the create targets
|
||||||
for w in self.what.0.iter() {
|
for w in self.what.0.iter() {
|
||||||
let v = w.compute(ctx, opt, txn, doc).await?;
|
let v = w.compute(ctx, opt, txn, doc).await?;
|
||||||
match v {
|
i.prepare(ctx, opt, txn, &stm, v).await.map_err(|e| match e {
|
||||||
Value::Table(v) => match &self.data {
|
Error::InvalidStatementTarget {
|
||||||
// There is a data clause so check for a record id
|
value: v,
|
||||||
Some(data) => match data.rid(ctx, opt, txn, &v).await {
|
} => Error::CreateStatement {
|
||||||
// There was a problem creating the record id
|
value: v,
|
||||||
Err(e) => return Err(e),
|
|
||||||
// There is an id field so use the record id
|
|
||||||
Ok(v) => i.ingest(Iterable::Thing(v)),
|
|
||||||
},
|
|
||||||
// There is no data clause so create a record id
|
|
||||||
None => i.ingest(Iterable::Thing(v.generate())),
|
|
||||||
},
|
},
|
||||||
Value::Thing(v) => i.ingest(Iterable::Thing(v)),
|
e => e,
|
||||||
Value::Model(v) => {
|
})?;
|
||||||
for v in v {
|
|
||||||
i.ingest(Iterable::Thing(v));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Value::Array(v) => {
|
|
||||||
for v in v {
|
|
||||||
match v {
|
|
||||||
Value::Table(v) => i.ingest(Iterable::Thing(v.generate())),
|
|
||||||
Value::Thing(v) => i.ingest(Iterable::Thing(v)),
|
|
||||||
Value::Model(v) => {
|
|
||||||
for v in v {
|
|
||||||
i.ingest(Iterable::Thing(v));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Value::Object(v) => match v.rid() {
|
|
||||||
Some(v) => i.ingest(Iterable::Thing(v)),
|
|
||||||
None => {
|
|
||||||
return Err(Error::CreateStatement {
|
|
||||||
value: v.to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
v => {
|
|
||||||
return Err(Error::CreateStatement {
|
|
||||||
value: v.to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Value::Object(v) => match v.rid() {
|
|
||||||
Some(v) => i.ingest(Iterable::Thing(v)),
|
|
||||||
None => {
|
|
||||||
return Err(Error::CreateStatement {
|
|
||||||
value: v.to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
v => {
|
|
||||||
return Err(Error::CreateStatement {
|
|
||||||
value: v.to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
// Assign the statement
|
|
||||||
let stm = Statement::from(self);
|
|
||||||
// Output the results
|
// Output the results
|
||||||
i.output(ctx, opt, txn, &stm).await
|
i.output(ctx, opt, txn, &stm).await
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::ctx::Context;
|
||||||
use crate::dbs::Iterator;
|
use crate::dbs::Iterator;
|
||||||
use crate::dbs::Options;
|
use crate::dbs::Options;
|
||||||
use crate::dbs::Statement;
|
use crate::dbs::Statement;
|
||||||
use crate::dbs::{Iterable, Transaction};
|
use crate::dbs::Transaction;
|
||||||
use crate::doc::CursorDoc;
|
use crate::doc::CursorDoc;
|
||||||
use crate::err::Error;
|
use crate::err::Error;
|
||||||
use crate::sql::comment::shouldbespace;
|
use crate::sql::comment::shouldbespace;
|
||||||
|
@ -55,65 +55,22 @@ impl DeleteStatement {
|
||||||
opt.valid_for_db()?;
|
opt.valid_for_db()?;
|
||||||
// Create a new iterator
|
// Create a new iterator
|
||||||
let mut i = Iterator::new();
|
let mut i = Iterator::new();
|
||||||
|
// Assign the statement
|
||||||
|
let stm = Statement::from(self);
|
||||||
// Ensure futures are stored
|
// Ensure futures are stored
|
||||||
let opt = &opt.new_with_futures(false);
|
let opt = &opt.new_with_futures(false);
|
||||||
// Loop over the delete targets
|
// Loop over the delete targets
|
||||||
for w in self.what.0.iter() {
|
for w in self.what.0.iter() {
|
||||||
let v = w.compute(ctx, opt, txn, doc).await?;
|
let v = w.compute(ctx, opt, txn, doc).await?;
|
||||||
match v {
|
i.prepare(ctx, opt, txn, &stm, v).await.map_err(|e| match e {
|
||||||
Value::Table(v) => i.ingest(Iterable::Table(v)),
|
Error::InvalidStatementTarget {
|
||||||
Value::Thing(v) => i.ingest(Iterable::Thing(v)),
|
value: v,
|
||||||
Value::Range(v) => i.ingest(Iterable::Range(*v)),
|
} => Error::DeleteStatement {
|
||||||
Value::Edges(v) => i.ingest(Iterable::Edges(*v)),
|
value: v,
|
||||||
Value::Model(v) => {
|
|
||||||
for v in v {
|
|
||||||
i.ingest(Iterable::Thing(v));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Value::Array(v) => {
|
|
||||||
for v in v {
|
|
||||||
match v {
|
|
||||||
Value::Table(v) => i.ingest(Iterable::Table(v)),
|
|
||||||
Value::Thing(v) => i.ingest(Iterable::Thing(v)),
|
|
||||||
Value::Edges(v) => i.ingest(Iterable::Edges(*v)),
|
|
||||||
Value::Model(v) => {
|
|
||||||
for v in v {
|
|
||||||
i.ingest(Iterable::Thing(v));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Value::Object(v) => match v.rid() {
|
|
||||||
Some(v) => i.ingest(Iterable::Thing(v)),
|
|
||||||
None => {
|
|
||||||
return Err(Error::DeleteStatement {
|
|
||||||
value: v.to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
v => {
|
|
||||||
return Err(Error::DeleteStatement {
|
|
||||||
value: v.to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Value::Object(v) => match v.rid() {
|
|
||||||
Some(v) => i.ingest(Iterable::Thing(v)),
|
|
||||||
None => {
|
|
||||||
return Err(Error::DeleteStatement {
|
|
||||||
value: v.to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
v => {
|
e => e,
|
||||||
return Err(Error::DeleteStatement {
|
})?;
|
||||||
value: v.to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
// Assign the statement
|
|
||||||
let stm = Statement::from(self);
|
|
||||||
// Output the results
|
// Output the results
|
||||||
i.output(ctx, opt, txn, &stm).await
|
i.output(ctx, opt, txn, &stm).await
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,12 +164,13 @@ impl RelateStatement {
|
||||||
// The relation does not have a specific record id
|
// The relation does not have a specific record id
|
||||||
Value::Table(tb) => match &self.data {
|
Value::Table(tb) => match &self.data {
|
||||||
// There is a data clause so check for a record id
|
// There is a data clause so check for a record id
|
||||||
Some(data) => match data.rid(ctx, opt, txn, tb).await {
|
Some(data) => {
|
||||||
// There was a problem creating the record id
|
let id = match data.rid(ctx, opt, txn).await? {
|
||||||
Err(e) => return Err(e),
|
Some(id) => id.generate(tb, false)?,
|
||||||
// There is an id field so use the record id
|
None => tb.generate(),
|
||||||
Ok(t) => i.ingest(Iterable::Relatable(f, t, w)),
|
};
|
||||||
},
|
i.ingest(Iterable::Relatable(f, id, w))
|
||||||
|
}
|
||||||
// There is no data clause so create a record id
|
// There is no data clause so create a record id
|
||||||
None => i.ingest(Iterable::Relatable(f, tb.generate(), w)),
|
None => i.ingest(Iterable::Relatable(f, tb.generate(), w)),
|
||||||
},
|
},
|
||||||
|
|
|
@ -111,7 +111,6 @@ impl SelectStatement {
|
||||||
Value::Array(v) => {
|
Value::Array(v) => {
|
||||||
for v in v {
|
for v in v {
|
||||||
match v {
|
match v {
|
||||||
Value::Table(v) => i.ingest(Iterable::Table(v)),
|
|
||||||
Value::Thing(v) => i.ingest(Iterable::Thing(v)),
|
Value::Thing(v) => i.ingest(Iterable::Thing(v)),
|
||||||
Value::Edges(v) => i.ingest(Iterable::Edges(*v)),
|
Value::Edges(v) => i.ingest(Iterable::Edges(*v)),
|
||||||
Value::Model(v) => {
|
Value::Model(v) => {
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::ctx::Context;
|
||||||
use crate::dbs::Iterator;
|
use crate::dbs::Iterator;
|
||||||
use crate::dbs::Options;
|
use crate::dbs::Options;
|
||||||
use crate::dbs::Statement;
|
use crate::dbs::Statement;
|
||||||
use crate::dbs::{Iterable, Transaction};
|
use crate::dbs::Transaction;
|
||||||
use crate::doc::CursorDoc;
|
use crate::doc::CursorDoc;
|
||||||
use crate::err::Error;
|
use crate::err::Error;
|
||||||
use crate::sql::comment::shouldbespace;
|
use crate::sql::comment::shouldbespace;
|
||||||
|
@ -56,65 +56,22 @@ impl UpdateStatement {
|
||||||
opt.valid_for_db()?;
|
opt.valid_for_db()?;
|
||||||
// Create a new iterator
|
// Create a new iterator
|
||||||
let mut i = Iterator::new();
|
let mut i = Iterator::new();
|
||||||
|
// Assign the statement
|
||||||
|
let stm = Statement::from(self);
|
||||||
// Ensure futures are stored
|
// Ensure futures are stored
|
||||||
let opt = &opt.new_with_futures(false);
|
let opt = &opt.new_with_futures(false);
|
||||||
// Loop over the update targets
|
// Loop over the update targets
|
||||||
for w in self.what.0.iter() {
|
for w in self.what.0.iter() {
|
||||||
let v = w.compute(ctx, opt, txn, doc).await?;
|
let v = w.compute(ctx, opt, txn, doc).await?;
|
||||||
match v {
|
i.prepare(ctx, opt, txn, &stm, v).await.map_err(|e| match e {
|
||||||
Value::Table(v) => i.ingest(Iterable::Table(v)),
|
Error::InvalidStatementTarget {
|
||||||
Value::Thing(v) => i.ingest(Iterable::Thing(v)),
|
value: v,
|
||||||
Value::Range(v) => i.ingest(Iterable::Range(*v)),
|
} => Error::UpdateStatement {
|
||||||
Value::Edges(v) => i.ingest(Iterable::Edges(*v)),
|
value: v,
|
||||||
Value::Model(v) => {
|
|
||||||
for v in v {
|
|
||||||
i.ingest(Iterable::Thing(v));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Value::Array(v) => {
|
|
||||||
for v in v {
|
|
||||||
match v {
|
|
||||||
Value::Table(v) => i.ingest(Iterable::Table(v)),
|
|
||||||
Value::Thing(v) => i.ingest(Iterable::Thing(v)),
|
|
||||||
Value::Edges(v) => i.ingest(Iterable::Edges(*v)),
|
|
||||||
Value::Model(v) => {
|
|
||||||
for v in v {
|
|
||||||
i.ingest(Iterable::Thing(v));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Value::Object(v) => match v.rid() {
|
|
||||||
Some(v) => i.ingest(Iterable::Thing(v)),
|
|
||||||
None => {
|
|
||||||
return Err(Error::UpdateStatement {
|
|
||||||
value: v.to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
v => {
|
|
||||||
return Err(Error::UpdateStatement {
|
|
||||||
value: v.to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Value::Object(v) => match v.rid() {
|
|
||||||
Some(v) => i.ingest(Iterable::Thing(v)),
|
|
||||||
None => {
|
|
||||||
return Err(Error::UpdateStatement {
|
|
||||||
value: v.to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
v => {
|
e => e,
|
||||||
return Err(Error::UpdateStatement {
|
})?;
|
||||||
value: v.to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
// Assign the statement
|
|
||||||
let stm = Statement::from(self);
|
|
||||||
// Output the results
|
// Output the results
|
||||||
i.output(ctx, opt, txn, &stm).await
|
i.output(ctx, opt, txn, &stm).await
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ impl Value {
|
||||||
id: id.as_int().into(),
|
id: id.as_int().into(),
|
||||||
}),
|
}),
|
||||||
// There is a string for the id field
|
// There is a string for the id field
|
||||||
Value::Strand(id) => Ok(Thing {
|
Value::Strand(id) if !id.is_empty() => Ok(Thing {
|
||||||
tb: tb.to_string(),
|
tb: tb.to_string(),
|
||||||
id: id.into(),
|
id: id.into(),
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -781,6 +781,14 @@ impl Value {
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert this Value to an Option
|
||||||
|
pub fn some(self) -> Option<Value> {
|
||||||
|
match self {
|
||||||
|
Value::None => None,
|
||||||
|
val => Some(val),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------
|
// -----------------------------------
|
||||||
// Simple value detection
|
// Simple value detection
|
||||||
// -----------------------------------
|
// -----------------------------------
|
||||||
|
|
|
@ -11,6 +11,7 @@ use surrealdb::sql::Value;
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn create_with_id() -> Result<(), Error> {
|
async fn create_with_id() -> Result<(), Error> {
|
||||||
let sql = "
|
let sql = "
|
||||||
|
-- Should succeed
|
||||||
CREATE person:test SET name = 'Tester';
|
CREATE person:test SET name = 'Tester';
|
||||||
CREATE person SET id = person:tobie, name = 'Tobie';
|
CREATE person SET id = person:tobie, name = 'Tobie';
|
||||||
CREATE person CONTENT { id: person:jaime, name: 'Jaime' };
|
CREATE person CONTENT { id: person:jaime, name: 'Jaime' };
|
||||||
|
@ -21,11 +22,17 @@ async fn create_with_id() -> Result<(), Error> {
|
||||||
CREATE test CONTENT { id: other:715917898417176677 };
|
CREATE test CONTENT { id: other:715917898417176677 };
|
||||||
CREATE test CONTENT { id: other:⟨715917898.417176677⟩ };
|
CREATE test CONTENT { id: other:⟨715917898.417176677⟩ };
|
||||||
CREATE test CONTENT { id: other:9223372036854775808 };
|
CREATE test CONTENT { id: other:9223372036854775808 };
|
||||||
|
-- Should error as id is empty
|
||||||
|
CREATE person SET id = '';
|
||||||
|
CREATE person CONTENT { id: '', name: 'Tester' };
|
||||||
|
-- Should error as id is mismatched
|
||||||
|
CREATE person:other SET id = 'tobie';
|
||||||
|
CREATE person:other CONTENT { id: 'tobie', name: 'Tester' };
|
||||||
";
|
";
|
||||||
let dbs = Datastore::new("memory").await?;
|
let dbs = Datastore::new("memory").await?;
|
||||||
let ses = Session::owner().with_ns("test").with_db("test");
|
let ses = Session::owner().with_ns("test").with_db("test");
|
||||||
let res = &mut dbs.execute(sql, &ses, None).await?;
|
let res = &mut dbs.execute(sql, &ses, None).await?;
|
||||||
assert_eq!(res.len(), 10);
|
assert_eq!(res.len(), 14);
|
||||||
//
|
//
|
||||||
let tmp = res.remove(0).result?;
|
let tmp = res.remove(0).result?;
|
||||||
let val = Value::parse(
|
let val = Value::parse(
|
||||||
|
@ -134,6 +141,30 @@ async fn create_with_id() -> Result<(), Error> {
|
||||||
);
|
);
|
||||||
assert_eq!(tmp, val);
|
assert_eq!(tmp, val);
|
||||||
//
|
//
|
||||||
|
let tmp = res.remove(0).result;
|
||||||
|
assert!(matches!(
|
||||||
|
tmp.err(),
|
||||||
|
Some(e) if e.to_string() == r#"Found '' for the Record ID but this is not a valid id"#
|
||||||
|
));
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result;
|
||||||
|
assert!(matches!(
|
||||||
|
tmp.err(),
|
||||||
|
Some(e) if e.to_string() == r#"Found '' for the Record ID but this is not a valid id"#
|
||||||
|
));
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result;
|
||||||
|
assert!(matches!(
|
||||||
|
tmp.err(),
|
||||||
|
Some(e) if e.to_string() == r#"Found 'tobie' for the id field, but a specific record has been specified"#
|
||||||
|
));
|
||||||
|
//
|
||||||
|
let tmp = res.remove(0).result;
|
||||||
|
assert!(matches!(
|
||||||
|
tmp.err(),
|
||||||
|
Some(e) if e.to_string() == r#"Found 'tobie' for the id field, but a specific record has been specified"#
|
||||||
|
));
|
||||||
|
//
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue