From af8a7b73f514ded9fde0ea629c7cf3c43553da1e Mon Sep 17 00:00:00 2001
From: Denis Smirnov <sd@picodata.io>
Date: Mon, 6 Mar 2023 14:50:42 +0700
Subject: [PATCH] fix: explicitly cast typle types when populate virtual tables

---
 .../test/integration/groupby_test.lua         |   7 +-
 sbroad-core/src/backend/sql/space.rs          |  13 +-
 sbroad-core/src/ir/value.rs                   | 137 ++++++++++++++++++
 3 files changed, 147 insertions(+), 10 deletions(-)

diff --git a/sbroad-cartridge/test_app/test/integration/groupby_test.lua b/sbroad-cartridge/test_app/test/integration/groupby_test.lua
index a2c6d25ce6..de2383d250 100644
--- a/sbroad-cartridge/test_app/test/integration/groupby_test.lua
+++ b/sbroad-cartridge/test_app/test/integration/groupby_test.lua
@@ -798,13 +798,12 @@ groupby_queries.test_count_works = function()
     })
 end
 
--- todo: this test fails if we remove the devision by 8, see issue 351
 groupby_queries.test_count = function()
     local api = cluster:server("api-1").net_box
     local r, err = api:call("sbroad.execute", {
         [[
         select cs, count("d") from (
-            SELECT "d", cast(count("e") + count("e" + "d") as double) / 8 as cs from "arithmetic_space"
+            SELECT "d", count("e") + count("e" + "d") as cs from "arithmetic_space"
             group by "d"
         ) as t
         where t."d" > 1
@@ -814,12 +813,12 @@ groupby_queries.test_count = function()
 
     t.assert_equals(err, nil)
     t.assert_equals(r.metadata, {
-        { name = "CS", type = "double" },
+        { name = "CS", type = "decimal" },
         { name = "COL_1" , type = "decimal" }
     })
 
     t.assert_items_equals(r.rows, {
-        {0.5, 1}
+        {4, 1}
     })
 end
 
diff --git a/sbroad-core/src/backend/sql/space.rs b/sbroad-core/src/backend/sql/space.rs
index 6beca9f5e6..a41e6fea00 100644
--- a/sbroad-core/src/backend/sql/space.rs
+++ b/sbroad-core/src/backend/sql/space.rs
@@ -7,6 +7,7 @@ use crate::ir::relation::SpaceEngine;
 mod prod_imports {
     pub use crate::error;
     pub use crate::errors::{Action, Entity};
+    pub use crate::ir::relation::Column;
     pub use crate::ir::value::EncodedValue;
     pub use tarantool::index::{FieldType, IndexOptions, IndexType, Part};
     pub use tarantool::space::{Field, Space, SpaceCreateOptions};
@@ -96,12 +97,12 @@ impl TmpSpace {
                 }
             }
             for (idx, values) in vtable.get_tuples_with_buckets(buckets).iter().enumerate() {
-                let extended_value = values
-                    .iter()
-                    .cloned()
-                    .map(EncodedValue::from)
-                    .chain(std::iter::once(EncodedValue::Unsigned(idx as u64)))
-                    .collect::<Vec<EncodedValue>>();
+                let mut extended_value: Vec<EncodedValue> = Vec::with_capacity(values.len() + 1);
+                for (v, c) in values.iter().zip(vtable.get_columns().iter()) {
+                    let casted_value = v.cast(&c.r#type)?;
+                    extended_value.push(EncodedValue::from(casted_value));
+                }
+                extended_value.push(EncodedValue::Unsigned(idx as u64));
                 let data = match rmp_serde::to_vec(&extended_value) {
                     Ok(data) => data,
                     Err(e) => {
diff --git a/sbroad-core/src/ir/value.rs b/sbroad-core/src/ir/value.rs
index 2389397b5b..0b8cc17643 100644
--- a/sbroad-core/src/ir/value.rs
+++ b/sbroad-core/src/ir/value.rs
@@ -10,7 +10,9 @@ use tarantool::decimal::Decimal;
 use tarantool::tlua::{self, LuaRead};
 
 use crate::error;
+use crate::errors::{Action, Entity, SbroadError};
 use crate::executor::hash::ToHashString;
+use crate::ir::relation::Type;
 use crate::ir::value::double::Double;
 
 #[derive(Debug, Serialize, Deserialize, Hash, PartialEq, Eq, Clone)]
@@ -314,6 +316,141 @@ impl Value {
             },
         }
     }
+
+    pub fn cast(&self, column_type: &Type) -> Result<Value, SbroadError> {
+        match column_type {
+            Type::Array => match self {
+                Value::Null => Ok(Value::Null),
+                _ => Err(SbroadError::FailedTo(
+                    Action::Serialize,
+                    Some(Entity::Value),
+                    format!("{self:?} into array"),
+                )),
+            },
+            Type::Boolean => match self {
+                Value::Boolean(_) => Ok(self.clone()),
+                Value::Null => Ok(Value::Null),
+                _ => Err(SbroadError::FailedTo(
+                    Action::Serialize,
+                    Some(Entity::Value),
+                    format!("{self:?} into boolean"),
+                )),
+            },
+            Type::Decimal => match self {
+                Value::Decimal(_) => Ok(self.clone()),
+                Value::Double(v) => Ok(Value::Decimal(
+                    Decimal::from_str(&format!("{v}")).map_err(|e| {
+                        SbroadError::FailedTo(
+                            Action::Serialize,
+                            Some(Entity::Value),
+                            format!("{e:?}"),
+                        )
+                    })?,
+                )),
+                Value::Integer(v) => Ok(Value::Decimal(Decimal::from(*v))),
+                Value::Unsigned(v) => Ok(Value::Decimal(Decimal::from(*v))),
+                Value::Null => Ok(Value::Null),
+                _ => Err(SbroadError::FailedTo(
+                    Action::Serialize,
+                    Some(Entity::Value),
+                    format!("{self:?} into decimal"),
+                )),
+            },
+            Type::Double => match self {
+                Value::Double(_) => Ok(self.clone()),
+                Value::Decimal(v) => Ok(Value::Double(Double::from_str(&format!("{v}"))?)),
+                Value::Integer(v) => Ok(Value::Double(Double::from(*v))),
+                Value::Unsigned(v) => Ok(Value::Double(Double::from(*v))),
+                Value::Null => Ok(Value::Null),
+                _ => Err(SbroadError::FailedTo(
+                    Action::Serialize,
+                    Some(Entity::Value),
+                    format!("{self:?} into double"),
+                )),
+            },
+            Type::Integer => match self {
+                Value::Integer(_) => Ok(self.clone()),
+                Value::Decimal(v) => Ok(Value::Integer(v.to_i64().ok_or_else(|| {
+                    SbroadError::FailedTo(
+                        Action::Serialize,
+                        Some(Entity::Value),
+                        format!("{self:?} into integer"),
+                    )
+                })?)),
+                Value::Double(v) => v
+                    .to_string()
+                    .parse::<i64>()
+                    .map(Value::Integer)
+                    .map_err(|e| {
+                        SbroadError::FailedTo(Action::Serialize, Some(Entity::Value), e.to_string())
+                    }),
+                Value::Unsigned(v) => Ok(Value::Integer(*v as i64)),
+                Value::Null => Ok(Value::Null),
+                _ => Err(SbroadError::FailedTo(
+                    Action::Serialize,
+                    Some(Entity::Value),
+                    format!("{self:?} into integer"),
+                )),
+            },
+            Type::Scalar => match self {
+                Value::Tuple(_) => Err(SbroadError::FailedTo(
+                    Action::Serialize,
+                    Some(Entity::Value),
+                    format!("{self:?} into scalar"),
+                )),
+                _ => Ok(self.clone()),
+            },
+            Type::String => match self {
+                Value::String(_) => Ok(self.clone()),
+                Value::Null => Ok(Value::Null),
+                _ => Err(SbroadError::FailedTo(
+                    Action::Serialize,
+                    Some(Entity::Value),
+                    format!("{self:?} into string"),
+                )),
+            },
+            Type::Number => match self {
+                Value::Integer(_) | Value::Decimal(_) | Value::Double(_) | Value::Unsigned(_) => {
+                    Ok(self.clone())
+                }
+                Value::Null => Ok(Value::Null),
+                _ => Err(SbroadError::FailedTo(
+                    Action::Serialize,
+                    Some(Entity::Value),
+                    format!("{self:?} into number"),
+                )),
+            },
+            Type::Unsigned => match self {
+                Value::Unsigned(_) => Ok(self.clone()),
+                Value::Integer(v) => Ok(Value::Unsigned(*v as u64)),
+                Value::Decimal(v) => Ok(Value::Unsigned(v.to_u64().ok_or_else(|| {
+                    SbroadError::FailedTo(
+                        Action::Serialize,
+                        Some(Entity::Value),
+                        format!("{self:?} into unsigned"),
+                    )
+                })?)),
+                Value::Double(v) => {
+                    v.to_string()
+                        .parse::<u64>()
+                        .map(Value::Unsigned)
+                        .map_err(|_| {
+                            SbroadError::FailedTo(
+                                Action::Serialize,
+                                Some(Entity::Value),
+                                format!("{self:?} into unsigned"),
+                            )
+                        })
+                }
+                Value::Null => Ok(Value::Null),
+                _ => Err(SbroadError::FailedTo(
+                    Action::Serialize,
+                    Some(Entity::Value),
+                    format!("{self:?} into unsigned"),
+                )),
+            },
+        }
+    }
 }
 
 impl ToHashString for Value {
-- 
GitLab