mirror of https://gitee.com/bigwinds/arangodb
fix out-of-bounds attribute accessor calls (#3273)
This commit is contained in:
parent
f8fa46a00f
commit
0a71b54e1f
|
@ -2687,7 +2687,7 @@ AstNode* Ast::optimizeBinaryOperatorRelational(AstNode* node) {
|
||||||
|
|
||||||
TRI_ASSERT(lhs->isConstant() && rhs->isConstant());
|
TRI_ASSERT(lhs->isConstant() && rhs->isConstant());
|
||||||
|
|
||||||
Expression exp(this, node);
|
Expression exp(nullptr, this, node);
|
||||||
FixedVarExpressionContext context;
|
FixedVarExpressionContext context;
|
||||||
bool mustDestroy;
|
bool mustDestroy;
|
||||||
// execute the expression using the C++ variant
|
// execute the expression using the C++ variant
|
||||||
|
@ -2973,7 +2973,7 @@ AstNode* Ast::optimizeFunctionCall(AstNode* node) {
|
||||||
TRI_ASSERT(_query->trx() != nullptr);
|
TRI_ASSERT(_query->trx() != nullptr);
|
||||||
|
|
||||||
if (func->hasImplementation() && node->isSimple()) {
|
if (func->hasImplementation() && node->isSimple()) {
|
||||||
Expression exp(this, node);
|
Expression exp(nullptr, this, node);
|
||||||
FixedVarExpressionContext context;
|
FixedVarExpressionContext context;
|
||||||
bool mustDestroy;
|
bool mustDestroy;
|
||||||
// execute the expression using the C++ variant
|
// execute the expression using the C++ variant
|
||||||
|
|
|
@ -34,7 +34,8 @@ using namespace arangodb::aql;
|
||||||
|
|
||||||
/// @brief create the accessor
|
/// @brief create the accessor
|
||||||
AttributeAccessor::AttributeAccessor(
|
AttributeAccessor::AttributeAccessor(
|
||||||
std::vector<std::string>&& attributeParts, Variable const* variable)
|
std::vector<std::string>&& attributeParts, Variable const* variable,
|
||||||
|
bool dataIsFromCollection)
|
||||||
: _attributeParts(attributeParts),
|
: _attributeParts(attributeParts),
|
||||||
_variable(variable),
|
_variable(variable),
|
||||||
_type(EXTRACT_MULTI) {
|
_type(EXTRACT_MULTI) {
|
||||||
|
@ -43,14 +44,18 @@ AttributeAccessor::AttributeAccessor(
|
||||||
TRI_ASSERT(!_attributeParts.empty());
|
TRI_ASSERT(!_attributeParts.empty());
|
||||||
|
|
||||||
// determine accessor type
|
// determine accessor type
|
||||||
|
// it is only safe to use the optimized accessor functions for system attributes
|
||||||
|
// when the input data are collection documents. it is not safe to use them for
|
||||||
|
// non-collection data, as the optimized functions may easily create out-of-bounds
|
||||||
|
// accesses in that case
|
||||||
if (_attributeParts.size() == 1) {
|
if (_attributeParts.size() == 1) {
|
||||||
if (attributeParts[0] == StaticStrings::KeyString) {
|
if (dataIsFromCollection && attributeParts[0] == StaticStrings::KeyString) {
|
||||||
_type = EXTRACT_KEY;
|
_type = EXTRACT_KEY;
|
||||||
} else if (attributeParts[0] == StaticStrings::IdString) {
|
} else if (dataIsFromCollection && attributeParts[0] == StaticStrings::IdString) {
|
||||||
_type = EXTRACT_ID;
|
_type = EXTRACT_ID;
|
||||||
} else if (attributeParts[0] == StaticStrings::FromString) {
|
} else if (dataIsFromCollection && attributeParts[0] == StaticStrings::FromString) {
|
||||||
_type = EXTRACT_FROM;
|
_type = EXTRACT_FROM;
|
||||||
} else if (attributeParts[0] == StaticStrings::ToString) {
|
} else if (dataIsFromCollection && attributeParts[0] == StaticStrings::ToString) {
|
||||||
_type = EXTRACT_TO;
|
_type = EXTRACT_TO;
|
||||||
} else {
|
} else {
|
||||||
_type = EXTRACT_SINGLE;
|
_type = EXTRACT_SINGLE;
|
||||||
|
|
|
@ -43,7 +43,7 @@ struct Variable;
|
||||||
/// @brief AttributeAccessor
|
/// @brief AttributeAccessor
|
||||||
class AttributeAccessor {
|
class AttributeAccessor {
|
||||||
public:
|
public:
|
||||||
AttributeAccessor(std::vector<std::string>&&, Variable const*);
|
AttributeAccessor(std::vector<std::string>&&, Variable const*, bool dataIsFromCollection);
|
||||||
~AttributeAccessor() = default;
|
~AttributeAccessor() = default;
|
||||||
|
|
||||||
/// @brief execute the accessor
|
/// @brief execute the accessor
|
||||||
|
|
|
@ -1332,7 +1332,7 @@ CalculationNode::CalculationNode(ExecutionPlan* plan,
|
||||||
: ExecutionNode(plan, base),
|
: ExecutionNode(plan, base),
|
||||||
_conditionVariable(Variable::varFromVPack(plan->getAst(), base, "conditionVariable", true)),
|
_conditionVariable(Variable::varFromVPack(plan->getAst(), base, "conditionVariable", true)),
|
||||||
_outVariable(Variable::varFromVPack(plan->getAst(), base, "outVariable")),
|
_outVariable(Variable::varFromVPack(plan->getAst(), base, "outVariable")),
|
||||||
_expression(new Expression(plan->getAst(), base)),
|
_expression(new Expression(plan, plan->getAst(), base)),
|
||||||
_canRemoveIfThrows(false) {}
|
_canRemoveIfThrows(false) {}
|
||||||
|
|
||||||
/// @brief toVelocyPack, for CalculationNode
|
/// @brief toVelocyPack, for CalculationNode
|
||||||
|
@ -1373,7 +1373,7 @@ ExecutionNode* CalculationNode::clone(ExecutionPlan* plan,
|
||||||
outVariable = plan->getAst()->variables()->createVariable(outVariable);
|
outVariable = plan->getAst()->variables()->createVariable(outVariable);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto c = new CalculationNode(plan, _id, _expression->clone(plan->getAst()),
|
auto c = new CalculationNode(plan, _id, _expression->clone(plan, plan->getAst()),
|
||||||
conditionVariable, outVariable);
|
conditionVariable, outVariable);
|
||||||
c->_canRemoveIfThrows = _canRemoveIfThrows;
|
c->_canRemoveIfThrows = _canRemoveIfThrows;
|
||||||
|
|
||||||
|
|
|
@ -400,7 +400,7 @@ ExecutionNode* ExecutionPlan::createCalculation(
|
||||||
|
|
||||||
// generate a temporary calculation node
|
// generate a temporary calculation node
|
||||||
auto expr =
|
auto expr =
|
||||||
std::make_unique<Expression>(_ast, const_cast<AstNode*>(expression));
|
std::make_unique<Expression>(this, _ast, const_cast<AstNode*>(expression));
|
||||||
|
|
||||||
CalculationNode* en;
|
CalculationNode* en;
|
||||||
if (conditionVariable != nullptr) {
|
if (conditionVariable != nullptr) {
|
||||||
|
|
|
@ -26,6 +26,8 @@
|
||||||
#include "Aql/AqlValue.h"
|
#include "Aql/AqlValue.h"
|
||||||
#include "Aql/Ast.h"
|
#include "Aql/Ast.h"
|
||||||
#include "Aql/AttributeAccessor.h"
|
#include "Aql/AttributeAccessor.h"
|
||||||
|
#include "Aql/ExecutionNode.h"
|
||||||
|
#include "Aql/ExecutionPlan.h"
|
||||||
#include "Aql/ExpressionContext.h"
|
#include "Aql/ExpressionContext.h"
|
||||||
#include "Aql/BaseExpressionContext.h"
|
#include "Aql/BaseExpressionContext.h"
|
||||||
#include "Aql/Function.h"
|
#include "Aql/Function.h"
|
||||||
|
@ -80,8 +82,9 @@ static void RegisterWarning(arangodb::aql::Ast const* ast,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create the expression
|
/// @brief create the expression
|
||||||
Expression::Expression(Ast* ast, AstNode* node)
|
Expression::Expression(ExecutionPlan* plan, Ast* ast, AstNode* node)
|
||||||
: _ast(ast),
|
: _plan(plan),
|
||||||
|
_ast(ast),
|
||||||
_node(node),
|
_node(node),
|
||||||
_type(UNPROCESSED),
|
_type(UNPROCESSED),
|
||||||
_canThrow(true),
|
_canThrow(true),
|
||||||
|
@ -96,8 +99,8 @@ Expression::Expression(Ast* ast, AstNode* node)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief create an expression from VPack
|
/// @brief create an expression from VPack
|
||||||
Expression::Expression(Ast* ast, arangodb::velocypack::Slice const& slice)
|
Expression::Expression(ExecutionPlan* plan, Ast* ast, arangodb::velocypack::Slice const& slice)
|
||||||
: Expression(ast, new AstNode(ast, slice.get("expression"))) {}
|
: Expression(plan, ast, new AstNode(ast, slice.get("expression"))) {}
|
||||||
|
|
||||||
/// @brief destroy the expression
|
/// @brief destroy the expression
|
||||||
Expression::~Expression() {
|
Expression::~Expression() {
|
||||||
|
@ -404,8 +407,20 @@ void Expression::analyzeExpression() {
|
||||||
if (member->type == NODE_TYPE_REFERENCE) {
|
if (member->type == NODE_TYPE_REFERENCE) {
|
||||||
auto v = static_cast<Variable const*>(member->getData());
|
auto v = static_cast<Variable const*>(member->getData());
|
||||||
|
|
||||||
|
bool dataIsFromCollection = false;
|
||||||
|
if (_plan != nullptr) {
|
||||||
|
// check if the variable we are referring to is set by
|
||||||
|
// a collection enumeration/index enumeration
|
||||||
|
auto setter = _plan->getVarSetBy(v->id);
|
||||||
|
if (setter != nullptr &&
|
||||||
|
(setter->getType() == ExecutionNode::INDEX || setter->getType() == ExecutionNode::ENUMERATE_COLLECTION)) {
|
||||||
|
// it is
|
||||||
|
dataIsFromCollection = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// specialize the simple expression into an attribute accessor
|
// specialize the simple expression into an attribute accessor
|
||||||
_accessor = new AttributeAccessor(std::move(parts), v);
|
_accessor = new AttributeAccessor(std::move(parts), v, dataIsFromCollection);
|
||||||
if (_accessor->isDynamic()) {
|
if (_accessor->isDynamic()) {
|
||||||
_type = ATTRIBUTE_DYNAMIC;
|
_type = ATTRIBUTE_DYNAMIC;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -48,6 +48,7 @@ class AqlItemBlock;
|
||||||
struct AqlValue;
|
struct AqlValue;
|
||||||
class Ast;
|
class Ast;
|
||||||
class AttributeAccessor;
|
class AttributeAccessor;
|
||||||
|
class ExecutionPlan;
|
||||||
class ExpressionContext;
|
class ExpressionContext;
|
||||||
struct V8Expression;
|
struct V8Expression;
|
||||||
|
|
||||||
|
@ -61,10 +62,10 @@ class Expression {
|
||||||
Expression() = delete;
|
Expression() = delete;
|
||||||
|
|
||||||
/// @brief constructor, using an AST start node
|
/// @brief constructor, using an AST start node
|
||||||
Expression(Ast*, AstNode*);
|
Expression(ExecutionPlan* plan, Ast*, AstNode*);
|
||||||
|
|
||||||
/// @brief constructor, using VPack
|
/// @brief constructor, using VPack
|
||||||
Expression(Ast*, arangodb::velocypack::Slice const&);
|
Expression(ExecutionPlan* plan, Ast*, arangodb::velocypack::Slice const&);
|
||||||
|
|
||||||
~Expression();
|
~Expression();
|
||||||
|
|
||||||
|
@ -105,10 +106,10 @@ class Expression {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief clone the expression, needed to clone execution plans
|
/// @brief clone the expression, needed to clone execution plans
|
||||||
Expression* clone(Ast* ast) {
|
Expression* clone(ExecutionPlan* plan, Ast* ast) {
|
||||||
// We do not need to copy the _ast, since it is managed by the
|
// We do not need to copy the _ast, since it is managed by the
|
||||||
// query object and the memory management of the ASTs
|
// query object and the memory management of the ASTs
|
||||||
return new Expression(ast != nullptr ? ast : _ast, _node);
|
return new Expression(plan, ast != nullptr ? ast : _ast, _node);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief return all variables used in the expression
|
/// @brief return all variables used in the expression
|
||||||
|
@ -328,6 +329,10 @@ class Expression {
|
||||||
bool& mustDestroy);
|
bool& mustDestroy);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/// @brief the query execution plan. note: this may be a nullptr for expressions
|
||||||
|
/// created in the early optimization stage!
|
||||||
|
ExecutionPlan* _plan;
|
||||||
|
|
||||||
/// @brief the AST
|
/// @brief the AST
|
||||||
Ast* _ast;
|
Ast* _ast;
|
||||||
|
|
||||||
|
|
|
@ -163,7 +163,7 @@ int IndexBlock::initialize() {
|
||||||
auto instantiateExpression = [&](size_t i, size_t j, size_t k,
|
auto instantiateExpression = [&](size_t i, size_t j, size_t k,
|
||||||
AstNode* a) -> void {
|
AstNode* a) -> void {
|
||||||
// all new AstNodes are registered with the Ast in the Query
|
// all new AstNodes are registered with the Ast in the Query
|
||||||
auto e = std::make_unique<Expression>(ast, a);
|
auto e = std::make_unique<Expression>(en->_plan, ast, a);
|
||||||
|
|
||||||
TRI_IF_FAILURE("IndexBlock::initialize") {
|
TRI_IF_FAILURE("IndexBlock::initialize") {
|
||||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_DEBUG);
|
||||||
|
|
|
@ -230,7 +230,7 @@ void arangodb::aql::sortInValuesRule(Optimizer* opt,
|
||||||
|
|
||||||
auto outVar = ast->variables()->createTemporaryVariable();
|
auto outVar = ast->variables()->createTemporaryVariable();
|
||||||
ExecutionNode* calculationNode = nullptr;
|
ExecutionNode* calculationNode = nullptr;
|
||||||
auto expression = new Expression(ast, sorted);
|
auto expression = new Expression(plan.get(), ast, sorted);
|
||||||
try {
|
try {
|
||||||
calculationNode =
|
calculationNode =
|
||||||
new CalculationNode(plan.get(), plan->nextId(), expression, outVar);
|
new CalculationNode(plan.get(), plan->nextId(), expression, outVar);
|
||||||
|
@ -1056,7 +1056,7 @@ void arangodb::aql::splitFiltersRule(Optimizer* opt,
|
||||||
|
|
||||||
ExecutionNode* calculationNode = nullptr;
|
ExecutionNode* calculationNode = nullptr;
|
||||||
auto outVar = plan->getAst()->variables()->createTemporaryVariable();
|
auto outVar = plan->getAst()->variables()->createTemporaryVariable();
|
||||||
auto expression = new Expression(plan->getAst(), current);
|
auto expression = new Expression(plan.get(), plan->getAst(), current);
|
||||||
try {
|
try {
|
||||||
calculationNode = new CalculationNode(plan.get(), plan->nextId(),
|
calculationNode = new CalculationNode(plan.get(), plan->nextId(),
|
||||||
expression, outVar);
|
expression, outVar);
|
||||||
|
@ -2038,7 +2038,7 @@ void arangodb::aql::removeFiltersCoveredByIndexRule(
|
||||||
} else if (newNode != condition->root()) {
|
} else if (newNode != condition->root()) {
|
||||||
// some condition is left, but it is a different one than
|
// some condition is left, but it is a different one than
|
||||||
// the one from the FILTER node
|
// the one from the FILTER node
|
||||||
auto expr = std::make_unique<Expression>(plan->getAst(), newNode);
|
auto expr = std::make_unique<Expression>(plan.get(), plan->getAst(), newNode);
|
||||||
CalculationNode* cn =
|
CalculationNode* cn =
|
||||||
new CalculationNode(plan.get(), plan->nextId(), expr.get(),
|
new CalculationNode(plan.get(), plan->nextId(), expr.get(),
|
||||||
calculationNode->outVariable());
|
calculationNode->outVariable());
|
||||||
|
@ -3713,7 +3713,7 @@ void arangodb::aql::replaceOrWithInRule(Optimizer* opt,
|
||||||
|
|
||||||
if (newRoot != root) {
|
if (newRoot != root) {
|
||||||
ExecutionNode* newNode = nullptr;
|
ExecutionNode* newNode = nullptr;
|
||||||
Expression* expr = new Expression(plan->getAst(), newRoot);
|
Expression* expr = new Expression(plan.get(), plan->getAst(), newRoot);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
TRI_IF_FAILURE("OptimizerRules::replaceOrWithInRuleOom") {
|
TRI_IF_FAILURE("OptimizerRules::replaceOrWithInRuleOom") {
|
||||||
|
@ -3902,7 +3902,7 @@ void arangodb::aql::removeRedundantOrRule(Optimizer* opt,
|
||||||
ExecutionNode* newNode = nullptr;
|
ExecutionNode* newNode = nullptr;
|
||||||
auto astNode = remover.createReplacementNode(plan->getAst());
|
auto astNode = remover.createReplacementNode(plan->getAst());
|
||||||
|
|
||||||
expr = new Expression(plan->getAst(), astNode);
|
expr = new Expression(plan.get(), plan->getAst(), astNode);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
newNode =
|
newNode =
|
||||||
|
@ -4172,7 +4172,7 @@ void arangodb::aql::removeFiltersCoveredByTraversal(Optimizer* opt, std::unique_
|
||||||
} else if (newNode != condition->root()) {
|
} else if (newNode != condition->root()) {
|
||||||
// some condition is left, but it is a different one than
|
// some condition is left, but it is a different one than
|
||||||
// the one from the FILTER node
|
// the one from the FILTER node
|
||||||
auto expr = std::make_unique<Expression>(plan->getAst(), newNode);
|
auto expr = std::make_unique<Expression>(plan.get(), plan->getAst(), newNode);
|
||||||
CalculationNode* cn =
|
CalculationNode* cn =
|
||||||
new CalculationNode(plan.get(), plan->nextId(), expr.get(),
|
new CalculationNode(plan.get(), plan->nextId(), expr.get(),
|
||||||
calculationNode->outVariable());
|
calculationNode->outVariable());
|
||||||
|
@ -4822,7 +4822,7 @@ void replaceGeoCondition(ExecutionPlan* plan, GeoIndexInfo& info) {
|
||||||
auto ast = plan->getAst();
|
auto ast = plan->getAst();
|
||||||
CalculationNode* newNode = nullptr;
|
CalculationNode* newNode = nullptr;
|
||||||
Expression* expr =
|
Expression* expr =
|
||||||
new Expression(ast, static_cast<CalculationNode*>(info.setter)
|
new Expression(plan, ast, static_cast<CalculationNode*>(info.setter)
|
||||||
->expression()
|
->expression()
|
||||||
->nodeForModification()
|
->nodeForModification()
|
||||||
->clone(ast));
|
->clone(ast));
|
||||||
|
|
|
@ -733,7 +733,7 @@ bool TraversalConditionFinder::isTrueOnNull(AstNode* node, Variable const* pathV
|
||||||
TRI_ASSERT(_plan->getAst() != nullptr);
|
TRI_ASSERT(_plan->getAst() != nullptr);
|
||||||
|
|
||||||
bool mustDestroy = false;
|
bool mustDestroy = false;
|
||||||
Expression tmpExp(_plan->getAst(), node);
|
Expression tmpExp(_plan, _plan->getAst(), node);
|
||||||
|
|
||||||
TRI_ASSERT(_plan->getAst()->query() != nullptr);
|
TRI_ASSERT(_plan->getAst()->query() != nullptr);
|
||||||
auto trx = _plan->getAst()->query()->trx();
|
auto trx = _plan->getAst()->query()->trx();
|
||||||
|
|
|
@ -550,7 +550,7 @@ void TraversalNode::prepareOptions() {
|
||||||
for (auto const& jt : _globalVertexConditions) {
|
for (auto const& jt : _globalVertexConditions) {
|
||||||
it.second->addMember(jt);
|
it.second->addMember(jt);
|
||||||
}
|
}
|
||||||
opts->_vertexExpressions.emplace(it.first, new Expression(ast, it.second));
|
opts->_vertexExpressions.emplace(it.first, new Expression(_plan, ast, it.second));
|
||||||
TRI_ASSERT(!opts->_vertexExpressions[it.first]->isV8());
|
TRI_ASSERT(!opts->_vertexExpressions[it.first]->isV8());
|
||||||
}
|
}
|
||||||
if (!_globalVertexConditions.empty()) {
|
if (!_globalVertexConditions.empty()) {
|
||||||
|
@ -559,7 +559,7 @@ void TraversalNode::prepareOptions() {
|
||||||
for (auto const& it : _globalVertexConditions) {
|
for (auto const& it : _globalVertexConditions) {
|
||||||
cond->addMember(it);
|
cond->addMember(it);
|
||||||
}
|
}
|
||||||
opts->_baseVertexExpression = new Expression(ast, cond);
|
opts->_baseVertexExpression = new Expression(_plan, ast, cond);
|
||||||
TRI_ASSERT(!opts->_baseVertexExpression->isV8());
|
TRI_ASSERT(!opts->_baseVertexExpression->isV8());
|
||||||
}
|
}
|
||||||
// If we use the path output the cache should activate document
|
// If we use the path output the cache should activate document
|
||||||
|
|
|
@ -95,7 +95,7 @@ BaseOptions::LookupInfo::LookupInfo(arangodb::aql::Query* query,
|
||||||
|
|
||||||
read = info.get("expression");
|
read = info.get("expression");
|
||||||
if (read.isObject()) {
|
if (read.isObject()) {
|
||||||
expression = new aql::Expression(query->ast(), read);
|
expression = new aql::Expression(query->plan(), query->ast(), read);
|
||||||
} else {
|
} else {
|
||||||
expression = nullptr;
|
expression = nullptr;
|
||||||
}
|
}
|
||||||
|
@ -117,7 +117,7 @@ BaseOptions::LookupInfo::LookupInfo(LookupInfo const& other)
|
||||||
conditionNeedUpdate(other.conditionNeedUpdate),
|
conditionNeedUpdate(other.conditionNeedUpdate),
|
||||||
conditionMemberToUpdate(other.conditionMemberToUpdate) {
|
conditionMemberToUpdate(other.conditionMemberToUpdate) {
|
||||||
if (other.expression != nullptr) {
|
if (other.expression != nullptr) {
|
||||||
expression = other.expression->clone(nullptr);
|
expression = other.expression->clone(nullptr, nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,7 +301,7 @@ void BaseOptions::injectLookupInfoInList(std::vector<LookupInfo>& list,
|
||||||
condition->removeMemberUnchecked(n - 1);
|
condition->removeMemberUnchecked(n - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
info.expression = new aql::Expression(plan->getAst(), condition);
|
info.expression = new aql::Expression(plan, plan->getAst(), condition);
|
||||||
}
|
}
|
||||||
list.emplace_back(std::move(info));
|
list.emplace_back(std::move(info));
|
||||||
}
|
}
|
||||||
|
|
|
@ -212,11 +212,11 @@ arangodb::traverser::TraverserOptions::TraverserOptions(
|
||||||
uint64_t d = basics::StringUtils::uint64(info.key.copyString());
|
uint64_t d = basics::StringUtils::uint64(info.key.copyString());
|
||||||
#ifdef ARANGODB_ENABLE_MAINAINER_MODE
|
#ifdef ARANGODB_ENABLE_MAINAINER_MODE
|
||||||
auto it = _vertexExpressions.emplace(
|
auto it = _vertexExpressions.emplace(
|
||||||
d, new aql::Expression(query->ast(), info.value));
|
d, new aql::Expression(query->plan(), query->ast(), info.value));
|
||||||
TRI_ASSERT(it.second);
|
TRI_ASSERT(it.second);
|
||||||
#else
|
#else
|
||||||
_vertexExpressions.emplace(d,
|
_vertexExpressions.emplace(d,
|
||||||
new aql::Expression(query->ast(), info.value));
|
new aql::Expression(query->plan(), query->ast(), info.value));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -228,7 +228,7 @@ arangodb::traverser::TraverserOptions::TraverserOptions(
|
||||||
TRI_ERROR_BAD_PARAMETER,
|
TRI_ERROR_BAD_PARAMETER,
|
||||||
"The options require vertexExpressions to be an object");
|
"The options require vertexExpressions to be an object");
|
||||||
}
|
}
|
||||||
_baseVertexExpression = new aql::Expression(query->ast(), read);
|
_baseVertexExpression = new aql::Expression(query->plan(), query->ast(), read);
|
||||||
}
|
}
|
||||||
// Check for illegal option combination:
|
// Check for illegal option combination:
|
||||||
TRI_ASSERT(uniqueEdges != TraverserOptions::UniquenessLevel::GLOBAL);
|
TRI_ASSERT(uniqueEdges != TraverserOptions::UniquenessLevel::GLOBAL);
|
||||||
|
|
|
@ -241,7 +241,6 @@ VPackSlice transaction::helpers::extractToFromDocument(VPackSlice slice) {
|
||||||
}
|
}
|
||||||
// this method must only be called on edges
|
// this method must only be called on edges
|
||||||
// this means we must have at least the attributes _key, _id, _from, _to and _rev
|
// this means we must have at least the attributes _key, _id, _from, _to and _rev
|
||||||
|
|
||||||
uint8_t const* p = slice.begin() + slice.findDataOffset(slice.head());
|
uint8_t const* p = slice.begin() + slice.findDataOffset(slice.head());
|
||||||
VPackValueLength count = 0;
|
VPackValueLength count = 0;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue