Support `null` in dict and tymeta

next
Sayan Nandan 2 years ago
parent 173832dd13
commit 986cb26ebd
No known key found for this signature in database
GPG Key ID: 8BC07A0A4D41DD52

@ -61,3 +61,24 @@ impl<const N: usize> From<[DataType; N]> for DataType {
Self::List(f.into())
}
}
#[repr(u8, align(1))]
pub enum DataKind {
// primitive: integer unsigned
UInt8 = 0,
UInt16 = 1,
Uint32 = 2,
UInt64 = 3,
// primitive: integer unsigned
SInt8 = 4,
SInt16 = 5,
SInt32 = 6,
SInt64 = 7,
// primitive: misc
Bool = 8,
// compound: flat
String = 9,
Binary = 10,
// compound: recursive
List = 11,
}

@ -80,7 +80,7 @@ pub(super) fn parse_list(
while i < l && okay && !stop {
let d = match &tok[i] {
Token::Lit(Lit::Str(s)) => DataType::String(s.to_string()),
Token::Lit(Lit::Num(n)) => DataType::Number(*n),
Token::Lit(Lit::UnsignedInt(n)) => DataType::Number(*n),
Token::Lit(Lit::Bool(b)) => DataType::Boolean(*b),
Token::Symbol(Symbol::TtOpenSqBracket) => {
// a nested list
@ -141,7 +141,7 @@ pub(super) fn parse_data_tuple_syntax(tok: &[Token]) -> (Vec<Option<DataType>>,
Token::Lit(Lit::Str(s)) => {
data.push(Some(s.to_string().into()));
}
Token::Lit(Lit::Num(n)) => {
Token::Lit(Lit::UnsignedInt(n)) => {
data.push(Some((*n).into()));
}
Token::Lit(Lit::Bool(b)) => {
@ -201,7 +201,7 @@ pub(super) fn parse_data_map_syntax<'a>(
.insert(unsafe { id.as_slice() }, Some(s.to_string().into()))
.is_none();
}
(Token::Ident(id), Token::Lit(Lit::Num(n))) => {
(Token::Ident(id), Token::Lit(Lit::UnsignedInt(n))) => {
okay &= data
.insert(unsafe { id.as_slice() }, Some((*n).into()))
.is_none();

@ -74,7 +74,7 @@ enum_impls! {
pub enum Lit {
Str(Box<str>),
Bool(bool),
Num(u64),
UnsignedInt(u64),
UnsafeLit(RawSlice),
}
@ -89,7 +89,7 @@ enum_impls! {
Box<str> as Str,
String as Str,
bool as Bool,
u64 as Num,
u64 as UnsignedInt,
}
}
@ -514,15 +514,16 @@ impl<'a> Lexer<'a> {
1234, // valid
1234a // invalid
*/
static TERMINAL_CHAR: [u8; 8] = [b';', b'}', b',', b' ', b'\n', b'\t', b',', b']'];
let wseof = self.peek_is(|b| TERMINAL_CHAR.contains(&b)) || self.exhausted();
let wseof = self.peek_is(|char| !char.is_ascii_alphabetic()) || self.exhausted();
match str::from_utf8_unchecked(slice::from_raw_parts(
s,
self.cursor().offset_from(s) as usize,
))
.parse()
{
Ok(num) if compiler::likely(wseof) => self.tokens.push(Token::Lit(Lit::Num(num))),
Ok(num) if compiler::likely(wseof) => {
self.tokens.push(Token::Lit(Lit::UnsignedInt(num)))
}
_ => self.last_error = Some(LangError::InvalidNumericLiteral),
}
}

@ -306,6 +306,19 @@ macro_rules! dict {
}};
}
macro_rules! nullable_dict {
() => {
dict! {}
};
($($key:expr => $value:expr),* $(,)?) => {
dict! {
$(
$key => $crate::engine::ql::tests::NullableMapEntry::data($value),
)*
}
};
}
macro_rules! dict_nullable {
() => {
<::std::collections::HashMap<_, _> as ::core::default::Default>::default()

@ -105,7 +105,7 @@ impl From<Dict> for DictEntry {
}
/// A metadata dictionary
pub type Dict = HashMap<String, DictEntry>;
pub type Dict = HashMap<String, Option<DictEntry>>;
#[derive(Debug, PartialEq)]
/// A layer contains a type and corresponding metadata
@ -271,11 +271,19 @@ pub(super) fn rfold_dict(mut state: DictFoldState, tok: &[Token], dict: &mut Dic
okay &= dict
.insert(
unsafe { tmp.assume_init_ref() }.to_string(),
l.clone().into(),
Some(l.clone().into()),
)
.is_none();
state = DictFoldState::COMMA_OR_CB;
}
(Token![null], DictFoldState::LIT_OR_OB) => {
// null
i += 1;
okay &= dict
.insert(unsafe { tmp.assume_init_ref() }.to_string(), None)
.is_none();
state = DictFoldState::COMMA_OR_CB;
}
// ONLY COMMA CAPTURE
(Token::Symbol(Symbol::SymComma), DictFoldState::COMMA_OR_CB) => {
i += 1;
@ -292,7 +300,7 @@ pub(super) fn rfold_dict(mut state: DictFoldState, tok: &[Token], dict: &mut Dic
okay &= dict
.insert(
unsafe { tmp.assume_init_ref() }.to_string(),
new_dict.into(),
Some(new_dict.into()),
)
.is_none();
// at the end of a dict we either expect a comma or close brace
@ -465,13 +473,22 @@ pub(super) fn rfold_tymeta<const ALLOW_RESET: bool>(
r.record(
dict.insert(
unsafe { tmp.assume_init_ref() }.to_string(),
lit.clone().into(),
Some(lit.clone().into()),
)
.is_none(),
);
// saw a literal. next is either comma or close brace
state = TyMetaFoldState::COMMA_OR_CB;
}
(Token![null], TyMetaFoldState::LIT_OR_OB) => {
r.incr();
r.record(
dict.insert(unsafe { tmp.assume_init_ref() }.to_string(), None)
.is_none(),
);
// saw null, start parsing another entry
state = TyMetaFoldState::COMMA_OR_CB;
}
(Token::Symbol(Symbol::SymComma), TyMetaFoldState::COMMA_OR_CB) => {
r.incr();
// next is strictly a close brace or ident
@ -488,10 +505,11 @@ pub(super) fn rfold_tymeta<const ALLOW_RESET: bool>(
);
r.incr_by(ret.pos());
r.record(ret.is_okay());
r.record(!ret.has_more()); // L2 cannot have type definitions
// end of definition or comma followed by something
// L2 cannot have type definitions
r.record(!ret.has_more());
// end of definition or comma followed by something
r.record(
dict.insert(unsafe { tmp.assume_init_ref() }.to_string(), d.into())
dict.insert(unsafe { tmp.assume_init_ref() }.to_string(), Some(d.into()))
.is_none(),
);
state = TyMetaFoldState::COMMA_OR_CB;
@ -870,9 +888,7 @@ pub(super) fn parse_alter_space_from_tokens(tok: &[Token]) -> LangResult<(AlterS
return Err(LangError::UnexpectedToken);
}
let space_name = unsafe {
extract!(tok[0], Token::Ident(ref space) => space.clone())
};
let space_name = unsafe { extract!(tok[0], Token::Ident(ref space) => space.clone()) };
i += 3;

@ -61,6 +61,34 @@ fn nullable_datatype(v: impl NullableData<DataType>) -> Option<DataType> {
v.data()
}
pub trait NullableMapEntry {
fn data(self) -> Option<super::schema::DictEntry>;
}
impl NullableMapEntry for Null {
fn data(self) -> Option<super::schema::DictEntry> {
None
}
}
impl NullableMapEntry for super::lexer::Lit {
fn data(self) -> Option<super::schema::DictEntry> {
Some(super::schema::DictEntry::Lit(self))
}
}
impl NullableMapEntry for super::schema::Dict {
fn data(self) -> Option<super::schema::DictEntry> {
Some(super::schema::DictEntry::Map(self))
}
}
macro_rules! fold_dict {
($($input:expr),* $(,)?) => {
($({$crate::engine::ql::schema::fold_dict(&super::lex($input).unwrap()).unwrap()}),*)
}
}
mod lexer_tests {
use {
super::{
@ -89,7 +117,10 @@ mod lexer_tests {
#[test]
fn lex_number() {
let number = v!("123456");
assert_eq!(lex(&number).unwrap(), vec![Token::Lit(Lit::Num(123456))]);
assert_eq!(
lex(&number).unwrap(),
vec![Token::Lit(Lit::UnsignedInt(123456))]
);
}
#[test]
fn lex_bool() {
@ -362,7 +393,7 @@ mod schema_tests {
r,
AlterSpace {
space_name: "mymodel".into(),
updated_props: dict! {}
updated_props: nullable_dict! {}
}
);
}
@ -380,8 +411,8 @@ mod schema_tests {
r,
AlterSpace {
space_name: "mymodel".into(),
updated_props: dict! {
"max_entry" => Lit::Num(1000),
updated_props: nullable_dict! {
"max_entry" => Lit::UnsignedInt(1000),
"driver" => Lit::Str("ts-0.8".into())
}
}
@ -392,19 +423,13 @@ mod schema_tests {
mod dict {
use super::*;
macro_rules! fold_dict {
($($input:expr),* $(,)?) => {
($({schema::fold_dict(&super::lex($input).unwrap()).unwrap()}),*)
}
}
#[test]
fn dict_read_mini() {
let (d1, d2) = fold_dict! {
br#"{name: "sayan"}"#,
br#"{name: "sayan",}"#,
};
let r = dict!("name" => Lit::Str("sayan".into()));
let r = nullable_dict!("name" => Lit::Str("sayan".into()));
multi_assert_eq!(d1, d2 => r);
}
#[test]
@ -425,10 +450,10 @@ mod schema_tests {
}
"#,
};
let r = dict! (
let r = nullable_dict! (
"name" => Lit::Str("sayan".into()),
"verified" => Lit::Bool(true),
"burgers" => Lit::Num(152),
"burgers" => Lit::UnsignedInt(152),
);
multi_assert_eq!(d1, d2 => r);
}
@ -466,12 +491,12 @@ mod schema_tests {
}"#
};
multi_assert_eq!(
d1, d2, d3 => dict! {
d1, d2, d3 => nullable_dict! {
"name" => Lit::Str("sayan".into()),
"notes" => dict! {
"notes" => nullable_dict! {
"burgers" => Lit::Str("all the time, extra mayo".into()),
"taco" => Lit::Bool(true),
"pretzels" => Lit::Num(1),
"pretzels" => Lit::UnsignedInt(1),
}
}
);
@ -521,11 +546,11 @@ mod schema_tests {
}"#
};
multi_assert_eq!(
d1, d2, d3 => dict! {
"well" => dict! {
"now" => dict! {
"this" => dict! {
"is" => dict! {
d1, d2, d3 => nullable_dict! {
"well" => nullable_dict! {
"now" => nullable_dict! {
"this" => nullable_dict! {
"is" => nullable_dict! {
"ridiculous" => Lit::Bool(true),
}
}
@ -554,16 +579,16 @@ mod schema_tests {
}
")
.unwrap();
let ret_dict = dict! {
let ret_dict = nullable_dict! {
"the_tradition_is" => Lit::Str("hello, world".into()),
"could_have_been" => dict! {
"could_have_been" => nullable_dict! {
"this" => Lit::Bool(true),
"or_maybe_this" => Lit::Num(100),
"or_maybe_this" => Lit::UnsignedInt(100),
"even_this" => Lit::Str("hello, universe!".into()),
},
"but_oh_well" => Lit::Str("it continues to be the 'annoying' phrase".into()),
"lorem" => dict! {
"ipsum" => dict! {
"lorem" => nullable_dict! {
"ipsum" => nullable_dict! {
"dolor" => Lit::Str("sit amet".into())
}
}
@ -591,7 +616,7 @@ mod schema_tests {
assert!(res.is_okay());
assert!(!res.has_more());
assert_eq!(res.pos(), 1);
assert_eq!(ret, dict!());
assert_eq!(ret, nullable_dict!());
}
#[test]
fn tymeta_mini_fail() {
@ -600,7 +625,7 @@ mod schema_tests {
assert!(!res.is_okay());
assert!(!res.has_more());
assert_eq!(res.pos(), 0);
assert_eq!(ret, dict!());
assert_eq!(ret, nullable_dict!());
}
#[test]
fn tymeta() {
@ -611,10 +636,10 @@ mod schema_tests {
assert_eq!(res.pos(), tok.len());
assert_eq!(
ret,
dict! {
nullable_dict! {
"hello" => Lit::Str("world".into()),
"loading" => Lit::Bool(true),
"size" => Lit::Num(100)
"size" => Lit::UnsignedInt(100)
}
);
}
@ -636,8 +661,8 @@ mod schema_tests {
final_ret.extend(ret2);
assert_eq!(
final_ret,
dict! {
"maxlen" => Lit::Num(100),
nullable_dict! {
"maxlen" => Lit::UnsignedInt(100),
"unique" => Lit::Bool(true)
}
)
@ -661,10 +686,10 @@ mod schema_tests {
final_ret.extend(ret2);
assert_eq!(
final_ret,
dict! {
"maxlen" => Lit::Num(100),
nullable_dict! {
"maxlen" => Lit::UnsignedInt(100),
"unique" => Lit::Bool(true),
"this" => dict! {
"this" => nullable_dict! {
"is" => Lit::Str("cool".into())
}
}
@ -684,10 +709,10 @@ mod schema_tests {
}
")
.unwrap();
let expected = dict! {
"maxlen" => Lit::Num(10),
let expected = nullable_dict! {
"maxlen" => Lit::UnsignedInt(10),
"unique" => Lit::Bool(true),
"auth" => dict! {
"auth" => nullable_dict! {
"maybe" => Lit::Bool(true),
},
"user" => Lit::Str("sayan".into())
@ -722,10 +747,10 @@ mod schema_tests {
}
")
.unwrap();
let expected = dict! {
"maxlen" => Lit::Num(10),
let expected = nullable_dict! {
"maxlen" => Lit::UnsignedInt(10),
"unique" => Lit::Bool(true),
"auth" => dict! {
"auth" => nullable_dict! {
"maybe" => Lit::Bool(true),
},
};
@ -751,7 +776,10 @@ mod schema_tests {
let (layers, c, okay) = schema::fold_layers(&tok);
assert_eq!(c, tok.len() - 1);
assert!(okay);
assert_eq!(layers, vec![Layer::new_noreset(Type::String, dict! {})]);
assert_eq!(
layers,
vec![Layer::new_noreset(Type::String, nullable_dict! {})]
);
}
#[test]
fn layer() {
@ -763,8 +791,8 @@ mod schema_tests {
layers,
vec![Layer::new_noreset(
Type::String,
dict! {
"maxlen" => Lit::Num(100)
nullable_dict! {
"maxlen" => Lit::UnsignedInt(100)
}
)]
);
@ -778,8 +806,8 @@ mod schema_tests {
assert_eq!(
layers,
vec![
Layer::new_noreset(Type::String, dict! {}),
Layer::new_noreset(Type::List, dict! {})
Layer::new_noreset(Type::String, nullable_dict! {}),
Layer::new_noreset(Type::List, nullable_dict! {})
]
);
}
@ -792,12 +820,12 @@ mod schema_tests {
assert_eq!(
layers,
vec![
Layer::new_noreset(Type::String, dict! {}),
Layer::new_noreset(Type::String, nullable_dict! {}),
Layer::new_noreset(
Type::List,
dict! {
nullable_dict! {
"unique" => Lit::Bool(true),
"maxlen" => Lit::Num(10),
"maxlen" => Lit::UnsignedInt(10),
}
)
]
@ -817,16 +845,16 @@ mod schema_tests {
vec![
Layer::new_noreset(
Type::String,
dict! {
nullable_dict! {
"ascii_only" => Lit::Bool(true),
"maxlen" => Lit::Num(255)
"maxlen" => Lit::UnsignedInt(255)
}
),
Layer::new_noreset(
Type::List,
dict! {
nullable_dict! {
"unique" => Lit::Bool(true),
"maxlen" => Lit::Num(10),
"maxlen" => Lit::UnsignedInt(10),
}
)
]
@ -846,14 +874,14 @@ mod schema_tests {
")
.unwrap();
let expected = vec![
Layer::new_noreset(Type::String, dict!()),
Layer::new_noreset(Type::String, nullable_dict!()),
Layer::new_noreset(
Type::List,
dict! {
"maxlen" => Lit::Num(100),
nullable_dict! {
"maxlen" => Lit::UnsignedInt(100),
},
),
Layer::new_noreset(Type::List, dict!("unique" => Lit::Bool(true))),
Layer::new_noreset(Type::List, nullable_dict!("unique" => Lit::Bool(true))),
];
fuzz_tokens(&tok, |should_pass, new_tok| {
let (layers, c, okay) = schema::fold_layers(&new_tok);
@ -916,7 +944,7 @@ mod schema_tests {
f,
Field {
field_name: "username".into(),
layers: [Layer::new_noreset(Type::String, dict! {})].into(),
layers: [Layer::new_noreset(Type::String, nullable_dict! {})].into(),
props: set![],
}
)
@ -933,7 +961,7 @@ mod schema_tests {
f,
Field {
field_name: "username".into(),
layers: [Layer::new_noreset(Type::String, dict! {})].into(),
layers: [Layer::new_noreset(Type::String, nullable_dict! {})].into(),
props: set!["primary"],
}
)
@ -955,8 +983,8 @@ mod schema_tests {
field_name: "username".into(),
layers: [Layer::new_noreset(
Type::String,
dict! {
"maxlen" => Lit::Num(10),
nullable_dict! {
"maxlen" => Lit::UnsignedInt(10),
"ascii_only" => Lit::Bool(true),
}
)]
@ -986,14 +1014,14 @@ mod schema_tests {
layers: [
Layer::new_noreset(
Type::String,
dict! {
"maxlen" => Lit::Num(255),
nullable_dict! {
"maxlen" => Lit::UnsignedInt(255),
"ascii_only" => Lit::Bool(true),
}
),
Layer::new_noreset(
Type::List,
dict! {
nullable_dict! {
"unique" => Lit::Bool(true)
}
),
@ -1032,16 +1060,16 @@ mod schema_tests {
fields: vec![
Field {
field_name: "username".into(),
layers: vec![Layer::new_noreset(Type::String, dict! {})],
layers: vec![Layer::new_noreset(Type::String, nullable_dict! {})],
props: set!["primary"]
},
Field {
field_name: "password".into(),
layers: vec![Layer::new_noreset(Type::Binary, dict! {})],
layers: vec![Layer::new_noreset(Type::Binary, nullable_dict! {})],
props: set![]
}
],
props: dict! {}
props: nullable_dict! {}
}
)
}
@ -1067,21 +1095,21 @@ mod schema_tests {
fields: vec![
Field {
field_name: "username".into(),
layers: vec![Layer::new_noreset(Type::String, dict! {})],
layers: vec![Layer::new_noreset(Type::String, nullable_dict! {})],
props: set!["primary"]
},
Field {
field_name: "password".into(),
layers: vec![Layer::new_noreset(Type::Binary, dict! {})],
layers: vec![Layer::new_noreset(Type::Binary, nullable_dict! {})],
props: set![]
},
Field {
field_name: "profile_pic".into(),
layers: vec![Layer::new_noreset(Type::Binary, dict! {})],
layers: vec![Layer::new_noreset(Type::Binary, nullable_dict! {})],
props: set!["null"]
}
],
props: dict! {}
props: nullable_dict! {}
}
)
}
@ -1112,26 +1140,26 @@ mod schema_tests {
fields: vec![
Field {
field_name: "username".into(),
layers: vec![Layer::new_noreset(Type::String, dict! {})],
layers: vec![Layer::new_noreset(Type::String, nullable_dict! {})],
props: set!["primary"]
},
Field {
field_name: "password".into(),
layers: vec![Layer::new_noreset(Type::Binary, dict! {})],
layers: vec![Layer::new_noreset(Type::Binary, nullable_dict! {})],
props: set![]
},
Field {
field_name: "profile_pic".into(),
layers: vec![Layer::new_noreset(Type::Binary, dict! {})],
layers: vec![Layer::new_noreset(Type::Binary, nullable_dict! {})],
props: set!["null"]
},
Field {
field_name: "notes".into(),
layers: vec![
Layer::new_noreset(Type::String, dict! {}),
Layer::new_noreset(Type::String, nullable_dict! {}),
Layer::new_noreset(
Type::List,
dict! {
nullable_dict! {
"unique" => Lit::Bool(true)
}
)
@ -1139,7 +1167,7 @@ mod schema_tests {
props: set!["null"]
}
],
props: dict! {}
props: nullable_dict! {}
}
)
}
@ -1175,26 +1203,26 @@ mod schema_tests {
fields: vec![
Field {
field_name: "username".into(),
layers: vec![Layer::new_noreset(Type::String, dict! {})],
layers: vec![Layer::new_noreset(Type::String, nullable_dict! {})],
props: set!["primary"]
},
Field {
field_name: "password".into(),
layers: vec![Layer::new_noreset(Type::Binary, dict! {})],
layers: vec![Layer::new_noreset(Type::Binary, nullable_dict! {})],
props: set![]
},
Field {
field_name: "profile_pic".into(),
layers: vec![Layer::new_noreset(Type::Binary, dict! {})],
layers: vec![Layer::new_noreset(Type::Binary, nullable_dict! {})],
props: set!["null"]
},
Field {
field_name: "notes".into(),
layers: vec![
Layer::new_noreset(Type::String, dict! {}),
Layer::new_noreset(Type::String, nullable_dict! {}),
Layer::new_noreset(
Type::List,
dict! {
nullable_dict! {
"unique" => Lit::Bool(true)
}
)
@ -1202,9 +1230,9 @@ mod schema_tests {
props: set!["null"]
}
],
props: dict! {
"env" => dict! {
"free_user_limit" => Lit::Num(100),
props: nullable_dict! {
"env" => nullable_dict! {
"free_user_limit" => Lit::UnsignedInt(100),
},
"storage_driver" => Lit::Str("skyheap".into()),
}
@ -1227,8 +1255,8 @@ mod schema_tests {
ef,
ExpandedField {
field_name: "username".into(),
layers: vec![Layer::new_noreset(Type::String, dict! {})],
props: dict! {},
layers: vec![Layer::new_noreset(Type::String, nullable_dict! {})],
props: nullable_dict! {},
reset: false
}
)
@ -1248,10 +1276,10 @@ mod schema_tests {
ef,
ExpandedField {
field_name: "username".into(),
props: dict! {
props: nullable_dict! {
"nullable" => Lit::Bool(false),
},
layers: vec![Layer::new_noreset(Type::String, dict! {})],
layers: vec![Layer::new_noreset(Type::String, nullable_dict! {})],
reset: false
}
);
@ -1275,15 +1303,15 @@ mod schema_tests {
ef,
ExpandedField {
field_name: "username".into(),
props: dict! {
props: nullable_dict! {
"nullable" => Lit::Bool(false),
"jingle_bells" => Lit::Str("snow".into()),
},
layers: vec![Layer::new_noreset(
Type::String,
dict! {
"minlen" => Lit::Num(6),
"maxlen" => Lit::Num(255),
nullable_dict! {
"minlen" => Lit::UnsignedInt(6),
"maxlen" => Lit::UnsignedInt(255),
}
)],
reset: false
@ -1311,20 +1339,20 @@ mod schema_tests {
ef,
ExpandedField {
field_name: "notes".into(),
props: dict! {
props: nullable_dict! {
"nullable" => Lit::Bool(true),
"jingle_bells" => Lit::Str("snow".into()),
},
layers: vec![
Layer::new_noreset(
Type::String,
dict! {
nullable_dict! {
"ascii_only" => Lit::Bool(true),
}
),
Layer::new_noreset(
Type::List,
dict! {
nullable_dict! {
"unique" => Lit::Bool(true),
}
)
@ -1391,8 +1419,8 @@ mod schema_tests {
r.as_ref(),
[ExpandedField {
field_name: "myfield".into(),
props: dict! {},
layers: [Layer::new_noreset(Type::String, dict! {})].into(),
props: nullable_dict! {},
layers: [Layer::new_noreset(Type::String, nullable_dict! {})].into(),
reset: false
}]
);
@ -1410,10 +1438,10 @@ mod schema_tests {
r.as_ref(),
[ExpandedField {
field_name: "myfield".into(),
props: dict! {
props: nullable_dict! {
"nullable" => Lit::Bool(true)
},
layers: [Layer::new_noreset(Type::String, dict! {})].into(),
layers: [Layer::new_noreset(Type::String, nullable_dict! {})].into(),
reset: false
}]
);
@ -1431,10 +1459,10 @@ mod schema_tests {
r.as_ref(),
[ExpandedField {
field_name: "myfield".into(),
props: dict! {
props: nullable_dict! {
"nullable" => Lit::Bool(true)
},
layers: [Layer::new_noreset(Type::String, dict! {})].into(),
layers: [Layer::new_noreset(Type::String, nullable_dict! {})].into(),
reset: false
}]
);
@ -1467,27 +1495,27 @@ mod schema_tests {
[
ExpandedField {
field_name: "myfield".into(),
props: dict! {
props: nullable_dict! {
"nullable" => Lit::Bool(true)
},
layers: [Layer::new_noreset(Type::String, dict! {})].into(),
layers: [Layer::new_noreset(Type::String, nullable_dict! {})].into(),
reset: false
},
ExpandedField {
field_name: "another".into(),
props: dict! {
props: nullable_dict! {
"nullable" => Lit::Bool(false)
},
layers: [
Layer::new_noreset(
Type::String,
dict! {
"maxlen" => Lit::Num(255)
nullable_dict! {
"maxlen" => Lit::UnsignedInt(255)
}
),
Layer::new_noreset(
Type::List,
dict! {
nullable_dict! {
"unique" => Lit::Bool(true)
},
)
@ -1519,8 +1547,8 @@ mod schema_tests {
r.as_ref(),
[ExpandedField {
field_name: "myfield".into(),
props: dict! {},
layers: [Layer::new_noreset(Type::String, dict! {})].into(),
props: nullable_dict! {},
layers: [Layer::new_noreset(Type::String, nullable_dict! {})].into(),
reset: true
}]
);
@ -1538,8 +1566,8 @@ mod schema_tests {
r.as_ref(),
[ExpandedField {
field_name: "myfield".into(),
props: dict! {},
layers: [Layer::new_noreset(Type::String, dict! {})].into(),
props: nullable_dict! {},
layers: [Layer::new_noreset(Type::String, nullable_dict! {})].into(),
reset: true
}]
);
@ -1563,10 +1591,10 @@ mod schema_tests {
r.as_ref(),
[ExpandedField {
field_name: "myfield".into(),
props: dict! {
props: nullable_dict! {
"nullable" => Lit::Bool(true)
},
layers: [Layer::new_noreset(Type::String, dict! {})].into(),
layers: [Layer::new_noreset(Type::String, nullable_dict! {})].into(),
reset: true
}]
);
@ -1595,16 +1623,16 @@ mod schema_tests {
[
ExpandedField {
field_name: "myfield".into(),
props: dict! {
props: nullable_dict! {
"nullable" => Lit::Bool(true)
},
layers: [Layer::new_noreset(Type::String, dict! {})].into(),
layers: [Layer::new_noreset(Type::String, nullable_dict! {})].into(),
reset: true
},
ExpandedField {
field_name: "myfield2".into(),
props: dict! {},
layers: [Layer::new_noreset(Type::String, dict! {})].into(),
props: nullable_dict! {},
layers: [Layer::new_noreset(Type::String, nullable_dict! {})].into(),
reset: true
}
]
@ -1637,18 +1665,18 @@ mod schema_tests {
[
ExpandedField {
field_name: "myfield".into(),
props: dict! {
props: nullable_dict! {
"nullable" => Lit::Bool(true)
},
layers: [Layer::new_reset(Type::String, dict! {})].into(),
layers: [Layer::new_reset(Type::String, nullable_dict! {})].into(),
reset: true
},
ExpandedField {
field_name: "myfield2".into(),
props: dict! {},
props: nullable_dict! {},
layers: [Layer::new_reset(
Type::String,
dict! {"maxlen" => Lit::Num(255)}
nullable_dict! {"maxlen" => Lit::UnsignedInt(255)}
)]
.into(),
reset: true
@ -1823,7 +1851,7 @@ mod dml_tests {
fn map_mini() {
let tok = lex(b"{}").unwrap();
let r = parse_data_map_syntax_full(&tok[1..]).unwrap();
assert_eq!(r, dict! {})
assert_eq!(r, nullable_dict! {})
}
#[test]
@ -1997,7 +2025,7 @@ mod dml_tests {
let e = InsertStatement {
primary_key: &("sayan".to_string().into()),
entity: Entity::Full("jotsy".into(), "app".into()),
data: dict! {}.into(),
data: nullable_dict! {}.into(),
};
assert_eq!(e, r);
}
@ -2290,3 +2318,90 @@ mod dml_tests {
}
}
}
mod nullable_dict_tests {
use super::*;
mod dict {
use {super::*, crate::engine::ql::lexer::Lit};
#[test]
fn null_mini() {
let d = fold_dict!(br"{ x: null }");
assert_eq!(
d,
nullable_dict! {
"x" => Null,
}
);
}
#[test]
fn null() {
let d = fold_dict! {
br#"
{
this_is_non_null: "hello",
but_this_is_null: null,
}
"#
};
assert_eq!(
d,
nullable_dict! {
"this_is_non_null" => Lit::Str("hello".into()),
"but_this_is_null" => Null,
}
)
}
#[test]
fn null_pro() {
let d = fold_dict! {
br#"
{
a_string: "this is a string",
num: 1234,
a_dict: {
a_null: null,
}
}
"#
};
assert_eq!(
d,
nullable_dict! {
"a_string" => Lit::Str("this is a string".into()),
"num" => Lit::UnsignedInt(1234),
"a_dict" => nullable_dict! {
"a_null" => Null,
}
}
)
}
#[test]
fn null_pro_max() {
let d = fold_dict! {
br#"
{
a_string: "this is a string",
num: 1234,
a_dict: {
a_null: null,
},
another_null: null,
}
"#
};
assert_eq!(
d,
nullable_dict! {
"a_string" => Lit::Str("this is a string".into()),
"num" => Lit::UnsignedInt(1234),
"a_dict" => nullable_dict! {
"a_null" => Null,
},
"another_null" => Null,
}
)
}
}
// TODO(@ohsayan): Add null tests
}

Loading…
Cancel
Save