Use a custom value printer for procedure signature

Reviewers: mferencevic

Reviewed By: mferencevic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2557
This commit is contained in:
Teon Banek 2019-11-21 10:47:11 +01:00
parent 5354a11a00
commit 019b6cddaa
4 changed files with 74 additions and 14 deletions

View File

@ -723,8 +723,9 @@ int mgp_proc_add_arg(struct mgp_proc *proc, const char *name,
/// `default_value` is copied and set as the default value for the argument.
/// Don't forget to call mgp_value_destroy when you are done using
/// `default_value`. When the procedure is called, if this argument is not
/// provided, `default_value` will be used instead. `default_value` must satisfy
/// the given `type`.
/// provided, `default_value` will be used instead. `default_value` must not be
/// a graph element (node, relationship, path) and it must satisfy the given
/// `type`.
///
/// 0 is returned if unable to allocate memory for an argument; if `name` is
/// not valid or `default_value` does not satisfy `type`. Non-zero is returned

View File

@ -10,6 +10,7 @@
#include "utils/algorithm.hpp"
#include "utils/math.hpp"
#include "utils/string.hpp"
// This file contains implementation of top level C API functions, but this is
// all actually part of query::procedure. So use that namespace for simplicity.
@ -1376,6 +1377,21 @@ int mgp_proc_add_opt_arg(mgp_proc *proc, const char *name, const mgp_type *type,
const mgp_value *default_value) {
if (!proc || !type || !default_value) return 0;
if (!IsValidIdentifierName(name)) return 0;
switch (mgp_value_get_type(default_value)) {
case MGP_VALUE_TYPE_VERTEX:
case MGP_VALUE_TYPE_EDGE:
case MGP_VALUE_TYPE_PATH:
// default_value must not be a graph element.
return 0;
case MGP_VALUE_TYPE_NULL:
case MGP_VALUE_TYPE_BOOL:
case MGP_VALUE_TYPE_INT:
case MGP_VALUE_TYPE_DOUBLE:
case MGP_VALUE_TYPE_STRING:
case MGP_VALUE_TYPE_LIST:
case MGP_VALUE_TYPE_MAP:
break;
}
// TODO: Check `default_value` satisfies `type`.
auto *memory = proc->opt_args.get_allocator().GetMemoryResource();
try {
@ -1419,6 +1435,47 @@ int mgp_proc_add_deprecated_result(mgp_proc *proc, const char *name,
namespace query::procedure {
namespace {
// Print the value in user presentable fashion.
// @throw std::bad_alloc
// @throw std::length_error
std::ostream &PrintValue(const TypedValue &value, std::ostream *stream) {
switch (value.type()) {
case TypedValue::Type::Null:
return (*stream) << "Null";
case TypedValue::Type::Bool:
return (*stream) << (value.ValueBool() ? "true" : "false");
case TypedValue::Type::Int:
return (*stream) << value.ValueInt();
case TypedValue::Type::Double:
return (*stream) << value.ValueDouble();
case TypedValue::Type::String:
// String value should be escaped, this allocates a new string.
return (*stream) << utils::Escape(value.ValueString());
case TypedValue::Type::List:
(*stream) << "[";
utils::PrintIterable(
*stream, value.ValueList(), ", ",
[](auto &stream, const auto &elem) { PrintValue(elem, &stream); });
return (*stream) << "]";
case TypedValue::Type::Map:
(*stream) << "{";
utils::PrintIterable(*stream, value.ValueMap(), ", ",
[](auto &stream, const auto &item) {
// Map keys are not escaped strings.
stream << item.first << ": ";
PrintValue(item.second, &stream);
});
return (*stream) << "}";
case TypedValue::Type::Vertex:
case TypedValue::Type::Edge:
case TypedValue::Type::Path:
LOG(FATAL) << "value must not be a graph element";
}
}
} // namespace
void PrintProcSignature(const mgp_proc &proc, std::ostream *stream) {
(*stream) << proc.name << "(";
utils::PrintIterable(
@ -1428,8 +1485,9 @@ void PrintProcSignature(const mgp_proc &proc, std::ostream *stream) {
if (!proc.opt_args.empty()) (*stream) << ", ";
utils::PrintIterable(
*stream, proc.opt_args, ", ", [](auto &stream, const auto &arg) {
stream << std::get<0>(arg) << " = " << std::get<2>(arg)
<< " :: " << std::get<1>(arg)->GetPresentableName();
stream << std::get<0>(arg) << " = ";
PrintValue(std::get<2>(arg), &stream)
<< " :: " << std::get<1>(arg)->GetPresentableName();
});
(*stream) << ") :: (";
utils::PrintIterable(

View File

@ -543,6 +543,8 @@ struct mgp_module {
namespace query::procedure {
/// @throw std::bad_alloc
/// @throw std::length_error
/// @throw anything std::ostream::operator<< may throw.
void PrintProcSignature(const mgp_proc &, std::ostream *);

View File

@ -77,17 +77,16 @@ TEST(Module, ProcedureSignature) {
CheckSignature(proc,
"proc(arg1 :: NUMBER, opt1 = Null :: ANY?) :: "
"(res1 :: LIST OF INTEGER)");
mgp_proc_add_opt_arg(proc, "opt2", mgp_type_int(),
mgp_value_make_int(42, &memory));
CheckSignature(
proc,
"proc(arg1 :: NUMBER, opt1 = Null :: ANY?, opt2 = 42 :: INTEGER) :: "
"(res1 :: LIST OF INTEGER)");
mgp_proc_add_deprecated_result(proc, "res2", mgp_type_string());
CheckSignature(
proc,
"proc(arg1 :: NUMBER, opt1 = Null :: ANY?, opt2 = 42 :: INTEGER) :: "
"(res1 :: LIST OF INTEGER, DEPRECATED res2 :: STRING)");
CheckSignature(proc,
"proc(arg1 :: NUMBER, opt1 = Null :: ANY?) :: "
"(res1 :: LIST OF INTEGER, DEPRECATED res2 :: STRING)");
EXPECT_FALSE(mgp_proc_add_result(proc, "res2", mgp_type_any()));
EXPECT_FALSE(mgp_proc_add_deprecated_result(proc, "res1", mgp_type_any()));
mgp_proc_add_opt_arg(proc, "opt2", mgp_type_string(),
mgp_value_make_string("string=\"value\"", &memory));
CheckSignature(proc,
"proc(arg1 :: NUMBER, opt1 = Null :: ANY?, "
"opt2 = \"string=\\\"value\\\"\" :: STRING) :: "
"(res1 :: LIST OF INTEGER, DEPRECATED res2 :: STRING)");
}