diff --git a/src/ast/dml/parts/order_by.rs b/src/ast/dml/parts/order_by.rs index 4214911..c71c692 100644 --- a/src/ast/dml/parts/order_by.rs +++ b/src/ast/dml/parts/order_by.rs @@ -7,7 +7,7 @@ pub struct OrderByClause { pub order_by_items: Vec, } -#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)] +#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Default)] pub struct OrderByItem { pub item: SQLExpression, pub order_type: OrderByType, @@ -20,8 +20,20 @@ pub enum OrderByType { Desc, } +impl Default for OrderByType { + fn default() -> Self { + OrderByType::Asc + } +} + #[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)] pub enum OrderByNulls { First, Last, } + +impl Default for OrderByNulls { + fn default() -> Self { + OrderByNulls::Last + } +} diff --git a/src/ast/types/expression.rs b/src/ast/types/expression.rs index a8e53e1..6c2930a 100644 --- a/src/ast/types/expression.rs +++ b/src/ast/types/expression.rs @@ -37,6 +37,12 @@ pub enum SQLExpression { Null, } +impl Default for SQLExpression { + fn default() -> Self { + Self::Null + } +} + impl SQLExpression { pub fn is_unary(&self) -> bool { #[allow(clippy::match_like_matches_macro)] diff --git a/src/parser/test/select.rs b/src/parser/test/select.rs index 0cf4e54..ff22cba 100644 --- a/src/parser/test/select.rs +++ b/src/parser/test/select.rs @@ -1859,3 +1859,114 @@ fn test_parse_select_item() { } } } + +#[test] +fn test_parse_order_by_item() { + struct TestCase { + name: String, + input: Vec, + expected: OrderByItem, + want_error: bool, + } + + let test_cases = vec![ + TestCase { + name: "p.id ASC".into(), + input: vec![ + Token::Identifier("p".into()), + Token::Period, + Token::Identifier("id".into()), + Token::Asc, + ], + expected: OrderByItem { + item: SelectColumn::new(Some("p".into()), "id".into()).into(), + order_type: OrderByType::Asc, + nulls: OrderByNulls::First, + }, + want_error: false, + }, + TestCase { + name: "p.id DESC".into(), + input: vec![ + Token::Identifier("p".into()), + Token::Period, + Token::Identifier("id".into()), + Token::Desc, + ], + expected: OrderByItem { + item: SelectColumn::new(Some("p".into()), "id".into()).into(), + order_type: OrderByType::Desc, + nulls: OrderByNulls::First, + }, + want_error: false, + }, + TestCase { + name: "p.id NULLS FIRST".into(), + input: vec![ + Token::Identifier("p".into()), + Token::Period, + Token::Identifier("id".into()), + Token::Nulls, + Token::First, + ], + expected: OrderByItem { + item: SelectColumn::new(Some("p".into()), "id".into()).into(), + order_type: OrderByType::Asc, + nulls: OrderByNulls::First, + }, + want_error: false, + }, + TestCase { + name: "p.id NULLS LAST".into(), + input: vec![ + Token::Identifier("p".into()), + Token::Period, + Token::Identifier("id".into()), + Token::Nulls, + Token::Last, + ], + expected: OrderByItem { + item: SelectColumn::new(Some("p".into()), "id".into()).into(), + order_type: OrderByType::Asc, + nulls: OrderByNulls::Last, + }, + want_error: false, + }, + TestCase { + name: "실패: 빈 토큰".into(), + input: vec![], + expected: Default::default(), + want_error: true, + }, + TestCase { + name: "실패: p.id NULLS".into(), + input: vec![ + Token::Identifier("p".into()), + Token::Period, + Token::Identifier("id".into()), + Token::Nulls, + ], + expected: Default::default(), + want_error: true, + }, + ]; + + for t in test_cases { + let mut parser = Parser::new(t.input); + + let got = parser.parse_order_by_item(Default::default()); + + assert_eq!( + got.is_err(), + t.want_error, + "{}: want_error: {}, error: {:?}", + t.name, + t.want_error, + got.err() + ); + + if let Ok(statements) = got { + assert_eq!(statements, t.expected, "TC: {}", t.name); + } + } +}