Implement DEFAULT
value clause on DEFINE FIELD
statements (#2468)
This commit is contained in:
parent
71adbe6f5a
commit
1fd6d991ae
2 changed files with 154 additions and 16 deletions
|
@ -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 {
|
||||
|
|
|
@ -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 = "
|
||||
|
|
Loading…
Reference in a new issue