Implement DEFAULT value clause on DEFINE FIELD statements (#2468)

This commit is contained in:
Tobie Morgan Hitchcock 2023-08-20 03:53:50 +01:00 committed by GitHub
parent 71adbe6f5a
commit 1fd6d991ae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 154 additions and 16 deletions

View file

@ -31,24 +31,43 @@ impl<'a> Document<'a> {
let old = self.initial.doc.pick(&k);
// Get the input value
let inp = inp.pick(&k);
// Get the default value
let def = match &fd.default {
Some(v) => Some(v),
_ => match &fd.value {
Some(v) if v.is_static() => Some(v),
_ => None,
},
};
// Check for a DEFAULT clause
if let Some(expr) = def {
if self.is_new() && val.is_none() {
// Configure the context
let mut ctx = Context::new(ctx);
ctx.add_value("input", &inp);
ctx.add_value("value", &val);
ctx.add_value("after", &val);
ctx.add_value("before", &old);
// Process the VALUE clause
val = expr.compute(&ctx, opt, txn, Some(&self.current)).await?;
}
}
// Check for a TYPE clause
if let Some(kind) = &fd.kind {
if !val.is_none() {
val = val.coerce_to(kind).map_err(|e| match e {
// There was a conversion error
Error::CoerceTo {
from,
..
} => Error::FieldCheck {
thing: rid.to_string(),
field: fd.name.clone(),
value: from.to_string(),
check: kind.to_string(),
},
// There was a different error
e => e,
})?;
}
val = val.coerce_to(kind).map_err(|e| match e {
// There was a conversion error
Error::CoerceTo {
from,
..
} => Error::FieldCheck {
thing: rid.to_string(),
field: fd.name.clone(),
value: from.to_string(),
check: kind.to_string(),
},
// There was a different error
e => e,
})?;
}
// Check for a VALUE clause
if let Some(expr) = &fd.value {

View file

@ -305,6 +305,125 @@ async fn field_definition_empty_nested_flexible() -> Result<(), Error> {
Ok(())
}
#[tokio::test]
async fn field_definition_default_value() -> Result<(), Error> {
let sql = "
DEFINE TABLE product SCHEMAFULL;
DEFINE FIELD primary ON product TYPE number VALUE 123.456;
DEFINE FIELD secondary ON product TYPE bool DEFAULT true VALUE $value;
DEFINE FIELD tertiary ON product TYPE string DEFAULT 'hello' VALUE 'tester';
--
CREATE product:test SET primary = NULL;
CREATE product:test SET secondary = 'oops';
CREATE product:test SET tertiary = 123;
CREATE product:test;
--
UPDATE product:test SET primary = 654.321;
UPDATE product:test SET secondary = false;
UPDATE product:test SET tertiary = 'something';
";
let dbs = Datastore::new("memory").await?;
let ses = Session::owner().with_ns("test").with_db("test");
let res = &mut dbs.execute(sql, &ses, None).await?;
assert_eq!(res.len(), 11);
//
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;
assert!(tmp.is_ok());
//
let tmp = res.remove(0).result;
assert!(tmp.is_ok());
//
let tmp = res.remove(0).result;
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Found NULL for field `primary`, with record `product:test`, but expected a number"
),
"{}",
tmp.unwrap_err().to_string()
);
//
let tmp = res.remove(0).result;
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Found 'oops' for field `secondary`, with record `product:test`, but expected a bool"
),
"{}",
tmp.unwrap_err().to_string()
);
//
let tmp = res.remove(0).result;
assert!(
matches!(
&tmp,
Err(e) if e.to_string() == "Found 123 for field `tertiary`, with record `product:test`, but expected a string"
),
"{}",
tmp.unwrap_err().to_string()
);
//
let tmp = res.remove(0).result?;
let val = Value::parse(
"[
{
id: product:test,
primary: 123.456,
secondary: true,
tertiary: 'tester',
}
]",
);
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result?;
let val = Value::parse(
"[
{
id: product:test,
primary: 123.456,
secondary: true,
tertiary: 'tester',
}
]",
);
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result?;
let val = Value::parse(
"[
{
id: product:test,
primary: 123.456,
secondary: false,
tertiary: 'tester',
}
]",
);
assert_eq!(tmp, val);
//
let tmp = res.remove(0).result?;
let val = Value::parse(
"[
{
id: product:test,
primary: 123.456,
secondary: false,
tertiary: 'tester',
}
]",
);
assert_eq!(tmp, val);
//
Ok(())
}
#[tokio::test]
async fn field_definition_value_reference() -> Result<(), Error> {
let sql = "