From 2ed1e52aa59ac2c0c3a6c34b9d361ffba7376b66 Mon Sep 17 00:00:00 2001
From: Kaitmazian Maksim <m.kaitmazian@picodata.io>
Date: Mon, 8 Apr 2024 18:31:40 +0300
Subject: [PATCH] add tests and fix explain format

---
 sbroad-core/src/executor/engine/mock.rs       |  3 +
 sbroad-core/src/frontend/sql/ir/tests.rs      |  2 +
 sbroad-core/src/frontend/sql/ir/tests/trim.rs | 60 +++++++++++++++++++
 sbroad-core/src/ir/explain.rs                 | 10 +++-
 sbroad-core/src/ir/expression.rs              |  6 +-
 5 files changed, 77 insertions(+), 4 deletions(-)
 create mode 100644 sbroad-core/src/frontend/sql/ir/tests/trim.rs

diff --git a/sbroad-core/src/executor/engine/mock.rs b/sbroad-core/src/executor/engine/mock.rs
index 0a3099596..a683ceacb 100644
--- a/sbroad-core/src/executor/engine/mock.rs
+++ b/sbroad-core/src/executor/engine/mock.rs
@@ -101,8 +101,11 @@ impl RouterConfigurationMock {
     pub fn new() -> Self {
         let name_func = normalize_name_from_sql("func");
         let fn_func = Function::new_stable(name_func.clone(), Type::Integer);
+        let name_trim = normalize_name_from_sql("trim");
+        let trim_func = Function::new_stable(name_trim.clone(), Type::String);
         let mut functions = HashMap::new();
         functions.insert(name_func, fn_func);
+        functions.insert(name_trim, trim_func);
 
         let mut tables = HashMap::new();
 
diff --git a/sbroad-core/src/frontend/sql/ir/tests.rs b/sbroad-core/src/frontend/sql/ir/tests.rs
index c1dca032a..5da91e9c8 100644
--- a/sbroad-core/src/frontend/sql/ir/tests.rs
+++ b/sbroad-core/src/frontend/sql/ir/tests.rs
@@ -3245,4 +3245,6 @@ mod params;
 #[cfg(test)]
 mod single;
 #[cfg(test)]
+mod trim;
+#[cfg(test)]
 mod update;
diff --git a/sbroad-core/src/frontend/sql/ir/tests/trim.rs b/sbroad-core/src/frontend/sql/ir/tests/trim.rs
new file mode 100644
index 000000000..d5df6085d
--- /dev/null
+++ b/sbroad-core/src/frontend/sql/ir/tests/trim.rs
@@ -0,0 +1,60 @@
+use crate::ir::transformation::helpers::sql_to_optimized_ir;
+use pretty_assertions::assert_eq;
+
+#[test]
+fn trim() {
+    let sql = r#"SELECT TRIM("FIRST_NAME") FROM "test_space""#;
+    let plan = sql_to_optimized_ir(sql, vec![]);
+
+    let expected_explain = String::from(
+        r#"projection ("TRIM"(("test_space"."FIRST_NAME"::string))::string -> "COL_1")
+    scan "test_space"
+execution options:
+sql_vdbe_max_steps = 45000
+vtable_max_rows = 5000
+"#,
+    );
+
+    assert_eq!(expected_explain, plan.as_explain().unwrap());
+}
+
+#[test]
+fn trim_leading_from() {
+    let sql = r#"SELECT TRIM(LEADING FROM "FIRST_NAME") FROM "test_space""#;
+    let plan = sql_to_optimized_ir(sql, vec![]);
+
+    let expected_explain = String::from(
+        r#"projection ("TRIM"(leading  from "test_space"."FIRST_NAME"::string)::string -> "COL_1")
+    scan "test_space"
+execution options:
+sql_vdbe_max_steps = 45000
+vtable_max_rows = 5000
+"#,
+    );
+
+    assert_eq!(expected_explain, plan.as_explain().unwrap());
+}
+
+#[test]
+fn trim_both_space_from() {
+    let sql = r#"SELECT TRIM(BOTH ' ' FROM "FIRST_NAME") FROM "test_space""#;
+    let plan = sql_to_optimized_ir(sql, vec![]);
+
+    let expected_explain = String::from(
+        r#"projection ("TRIM"(both ' '::string from "test_space"."FIRST_NAME"::string)::string -> "COL_1")
+    scan "test_space"
+execution options:
+sql_vdbe_max_steps = 45000
+vtable_max_rows = 5000
+"#,
+    );
+
+    assert_eq!(expected_explain, plan.as_explain().unwrap());
+}
+
+#[test]
+#[should_panic]
+fn trim_trailing_without_from_should_fail() {
+    let sql = r#"SELECT TRIM(TRAILING "FIRST_NAME") FROM "test_space""#;
+    let plan = sql_to_optimized_ir(sql, vec![]);
+}
diff --git a/sbroad-core/src/ir/explain.rs b/sbroad-core/src/ir/explain.rs
index 9064f8d6b..5ce3ab692 100644
--- a/sbroad-core/src/ir/explain.rs
+++ b/sbroad-core/src/ir/explain.rs
@@ -59,7 +59,15 @@ impl Display for ColExpr {
             ColExpr::StableFunction(name, args, feature, func_type) => {
                 let is_distinct = matches!(feature, Some(FunctionFeature::Distinct));
                 let formatted_args = if let Some(FunctionFeature::Trim(kind)) = feature {
-                    format!("{} {}", kind.as_str(), args.iter().format(" "))
+                    let (string, removal_chars) = args
+                        .split_last()
+                        .expect("string is required by the grammar");
+                    format!(
+                        "{} {} from {}",
+                        kind.as_str(),
+                        removal_chars.iter().format(""),
+                        string
+                    )
                 } else {
                     format!("({})", args.iter().format(", "))
                 };
diff --git a/sbroad-core/src/ir/expression.rs b/sbroad-core/src/ir/expression.rs
index 6fa8cb7ad..b114ec327 100644
--- a/sbroad-core/src/ir/expression.rs
+++ b/sbroad-core/src/ir/expression.rs
@@ -186,9 +186,9 @@ impl TrimKind {
     #[must_use]
     pub fn as_str(&self) -> &'static str {
         match self {
-            TrimKind::Leading => "LEADING",
-            TrimKind::Trailing => "TRAILING",
-            TrimKind::Both => "BOTH",
+            TrimKind::Leading => "leading",
+            TrimKind::Trailing => "trailing",
+            TrimKind::Both => "both",
         }
     }
 }
-- 
GitLab