Use memory from EvaluationContext in awesome functions

Reviewers: mferencevic, msantl, mtomic

Reviewed By: msantl

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D2133
This commit is contained in:
Teon Banek 2019-06-07 16:21:16 +02:00
parent 3fd14e2d5f
commit 65ab2574bc
3 changed files with 260 additions and 226 deletions

View File

@ -34,51 +34,57 @@ namespace {
// TODO: Implement degrees, haversin, radians // TODO: Implement degrees, haversin, radians
// TODO: Implement spatial functions // TODO: Implement spatial functions
TypedValue EndNode(TypedValue *args, int64_t nargs, const EvaluationContext &, /////////////////////////// IMPORTANT NOTE! ////////////////////////////////////
database::GraphDbAccessor *) { // All of the functions take mutable `TypedValue *` to arguments, but none of
// the functions should ever need to actually modify the arguments! Let's try to
// keep our sanity in a good state by treating the arguments as immutable.
////////////////////////////////////////////////////////////////////////////////
TypedValue EndNode(TypedValue *args, int64_t nargs,
const EvaluationContext &ctx, database::GraphDbAccessor *) {
if (nargs != 1) { if (nargs != 1) {
throw QueryRuntimeException("'endNode' requires exactly one argument."); throw QueryRuntimeException("'endNode' requires exactly one argument.");
} }
switch (args[0].type()) { switch (args[0].type()) {
case TypedValue::Type::Null: case TypedValue::Type::Null:
return TypedValue(); return TypedValue(ctx.memory);
case TypedValue::Type::Edge: case TypedValue::Type::Edge:
return TypedValue(args[0].Value<EdgeAccessor>().to()); return TypedValue(args[0].Value<EdgeAccessor>().to(), ctx.memory);
default: default:
throw QueryRuntimeException("'endNode' argument must be an edge."); throw QueryRuntimeException("'endNode' argument must be an edge.");
} }
} }
TypedValue Head(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue Head(TypedValue *args, int64_t nargs, const EvaluationContext &ctx,
database::GraphDbAccessor *) { database::GraphDbAccessor *) {
if (nargs != 1) { if (nargs != 1) {
throw QueryRuntimeException("'head' requires exactly one argument."); throw QueryRuntimeException("'head' requires exactly one argument.");
} }
switch (args[0].type()) { switch (args[0].type()) {
case TypedValue::Type::Null: case TypedValue::Type::Null:
return TypedValue(); return TypedValue(ctx.memory);
case TypedValue::Type::List: { case TypedValue::Type::List: {
const auto &list = args[0].ValueList(); const auto &list = args[0].ValueList();
if (list.empty()) return TypedValue(); if (list.empty()) return TypedValue(ctx.memory);
return list[0]; return TypedValue(list[0], ctx.memory);
} }
default: default:
throw QueryRuntimeException("'head' argument must be a list."); throw QueryRuntimeException("'head' argument must be a list.");
} }
} }
TypedValue Last(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue Last(TypedValue *args, int64_t nargs, const EvaluationContext &ctx,
database::GraphDbAccessor *) { database::GraphDbAccessor *) {
if (nargs != 1) { if (nargs != 1) {
throw QueryRuntimeException("'last' requires exactly one argument."); throw QueryRuntimeException("'last' requires exactly one argument.");
} }
switch (args[0].type()) { switch (args[0].type()) {
case TypedValue::Type::Null: case TypedValue::Type::Null:
return TypedValue(); return TypedValue(ctx.memory);
case TypedValue::Type::List: { case TypedValue::Type::List: {
const auto &list = args[0].ValueList(); const auto &list = args[0].ValueList();
if (list.empty()) return TypedValue(); if (list.empty()) return TypedValue(ctx.memory);
return list.back(); return TypedValue(list.back(), ctx.memory);
} }
default: default:
throw QueryRuntimeException("'last' argument must be a list."); throw QueryRuntimeException("'last' argument must be a list.");
@ -86,22 +92,21 @@ TypedValue Last(TypedValue *args, int64_t nargs, const EvaluationContext &,
} }
TypedValue Properties(TypedValue *args, int64_t nargs, TypedValue Properties(TypedValue *args, int64_t nargs,
const EvaluationContext &, const EvaluationContext &ctx,
database::GraphDbAccessor *dba) { database::GraphDbAccessor *dba) {
if (nargs != 1) { if (nargs != 1) {
throw QueryRuntimeException("'properties' requires exactly one argument."); throw QueryRuntimeException("'properties' requires exactly one argument.");
} }
auto get_properties = [&](const auto &record_accessor) { auto get_properties = [&](const auto &record_accessor) {
std::map<std::string, TypedValue> properties; TypedValue::TMap properties(ctx.memory);
for (const auto &property : record_accessor.Properties()) { for (const auto &property : record_accessor.Properties()) {
properties[dba->PropertyName(property.first)] = properties.emplace(dba->PropertyName(property.first), property.second);
TypedValue(property.second);
} }
return TypedValue(properties); return TypedValue(std::move(properties));
}; };
switch (args[0].type()) { switch (args[0].type()) {
case TypedValue::Type::Null: case TypedValue::Type::Null:
return TypedValue(); return TypedValue(ctx.memory);
case TypedValue::Type::Vertex: case TypedValue::Type::Vertex:
return get_properties(args[0].Value<VertexAccessor>()); return get_properties(args[0].Value<VertexAccessor>());
case TypedValue::Type::Edge: case TypedValue::Type::Edge:
@ -112,83 +117,89 @@ TypedValue Properties(TypedValue *args, int64_t nargs,
} }
} }
TypedValue Size(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue Size(TypedValue *args, int64_t nargs, const EvaluationContext &ctx,
database::GraphDbAccessor *) { database::GraphDbAccessor *) {
if (nargs != 1) { if (nargs != 1) {
throw QueryRuntimeException("'size' requires exactly one argument."); throw QueryRuntimeException("'size' requires exactly one argument.");
} }
switch (args[0].type()) { switch (args[0].type()) {
case TypedValue::Type::Null: case TypedValue::Type::Null:
return TypedValue(); return TypedValue(ctx.memory);
case TypedValue::Type::List: case TypedValue::Type::List:
return TypedValue(static_cast<int64_t>(args[0].ValueList().size())); return TypedValue(static_cast<int64_t>(args[0].ValueList().size()),
ctx.memory);
case TypedValue::Type::String: case TypedValue::Type::String:
return TypedValue(static_cast<int64_t>(args[0].ValueString().size())); return TypedValue(static_cast<int64_t>(args[0].ValueString().size()),
ctx.memory);
case TypedValue::Type::Map: case TypedValue::Type::Map:
// neo4j doesn't implement size for map, but I don't see a good reason not // neo4j doesn't implement size for map, but I don't see a good reason not
// to do it. // to do it.
return TypedValue(static_cast<int64_t>(args[0].ValueMap().size())); return TypedValue(static_cast<int64_t>(args[0].ValueMap().size()),
ctx.memory);
case TypedValue::Type::Path: case TypedValue::Type::Path:
return TypedValue( return TypedValue(
static_cast<int64_t>(args[0].ValuePath().edges().size())); static_cast<int64_t>(args[0].ValuePath().edges().size()), ctx.memory);
default: default:
throw QueryRuntimeException( throw QueryRuntimeException(
"'size' argument must be a string, a collection or a path."); "'size' argument must be a string, a collection or a path.");
} }
} }
TypedValue StartNode(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue StartNode(TypedValue *args, int64_t nargs,
const EvaluationContext &ctx,
database::GraphDbAccessor *) { database::GraphDbAccessor *) {
if (nargs != 1) { if (nargs != 1) {
throw QueryRuntimeException("'startNode' requires exactly one argument."); throw QueryRuntimeException("'startNode' requires exactly one argument.");
} }
switch (args[0].type()) { switch (args[0].type()) {
case TypedValue::Type::Null: case TypedValue::Type::Null:
return TypedValue(); return TypedValue(ctx.memory);
case TypedValue::Type::Edge: case TypedValue::Type::Edge:
return TypedValue(args[0].Value<EdgeAccessor>().from()); return TypedValue(args[0].Value<EdgeAccessor>().from(), ctx.memory);
default: default:
throw QueryRuntimeException("'startNode' argument must be an edge."); throw QueryRuntimeException("'startNode' argument must be an edge.");
} }
} }
TypedValue Degree(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue Degree(TypedValue *args, int64_t nargs, const EvaluationContext &ctx,
database::GraphDbAccessor *) { database::GraphDbAccessor *) {
if (nargs != 1) { if (nargs != 1) {
throw QueryRuntimeException("'degree' requires exactly one argument."); throw QueryRuntimeException("'degree' requires exactly one argument.");
} }
switch (args[0].type()) { switch (args[0].type()) {
case TypedValue::Type::Null: case TypedValue::Type::Null:
return TypedValue(); return TypedValue(ctx.memory);
case TypedValue::Type::Vertex: { case TypedValue::Type::Vertex: {
auto &vertex = args[0].Value<VertexAccessor>(); const auto &vertex = args[0].Value<VertexAccessor>();
return TypedValue( return TypedValue(
static_cast<int64_t>(vertex.out_degree() + vertex.in_degree())); static_cast<int64_t>(vertex.out_degree() + vertex.in_degree()),
ctx.memory);
} }
default: default:
throw QueryRuntimeException("'degree' argument must be a node."); throw QueryRuntimeException("'degree' argument must be a node.");
} }
} }
TypedValue InDegree(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue InDegree(TypedValue *args, int64_t nargs,
database::GraphDbAccessor *) { const EvaluationContext &ctx, database::GraphDbAccessor *) {
if (nargs != 1) { if (nargs != 1) {
throw QueryRuntimeException("'inDegree' requires exactly one argument."); throw QueryRuntimeException("'inDegree' requires exactly one argument.");
} }
switch (args[0].type()) { switch (args[0].type()) {
case TypedValue::Type::Null: case TypedValue::Type::Null:
return TypedValue(); return TypedValue(ctx.memory);
case TypedValue::Type::Vertex: { case TypedValue::Type::Vertex: {
auto &vertex = args[0].Value<VertexAccessor>(); const auto &vertex = args[0].Value<VertexAccessor>();
return TypedValue(static_cast<int64_t>(vertex.in_degree())); return TypedValue(static_cast<int64_t>(vertex.in_degree()), ctx.memory);
} }
default: default:
throw QueryRuntimeException("'inDegree' argument must be a node."); throw QueryRuntimeException("'inDegree' argument must be a node.");
} }
} }
TypedValue OutDegree(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue OutDegree(TypedValue *args, int64_t nargs,
const EvaluationContext &ctx,
database::GraphDbAccessor *) { database::GraphDbAccessor *) {
if (nargs != 1) { if (nargs != 1) {
throw QueryRuntimeException("'outDegree' requires exactly one argument."); throw QueryRuntimeException("'outDegree' requires exactly one argument.");
@ -196,35 +207,36 @@ TypedValue OutDegree(TypedValue *args, int64_t nargs, const EvaluationContext &,
switch (args[0].type()) { switch (args[0].type()) {
case TypedValue::Type::Null: case TypedValue::Type::Null:
return TypedValue(); return TypedValue(ctx.memory);
case TypedValue::Type::Vertex: { case TypedValue::Type::Vertex: {
auto &vertex = args[0].Value<VertexAccessor>(); const auto &vertex = args[0].Value<VertexAccessor>();
return TypedValue(static_cast<int64_t>(vertex.out_degree())); return TypedValue(static_cast<int64_t>(vertex.out_degree()), ctx.memory);
} }
default: default:
throw QueryRuntimeException("'outDegree' argument must be a node."); throw QueryRuntimeException("'outDegree' argument must be a node.");
} }
} }
TypedValue ToBoolean(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue ToBoolean(TypedValue *args, int64_t nargs,
const EvaluationContext &ctx,
database::GraphDbAccessor *) { database::GraphDbAccessor *) {
if (nargs != 1) { if (nargs != 1) {
throw QueryRuntimeException("'toBoolean' requires exactly one argument."); throw QueryRuntimeException("'toBoolean' requires exactly one argument.");
} }
switch (args[0].type()) { switch (args[0].type()) {
case TypedValue::Type::Null: case TypedValue::Type::Null:
return TypedValue(); return TypedValue(ctx.memory);
case TypedValue::Type::Bool: case TypedValue::Type::Bool:
return TypedValue(args[0].Value<bool>()); return TypedValue(args[0].Value<bool>(), ctx.memory);
case TypedValue::Type::Int: case TypedValue::Type::Int:
return TypedValue(args[0].ValueInt() != 0L); return TypedValue(args[0].ValueInt() != 0L, ctx.memory);
case TypedValue::Type::String: { case TypedValue::Type::String: {
auto s = utils::ToUpperCase(utils::Trim(args[0].ValueString())); auto s = utils::ToUpperCase(utils::Trim(args[0].ValueString()));
if (s == "TRUE") return TypedValue(true); if (s == "TRUE") return TypedValue(true, ctx.memory);
if (s == "FALSE") return TypedValue(false); if (s == "FALSE") return TypedValue(false, ctx.memory);
// I think this is just stupid and that exception should be thrown, but // I think this is just stupid and that exception should be thrown, but
// neo4j does it this way... // neo4j does it this way...
return TypedValue(); return TypedValue(ctx.memory);
} }
default: default:
throw QueryRuntimeException( throw QueryRuntimeException(
@ -232,24 +244,25 @@ TypedValue ToBoolean(TypedValue *args, int64_t nargs, const EvaluationContext &,
} }
} }
TypedValue ToFloat(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue ToFloat(TypedValue *args, int64_t nargs,
database::GraphDbAccessor *) { const EvaluationContext &ctx, database::GraphDbAccessor *) {
if (nargs != 1) { if (nargs != 1) {
throw QueryRuntimeException("'toFloat' requires exactly one argument."); throw QueryRuntimeException("'toFloat' requires exactly one argument.");
} }
switch (args[0].type()) { switch (args[0].type()) {
case TypedValue::Type::Null: case TypedValue::Type::Null:
return TypedValue(); return TypedValue(ctx.memory);
case TypedValue::Type::Int: case TypedValue::Type::Int:
return TypedValue(static_cast<double>(args[0].Value<int64_t>())); return TypedValue(static_cast<double>(args[0].Value<int64_t>()),
ctx.memory);
case TypedValue::Type::Double: case TypedValue::Type::Double:
return args[0]; return TypedValue(args[0], ctx.memory);
case TypedValue::Type::String: case TypedValue::Type::String:
try { try {
return TypedValue( return TypedValue(
utils::ParseDouble(utils::Trim(args[0].ValueString()))); utils::ParseDouble(utils::Trim(args[0].ValueString())), ctx.memory);
} catch (const utils::BasicException &) { } catch (const utils::BasicException &) {
return TypedValue(); return TypedValue(ctx.memory);
} }
default: default:
throw QueryRuntimeException( throw QueryRuntimeException(
@ -257,28 +270,31 @@ TypedValue ToFloat(TypedValue *args, int64_t nargs, const EvaluationContext &,
} }
} }
TypedValue ToInteger(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue ToInteger(TypedValue *args, int64_t nargs,
const EvaluationContext &ctx,
database::GraphDbAccessor *) { database::GraphDbAccessor *) {
if (nargs != 1) { if (nargs != 1) {
throw QueryRuntimeException("'toInteger' requires exactly one argument'"); throw QueryRuntimeException("'toInteger' requires exactly one argument'");
} }
switch (args[0].type()) { switch (args[0].type()) {
case TypedValue::Type::Null: case TypedValue::Type::Null:
return TypedValue(); return TypedValue(ctx.memory);
case TypedValue::Type::Bool: case TypedValue::Type::Bool:
return TypedValue(args[0].ValueBool() ? 1L : 0L); return TypedValue(args[0].ValueBool() ? 1L : 0L, ctx.memory);
case TypedValue::Type::Int: case TypedValue::Type::Int:
return args[0]; return TypedValue(args[0], ctx.memory);
case TypedValue::Type::Double: case TypedValue::Type::Double:
return TypedValue(static_cast<int64_t>(args[0].Value<double>())); return TypedValue(static_cast<int64_t>(args[0].Value<double>()),
ctx.memory);
case TypedValue::Type::String: case TypedValue::Type::String:
try { try {
// Yup, this is correct. String is valid if it has floating point // Yup, this is correct. String is valid if it has floating point
// number, then it is parsed and converted to int. // number, then it is parsed and converted to int.
return TypedValue(static_cast<int64_t>( return TypedValue(static_cast<int64_t>(utils::ParseDouble(
utils::ParseDouble(utils::Trim(args[0].ValueString())))); utils::Trim(args[0].ValueString()))),
ctx.memory);
} catch (const utils::BasicException &) { } catch (const utils::BasicException &) {
return TypedValue(); return TypedValue(ctx.memory);
} }
default: default:
throw QueryRuntimeException( throw QueryRuntimeException(
@ -286,37 +302,38 @@ TypedValue ToInteger(TypedValue *args, int64_t nargs, const EvaluationContext &,
} }
} }
TypedValue Type(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue Type(TypedValue *args, int64_t nargs, const EvaluationContext &ctx,
database::GraphDbAccessor *dba) { database::GraphDbAccessor *dba) {
if (nargs != 1) { if (nargs != 1) {
throw QueryRuntimeException("'type' requires exactly one argument."); throw QueryRuntimeException("'type' requires exactly one argument.");
} }
switch (args[0].type()) { switch (args[0].type()) {
case TypedValue::Type::Null: case TypedValue::Type::Null:
return TypedValue(); return TypedValue(ctx.memory);
case TypedValue::Type::Edge: case TypedValue::Type::Edge:
return TypedValue( return TypedValue(
dba->EdgeTypeName(args[0].Value<EdgeAccessor>().EdgeType())); dba->EdgeTypeName(args[0].Value<EdgeAccessor>().EdgeType()),
ctx.memory);
default: default:
throw QueryRuntimeException("'type' argument must be an edge."); throw QueryRuntimeException("'type' argument must be an edge.");
} }
} }
TypedValue Keys(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue Keys(TypedValue *args, int64_t nargs, const EvaluationContext &ctx,
database::GraphDbAccessor *dba) { database::GraphDbAccessor *dba) {
if (nargs != 1) { if (nargs != 1) {
throw QueryRuntimeException("'keys' requires exactly one argument."); throw QueryRuntimeException("'keys' requires exactly one argument.");
} }
auto get_keys = [&](const auto &record_accessor) { auto get_keys = [&](const auto &record_accessor) {
std::vector<TypedValue> keys; TypedValue::TVector keys(ctx.memory);
for (const auto &property : record_accessor.Properties()) { for (const auto &property : record_accessor.Properties()) {
keys.emplace_back(dba->PropertyName(property.first)); keys.emplace_back(dba->PropertyName(property.first));
} }
return TypedValue(keys); return TypedValue(std::move(keys));
}; };
switch (args[0].type()) { switch (args[0].type()) {
case TypedValue::Type::Null: case TypedValue::Type::Null:
return TypedValue(); return TypedValue(ctx.memory);
case TypedValue::Type::Vertex: case TypedValue::Type::Vertex:
return get_keys(args[0].Value<VertexAccessor>()); return get_keys(args[0].Value<VertexAccessor>());
case TypedValue::Type::Edge: case TypedValue::Type::Edge:
@ -326,55 +343,61 @@ TypedValue Keys(TypedValue *args, int64_t nargs, const EvaluationContext &,
} }
} }
TypedValue Labels(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue Labels(TypedValue *args, int64_t nargs, const EvaluationContext &ctx,
database::GraphDbAccessor *dba) { database::GraphDbAccessor *dba) {
if (nargs != 1) { if (nargs != 1) {
throw QueryRuntimeException("'labels' requires exactly one argument."); throw QueryRuntimeException("'labels' requires exactly one argument.");
} }
switch (args[0].type()) { switch (args[0].type()) {
case TypedValue::Type::Null: case TypedValue::Type::Null:
return TypedValue(); return TypedValue(ctx.memory);
case TypedValue::Type::Vertex: { case TypedValue::Type::Vertex: {
std::vector<TypedValue> labels; TypedValue::TVector labels(ctx.memory);
for (const auto &label : args[0].Value<VertexAccessor>().labels()) { for (const auto &label : args[0].Value<VertexAccessor>().labels()) {
labels.emplace_back(dba->LabelName(label)); labels.emplace_back(dba->LabelName(label));
} }
return TypedValue(labels); return TypedValue(std::move(labels));
} }
default: default:
throw QueryRuntimeException("'labels' argument must be a node."); throw QueryRuntimeException("'labels' argument must be a node.");
} }
} }
TypedValue Nodes(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue Nodes(TypedValue *args, int64_t nargs, const EvaluationContext &ctx,
database::GraphDbAccessor *) { database::GraphDbAccessor *) {
if (nargs != 1) { if (nargs != 1) {
throw QueryRuntimeException("'nodes' requires exactly one argument."); throw QueryRuntimeException("'nodes' requires exactly one argument.");
} }
if (args[0].IsNull()) return TypedValue(); if (args[0].IsNull()) return TypedValue(ctx.memory);
if (!args[0].IsPath()) { if (!args[0].IsPath()) {
throw QueryRuntimeException("'nodes' argument should be a path."); throw QueryRuntimeException("'nodes' argument should be a path.");
} }
auto &vertices = args[0].ValuePath().vertices(); const auto &vertices = args[0].ValuePath().vertices();
return TypedValue(std::vector<TypedValue>(vertices.begin(), vertices.end())); TypedValue::TVector values(ctx.memory);
values.reserve(vertices.size());
for (const auto &v : vertices) values.emplace_back(v);
return TypedValue(std::move(values));
} }
TypedValue Relationships(TypedValue *args, int64_t nargs, TypedValue Relationships(TypedValue *args, int64_t nargs,
const EvaluationContext &, const EvaluationContext &ctx,
database::GraphDbAccessor *) { database::GraphDbAccessor *) {
if (nargs != 1) { if (nargs != 1) {
throw QueryRuntimeException( throw QueryRuntimeException(
"'relationships' requires exactly one argument."); "'relationships' requires exactly one argument.");
} }
if (args[0].IsNull()) return TypedValue(); if (args[0].IsNull()) return TypedValue(ctx.memory);
if (!args[0].IsPath()) { if (!args[0].IsPath()) {
throw QueryRuntimeException("'relationships' argument must be a path."); throw QueryRuntimeException("'relationships' argument must be a path.");
} }
auto &edges = args[0].ValuePath().edges(); const auto &edges = args[0].ValuePath().edges();
return TypedValue(std::vector<TypedValue>(edges.begin(), edges.end())); TypedValue::TVector values(ctx.memory);
values.reserve(edges.size());
for (const auto &e : edges) values.emplace_back(e);
return TypedValue(std::move(values));
} }
TypedValue Range(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue Range(TypedValue *args, int64_t nargs, const EvaluationContext &ctx,
database::GraphDbAccessor *) { database::GraphDbAccessor *) {
if (nargs != 2 && nargs != 3) { if (nargs != 2 && nargs != 3) {
throw QueryRuntimeException("'range' requires two or three arguments."); throw QueryRuntimeException("'range' requires two or three arguments.");
@ -388,14 +411,14 @@ TypedValue Range(TypedValue *args, int64_t nargs, const EvaluationContext &,
} }
}; };
for (int64_t i = 0; i < nargs; ++i) check_type(args[i]); for (int64_t i = 0; i < nargs; ++i) check_type(args[i]);
if (has_null) return TypedValue(); if (has_null) return TypedValue(ctx.memory);
auto lbound = args[0].Value<int64_t>(); auto lbound = args[0].Value<int64_t>();
auto rbound = args[1].Value<int64_t>(); auto rbound = args[1].Value<int64_t>();
int64_t step = nargs == 3 ? args[2].Value<int64_t>() : 1; int64_t step = nargs == 3 ? args[2].Value<int64_t>() : 1;
if (step == 0) { if (step == 0) {
throw QueryRuntimeException("step argument of 'range' can't be zero."); throw QueryRuntimeException("step argument of 'range' can't be zero.");
} }
std::vector<TypedValue> list; TypedValue::TVector list(ctx.memory);
if (lbound <= rbound && step > 0) { if (lbound <= rbound && step > 0) {
for (auto i = lbound; i <= rbound; i += step) { for (auto i = lbound; i <= rbound; i += step) {
list.emplace_back(i); list.emplace_back(i);
@ -405,22 +428,22 @@ TypedValue Range(TypedValue *args, int64_t nargs, const EvaluationContext &,
list.emplace_back(i); list.emplace_back(i);
} }
} }
return TypedValue(list); return TypedValue(std::move(list));
} }
TypedValue Tail(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue Tail(TypedValue *args, int64_t nargs, const EvaluationContext &ctx,
database::GraphDbAccessor *) { database::GraphDbAccessor *) {
if (nargs != 1) { if (nargs != 1) {
throw QueryRuntimeException("'tail' requires exactly one argument."); throw QueryRuntimeException("'tail' requires exactly one argument.");
} }
switch (args[0].type()) { switch (args[0].type()) {
case TypedValue::Type::Null: case TypedValue::Type::Null:
return TypedValue(); return TypedValue(ctx.memory);
case TypedValue::Type::List: { case TypedValue::Type::List: {
auto list = args[0].ValueList(); TypedValue::TVector list(args[0].ValueList(), ctx.memory);
if (list.empty()) return TypedValue(list); if (list.empty()) return TypedValue(std::move(list));
list.erase(list.begin()); list.erase(list.begin());
return TypedValue(list); return TypedValue(std::move(list));
} }
default: default:
throw QueryRuntimeException("'tail' argument must be a list."); throw QueryRuntimeException("'tail' argument must be a list.");
@ -428,7 +451,7 @@ TypedValue Tail(TypedValue *args, int64_t nargs, const EvaluationContext &,
} }
TypedValue UniformSample(TypedValue *args, int64_t nargs, TypedValue UniformSample(TypedValue *args, int64_t nargs,
const EvaluationContext &, const EvaluationContext &ctx,
database::GraphDbAccessor *) { database::GraphDbAccessor *) {
static thread_local std::mt19937 pseudo_rand_gen_{std::random_device{}()}; static thread_local std::mt19937 pseudo_rand_gen_{std::random_device{}()};
if (nargs != 2) { if (nargs != 2) {
@ -438,7 +461,7 @@ TypedValue UniformSample(TypedValue *args, int64_t nargs,
switch (args[0].type()) { switch (args[0].type()) {
case TypedValue::Type::Null: case TypedValue::Type::Null:
if (args[1].IsNull() || (args[1].IsInt() && args[1].ValueInt() >= 0)) { if (args[1].IsNull() || (args[1].IsInt() && args[1].ValueInt() >= 0)) {
return TypedValue(); return TypedValue(ctx.memory);
} }
throw QueryRuntimeException( throw QueryRuntimeException(
"Second argument of 'uniformSample' must be a non-negative integer."); "Second argument of 'uniformSample' must be a non-negative integer.");
@ -446,16 +469,16 @@ TypedValue UniformSample(TypedValue *args, int64_t nargs,
if (args[1].IsInt() && args[1].ValueInt() >= 0) { if (args[1].IsInt() && args[1].ValueInt() >= 0) {
auto &population = args[0].ValueList(); auto &population = args[0].ValueList();
auto population_size = population.size(); auto population_size = population.size();
if (population_size == 0) return TypedValue(); if (population_size == 0) return TypedValue(ctx.memory);
auto desired_length = args[1].ValueInt(); auto desired_length = args[1].ValueInt();
std::uniform_int_distribution<uint64_t> rand_dist{0, std::uniform_int_distribution<uint64_t> rand_dist{0,
population_size - 1}; population_size - 1};
std::vector<TypedValue> sampled; TypedValue::TVector sampled(ctx.memory);
sampled.reserve(desired_length); sampled.reserve(desired_length);
for (int i = 0; i < desired_length; ++i) { for (int i = 0; i < desired_length; ++i) {
sampled.push_back(population[rand_dist(pseudo_rand_gen_)]); sampled.emplace_back(population[rand_dist(pseudo_rand_gen_)]);
} }
return TypedValue(sampled); return TypedValue(std::move(sampled));
} }
throw QueryRuntimeException( throw QueryRuntimeException(
"Second argument of 'uniformSample' must be a non-negative integer."); "Second argument of 'uniformSample' must be a non-negative integer.");
@ -465,37 +488,39 @@ TypedValue UniformSample(TypedValue *args, int64_t nargs,
} }
} }
TypedValue Abs(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue Abs(TypedValue *args, int64_t nargs, const EvaluationContext &ctx,
database::GraphDbAccessor *) { database::GraphDbAccessor *) {
if (nargs != 1) { if (nargs != 1) {
throw QueryRuntimeException("'abs' requires exactly one argument."); throw QueryRuntimeException("'abs' requires exactly one argument.");
} }
switch (args[0].type()) { switch (args[0].type()) {
case TypedValue::Type::Null: case TypedValue::Type::Null:
return TypedValue(); return TypedValue(ctx.memory);
case TypedValue::Type::Int: case TypedValue::Type::Int:
return TypedValue(std::abs(args[0].Value<int64_t>())); return TypedValue(std::abs(args[0].Value<int64_t>()), ctx.memory);
case TypedValue::Type::Double: case TypedValue::Type::Double:
return TypedValue(std::abs(args[0].Value<double>())); return TypedValue(std::abs(args[0].Value<double>()), ctx.memory);
default: default:
throw QueryRuntimeException("'abs' argument should be a number."); throw QueryRuntimeException("'abs' argument should be a number.");
} }
} }
#define WRAP_CMATH_FLOAT_FUNCTION(name, lowercased_name) \ #define WRAP_CMATH_FLOAT_FUNCTION(name, lowercased_name) \
TypedValue name(TypedValue *args, int64_t nargs, const EvaluationContext &, \ TypedValue name(TypedValue *args, int64_t nargs, \
database::GraphDbAccessor *) { \ const EvaluationContext &ctx, database::GraphDbAccessor *) { \
if (nargs != 1) { \ if (nargs != 1) { \
throw QueryRuntimeException("'" #lowercased_name \ throw QueryRuntimeException("'" #lowercased_name \
"' requires exactly one argument."); \ "' requires exactly one argument."); \
} \ } \
switch (args[0].type()) { \ switch (args[0].type()) { \
case TypedValue::Type::Null: \ case TypedValue::Type::Null: \
return TypedValue(); \ return TypedValue(ctx.memory); \
case TypedValue::Type::Int: \ case TypedValue::Type::Int: \
return TypedValue(lowercased_name(args[0].Value<int64_t>())); \ return TypedValue(lowercased_name(args[0].Value<int64_t>()), \
ctx.memory); \
case TypedValue::Type::Double: \ case TypedValue::Type::Double: \
return TypedValue(lowercased_name(args[0].Value<double>())); \ return TypedValue(lowercased_name(args[0].Value<double>()), \
ctx.memory); \
default: \ default: \
throw QueryRuntimeException(#lowercased_name \ throw QueryRuntimeException(#lowercased_name \
" argument must be a number."); \ " argument must be a number."); \
@ -520,13 +545,13 @@ WRAP_CMATH_FLOAT_FUNCTION(Tan, tan)
#undef WRAP_CMATH_FLOAT_FUNCTION #undef WRAP_CMATH_FLOAT_FUNCTION
TypedValue Atan2(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue Atan2(TypedValue *args, int64_t nargs, const EvaluationContext &ctx,
database::GraphDbAccessor *) { database::GraphDbAccessor *) {
if (nargs != 2) { if (nargs != 2) {
throw QueryRuntimeException("'atan2' requires two arguments."); throw QueryRuntimeException("'atan2' requires two arguments.");
} }
if (args[0].type() == TypedValue::Type::Null) return TypedValue(); if (args[0].type() == TypedValue::Type::Null) return TypedValue(ctx.memory);
if (args[1].type() == TypedValue::Type::Null) return TypedValue(); if (args[1].type() == TypedValue::Type::Null) return TypedValue(ctx.memory);
auto to_double = [](const TypedValue &t) -> double { auto to_double = [](const TypedValue &t) -> double {
switch (t.type()) { switch (t.type()) {
case TypedValue::Type::Int: case TypedValue::Type::Int:
@ -539,18 +564,18 @@ TypedValue Atan2(TypedValue *args, int64_t nargs, const EvaluationContext &,
}; };
double y = to_double(args[0]); double y = to_double(args[0]);
double x = to_double(args[1]); double x = to_double(args[1]);
return TypedValue(atan2(y, x)); return TypedValue(atan2(y, x), ctx.memory);
} }
TypedValue Sign(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue Sign(TypedValue *args, int64_t nargs, const EvaluationContext &ctx,
database::GraphDbAccessor *) { database::GraphDbAccessor *) {
if (nargs != 1) { if (nargs != 1) {
throw QueryRuntimeException("'sign' requires exactly one argument."); throw QueryRuntimeException("'sign' requires exactly one argument.");
} }
auto sign = [](auto x) { return TypedValue((0 < x) - (x < 0)); }; auto sign = [&](auto x) { return TypedValue((0 < x) - (x < 0), ctx.memory); };
switch (args[0].type()) { switch (args[0].type()) {
case TypedValue::Type::Null: case TypedValue::Type::Null:
return TypedValue(); return TypedValue(ctx.memory);
case TypedValue::Type::Int: case TypedValue::Type::Int:
return sign(args[0].Value<int64_t>()); return sign(args[0].Value<int64_t>());
case TypedValue::Type::Double: case TypedValue::Type::Double:
@ -560,36 +585,36 @@ TypedValue Sign(TypedValue *args, int64_t nargs, const EvaluationContext &,
} }
} }
TypedValue E(TypedValue *, int64_t nargs, const EvaluationContext &, TypedValue E(TypedValue *, int64_t nargs, const EvaluationContext &ctx,
database::GraphDbAccessor *) { database::GraphDbAccessor *) {
if (nargs != 0) { if (nargs != 0) {
throw QueryRuntimeException("'e' requires no arguments."); throw QueryRuntimeException("'e' requires no arguments.");
} }
return TypedValue(M_E); return TypedValue(M_E, ctx.memory);
} }
TypedValue Pi(TypedValue *, int64_t nargs, const EvaluationContext &, TypedValue Pi(TypedValue *, int64_t nargs, const EvaluationContext &ctx,
database::GraphDbAccessor *) { database::GraphDbAccessor *) {
if (nargs != 0) { if (nargs != 0) {
throw QueryRuntimeException("'pi' requires no arguments."); throw QueryRuntimeException("'pi' requires no arguments.");
} }
return TypedValue(M_PI); return TypedValue(M_PI, ctx.memory);
} }
TypedValue Rand(TypedValue *, int64_t nargs, const EvaluationContext &, TypedValue Rand(TypedValue *, int64_t nargs, const EvaluationContext &ctx,
database::GraphDbAccessor *) { database::GraphDbAccessor *) {
static thread_local std::mt19937 pseudo_rand_gen_{std::random_device{}()}; static thread_local std::mt19937 pseudo_rand_gen_{std::random_device{}()};
static thread_local std::uniform_real_distribution<> rand_dist_{0, 1}; static thread_local std::uniform_real_distribution<> rand_dist_{0, 1};
if (nargs != 0) { if (nargs != 0) {
throw QueryRuntimeException("'rand' requires no arguments."); throw QueryRuntimeException("'rand' requires no arguments.");
} }
return TypedValue(rand_dist_(pseudo_rand_gen_)); return TypedValue(rand_dist_(pseudo_rand_gen_), ctx.memory);
} }
template <bool (*Predicate)(const TypedValue::TString &s1, template <bool (*Predicate)(const TypedValue::TString &s1,
const TypedValue::TString &s2)> const TypedValue::TString &s2)>
TypedValue StringMatchOperator(TypedValue *args, int64_t nargs, TypedValue StringMatchOperator(TypedValue *args, int64_t nargs,
const EvaluationContext &, const EvaluationContext &ctx,
database::GraphDbAccessor *) { database::GraphDbAccessor *) {
if (nargs != 2) { if (nargs != 2) {
throw QueryRuntimeException( throw QueryRuntimeException(
@ -606,10 +631,10 @@ TypedValue StringMatchOperator(TypedValue *args, int64_t nargs,
}; };
check_arg(args[0]); check_arg(args[0]);
check_arg(args[1]); check_arg(args[1]);
if (has_null) return TypedValue(); if (has_null) return TypedValue(ctx.memory);
const auto &s1 = args[0].ValueString(); const auto &s1 = args[0].ValueString();
const auto &s2 = args[1].ValueString(); const auto &s2 = args[1].ValueString();
return TypedValue(Predicate(s1, s2)); return TypedValue(Predicate(s1, s2), ctx.memory);
} }
// Check if s1 starts with s2. // Check if s1 starts with s2.
@ -636,7 +661,7 @@ bool ContainsPredicate(const TypedValue::TString &s1,
} }
auto Contains = StringMatchOperator<ContainsPredicate>; auto Contains = StringMatchOperator<ContainsPredicate>;
TypedValue Assert(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue Assert(TypedValue *args, int64_t nargs, const EvaluationContext &ctx,
database::GraphDbAccessor *) { database::GraphDbAccessor *) {
if (nargs < 1 || nargs > 2) { if (nargs < 1 || nargs > 2) {
throw QueryRuntimeException("'assert' requires one or two arguments"); throw QueryRuntimeException("'assert' requires one or two arguments");
@ -656,7 +681,7 @@ TypedValue Assert(TypedValue *args, int64_t nargs, const EvaluationContext &,
message += "."; message += ".";
throw QueryRuntimeException(message); throw QueryRuntimeException(message);
} }
return args[0]; return TypedValue(args[0], ctx.memory);
} }
#if defined(MG_SINGLE_NODE) || defined(MG_SINGLE_NODE_HA) #if defined(MG_SINGLE_NODE) || defined(MG_SINGLE_NODE_HA)
@ -684,22 +709,24 @@ TypedValue Counter(TypedValue *args, int64_t nargs,
auto value = it->second; auto value = it->second;
it->second += step; it->second += step;
return TypedValue(value); return TypedValue(value, context.memory);
} }
#endif #endif
#ifdef MG_DISTRIBUTED #ifdef MG_DISTRIBUTED
TypedValue WorkerId(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue WorkerId(TypedValue *args, int64_t nargs,
database::GraphDbAccessor *) { const EvaluationContext &ctx, database::GraphDbAccessor *) {
if (nargs != 1) { if (nargs != 1) {
throw QueryRuntimeException("'workerId' requires exactly one argument."); throw QueryRuntimeException("'workerId' requires exactly one argument.");
} }
auto &arg = args[0]; const auto &arg = args[0];
switch (arg.type()) { switch (arg.type()) {
case TypedValue::Type::Vertex: case TypedValue::Type::Vertex:
return TypedValue(arg.ValueVertex().GlobalAddress().worker_id()); return TypedValue(arg.ValueVertex().GlobalAddress().worker_id(),
ctx.memory);
case TypedValue::Type::Edge: case TypedValue::Type::Edge:
return TypedValue(arg.ValueEdge().GlobalAddress().worker_id()); return TypedValue(arg.ValueEdge().GlobalAddress().worker_id(),
ctx.memory);
default: default:
throw QueryRuntimeException( throw QueryRuntimeException(
"'workerId' argument must be a node or an edge."); "'workerId' argument must be a node or an edge.");
@ -707,41 +734,43 @@ TypedValue WorkerId(TypedValue *args, int64_t nargs, const EvaluationContext &,
} }
#endif #endif
TypedValue Id(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue Id(TypedValue *args, int64_t nargs, const EvaluationContext &ctx,
database::GraphDbAccessor *dba) { database::GraphDbAccessor *dba) {
if (nargs != 1) { if (nargs != 1) {
throw QueryRuntimeException("'id' requires exactly one argument."); throw QueryRuntimeException("'id' requires exactly one argument.");
} }
auto &arg = args[0]; const auto &arg = args[0];
switch (arg.type()) { switch (arg.type()) {
case TypedValue::Type::Vertex: { case TypedValue::Type::Vertex: {
return TypedValue(arg.ValueVertex().CypherId()); return TypedValue(arg.ValueVertex().CypherId(), ctx.memory);
} }
case TypedValue::Type::Edge: { case TypedValue::Type::Edge: {
return TypedValue(arg.ValueEdge().CypherId()); return TypedValue(arg.ValueEdge().CypherId(), ctx.memory);
} }
default: default:
throw QueryRuntimeException("'id' argument must be a node or an edge."); throw QueryRuntimeException("'id' argument must be a node or an edge.");
} }
} }
TypedValue ToString(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue ToString(TypedValue *args, int64_t nargs,
database::GraphDbAccessor *) { const EvaluationContext &ctx, database::GraphDbAccessor *) {
if (nargs != 1) { if (nargs != 1) {
throw QueryRuntimeException("'toString' requires exactly one argument."); throw QueryRuntimeException("'toString' requires exactly one argument.");
} }
auto &arg = args[0]; const auto &arg = args[0];
switch (arg.type()) { switch (arg.type()) {
case TypedValue::Type::Null: case TypedValue::Type::Null:
return TypedValue(); return TypedValue(ctx.memory);
case TypedValue::Type::String: case TypedValue::Type::String:
return arg; return TypedValue(arg, ctx.memory);
case TypedValue::Type::Int: case TypedValue::Type::Int:
return TypedValue(std::to_string(arg.ValueInt())); // TODO: This is making a pointless copy of std::string, we may want to
// use a different conversion to string
return TypedValue(std::to_string(arg.ValueInt()), ctx.memory);
case TypedValue::Type::Double: case TypedValue::Type::Double:
return TypedValue(std::to_string(arg.ValueDouble())); return TypedValue(std::to_string(arg.ValueDouble()), ctx.memory);
case TypedValue::Type::Bool: case TypedValue::Type::Bool:
return TypedValue(arg.ValueBool() ? "true" : "false"); return TypedValue(arg.ValueBool() ? "true" : "false", ctx.memory);
default: default:
throw QueryRuntimeException( throw QueryRuntimeException(
"'toString' argument must be a number, a string or a boolean."); "'toString' argument must be a number, a string or a boolean.");
@ -753,10 +782,10 @@ TypedValue Timestamp(TypedValue *, int64_t nargs, const EvaluationContext &ctx,
if (nargs != 0) { if (nargs != 0) {
throw QueryRuntimeException("'timestamp' requires no arguments."); throw QueryRuntimeException("'timestamp' requires no arguments.");
} }
return TypedValue(ctx.timestamp); return TypedValue(ctx.timestamp, ctx.memory);
} }
TypedValue Left(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue Left(TypedValue *args, int64_t nargs, const EvaluationContext &ctx,
database::GraphDbAccessor *dba) { database::GraphDbAccessor *dba) {
if (nargs != 2) { if (nargs != 2) {
throw QueryRuntimeException("'left' requires two arguments."); throw QueryRuntimeException("'left' requires two arguments.");
@ -764,16 +793,15 @@ TypedValue Left(TypedValue *args, int64_t nargs, const EvaluationContext &,
switch (args[0].type()) { switch (args[0].type()) {
case TypedValue::Type::Null: case TypedValue::Type::Null:
if (args[1].IsNull() || (args[1].IsInt() && args[1].ValueInt() >= 0)) { if (args[1].IsNull() || (args[1].IsInt() && args[1].ValueInt() >= 0)) {
return TypedValue(); return TypedValue(ctx.memory);
} }
throw QueryRuntimeException( throw QueryRuntimeException(
"Second argument of 'left' must be a non-negative integer."); "Second argument of 'left' must be a non-negative integer.");
case TypedValue::Type::String: case TypedValue::Type::String:
if (args[1].IsInt() && args[1].ValueInt() >= 0) { if (args[1].IsInt() && args[1].ValueInt() >= 0) {
auto *memory = args[0].GetMemoryResource();
return TypedValue( return TypedValue(
utils::Substr(args[0].ValueString(), 0, args[1].ValueInt()), utils::Substr(args[0].ValueString(), 0, args[1].ValueInt()),
memory); ctx.memory);
} }
throw QueryRuntimeException( throw QueryRuntimeException(
"Second argument of 'left' must be a non-negative integer."); "Second argument of 'left' must be a non-negative integer.");
@ -782,7 +810,7 @@ TypedValue Left(TypedValue *args, int64_t nargs, const EvaluationContext &,
} }
} }
TypedValue Right(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue Right(TypedValue *args, int64_t nargs, const EvaluationContext &ctx,
database::GraphDbAccessor *dba) { database::GraphDbAccessor *dba) {
if (nargs != 2) { if (nargs != 2) {
throw QueryRuntimeException("'right' requires two arguments."); throw QueryRuntimeException("'right' requires two arguments.");
@ -790,7 +818,7 @@ TypedValue Right(TypedValue *args, int64_t nargs, const EvaluationContext &,
switch (args[0].type()) { switch (args[0].type()) {
case TypedValue::Type::Null: case TypedValue::Type::Null:
if (args[1].IsNull() || (args[1].IsInt() && args[1].ValueInt() >= 0)) { if (args[1].IsNull() || (args[1].IsInt() && args[1].ValueInt() >= 0)) {
return TypedValue(); return TypedValue(ctx.memory);
} }
throw QueryRuntimeException( throw QueryRuntimeException(
"Second argument of 'right' must be a non-negative integer."); "Second argument of 'right' must be a non-negative integer.");
@ -798,11 +826,10 @@ TypedValue Right(TypedValue *args, int64_t nargs, const EvaluationContext &,
const auto &str = args[0].ValueString(); const auto &str = args[0].ValueString();
if (args[1].IsInt() && args[1].ValueInt() >= 0) { if (args[1].IsInt() && args[1].ValueInt() >= 0) {
auto len = args[1].ValueInt(); auto len = args[1].ValueInt();
auto *memory = args[0].GetMemoryResource();
return len <= str.size() return len <= str.size()
? TypedValue(utils::Substr(str, str.size() - len, len), ? TypedValue(utils::Substr(str, str.size() - len, len),
memory) ctx.memory)
: TypedValue(str, memory); : TypedValue(str, ctx.memory);
} }
throw QueryRuntimeException( throw QueryRuntimeException(
"Second argument of 'right' must be a non-negative integer."); "Second argument of 'right' must be a non-negative integer.");
@ -814,7 +841,8 @@ TypedValue Right(TypedValue *args, int64_t nargs, const EvaluationContext &,
} }
TypedValue CallStringFunction( TypedValue CallStringFunction(
TypedValue *args, int64_t nargs, const std::string &name, TypedValue *args, int64_t nargs, utils::MemoryResource *memory,
const std::string &name,
std::function<TypedValue::TString(const TypedValue::TString &)> fun) { std::function<TypedValue::TString(const TypedValue::TString &)> fun) {
if (nargs != 1) { if (nargs != 1) {
throw QueryRuntimeException("'" + name + throw QueryRuntimeException("'" + name +
@ -822,62 +850,68 @@ TypedValue CallStringFunction(
} }
switch (args[0].type()) { switch (args[0].type()) {
case TypedValue::Type::Null: case TypedValue::Type::Null:
return TypedValue(args[0].GetMemoryResource()); return TypedValue(memory);
case TypedValue::Type::String: case TypedValue::Type::String:
return TypedValue(fun(args[0].ValueString())); return TypedValue(fun(args[0].ValueString()), memory);
default: default:
throw QueryRuntimeException("'" + name + throw QueryRuntimeException("'" + name +
"' argument should be a string."); "' argument should be a string.");
} }
} }
TypedValue LTrim(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue LTrim(TypedValue *args, int64_t nargs, const EvaluationContext &ctx,
database::GraphDbAccessor *) { database::GraphDbAccessor *) {
return CallStringFunction(args, nargs, "lTrim", [](const auto &str) { return CallStringFunction(
return TypedValue::TString(utils::LTrim(str), str.get_allocator()); args, nargs, ctx.memory, "lTrim", [&](const auto &str) {
return TypedValue::TString(utils::LTrim(str), ctx.memory);
}); });
} }
TypedValue RTrim(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue RTrim(TypedValue *args, int64_t nargs, const EvaluationContext &ctx,
database::GraphDbAccessor *) { database::GraphDbAccessor *) {
return CallStringFunction(args, nargs, "rTrim", [](const auto &str) { return CallStringFunction(
return TypedValue::TString(utils::RTrim(str), str.get_allocator()); args, nargs, ctx.memory, "rTrim", [&](const auto &str) {
return TypedValue::TString(utils::RTrim(str), ctx.memory);
}); });
} }
TypedValue Trim(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue Trim(TypedValue *args, int64_t nargs, const EvaluationContext &ctx,
database::GraphDbAccessor *) { database::GraphDbAccessor *) {
return CallStringFunction(args, nargs, "trim", [](const auto &str) { return CallStringFunction(
return TypedValue::TString(utils::Trim(str), str.get_allocator()); args, nargs, ctx.memory, "trim", [&](const auto &str) {
return TypedValue::TString(utils::Trim(str), ctx.memory);
}); });
} }
TypedValue Reverse(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue Reverse(TypedValue *args, int64_t nargs,
database::GraphDbAccessor *) { const EvaluationContext &ctx, database::GraphDbAccessor *) {
return CallStringFunction(args, nargs, "reverse", [](const auto &str) { return CallStringFunction(
return utils::Reversed(str, str.get_allocator()); args, nargs, ctx.memory, "reverse",
}); [&](const auto &str) { return utils::Reversed(str, ctx.memory); });
} }
TypedValue ToLower(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue ToLower(TypedValue *args, int64_t nargs,
database::GraphDbAccessor *) { const EvaluationContext &ctx, database::GraphDbAccessor *) {
return CallStringFunction(args, nargs, "toLower", [](const auto &str) { return CallStringFunction(args, nargs, ctx.memory, "toLower",
TypedValue::TString res(str.get_allocator()); [&](const auto &str) {
TypedValue::TString res(ctx.memory);
utils::ToLowerCase(&res, str); utils::ToLowerCase(&res, str);
return res; return res;
}); });
} }
TypedValue ToUpper(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue ToUpper(TypedValue *args, int64_t nargs,
database::GraphDbAccessor *) { const EvaluationContext &ctx, database::GraphDbAccessor *) {
return CallStringFunction(args, nargs, "toUpper", [](const auto &str) { return CallStringFunction(args, nargs, ctx.memory, "toUpper",
TypedValue::TString res(str.get_allocator()); [&](const auto &str) {
TypedValue::TString res(ctx.memory);
utils::ToUpperCase(&res, str); utils::ToUpperCase(&res, str);
return res; return res;
}); });
} }
TypedValue Replace(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue Replace(TypedValue *args, int64_t nargs,
const EvaluationContext &ctx,
database::GraphDbAccessor *dba) { database::GraphDbAccessor *dba) {
if (nargs != 3) { if (nargs != 3) {
throw QueryRuntimeException("'replace' requires three arguments."); throw QueryRuntimeException("'replace' requires three arguments.");
@ -895,13 +929,15 @@ TypedValue Replace(TypedValue *args, int64_t nargs, const EvaluationContext &,
"Third argument of 'replace' should be a string."); "Third argument of 'replace' should be a string.");
} }
if (args[0].IsNull() || args[1].IsNull() || args[2].IsNull()) { if (args[0].IsNull() || args[1].IsNull() || args[2].IsNull()) {
return TypedValue(); return TypedValue(ctx.memory);
} }
return TypedValue(utils::Replace(args[0].ValueString(), args[1].ValueString(), TypedValue::TString replaced(ctx.memory);
args[2].ValueString())); utils::Replace(&replaced, args[0].ValueString(), args[1].ValueString(),
args[2].ValueString());
return TypedValue(std::move(replaced));
} }
TypedValue Split(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue Split(TypedValue *args, int64_t nargs, const EvaluationContext &ctx,
database::GraphDbAccessor *dba) { database::GraphDbAccessor *dba) {
if (nargs != 2) { if (nargs != 2) {
throw QueryRuntimeException("'split' requires two arguments."); throw QueryRuntimeException("'split' requires two arguments.");
@ -915,17 +951,15 @@ TypedValue Split(TypedValue *args, int64_t nargs, const EvaluationContext &,
"Second argument of 'split' should be a string."); "Second argument of 'split' should be a string.");
} }
if (args[0].IsNull() || args[1].IsNull()) { if (args[0].IsNull() || args[1].IsNull()) {
return TypedValue(); return TypedValue(ctx.memory);
} }
std::vector<TypedValue> result; TypedValue::TVector result(ctx.memory);
for (const auto &str : utils::Split(&result, args[0].ValueString(), args[1].ValueString());
utils::Split(args[0].ValueString(), args[1].ValueString())) { return TypedValue(std::move(result));
result.emplace_back(str);
}
return TypedValue(result);
} }
TypedValue Substring(TypedValue *args, int64_t nargs, const EvaluationContext &, TypedValue Substring(TypedValue *args, int64_t nargs,
const EvaluationContext &ctx,
database::GraphDbAccessor *) { database::GraphDbAccessor *) {
if (nargs != 2 && nargs != 3) { if (nargs != 2 && nargs != 3) {
throw QueryRuntimeException("'substring' requires two or three arguments."); throw QueryRuntimeException("'substring' requires two or three arguments.");
@ -943,21 +977,20 @@ TypedValue Substring(TypedValue *args, int64_t nargs, const EvaluationContext &,
"Third argument of 'substring' should be a non-negative integer."); "Third argument of 'substring' should be a non-negative integer.");
} }
if (args[0].IsNull()) { if (args[0].IsNull()) {
return TypedValue(); return TypedValue(ctx.memory);
} }
const auto &str = args[0].ValueString(); const auto &str = args[0].ValueString();
auto start = args[1].ValueInt(); auto start = args[1].ValueInt();
auto *memory = args[0].GetMemoryResource();
if (nargs == 2) { if (nargs == 2) {
return TypedValue(utils::Substr(str, start), memory); return TypedValue(utils::Substr(str, start), ctx.memory);
} }
auto len = args[2].ValueInt(); auto len = args[2].ValueInt();
return TypedValue(utils::Substr(str, start, len), memory); return TypedValue(utils::Substr(str, start, len), ctx.memory);
} }
} // namespace } // namespace
std::function<TypedValue(TypedValue *, int64_t, const EvaluationContext &, std::function<TypedValue(TypedValue *, int64_t, const EvaluationContext &ctx,
database::GraphDbAccessor *)> database::GraphDbAccessor *)>
NameToFunction(const std::string &function_name) { NameToFunction(const std::string &function_name) {
// Scalar functions // Scalar functions

View File

@ -381,22 +381,23 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
for (size_t i = 0; i < function.arguments_.size(); ++i) { for (size_t i = 0; i < function.arguments_.size(); ++i) {
arguments[i] = function.arguments_[i]->Accept(*this); arguments[i] = function.arguments_[i]->Accept(*this);
} }
// TODO: Update awesome_memgraph_functions to use the allocator from ctx_ auto res = function.function_(arguments, function.arguments_.size(),
return TypedValue(function.function_( *ctx_, dba_);
arguments, function.arguments_.size(), *ctx_, dba_), CHECK(res.GetMemoryResource() == ctx_->memory);
ctx_->memory); return res;
} else { } else {
TypedValue::TVector arguments(ctx_->memory); TypedValue::TVector arguments(ctx_->memory);
arguments.reserve(function.arguments_.size()); arguments.reserve(function.arguments_.size());
for (const auto &argument : function.arguments_) { for (const auto &argument : function.arguments_) {
arguments.emplace_back(argument->Accept(*this)); arguments.emplace_back(argument->Accept(*this));
} }
// TODO: Update awesome_memgraph_functions to use the allocator from ctx_ auto res =
return TypedValue( function.function_(arguments.data(), arguments.size(), *ctx_, dba_);
function.function_(arguments.data(), arguments.size(), *ctx_, dba_), CHECK(res.GetMemoryResource() == ctx_->memory);
ctx_->memory); return res;
} }
} }
TypedValue Visit(Reduce &reduce) override { TypedValue Visit(Reduce &reduce) override {
auto list_value = reduce.list_->Accept(*this); auto list_value = reduce.list_->Accept(*this);
if (list_value.IsNull()) { if (list_value.IsNull()) {

View File

@ -175,7 +175,7 @@ template <class TAllocator>
std::basic_string<char, std::char_traits<char>, TAllocator> *Replace( std::basic_string<char, std::char_traits<char>, TAllocator> *Replace(
std::basic_string<char, std::char_traits<char>, TAllocator> *out, std::basic_string<char, std::char_traits<char>, TAllocator> *out,
const std::string_view &src, const std::string_view &match, const std::string_view &src, const std::string_view &match,
const std::string_view &replacement, const TAllocator &alloc) { const std::string_view &replacement) {
// TODO: This could be implemented much more efficiently. // TODO: This could be implemented much more efficiently.
*out = src; *out = src;
for (size_t pos = out->find(match); pos != std::string::npos; for (size_t pos = out->find(match); pos != std::string::npos;
@ -190,7 +190,7 @@ inline std::string Replace(const std::string_view &src,
const std::string_view &match, const std::string_view &match,
const std::string_view &replacement) { const std::string_view &replacement) {
std::string res; std::string res;
Replace(&res, src, match, replacement, std::allocator<char>()); Replace(&res, src, match, replacement);
return res; return res;
} }