Skip ASSERT for type option<T> and value NONE (#4483)

Co-authored-by: Tobie Morgan Hitchcock <tobie@surrealdb.com>
This commit is contained in:
Micha de Vries 2024-08-08 17:11:39 +01:00 committed by GitHub
parent c5a6e08225
commit 3f5ef43248
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 79 additions and 14 deletions

View file

@ -6,6 +6,7 @@ use crate::err::Error;
use crate::iam::Action; use crate::iam::Action;
use crate::sql::permission::Permission; use crate::sql::permission::Permission;
use crate::sql::value::Value; use crate::sql::value::Value;
use crate::sql::Kind;
use reblessive::tree::Stk; use reblessive::tree::Stk;
impl<'a> Document<'a> { impl<'a> Document<'a> {
@ -110,20 +111,30 @@ impl<'a> Document<'a> {
} }
// Check for a ASSERT clause // Check for a ASSERT clause
if let Some(expr) = &fd.assert { if let Some(expr) = &fd.assert {
// Configure the context match (&val, &fd.kind) {
let mut ctx = Context::new(ctx); // The field TYPE is optional, and the field
ctx.add_value("input", &inp); // value was not set or a NONE value was
ctx.add_value("value", &val); // specified, so let's ignore the ASSERT clause
ctx.add_value("after", &val); (Value::None, Some(Kind::Option(_))) => (),
ctx.add_value("before", &old); // Otherwise let's process the ASSERT clause
// Process the ASSERT clause _ => {
if !expr.compute(stk, &ctx, opt, Some(&self.current)).await?.is_truthy() { // Configure the context
return Err(Error::FieldValue { let mut ctx = Context::new(ctx);
thing: rid.to_string(), ctx.add_value("input", &inp);
field: fd.name.clone(), ctx.add_value("value", &val);
value: val.to_string(), ctx.add_value("after", &val);
check: expr.to_string(), ctx.add_value("before", &old);
}); // Process the ASSERT clause
if !expr.compute(stk, &ctx, opt, Some(&self.current)).await?.is_truthy()
{
return Err(Error::FieldValue {
thing: rid.to_string(),
field: fd.name.clone(),
value: val.to_string(),
check: expr.to_string(),
});
}
}
} }
} }
// Check for a PERMISSIONS clause // Check for a PERMISSIONS clause

View file

@ -138,6 +138,60 @@ async fn field_definition_value_assert_success() -> Result<(), Error> {
Ok(()) Ok(())
} }
#[tokio::test]
async fn field_definition_option_kind_assert() -> Result<(), Error> {
let sql = "
DEFINE TABLE person SCHEMAFULL;
DEFINE FIELD name ON TABLE person TYPE option<string> ASSERT string::len($value) > 3;
CREATE person:test;
CREATE person:mark SET name = 'mark';
CREATE person:bob SET name = 'bob';
";
let dbs = new_ds().await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 5);
//
let tmp = res.remove(0).result;
assert!(tmp.is_ok());
//
let tmp = res.remove(0).result;
assert!(tmp.is_ok());
//
let tmp = res.remove(0).result?;
let val = Value::parse(
"[
{
id: person:test
}
]",
);
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result?;
let val = Value::parse(
"[
{
id: person:mark,
name: 'mark'
}
]",
);
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result;
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Found 'bob' for field `name`, with record `person:bob`, but field must conform to: string::len($value) > 3"
),
"{}",
tmp.unwrap_err().to_string()
);
Ok(())
}
#[tokio::test] #[tokio::test]
async fn field_definition_empty_nested_objects() -> Result<(), Error> { async fn field_definition_empty_nested_objects() -> Result<(), Error> {
let sql = " let sql = "