diff --git a/lib/src/doc/field.rs b/lib/src/doc/field.rs index 895e9f33..5b01cb96 100644 --- a/lib/src/doc/field.rs +++ b/lib/src/doc/field.rs @@ -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 { diff --git a/lib/tests/field.rs b/lib/tests/field.rs index 71890972..9d22ab8c 100644 --- a/lib/tests/field.rs +++ b/lib/tests/field.rs @@ -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 = "