Assert function added
Summary: `assert` function added. Useful to us for asserting DB state in harness tests. Potentially useful to the client for breaking out of a query as soon as a predicate fails, as opposed to collecting result and checking them client-side. Reviewers: buda, mislav.bradac, teon.banek Reviewed By: teon.banek Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D750
This commit is contained in:
parent
21eba052ae
commit
4975743f35
@ -9,6 +9,7 @@
|
|||||||
* Maps can now be stored as vertex/edge properties.
|
* Maps can now be stored as vertex/edge properties.
|
||||||
* `collect` aggregation now supports Map collection.
|
* `collect` aggregation now supports Map collection.
|
||||||
* Map indexing supported.
|
* Map indexing supported.
|
||||||
|
* `assert` function added.
|
||||||
|
|
||||||
### Bug Fixes and Other Changes
|
### Bug Fixes and Other Changes
|
||||||
|
|
||||||
|
@ -453,6 +453,7 @@ functions.
|
|||||||
`endsWith` | Check if the first argument ends with the second.
|
`endsWith` | Check if the first argument ends with the second.
|
||||||
`contains` | Check if the first argument has an element which is equal to the second argument.
|
`contains` | Check if the first argument has an element which is equal to the second argument.
|
||||||
`all` | Check if all elements of a list satisfy a predicate.<br/>The syntax is: `all(variable IN list WHERE predicate)`.
|
`all` | Check if all elements of a list satisfy a predicate.<br/>The syntax is: `all(variable IN list WHERE predicate)`.
|
||||||
|
`assert` | Raises an exception reported to the client if the given argument is not `true`.
|
||||||
|
|
||||||
#### String Operators
|
#### String Operators
|
||||||
|
|
||||||
|
@ -506,7 +506,24 @@ bool ContainsPredicate(const std::string &s1, const std::string &s2) {
|
|||||||
return s1.find(s2) != std::string::npos;
|
return s1.find(s2) != std::string::npos;
|
||||||
}
|
}
|
||||||
auto Contains = StringMatchOperator<ContainsPredicate>;
|
auto Contains = StringMatchOperator<ContainsPredicate>;
|
||||||
|
|
||||||
|
TypedValue Assert(const std::vector<TypedValue> &args, GraphDbAccessor &) {
|
||||||
|
if (args.size() < 1U || args.size() > 2U) {
|
||||||
|
throw QueryRuntimeException("assert takes one or two arguments");
|
||||||
|
}
|
||||||
|
if (args[0].type() != TypedValue::Type::Bool)
|
||||||
|
throw QueryRuntimeException("first assert argument must be bool");
|
||||||
|
if (args.size() == 2U && args[1].type() != TypedValue::Type::String)
|
||||||
|
throw QueryRuntimeException("second assert argument must be a string");
|
||||||
|
if (!args[0].ValueBool()) {
|
||||||
|
std::string message("assertion failed");
|
||||||
|
if (args.size() == 2U)
|
||||||
|
message += ": " + args[1].ValueString();
|
||||||
|
throw QueryRuntimeException(message);
|
||||||
|
}
|
||||||
|
return args[0];
|
||||||
}
|
}
|
||||||
|
} // annonymous namespace
|
||||||
|
|
||||||
std::function<TypedValue(const std::vector<TypedValue> &, GraphDbAccessor &)>
|
std::function<TypedValue(const std::vector<TypedValue> &, GraphDbAccessor &)>
|
||||||
NameToFunction(const std::string &function_name) {
|
NameToFunction(const std::string &function_name) {
|
||||||
@ -548,6 +565,7 @@ NameToFunction(const std::string &function_name) {
|
|||||||
if (function_name == kStartsWith) return StartsWith;
|
if (function_name == kStartsWith) return StartsWith;
|
||||||
if (function_name == kEndsWith) return EndsWith;
|
if (function_name == kEndsWith) return EndsWith;
|
||||||
if (function_name == kContains) return Contains;
|
if (function_name == kContains) return Contains;
|
||||||
|
if (function_name == "ASSERT") return Assert;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
}
|
} // namespace query
|
||||||
|
@ -20,7 +20,7 @@ namespace utils {
|
|||||||
*
|
*
|
||||||
* @return trimmed string
|
* @return trimmed string
|
||||||
*/
|
*/
|
||||||
inline std::string Trim(const std::string& s) {
|
inline std::string Trim(const std::string &s) {
|
||||||
auto begin = s.begin();
|
auto begin = s.begin();
|
||||||
auto end = s.end();
|
auto end = s.end();
|
||||||
if (begin == end) {
|
if (begin == end) {
|
||||||
@ -58,11 +58,11 @@ inline std::string ToUpperCase(std::string s) {
|
|||||||
/**
|
/**
|
||||||
* Join strings in vector separated by a given separator.
|
* Join strings in vector separated by a given separator.
|
||||||
*/
|
*/
|
||||||
inline std::string Join(const std::vector<std::string>& strings,
|
inline std::string Join(const std::vector<std::string> &strings,
|
||||||
const std::string& separator) {
|
const std::string &separator) {
|
||||||
if (strings.size() == 0U) return "";
|
if (strings.size() == 0U) return "";
|
||||||
int64_t total_size = 0;
|
int64_t total_size = 0;
|
||||||
for (const auto& x : strings) {
|
for (const auto &x : strings) {
|
||||||
total_size += x.size();
|
total_size += x.size();
|
||||||
}
|
}
|
||||||
total_size += separator.size() * (static_cast<int64_t>(strings.size()) - 1);
|
total_size += separator.size() * (static_cast<int64_t>(strings.size()) - 1);
|
||||||
@ -80,8 +80,8 @@ inline std::string Join(const std::vector<std::string>& strings,
|
|||||||
* Replaces all occurences of <match> in <src> with <replacement>.
|
* Replaces all occurences of <match> in <src> with <replacement>.
|
||||||
*/
|
*/
|
||||||
// TODO: This could be implemented much more efficient.
|
// TODO: This could be implemented much more efficient.
|
||||||
inline std::string Replace(std::string src, const std::string& match,
|
inline std::string Replace(std::string src, const std::string &match,
|
||||||
const std::string& replacement) {
|
const std::string &replacement) {
|
||||||
for (size_t pos = src.find(match); pos != std::string::npos;
|
for (size_t pos = src.find(match); pos != std::string::npos;
|
||||||
pos = src.find(match, pos + replacement.size())) {
|
pos = src.find(match, pos + replacement.size())) {
|
||||||
src.erase(pos, match.length()).insert(pos, replacement);
|
src.erase(pos, match.length()).insert(pos, replacement);
|
||||||
@ -113,7 +113,7 @@ inline std::vector<std::string> Split(const std::string &src,
|
|||||||
* Parse double using classic locale, throws BasicException if it wasn't able to
|
* Parse double using classic locale, throws BasicException if it wasn't able to
|
||||||
* parse whole string.
|
* parse whole string.
|
||||||
*/
|
*/
|
||||||
inline double ParseDouble(const std::string& s) {
|
inline double ParseDouble(const std::string &s) {
|
||||||
// stod would be nicer but it uses current locale so we shouldn't use it.
|
// stod would be nicer but it uses current locale so we shouldn't use it.
|
||||||
double t = 0.0;
|
double t = 0.0;
|
||||||
std::istringstream iss(s);
|
std::istringstream iss(s);
|
||||||
@ -124,4 +124,12 @@ inline double ParseDouble(const std::string& s) {
|
|||||||
}
|
}
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given string `s` ends with the given `suffix`.
|
||||||
|
*/
|
||||||
|
inline bool EndsWith(const std::string &s, const std::string &suffix) {
|
||||||
|
return s.size() >= suffix.size() &&
|
||||||
|
s.compare(s.size() - suffix.size(), std::string::npos, suffix) == 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -649,3 +649,44 @@ Feature: Functions
|
|||||||
RETURN all(x IN [1, 2, '3'] WHERE x < 3) AS a
|
RETURN all(x IN [1, 2, '3'] WHERE x < 3) AS a
|
||||||
"""
|
"""
|
||||||
Then an error should be raised
|
Then an error should be raised
|
||||||
|
|
||||||
|
|
||||||
|
Scenario: Assert test fail, no message:
|
||||||
|
Given an empty graph
|
||||||
|
And having executed:
|
||||||
|
"""
|
||||||
|
CREATE ({a: 1})
|
||||||
|
"""
|
||||||
|
When executing query:
|
||||||
|
"""
|
||||||
|
MATCH (n) RETURN assert(n.a = 2) AS res
|
||||||
|
"""
|
||||||
|
Then an error should be raised
|
||||||
|
|
||||||
|
|
||||||
|
Scenario: Assert test fail:
|
||||||
|
Given an empty graph
|
||||||
|
And having executed:
|
||||||
|
"""
|
||||||
|
CREATE ({a: 1, b: "string"})
|
||||||
|
"""
|
||||||
|
When executing query:
|
||||||
|
"""
|
||||||
|
MATCH (n) RETURN assert(n.a = 2, n.b) AS res
|
||||||
|
"""
|
||||||
|
Then an error should be raised
|
||||||
|
|
||||||
|
|
||||||
|
Scenario: Assert test pass:
|
||||||
|
Given an empty graph
|
||||||
|
And having executed:
|
||||||
|
"""
|
||||||
|
CREATE ({a: 1, b: "string"})
|
||||||
|
"""
|
||||||
|
When executing query:
|
||||||
|
"""
|
||||||
|
MATCH (n) RETURN assert(n.a = 1, n.b) AS res
|
||||||
|
"""
|
||||||
|
Then the result should be:
|
||||||
|
| res |
|
||||||
|
| true |
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "query/interpret/awesome_memgraph_functions.hpp"
|
#include "query/interpret/awesome_memgraph_functions.hpp"
|
||||||
#include "query/interpret/eval.hpp"
|
#include "query/interpret/eval.hpp"
|
||||||
#include "query/interpret/frame.hpp"
|
#include "query/interpret/frame.hpp"
|
||||||
|
#include "utils/string.hpp"
|
||||||
|
|
||||||
#include "query_common.hpp"
|
#include "query_common.hpp"
|
||||||
|
|
||||||
@ -1159,4 +1160,25 @@ TEST(ExpressionEvaluator, FunctionAllWhereWrongType) {
|
|||||||
eval.symbol_table[*all->identifier_] = x_sym;
|
eval.symbol_table[*all->identifier_] = x_sym;
|
||||||
EXPECT_THROW(all->Accept(eval.eval), QueryRuntimeException);
|
EXPECT_THROW(all->Accept(eval.eval), QueryRuntimeException);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(ExpressionEvaluator, FunctionAssert) {
|
||||||
|
// Invalid calls.
|
||||||
|
ASSERT_THROW(EvaluateFunction("ASSERT", {}), QueryRuntimeException);
|
||||||
|
ASSERT_THROW(EvaluateFunction("ASSERT", {false, false}), QueryRuntimeException);
|
||||||
|
ASSERT_THROW(EvaluateFunction("ASSERT", {"string", false}), QueryRuntimeException);
|
||||||
|
ASSERT_THROW(EvaluateFunction("ASSERT", {false, "reason", true}), QueryRuntimeException);
|
||||||
|
|
||||||
|
// Valid calls, assertion fails.
|
||||||
|
ASSERT_THROW(EvaluateFunction("ASSERT", {false}), QueryRuntimeException);
|
||||||
|
ASSERT_THROW(EvaluateFunction("ASSERT", {false, "message"}), QueryRuntimeException);
|
||||||
|
try {
|
||||||
|
EvaluateFunction("ASSERT", {false, "bbgba"});
|
||||||
|
} catch (QueryRuntimeException &e) {
|
||||||
|
ASSERT_TRUE(utils::EndsWith(e.what(), "bbgba"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid calls, assertion passes.
|
||||||
|
ASSERT_TRUE(EvaluateFunction("ASSERT", {true}).ValueBool());
|
||||||
|
ASSERT_TRUE(EvaluateFunction("ASSERT", {true, "message"}).ValueBool());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user