From 054c127175bdceb7322165c2bf0374741574cd34 Mon Sep 17 00:00:00 2001
From: Marin Tomic <marin.tomic@memgraph.io>
Date: Fri, 15 Jun 2018 10:40:51 +0200
Subject: [PATCH] Implement toString function

Summary: https://neo4j.com/docs/developer-manual/3.4/cypher/functions/string/#functions-tostring

Reviewers: teon.banek, buda

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D1430
---
 .../interpret/awesome_memgraph_functions.cpp  | 24 ++++++++++
 tests/unit/query_expression_evaluator.cpp     | 44 +++++++++++++++++++
 2 files changed, 68 insertions(+)

diff --git a/src/query/interpret/awesome_memgraph_functions.cpp b/src/query/interpret/awesome_memgraph_functions.cpp
index 53cf70c15..a9fb16341 100644
--- a/src/query/interpret/awesome_memgraph_functions.cpp
+++ b/src/query/interpret/awesome_memgraph_functions.cpp
@@ -655,6 +655,29 @@ TypedValue Id(const std::vector<TypedValue> &args,
   }
 }
 
+TypedValue ToString(const std::vector<TypedValue> &args,
+                    database::GraphDbAccessor &) {
+  if (args.size() != 1U) {
+    throw QueryRuntimeException("toString takes one argument");
+  }
+  auto &arg = args[0];
+  switch (arg.type()) {
+    case TypedValue::Type::Null:
+      return TypedValue::Null;
+    case TypedValue::Type::String:
+      return arg;
+    case TypedValue::Type::Int:
+      return std::to_string(arg.ValueInt());
+    case TypedValue::Type::Double:
+      return std::to_string(arg.ValueDouble());
+    case TypedValue::Type::Bool:
+      return arg.ValueBool() ? "true" : "false";
+    default:
+      throw QueryRuntimeException(
+          "toString argument must be a number, string or boolean");
+  }
+}
+
 }  // namespace
 
 std::function<TypedValue(const std::vector<TypedValue> &,
@@ -706,6 +729,7 @@ NameToFunction(const std::string &function_name) {
   if (function_name == "INDEXINFO") return IndexInfo;
   if (function_name == "WORKERID") return WorkerId;
   if (function_name == "ID") return Id;
+  if (function_name == "TOSTRING") return ToString;
   return nullptr;
 }
 }  // namespace query
diff --git a/tests/unit/query_expression_evaluator.cpp b/tests/unit/query_expression_evaluator.cpp
index 0cd4b59bb..c319f6e2e 100644
--- a/tests/unit/query_expression_evaluator.cpp
+++ b/tests/unit/query_expression_evaluator.cpp
@@ -1424,4 +1424,48 @@ TEST(ExpressionEvaluator, FunctionWorkerIdSingleNode) {
   EXPECT_EQ(EvaluateFunction("WORKERID", {va}, db).Value<int64_t>(),
             db.WorkerId());
 }
+
+TEST(ExpressionEvaluator, FunctionToStringNull) {
+  database::SingleNode db;
+  EXPECT_TRUE(EvaluateFunction("TOSTRING", {TypedValue::Null}, db).IsNull());
+}
+
+TEST(ExpressionEvaluator, FunctionToStringString) {
+  database::SingleNode db;
+  EXPECT_EQ(EvaluateFunction("TOSTRING", {""}, db).ValueString(), "");
+  EXPECT_EQ(
+      EvaluateFunction("TOSTRING", {"this is a string"}, db).ValueString(),
+      "this is a string");
+}
+
+TEST(ExpressionEvaluator, FunctionToStringInteger) {
+  database::SingleNode db;
+  EXPECT_EQ(EvaluateFunction("TOSTRING", {-23321312}, db).ValueString(),
+            "-23321312");
+  EXPECT_EQ(EvaluateFunction("TOSTRING", {0}, db).ValueString(), "0");
+  EXPECT_EQ(EvaluateFunction("TOSTRING", {42}, db).ValueString(), "42");
+}
+
+TEST(ExpressionEvaluator, FunctionToStringDouble) {
+  database::SingleNode db;
+  EXPECT_EQ(EvaluateFunction("TOSTRING", {-42.42}, db).ValueString(),
+            "-42.420000");
+  EXPECT_EQ(EvaluateFunction("TOSTRING", {0.0}, db).ValueString(), "0.000000");
+  EXPECT_EQ(EvaluateFunction("TOSTRING", {238910.2313217}, db).ValueString(),
+            "238910.231322");
+}
+
+TEST(ExpressionEvaluator, FunctionToStringBool) {
+  database::SingleNode db;
+  EXPECT_EQ(EvaluateFunction("TOSTRING", {true}, db).ValueString(), "true");
+  EXPECT_EQ(EvaluateFunction("TOSTRING", {false}, db).ValueString(), "false");
+}
+
+TEST(ExpressionEvaluator, FunctionToStringExceptions) {
+  database::SingleNode db;
+  EXPECT_THROW(EvaluateFunction("TOSTRING", {1, 2, 3}, db),
+               QueryRuntimeException);
+  std::vector<TypedValue> l{1, 2, 3};
+  EXPECT_THROW(EvaluateFunction("TOSTRING", l, db), QueryRuntimeException);
+}
 }  // namespace