mirror of https://gitee.com/bigwinds/arangodb
fixed issue #2210
This commit is contained in:
parent
b8c72dece8
commit
d2742f2ef3
|
@ -1634,6 +1634,7 @@ void Ast::validateAndOptimize() {
|
|||
struct TraversalContext {
|
||||
std::unordered_set<std::string> writeCollectionsSeen;
|
||||
std::unordered_map<std::string, int64_t> collectionsFirstSeen;
|
||||
std::unordered_map<Variable const*, AstNode const*> variableDefinitions;
|
||||
int64_t stopOptimizationRequests = 0;
|
||||
int64_t nestingLevel = 0;
|
||||
bool isInFilter = false;
|
||||
|
@ -1769,7 +1770,7 @@ void Ast::validateAndOptimize() {
|
|||
|
||||
// attribute access
|
||||
if (node->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
|
||||
return this->optimizeAttributeAccess(node);
|
||||
return this->optimizeAttributeAccess(node, static_cast<TraversalContext*>(data)->variableDefinitions);
|
||||
}
|
||||
|
||||
// passthru node
|
||||
|
@ -1813,6 +1814,22 @@ void Ast::validateAndOptimize() {
|
|||
|
||||
// LET
|
||||
if (node->type == NODE_TYPE_LET) {
|
||||
// remember variable assignments
|
||||
TRI_ASSERT(node->numMembers() == 2);
|
||||
auto context = static_cast<TraversalContext*>(data);
|
||||
Variable const* variable = static_cast<Variable const*>(node->getMember(0)->getData());
|
||||
AstNode const* definition = node->getMember(1);
|
||||
// recursively process assignments so we can track LET a = b LET c = b
|
||||
|
||||
while (definition->type == NODE_TYPE_REFERENCE) {
|
||||
auto it = context->variableDefinitions.find(static_cast<Variable const*>(definition->getData()));
|
||||
if (it == context->variableDefinitions.end()) {
|
||||
break;
|
||||
}
|
||||
definition = (*it).second;
|
||||
}
|
||||
|
||||
context->variableDefinitions.emplace(variable, definition);
|
||||
return this->optimizeLet(node);
|
||||
}
|
||||
|
||||
|
@ -2669,12 +2686,21 @@ AstNode* Ast::optimizeTernaryOperator(AstNode* node) {
|
|||
}
|
||||
|
||||
/// @brief optimizes an attribute access
|
||||
AstNode* Ast::optimizeAttributeAccess(AstNode* node) {
|
||||
AstNode* Ast::optimizeAttributeAccess(AstNode* node, std::unordered_map<Variable const*, AstNode const*> const& variableDefinitions) {
|
||||
TRI_ASSERT(node != nullptr);
|
||||
TRI_ASSERT(node->type == NODE_TYPE_ATTRIBUTE_ACCESS);
|
||||
TRI_ASSERT(node->numMembers() == 1);
|
||||
|
||||
AstNode* what = node->getMember(0);
|
||||
AstNode const* what = node->getMember(0);
|
||||
|
||||
if (what->type == NODE_TYPE_REFERENCE) {
|
||||
// check if the access value is a variable and if it is an alias
|
||||
auto it = variableDefinitions.find(static_cast<Variable const*>(what->getData()));
|
||||
|
||||
if (it != variableDefinitions.end()) {
|
||||
what = (*it).second;
|
||||
}
|
||||
}
|
||||
|
||||
if (!what->isConstant()) {
|
||||
return node;
|
||||
|
@ -2689,6 +2715,7 @@ AstNode* Ast::optimizeAttributeAccess(AstNode* node) {
|
|||
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
AstNode const* member = what->getMember(0);
|
||||
|
||||
if (member->type == NODE_TYPE_OBJECT_ELEMENT &&
|
||||
member->getStringLength() == length &&
|
||||
memcmp(name, member->getStringValue(), length) == 0) {
|
||||
|
@ -2943,6 +2970,8 @@ AstNode* Ast::nodeFromVPack(VPackSlice const& slice, bool copyStringValues) {
|
|||
node->addMember(nodeFromVPack(it, copyStringValues));
|
||||
}
|
||||
|
||||
node->setFlag(DETERMINED_CONSTANT, VALUE_CONSTANT);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
|
@ -2964,12 +2993,69 @@ AstNode* Ast::nodeFromVPack(VPackSlice const& slice, bool copyStringValues) {
|
|||
attributeName, static_cast<size_t>(nameLength), nodeFromVPack(it.value, copyStringValues)));
|
||||
}
|
||||
|
||||
node->setFlag(DETERMINED_CONSTANT, VALUE_CONSTANT);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
return createNodeValueNull();
|
||||
}
|
||||
|
||||
/// @brief resolve an attribute access
|
||||
AstNode const* Ast::resolveConstAttributeAccess(AstNode const* node) {
|
||||
TRI_ASSERT(node != nullptr);
|
||||
TRI_ASSERT(node->type == NODE_TYPE_ATTRIBUTE_ACCESS);
|
||||
|
||||
std::vector<std::string> attributeNames;
|
||||
|
||||
while (node->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
|
||||
attributeNames.emplace_back(node->getString());
|
||||
node = node->getMember(0);
|
||||
}
|
||||
|
||||
size_t which = attributeNames.size();
|
||||
TRI_ASSERT(which > 0);
|
||||
|
||||
while (which > 0) {
|
||||
TRI_ASSERT(node->type == NODE_TYPE_VALUE || node->type == NODE_TYPE_ARRAY ||
|
||||
node->type == NODE_TYPE_OBJECT);
|
||||
|
||||
bool found = false;
|
||||
|
||||
if (node->type == NODE_TYPE_OBJECT) {
|
||||
TRI_ASSERT(which > 0);
|
||||
std::string const& attributeName = attributeNames[which - 1];
|
||||
--which;
|
||||
|
||||
size_t const n = node->numMembers();
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
auto member = node->getMember(i);
|
||||
|
||||
if (member->type == NODE_TYPE_OBJECT_ELEMENT &&
|
||||
member->getString() == attributeName) {
|
||||
// found the attribute
|
||||
node = member->getMember(0);
|
||||
if (which == 0) {
|
||||
// we found what we looked for
|
||||
return node;
|
||||
}
|
||||
// we found the correct attribute but there is now an attribute
|
||||
// access on the result
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// attribute not found or non-array
|
||||
return createNodeValueNull();
|
||||
}
|
||||
|
||||
/// @brief traverse the AST, using pre- and post-order visitors
|
||||
AstNode* Ast::traverseAndModify(
|
||||
AstNode* node, std::function<bool(AstNode const*, void*)> preVisitor,
|
||||
|
|
|
@ -413,6 +413,9 @@ class Ast {
|
|||
/// @brief create an AST node from vpack
|
||||
AstNode* nodeFromVPack(arangodb::velocypack::Slice const&, bool);
|
||||
|
||||
/// @brief resolve an attribute access
|
||||
static AstNode const* resolveConstAttributeAccess(AstNode const*);
|
||||
|
||||
/// @brief traverse the AST using a depth-first visitor
|
||||
static AstNode* traverseAndModify(AstNode*,
|
||||
std::function<AstNode*(AstNode*, void*)>,
|
||||
|
@ -457,7 +460,7 @@ class Ast {
|
|||
AstNode* optimizeTernaryOperator(AstNode*);
|
||||
|
||||
/// @brief optimizes an attribute access
|
||||
AstNode* optimizeAttributeAccess(AstNode*);
|
||||
AstNode* optimizeAttributeAccess(AstNode*, std::unordered_map<Variable const*, AstNode const*> const&);
|
||||
|
||||
/// @brief optimizes a call to a built-in function
|
||||
AstNode* optimizeFunctionCall(AstNode*);
|
||||
|
|
|
@ -187,61 +187,6 @@ std::unordered_map<int, std::string const> const AstNode::ValueTypeNames{
|
|||
|
||||
namespace {
|
||||
|
||||
/// @brief resolve an attribute access
|
||||
static AstNode const* ResolveAttribute(AstNode const* node) {
|
||||
TRI_ASSERT(node != nullptr);
|
||||
TRI_ASSERT(node->type == NODE_TYPE_ATTRIBUTE_ACCESS);
|
||||
|
||||
std::vector<std::string> attributeNames;
|
||||
|
||||
while (node->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
|
||||
attributeNames.emplace_back(node->getString());
|
||||
node = node->getMember(0);
|
||||
}
|
||||
|
||||
size_t which = attributeNames.size();
|
||||
TRI_ASSERT(which > 0);
|
||||
|
||||
while (which > 0) {
|
||||
TRI_ASSERT(node->type == NODE_TYPE_VALUE || node->type == NODE_TYPE_ARRAY ||
|
||||
node->type == NODE_TYPE_OBJECT);
|
||||
|
||||
bool found = false;
|
||||
|
||||
if (node->type == NODE_TYPE_OBJECT) {
|
||||
TRI_ASSERT(which > 0);
|
||||
std::string const& attributeName = attributeNames[which - 1];
|
||||
--which;
|
||||
|
||||
size_t const n = node->numMembers();
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
auto member = node->getMember(i);
|
||||
|
||||
if (member->type == NODE_TYPE_OBJECT_ELEMENT &&
|
||||
member->getString() == attributeName) {
|
||||
// found the attribute
|
||||
node = member->getMember(0);
|
||||
if (which == 0) {
|
||||
// we found what we looked for
|
||||
return node;
|
||||
}
|
||||
// we found the correct attribute but there is now an attribute
|
||||
// access on the result
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// attribute not found or non-array
|
||||
return Ast::createNodeValueNull();
|
||||
}
|
||||
|
||||
/// @brief get the node type for inter-node comparisons
|
||||
static VPackValueType GetNodeCompareType(AstNode const* node) {
|
||||
TRI_ASSERT(node != nullptr);
|
||||
|
@ -276,10 +221,10 @@ int arangodb::aql::CompareAstNodes(AstNode const* lhs, AstNode const* rhs,
|
|||
TRI_ASSERT(rhs != nullptr);
|
||||
|
||||
if (lhs->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
|
||||
lhs = ResolveAttribute(lhs);
|
||||
lhs = Ast::resolveConstAttributeAccess(lhs);
|
||||
}
|
||||
if (rhs->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
|
||||
rhs = ResolveAttribute(rhs);
|
||||
rhs = Ast::resolveConstAttributeAccess(rhs);
|
||||
}
|
||||
|
||||
auto lType = GetNodeCompareType(lhs);
|
||||
|
@ -419,6 +364,7 @@ AstNode::AstNode(bool v, AstNodeValueType valueType)
|
|||
value.value._bool = v;
|
||||
TRI_ASSERT(flags == 0);
|
||||
TRI_ASSERT(computedValue == nullptr);
|
||||
setFlag(DETERMINED_CONSTANT, VALUE_CONSTANT);
|
||||
}
|
||||
|
||||
/// @brief create an int node, with defining a value
|
||||
|
@ -428,6 +374,7 @@ AstNode::AstNode(int64_t v, AstNodeValueType valueType)
|
|||
value.value._int = v;
|
||||
TRI_ASSERT(flags == 0);
|
||||
TRI_ASSERT(computedValue == nullptr);
|
||||
setFlag(DETERMINED_CONSTANT, VALUE_CONSTANT);
|
||||
}
|
||||
|
||||
/// @brief create a string node, with defining a value
|
||||
|
@ -437,6 +384,7 @@ AstNode::AstNode(char const* v, size_t length, AstNodeValueType valueType)
|
|||
setStringValue(v, length);
|
||||
TRI_ASSERT(flags == 0);
|
||||
TRI_ASSERT(computedValue == nullptr);
|
||||
setFlag(DETERMINED_CONSTANT, VALUE_CONSTANT);
|
||||
}
|
||||
|
||||
/// @brief create the node from VPack
|
||||
|
|
|
@ -722,14 +722,20 @@ void Condition::optimize(ExecutionPlan* plan) {
|
|||
auto operand = andNode->getMemberUnchecked(j);
|
||||
|
||||
if (operand->isComparisonOperator()) {
|
||||
auto lhs = operand->getMember(0);
|
||||
auto rhs = operand->getMember(1);
|
||||
AstNode const* lhs = operand->getMember(0);
|
||||
AstNode const* rhs = operand->getMember(1);
|
||||
|
||||
if (lhs->type == NODE_TYPE_ATTRIBUTE_ACCESS) {
|
||||
if (lhs->isConstant()) {
|
||||
lhs = Ast::resolveConstAttributeAccess(lhs);
|
||||
}
|
||||
storeAttributeAccess(varAccess, variableUsage, lhs, j, ATTRIBUTE_LEFT);
|
||||
}
|
||||
if (rhs->type == NODE_TYPE_ATTRIBUTE_ACCESS ||
|
||||
rhs->type == NODE_TYPE_EXPANSION) {
|
||||
if (rhs->type == NODE_TYPE_ATTRIBUTE_ACCESS && rhs->isConstant()) {
|
||||
rhs = Ast::resolveConstAttributeAccess(rhs);
|
||||
}
|
||||
storeAttributeAccess(varAccess, variableUsage, rhs, j, ATTRIBUTE_RIGHT);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,6 +53,30 @@ IndexBlock::IndexBlock(ExecutionEngine* engine, IndexNode const* en)
|
|||
_hasV8Expression(false) {
|
||||
|
||||
_mmdr.reset(new ManagedDocumentResult(transaction()));
|
||||
|
||||
if (_condition != nullptr) {
|
||||
// fix const attribute accesses, e.g. { "a": 1 }.a
|
||||
for (size_t i = 0; i < _condition->numMembers(); ++i) {
|
||||
auto andCond = _condition->getMemberUnchecked(i);
|
||||
for (size_t j = 0; j < andCond->numMembers(); ++j) {
|
||||
auto leaf = andCond->getMemberUnchecked(j);
|
||||
|
||||
// We only support binary conditions
|
||||
TRI_ASSERT(leaf->numMembers() == 2);
|
||||
AstNode* lhs = leaf->getMember(0);
|
||||
AstNode* rhs = leaf->getMember(1);
|
||||
|
||||
if (lhs->type == NODE_TYPE_ATTRIBUTE_ACCESS && lhs->isConstant()) {
|
||||
lhs = const_cast<AstNode*>(Ast::resolveConstAttributeAccess(lhs));
|
||||
leaf->changeMember(0, lhs);
|
||||
}
|
||||
if (rhs->type == NODE_TYPE_ATTRIBUTE_ACCESS && rhs->isConstant()) {
|
||||
rhs = const_cast<AstNode*>(Ast::resolveConstAttributeAccess(rhs));
|
||||
leaf->changeMember(1, rhs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IndexBlock::~IndexBlock() {
|
||||
|
@ -176,8 +200,8 @@ int IndexBlock::initialize() {
|
|||
|
||||
// We only support binary conditions
|
||||
TRI_ASSERT(leaf->numMembers() == 2);
|
||||
auto lhs = leaf->getMember(0);
|
||||
auto rhs = leaf->getMember(1);
|
||||
AstNode* lhs = leaf->getMember(0);
|
||||
AstNode* rhs = leaf->getMember(1);
|
||||
|
||||
if (lhs->isAttributeAccessForVariable(outVariable, false)) {
|
||||
// Index is responsible for the left side, check if right side has to be
|
||||
|
|
Loading…
Reference in New Issue