diff --git a/src/distributed/produce_rpc_server.cpp b/src/distributed/produce_rpc_server.cpp index 642ed1d4f..915ba8899 100644 --- a/src/distributed/produce_rpc_server.cpp +++ b/src/distributed/produce_rpc_server.cpp @@ -21,7 +21,7 @@ ProduceRpcServer::OngoingProduce::OngoingProduce( cursor_(op->MakeCursor(*dba_)) { context_.symbol_table_ = std::move(symbol_table); context_.parameters_ = std::move(parameters); - context_.timestamp_ = timestamp; + context_.evaluation_context_.timestamp = timestamp; } std::pair, PullState> diff --git a/src/query/context.hpp b/src/query/context.hpp index f1bc943c5..69c386c9f 100644 --- a/src/query/context.hpp +++ b/src/query/context.hpp @@ -15,6 +15,10 @@ class Streams; namespace query { +struct EvaluationContext { + int64_t timestamp{-1}; +}; + class Context { public: // Since we also return some information from context (is_index_created_) we @@ -27,11 +31,11 @@ class Context { explicit Context(database::GraphDbAccessor &db_accessor) : db_accessor_(db_accessor) {} database::GraphDbAccessor &db_accessor_; - SymbolTable symbol_table_; - Parameters parameters_; bool in_explicit_transaction_ = false; bool is_index_created_ = false; - int64_t timestamp_{-1}; + SymbolTable symbol_table_; + Parameters parameters_; + EvaluationContext evaluation_context_; auth::Auth *auth_ = nullptr; integrations::kafka::Streams *kafka_streams_ = nullptr; diff --git a/src/query/frontend/ast/ast.hpp b/src/query/frontend/ast/ast.hpp index f39d92c7a..fdc8b7f61 100644 --- a/src/query/frontend/ast/ast.hpp +++ b/src/query/frontend/ast/ast.hpp @@ -1013,7 +1013,9 @@ class Function : public Expression { private: std::string function_name_; - std::function function_; + std::function + function_; }; class Aggregation : public BinaryOperator { diff --git a/src/query/interpret/awesome_memgraph_functions.cpp b/src/query/interpret/awesome_memgraph_functions.cpp index 55590cc91..fe685bd9e 100644 --- a/src/query/interpret/awesome_memgraph_functions.cpp +++ b/src/query/interpret/awesome_memgraph_functions.cpp @@ -32,7 +32,8 @@ namespace { // TODO: Implement degrees, haversin, radians // TODO: Implement spatial functions -TypedValue Coalesce(TypedValue *args, int64_t nargs, Context *) { +TypedValue Coalesce(TypedValue *args, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *) { // TODO: Perhaps this function should be done by the evaluator itself, so as // to avoid evaluating all the arguments. if (nargs == 0) { @@ -46,7 +47,8 @@ TypedValue Coalesce(TypedValue *args, int64_t nargs, Context *) { return TypedValue::Null; } -TypedValue EndNode(TypedValue *args, int64_t nargs, Context *) { +TypedValue EndNode(TypedValue *args, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *) { if (nargs != 1) { throw QueryRuntimeException("'endNode' requires exactly one argument."); } @@ -60,7 +62,8 @@ TypedValue EndNode(TypedValue *args, int64_t nargs, Context *) { } } -TypedValue Head(TypedValue *args, int64_t nargs, Context *) { +TypedValue Head(TypedValue *args, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *) { if (nargs != 1) { throw QueryRuntimeException("'head' requires exactly one argument."); } @@ -77,7 +80,8 @@ TypedValue Head(TypedValue *args, int64_t nargs, Context *) { } } -TypedValue Last(TypedValue *args, int64_t nargs, Context *) { +TypedValue Last(TypedValue *args, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *) { if (nargs != 1) { throw QueryRuntimeException("'last' requires exactly one argument."); } @@ -94,15 +98,16 @@ TypedValue Last(TypedValue *args, int64_t nargs, Context *) { } } -TypedValue Properties(TypedValue *args, int64_t nargs, Context *ctx) { +TypedValue Properties(TypedValue *args, int64_t nargs, + const EvaluationContext &, + database::GraphDbAccessor *dba) { if (nargs != 1) { throw QueryRuntimeException("'properties' requires exactly one argument."); } auto get_properties = [&](const auto &record_accessor) { std::map properties; for (const auto &property : record_accessor.Properties()) { - properties[ctx->db_accessor_.PropertyName(property.first)] = - property.second; + properties[dba->PropertyName(property.first)] = property.second; } return properties; }; @@ -119,7 +124,8 @@ TypedValue Properties(TypedValue *args, int64_t nargs, Context *ctx) { } } -TypedValue Size(TypedValue *args, int64_t nargs, Context *) { +TypedValue Size(TypedValue *args, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *) { if (nargs != 1) { throw QueryRuntimeException("'size' requires exactly one argument."); } @@ -144,7 +150,8 @@ TypedValue Size(TypedValue *args, int64_t nargs, Context *) { } } -TypedValue StartNode(TypedValue *args, int64_t nargs, Context *) { +TypedValue StartNode(TypedValue *args, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *) { if (nargs != 1) { throw QueryRuntimeException("'startNode' requires exactly one argument."); } @@ -158,7 +165,8 @@ TypedValue StartNode(TypedValue *args, int64_t nargs, Context *) { } } -TypedValue Degree(TypedValue *args, int64_t nargs, Context *) { +TypedValue Degree(TypedValue *args, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *) { if (nargs != 1) { throw QueryRuntimeException("'degree' requires exactly one argument."); } @@ -174,7 +182,8 @@ TypedValue Degree(TypedValue *args, int64_t nargs, Context *) { } } -TypedValue ToBoolean(TypedValue *args, int64_t nargs, Context *) { +TypedValue ToBoolean(TypedValue *args, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *) { if (nargs != 1) { throw QueryRuntimeException("'toBoolean' requires exactly one argument."); } @@ -199,7 +208,8 @@ TypedValue ToBoolean(TypedValue *args, int64_t nargs, Context *) { } } -TypedValue ToFloat(TypedValue *args, int64_t nargs, Context *) { +TypedValue ToFloat(TypedValue *args, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *) { if (nargs != 1) { throw QueryRuntimeException("'toFloat' requires exactly one argument."); } @@ -222,7 +232,8 @@ TypedValue ToFloat(TypedValue *args, int64_t nargs, Context *) { } } -TypedValue ToInteger(TypedValue *args, int64_t nargs, Context *) { +TypedValue ToInteger(TypedValue *args, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *) { if (nargs != 1) { throw QueryRuntimeException("'toInteger' requires exactly one argument'"); } @@ -250,7 +261,8 @@ TypedValue ToInteger(TypedValue *args, int64_t nargs, Context *) { } } -TypedValue Type(TypedValue *args, int64_t nargs, Context *ctx) { +TypedValue Type(TypedValue *args, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *dba) { if (nargs != 1) { throw QueryRuntimeException("'type' requires exactly one argument."); } @@ -258,21 +270,21 @@ TypedValue Type(TypedValue *args, int64_t nargs, Context *ctx) { case TypedValue::Type::Null: return TypedValue::Null; case TypedValue::Type::Edge: - return ctx->db_accessor_.EdgeTypeName( - args[0].Value().EdgeType()); + return dba->EdgeTypeName(args[0].Value().EdgeType()); default: throw QueryRuntimeException("'type' argument must be an edge."); } } -TypedValue Keys(TypedValue *args, int64_t nargs, Context *ctx) { +TypedValue Keys(TypedValue *args, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *dba) { if (nargs != 1) { throw QueryRuntimeException("'keys' requires exactly one argument."); } auto get_keys = [&](const auto &record_accessor) { std::vector keys; for (const auto &property : record_accessor.Properties()) { - keys.push_back(ctx->db_accessor_.PropertyName(property.first)); + keys.push_back(dba->PropertyName(property.first)); } return keys; }; @@ -288,7 +300,8 @@ TypedValue Keys(TypedValue *args, int64_t nargs, Context *ctx) { } } -TypedValue Labels(TypedValue *args, int64_t nargs, Context *ctx) { +TypedValue Labels(TypedValue *args, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *dba) { if (nargs != 1) { throw QueryRuntimeException("'labels' requires exactly one argument."); } @@ -298,7 +311,7 @@ TypedValue Labels(TypedValue *args, int64_t nargs, Context *ctx) { case TypedValue::Type::Vertex: { std::vector labels; for (const auto &label : args[0].Value().labels()) { - labels.push_back(ctx->db_accessor_.LabelName(label)); + labels.push_back(dba->LabelName(label)); } return labels; } @@ -307,7 +320,8 @@ TypedValue Labels(TypedValue *args, int64_t nargs, Context *ctx) { } } -TypedValue Nodes(TypedValue *args, int64_t nargs, Context *) { +TypedValue Nodes(TypedValue *args, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *) { if (nargs != 1) { throw QueryRuntimeException("'nodes' requires exactly one argument."); } @@ -319,7 +333,9 @@ TypedValue Nodes(TypedValue *args, int64_t nargs, Context *) { return std::vector(vertices.begin(), vertices.end()); } -TypedValue Relationships(TypedValue *args, int64_t nargs, Context *) { +TypedValue Relationships(TypedValue *args, int64_t nargs, + const EvaluationContext &, + database::GraphDbAccessor *) { if (nargs != 1) { throw QueryRuntimeException( "'relationships' requires exactly one argument."); @@ -332,7 +348,8 @@ TypedValue Relationships(TypedValue *args, int64_t nargs, Context *) { return std::vector(edges.begin(), edges.end()); } -TypedValue Range(TypedValue *args, int64_t nargs, Context *) { +TypedValue Range(TypedValue *args, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *) { if (nargs != 2 && nargs != 3) { throw QueryRuntimeException("'range' requires two or three arguments."); } @@ -365,7 +382,8 @@ TypedValue Range(TypedValue *args, int64_t nargs, Context *) { return list; } -TypedValue Tail(TypedValue *args, int64_t nargs, Context *) { +TypedValue Tail(TypedValue *args, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *) { if (nargs != 1) { throw QueryRuntimeException("'tail' requires exactly one argument."); } @@ -383,7 +401,8 @@ TypedValue Tail(TypedValue *args, int64_t nargs, Context *) { } } -TypedValue Abs(TypedValue *args, int64_t nargs, Context *) { +TypedValue Abs(TypedValue *args, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *) { if (nargs != 1) { throw QueryRuntimeException("'abs' requires exactly one argument."); } @@ -400,23 +419,24 @@ TypedValue Abs(TypedValue *args, int64_t nargs, Context *) { } } -#define WRAP_CMATH_FLOAT_FUNCTION(name, lowercased_name) \ - TypedValue name(TypedValue *args, int64_t nargs, Context *) { \ - if (nargs != 1) { \ - throw QueryRuntimeException("'" #lowercased_name \ - "' requires exactly one argument."); \ - } \ - switch (args[0].type()) { \ - case TypedValue::Type::Null: \ - return TypedValue::Null; \ - case TypedValue::Type::Int: \ - return lowercased_name(args[0].Value()); \ - case TypedValue::Type::Double: \ - return lowercased_name(args[0].Value()); \ - default: \ - throw QueryRuntimeException(#lowercased_name \ - " argument must be a number."); \ - } \ +#define WRAP_CMATH_FLOAT_FUNCTION(name, lowercased_name) \ + TypedValue name(TypedValue *args, int64_t nargs, const EvaluationContext &, \ + database::GraphDbAccessor *) { \ + if (nargs != 1) { \ + throw QueryRuntimeException("'" #lowercased_name \ + "' requires exactly one argument."); \ + } \ + switch (args[0].type()) { \ + case TypedValue::Type::Null: \ + return TypedValue::Null; \ + case TypedValue::Type::Int: \ + return lowercased_name(args[0].Value()); \ + case TypedValue::Type::Double: \ + return lowercased_name(args[0].Value()); \ + default: \ + throw QueryRuntimeException(#lowercased_name \ + " argument must be a number."); \ + } \ } WRAP_CMATH_FLOAT_FUNCTION(Ceil, ceil) @@ -437,7 +457,8 @@ WRAP_CMATH_FLOAT_FUNCTION(Tan, tan) #undef WRAP_CMATH_FLOAT_FUNCTION -TypedValue Atan2(TypedValue *args, int64_t nargs, Context *) { +TypedValue Atan2(TypedValue *args, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *) { if (nargs != 2) { throw QueryRuntimeException("'atan2' requires two arguments."); } @@ -458,7 +479,8 @@ TypedValue Atan2(TypedValue *args, int64_t nargs, Context *) { return atan2(y, x); } -TypedValue Sign(TypedValue *args, int64_t nargs, Context *) { +TypedValue Sign(TypedValue *args, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *) { if (nargs != 1) { throw QueryRuntimeException("'sign' requires exactly one argument."); } @@ -475,21 +497,24 @@ TypedValue Sign(TypedValue *args, int64_t nargs, Context *) { } } -TypedValue E(TypedValue *, int64_t nargs, Context *) { +TypedValue E(TypedValue *, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *) { if (nargs != 0) { throw QueryRuntimeException("'e' requires no arguments."); } return M_E; } -TypedValue Pi(TypedValue *, int64_t nargs, Context *) { +TypedValue Pi(TypedValue *, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *) { if (nargs != 0) { throw QueryRuntimeException("'pi' requires no arguments."); } return M_PI; } -TypedValue Rand(TypedValue *, int64_t nargs, Context *) { +TypedValue Rand(TypedValue *, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *) { static thread_local std::mt19937 pseudo_rand_gen_{std::random_device{}()}; static thread_local std::uniform_real_distribution<> rand_dist_{0, 1}; if (nargs != 0) { @@ -499,7 +524,9 @@ TypedValue Rand(TypedValue *, int64_t nargs, Context *) { } template -TypedValue StringMatchOperator(TypedValue *args, int64_t nargs, Context *) { +TypedValue StringMatchOperator(TypedValue *args, int64_t nargs, + const EvaluationContext &, + database::GraphDbAccessor *) { if (nargs != 2) { throw QueryRuntimeException( "'startsWith' and 'endsWith' require two arguments."); @@ -542,7 +569,8 @@ bool ContainsPredicate(const std::string &s1, const std::string &s2) { } auto Contains = StringMatchOperator; -TypedValue Assert(TypedValue *args, int64_t nargs, Context *) { +TypedValue Assert(TypedValue *args, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *) { if (nargs < 1 || nargs > 2) { throw QueryRuntimeException("'assert' requires one or two arguments"); } @@ -561,17 +589,20 @@ TypedValue Assert(TypedValue *args, int64_t nargs, Context *) { return args[0]; } -TypedValue Counter(TypedValue *args, int64_t nargs, Context *ctx) { +TypedValue Counter(TypedValue *args, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *dba) { if (nargs != 1) { throw QueryRuntimeException("'counter' requires exactly one argument."); } if (!args[0].IsString()) throw QueryRuntimeException("'counter' argument must be a string."); - return ctx->db_accessor_.Counter(args[0].ValueString()); + return dba->Counter(args[0].ValueString()); } -TypedValue CounterSet(TypedValue *args, int64_t nargs, Context *ctx) { +TypedValue CounterSet(TypedValue *args, int64_t nargs, + const EvaluationContext &, + database::GraphDbAccessor *dba) { if (nargs != 2) { throw QueryRuntimeException("'counterSet' requires two arguments."); } @@ -581,19 +612,21 @@ TypedValue CounterSet(TypedValue *args, int64_t nargs, Context *ctx) { if (!args[1].IsInt()) throw QueryRuntimeException( "Second argument of 'counterSet' must be an integer."); - ctx->db_accessor_.CounterSet(args[0].ValueString(), args[1].ValueInt()); + dba->CounterSet(args[0].ValueString(), args[1].ValueInt()); return TypedValue::Null; } -TypedValue IndexInfo(TypedValue *, int64_t nargs, Context *ctx) { +TypedValue IndexInfo(TypedValue *, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *dba) { if (nargs != 0) throw QueryRuntimeException("'indexInfo' requires no arguments."); - auto info = ctx->db_accessor_.IndexInfo(); + auto info = dba->IndexInfo(); return std::vector(info.begin(), info.end()); } -TypedValue WorkerId(TypedValue *args, int64_t nargs, Context *) { +TypedValue WorkerId(TypedValue *args, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *) { if (nargs != 1) { throw QueryRuntimeException("'workerId' requires exactly one argument."); } @@ -609,7 +642,8 @@ TypedValue WorkerId(TypedValue *args, int64_t nargs, Context *) { } } -TypedValue Id(TypedValue *args, int64_t nargs, Context *ctx) { +TypedValue Id(TypedValue *args, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *dba) { if (nargs != 1) { throw QueryRuntimeException("'id' requires exactly one argument."); } @@ -626,7 +660,8 @@ TypedValue Id(TypedValue *args, int64_t nargs, Context *ctx) { } } -TypedValue ToString(TypedValue *args, int64_t nargs, Context *) { +TypedValue ToString(TypedValue *args, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *) { if (nargs != 1) { throw QueryRuntimeException("'toString' requires exactly one argument."); } @@ -648,14 +683,16 @@ TypedValue ToString(TypedValue *args, int64_t nargs, Context *) { } } -TypedValue Timestamp(TypedValue *, int64_t nargs, Context *ctx) { +TypedValue Timestamp(TypedValue *, int64_t nargs, const EvaluationContext &ctx, + database::GraphDbAccessor *) { if (nargs != 0) { throw QueryRuntimeException("'timestamp' requires no arguments."); } - return ctx->timestamp_; + return ctx.timestamp; } -TypedValue Left(TypedValue *args, int64_t nargs, Context *ctx) { +TypedValue Left(TypedValue *args, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *dba) { if (nargs != 2) { throw QueryRuntimeException("'left' requires two arguments."); } @@ -677,7 +714,8 @@ TypedValue Left(TypedValue *args, int64_t nargs, Context *ctx) { } } -TypedValue Right(TypedValue *args, int64_t nargs, Context *ctx) { +TypedValue Right(TypedValue *args, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *dba) { if (nargs != 2) { throw QueryRuntimeException("'right' requires two arguments."); } @@ -703,21 +741,22 @@ TypedValue Right(TypedValue *args, int64_t nargs, Context *ctx) { } } -#define WRAP_STRING_FUNCTION(name, lowercased_name, function) \ - TypedValue name(TypedValue *args, int64_t nargs, Context *) { \ - if (nargs != 1) { \ - throw QueryRuntimeException("'" #lowercased_name \ - "' requires exactly one argument."); \ - } \ - switch (args[0].type()) { \ - case TypedValue::Type::Null: \ - return TypedValue::Null; \ - case TypedValue::Type::String: \ - return function(args[0].ValueString()); \ - default: \ - throw QueryRuntimeException("'" #lowercased_name \ - "' argument should be a string."); \ - } \ +#define WRAP_STRING_FUNCTION(name, lowercased_name, function) \ + TypedValue name(TypedValue *args, int64_t nargs, const EvaluationContext &, \ + database::GraphDbAccessor *) { \ + if (nargs != 1) { \ + throw QueryRuntimeException("'" #lowercased_name \ + "' requires exactly one argument."); \ + } \ + switch (args[0].type()) { \ + case TypedValue::Type::Null: \ + return TypedValue::Null; \ + case TypedValue::Type::String: \ + return function(args[0].ValueString()); \ + default: \ + throw QueryRuntimeException("'" #lowercased_name \ + "' argument should be a string."); \ + } \ } WRAP_STRING_FUNCTION(LTrim, lTrim, utils::LTrim); @@ -727,7 +766,8 @@ WRAP_STRING_FUNCTION(Reverse, reverse, utils::Reversed); WRAP_STRING_FUNCTION(ToLower, toLower, utils::ToLowerCase); WRAP_STRING_FUNCTION(ToUpper, toUpper, utils::ToUpperCase); -TypedValue Replace(TypedValue *args, int64_t nargs, Context *ctx) { +TypedValue Replace(TypedValue *args, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *dba) { if (nargs != 3) { throw QueryRuntimeException("'replace' requires three arguments."); } @@ -750,7 +790,8 @@ TypedValue Replace(TypedValue *args, int64_t nargs, Context *ctx) { args[2].ValueString()); } -TypedValue Split(TypedValue *args, int64_t nargs, Context *ctx) { +TypedValue Split(TypedValue *args, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *dba) { if (nargs != 2) { throw QueryRuntimeException("'split' requires two arguments."); } @@ -773,7 +814,8 @@ TypedValue Split(TypedValue *args, int64_t nargs, Context *ctx) { return result; } -TypedValue Substring(TypedValue *args, int64_t nargs, Context *) { +TypedValue Substring(TypedValue *args, int64_t nargs, const EvaluationContext &, + database::GraphDbAccessor *) { if (nargs != 2 && nargs != 3) { throw QueryRuntimeException("'substring' requires two or three arguments."); } @@ -803,7 +845,8 @@ TypedValue Substring(TypedValue *args, int64_t nargs, Context *) { } // namespace -std::function +std::function NameToFunction(const std::string &function_name) { // Scalar functions if (function_name == "COALESCE") return Coalesce; diff --git a/src/query/interpret/awesome_memgraph_functions.hpp b/src/query/interpret/awesome_memgraph_functions.hpp index b42f4cb54..dd4602d6f 100644 --- a/src/query/interpret/awesome_memgraph_functions.hpp +++ b/src/query/interpret/awesome_memgraph_functions.hpp @@ -7,7 +7,7 @@ namespace query { -class Context; +struct EvaluationContext; namespace { const char kStartsWith[] = "STARTSWITH"; @@ -22,7 +22,8 @@ const char kContains[] = "CONTAINS"; /// contiguous in memory. Since most functions don't take many arguments, it's /// convenient to have them stored in the calling stack frame. std::function + const EvaluationContext &context, + database::GraphDbAccessor *)> NameToFunction(const std::string &function_name); } // namespace query diff --git a/src/query/interpret/eval.hpp b/src/query/interpret/eval.hpp index b357c7e39..967456e73 100644 --- a/src/query/interpret/eval.hpp +++ b/src/query/interpret/eval.hpp @@ -21,8 +21,16 @@ namespace query { class ExpressionEvaluator : public TreeVisitor { public: - ExpressionEvaluator(Frame &frame, Context *context, GraphView graph_view) - : frame_(frame), context_(context), graph_view_(graph_view) {} + ExpressionEvaluator(Frame *frame, const SymbolTable &symbol_table, + const Parameters ¶meters, + const EvaluationContext &ctx, + database::GraphDbAccessor *dba, GraphView graph_view) + : frame_(frame), + symbol_table_(&symbol_table), + parameters_(¶meters), + ctx_(&ctx), + dba_(dba), + graph_view_(graph_view) {} using TreeVisitor::Visit; @@ -62,14 +70,14 @@ class ExpressionEvaluator : public TreeVisitor { #undef BLOCK_VISIT TypedValue Visit(NamedExpression &named_expression) override { - const auto &symbol = context_->symbol_table_.at(named_expression); + const auto &symbol = symbol_table_->at(named_expression); auto value = named_expression.expression_->Accept(*this); - frame_[symbol] = value; + frame_->at(symbol) = value; return value; } TypedValue Visit(Identifier &ident) override { - auto value = frame_[context_->symbol_table_.at(ident)]; + auto value = frame_->at(symbol_table_->at(ident)); SwitchAccessors(value); return value; } @@ -223,7 +231,7 @@ class ExpressionEvaluator : public TreeVisitor { throw QueryRuntimeException( "Expected a string as a property name, got {}.", index.type()); return lhs.Value().PropsAt( - context_->db_accessor_.Property(index.Value())); + dba_->Property(index.Value())); } if (lhs.IsEdge()) { @@ -231,7 +239,7 @@ class ExpressionEvaluator : public TreeVisitor { throw QueryRuntimeException( "Expected a string as a property name, got {}.", index.type()); return lhs.Value().PropsAt( - context_->db_accessor_.Property(index.Value())); + dba_->Property(index.Value())); } // lhs is Null @@ -358,7 +366,7 @@ class ExpressionEvaluator : public TreeVisitor { } TypedValue Visit(Aggregation &aggregation) override { - auto value = frame_[context_->symbol_table_.at(aggregation)]; + auto value = frame_->at(symbol_table_->at(aggregation)); // Aggregation is probably always simple type, but let's switch accessor // just to be sure. SwitchAccessors(value); @@ -372,15 +380,16 @@ class ExpressionEvaluator : public TreeVisitor { for (size_t i = 0; i < function.arguments_.size(); ++i) { arguments[i] = function.arguments_[i]->Accept(*this); } - return function.function()(arguments, function.arguments_.size(), - context_); + return function.function()(arguments, function.arguments_.size(), *ctx_, + dba_); } else { std::vector arguments; arguments.reserve(function.arguments_.size()); for (const auto &argument : function.arguments_) { arguments.emplace_back(argument->Accept(*this)); } - return function.function()(arguments.data(), arguments.size(), context_); + return function.function()(arguments.data(), arguments.size(), *ctx_, + dba_); } } @@ -394,14 +403,12 @@ class ExpressionEvaluator : public TreeVisitor { list_value.type()); } const auto &list = list_value.Value>(); - const auto &element_symbol = - context_->symbol_table_.at(*reduce.identifier_); - const auto &accumulator_symbol = - context_->symbol_table_.at(*reduce.accumulator_); + const auto &element_symbol = symbol_table_->at(*reduce.identifier_); + const auto &accumulator_symbol = symbol_table_->at(*reduce.accumulator_); auto accumulator = reduce.initializer_->Accept(*this); for (const auto &element : list) { - frame_[accumulator_symbol] = accumulator; - frame_[element_symbol] = element; + frame_->at(accumulator_symbol) = accumulator; + frame_->at(element_symbol) = element; accumulator = reduce.expression_->Accept(*this); } return accumulator; @@ -417,15 +424,14 @@ class ExpressionEvaluator : public TreeVisitor { list_value.type()); } const auto &list = list_value.Value>(); - const auto &element_symbol = - context_->symbol_table_.at(*extract.identifier_); + const auto &element_symbol = symbol_table_->at(*extract.identifier_); std::vector result; result.reserve(list.size()); for (const auto &element : list) { if (element.IsNull()) { result.push_back(TypedValue::Null); } else { - frame_[element_symbol] = element; + frame_->at(element_symbol) = element; result.emplace_back(extract.expression_->Accept(*this)); } } @@ -442,9 +448,9 @@ class ExpressionEvaluator : public TreeVisitor { list_value.type()); } const auto &list = list_value.Value>(); - const auto &symbol = context_->symbol_table_.at(*all.identifier_); + const auto &symbol = symbol_table_->at(*all.identifier_); for (const auto &element : list) { - frame_[symbol] = element; + frame_->at(symbol) = element; auto result = all.where_->expression_->Accept(*this); if (!result.IsNull() && result.type() != TypedValue::Type::Bool) { throw QueryRuntimeException( @@ -468,10 +474,10 @@ class ExpressionEvaluator : public TreeVisitor { list_value.type()); } const auto &list = list_value.Value>(); - const auto &symbol = context_->symbol_table_.at(*single.identifier_); + const auto &symbol = symbol_table_->at(*single.identifier_); bool predicate_satisfied = false; for (const auto &element : list) { - frame_[symbol] = element; + frame_->at(symbol) = element; auto result = single.where_->expression_->Accept(*this); if (!result.IsNull() && result.type() != TypedValue::Type::Bool) { throw QueryRuntimeException( @@ -492,7 +498,7 @@ class ExpressionEvaluator : public TreeVisitor { } TypedValue Visit(ParameterLookup ¶m_lookup) override { - return context_->parameters_.AtTokenPosition(param_lookup.token_position_); + return parameters_->AtTokenPosition(param_lookup.token_position_); } private: @@ -558,8 +564,11 @@ class ExpressionEvaluator : public TreeVisitor { } } - Frame &frame_; - Context *context_; + Frame *frame_; + const SymbolTable *symbol_table_; + const Parameters *parameters_; + const EvaluationContext *ctx_; + database::GraphDbAccessor *dba_; // which switching approach should be used when evaluating const GraphView graph_view_; }; diff --git a/src/query/interpreter.cpp b/src/query/interpreter.cpp index 370989b39..fd05e976d 100644 --- a/src/query/interpreter.cpp +++ b/src/query/interpreter.cpp @@ -28,13 +28,18 @@ Interpreter::Results Interpreter::operator()( const std::map ¶ms, bool in_explicit_transaction) { utils::Timer frontend_timer; + + EvaluationContext evaluation_context; + evaluation_context.timestamp = + std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + Context ctx(db_accessor); ctx.in_explicit_transaction_ = in_explicit_transaction; - ctx.timestamp_ = std::chrono::duration_cast( - std::chrono::system_clock::now().time_since_epoch()) - .count(); ctx.auth_ = auth_; ctx.kafka_streams_ = kafka_streams_; + ctx.evaluation_context_ = evaluation_context; // query -> stripped query StrippedQuery stripped(query); diff --git a/src/query/plan/distributed_ops.cpp b/src/query/plan/distributed_ops.cpp index db7998907..2393fc8c3 100644 --- a/src/query/plan/distributed_ops.cpp +++ b/src/query/plan/distributed_ops.cpp @@ -342,7 +342,7 @@ class RemotePuller { void UpdatePullForWorker(int worker_id, Context &context) { remote_pulls_[worker_id] = pull_clients_->Pull( &db_, worker_id, plan_id_, command_id_, context.parameters_, symbols_, - context.timestamp_, false); + context.evaluation_context_.timestamp, false); } }; @@ -530,7 +530,7 @@ class SynchronizeCursor : public Cursor { worker_accumulations.emplace_back(pull_clients_->Pull( &context.db_accessor_, worker_id, self_.pull_remote()->plan_id(), command_id_, context.parameters_, self_.pull_remote()->symbols(), - context.timestamp_, true, 0)); + context.evaluation_context_.timestamp, true, 0)); } } @@ -627,7 +627,9 @@ class PullRemoteOrderByCursor : public Cursor { bool Pull(Frame &frame, Context &context) { if (context.db_accessor_.should_abort()) throw HintedAbortError(); - ExpressionEvaluator evaluator(frame, &context, GraphView::OLD); + ExpressionEvaluator evaluator( + &frame, context.symbol_table_, context.parameters_, + context.evaluation_context_, &context.db_accessor_, GraphView::OLD); auto evaluate_result = [this, &evaluator]() { std::vector order_by; @@ -1005,7 +1007,9 @@ class DistributedExpandBfsCursor : public query::plan::Cursor { } // Evaluator for the filtering condition and expansion depth. - ExpressionEvaluator evaluator(frame, &context, self_.graph_view()); + ExpressionEvaluator evaluator( + &frame, context.symbol_table_, context.parameters_, + context.evaluation_context_, &context.db_accessor_, self_.graph_view()); while (true) { TypedValue last_vertex; @@ -1181,7 +1185,9 @@ VertexAccessor &CreateVertexOnWorker(int worker_id, NodeAtom *node_atom, // Evaluator should use the latest accessors, as modified in this query, when // setting properties on new nodes. - ExpressionEvaluator evaluator(frame, &context, GraphView::NEW); + ExpressionEvaluator evaluator( + &frame, context.symbol_table_, context.parameters_, + context.evaluation_context_, &context.db_accessor_, GraphView::NEW); for (auto &kv : node_atom->properties_) { auto value = kv.second->Accept(evaluator); if (!value.IsPropertyValue()) { @@ -1253,7 +1259,9 @@ class DistributedCreateExpandCursor : public query::plan::Cursor { // Similarly to CreateNode, newly created edges and nodes should use the // latest accesors. - ExpressionEvaluator evaluator(frame, &context, GraphView::NEW); + ExpressionEvaluator evaluator( + &frame, context.symbol_table_, context.parameters_, + context.evaluation_context_, &context.db_accessor_, GraphView::NEW); // E.g. we pickup new properties: `CREATE (n {p: 42}) -[:r {ep: n.p}]-> ()` v1.SwitchNew(); diff --git a/src/query/plan/operator.cpp b/src/query/plan/operator.cpp index 349bb1fb4..11237a840 100644 --- a/src/query/plan/operator.cpp +++ b/src/query/plan/operator.cpp @@ -101,7 +101,9 @@ VertexAccessor &CreateLocalVertex(NodeAtom *node_atom, Frame &frame, // Evaluator should use the latest accessors, as modified in this query, when // setting properties on new nodes. - ExpressionEvaluator evaluator(frame, &context, GraphView::NEW); + ExpressionEvaluator evaluator( + &frame, context.symbol_table_, context.parameters_, + context.evaluation_context_, &context.db_accessor_, GraphView::NEW); for (auto &kv : node_atom->properties_) PropsSetChecked(&new_node, kv.first.second, kv.second->Accept(evaluator)); frame[context.symbol_table_.at(*node_atom->identifier_)] = new_node; @@ -176,7 +178,9 @@ bool CreateExpand::CreateExpandCursor::Pull(Frame &frame, Context &context) { // Similarly to CreateNode, newly created edges and nodes should use the // latest accesors. - ExpressionEvaluator evaluator(frame, &context, GraphView::NEW); + ExpressionEvaluator evaluator( + &frame, context.symbol_table_, context.parameters_, + context.evaluation_context_, &context.db_accessor_, GraphView::NEW); // E.g. we pickup new properties: `CREATE (n {p: 42}) -[:r {ep: n.p}]-> ()` v1.SwitchNew(); @@ -339,7 +343,9 @@ std::unique_ptr ScanAllByLabelPropertyRange::MakeCursor( -> std::experimental::optional { - ExpressionEvaluator evaluator(frame, &context, graph_view_); + ExpressionEvaluator evaluator( + &frame, context.symbol_table_, context.parameters_, + context.evaluation_context_, &context.db_accessor_, graph_view_); auto convert = [&evaluator](const auto &bound) -> std::experimental::optional> { if (!bound) return std::experimental::nullopt; @@ -386,7 +392,9 @@ std::unique_ptr ScanAllByLabelPropertyValue::MakeCursor( auto vertices = [this, &db](Frame &frame, Context &context) -> std::experimental::optional { - ExpressionEvaluator evaluator(frame, &context, graph_view_); + ExpressionEvaluator evaluator( + &frame, context.symbol_table_, context.parameters_, + context.evaluation_context_, &context.db_accessor_, graph_view_); auto value = expression_->Accept(evaluator); if (value.IsNull()) return std::experimental::nullopt; try { @@ -651,7 +659,9 @@ class ExpandVariableCursor : public Cursor { : self_(self), input_cursor_(self.input_->MakeCursor(db)) {} bool Pull(Frame &frame, Context &context) override { - ExpressionEvaluator evaluator(frame, &context, self_.graph_view_); + ExpressionEvaluator evaluator( + &frame, context.symbol_table_, context.parameters_, + context.evaluation_context_, &context.db_accessor_, self_.graph_view_); while (true) { if (Expand(frame, context)) return true; @@ -723,7 +733,10 @@ class ExpandVariableCursor : public Cursor { SwitchAccessor(vertex, self_.graph_view_); // Evaluate the upper and lower bounds. - ExpressionEvaluator evaluator(frame, &context, self_.graph_view_); + ExpressionEvaluator evaluator(&frame, context.symbol_table_, + context.parameters_, + context.evaluation_context_, + &context.db_accessor_, self_.graph_view_); auto calc_bound = [&evaluator](auto &bound) { auto value = EvaluateInt(&evaluator, bound, "Variable expansion bound"); if (value < 0) @@ -782,7 +795,9 @@ class ExpandVariableCursor : public Cursor { * vertex and another Pull from the input cursor should be performed. */ bool Expand(Frame &frame, Context &context) { - ExpressionEvaluator evaluator(frame, &context, self_.graph_view_); + ExpressionEvaluator evaluator( + &frame, context.symbol_table_, context.parameters_, + context.evaluation_context_, &context.db_accessor_, self_.graph_view_); // Some expansions might not be valid due to edge uniqueness and // existing_node criterions, so expand in a loop until either the input // vertex is exhausted or a valid variable-length expansion is available. @@ -875,7 +890,9 @@ class STShortestPathCursor : public query::plan::Cursor { } bool Pull(Frame &frame, Context &context) override { - ExpressionEvaluator evaluator(frame, &context, GraphView::OLD); + ExpressionEvaluator evaluator( + &frame, context.symbol_table_, context.parameters_, + context.evaluation_context_, &context.db_accessor_, GraphView::OLD); while (input_cursor_->Pull(frame, context)) { auto source_tv = frame[self_.input_symbol()]; auto sink_tv = frame[self_.node_symbol()]; @@ -1102,7 +1119,9 @@ class SingleSourceShortestPathCursor : public query::plan::Cursor { } bool Pull(Frame &frame, Context &context) override { - ExpressionEvaluator evaluator(frame, &context, GraphView::OLD); + ExpressionEvaluator evaluator( + &frame, context.symbol_table_, context.parameters_, + context.evaluation_context_, &context.db_accessor_, GraphView::OLD); // for the given (edge, vertex) pair checks if they satisfy the // "where" condition. if so, places them in the to_visit_ structure. @@ -1247,7 +1266,9 @@ class ExpandWeightedShortestPathCursor : public query::plan::Cursor { : self_(self), input_cursor_(self_.input_->MakeCursor(db)) {} bool Pull(Frame &frame, Context &context) override { - ExpressionEvaluator evaluator(frame, &context, self_.graph_view_); + ExpressionEvaluator evaluator( + &frame, context.symbol_table_, context.parameters_, + context.evaluation_context_, &context.db_accessor_, self_.graph_view_); auto create_state = [this](VertexAccessor vertex, int depth) { return std::make_pair(vertex, upper_bound_set_ ? depth : 0); }; @@ -1610,7 +1631,9 @@ Filter::FilterCursor::FilterCursor(const Filter &self, bool Filter::FilterCursor::Pull(Frame &frame, Context &context) { // Like all filters, newly set values should not affect filtering of old // nodes and edges. - ExpressionEvaluator evaluator(frame, &context, GraphView::OLD); + ExpressionEvaluator evaluator( + &frame, context.symbol_table_, context.parameters_, + context.evaluation_context_, &context.db_accessor_, GraphView::OLD); while (input_cursor_->Pull(frame, context)) { if (EvaluateFilter(evaluator, self_.expression_)) return true; } @@ -1651,7 +1674,9 @@ Produce::ProduceCursor::ProduceCursor(const Produce &self, bool Produce::ProduceCursor::Pull(Frame &frame, Context &context) { if (input_cursor_->Pull(frame, context)) { // Produce should always yield the latest results. - ExpressionEvaluator evaluator(frame, &context, GraphView::NEW); + ExpressionEvaluator evaluator( + &frame, context.symbol_table_, context.parameters_, + context.evaluation_context_, &context.db_accessor_, GraphView::NEW); for (auto named_expr : self_.named_expressions_) named_expr->Accept(evaluator); return true; @@ -1686,7 +1711,9 @@ bool Delete::DeleteCursor::Pull(Frame &frame, Context &context) { // Delete should get the latest information, this way it is also possible // to // delete newly added nodes and edges. - ExpressionEvaluator evaluator(frame, &context, GraphView::NEW); + ExpressionEvaluator evaluator( + &frame, context.symbol_table_, context.parameters_, + context.evaluation_context_, &context.db_accessor_, GraphView::NEW); // collect expressions results so edges can get deleted before vertices // this is necessary because an edge that gets deleted could block vertex // deletion @@ -1754,7 +1781,9 @@ bool SetProperty::SetPropertyCursor::Pull(Frame &frame, Context &context) { if (!input_cursor_->Pull(frame, context)) return false; // Set, just like Create needs to see the latest changes. - ExpressionEvaluator evaluator(frame, &context, GraphView::NEW); + ExpressionEvaluator evaluator( + &frame, context.symbol_table_, context.parameters_, + context.evaluation_context_, &context.db_accessor_, GraphView::NEW); TypedValue lhs = self_.lhs_->expression_->Accept(evaluator); TypedValue rhs = self_.rhs_->Accept(evaluator); @@ -1810,7 +1839,9 @@ bool SetProperties::SetPropertiesCursor::Pull(Frame &frame, Context &context) { TypedValue &lhs = frame[self_.input_symbol_]; // Set, just like Create needs to see the latest changes. - ExpressionEvaluator evaluator(frame, &context, GraphView::NEW); + ExpressionEvaluator evaluator( + &frame, context.symbol_table_, context.parameters_, + context.evaluation_context_, &context.db_accessor_, GraphView::NEW); TypedValue rhs = self_.rhs_->Accept(evaluator); switch (lhs.type()) { @@ -1945,7 +1976,9 @@ bool RemoveProperty::RemovePropertyCursor::Pull(Frame &frame, if (!input_cursor_->Pull(frame, context)) return false; // Remove, just like Delete needs to see the latest changes. - ExpressionEvaluator evaluator(frame, &context, GraphView::NEW); + ExpressionEvaluator evaluator( + &frame, context.symbol_table_, context.parameters_, + context.evaluation_context_, &context.db_accessor_, GraphView::NEW); TypedValue lhs = self_.lhs_->expression_->Accept(evaluator); switch (lhs.type()) { @@ -2243,7 +2276,9 @@ bool Aggregate::AggregateCursor::Pull(Frame &frame, Context &context) { } void Aggregate::AggregateCursor::ProcessAll(Frame &frame, Context &context) { - ExpressionEvaluator evaluator(frame, &context, GraphView::NEW); + ExpressionEvaluator evaluator( + &frame, context.symbol_table_, context.parameters_, + context.evaluation_context_, &context.db_accessor_, GraphView::NEW); while (input_cursor_->Pull(frame, context)) ProcessOne(frame, context.symbol_table_, evaluator); @@ -2470,7 +2505,9 @@ bool Skip::SkipCursor::Pull(Frame &frame, Context &context) { // First successful pull from the input, evaluate the skip expression. // The skip expression doesn't contain identifiers so graph view // parameter is not important. - ExpressionEvaluator evaluator(frame, &context, GraphView::OLD); + ExpressionEvaluator evaluator( + &frame, context.symbol_table_, context.parameters_, + context.evaluation_context_, &context.db_accessor_, GraphView::OLD); TypedValue to_skip = self_.expression_->Accept(evaluator); if (to_skip.type() != TypedValue::Type::Int) throw QueryRuntimeException( @@ -2526,7 +2563,9 @@ bool Limit::LimitCursor::Pull(Frame &frame, Context &context) { if (limit_ == -1) { // Limit expression doesn't contain identifiers so graph view is not // important. - ExpressionEvaluator evaluator(frame, &context, GraphView::OLD); + ExpressionEvaluator evaluator( + &frame, context.symbol_table_, context.parameters_, + context.evaluation_context_, &context.db_accessor_, GraphView::OLD); TypedValue limit = self_.expression_->Accept(evaluator); if (limit.type() != TypedValue::Type::Int) throw QueryRuntimeException( @@ -2588,7 +2627,9 @@ OrderBy::OrderByCursor::OrderByCursor(const OrderBy &self, bool OrderBy::OrderByCursor::Pull(Frame &frame, Context &context) { if (!did_pull_all_) { - ExpressionEvaluator evaluator(frame, &context, GraphView::OLD); + ExpressionEvaluator evaluator( + &frame, context.symbol_table_, context.parameters_, + context.evaluation_context_, &context.db_accessor_, GraphView::OLD); while (input_cursor_->Pull(frame, context)) { // collect the order_by elements std::vector order_by; @@ -2817,7 +2858,9 @@ bool Unwind::UnwindCursor::Pull(Frame &frame, Context &context) { if (!input_cursor_->Pull(frame, context)) return false; // successful pull from input, initialize value and iterator - ExpressionEvaluator evaluator(frame, &context, GraphView::OLD); + ExpressionEvaluator evaluator( + &frame, context.symbol_table_, context.parameters_, + context.evaluation_context_, &context.db_accessor_, GraphView::OLD); TypedValue input_value = self_.input_expression_->Accept(evaluator); if (input_value.type() != TypedValue::Type::List) throw QueryRuntimeException( @@ -3216,7 +3259,9 @@ class AuthHandlerCursor : public Cursor { throw UserModificationInMulticommandTxException(); } - ExpressionEvaluator evaluator(frame, &ctx, GraphView::OLD); + ExpressionEvaluator evaluator(&frame, ctx.symbol_table_, ctx.parameters_, + ctx.evaluation_context_, &ctx.db_accessor_, + GraphView::OLD); std::experimental::optional password; if (self_.password()) { auto password_tv = self_.password()->Accept(evaluator); @@ -3508,7 +3553,9 @@ class CreateStreamCursor : public Cursor { if (ctx.in_explicit_transaction_) { throw StreamClauseInMulticommandTxException(); } - ExpressionEvaluator evaluator(frame, &ctx, GraphView::OLD); + ExpressionEvaluator evaluator(&frame, ctx.symbol_table_, ctx.parameters_, + ctx.evaluation_context_, &ctx.db_accessor_, + GraphView::OLD); TypedValue stream_uri = self_.stream_uri()->Accept(evaluator); TypedValue stream_topic = self_.stream_topic()->Accept(evaluator); @@ -3666,7 +3713,9 @@ class StartStopStreamCursor : public Cursor { throw StreamClauseInMulticommandTxException(); } - ExpressionEvaluator evaluator(frame, &ctx, GraphView::OLD); + ExpressionEvaluator evaluator(&frame, ctx.symbol_table_, ctx.parameters_, + ctx.evaluation_context_, &ctx.db_accessor_, + GraphView::OLD); std::experimental::optional limit_batches; if (self_.limit_batches()) { @@ -3762,7 +3811,9 @@ class TestStreamCursor : public Cursor { } if (!is_initialized_) { - ExpressionEvaluator evaluator(frame, &ctx, GraphView::OLD); + ExpressionEvaluator evaluator(&frame, ctx.symbol_table_, ctx.parameters_, + ctx.evaluation_context_, &ctx.db_accessor_, + GraphView::OLD); std::experimental::optional limit_batches; if (self_.limit_batches()) { diff --git a/tests/benchmark/query/eval.cpp b/tests/benchmark/query/eval.cpp index af6294d09..fd1781662 100644 --- a/tests/benchmark/query/eval.cpp +++ b/tests/benchmark/query/eval.cpp @@ -16,7 +16,10 @@ static void BenchmarkCoalesceCallWithNulls(benchmark::State &state) { query::Frame frame(0); database::GraphDbAccessor *dba = nullptr; query::Context context(*dba); - query::ExpressionEvaluator evaluator(frame, &context, query::GraphView::OLD); + query::ExpressionEvaluator evaluator( + &frame, context.symbol_table_, context.parameters_, + context.evaluation_context_, &context.db_accessor_, + query::GraphView::OLD); while (state.KeepRunning()) { function->Accept(evaluator); } @@ -35,7 +38,10 @@ static void BenchmarkCoalesceCallWithStrings(benchmark::State &state) { query::Frame frame(0); database::GraphDbAccessor *dba = nullptr; query::Context context(*dba); - query::ExpressionEvaluator evaluator(frame, &context, query::GraphView::OLD); + query::ExpressionEvaluator evaluator( + &frame, context.symbol_table_, context.parameters_, + context.evaluation_context_, &context.db_accessor_, + query::GraphView::OLD); while (state.KeepRunning()) { function->Accept(evaluator); } diff --git a/tests/unit/query_expression_evaluator.cpp b/tests/unit/query_expression_evaluator.cpp index 005dbcfa8..21b6f2e52 100644 --- a/tests/unit/query_expression_evaluator.cpp +++ b/tests/unit/query_expression_evaluator.cpp @@ -27,286 +27,236 @@ using testing::UnorderedElementsAre; namespace { -struct NoContextExpressionEvaluator { - NoContextExpressionEvaluator() {} - Frame frame{128}; +class ExpressionEvaluatorTest : public ::testing::Test { + protected: database::SingleNode db; std::unique_ptr dba{db.Access()}; - Context ctx{*dba}; - ExpressionEvaluator eval{frame, &ctx, GraphView::OLD}; + + AstStorage storage; + EvaluationContext ctx; + SymbolTable symbol_table; + Parameters parameters; + + Frame frame{128}; + ExpressionEvaluator eval{&frame, symbol_table, parameters, + ctx, dba.get(), GraphView::OLD}; }; -TypedValue EvaluateFunction(const std::string &function_name, - const std::vector &args, - Context *context) { - AstStorage storage; - Frame frame{128}; - ExpressionEvaluator eval{frame, context, GraphView::OLD}; - - std::vector expressions; - for (const auto &arg : args) { - expressions.push_back(storage.Create(arg)); - } - auto *op = storage.Create(function_name, expressions); - return op->Accept(eval); -} - -TypedValue EvaluateFunction(const std::string &function_name, - const std::vector &args) { - database::SingleNode db; - auto dba = db.Access(); - Context ctx{*dba}; - return EvaluateFunction(function_name, args, &ctx); -} - -TEST(ExpressionEvaluator, OrOperator) { - AstStorage storage; - NoContextExpressionEvaluator eval; +TEST_F(ExpressionEvaluatorTest, OrOperator) { auto *op = storage.Create(storage.Create(true), storage.Create(false)); - auto val1 = op->Accept(eval.eval); - ASSERT_EQ(val1.Value(), true); + auto val1 = op->Accept(eval); + ASSERT_EQ(val1.ValueBool(), true); op = storage.Create(storage.Create(true), storage.Create(true)); - auto val2 = op->Accept(eval.eval); - ASSERT_EQ(val2.Value(), true); + auto val2 = op->Accept(eval); + ASSERT_EQ(val2.ValueBool(), true); } -TEST(ExpressionEvaluator, XorOperator) { - AstStorage storage; - NoContextExpressionEvaluator eval; +TEST_F(ExpressionEvaluatorTest, XorOperator) { auto *op = storage.Create(storage.Create(true), storage.Create(false)); - auto val1 = op->Accept(eval.eval); - ASSERT_EQ(val1.Value(), true); + auto val1 = op->Accept(eval); + ASSERT_EQ(val1.ValueBool(), true); op = storage.Create(storage.Create(true), storage.Create(true)); - auto val2 = op->Accept(eval.eval); - ASSERT_EQ(val2.Value(), false); + auto val2 = op->Accept(eval); + ASSERT_EQ(val2.ValueBool(), false); } -TEST(ExpressionEvaluator, AndOperator) { - AstStorage storage; - NoContextExpressionEvaluator eval; +TEST_F(ExpressionEvaluatorTest, AndOperator) { auto *op = storage.Create(storage.Create(true), storage.Create(true)); - auto val1 = op->Accept(eval.eval); - ASSERT_EQ(val1.Value(), true); + auto val1 = op->Accept(eval); + ASSERT_EQ(val1.ValueBool(), true); op = storage.Create(storage.Create(false), storage.Create(true)); - auto val2 = op->Accept(eval.eval); - ASSERT_EQ(val2.Value(), false); + auto val2 = op->Accept(eval); + ASSERT_EQ(val2.ValueBool(), false); } -TEST(ExpressionEvaluator, AndOperatorShortCircuit) { - AstStorage storage; - NoContextExpressionEvaluator eval; +TEST_F(ExpressionEvaluatorTest, AndOperatorShortCircuit) { { auto *op = storage.Create(storage.Create(false), storage.Create(5)); - auto value = op->Accept(eval.eval); - EXPECT_EQ(value.Value(), false); + auto value = op->Accept(eval); + EXPECT_EQ(value.ValueBool(), false); } { auto *op = storage.Create(storage.Create(5), storage.Create(false)); - // We are evaluating left to right, so we don't short circuit here and raise - // due to `5`. This differs from neo4j, where they evaluate both sides and - // return `false` without checking for type of the first expression. - EXPECT_THROW(op->Accept(eval.eval), QueryRuntimeException); + // We are evaluating left to right, so we don't short circuit here and + // raise due to `5`. This differs from neo4j, where they evaluate both + // sides and return `false` without checking for type of the first + // expression. + EXPECT_THROW(op->Accept(eval), QueryRuntimeException); } } -TEST(ExpressionEvaluator, AndOperatorNull) { - AstStorage storage; - NoContextExpressionEvaluator eval; +TEST_F(ExpressionEvaluatorTest, AndOperatorNull) { { // Null doesn't short circuit auto *op = storage.Create( storage.Create(TypedValue::Null), storage.Create(5)); - EXPECT_THROW(op->Accept(eval.eval), QueryRuntimeException); + EXPECT_THROW(op->Accept(eval), QueryRuntimeException); } { auto *op = storage.Create( storage.Create(TypedValue::Null), storage.Create(true)); - auto value = op->Accept(eval.eval); + auto value = op->Accept(eval); EXPECT_TRUE(value.IsNull()); } { auto *op = storage.Create( storage.Create(TypedValue::Null), storage.Create(false)); - auto value = op->Accept(eval.eval); + auto value = op->Accept(eval); ASSERT_TRUE(value.IsBool()); - EXPECT_EQ(value.Value(), false); + EXPECT_EQ(value.ValueBool(), false); } } -TEST(ExpressionEvaluator, AdditionOperator) { - AstStorage storage; - NoContextExpressionEvaluator eval; +TEST_F(ExpressionEvaluatorTest, AdditionOperator) { auto *op = storage.Create( storage.Create(2), storage.Create(3)); - auto value = op->Accept(eval.eval); - ASSERT_EQ(value.Value(), 5); + auto value = op->Accept(eval); + ASSERT_EQ(value.ValueInt(), 5); } -TEST(ExpressionEvaluator, SubtractionOperator) { - AstStorage storage; - NoContextExpressionEvaluator eval; +TEST_F(ExpressionEvaluatorTest, SubtractionOperator) { auto *op = storage.Create( storage.Create(2), storage.Create(3)); - auto value = op->Accept(eval.eval); - ASSERT_EQ(value.Value(), -1); + auto value = op->Accept(eval); + ASSERT_EQ(value.ValueInt(), -1); } -TEST(ExpressionEvaluator, MultiplicationOperator) { - AstStorage storage; - NoContextExpressionEvaluator eval; +TEST_F(ExpressionEvaluatorTest, MultiplicationOperator) { auto *op = storage.Create( storage.Create(2), storage.Create(3)); - auto value = op->Accept(eval.eval); - ASSERT_EQ(value.Value(), 6); + auto value = op->Accept(eval); + ASSERT_EQ(value.ValueInt(), 6); } -TEST(ExpressionEvaluator, DivisionOperator) { - AstStorage storage; - NoContextExpressionEvaluator eval; +TEST_F(ExpressionEvaluatorTest, DivisionOperator) { auto *op = storage.Create(storage.Create(50), storage.Create(10)); - auto value = op->Accept(eval.eval); - ASSERT_EQ(value.Value(), 5); + auto value = op->Accept(eval); + ASSERT_EQ(value.ValueInt(), 5); } -TEST(ExpressionEvaluator, ModOperator) { - AstStorage storage; - NoContextExpressionEvaluator eval; +TEST_F(ExpressionEvaluatorTest, ModOperator) { auto *op = storage.Create(storage.Create(65), storage.Create(10)); - auto value = op->Accept(eval.eval); - ASSERT_EQ(value.Value(), 5); + auto value = op->Accept(eval); + ASSERT_EQ(value.ValueInt(), 5); } -TEST(ExpressionEvaluator, EqualOperator) { - AstStorage storage; - NoContextExpressionEvaluator eval; +TEST_F(ExpressionEvaluatorTest, EqualOperator) { auto *op = storage.Create(storage.Create(10), storage.Create(15)); - auto val1 = op->Accept(eval.eval); - ASSERT_EQ(val1.Value(), false); + auto val1 = op->Accept(eval); + ASSERT_EQ(val1.ValueBool(), false); op = storage.Create(storage.Create(15), storage.Create(15)); - auto val2 = op->Accept(eval.eval); - ASSERT_EQ(val2.Value(), true); + auto val2 = op->Accept(eval); + ASSERT_EQ(val2.ValueBool(), true); op = storage.Create(storage.Create(20), storage.Create(15)); - auto val3 = op->Accept(eval.eval); - ASSERT_EQ(val3.Value(), false); + auto val3 = op->Accept(eval); + ASSERT_EQ(val3.ValueBool(), false); } -TEST(ExpressionEvaluator, NotEqualOperator) { - AstStorage storage; - NoContextExpressionEvaluator eval; +TEST_F(ExpressionEvaluatorTest, NotEqualOperator) { auto *op = storage.Create(storage.Create(10), storage.Create(15)); - auto val1 = op->Accept(eval.eval); - ASSERT_EQ(val1.Value(), true); + auto val1 = op->Accept(eval); + ASSERT_EQ(val1.ValueBool(), true); op = storage.Create(storage.Create(15), storage.Create(15)); - auto val2 = op->Accept(eval.eval); - ASSERT_EQ(val2.Value(), false); + auto val2 = op->Accept(eval); + ASSERT_EQ(val2.ValueBool(), false); op = storage.Create(storage.Create(20), storage.Create(15)); - auto val3 = op->Accept(eval.eval); - ASSERT_EQ(val3.Value(), true); + auto val3 = op->Accept(eval); + ASSERT_EQ(val3.ValueBool(), true); } -TEST(ExpressionEvaluator, LessOperator) { - AstStorage storage; - NoContextExpressionEvaluator eval; +TEST_F(ExpressionEvaluatorTest, LessOperator) { auto *op = storage.Create(storage.Create(10), storage.Create(15)); - auto val1 = op->Accept(eval.eval); - ASSERT_EQ(val1.Value(), true); + auto val1 = op->Accept(eval); + ASSERT_EQ(val1.ValueBool(), true); op = storage.Create(storage.Create(15), storage.Create(15)); - auto val2 = op->Accept(eval.eval); - ASSERT_EQ(val2.Value(), false); + auto val2 = op->Accept(eval); + ASSERT_EQ(val2.ValueBool(), false); op = storage.Create(storage.Create(20), storage.Create(15)); - auto val3 = op->Accept(eval.eval); - ASSERT_EQ(val3.Value(), false); + auto val3 = op->Accept(eval); + ASSERT_EQ(val3.ValueBool(), false); } -TEST(ExpressionEvaluator, GreaterOperator) { - AstStorage storage; - NoContextExpressionEvaluator eval; +TEST_F(ExpressionEvaluatorTest, GreaterOperator) { auto *op = storage.Create(storage.Create(10), storage.Create(15)); - auto val1 = op->Accept(eval.eval); - ASSERT_EQ(val1.Value(), false); + auto val1 = op->Accept(eval); + ASSERT_EQ(val1.ValueBool(), false); op = storage.Create(storage.Create(15), storage.Create(15)); - auto val2 = op->Accept(eval.eval); - ASSERT_EQ(val2.Value(), false); + auto val2 = op->Accept(eval); + ASSERT_EQ(val2.ValueBool(), false); op = storage.Create(storage.Create(20), storage.Create(15)); - auto val3 = op->Accept(eval.eval); - ASSERT_EQ(val3.Value(), true); + auto val3 = op->Accept(eval); + ASSERT_EQ(val3.ValueBool(), true); } -TEST(ExpressionEvaluator, LessEqualOperator) { - AstStorage storage; - NoContextExpressionEvaluator eval; +TEST_F(ExpressionEvaluatorTest, LessEqualOperator) { auto *op = storage.Create(storage.Create(10), storage.Create(15)); - auto val1 = op->Accept(eval.eval); - ASSERT_EQ(val1.Value(), true); + auto val1 = op->Accept(eval); + ASSERT_EQ(val1.ValueBool(), true); op = storage.Create(storage.Create(15), storage.Create(15)); - auto val2 = op->Accept(eval.eval); - ASSERT_EQ(val2.Value(), true); + auto val2 = op->Accept(eval); + ASSERT_EQ(val2.ValueBool(), true); op = storage.Create(storage.Create(20), storage.Create(15)); - auto val3 = op->Accept(eval.eval); - ASSERT_EQ(val3.Value(), false); + auto val3 = op->Accept(eval); + ASSERT_EQ(val3.ValueBool(), false); } -TEST(ExpressionEvaluator, GreaterEqualOperator) { - AstStorage storage; - NoContextExpressionEvaluator eval; +TEST_F(ExpressionEvaluatorTest, GreaterEqualOperator) { auto *op = storage.Create( storage.Create(10), storage.Create(15)); - auto val1 = op->Accept(eval.eval); - ASSERT_EQ(val1.Value(), false); + auto val1 = op->Accept(eval); + ASSERT_EQ(val1.ValueBool(), false); op = storage.Create( storage.Create(15), storage.Create(15)); - auto val2 = op->Accept(eval.eval); - ASSERT_EQ(val2.Value(), true); + auto val2 = op->Accept(eval); + ASSERT_EQ(val2.ValueBool(), true); op = storage.Create( storage.Create(20), storage.Create(15)); - auto val3 = op->Accept(eval.eval); - ASSERT_EQ(val3.Value(), true); + auto val3 = op->Accept(eval); + ASSERT_EQ(val3.ValueBool(), true); } -TEST(ExpressionEvaluator, InListOperator) { - AstStorage storage; - NoContextExpressionEvaluator eval; +TEST_F(ExpressionEvaluatorTest, InListOperator) { auto *list_literal = storage.Create(std::vector{ storage.Create(1), storage.Create(2), storage.Create("a")}); @@ -314,15 +264,15 @@ TEST(ExpressionEvaluator, InListOperator) { // Element exists in list. auto *op = storage.Create( storage.Create(2), list_literal); - auto value = op->Accept(eval.eval); - EXPECT_EQ(value.Value(), true); + auto value = op->Accept(eval); + EXPECT_EQ(value.ValueBool(), true); } { // Element doesn't exist in list. auto *op = storage.Create( storage.Create("x"), list_literal); - auto value = op->Accept(eval.eval); - EXPECT_EQ(value.Value(), false); + auto value = op->Accept(eval); + EXPECT_EQ(value.ValueBool(), false); } { auto *list_literal = storage.Create(std::vector{ @@ -332,7 +282,7 @@ TEST(ExpressionEvaluator, InListOperator) { // Element doesn't exist in list with null element. auto *op = storage.Create( storage.Create("x"), list_literal); - auto value = op->Accept(eval.eval); + auto value = op->Accept(eval); EXPECT_TRUE(value.IsNull()); } { @@ -340,14 +290,14 @@ TEST(ExpressionEvaluator, InListOperator) { auto *op = storage.Create( storage.Create("x"), storage.Create(TypedValue::Null)); - auto value = op->Accept(eval.eval); + auto value = op->Accept(eval); EXPECT_TRUE(value.IsNull()); } { // Null literal. auto *op = storage.Create( storage.Create(TypedValue::Null), list_literal); - auto value = op->Accept(eval.eval); + auto value = op->Accept(eval); EXPECT_TRUE(value.IsNull()); } { @@ -355,14 +305,12 @@ TEST(ExpressionEvaluator, InListOperator) { auto *op = storage.Create( storage.Create(TypedValue::Null), storage.Create(std::vector())); - auto value = op->Accept(eval.eval); + auto value = op->Accept(eval); EXPECT_FALSE(value.ValueBool()); } } -TEST(ExpressionEvaluator, ListIndexing) { - AstStorage storage; - NoContextExpressionEvaluator eval; +TEST_F(ExpressionEvaluatorTest, ListIndexing) { auto *list_literal = storage.Create(std::vector{ storage.Create(1), storage.Create(2), storage.Create(3), @@ -371,96 +319,89 @@ TEST(ExpressionEvaluator, ListIndexing) { // Legal indexing. auto *op = storage.Create( list_literal, storage.Create(2)); - auto value = op->Accept(eval.eval); - EXPECT_EQ(value.Value(), 3); + auto value = op->Accept(eval); + EXPECT_EQ(value.ValueInt(), 3); } { // Out of bounds indexing. auto *op = storage.Create( list_literal, storage.Create(4)); - auto value = op->Accept(eval.eval); - EXPECT_EQ(value.type(), TypedValue::Type::Null); + auto value = op->Accept(eval); + EXPECT_TRUE(value.IsNull()); } { // Out of bounds indexing with negative bound. auto *op = storage.Create( list_literal, storage.Create(-100)); - auto value = op->Accept(eval.eval); - EXPECT_EQ(value.type(), TypedValue::Type::Null); + auto value = op->Accept(eval); + EXPECT_TRUE(value.IsNull()); } { // Legal indexing with negative index. auto *op = storage.Create( list_literal, storage.Create(-2)); - auto value = op->Accept(eval.eval); - EXPECT_EQ(value.Value(), 3); + auto value = op->Accept(eval); + EXPECT_EQ(value.ValueInt(), 3); } { // Indexing with one operator being null. auto *op = storage.Create( storage.Create(TypedValue::Null), storage.Create(-2)); - auto value = op->Accept(eval.eval); - EXPECT_EQ(value.type(), TypedValue::Type::Null); + auto value = op->Accept(eval); + EXPECT_TRUE(value.IsNull()); } { // Indexing with incompatible type. auto *op = storage.Create( list_literal, storage.Create("bla")); - EXPECT_THROW(op->Accept(eval.eval), QueryRuntimeException); + EXPECT_THROW(op->Accept(eval), QueryRuntimeException); } } -TEST(ExpressionEvaluator, MapIndexing) { - AstStorage storage; - NoContextExpressionEvaluator eval; - database::SingleNode db; - auto dba_ptr = db.Access(); - auto &dba = *dba_ptr; +TEST_F(ExpressionEvaluatorTest, MapIndexing) { auto *map_literal = storage.Create( std::unordered_map, - Expression *>{ - {PROPERTY_PAIR("a"), storage.Create(1)}, - {PROPERTY_PAIR("b"), storage.Create(2)}, - {PROPERTY_PAIR("c"), storage.Create(3)}}); + Expression *>{{std::make_pair("a", dba->Property("a")), + storage.Create(1)}, + {std::make_pair("b", dba->Property("b")), + storage.Create(2)}, + {std::make_pair("c", dba->Property("c")), + storage.Create(3)}}); { // Legal indexing. auto *op = storage.Create( map_literal, storage.Create("b")); - auto value = op->Accept(eval.eval); - EXPECT_EQ(value.Value(), 2); + auto value = op->Accept(eval); + EXPECT_EQ(value.ValueInt(), 2); } { // Legal indexing, non-existing key. auto *op = storage.Create( map_literal, storage.Create("z")); - auto value = op->Accept(eval.eval); + auto value = op->Accept(eval); EXPECT_TRUE(value.IsNull()); } { // Wrong key type. auto *op = storage.Create( map_literal, storage.Create(42)); - EXPECT_THROW(op->Accept(eval.eval), QueryRuntimeException); + EXPECT_THROW(op->Accept(eval), QueryRuntimeException); } { // Indexing with Null. auto *op = storage.Create( map_literal, storage.Create(TypedValue::Null)); - auto value = op->Accept(eval.eval); + auto value = op->Accept(eval); EXPECT_TRUE(value.IsNull()); } } -TEST(ExpressionEvaluator, VertexAndEdgeIndexing) { - AstStorage storage; - NoContextExpressionEvaluator eval; - auto &dba = *eval.dba; - - auto edge_type = dba.EdgeType("edge_type"); - auto prop = dba.Property("prop"); - auto v1 = dba.InsertVertex(); - auto e11 = dba.InsertEdge(v1, v1, edge_type); +TEST_F(ExpressionEvaluatorTest, VertexAndEdgeIndexing) { + auto edge_type = dba->EdgeType("edge_type"); + auto prop = dba->Property("prop"); + auto v1 = dba->InsertVertex(); + auto e11 = dba->InsertEdge(v1, v1, edge_type); v1.PropsSet(prop, 42); e11.PropsSet(prop, 43); @@ -470,53 +411,51 @@ TEST(ExpressionEvaluator, VertexAndEdgeIndexing) { // Legal indexing. auto *op1 = storage.Create( vertex_literal, storage.Create("prop")); - auto value1 = op1->Accept(eval.eval); - EXPECT_EQ(value1.Value(), 42); + auto value1 = op1->Accept(eval); + EXPECT_EQ(value1.ValueInt(), 42); auto *op2 = storage.Create( edge_literal, storage.Create("prop")); - auto value2 = op2->Accept(eval.eval); - EXPECT_EQ(value2.Value(), 43); + auto value2 = op2->Accept(eval); + EXPECT_EQ(value2.ValueInt(), 43); } { // Legal indexing, non-existing key. auto *op1 = storage.Create( vertex_literal, storage.Create("blah")); - auto value1 = op1->Accept(eval.eval); + auto value1 = op1->Accept(eval); EXPECT_TRUE(value1.IsNull()); auto *op2 = storage.Create( edge_literal, storage.Create("blah")); - auto value2 = op2->Accept(eval.eval); + auto value2 = op2->Accept(eval); EXPECT_TRUE(value2.IsNull()); } { // Wrong key type. auto *op1 = storage.Create( vertex_literal, storage.Create(1)); - EXPECT_THROW(op1->Accept(eval.eval), QueryRuntimeException); + EXPECT_THROW(op1->Accept(eval), QueryRuntimeException); auto *op2 = storage.Create( edge_literal, storage.Create(1)); - EXPECT_THROW(op2->Accept(eval.eval), QueryRuntimeException); + EXPECT_THROW(op2->Accept(eval), QueryRuntimeException); } { // Indexing with Null. auto *op1 = storage.Create( vertex_literal, storage.Create(TypedValue::Null)); - auto value1 = op1->Accept(eval.eval); + auto value1 = op1->Accept(eval); EXPECT_TRUE(value1.IsNull()); auto *op2 = storage.Create( edge_literal, storage.Create(TypedValue::Null)); - auto value2 = op2->Accept(eval.eval); + auto value2 = op2->Accept(eval); EXPECT_TRUE(value2.IsNull()); } } -TEST(ExpressionEvaluator, ListSlicingOperator) { - AstStorage storage; - NoContextExpressionEvaluator eval; +TEST_F(ExpressionEvaluatorTest, ListSlicingOperator) { auto *list_literal = storage.Create(std::vector{ storage.Create(1), storage.Create(2), storage.Create(3), @@ -524,8 +463,8 @@ TEST(ExpressionEvaluator, ListSlicingOperator) { auto extract_ints = [](TypedValue list) { std::vector int_list; - for (auto x : list.Value>()) { - int_list.push_back(x.Value()); + for (auto x : list.ValueList()) { + int_list.push_back(x.ValueInt()); } return int_list; }; @@ -534,7 +473,7 @@ TEST(ExpressionEvaluator, ListSlicingOperator) { auto *op = storage.Create( list_literal, storage.Create(2), storage.Create(4)); - auto value = op->Accept(eval.eval); + auto value = op->Accept(eval); EXPECT_THAT(extract_ints(value), ElementsAre(3, 4)); } { @@ -542,7 +481,7 @@ TEST(ExpressionEvaluator, ListSlicingOperator) { auto *op = storage.Create( list_literal, storage.Create(2), storage.Create(-1)); - auto value = op->Accept(eval.eval); + auto value = op->Accept(eval); EXPECT_THAT(extract_ints(value), ElementsAre(3)); } { @@ -550,7 +489,7 @@ TEST(ExpressionEvaluator, ListSlicingOperator) { auto *op = storage.Create( list_literal, storage.Create(2), storage.Create(-4)); - auto value = op->Accept(eval.eval); + auto value = op->Accept(eval); EXPECT_THAT(extract_ints(value), ElementsAre()); } { @@ -558,21 +497,21 @@ TEST(ExpressionEvaluator, ListSlicingOperator) { auto *op = storage.Create( list_literal, storage.Create(-100), storage.Create(10)); - auto value = op->Accept(eval.eval); + auto value = op->Accept(eval); EXPECT_THAT(extract_ints(value), ElementsAre(1, 2, 3, 4)); } { // Lower bound undefined. auto *op = storage.Create( list_literal, nullptr, storage.Create(3)); - auto value = op->Accept(eval.eval); + auto value = op->Accept(eval); EXPECT_THAT(extract_ints(value), ElementsAre(1, 2, 3)); } { // Upper bound undefined. auto *op = storage.Create( list_literal, storage.Create(-2), nullptr); - auto value = op->Accept(eval.eval); + auto value = op->Accept(eval); EXPECT_THAT(extract_ints(value), ElementsAre(3, 4)); } { @@ -580,36 +519,36 @@ TEST(ExpressionEvaluator, ListSlicingOperator) { auto *op = storage.Create( list_literal, storage.Create(TypedValue::Null), storage.Create("mirko")); - EXPECT_THROW(op->Accept(eval.eval), QueryRuntimeException); + EXPECT_THROW(op->Accept(eval), QueryRuntimeException); } { // List of illegal type. auto *op = storage.Create( storage.Create("a"), storage.Create(-2), nullptr); - EXPECT_THROW(op->Accept(eval.eval), QueryRuntimeException); + EXPECT_THROW(op->Accept(eval), QueryRuntimeException); } { // Null value list with undefined upper bound. auto *op = storage.Create( storage.Create(TypedValue::Null), storage.Create(-2), nullptr); - auto value = op->Accept(eval.eval); - EXPECT_EQ(value.type(), TypedValue::Type::Null); + auto value = op->Accept(eval); + EXPECT_TRUE(value.IsNull()); + ; } { // Null value index. auto *op = storage.Create( list_literal, storage.Create(-2), storage.Create(TypedValue::Null)); - auto value = op->Accept(eval.eval); - EXPECT_EQ(value.type(), TypedValue::Type::Null); + auto value = op->Accept(eval); + EXPECT_TRUE(value.IsNull()); + ; } } -TEST(ExpressionEvaluator, IfOperator) { - AstStorage storage; - NoContextExpressionEvaluator eval; +TEST_F(ExpressionEvaluatorTest, IfOperator) { auto *then_expression = storage.Create(10); auto *else_expression = storage.Create(20); { @@ -618,8 +557,8 @@ TEST(ExpressionEvaluator, IfOperator) { storage.Create(2)); auto *op = storage.Create(condition_true, then_expression, else_expression); - auto value = op->Accept(eval.eval); - ASSERT_EQ(value.Value(), 10); + auto value = op->Accept(eval); + ASSERT_EQ(value.ValueInt(), 10); } { auto *condition_false = @@ -627,8 +566,8 @@ TEST(ExpressionEvaluator, IfOperator) { storage.Create(3)); auto *op = storage.Create(condition_false, then_expression, else_expression); - auto value = op->Accept(eval.eval); - ASSERT_EQ(value.Value(), 20); + auto value = op->Accept(eval); + ASSERT_EQ(value.ValueInt(), 20); } { auto *condition_exception = @@ -636,273 +575,379 @@ TEST(ExpressionEvaluator, IfOperator) { storage.Create(3)); auto *op = storage.Create(condition_exception, then_expression, else_expression); - ASSERT_THROW(op->Accept(eval.eval), QueryRuntimeException); + ASSERT_THROW(op->Accept(eval), QueryRuntimeException); } } -TEST(ExpressionEvaluator, NotOperator) { - AstStorage storage; - NoContextExpressionEvaluator eval; +TEST_F(ExpressionEvaluatorTest, NotOperator) { auto *op = storage.Create(storage.Create(false)); - auto value = op->Accept(eval.eval); - ASSERT_EQ(value.Value(), true); + auto value = op->Accept(eval); + ASSERT_EQ(value.ValueBool(), true); } -TEST(ExpressionEvaluator, UnaryPlusOperator) { - AstStorage storage; - NoContextExpressionEvaluator eval; +TEST_F(ExpressionEvaluatorTest, UnaryPlusOperator) { auto *op = storage.Create(storage.Create(5)); - auto value = op->Accept(eval.eval); - ASSERT_EQ(value.Value(), 5); + auto value = op->Accept(eval); + ASSERT_EQ(value.ValueInt(), 5); } -TEST(ExpressionEvaluator, UnaryMinusOperator) { - AstStorage storage; - NoContextExpressionEvaluator eval; +TEST_F(ExpressionEvaluatorTest, UnaryMinusOperator) { auto *op = storage.Create(storage.Create(5)); - auto value = op->Accept(eval.eval); - ASSERT_EQ(value.Value(), -5); + auto value = op->Accept(eval); + ASSERT_EQ(value.ValueInt(), -5); } -TEST(ExpressionEvaluator, IsNullOperator) { - AstStorage storage; - NoContextExpressionEvaluator eval; +TEST_F(ExpressionEvaluatorTest, IsNullOperator) { auto *op = storage.Create(storage.Create(1)); - auto val1 = op->Accept(eval.eval); - ASSERT_EQ(val1.Value(), false); + auto val1 = op->Accept(eval); + ASSERT_EQ(val1.ValueBool(), false); op = storage.Create( storage.Create(TypedValue::Null)); - auto val2 = op->Accept(eval.eval); - ASSERT_EQ(val2.Value(), true); + auto val2 = op->Accept(eval); + ASSERT_EQ(val2.ValueBool(), true); } -class ExpressionEvaluatorPropertyLookup : public testing::Test { - protected: - AstStorage storage; - NoContextExpressionEvaluator eval; - database::SingleNode db; - std::unique_ptr dba_ptr{db.Access()}; - database::GraphDbAccessor &dba{*dba_ptr}; - std::pair prop_age = PROPERTY_PAIR("age"); - std::pair prop_height = - PROPERTY_PAIR("height"); - Expression *identifier = storage.Create("element"); - Symbol symbol = eval.ctx.symbol_table_.CreateSymbol("element", true); - - void SetUp() { eval.ctx.symbol_table_[*identifier] = symbol; } - - auto Value(std::pair property) { - auto *op = storage.Create(identifier, property); - return op->Accept(eval.eval); - } -}; - -TEST_F(ExpressionEvaluatorPropertyLookup, Vertex) { - auto v1 = dba.InsertVertex(); - v1.PropsSet(prop_age.second, 10); - eval.frame[symbol] = v1; - EXPECT_EQ(Value(prop_age).Value(), 10); - EXPECT_TRUE(Value(prop_height).IsNull()); -} - -TEST_F(ExpressionEvaluatorPropertyLookup, Edge) { - auto v1 = dba.InsertVertex(); - auto v2 = dba.InsertVertex(); - auto e12 = dba.InsertEdge(v1, v2, dba.EdgeType("edge_type")); - e12.PropsSet(prop_age.second, 10); - eval.frame[symbol] = e12; - EXPECT_EQ(Value(prop_age).Value(), 10); - EXPECT_TRUE(Value(prop_height).IsNull()); -} - -TEST_F(ExpressionEvaluatorPropertyLookup, Null) { - eval.frame[symbol] = TypedValue::Null; - EXPECT_TRUE(Value(prop_age).IsNull()); -} - -TEST_F(ExpressionEvaluatorPropertyLookup, MapLiteral) { - eval.frame[symbol] = std::map{{prop_age.first, 10}}; - EXPECT_EQ(Value(prop_age).Value(), 10); - EXPECT_TRUE(Value(prop_height).IsNull()); -} - -TEST(ExpressionEvaluator, LabelsTest) { - AstStorage storage; - NoContextExpressionEvaluator eval; - database::SingleNode db; - auto dba = db.Access(); +TEST_F(ExpressionEvaluatorTest, LabelsTest) { auto v1 = dba->InsertVertex(); v1.add_label(dba->Label("ANIMAL")); v1.add_label(dba->Label("DOG")); v1.add_label(dba->Label("NICE_DOG")); auto *identifier = storage.Create("n"); - auto node_symbol = eval.ctx.symbol_table_.CreateSymbol("n", true); - eval.ctx.symbol_table_[*identifier] = node_symbol; - eval.frame[node_symbol] = v1; + auto node_symbol = symbol_table.CreateSymbol("n", true); + symbol_table[*identifier] = node_symbol; + frame[node_symbol] = v1; { auto *op = storage.Create( identifier, std::vector{dba->Label("DOG"), dba->Label("ANIMAL")}); - auto value = op->Accept(eval.eval); - EXPECT_EQ(value.Value(), true); + auto value = op->Accept(eval); + EXPECT_EQ(value.ValueBool(), true); } { auto *op = storage.Create( identifier, std::vector{dba->Label("DOG"), dba->Label("BAD_DOG"), dba->Label("ANIMAL")}); - auto value = op->Accept(eval.eval); - EXPECT_EQ(value.Value(), false); + auto value = op->Accept(eval); + EXPECT_EQ(value.ValueBool(), false); } { - eval.frame[node_symbol] = TypedValue::Null; + frame[node_symbol] = TypedValue::Null; auto *op = storage.Create( identifier, std::vector{dba->Label("DOG"), dba->Label("BAD_DOG"), dba->Label("ANIMAL")}); - auto value = op->Accept(eval.eval); + auto value = op->Accept(eval); EXPECT_TRUE(value.IsNull()); } } -TEST(ExpressionEvaluator, Aggregation) { - AstStorage storage; +TEST_F(ExpressionEvaluatorTest, Aggregation) { auto aggr = storage.Create(storage.Create(42), nullptr, Aggregation::Op::COUNT); - database::SingleNode db; - auto dba = db.Access(); - Context ctx(*dba); - auto aggr_sym = ctx.symbol_table_.CreateSymbol("aggr", true); - ctx.symbol_table_[*aggr] = aggr_sym; - Frame frame{ctx.symbol_table_.max_position()}; + auto aggr_sym = symbol_table.CreateSymbol("aggr", true); + symbol_table[*aggr] = aggr_sym; frame[aggr_sym] = TypedValue(1); Parameters parameters; - ExpressionEvaluator eval{frame, &ctx, GraphView::OLD}; auto value = aggr->Accept(eval); - EXPECT_EQ(value.Value(), 1); + EXPECT_EQ(value.ValueInt(), 1); } -TEST(ExpressionEvaluator, ListLiteral) { - AstStorage storage; - NoContextExpressionEvaluator eval; +TEST_F(ExpressionEvaluatorTest, ListLiteral) { auto *list_literal = storage.Create( std::vector{storage.Create(1), storage.Create("bla"), storage.Create(true)}); - TypedValue result = list_literal->Accept(eval.eval); - ASSERT_EQ(result.type(), TypedValue::Type::List); - auto &result_elems = result.Value>(); + TypedValue result = list_literal->Accept(eval); + ASSERT_TRUE(result.IsList()); + auto &result_elems = result.ValueList(); ASSERT_EQ(3, result_elems.size()); - EXPECT_EQ(result_elems[0].type(), TypedValue::Type::Int); - EXPECT_EQ(result_elems[1].type(), TypedValue::Type::String); - EXPECT_EQ(result_elems[2].type(), TypedValue::Type::Bool); + EXPECT_TRUE(result_elems[0].IsInt()); + ; + EXPECT_TRUE(result_elems[1].IsString()); + ; + EXPECT_TRUE(result_elems[2].IsBool()); + ; } -TEST(ExpressionEvaluator, FunctionCoalesce) { +TEST_F(ExpressionEvaluatorTest, ParameterLookup) { + parameters.Add(0, 42); + auto *param_lookup = storage.Create(0); + auto value = param_lookup->Accept(eval); + ASSERT_TRUE(value.IsInt()); + EXPECT_EQ(value.ValueInt(), 42); +} + +TEST_F(ExpressionEvaluatorTest, All) { + AstStorage storage; + auto *ident_x = IDENT("x"); + auto *all = + ALL("x", LIST(LITERAL(1), LITERAL(2)), WHERE(EQ(ident_x, LITERAL(1)))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + symbol_table[*all->identifier_] = x_sym; + symbol_table[*ident_x] = x_sym; + auto value = all->Accept(eval); + ASSERT_TRUE(value.IsBool()); + EXPECT_FALSE(value.ValueBool()); +} + +TEST_F(ExpressionEvaluatorTest, FunctionAllNullList) { + AstStorage storage; + auto *all = ALL("x", LITERAL(TypedValue::Null), WHERE(LITERAL(true))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + symbol_table[*all->identifier_] = x_sym; + auto value = all->Accept(eval); + EXPECT_TRUE(value.IsNull()); +} + +TEST_F(ExpressionEvaluatorTest, FunctionAllWhereWrongType) { + AstStorage storage; + auto *all = ALL("x", LIST(LITERAL(1)), WHERE(LITERAL(2))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + symbol_table[*all->identifier_] = x_sym; + EXPECT_THROW(all->Accept(eval), QueryRuntimeException); +} + +TEST_F(ExpressionEvaluatorTest, FunctionSingle) { + AstStorage storage; + auto *ident_x = IDENT("x"); + auto *single = + SINGLE("x", LIST(LITERAL(1), LITERAL(2)), WHERE(EQ(ident_x, LITERAL(1)))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + symbol_table[*single->identifier_] = x_sym; + symbol_table[*ident_x] = x_sym; + auto value = single->Accept(eval); + ASSERT_TRUE(value.IsBool()); + EXPECT_TRUE(value.ValueBool()); +} + +TEST_F(ExpressionEvaluatorTest, FunctionSingle2) { + AstStorage storage; + auto *ident_x = IDENT("x"); + auto *single = SINGLE("x", LIST(LITERAL(1), LITERAL(2)), + WHERE(GREATER(ident_x, LITERAL(0)))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + symbol_table[*single->identifier_] = x_sym; + symbol_table[*ident_x] = x_sym; + auto value = single->Accept(eval); + ASSERT_TRUE(value.IsBool()); + EXPECT_FALSE(value.ValueBool()); +} + +TEST_F(ExpressionEvaluatorTest, FunctionSingleNullList) { + AstStorage storage; + auto *single = SINGLE("x", LITERAL(TypedValue::Null), WHERE(LITERAL(true))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + symbol_table[*single->identifier_] = x_sym; + auto value = single->Accept(eval); + EXPECT_TRUE(value.IsNull()); +} + +TEST_F(ExpressionEvaluatorTest, FunctionReduce) { + AstStorage storage; + auto *ident_sum = IDENT("sum"); + auto *ident_x = IDENT("x"); + auto *reduce = REDUCE("sum", LITERAL(0), "x", LIST(LITERAL(1), LITERAL(2)), + ADD(ident_sum, ident_x)); + const auto sum_sym = symbol_table.CreateSymbol("sum", true); + symbol_table[*reduce->accumulator_] = sum_sym; + symbol_table[*ident_sum] = sum_sym; + const auto x_sym = symbol_table.CreateSymbol("x", true); + symbol_table[*reduce->identifier_] = x_sym; + symbol_table[*ident_x] = x_sym; + auto value = reduce->Accept(eval); + ASSERT_TRUE(value.IsInt()); + EXPECT_EQ(value.ValueInt(), 3); +} + +TEST_F(ExpressionEvaluatorTest, FunctionExtract) { + AstStorage storage; + auto *ident_x = IDENT("x"); + auto *extract = + EXTRACT("x", LIST(LITERAL(1), LITERAL(2), LITERAL(TypedValue::Null)), + ADD(ident_x, LITERAL(1))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + symbol_table[*extract->identifier_] = x_sym; + symbol_table[*ident_x] = x_sym; + auto value = extract->Accept(eval); + EXPECT_TRUE(value.IsList()); + ; + auto result = value.ValueList(); + EXPECT_EQ(result[0].ValueInt(), 2); + EXPECT_EQ(result[1].ValueInt(), 3); + EXPECT_TRUE(result[2].IsNull()); +} + +TEST_F(ExpressionEvaluatorTest, FunctionExtractNull) { + AstStorage storage; + auto *ident_x = IDENT("x"); + auto *extract = + EXTRACT("x", LITERAL(TypedValue::Null), ADD(ident_x, LITERAL(1))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + symbol_table[*extract->identifier_] = x_sym; + symbol_table[*ident_x] = x_sym; + auto value = extract->Accept(eval); + EXPECT_TRUE(value.IsNull()); +} + +TEST_F(ExpressionEvaluatorTest, FunctionExtractExceptions) { + AstStorage storage; + auto *ident_x = IDENT("x"); + auto *extract = EXTRACT("x", LITERAL("bla"), ADD(ident_x, LITERAL(1))); + const auto x_sym = symbol_table.CreateSymbol("x", true); + symbol_table[*extract->identifier_] = x_sym; + symbol_table[*ident_x] = x_sym; + EXPECT_THROW(extract->Accept(eval), QueryRuntimeException); +} + +class ExpressionEvaluatorPropertyLookup : public ExpressionEvaluatorTest { + protected: + std::pair prop_age = + std::make_pair("age", dba->Property("age")); + std::pair prop_height = + std::make_pair("height", dba->Property("height")); + Expression *identifier = storage.Create("element"); + Symbol symbol = symbol_table.CreateSymbol("element", true); + + void SetUp() { symbol_table[*identifier] = symbol; } + + auto Value(std::pair property) { + auto *op = storage.Create(identifier, property); + return op->Accept(eval); + } +}; + +TEST_F(ExpressionEvaluatorPropertyLookup, Vertex) { + auto v1 = dba->InsertVertex(); + v1.PropsSet(prop_age.second, 10); + frame[symbol] = v1; + EXPECT_EQ(Value(prop_age).ValueInt(), 10); + EXPECT_TRUE(Value(prop_height).IsNull()); +} + +TEST_F(ExpressionEvaluatorPropertyLookup, Edge) { + auto v1 = dba->InsertVertex(); + auto v2 = dba->InsertVertex(); + auto e12 = dba->InsertEdge(v1, v2, dba->EdgeType("edge_type")); + e12.PropsSet(prop_age.second, 10); + frame[symbol] = e12; + EXPECT_EQ(Value(prop_age).ValueInt(), 10); + EXPECT_TRUE(Value(prop_height).IsNull()); +} + +TEST_F(ExpressionEvaluatorPropertyLookup, Null) { + frame[symbol] = TypedValue::Null; + EXPECT_TRUE(Value(prop_age).IsNull()); +} + +TEST_F(ExpressionEvaluatorPropertyLookup, MapLiteral) { + frame[symbol] = std::map{{prop_age.first, 10}}; + EXPECT_EQ(Value(prop_age).ValueInt(), 10); + EXPECT_TRUE(Value(prop_height).IsNull()); +} + +class FunctionTest : public ExpressionEvaluatorTest { + protected: + TypedValue EvaluateFunction(const std::string &function_name, + const std::vector &args) { + std::vector expressions; + for (const auto &arg : args) { + expressions.push_back(storage.Create(arg)); + } + auto *op = storage.Create(function_name, expressions); + return op->Accept(eval); + } +}; + +TEST_F(FunctionTest, Coalesce) { ASSERT_THROW(EvaluateFunction("COALESCE", {}), QueryRuntimeException); - ASSERT_EQ( - EvaluateFunction("COALESCE", {TypedValue::Null, TypedValue::Null}).type(), - TypedValue::Type::Null); - ASSERT_EQ( - EvaluateFunction("COALESCE", {TypedValue::Null, 2, 3}).Value(), - 2); + ASSERT_TRUE(EvaluateFunction("COALESCE", {TypedValue::Null, TypedValue::Null}) + .IsNull()); + ASSERT_EQ(EvaluateFunction("COALESCE", {TypedValue::Null, 2, 3}).ValueInt(), + 2); } -TEST(ExpressionEvaluator, FunctionEndNode) { +TEST_F(FunctionTest, EndNode) { ASSERT_THROW(EvaluateFunction("ENDNODE", {}), QueryRuntimeException); - ASSERT_EQ(EvaluateFunction("ENDNODE", {TypedValue::Null}).type(), - TypedValue::Type::Null); - database::SingleNode db; - auto dba = db.Access(); + ASSERT_TRUE(EvaluateFunction("ENDNODE", {TypedValue::Null}).IsNull()); auto v1 = dba->InsertVertex(); v1.add_label(dba->Label("label1")); auto v2 = dba->InsertVertex(); v2.add_label(dba->Label("label2")); auto e = dba->InsertEdge(v1, v2, dba->EdgeType("t")); ASSERT_TRUE(EvaluateFunction("ENDNODE", {e}) - .Value() + .ValueVertex() .has_label(dba->Label("label2"))); ASSERT_THROW(EvaluateFunction("ENDNODE", {2}), QueryRuntimeException); } -TEST(ExpressionEvaluator, FunctionHead) { +TEST_F(FunctionTest, Head) { ASSERT_THROW(EvaluateFunction("HEAD", {}), QueryRuntimeException); - ASSERT_EQ(EvaluateFunction("HEAD", {TypedValue::Null}).type(), - TypedValue::Type::Null); + ASSERT_TRUE(EvaluateFunction("HEAD", {TypedValue::Null}).IsNull()); std::vector arguments; arguments.push_back(std::vector{3, 4, 5}); - ASSERT_EQ(EvaluateFunction("HEAD", arguments).Value(), 3); - arguments[0].Value>().clear(); - ASSERT_EQ(EvaluateFunction("HEAD", arguments).type(), TypedValue::Type::Null); + ASSERT_EQ(EvaluateFunction("HEAD", arguments).ValueInt(), 3); + arguments[0].ValueList().clear(); + ASSERT_TRUE(EvaluateFunction("HEAD", arguments).IsNull()); ASSERT_THROW(EvaluateFunction("HEAD", {2}), QueryRuntimeException); } -TEST(ExpressionEvaluator, FunctionProperties) { +TEST_F(FunctionTest, Properties) { ASSERT_THROW(EvaluateFunction("PROPERTIES", {}), QueryRuntimeException); - ASSERT_EQ(EvaluateFunction("PROPERTIES", {TypedValue::Null}).type(), - TypedValue::Type::Null); - NoContextExpressionEvaluator eval; - auto &dba = *eval.dba; - auto v1 = dba.InsertVertex(); - v1.PropsSet(dba.Property("height"), 5); - v1.PropsSet(dba.Property("age"), 10); - auto v2 = dba.InsertVertex(); - auto e = dba.InsertEdge(v1, v2, dba.EdgeType("type1")); - e.PropsSet(dba.Property("height"), 3); - e.PropsSet(dba.Property("age"), 15); + ASSERT_TRUE(EvaluateFunction("PROPERTIES", {TypedValue::Null}).IsNull()); + auto v1 = dba->InsertVertex(); + v1.PropsSet(dba->Property("height"), 5); + v1.PropsSet(dba->Property("age"), 10); + auto v2 = dba->InsertVertex(); + auto e = dba->InsertEdge(v1, v2, dba->EdgeType("type1")); + e.PropsSet(dba->Property("height"), 3); + e.PropsSet(dba->Property("age"), 15); auto prop_values_to_int = [](TypedValue t) { std::unordered_map properties; for (auto property : t.Value>()) { - properties[property.first] = property.second.Value(); + properties[property.first] = property.second.ValueInt(); } return properties; }; - ASSERT_THAT( - prop_values_to_int(EvaluateFunction("PROPERTIES", {v1}, &eval.ctx)), - UnorderedElementsAre(testing::Pair("height", 5), - testing::Pair("age", 10))); - ASSERT_THAT( - prop_values_to_int(EvaluateFunction("PROPERTIES", {e}, &eval.ctx)), - UnorderedElementsAre(testing::Pair("height", 3), - testing::Pair("age", 15))); - ASSERT_THROW(EvaluateFunction("PROPERTIES", {2}, &eval.ctx), - QueryRuntimeException); + ASSERT_THAT(prop_values_to_int(EvaluateFunction("PROPERTIES", {v1})), + UnorderedElementsAre(testing::Pair("height", 5), + testing::Pair("age", 10))); + ASSERT_THAT(prop_values_to_int(EvaluateFunction("PROPERTIES", {e})), + UnorderedElementsAre(testing::Pair("height", 3), + testing::Pair("age", 15))); + ASSERT_THROW(EvaluateFunction("PROPERTIES", {2}), QueryRuntimeException); } -TEST(ExpressionEvaluator, FunctionLast) { +TEST_F(FunctionTest, Last) { ASSERT_THROW(EvaluateFunction("LAST", {}), QueryRuntimeException); - ASSERT_EQ(EvaluateFunction("LAST", {TypedValue::Null}).type(), - TypedValue::Type::Null); + ASSERT_TRUE(EvaluateFunction("LAST", {TypedValue::Null}).IsNull()); std::vector arguments; arguments.push_back(std::vector{3, 4, 5}); - ASSERT_EQ(EvaluateFunction("LAST", arguments).Value(), 5); - arguments[0].Value>().clear(); - ASSERT_EQ(EvaluateFunction("LAST", arguments).type(), TypedValue::Type::Null); + ASSERT_EQ(EvaluateFunction("LAST", arguments).ValueInt(), 5); + arguments[0].ValueList().clear(); + ASSERT_TRUE(EvaluateFunction("LAST", arguments).IsNull()); ASSERT_THROW(EvaluateFunction("LAST", {5}), QueryRuntimeException); } -TEST(ExpressionEvaluator, FunctionSize) { +TEST_F(FunctionTest, Size) { ASSERT_THROW(EvaluateFunction("SIZE", {}), QueryRuntimeException); - ASSERT_EQ(EvaluateFunction("SIZE", {TypedValue::Null}).type(), - TypedValue::Type::Null); + ASSERT_TRUE(EvaluateFunction("SIZE", {TypedValue::Null}).IsNull()); std::vector arguments; arguments.push_back(std::vector{3, 4, 5}); - ASSERT_EQ(EvaluateFunction("SIZE", arguments).Value(), 3); - ASSERT_EQ(EvaluateFunction("SIZE", {"john"}).Value(), 4); + ASSERT_EQ(EvaluateFunction("SIZE", arguments).ValueInt(), 3); + ASSERT_EQ(EvaluateFunction("SIZE", {"john"}).ValueInt(), 4); ASSERT_EQ(EvaluateFunction("SIZE", {std::map{ {"a", 5}, {"b", true}, {"c", "123"}}}) - .Value(), + .ValueInt(), 3); ASSERT_THROW(EvaluateFunction("SIZE", {5}), QueryRuntimeException); - database::SingleNode db; - auto dba = db.Access(); auto v0 = dba->InsertVertex(); query::Path path(v0); EXPECT_EQ(EvaluateFunction("SIZE", {path}).ValueInt(), 0); @@ -912,133 +957,110 @@ TEST(ExpressionEvaluator, FunctionSize) { EXPECT_EQ(EvaluateFunction("SIZE", {path}).ValueInt(), 1); } -TEST(ExpressionEvaluator, FunctionStartNode) { +TEST_F(FunctionTest, StartNode) { ASSERT_THROW(EvaluateFunction("STARTNODE", {}), QueryRuntimeException); - ASSERT_EQ(EvaluateFunction("STARTNODE", {TypedValue::Null}).type(), - TypedValue::Type::Null); - database::SingleNode db; - auto dba = db.Access(); + ASSERT_TRUE(EvaluateFunction("STARTNODE", {TypedValue::Null}).IsNull()); auto v1 = dba->InsertVertex(); v1.add_label(dba->Label("label1")); auto v2 = dba->InsertVertex(); v2.add_label(dba->Label("label2")); auto e = dba->InsertEdge(v1, v2, dba->EdgeType("t")); ASSERT_TRUE(EvaluateFunction("STARTNODE", {e}) - .Value() + .ValueVertex() .has_label(dba->Label("label1"))); ASSERT_THROW(EvaluateFunction("STARTNODE", {2}), QueryRuntimeException); } -TEST(ExpressionEvaluator, FunctionDegree) { +TEST_F(FunctionTest, Degree) { ASSERT_THROW(EvaluateFunction("DEGREE", {}), QueryRuntimeException); - ASSERT_EQ(EvaluateFunction("DEGREE", {TypedValue::Null}).type(), - TypedValue::Type::Null); - database::SingleNode db; - auto dba = db.Access(); + ASSERT_TRUE(EvaluateFunction("DEGREE", {TypedValue::Null}).IsNull()); auto v1 = dba->InsertVertex(); auto v2 = dba->InsertVertex(); auto v3 = dba->InsertVertex(); auto e12 = dba->InsertEdge(v1, v2, dba->EdgeType("t")); dba->InsertEdge(v3, v2, dba->EdgeType("t")); - ASSERT_EQ(EvaluateFunction("DEGREE", {v1}).Value(), 1); - ASSERT_EQ(EvaluateFunction("DEGREE", {v2}).Value(), 2); - ASSERT_EQ(EvaluateFunction("DEGREE", {v3}).Value(), 1); + ASSERT_EQ(EvaluateFunction("DEGREE", {v1}).ValueInt(), 1); + ASSERT_EQ(EvaluateFunction("DEGREE", {v2}).ValueInt(), 2); + ASSERT_EQ(EvaluateFunction("DEGREE", {v3}).ValueInt(), 1); ASSERT_THROW(EvaluateFunction("DEGREE", {2}), QueryRuntimeException); ASSERT_THROW(EvaluateFunction("DEGREE", {e12}), QueryRuntimeException); } -TEST(ExpressionEvaluator, FunctionToBoolean) { +TEST_F(FunctionTest, ToBoolean) { ASSERT_THROW(EvaluateFunction("TOBOOLEAN", {}), QueryRuntimeException); - ASSERT_EQ(EvaluateFunction("TOBOOLEAN", {TypedValue::Null}).type(), - TypedValue::Type::Null); + ASSERT_TRUE(EvaluateFunction("TOBOOLEAN", {TypedValue::Null}).IsNull()); ASSERT_EQ(EvaluateFunction("TOBOOLEAN", {123}).ValueBool(), true); ASSERT_EQ(EvaluateFunction("TOBOOLEAN", {-213}).ValueBool(), true); ASSERT_EQ(EvaluateFunction("TOBOOLEAN", {0}).ValueBool(), false); - ASSERT_EQ(EvaluateFunction("TOBOOLEAN", {" trUE \n\t"}).Value(), true); - ASSERT_EQ(EvaluateFunction("TOBOOLEAN", {"\n\tFalsE "}).Value(), false); - ASSERT_EQ(EvaluateFunction("TOBOOLEAN", {"\n\tFALSEA "}).type(), - TypedValue::Type::Null); - ASSERT_EQ(EvaluateFunction("TOBOOLEAN", {true}).Value(), true); - ASSERT_EQ(EvaluateFunction("TOBOOLEAN", {false}).Value(), false); + ASSERT_EQ(EvaluateFunction("TOBOOLEAN", {" trUE \n\t"}).ValueBool(), true); + ASSERT_EQ(EvaluateFunction("TOBOOLEAN", {"\n\tFalsE"}).ValueBool(), false); + ASSERT_TRUE(EvaluateFunction("TOBOOLEAN", {"\n\tFALSEA "}).IsNull()); + ASSERT_EQ(EvaluateFunction("TOBOOLEAN", {true}).ValueBool(), true); + ASSERT_EQ(EvaluateFunction("TOBOOLEAN", {false}).ValueBool(), false); } -TEST(ExpressionEvaluator, FunctionToFloat) { +TEST_F(FunctionTest, ToFloat) { ASSERT_THROW(EvaluateFunction("TOFLOAT", {}), QueryRuntimeException); - ASSERT_EQ(EvaluateFunction("TOFLOAT", {TypedValue::Null}).type(), - TypedValue::Type::Null); - ASSERT_EQ(EvaluateFunction("TOFLOAT", {" -3.5 \n\t"}).Value(), -3.5); - ASSERT_EQ(EvaluateFunction("TOFLOAT", {"\n\t0.5e-1"}).Value(), 0.05); - ASSERT_EQ(EvaluateFunction("TOFLOAT", {"\n\t3.4e-3X "}).type(), - TypedValue::Type::Null); - ASSERT_EQ(EvaluateFunction("TOFLOAT", {-3.5}).Value(), -3.5); - ASSERT_EQ(EvaluateFunction("TOFLOAT", {-3}).Value(), -3.0); + ASSERT_TRUE(EvaluateFunction("TOFLOAT", {TypedValue::Null}).IsNull()); + ASSERT_EQ(EvaluateFunction("TOFLOAT", {" -3.5 \n\t"}).ValueDouble(), -3.5); + ASSERT_EQ(EvaluateFunction("TOFLOAT", {"\n\t0.5e-1"}).ValueDouble(), 0.05); + ASSERT_TRUE(EvaluateFunction("TOFLOAT", {"\n\t3.4e-3X "}).IsNull()); + ASSERT_EQ(EvaluateFunction("TOFLOAT", {-3.5}).ValueDouble(), -3.5); + ASSERT_EQ(EvaluateFunction("TOFLOAT", {-3}).ValueDouble(), -3.0); ASSERT_THROW(EvaluateFunction("TOFLOAT", {true}), QueryRuntimeException); } -TEST(ExpressionEvaluator, FunctionToInteger) { +TEST_F(FunctionTest, ToInteger) { ASSERT_THROW(EvaluateFunction("TOINTEGER", {}), QueryRuntimeException); - ASSERT_EQ(EvaluateFunction("TOINTEGER", {TypedValue::Null}).type(), - TypedValue::Type::Null); - ASSERT_EQ(EvaluateFunction("TOINTEGER", {false}).Value(), 0); - ASSERT_EQ(EvaluateFunction("TOINTEGER", {true}).Value(), 1); - ASSERT_EQ(EvaluateFunction("TOINTEGER", {"\n\t3"}).Value(), 3); - ASSERT_EQ(EvaluateFunction("TOINTEGER", {" -3.5 \n\t"}).Value(), -3); - ASSERT_EQ(EvaluateFunction("TOINTEGER", {"\n\t3X "}).type(), - TypedValue::Type::Null); - ASSERT_EQ(EvaluateFunction("TOINTEGER", {-3.5}).Value(), -3); - ASSERT_EQ(EvaluateFunction("TOINTEGER", {3.5}).Value(), 3); + ASSERT_TRUE(EvaluateFunction("TOINTEGER", {TypedValue::Null}).IsNull()); + ASSERT_EQ(EvaluateFunction("TOINTEGER", {false}).ValueInt(), 0); + ASSERT_EQ(EvaluateFunction("TOINTEGER", {true}).ValueInt(), 1); + ASSERT_EQ(EvaluateFunction("TOINTEGER", {"\n\t3"}).ValueInt(), 3); + ASSERT_EQ(EvaluateFunction("TOINTEGER", {" -3.5 \n\t"}).ValueInt(), -3); + ASSERT_TRUE(EvaluateFunction("TOINTEGER", {"\n\t3X "}).IsNull()); + ASSERT_EQ(EvaluateFunction("TOINTEGER", {-3.5}).ValueInt(), -3); + ASSERT_EQ(EvaluateFunction("TOINTEGER", {3.5}).ValueInt(), 3); } -TEST(ExpressionEvaluator, FunctionType) { +TEST_F(FunctionTest, Type) { ASSERT_THROW(EvaluateFunction("TYPE", {}), QueryRuntimeException); - ASSERT_EQ(EvaluateFunction("TYPE", {TypedValue::Null}).type(), - TypedValue::Type::Null); - NoContextExpressionEvaluator eval; - auto &dba = *eval.dba; - auto v1 = dba.InsertVertex(); - v1.add_label(dba.Label("label1")); - auto v2 = dba.InsertVertex(); - v2.add_label(dba.Label("label2")); - auto e = dba.InsertEdge(v1, v2, dba.EdgeType("type1")); - ASSERT_EQ(EvaluateFunction("TYPE", {e}, &eval.ctx).Value(), - "type1"); - ASSERT_THROW(EvaluateFunction("TYPE", {2}, &eval.ctx), QueryRuntimeException); + ASSERT_TRUE(EvaluateFunction("TYPE", {TypedValue::Null}).IsNull()); + auto v1 = dba->InsertVertex(); + v1.add_label(dba->Label("label1")); + auto v2 = dba->InsertVertex(); + v2.add_label(dba->Label("label2")); + auto e = dba->InsertEdge(v1, v2, dba->EdgeType("type1")); + ASSERT_EQ(EvaluateFunction("TYPE", {e}).ValueString(), "type1"); + ASSERT_THROW(EvaluateFunction("TYPE", {2}), QueryRuntimeException); } -TEST(ExpressionEvaluator, FunctionLabels) { +TEST_F(FunctionTest, Labels) { ASSERT_THROW(EvaluateFunction("LABELS", {}), QueryRuntimeException); - ASSERT_EQ(EvaluateFunction("LABELS", {TypedValue::Null}).type(), - TypedValue::Type::Null); - NoContextExpressionEvaluator eval; - auto &dba = *eval.dba; - auto v = dba.InsertVertex(); - v.add_label(dba.Label("label1")); - v.add_label(dba.Label("label2")); + ASSERT_TRUE(EvaluateFunction("LABELS", {TypedValue::Null}).IsNull()); + auto v = dba->InsertVertex(); + v.add_label(dba->Label("label1")); + v.add_label(dba->Label("label2")); std::vector labels; - auto _labels = EvaluateFunction("LABELS", {v}, &eval.ctx) - .Value>(); + auto _labels = EvaluateFunction("LABELS", {v}).ValueList(); for (auto label : _labels) { - labels.push_back(label.Value()); + labels.push_back(label.ValueString()); } ASSERT_THAT(labels, UnorderedElementsAre("label1", "label2")); - ASSERT_THROW(EvaluateFunction("LABELS", {2}, &eval.ctx), - QueryRuntimeException); + ASSERT_THROW(EvaluateFunction("LABELS", {2}), QueryRuntimeException); } -TEST(ExpressionEvaluator, FunctionNodesRelationships) { +TEST_F(FunctionTest, NodesRelationships) { EXPECT_THROW(EvaluateFunction("NODES", {}), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("RELATIONSHIPS", {}), QueryRuntimeException); EXPECT_TRUE(EvaluateFunction("NODES", {TypedValue::Null}).IsNull()); EXPECT_TRUE(EvaluateFunction("RELATIONSHIPS", {TypedValue::Null}).IsNull()); { - NoContextExpressionEvaluator eval; - auto &dba = *eval.dba; - auto v1 = dba.InsertVertex(); - auto v2 = dba.InsertVertex(); - auto v3 = dba.InsertVertex(); - auto e1 = dba.InsertEdge(v1, v2, dba.EdgeType("Type")); - auto e2 = dba.InsertEdge(v2, v3, dba.EdgeType("Type")); + auto v1 = dba->InsertVertex(); + auto v2 = dba->InsertVertex(); + auto v3 = dba->InsertVertex(); + auto e1 = dba->InsertEdge(v1, v2, dba->EdgeType("Type")); + auto e2 = dba->InsertEdge(v2, v3, dba->EdgeType("Type")); query::Path path(v1, e1, v2, e2, v3); auto _nodes = EvaluateFunction("NODES", {path}).ValueList(); @@ -1060,7 +1082,7 @@ TEST(ExpressionEvaluator, FunctionNodesRelationships) { EXPECT_THROW(EvaluateFunction("RELATIONSHIPS", {2}), QueryRuntimeException); } -TEST(ExpressionEvaluator, FunctionRange) { +TEST_F(FunctionTest, Range) { EXPECT_THROW(EvaluateFunction("RANGE", {}), QueryRuntimeException); EXPECT_TRUE(EvaluateFunction("RANGE", {1, 2, TypedValue::Null}).IsNull()); EXPECT_THROW(EvaluateFunction("RANGE", {1, TypedValue::Null, 1.3}), @@ -1086,97 +1108,87 @@ TEST(ExpressionEvaluator, FunctionRange) { ElementsAre()); } -TEST(ExpressionEvaluator, FunctionKeys) { +TEST_F(FunctionTest, Keys) { ASSERT_THROW(EvaluateFunction("KEYS", {}), QueryRuntimeException); - ASSERT_EQ(EvaluateFunction("KEYS", {TypedValue::Null}).type(), - TypedValue::Type::Null); - NoContextExpressionEvaluator eval; - auto &dba = *eval.dba; - auto v1 = dba.InsertVertex(); - v1.PropsSet(dba.Property("height"), 5); - v1.PropsSet(dba.Property("age"), 10); - auto v2 = dba.InsertVertex(); - auto e = dba.InsertEdge(v1, v2, dba.EdgeType("type1")); - e.PropsSet(dba.Property("width"), 3); - e.PropsSet(dba.Property("age"), 15); + ASSERT_TRUE(EvaluateFunction("KEYS", {TypedValue::Null}).IsNull()); + auto v1 = dba->InsertVertex(); + v1.PropsSet(dba->Property("height"), 5); + v1.PropsSet(dba->Property("age"), 10); + auto v2 = dba->InsertVertex(); + auto e = dba->InsertEdge(v1, v2, dba->EdgeType("type1")); + e.PropsSet(dba->Property("width"), 3); + e.PropsSet(dba->Property("age"), 15); auto prop_keys_to_string = [](TypedValue t) { std::vector keys; - for (auto property : t.Value>()) { - keys.push_back(property.Value()); + for (auto property : t.ValueList()) { + keys.push_back(property.ValueString()); } return keys; }; - ASSERT_THAT(prop_keys_to_string(EvaluateFunction("KEYS", {v1}, &eval.ctx)), + ASSERT_THAT(prop_keys_to_string(EvaluateFunction("KEYS", {v1})), UnorderedElementsAre("height", "age")); - ASSERT_THAT(prop_keys_to_string(EvaluateFunction("KEYS", {e}, &eval.ctx)), + ASSERT_THAT(prop_keys_to_string(EvaluateFunction("KEYS", {e})), UnorderedElementsAre("width", "age")); - ASSERT_THROW(EvaluateFunction("KEYS", {2}, &eval.ctx), QueryRuntimeException); + ASSERT_THROW(EvaluateFunction("KEYS", {2}), QueryRuntimeException); } -TEST(ExpressionEvaluator, FunctionTail) { +TEST_F(FunctionTest, Tail) { ASSERT_THROW(EvaluateFunction("TAIL", {}), QueryRuntimeException); - ASSERT_EQ(EvaluateFunction("TAIL", {TypedValue::Null}).type(), - TypedValue::Type::Null); + ASSERT_TRUE(EvaluateFunction("TAIL", {TypedValue::Null}).IsNull()); std::vector arguments; arguments.push_back(std::vector{}); - ASSERT_EQ(EvaluateFunction("TAIL", arguments) - .Value>() - .size(), - 0U); + ASSERT_EQ(EvaluateFunction("TAIL", arguments).ValueList().size(), 0U); arguments[0] = std::vector{3, 4, true, "john"}; - auto list = - EvaluateFunction("TAIL", arguments).Value>(); + auto list = EvaluateFunction("TAIL", arguments).ValueList(); ASSERT_EQ(list.size(), 3U); - ASSERT_EQ(list[0].Value(), 4); - ASSERT_EQ(list[1].Value(), true); - ASSERT_EQ(list[2].Value(), "john"); + ASSERT_EQ(list[0].ValueInt(), 4); + ASSERT_EQ(list[1].ValueBool(), true); + ASSERT_EQ(list[2].ValueString(), "john"); ASSERT_THROW(EvaluateFunction("TAIL", {2}), QueryRuntimeException); } -TEST(ExpressionEvaluator, FunctionAbs) { +TEST_F(FunctionTest, Abs) { ASSERT_THROW(EvaluateFunction("ABS", {}), QueryRuntimeException); - ASSERT_EQ(EvaluateFunction("ABS", {TypedValue::Null}).type(), - TypedValue::Type::Null); - ASSERT_EQ(EvaluateFunction("ABS", {-2}).Value(), 2); - ASSERT_EQ(EvaluateFunction("ABS", {-2.5}).Value(), 2.5); + ASSERT_TRUE(EvaluateFunction("ABS", {TypedValue::Null}).IsNull()); + ASSERT_EQ(EvaluateFunction("ABS", {-2}).ValueInt(), 2); + ASSERT_EQ(EvaluateFunction("ABS", {-2.5}).ValueDouble(), 2.5); ASSERT_THROW(EvaluateFunction("ABS", {true}), QueryRuntimeException); } // Test if log works. If it does then all functions wrapped with // WRAP_CMATH_FLOAT_FUNCTION macro should work and are not gonna be tested for // correctnes.. -TEST(ExpressionEvaluator, FunctionLog) { +TEST_F(FunctionTest, Log) { ASSERT_THROW(EvaluateFunction("LOG", {}), QueryRuntimeException); - ASSERT_EQ(EvaluateFunction("LOG", {TypedValue::Null}).type(), - TypedValue::Type::Null); - ASSERT_DOUBLE_EQ(EvaluateFunction("LOG", {2}).Value(), log(2)); - ASSERT_DOUBLE_EQ(EvaluateFunction("LOG", {1.5}).Value(), log(1.5)); + ASSERT_TRUE(EvaluateFunction("LOG", {TypedValue::Null}).IsNull()); + ASSERT_DOUBLE_EQ(EvaluateFunction("LOG", {2}).ValueDouble(), log(2)); + ASSERT_DOUBLE_EQ(EvaluateFunction("LOG", {1.5}).ValueDouble(), log(1.5)); // Not portable, but should work on most platforms. - ASSERT_TRUE(std::isnan(EvaluateFunction("LOG", {-1.5}).Value())); + ASSERT_TRUE(std::isnan(EvaluateFunction("LOG", {-1.5}).ValueDouble())); ASSERT_THROW(EvaluateFunction("LOG", {true}), QueryRuntimeException); } -// Function Round wraps round from cmath and will work if FunctionLog test +// Function Round wraps round from cmath and will work if FunctionTest.Log test // passes. This test is used to show behavior of round since it differs from // neo4j's round. -TEST(ExpressionEvaluator, FunctionRound) { +TEST_F(FunctionTest, Round) { ASSERT_THROW(EvaluateFunction("ROUND", {}), QueryRuntimeException); - ASSERT_EQ(EvaluateFunction("ROUND", {TypedValue::Null}).type(), - TypedValue::Type::Null); - ASSERT_EQ(EvaluateFunction("ROUND", {-2}).Value(), -2); - ASSERT_EQ(EvaluateFunction("ROUND", {-2.4}).Value(), -2); - ASSERT_EQ(EvaluateFunction("ROUND", {-2.5}).Value(), -3); - ASSERT_EQ(EvaluateFunction("ROUND", {-2.6}).Value(), -3); - ASSERT_EQ(EvaluateFunction("ROUND", {2.4}).Value(), 2); - ASSERT_EQ(EvaluateFunction("ROUND", {2.5}).Value(), 3); - ASSERT_EQ(EvaluateFunction("ROUND", {2.6}).Value(), 3); + ASSERT_TRUE(EvaluateFunction("ROUND", {TypedValue::Null}).IsNull()); + ASSERT_EQ(EvaluateFunction("ROUND", {-2}).ValueDouble(), -2); + ASSERT_EQ(EvaluateFunction("ROUND", {-2.4}).ValueDouble(), -2); + ASSERT_EQ(EvaluateFunction("ROUND", {-2.5}).ValueDouble(), -3); + ASSERT_EQ(EvaluateFunction("ROUND", {-2.6}).ValueDouble(), -3); + ASSERT_EQ(EvaluateFunction("ROUND", {2.4}).ValueDouble(), 2); + ASSERT_EQ(EvaluateFunction("ROUND", {2.5}).ValueDouble(), 3); + ASSERT_EQ(EvaluateFunction("ROUND", {2.6}).ValueDouble(), 3); ASSERT_THROW(EvaluateFunction("ROUND", {true}), QueryRuntimeException); } // Check if wrapped functions are callable (check if everything was spelled -// correctly...). Wrapper correctnes is checked in FunctionLog test. -TEST(ExpressionEvaluator, FunctionWrappedMathFunctions) { +// correctly...). Wrapper correctnes is checked in FunctionTest.Log function +// test. +TEST_F(FunctionTest, WrappedMathFunctions) { for (auto function_name : {"FLOOR", "CEIL", "ROUND", "EXP", "LOG", "LOG10", "SQRT", "ACOS", "ASIN", "ATAN", "COS", "SIN", "TAN"}) { @@ -1184,209 +1196,75 @@ TEST(ExpressionEvaluator, FunctionWrappedMathFunctions) { } } -TEST(ExpressionEvaluator, FunctionAtan2) { +TEST_F(FunctionTest, Atan2) { ASSERT_THROW(EvaluateFunction("ATAN2", {}), QueryRuntimeException); - ASSERT_EQ(EvaluateFunction("ATAN2", {TypedValue::Null, 1}).type(), - TypedValue::Type::Null); - ASSERT_EQ(EvaluateFunction("ATAN2", {1, TypedValue::Null}).type(), - TypedValue::Type::Null); - ASSERT_DOUBLE_EQ(EvaluateFunction("ATAN2", {2, -1.0}).Value(), + ASSERT_TRUE(EvaluateFunction("ATAN2", {TypedValue::Null, 1}).IsNull()); + ASSERT_TRUE(EvaluateFunction("ATAN2", {1, TypedValue::Null}).IsNull()); + ASSERT_DOUBLE_EQ(EvaluateFunction("ATAN2", {2, -1.0}).ValueDouble(), atan2(2, -1)); ASSERT_THROW(EvaluateFunction("ATAN2", {3.0, true}), QueryRuntimeException); } -TEST(ExpressionEvaluator, FunctionSign) { +TEST_F(FunctionTest, Sign) { ASSERT_THROW(EvaluateFunction("SIGN", {}), QueryRuntimeException); - ASSERT_EQ(EvaluateFunction("SIGN", {TypedValue::Null}).type(), - TypedValue::Type::Null); - ASSERT_EQ(EvaluateFunction("SIGN", {-2}).Value(), -1); - ASSERT_EQ(EvaluateFunction("SIGN", {-0.2}).Value(), -1); - ASSERT_EQ(EvaluateFunction("SIGN", {0.0}).Value(), 0); - ASSERT_EQ(EvaluateFunction("SIGN", {2.5}).Value(), 1); + ASSERT_TRUE(EvaluateFunction("SIGN", {TypedValue::Null}).IsNull()); + ASSERT_EQ(EvaluateFunction("SIGN", {-2}).ValueInt(), -1); + ASSERT_EQ(EvaluateFunction("SIGN", {-0.2}).ValueInt(), -1); + ASSERT_EQ(EvaluateFunction("SIGN", {0.0}).ValueInt(), 0); + ASSERT_EQ(EvaluateFunction("SIGN", {2.5}).ValueInt(), 1); ASSERT_THROW(EvaluateFunction("SIGN", {true}), QueryRuntimeException); } -TEST(ExpressionEvaluator, FunctionE) { +TEST_F(FunctionTest, E) { ASSERT_THROW(EvaluateFunction("E", {1}), QueryRuntimeException); - ASSERT_DOUBLE_EQ(EvaluateFunction("E", {}).Value(), M_E); + ASSERT_DOUBLE_EQ(EvaluateFunction("E", {}).ValueDouble(), M_E); } -TEST(ExpressionEvaluator, FunctionPi) { +TEST_F(FunctionTest, Pi) { ASSERT_THROW(EvaluateFunction("PI", {1}), QueryRuntimeException); - ASSERT_DOUBLE_EQ(EvaluateFunction("PI", {}).Value(), M_PI); + ASSERT_DOUBLE_EQ(EvaluateFunction("PI", {}).ValueDouble(), M_PI); } -TEST(ExpressionEvaluator, FunctionRand) { +TEST_F(FunctionTest, Rand) { ASSERT_THROW(EvaluateFunction("RAND", {1}), QueryRuntimeException); - ASSERT_GE(EvaluateFunction("RAND", {}).Value(), 0.0); - ASSERT_LT(EvaluateFunction("RAND", {}).Value(), 1.0); + ASSERT_GE(EvaluateFunction("RAND", {}).ValueDouble(), 0.0); + ASSERT_LT(EvaluateFunction("RAND", {}).ValueDouble(), 1.0); } -TEST(ExpressionEvaluator, FunctionStartsWith) { +TEST_F(FunctionTest, StartsWith) { EXPECT_THROW(EvaluateFunction(kStartsWith, {}), QueryRuntimeException); EXPECT_TRUE(EvaluateFunction(kStartsWith, {"a", TypedValue::Null}).IsNull()); EXPECT_THROW(EvaluateFunction(kStartsWith, {TypedValue::Null, 1.3}), QueryRuntimeException); - EXPECT_TRUE(EvaluateFunction(kStartsWith, {"abc", "abc"}).Value()); - EXPECT_TRUE(EvaluateFunction(kStartsWith, {"abcdef", "abc"}).Value()); - EXPECT_FALSE(EvaluateFunction(kStartsWith, {"abcdef", "aBc"}).Value()); - EXPECT_FALSE(EvaluateFunction(kStartsWith, {"abc", "abcd"}).Value()); + EXPECT_TRUE(EvaluateFunction(kStartsWith, {"abc", "abc"}).ValueBool()); + EXPECT_TRUE(EvaluateFunction(kStartsWith, {"abcdef", "abc"}).ValueBool()); + EXPECT_FALSE(EvaluateFunction(kStartsWith, {"abcdef", "aBc"}).ValueBool()); + EXPECT_FALSE(EvaluateFunction(kStartsWith, {"abc", "abcd"}).ValueBool()); } -TEST(ExpressionEvaluator, FunctionEndsWith) { +TEST_F(FunctionTest, EndsWith) { EXPECT_THROW(EvaluateFunction(kEndsWith, {}), QueryRuntimeException); EXPECT_TRUE(EvaluateFunction(kEndsWith, {"a", TypedValue::Null}).IsNull()); EXPECT_THROW(EvaluateFunction(kEndsWith, {TypedValue::Null, 1.3}), QueryRuntimeException); - EXPECT_TRUE(EvaluateFunction(kEndsWith, {"abc", "abc"}).Value()); - EXPECT_TRUE(EvaluateFunction(kEndsWith, {"abcdef", "def"}).Value()); - EXPECT_FALSE(EvaluateFunction(kEndsWith, {"abcdef", "dEf"}).Value()); - EXPECT_FALSE(EvaluateFunction(kEndsWith, {"bcd", "abcd"}).Value()); + EXPECT_TRUE(EvaluateFunction(kEndsWith, {"abc", "abc"}).ValueBool()); + EXPECT_TRUE(EvaluateFunction(kEndsWith, {"abcdef", "def"}).ValueBool()); + EXPECT_FALSE(EvaluateFunction(kEndsWith, {"abcdef", "dEf"}).ValueBool()); + EXPECT_FALSE(EvaluateFunction(kEndsWith, {"bcd", "abcd"}).ValueBool()); } -TEST(ExpressionEvaluator, FunctionContains) { +TEST_F(FunctionTest, Contains) { EXPECT_THROW(EvaluateFunction(kContains, {}), QueryRuntimeException); EXPECT_TRUE(EvaluateFunction(kContains, {"a", TypedValue::Null}).IsNull()); EXPECT_THROW(EvaluateFunction(kContains, {TypedValue::Null, 1.3}), QueryRuntimeException); - EXPECT_TRUE(EvaluateFunction(kContains, {"abc", "abc"}).Value()); - EXPECT_TRUE(EvaluateFunction(kContains, {"abcde", "bcd"}).Value()); - EXPECT_FALSE(EvaluateFunction(kContains, {"cde", "abcdef"}).Value()); - EXPECT_FALSE(EvaluateFunction(kContains, {"abcdef", "dEf"}).Value()); + EXPECT_TRUE(EvaluateFunction(kContains, {"abc", "abc"}).ValueBool()); + EXPECT_TRUE(EvaluateFunction(kContains, {"abcde", "bcd"}).ValueBool()); + EXPECT_FALSE(EvaluateFunction(kContains, {"cde", "abcdef"}).ValueBool()); + EXPECT_FALSE(EvaluateFunction(kContains, {"abcdef", "dEf"}).ValueBool()); } -TEST(ExpressionEvaluator, FunctionAll) { - AstStorage storage; - auto *ident_x = IDENT("x"); - auto *all = - ALL("x", LIST(LITERAL(1), LITERAL(2)), WHERE(EQ(ident_x, LITERAL(1)))); - NoContextExpressionEvaluator eval; - const auto x_sym = eval.ctx.symbol_table_.CreateSymbol("x", true); - eval.ctx.symbol_table_[*all->identifier_] = x_sym; - eval.ctx.symbol_table_[*ident_x] = x_sym; - auto value = all->Accept(eval.eval); - ASSERT_EQ(value.type(), TypedValue::Type::Bool); - EXPECT_FALSE(value.Value()); -} - -TEST(ExpressionEvaluator, FunctionAllNullList) { - AstStorage storage; - auto *all = ALL("x", LITERAL(TypedValue::Null), WHERE(LITERAL(true))); - NoContextExpressionEvaluator eval; - const auto x_sym = eval.ctx.symbol_table_.CreateSymbol("x", true); - eval.ctx.symbol_table_[*all->identifier_] = x_sym; - auto value = all->Accept(eval.eval); - EXPECT_TRUE(value.IsNull()); -} - -TEST(ExpressionEvaluator, FunctionAllWhereWrongType) { - AstStorage storage; - auto *all = ALL("x", LIST(LITERAL(1)), WHERE(LITERAL(2))); - NoContextExpressionEvaluator eval; - const auto x_sym = eval.ctx.symbol_table_.CreateSymbol("x", true); - eval.ctx.symbol_table_[*all->identifier_] = x_sym; - EXPECT_THROW(all->Accept(eval.eval), QueryRuntimeException); -} - -TEST(ExpressionEvaluator, FunctionSingle) { - AstStorage storage; - auto *ident_x = IDENT("x"); - auto *single = - SINGLE("x", LIST(LITERAL(1), LITERAL(2)), WHERE(EQ(ident_x, LITERAL(1)))); - NoContextExpressionEvaluator eval; - const auto x_sym = eval.ctx.symbol_table_.CreateSymbol("x", true); - eval.ctx.symbol_table_[*single->identifier_] = x_sym; - eval.ctx.symbol_table_[*ident_x] = x_sym; - auto value = single->Accept(eval.eval); - ASSERT_EQ(value.type(), TypedValue::Type::Bool); - EXPECT_TRUE(value.Value()); -} - -TEST(ExpressionEvaluator, FunctionSingle2) { - AstStorage storage; - auto *ident_x = IDENT("x"); - auto *single = SINGLE("x", LIST(LITERAL(1), LITERAL(2)), - WHERE(GREATER(ident_x, LITERAL(0)))); - NoContextExpressionEvaluator eval; - const auto x_sym = eval.ctx.symbol_table_.CreateSymbol("x", true); - eval.ctx.symbol_table_[*single->identifier_] = x_sym; - eval.ctx.symbol_table_[*ident_x] = x_sym; - auto value = single->Accept(eval.eval); - ASSERT_EQ(value.type(), TypedValue::Type::Bool); - EXPECT_FALSE(value.Value()); -} - -TEST(ExpressionEvaluator, FunctionSingleNullList) { - AstStorage storage; - auto *single = SINGLE("x", LITERAL(TypedValue::Null), WHERE(LITERAL(true))); - NoContextExpressionEvaluator eval; - const auto x_sym = eval.ctx.symbol_table_.CreateSymbol("x", true); - eval.ctx.symbol_table_[*single->identifier_] = x_sym; - auto value = single->Accept(eval.eval); - EXPECT_TRUE(value.IsNull()); -} - -TEST(ExpressionEvaluator, FunctionReduce) { - AstStorage storage; - auto *ident_sum = IDENT("sum"); - auto *ident_x = IDENT("x"); - auto *reduce = REDUCE("sum", LITERAL(0), "x", LIST(LITERAL(1), LITERAL(2)), - ADD(ident_sum, ident_x)); - NoContextExpressionEvaluator eval; - const auto sum_sym = eval.ctx.symbol_table_.CreateSymbol("sum", true); - eval.ctx.symbol_table_[*reduce->accumulator_] = sum_sym; - eval.ctx.symbol_table_[*ident_sum] = sum_sym; - const auto x_sym = eval.ctx.symbol_table_.CreateSymbol("x", true); - eval.ctx.symbol_table_[*reduce->identifier_] = x_sym; - eval.ctx.symbol_table_[*ident_x] = x_sym; - auto value = reduce->Accept(eval.eval); - ASSERT_EQ(value.type(), TypedValue::Type::Int); - EXPECT_EQ(value.Value(), 3); -} - -TEST(ExpressionEvaluator, FunctionExtract) { - AstStorage storage; - auto *ident_x = IDENT("x"); - auto *extract = - EXTRACT("x", LIST(LITERAL(1), LITERAL(2), LITERAL(TypedValue::Null)), - ADD(ident_x, LITERAL(1))); - NoContextExpressionEvaluator eval; - const auto x_sym = eval.ctx.symbol_table_.CreateSymbol("x", true); - eval.ctx.symbol_table_[*extract->identifier_] = x_sym; - eval.ctx.symbol_table_[*ident_x] = x_sym; - auto value = extract->Accept(eval.eval); - EXPECT_EQ(value.type(), TypedValue::Type::List); - auto result = value.ValueList(); - EXPECT_EQ(result[0].ValueInt(), 2); - EXPECT_EQ(result[1].ValueInt(), 3); - EXPECT_TRUE(result[2].IsNull()); -} - -TEST(ExpressionEvaluator, FunctionExtractNull) { - AstStorage storage; - auto *ident_x = IDENT("x"); - auto *extract = - EXTRACT("x", LITERAL(TypedValue::Null), ADD(ident_x, LITERAL(1))); - NoContextExpressionEvaluator eval; - const auto x_sym = eval.ctx.symbol_table_.CreateSymbol("x", true); - eval.ctx.symbol_table_[*extract->identifier_] = x_sym; - eval.ctx.symbol_table_[*ident_x] = x_sym; - auto value = extract->Accept(eval.eval); - EXPECT_TRUE(value.IsNull()); -} - -TEST(ExpressionEvaluator, FunctionExtractExceptions) { - AstStorage storage; - auto *ident_x = IDENT("x"); - auto *extract = EXTRACT("x", LITERAL("bla"), ADD(ident_x, LITERAL(1))); - NoContextExpressionEvaluator eval; - const auto x_sym = eval.ctx.symbol_table_.CreateSymbol("x", true); - eval.ctx.symbol_table_[*extract->identifier_] = x_sym; - eval.ctx.symbol_table_[*ident_x] = x_sym; - EXPECT_THROW(extract->Accept(eval.eval), QueryRuntimeException); -} - -TEST(ExpressionEvaluator, FunctionAssert) { +TEST_F(FunctionTest, Assert) { // Invalid calls. ASSERT_THROW(EvaluateFunction("ASSERT", {}), QueryRuntimeException); ASSERT_THROW(EvaluateFunction("ASSERT", {false, false}), @@ -1411,152 +1289,119 @@ TEST(ExpressionEvaluator, FunctionAssert) { ASSERT_TRUE(EvaluateFunction("ASSERT", {true, "message"}).ValueBool()); } -TEST(ExpressionEvaluator, ParameterLookup) { - NoContextExpressionEvaluator eval; - eval.ctx.parameters_.Add(0, 42); - AstStorage storage; - auto *param_lookup = storage.Create(0); - auto value = param_lookup->Accept(eval.eval); - ASSERT_EQ(value.type(), TypedValue::Type::Int); - EXPECT_EQ(value.Value(), 42); +TEST_F(FunctionTest, Counter) { + EXPECT_THROW(EvaluateFunction("COUNTER", {}), QueryRuntimeException); + EXPECT_THROW(EvaluateFunction("COUNTER", {"a", "b"}), QueryRuntimeException); + EXPECT_EQ(EvaluateFunction("COUNTER", {"c1"}).ValueInt(), 0); + EXPECT_EQ(EvaluateFunction("COUNTER", {"c1"}).ValueInt(), 1); + EXPECT_EQ(EvaluateFunction("COUNTER", {"c2"}).ValueInt(), 0); + EXPECT_EQ(EvaluateFunction("COUNTER", {"c1"}).ValueInt(), 2); + EXPECT_EQ(EvaluateFunction("COUNTER", {"c2"}).ValueInt(), 1); } -TEST(ExpressionEvaluator, FunctionCounter) { - NoContextExpressionEvaluator eval; - EXPECT_THROW(EvaluateFunction("COUNTER", {}, &eval.ctx), +TEST_F(FunctionTest, CounterSet) { + EXPECT_THROW(EvaluateFunction("COUNTERSET", {}), QueryRuntimeException); + EXPECT_THROW(EvaluateFunction("COUNTERSET", {"a"}), QueryRuntimeException); + EXPECT_THROW(EvaluateFunction("COUNTERSET", {"a", "b"}), QueryRuntimeException); - EXPECT_THROW(EvaluateFunction("COUNTER", {"a", "b"}, &eval.ctx), + EXPECT_THROW(EvaluateFunction("COUNTERSET", {"a", 11, 12}), QueryRuntimeException); - EXPECT_EQ(EvaluateFunction("COUNTER", {"c1"}, &eval.ctx).ValueInt(), 0); - EXPECT_EQ(EvaluateFunction("COUNTER", {"c1"}, &eval.ctx).ValueInt(), 1); - EXPECT_EQ(EvaluateFunction("COUNTER", {"c2"}, &eval.ctx).ValueInt(), 0); - EXPECT_EQ(EvaluateFunction("COUNTER", {"c1"}, &eval.ctx).ValueInt(), 2); - EXPECT_EQ(EvaluateFunction("COUNTER", {"c2"}, &eval.ctx).ValueInt(), 1); + EXPECT_EQ(EvaluateFunction("COUNTER", {"c1"}).ValueInt(), 0); + EvaluateFunction("COUNTERSET", {"c1", 12}); + EXPECT_EQ(EvaluateFunction("COUNTER", {"c1"}).ValueInt(), 12); + EvaluateFunction("COUNTERSET", {"c2", 42}); + EXPECT_EQ(EvaluateFunction("COUNTER", {"c2"}).ValueInt(), 42); + EXPECT_EQ(EvaluateFunction("COUNTER", {"c1"}).ValueInt(), 13); + EXPECT_EQ(EvaluateFunction("COUNTER", {"c2"}).ValueInt(), 43); } -TEST(ExpressionEvaluator, FunctionCounterSet) { - NoContextExpressionEvaluator eval; - EXPECT_THROW(EvaluateFunction("COUNTERSET", {}, &eval.ctx), - QueryRuntimeException); - EXPECT_THROW(EvaluateFunction("COUNTERSET", {"a"}, &eval.ctx), - QueryRuntimeException); - EXPECT_THROW(EvaluateFunction("COUNTERSET", {"a", "b"}, &eval.ctx), - QueryRuntimeException); - EXPECT_THROW(EvaluateFunction("COUNTERSET", {"a", 11, 12}, &eval.ctx), - QueryRuntimeException); - EXPECT_EQ(EvaluateFunction("COUNTER", {"c1"}, &eval.ctx).ValueInt(), 0); - EvaluateFunction("COUNTERSET", {"c1", 12}, &eval.ctx); - EXPECT_EQ(EvaluateFunction("COUNTER", {"c1"}, &eval.ctx).ValueInt(), 12); - EvaluateFunction("COUNTERSET", {"c2", 42}, &eval.ctx); - EXPECT_EQ(EvaluateFunction("COUNTER", {"c2"}, &eval.ctx).ValueInt(), 42); - EXPECT_EQ(EvaluateFunction("COUNTER", {"c1"}, &eval.ctx).ValueInt(), 13); - EXPECT_EQ(EvaluateFunction("COUNTER", {"c2"}, &eval.ctx).ValueInt(), 43); -} - -TEST(ExpressionEvaluator, FunctionIndexInfo) { - NoContextExpressionEvaluator eval; - EXPECT_THROW(EvaluateFunction("INDEXINFO", {1}, &eval.ctx), - QueryRuntimeException); - EXPECT_EQ(EvaluateFunction("INDEXINFO", {}, &eval.ctx).ValueList().size(), 0); - auto &dba = *eval.dba; - dba.InsertVertex().add_label(dba.Label("l1")); +TEST_F(FunctionTest, IndexInfo) { + EXPECT_THROW(EvaluateFunction("INDEXINFO", {1}), QueryRuntimeException); + EXPECT_EQ(EvaluateFunction("INDEXINFO", {}).ValueList().size(), 0); + dba->InsertVertex().add_label(dba->Label("l1")); { - auto info = - ToList(EvaluateFunction("INDEXINFO", {}, &eval.ctx)); + auto info = ToList(EvaluateFunction("INDEXINFO", {})); EXPECT_EQ(info.size(), 1); EXPECT_EQ(info[0], ":l1"); } { - dba.BuildIndex(dba.Label("l1"), dba.Property("prop")); - auto info = - ToList(EvaluateFunction("INDEXINFO", {}, &eval.ctx)); + dba->BuildIndex(dba->Label("l1"), dba->Property("prop")); + auto info = ToList(EvaluateFunction("INDEXINFO", {})); EXPECT_EQ(info.size(), 2); EXPECT_THAT(info, testing::UnorderedElementsAre(":l1", ":l1(prop)")); } } -TEST(ExpressionEvaluator, FunctionId) { - NoContextExpressionEvaluator eval; - auto &dba = *eval.dba; - auto va = dba.InsertVertex(); - auto ea = dba.InsertEdge(va, va, dba.EdgeType("edge")); - auto vb = dba.InsertVertex(); - EXPECT_EQ(EvaluateFunction("ID", {va}, &eval.ctx).Value(), 0); - EXPECT_EQ(EvaluateFunction("ID", {ea}, &eval.ctx).Value(), 0); - EXPECT_EQ(EvaluateFunction("ID", {vb}, &eval.ctx).Value(), 1024); - EXPECT_THROW(EvaluateFunction("ID", {}, &eval.ctx), QueryRuntimeException); - EXPECT_THROW(EvaluateFunction("ID", {0}, &eval.ctx), QueryRuntimeException); - EXPECT_THROW(EvaluateFunction("ID", {va, ea}, &eval.ctx), - QueryRuntimeException); +TEST_F(FunctionTest, Id) { + auto va = dba->InsertVertex(); + auto ea = dba->InsertEdge(va, va, dba->EdgeType("edge")); + auto vb = dba->InsertVertex(); + EXPECT_EQ(EvaluateFunction("ID", {va}).ValueInt(), 0); + EXPECT_EQ(EvaluateFunction("ID", {ea}).ValueInt(), 0); + EXPECT_EQ(EvaluateFunction("ID", {vb}).ValueInt(), 1024); + EXPECT_THROW(EvaluateFunction("ID", {}), QueryRuntimeException); + EXPECT_THROW(EvaluateFunction("ID", {0}), QueryRuntimeException); + EXPECT_THROW(EvaluateFunction("ID", {va, ea}), QueryRuntimeException); } -TEST(ExpressionEvaluator, FunctionWorkerIdException) { - database::SingleNode db; - NoContextExpressionEvaluator eval; - auto &dba = *eval.dba; - auto va = dba.InsertVertex(); - EXPECT_THROW(EvaluateFunction("WORKERID", {}, &eval.ctx), - QueryRuntimeException); - EXPECT_THROW(EvaluateFunction("WORKERID", {va, va}, &eval.ctx), - QueryRuntimeException); +TEST_F(FunctionTest, WorkerIdException) { + auto va = dba->InsertVertex(); + EXPECT_THROW(EvaluateFunction("WORKERID", {}), QueryRuntimeException); + EXPECT_THROW(EvaluateFunction("WORKERID", {va, va}), QueryRuntimeException); } -TEST(ExpressionEvaluator, FunctionWorkerIdSingleNode) { - NoContextExpressionEvaluator eval; - auto &dba = *eval.dba; - auto va = dba.InsertVertex(); - EXPECT_EQ(EvaluateFunction("WORKERID", {va}, &eval.ctx).Value(), 0); +TEST_F(FunctionTest, WorkerIdSingleNode) { + auto va = dba->InsertVertex(); + EXPECT_EQ(EvaluateFunction("WORKERID", {va}).ValueInt(), 0); } -TEST(ExpressionEvaluator, FunctionToStringNull) { +TEST_F(FunctionTest, ToStringNull) { EXPECT_TRUE(EvaluateFunction("TOSTRING", {TypedValue::Null}).IsNull()); } -TEST(ExpressionEvaluator, FunctionToStringString) { +TEST_F(FunctionTest, ToStringString) { EXPECT_EQ(EvaluateFunction("TOSTRING", {""}).ValueString(), ""); EXPECT_EQ(EvaluateFunction("TOSTRING", {"this is a string"}).ValueString(), "this is a string"); } -TEST(ExpressionEvaluator, FunctionToStringInteger) { +TEST_F(FunctionTest, ToStringInteger) { EXPECT_EQ(EvaluateFunction("TOSTRING", {-23321312}).ValueString(), "-23321312"); EXPECT_EQ(EvaluateFunction("TOSTRING", {0}).ValueString(), "0"); EXPECT_EQ(EvaluateFunction("TOSTRING", {42}).ValueString(), "42"); } -TEST(ExpressionEvaluator, FunctionToStringDouble) { +TEST_F(FunctionTest, ToStringDouble) { EXPECT_EQ(EvaluateFunction("TOSTRING", {-42.42}).ValueString(), "-42.420000"); EXPECT_EQ(EvaluateFunction("TOSTRING", {0.0}).ValueString(), "0.000000"); EXPECT_EQ(EvaluateFunction("TOSTRING", {238910.2313217}).ValueString(), "238910.231322"); } -TEST(ExpressionEvaluator, FunctionToStringBool) { +TEST_F(FunctionTest, ToStringBool) { EXPECT_EQ(EvaluateFunction("TOSTRING", {true}).ValueString(), "true"); EXPECT_EQ(EvaluateFunction("TOSTRING", {false}).ValueString(), "false"); } -TEST(ExpressionEvaluator, FunctionToStringExceptions) { +TEST_F(FunctionTest, ToStringExceptions) { EXPECT_THROW(EvaluateFunction("TOSTRING", {1, 2, 3}), QueryRuntimeException); std::vector l{1, 2, 3}; EXPECT_THROW(EvaluateFunction("TOSTRING", l), QueryRuntimeException); } -TEST(ExpressionEvaluator, FunctionTimestamp) { - NoContextExpressionEvaluator eval; - eval.ctx.timestamp_ = 42; - EXPECT_EQ(EvaluateFunction("TIMESTAMP", {}, &eval.ctx).ValueInt(), 42); +TEST_F(FunctionTest, Timestamp) { + ctx.timestamp = 42; + EXPECT_EQ(EvaluateFunction("TIMESTAMP", {}).ValueInt(), 42); } -TEST(ExpressionEvaluator, FunctionTimestampExceptions) { - NoContextExpressionEvaluator eval; - eval.ctx.timestamp_ = 42; - EXPECT_THROW(EvaluateFunction("TIMESTAMP", {1}, &eval.ctx).ValueInt(), +TEST_F(FunctionTest, TimestampExceptions) { + ctx.timestamp = 42; + EXPECT_THROW(EvaluateFunction("TIMESTAMP", {1}).ValueInt(), QueryRuntimeException); } -TEST(ExpressionEvaluator, FunctionLeft) { +TEST_F(FunctionTest, Left) { EXPECT_THROW(EvaluateFunction("LEFT", {}), QueryRuntimeException); EXPECT_TRUE( @@ -1577,7 +1422,7 @@ TEST(ExpressionEvaluator, FunctionLeft) { EXPECT_THROW(EvaluateFunction("LEFT", {132, 10}), QueryRuntimeException); } -TEST(ExpressionEvaluator, FunctionRight) { +TEST_F(FunctionTest, Right) { EXPECT_THROW(EvaluateFunction("RIGHT", {}), QueryRuntimeException); EXPECT_TRUE( @@ -1598,13 +1443,13 @@ TEST(ExpressionEvaluator, FunctionRight) { EXPECT_THROW(EvaluateFunction("RIGHT", {132, 10}), QueryRuntimeException); } -TEST(ExpressionEvaluator, Trimming) { +TEST_F(FunctionTest, Trimming) { EXPECT_TRUE(EvaluateFunction("LTRIM", {TypedValue::Null}).IsNull()); EXPECT_TRUE(EvaluateFunction("RTRIM", {TypedValue::Null}).IsNull()); EXPECT_TRUE(EvaluateFunction("TRIM", {TypedValue::Null}).IsNull()); EXPECT_EQ(EvaluateFunction("LTRIM", {" abc "}).ValueString(), "abc "); - EXPECT_EQ(EvaluateFunction("RTRIM", {" abc "}).ValueString(), " abc"); + EXPECT_EQ(EvaluateFunction("RTRIM", {" abc "}).ValueString(), " abc"); EXPECT_EQ(EvaluateFunction("TRIM", {"abc"}).ValueString(), "abc"); EXPECT_THROW(EvaluateFunction("LTRIM", {"x", "y"}), QueryRuntimeException); @@ -1612,13 +1457,13 @@ TEST(ExpressionEvaluator, Trimming) { EXPECT_THROW(EvaluateFunction("TRIM", {"x", "y"}), QueryRuntimeException); } -TEST(ExpressionEvaluator, FunctionReverse) { +TEST_F(FunctionTest, Reverse) { EXPECT_TRUE(EvaluateFunction("REVERSE", {TypedValue::Null}).IsNull()); EXPECT_EQ(EvaluateFunction("REVERSE", {"abc"}).ValueString(), "cba"); EXPECT_THROW(EvaluateFunction("REVERSE", {"x", "y"}), QueryRuntimeException); } -TEST(ExpressionEvaluator, FunctionReplace) { +TEST_F(FunctionTest, Replace) { EXPECT_THROW(EvaluateFunction("REPLACE", {}), QueryRuntimeException); EXPECT_TRUE( EvaluateFunction("REPLACE", {TypedValue::Null, "l", "w"}).IsNull()); @@ -1637,7 +1482,7 @@ TEST(ExpressionEvaluator, FunctionReplace) { QueryRuntimeException); } -TEST(ExpressionEvaluator, FunctionSplit) { +TEST_F(FunctionTest, Split) { EXPECT_THROW(EvaluateFunction("SPLIT", {}), QueryRuntimeException); EXPECT_THROW(EvaluateFunction("SPLIT", {"one,two", 1}), QueryRuntimeException); @@ -1656,7 +1501,7 @@ TEST(ExpressionEvaluator, FunctionSplit) { EXPECT_EQ(result.ValueList()[1].ValueString(), "two"); } -TEST(ExpressionEvaluator, FunctionSubstring) { +TEST_F(FunctionTest, Substring) { EXPECT_THROW(EvaluateFunction("SUBSTRING", {}), QueryRuntimeException); EXPECT_TRUE( @@ -1683,13 +1528,13 @@ TEST(ExpressionEvaluator, FunctionSubstring) { "ello"); } -TEST(ExpressionEvaluator, FunctionToLower) { +TEST_F(FunctionTest, ToLower) { EXPECT_THROW(EvaluateFunction("TOLOWER", {}), QueryRuntimeException); EXPECT_TRUE(EvaluateFunction("TOLOWER", {TypedValue::Null}).IsNull()); EXPECT_EQ(EvaluateFunction("TOLOWER", {"Ab__C"}).ValueString(), "ab__c"); } -TEST(ExpressionEvaluator, FunctionToUpper) { +TEST_F(FunctionTest, ToUpper) { EXPECT_THROW(EvaluateFunction("TOUPPER", {}), QueryRuntimeException); EXPECT_TRUE(EvaluateFunction("TOUPPER", {TypedValue::Null}).IsNull()); EXPECT_EQ(EvaluateFunction("TOUPPER", {"Ab__C"}).ValueString(), "AB__C");