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:
florijan 2017-09-05 11:26:46 +02:00
parent 21eba052ae
commit 4975743f35
6 changed files with 99 additions and 8 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}
} }

View File

@ -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 |

View File

@ -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());
}
} }